diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..4c3f26a --- /dev/null +++ b/.clang-format @@ -0,0 +1,16 @@ +BasedOnStyle: Chromium +AlignTrailingComments: true +BreakBeforeBraces: Attach +ColumnLimit: 0 +IndentWidth: 4 +KeepEmptyLinesAtTheStartOfBlocks: false +ObjCBlockIndentWidth: 4 +ObjCSpaceAfterProperty: true +ObjCSpaceBeforeProtocolList: true +PointerBindsToType: false +SpacesBeforeTrailingComments: 1 +TabWidth: 8 +UseTab: Never +SpacesInContainerLiterals: false +AllowShortIfStatementsOnASingleLine: true +AllowShortBlocksOnASingleLine: true \ No newline at end of file diff --git a/.swift-version b/.swift-version deleted file mode 100644 index 9f55b2c..0000000 --- a/.swift-version +++ /dev/null @@ -1 +0,0 @@ -3.0 diff --git a/.swiftlint.yml b/.swiftlint.yml deleted file mode 100644 index 81c75f9..0000000 --- a/.swiftlint.yml +++ /dev/null @@ -1,10 +0,0 @@ -included: -- Zapic - -excluded: -- Examples -- Pods -- Tests - -disabled_rules: -- line_length diff --git a/.travis.yml b/.travis.yml index abf78f3..7c591fc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,46 +1,26 @@ language: objective-c -osx_image: xcode9 +osx_image: xcode9.3 branches: only: - master - # Tags formatted vX.Y.Z* - - /v\d+\.\d+\.\d+.*/ + # Tags formatted X.Y.Z* + - /\d+\.\d+\.\d+.*/ env: global: - - POD_LINT = "NO" - -# cache: -# directories: -# - Carthage - -# before_install: - # - brew update - # - brew outdated carthage || brew upgrade carthage - # - carthage bootstrap --no-use-binaries --platform iOS --cache-builds + - POD_LINT = "NO" script: -- set -o pipefail -- xcodebuild -version -- xcodebuild -showsdks -- swiftlint + - set -o pipefail + - xcodebuild -version + - xcodebuild -showsdks + + # Run `pod lib lint` if specified + - if [ $POD_LINT == "YES" ]; then + pod lib lint; + fi -# Run `pod lib lint` if specified -- if [ $POD_LINT == "YES" ]; then - pod lib lint; - fi - -#- xcodebuild -workspace Zapic.xcworkspace -scheme Zapic -destination 'OS=10.3.1,name=iPhone 7' ENABLE_TESTABILITY=YES test | xcpretty -#- xcodebuild -workspace Zapic.xcworkspace -scheme Zapic -destination 'OS=10.3.1,name=iPhone 7' build | xcpretty -- xcodebuild -workspace Zapic.xcworkspace -scheme Zapic archive | xcpretty -- zip -r Zapic.framework.zip zapic.framework + - brew install clang-format -deploy: - provider: releases - skip_cleanup: true - api_key: $GITHUB - file: Zapic.framework.zip - on: - repo: ZapicInc/Zapic-SDK-iOS - tags: true \ No newline at end of file + - xcodebuild -workspace Zapic.xcworkspace -scheme Zapic archive | xcpretty diff --git a/Cartfile.private b/Cartfile.private deleted file mode 100644 index c2786fb..0000000 --- a/Cartfile.private +++ /dev/null @@ -1,2 +0,0 @@ -github "Quick/Quick" -github "Quick/Nimble" diff --git a/Cartfile.resolved b/Cartfile.resolved deleted file mode 100644 index 521e3f8..0000000 --- a/Cartfile.resolved +++ /dev/null @@ -1,2 +0,0 @@ -github "Quick/Nimble" "v7.0.2" -github "Quick/Quick" "v1.2.0" diff --git a/Examples/iOS Example/iOS Example.xcodeproj/project.pbxproj b/Examples/iOS Example/iOS Example.xcodeproj/project.pbxproj deleted file mode 100644 index 3bca458..0000000 --- a/Examples/iOS Example/iOS Example.xcodeproj/project.pbxproj +++ /dev/null @@ -1,438 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 46; - objects = { - -/* Begin PBXBuildFile section */ - 651624361F16DA9F00932E26 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 651624351F16DA9F00932E26 /* AppDelegate.swift */; }; - 651624381F16DA9F00932E26 /* GameScene.sks in Resources */ = {isa = PBXBuildFile; fileRef = 651624371F16DA9F00932E26 /* GameScene.sks */; }; - 6516243A1F16DA9F00932E26 /* Actions.sks in Resources */ = {isa = PBXBuildFile; fileRef = 651624391F16DA9F00932E26 /* Actions.sks */; }; - 6516243C1F16DA9F00932E26 /* GameScene.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6516243B1F16DA9F00932E26 /* GameScene.swift */; }; - 6516243E1F16DA9F00932E26 /* GameViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6516243D1F16DA9F00932E26 /* GameViewController.swift */; }; - 651624411F16DA9F00932E26 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 6516243F1F16DA9F00932E26 /* Main.storyboard */; }; - 651624431F16DA9F00932E26 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 651624421F16DA9F00932E26 /* Assets.xcassets */; }; - 651624461F16DA9F00932E26 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 651624441F16DA9F00932E26 /* LaunchScreen.storyboard */; }; - 6576C5651F1949760075E226 /* Zapic.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6576C5621F1949240075E226 /* Zapic.framework */; }; - 6576C5661F1949760075E226 /* Zapic.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 6576C5621F1949240075E226 /* Zapic.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; -/* End PBXBuildFile section */ - -/* Begin PBXContainerItemProxy section */ - 6576C5611F1949240075E226 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 6576C55C1F1949240075E226 /* Zapic.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 651AEDA11F16C49900986F05; - remoteInfo = Zapic; - }; - 6576C5631F1949240075E226 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 6576C55C1F1949240075E226 /* Zapic.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 651AEDAA1F16C49900986F05; - remoteInfo = ZapicTests; - }; - 6576C5671F1949760075E226 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 6576C55C1F1949240075E226 /* Zapic.xcodeproj */; - proxyType = 1; - remoteGlobalIDString = 651AEDA01F16C49900986F05; - remoteInfo = Zapic; - }; -/* End PBXContainerItemProxy section */ - -/* Begin PBXCopyFilesBuildPhase section */ - 6576C5691F1949760075E226 /* Embed Frameworks */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = ""; - dstSubfolderSpec = 10; - files = ( - 6576C5661F1949760075E226 /* Zapic.framework in Embed Frameworks */, - ); - name = "Embed Frameworks"; - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXCopyFilesBuildPhase section */ - -/* Begin PBXFileReference section */ - 651624321F16DA9F00932E26 /* iOS Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "iOS Example.app"; sourceTree = BUILT_PRODUCTS_DIR; }; - 651624351F16DA9F00932E26 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; - 651624371F16DA9F00932E26 /* GameScene.sks */ = {isa = PBXFileReference; lastKnownFileType = file.sks; path = GameScene.sks; sourceTree = ""; }; - 651624391F16DA9F00932E26 /* Actions.sks */ = {isa = PBXFileReference; lastKnownFileType = file.sks; path = Actions.sks; sourceTree = ""; }; - 6516243B1F16DA9F00932E26 /* GameScene.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GameScene.swift; sourceTree = ""; }; - 6516243D1F16DA9F00932E26 /* GameViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GameViewController.swift; sourceTree = ""; }; - 651624401F16DA9F00932E26 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; - 651624421F16DA9F00932E26 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; - 651624451F16DA9F00932E26 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; - 651624471F16DA9F00932E26 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 6576C55C1F1949240075E226 /* Zapic.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Zapic.xcodeproj; path = ../../Zapic.xcodeproj; sourceTree = ""; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - 6516242F1F16DA9F00932E26 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 6576C5651F1949760075E226 /* Zapic.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 651624291F16DA9F00932E26 = { - isa = PBXGroup; - children = ( - 6576C55C1F1949240075E226 /* Zapic.xcodeproj */, - 651624341F16DA9F00932E26 /* iOS Example */, - 651624331F16DA9F00932E26 /* Products */, - 6576C56A1F1949DD0075E226 /* Frameworks */, - ); - sourceTree = ""; - }; - 651624331F16DA9F00932E26 /* Products */ = { - isa = PBXGroup; - children = ( - 651624321F16DA9F00932E26 /* iOS Example.app */, - ); - name = Products; - sourceTree = ""; - }; - 651624341F16DA9F00932E26 /* iOS Example */ = { - isa = PBXGroup; - children = ( - 651624351F16DA9F00932E26 /* AppDelegate.swift */, - 651624371F16DA9F00932E26 /* GameScene.sks */, - 651624391F16DA9F00932E26 /* Actions.sks */, - 6516243B1F16DA9F00932E26 /* GameScene.swift */, - 6516243D1F16DA9F00932E26 /* GameViewController.swift */, - 6516243F1F16DA9F00932E26 /* Main.storyboard */, - 651624421F16DA9F00932E26 /* Assets.xcassets */, - 651624441F16DA9F00932E26 /* LaunchScreen.storyboard */, - 651624471F16DA9F00932E26 /* Info.plist */, - ); - path = "iOS Example"; - sourceTree = ""; - }; - 6576C55D1F1949240075E226 /* Products */ = { - isa = PBXGroup; - children = ( - 6576C5621F1949240075E226 /* Zapic.framework */, - 6576C5641F1949240075E226 /* ZapicTests.xctest */, - ); - name = Products; - sourceTree = ""; - }; - 6576C56A1F1949DD0075E226 /* Frameworks */ = { - isa = PBXGroup; - children = ( - ); - name = Frameworks; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXNativeTarget section */ - 651624311F16DA9F00932E26 /* iOS Example */ = { - isa = PBXNativeTarget; - buildConfigurationList = 6516244A1F16DA9F00932E26 /* Build configuration list for PBXNativeTarget "iOS Example" */; - buildPhases = ( - 6516242E1F16DA9F00932E26 /* Sources */, - 6516242F1F16DA9F00932E26 /* Frameworks */, - 651624301F16DA9F00932E26 /* Resources */, - 6576C5691F1949760075E226 /* Embed Frameworks */, - ); - buildRules = ( - ); - dependencies = ( - 6576C5681F1949760075E226 /* PBXTargetDependency */, - ); - name = "iOS Example"; - productName = "iOS Example"; - productReference = 651624321F16DA9F00932E26 /* iOS Example.app */; - productType = "com.apple.product-type.application"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - 6516242A1F16DA9F00932E26 /* Project object */ = { - isa = PBXProject; - attributes = { - LastSwiftUpdateCheck = 0830; - LastUpgradeCheck = 0900; - ORGANIZATIONNAME = zapic; - TargetAttributes = { - 651624311F16DA9F00932E26 = { - CreatedOnToolsVersion = 8.3.3; - DevelopmentTeam = 24QXGCES3E; - LastSwiftMigration = 0920; - ProvisioningStyle = Automatic; - }; - }; - }; - buildConfigurationList = 6516242D1F16DA9F00932E26 /* Build configuration list for PBXProject "iOS Example" */; - compatibilityVersion = "Xcode 3.2"; - developmentRegion = English; - hasScannedForEncodings = 0; - knownRegions = ( - en, - Base, - ); - mainGroup = 651624291F16DA9F00932E26; - productRefGroup = 651624331F16DA9F00932E26 /* Products */; - projectDirPath = ""; - projectReferences = ( - { - ProductGroup = 6576C55D1F1949240075E226 /* Products */; - ProjectRef = 6576C55C1F1949240075E226 /* Zapic.xcodeproj */; - }, - ); - projectRoot = ""; - targets = ( - 651624311F16DA9F00932E26 /* iOS Example */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXReferenceProxy section */ - 6576C5621F1949240075E226 /* Zapic.framework */ = { - isa = PBXReferenceProxy; - fileType = wrapper.framework; - path = Zapic.framework; - remoteRef = 6576C5611F1949240075E226 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 6576C5641F1949240075E226 /* ZapicTests.xctest */ = { - isa = PBXReferenceProxy; - fileType = wrapper.cfbundle; - path = ZapicTests.xctest; - remoteRef = 6576C5631F1949240075E226 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; -/* End PBXReferenceProxy section */ - -/* Begin PBXResourcesBuildPhase section */ - 651624301F16DA9F00932E26 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 651624411F16DA9F00932E26 /* Main.storyboard in Resources */, - 651624381F16DA9F00932E26 /* GameScene.sks in Resources */, - 651624431F16DA9F00932E26 /* Assets.xcassets in Resources */, - 651624461F16DA9F00932E26 /* LaunchScreen.storyboard in Resources */, - 6516243A1F16DA9F00932E26 /* Actions.sks in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - 6516242E1F16DA9F00932E26 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 6516243C1F16DA9F00932E26 /* GameScene.swift in Sources */, - 6516243E1F16DA9F00932E26 /* GameViewController.swift in Sources */, - 651624361F16DA9F00932E26 /* AppDelegate.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin PBXTargetDependency section */ - 6576C5681F1949760075E226 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - name = Zapic; - targetProxy = 6576C5671F1949760075E226 /* PBXContainerItemProxy */; - }; -/* End PBXTargetDependency section */ - -/* Begin PBXVariantGroup section */ - 6516243F1F16DA9F00932E26 /* Main.storyboard */ = { - isa = PBXVariantGroup; - children = ( - 651624401F16DA9F00932E26 /* Base */, - ); - name = Main.storyboard; - sourceTree = ""; - }; - 651624441F16DA9F00932E26 /* LaunchScreen.storyboard */ = { - isa = PBXVariantGroup; - children = ( - 651624451F16DA9F00932E26 /* Base */, - ); - name = LaunchScreen.storyboard; - sourceTree = ""; - }; -/* End PBXVariantGroup section */ - -/* Begin XCBuildConfiguration section */ - 651624481F16DA9F00932E26 /* 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++0x"; - 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_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_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_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = dwarf; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_TESTABILITY = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_DYNAMIC_NO_PIC = NO; - GCC_NO_COMMON_BLOCKS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 10.3; - MTL_ENABLE_DEBUG_INFO = YES; - ONLY_ACTIVE_ARCH = YES; - SDKROOT = iphoneos; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Debug; - }; - 651624491F16DA9F00932E26 /* 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++0x"; - 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_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_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_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 10.3; - MTL_ENABLE_DEBUG_INFO = NO; - SDKROOT = iphoneos; - SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; - TARGETED_DEVICE_FAMILY = "1,2"; - VALIDATE_PRODUCT = YES; - }; - name = Release; - }; - 6516244B1F16DA9F00932E26 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - DEVELOPMENT_TEAM = 24QXGCES3E; - FRAMEWORK_SEARCH_PATHS = "$(PROJECT_DIR)/../../Carthage/Build/iOS"; - INFOPLIST_FILE = "iOS Example/Info.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 9.1; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = "com.zapic.iOS-Example"; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_SWIFT3_OBJC_INFERENCE = Default; - SWIFT_VERSION = 4.0; - }; - name = Debug; - }; - 6516244C1F16DA9F00932E26 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - DEVELOPMENT_TEAM = 24QXGCES3E; - FRAMEWORK_SEARCH_PATHS = "$(PROJECT_DIR)/../../Carthage/Build/iOS"; - INFOPLIST_FILE = "iOS Example/Info.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 9.1; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = "com.zapic.iOS-Example"; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_SWIFT3_OBJC_INFERENCE = Default; - SWIFT_VERSION = 4.0; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 6516242D1F16DA9F00932E26 /* Build configuration list for PBXProject "iOS Example" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 651624481F16DA9F00932E26 /* Debug */, - 651624491F16DA9F00932E26 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 6516244A1F16DA9F00932E26 /* Build configuration list for PBXNativeTarget "iOS Example" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 6516244B1F16DA9F00932E26 /* Debug */, - 6516244C1F16DA9F00932E26 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = 6516242A1F16DA9F00932E26 /* Project object */; -} diff --git a/Examples/iOS Example/iOS Example/Actions.sks b/Examples/iOS Example/iOS Example/Actions.sks deleted file mode 100644 index 0530520..0000000 Binary files a/Examples/iOS Example/iOS Example/Actions.sks and /dev/null differ diff --git a/Examples/iOS Example/iOS Example/Assets.xcassets/116713-OP8CZG-703.imageset/116713-OP8CZG-703.jpg b/Examples/iOS Example/iOS Example/Assets.xcassets/116713-OP8CZG-703.imageset/116713-OP8CZG-703.jpg deleted file mode 100644 index 8de6cab..0000000 Binary files a/Examples/iOS Example/iOS Example/Assets.xcassets/116713-OP8CZG-703.imageset/116713-OP8CZG-703.jpg and /dev/null differ diff --git a/Examples/iOS Example/iOS Example/Assets.xcassets/116713-OP8CZG-703.imageset/Contents.json b/Examples/iOS Example/iOS Example/Assets.xcassets/116713-OP8CZG-703.imageset/Contents.json deleted file mode 100644 index 5872bed..0000000 --- a/Examples/iOS Example/iOS Example/Assets.xcassets/116713-OP8CZG-703.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "filename" : "116713-OP8CZG-703.jpg", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Examples/iOS Example/iOS Example/Assets.xcassets/Icon400x400.imageset/Contents.json b/Examples/iOS Example/iOS Example/Assets.xcassets/Icon400x400.imageset/Contents.json deleted file mode 100644 index ee5874e..0000000 --- a/Examples/iOS Example/iOS Example/Assets.xcassets/Icon400x400.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "filename" : "Icon400x400.png", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Examples/iOS Example/iOS Example/Assets.xcassets/Icon400x400.imageset/Icon400x400.png b/Examples/iOS Example/iOS Example/Assets.xcassets/Icon400x400.imageset/Icon400x400.png deleted file mode 100644 index eceda8e..0000000 Binary files a/Examples/iOS Example/iOS Example/Assets.xcassets/Icon400x400.imageset/Icon400x400.png and /dev/null differ diff --git a/Examples/iOS Example/iOS Example/Assets.xcassets/Spaceship.imageset/Contents.json b/Examples/iOS Example/iOS Example/Assets.xcassets/Spaceship.imageset/Contents.json deleted file mode 100644 index 6799334..0000000 --- a/Examples/iOS Example/iOS Example/Assets.xcassets/Spaceship.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "scale" : "1x", - "filename" : "Spaceship.png" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Examples/iOS Example/iOS Example/Assets.xcassets/Spaceship.imageset/Spaceship.png b/Examples/iOS Example/iOS Example/Assets.xcassets/Spaceship.imageset/Spaceship.png deleted file mode 100644 index e2ebb08..0000000 Binary files a/Examples/iOS Example/iOS Example/Assets.xcassets/Spaceship.imageset/Spaceship.png and /dev/null differ diff --git a/Examples/iOS Example/iOS Example/Assets.xcassets/blue_button02.imageset/Contents.json b/Examples/iOS Example/iOS Example/Assets.xcassets/blue_button02.imageset/Contents.json deleted file mode 100644 index d51925e..0000000 --- a/Examples/iOS Example/iOS Example/Assets.xcassets/blue_button02.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "filename" : "blue_button02.png", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Examples/iOS Example/iOS Example/Assets.xcassets/blue_button02.imageset/blue_button02.png b/Examples/iOS Example/iOS Example/Assets.xcassets/blue_button02.imageset/blue_button02.png deleted file mode 100644 index 4cd391f..0000000 Binary files a/Examples/iOS Example/iOS Example/Assets.xcassets/blue_button02.imageset/blue_button02.png and /dev/null differ diff --git a/Examples/iOS Example/iOS Example/Base.lproj/Main.storyboard b/Examples/iOS Example/iOS Example/Base.lproj/Main.storyboard deleted file mode 100644 index dc4995f..0000000 --- a/Examples/iOS Example/iOS Example/Base.lproj/Main.storyboard +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Examples/iOS Example/iOS Example/GameScene.sks b/Examples/iOS Example/iOS Example/GameScene.sks deleted file mode 100644 index 8bdee47..0000000 Binary files a/Examples/iOS Example/iOS Example/GameScene.sks and /dev/null differ diff --git a/Examples/iOS Example/iOS Example/GameScene.swift b/Examples/iOS Example/iOS Example/GameScene.swift deleted file mode 100644 index 97292d0..0000000 --- a/Examples/iOS Example/iOS Example/GameScene.swift +++ /dev/null @@ -1,60 +0,0 @@ -// -// GameScene.swift -// iOS Example -// -// Created by Daniel Sarfati on 7/5/17. -// Copyright © 2017 Zapic. All rights reserved. -// - -import SpriteKit -import GameplayKit -import Zapic - -class GameScene: SKScene { - - private var label : SKLabelNode? - private var spinnyNode : SKShapeNode? - private var button: SKNode? - - override func didMove(to view: SKView) { - - // Get label node from scene and store it for use later - self.label = self.childNode(withName: "//helloLabel") as? SKLabelNode - if let label = self.label { - label.alpha = 0.0 - label.run(SKAction.fadeIn(withDuration: 2.0)) - } - - - // Create shape node to use during mouse interaction - let w = (self.size.width + self.size.height) * 0.05 - self.spinnyNode = SKShapeNode.init(rectOf: CGSize.init(width: w, height: w), cornerRadius: w * 0.3) - - if let spinnyNode = self.spinnyNode { - spinnyNode.lineWidth = 2.5 - - spinnyNode.run(SKAction.repeatForever(SKAction.rotate(byAngle: CGFloat(Double.pi), duration: 1))) - spinnyNode.run(SKAction.sequence([SKAction.wait(forDuration: 0.5), - SKAction.fadeOut(withDuration: 0.5), - SKAction.removeFromParent()])) - } - - self.button = self.childNode(withName: "//zapicButton") - } - - override func touchesEnded(_ touches: Set, with event: UIEvent?) { - let touch = touches.first - let touchLocation = touch!.location(in: self) - // Check if the location of the touch is within the button's bounds - if button!.contains(touchLocation) { - Zapic.show(view: .main) - } - Zapic.submitEvent(["Event123": 34,"Score":22]) - - guard let idString = Zapic.playerId?.uuidString else { - return - } - //Update the label to show the player's id - self.label?.text = "Hello player \(idString)" - } -} diff --git a/Examples/iOS Example/iOS Example/GameViewController.swift b/Examples/iOS Example/iOS Example/GameViewController.swift deleted file mode 100644 index 3a349e1..0000000 --- a/Examples/iOS Example/iOS Example/GameViewController.swift +++ /dev/null @@ -1,78 +0,0 @@ -// -// GameViewController.swift -// iOS Example -// -// Created by Daniel Sarfati on 7/5/17. -// Copyright © 2017 Zapic. All rights reserved. -// - -import UIKit -import SpriteKit -import GameplayKit - -class GameViewController: UIViewController { - - override func viewDidLoad() { - super.viewDidLoad() - - if let view = self.view as! SKView? { - // Load the SKScene from 'GameScene.sks' - if let scene = SKScene(fileNamed: "GameScene") { - // Set the scale mode to scale to fit the window - scene.scaleMode = .aspectFill - - // Present the scene - view.presentScene(scene) - } - - //view.ignoresSiblingOrder = true - - //view.showsFPS = true - //view.showsNodeCount = true - - } - } - - func showUrlDialog(){ - let ac = UIAlertController(title: "Enter Web Client URL", message: nil, preferredStyle: .alert) - ac.addTextField(configurationHandler: nil) - - ac.addAction(UIAlertAction(title: "OK", style: .default) { [unowned self, ac] _ in - let url = ac.textFields![0] - UserDefaults.standard.set(url.text, forKey: "ZAPIC_URL") - }) - - self.present(ac, animated: true, completion: nil) - } - - override func motionEnded(_ motion: UIEventSubtype, with event: UIEvent?) { - if motion == .motionShake { - print("shake") - showUrlDialog() - } - } - - override func viewDidAppear(_ animated: Bool) { - } - - override var shouldAutorotate: Bool { - return true - } - - override var supportedInterfaceOrientations: UIInterfaceOrientationMask { - if UIDevice.current.userInterfaceIdiom == .phone { - return .allButUpsideDown - } else { - return .all - } - } - - override func didReceiveMemoryWarning() { - super.didReceiveMemoryWarning() - // Release any cached data, images, etc that aren't in use. - } - - override var prefersStatusBarHidden: Bool { - return true - } -} diff --git a/LICENSE b/LICENSE index 1b789ee..b6c984c 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2017 Zapic +Copyright (c) 2017-2018 Zapic, Inc. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/Modules/module.modulemap b/Modules/module.modulemap new file mode 100644 index 0000000..705398a --- /dev/null +++ b/Modules/module.modulemap @@ -0,0 +1,5 @@ +framework module Zapic { + umbrella header "Zapic.h" + export * + module * { export * } +} diff --git a/README.md b/README.md index 97901ad..6e15559 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,39 @@ -# Zapic-SDK-iOS +# Zapic SDK for iOS -[![Build Status](https://travis-ci.org/ZapicInc/Zapic-SDK-iOS.svg?branch=master)](https://travis-ci.org/ZapicInc/Zapic-SDK-iOS) -[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)[![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) +[![Build Status](https://travis-ci.org/ZapicInc/Zapic-SDK-iOS.svg?branch=master)](https://travis-ci.org/ZapicInc/Zapic-SDK-iOS) [![CodeFactor](https://www.codefactor.io/repository/github/zapicinc/zapic-sdk-ios/badge)](https://www.codefactor.io/repository/github/zapicinc/zapic-sdk-ios) [![MIT](https://img.shields.io/badge/license-MIT-yellow.svg)](https://opensource.org/licenses/MIT) [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg)](https://github.com/Carthage/Carthage) ![Discord](https://img.shields.io/discord/430949891104309249.svg) -## Installation +Copyright (c) 2017-2018 Zapic, Inc. -### [Carthage](https://github.com/Carthage/Carthage) +The Zapic SDK for iOS is an open-source project that allows game developers to integrate with the Zapic platform from a game written in Swift or Objective-C for iOS. -Add this to `Cartfile` +_iOS is a trademark of Apple, Inc._ -``` -github "ZapicInc/Zapic-SDK-iOS" -``` +## Getting Started -```bash -$ carthage update -``` +Learn more about integrating the SDK and configuring your iOS game in the Zapic platform at https://www.zapic.com/docs/ios. + +## Community + +Chat on [Discord](https://discord.gg/Kduh53S). + +Follow [@ZapicInc](https://twitter.com/ZapicInc) on Twitter for important announcements. + +Report bugs and discuss new features on [GitHub](https://github.com/ZapicInc/Support). + +## Contributing + +We accept contributions to the Zapic SDK for iOS. Simply fork the repository and submit a pull request on [GitHub](https://github.com/ZapicInc/Zapic-SDK-iOS/pulls). + +## Quick Links + +* [Zapic Documentation](https://docs.zapic.com) +* [Zapic SDK for Unity](https://github.com/ZapicInc/Zapic-SDK-Unity) +* [Zapic SDK for Android](https://github.com/ZapicInc/Zapic-SDK-Android) + +## How to create a release +1. Update version number of Zapic project +2. Update version number in `ZPCInjectedJS.m` +3. Update version number of podspec +4. Check pod `pod lib lint` +5. Create release in Github +6. Publish release to Cocoapods repo `pod trunk push Zapic.podspec` diff --git a/Scripts/format-code.sh b/Scripts/format-code.sh new file mode 100755 index 0000000..98697a4 --- /dev/null +++ b/Scripts/format-code.sh @@ -0,0 +1,6 @@ +# Runs clang-format on all code files +find $SRCROOT/Zapic/**/*.[chm] | xargs clang-format -i -style=file + +# Regex to remove Xcode header comments +# TODO: Add this to the script +# //\n//.+\n//.+\n//\n//.+\n//..Copyright.+$\n//\n\n diff --git a/Zapic.podspec b/Zapic.podspec index 38a5f5c..1445af0 100644 --- a/Zapic.podspec +++ b/Zapic.podspec @@ -1,22 +1,11 @@ Pod::Spec.new do |s| s.name = 'Zapic' - s.version = '0.1.0' - s.license = { :type => 'MIT', :file => 'LICENSE' } - s.homepage = 'https://zapic.com' + s.version = '2.0.1' + s.license = "MIT" + s.homepage = 'https://www.zapic.com' s.summary = 'Client SDK to connect iOS apps to the Zapic platform.' s.authors = { 'Daniel Sarfati' => 'daniel@zapic.com' } - - s.source = { :git => 'https://github.com/ZapicInc/Zapic-SDK-iOS.git', :tag => s.version.to_s } - - s.platform = :ios - s.ios.deployment_target = '9.0' - + s.source = { :git => 'https://github.com/ZapicInc/Zapic-SDK-iOS.git', :tag => "#{s.version}" } + s.platform = :ios, "9.0" s.source_files = "Zapic", "Zapic/**/*.{h,m,swift}" - s.public_header_files = "Zapic/*.h" - s.resource = 'Zapic/ZapicAssets.xcassets' - - s.pod_target_xcconfig = { 'SWIFT_VERSION' => '3' } - s.dependency 'NotificationBannerSwift' - s.dependency 'RxSwift', '~> 3.0' - s.dependency 'RxCocoa', '~> 3.0' end diff --git a/Zapic.xcodeproj/project.pbxproj b/Zapic.xcodeproj/project.pbxproj index 00d73f2..3118af9 100644 --- a/Zapic.xcodeproj/project.pbxproj +++ b/Zapic.xcodeproj/project.pbxproj @@ -3,247 +3,378 @@ archiveVersion = 1; classes = { }; - objectVersion = 46; + objectVersion = 50; objects = { /* Begin PBXBuildFile section */ - 650116061F23141300CED7B2 /* ZapicUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 650116051F23141300CED7B2 /* ZapicUtils.swift */; }; - 651AEDB21F16C49900986F05 /* Zapic.h in Headers */ = {isa = PBXBuildFile; fileRef = 651AEDA41F16C49900986F05 /* Zapic.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 651AEDC71F16C4CE00986F05 /* CInterface.h in Headers */ = {isa = PBXBuildFile; fileRef = 651AEDBC1F16C4CE00986F05 /* CInterface.h */; }; - 651AEDC81F16C4CE00986F05 /* CInterface.mm in Sources */ = {isa = PBXBuildFile; fileRef = 651AEDBD1F16C4CE00986F05 /* CInterface.mm */; }; - 651AEDC91F16C4CE00986F05 /* GameCenterHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 651AEDBE1F16C4CE00986F05 /* GameCenterHelper.swift */; }; - 651AEDCD1F16C4CE00986F05 /* Zapic.swift in Sources */ = {isa = PBXBuildFile; fileRef = 651AEDC21F16C4CE00986F05 /* Zapic.swift */; }; - 651AEDCE1F16C4CE00986F05 /* ZapicAssets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 651AEDC31F16C4CE00986F05 /* ZapicAssets.xcassets */; }; - 651AEDCF1F16C4CE00986F05 /* ZapicColors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 651AEDC41F16C4CE00986F05 /* ZapicColors.swift */; }; - 651AEDD01F16C4CE00986F05 /* ZapicController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 651AEDC51F16C4CE00986F05 /* ZapicController.swift */; }; - 6543B5DF1F96C35500D3767A /* ZapicDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6543B5DE1F96C35500D3767A /* ZapicDelegate.swift */; }; - 6543B5E11F96C43F00D3767A /* ZapicWebClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6543B5E01F96C43F00D3767A /* ZapicWebClient.swift */; }; - 654BF0B41F27F48700E0B9BC /* Zapic.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 651AEDA11F16C49900986F05 /* Zapic.framework */; }; - 654BF0E61F27FA6E00E0B9BC /* Nimble.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 654BF0E41F27FA6E00E0B9BC /* Nimble.framework */; }; - 654BF0E71F27FA6E00E0B9BC /* Quick.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 654BF0E51F27FA6E00E0B9BC /* Quick.framework */; }; - 654BF0E91F27FB9400E0B9BC /* ZapicSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65AC48DA1F1D40CD0026E101 /* ZapicSpec.swift */; }; - 654E3D711F47400700FCD261 /* ZapicCore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 654E3D701F47400700FCD261 /* ZapicCore.swift */; }; - 654E3D731F4743D200FCD261 /* ContactManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 654E3D721F4743D200FCD261 /* ContactManager.swift */; }; - 6551B0DA1FA44B44003CE65F /* StringExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6551B0D91FA44B44003CE65F /* StringExtensions.swift */; }; - 6551B0DC1FA44BD9003CE65F /* StringExtensionsSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6551B0DB1FA44BD9003CE65F /* StringExtensionsSpec.swift */; }; - 6578105D1F8F50D900684DD7 /* ZapicUtilsSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6578105C1F8F50D900684DD7 /* ZapicUtilsSpec.swift */; }; - 65DA7F931F908E6F00C28025 /* Nimble.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 654BF0E41F27FA6E00E0B9BC /* Nimble.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 65DA7F941F908E6F00C28025 /* Quick.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 654BF0E51F27FA6E00E0B9BC /* Quick.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 65E050CB1F326DCB005985F6 /* ZLog.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65E050CA1F326DCB005985F6 /* ZLog.swift */; }; - 65E050D41F331201005985F6 /* LoadingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65E050D01F331201005985F6 /* LoadingView.swift */; }; - 65E050D51F331201005985F6 /* OfflineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65E050D11F331201005985F6 /* OfflineView.swift */; }; - 65E050D61F331201005985F6 /* ZapicView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65E050D21F331201005985F6 /* ZapicView.swift */; }; - 65E050D71F331201005985F6 /* ZapicWebView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65E050D31F331201005985F6 /* ZapicWebView.swift */; }; - 65E050DF1F33150C005985F6 /* Banner.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65E050DD1F33150C005985F6 /* Banner.swift */; }; - 65E050E01F33150C005985F6 /* BannerContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65E050DE1F33150C005985F6 /* BannerContent.swift */; }; - 65E050E21F331553005985F6 /* ColorBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65E050E11F331553005985F6 /* ColorBar.swift */; }; - 65E050E41F336E0E005985F6 /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65E050E31F336E0E005985F6 /* Extensions.swift */; }; - 65E050E61F33BAE4005985F6 /* Queue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65E050E51F33BAE4005985F6 /* Queue.swift */; }; - 65FD63181F97E40C00C5CC8D /* InjectedJS.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65FD63171F97E40C00C5CC8D /* InjectedJS.swift */; }; + 651BF25C212A719900067178 /* ZPCQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = 651BF25A212A719900067178 /* ZPCQueue.h */; }; + 651BF25D212A719900067178 /* ZPCQueue.m in Sources */ = {isa = PBXBuildFile; fileRef = 651BF25B212A719900067178 /* ZPCQueue.m */; }; + 651BF260212B0AE100067178 /* ZPCPlayer.h in Headers */ = {isa = PBXBuildFile; fileRef = 651BF25E212B0AE100067178 /* ZPCPlayer.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 651BF261212B0AE100067178 /* ZPCPlayer.m in Sources */ = {isa = PBXBuildFile; fileRef = 651BF25F212B0AE100067178 /* ZPCPlayer.m */; }; + 651BF29D212B55C100067178 /* ZPCPlayerManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 651BF29B212B55C100067178 /* ZPCPlayerManager.h */; }; + 651BF29E212B55C100067178 /* ZPCPlayerManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 651BF29C212B55C100067178 /* ZPCPlayerManager.m */; }; + 651BF2AC212B6F8D00067178 /* ZPCSafariManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 651BF2AA212B6F8D00067178 /* ZPCSafariManager.h */; }; + 651BF2AD212B6F8D00067178 /* ZPCSafariManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 651BF2AB212B6F8D00067178 /* ZPCSafariManager.m */; }; + 65202DD720F7ED8C00BFF532 /* Zapic.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 65202DCD20F7ED8C00BFF532 /* Zapic.framework */; }; + 65202DDC20F7ED8C00BFF532 /* ZapicTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 65202DDB20F7ED8C00BFF532 /* ZapicTests.m */; }; + 65202DDE20F7ED8C00BFF532 /* Zapic.h in Headers */ = {isa = PBXBuildFile; fileRef = 65202DD020F7ED8C00BFF532 /* Zapic.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 6527F3AB2140724400DB41AA /* ZPCStatistic.h in Headers */ = {isa = PBXBuildFile; fileRef = 6527F3A92140724400DB41AA /* ZPCStatistic.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 6527F3AC2140724400DB41AA /* ZPCStatistic.m in Sources */ = {isa = PBXBuildFile; fileRef = 6527F3AA2140724400DB41AA /* ZPCStatistic.m */; }; + 6527F3AF21408F3100DB41AA /* ZPCChallenge.h in Headers */ = {isa = PBXBuildFile; fileRef = 6527F3AD21408F3100DB41AA /* ZPCChallenge.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 6527F3B021408F3100DB41AA /* ZPCChallenge.m in Sources */ = {isa = PBXBuildFile; fileRef = 6527F3AE21408F3100DB41AA /* ZPCChallenge.m */; }; + 65548F64215AF16F00FF91B9 /* ZPCErrors.h in Headers */ = {isa = PBXBuildFile; fileRef = 65548F62215AF16F00FF91B9 /* ZPCErrors.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 65548F67215AF2C400FF91B9 /* ZPCErrors.m in Sources */ = {isa = PBXBuildFile; fileRef = 65548F66215AF2C400FF91B9 /* ZPCErrors.m */; }; + 655812692139C60A00E855A9 /* ZPCCompetition.h in Headers */ = {isa = PBXBuildFile; fileRef = 655812672139C60A00E855A9 /* ZPCCompetition.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 6558126A2139C60A00E855A9 /* ZPCCompetition.m in Sources */ = {isa = PBXBuildFile; fileRef = 655812682139C60A00E855A9 /* ZPCCompetition.m */; }; + 6558126D2139E36B00E855A9 /* ZPCQueryManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 6558126B2139E36B00E855A9 /* ZPCQueryManager.h */; }; + 6558126E2139E36B00E855A9 /* ZPCQueryManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 6558126C2139E36B00E855A9 /* ZPCQueryManager.m */; }; + 656754642120CB2A009F8917 /* ZPCBanner.h in Headers */ = {isa = PBXBuildFile; fileRef = 656754622120CB2A009F8917 /* ZPCBanner.h */; }; + 656754652120CB2A009F8917 /* ZPCBanner.m in Sources */ = {isa = PBXBuildFile; fileRef = 656754632120CB2A009F8917 /* ZPCBanner.m */; }; + 656754682120DD17009F8917 /* ZPCGradientBar.h in Headers */ = {isa = PBXBuildFile; fileRef = 656754662120DD17009F8917 /* ZPCGradientBar.h */; }; + 656754692120DD17009F8917 /* ZPCGradientBar.m in Sources */ = {isa = PBXBuildFile; fileRef = 656754672120DD17009F8917 /* ZPCGradientBar.m */; }; + 656F3CE920F7F51600AB8B1A /* Zapic.m in Sources */ = {isa = PBXBuildFile; fileRef = 656F3CE820F7F51600AB8B1A /* Zapic.m */; }; + 656F3CEC20F7F7A400AB8B1A /* ZPCLog.h in Headers */ = {isa = PBXBuildFile; fileRef = 656F3CEA20F7F7A400AB8B1A /* ZPCLog.h */; }; + 656F3CED20F7F7A400AB8B1A /* ZPCLog.m in Sources */ = {isa = PBXBuildFile; fileRef = 656F3CEB20F7F7A400AB8B1A /* ZPCLog.m */; }; + 656F3CF220F8099500AB8B1A /* ZPCCore.h in Headers */ = {isa = PBXBuildFile; fileRef = 656F3CF020F8099500AB8B1A /* ZPCCore.h */; }; + 656F3CF320F8099500AB8B1A /* ZPCCore.m in Sources */ = {isa = PBXBuildFile; fileRef = 656F3CF120F8099500AB8B1A /* ZPCCore.m */; }; + 656F3CF620F80D1B00AB8B1A /* ZPCWebApp.h in Headers */ = {isa = PBXBuildFile; fileRef = 656F3CF420F80D1B00AB8B1A /* ZPCWebApp.h */; }; + 656F3CF720F80D1B00AB8B1A /* ZPCWebApp.m in Sources */ = {isa = PBXBuildFile; fileRef = 656F3CF520F80D1B00AB8B1A /* ZPCWebApp.m */; }; + 656F3CFA20F8102500AB8B1A /* ZPCBackgroundView.h in Headers */ = {isa = PBXBuildFile; fileRef = 656F3CF820F8102500AB8B1A /* ZPCBackgroundView.h */; }; + 656F3CFB20F8102500AB8B1A /* ZPCBackgroundView.m in Sources */ = {isa = PBXBuildFile; fileRef = 656F3CF920F8102500AB8B1A /* ZPCBackgroundView.m */; }; + 656F3CFE20F81AC600AB8B1A /* ZPCInjectedJS.h in Headers */ = {isa = PBXBuildFile; fileRef = 656F3CFC20F81AC600AB8B1A /* ZPCInjectedJS.h */; }; + 656F3CFF20F81AC600AB8B1A /* ZPCInjectedJS.m in Sources */ = {isa = PBXBuildFile; fileRef = 656F3CFD20F81AC600AB8B1A /* ZPCInjectedJS.m */; }; + 657132B62136223D00F79C70 /* ZPCSelectorHelpers.h in Headers */ = {isa = PBXBuildFile; fileRef = 657132B42136223D00F79C70 /* ZPCSelectorHelpers.h */; }; + 657132B72136223D00F79C70 /* ZPCSelectorHelpers.m in Sources */ = {isa = PBXBuildFile; fileRef = 657132B52136223D00F79C70 /* ZPCSelectorHelpers.m */; }; + 6576A708212DC4B1000D55B4 /* ZPCLoadingView.h in Headers */ = {isa = PBXBuildFile; fileRef = 6576A706212DC4B1000D55B4 /* ZPCLoadingView.h */; }; + 6576A709212DC4B1000D55B4 /* ZPCLoadingView.m in Sources */ = {isa = PBXBuildFile; fileRef = 6576A707212DC4B1000D55B4 /* ZPCLoadingView.m */; }; + 6576A70C212DC712000D55B4 /* ZPCErrorView.h in Headers */ = {isa = PBXBuildFile; fileRef = 6576A70A212DC712000D55B4 /* ZPCErrorView.h */; }; + 6576A70D212DC712000D55B4 /* ZPCErrorView.m in Sources */ = {isa = PBXBuildFile; fileRef = 6576A70B212DC712000D55B4 /* ZPCErrorView.m */; }; + 6576A710212DCA29000D55B4 /* ZPCBaseView.h in Headers */ = {isa = PBXBuildFile; fileRef = 6576A70E212DCA29000D55B4 /* ZPCBaseView.h */; }; + 6576A711212DCA29000D55B4 /* ZPCBaseView.m in Sources */ = {isa = PBXBuildFile; fileRef = 6576A70F212DCA29000D55B4 /* ZPCBaseView.m */; }; + 6578A660212F052A00A8C4A7 /* ZPCShareManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 6578A65E212F052A00A8C4A7 /* ZPCShareManager.h */; }; + 6578A661212F052A00A8C4A7 /* ZPCShareManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 6578A65F212F052A00A8C4A7 /* ZPCShareManager.m */; }; + 6578A664212F0B9D00A8C4A7 /* ZPCStorage.h in Headers */ = {isa = PBXBuildFile; fileRef = 6578A662212F0B9D00A8C4A7 /* ZPCStorage.h */; }; + 6578A665212F0B9D00A8C4A7 /* ZPCStorage.m in Sources */ = {isa = PBXBuildFile; fileRef = 6578A663212F0B9D00A8C4A7 /* ZPCStorage.m */; }; + 65904514212F018400AB9D0B /* ZPCShareMessage.h in Headers */ = {isa = PBXBuildFile; fileRef = 65904512212F018400AB9D0B /* ZPCShareMessage.h */; }; + 65904515212F018400AB9D0B /* ZPCShareMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = 65904513212F018400AB9D0B /* ZPCShareMessage.m */; }; + 65AB333E211BC02D00D54953 /* ZPCBannerManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 65AB333C211BC02D00D54953 /* ZPCBannerManager.h */; }; + 65AB333F211BC02D00D54953 /* ZPCBannerManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 65AB333D211BC02D00D54953 /* ZPCBannerManager.m */; }; + 65C05A7421272FE000BEB7EB /* ZPCAppStatusMessage.h in Headers */ = {isa = PBXBuildFile; fileRef = 65C05A7221272FE000BEB7EB /* ZPCAppStatusMessage.h */; }; + 65C05A7521272FE000BEB7EB /* ZPCAppStatusMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = 65C05A7321272FE000BEB7EB /* ZPCAppStatusMessage.m */; }; + 65C05A7821274BC400BEB7EB /* ZPCUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = 65C05A7621274BC400BEB7EB /* ZPCUtils.h */; }; + 65C05A7921274BC400BEB7EB /* ZPCUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 65C05A7721274BC400BEB7EB /* ZPCUtils.m */; }; + 65C05A7C2127524100BEB7EB /* ZPCMessageQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = 65C05A7A2127524100BEB7EB /* ZPCMessageQueue.h */; }; + 65C05A7D2127524100BEB7EB /* ZPCMessageQueue.m in Sources */ = {isa = PBXBuildFile; fileRef = 65C05A7B2127524100BEB7EB /* ZPCMessageQueue.m */; }; + 65DBCECF211B448C00E04CB7 /* ZPCScriptMessageHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = 65DBCECD211B448C00E04CB7 /* ZPCScriptMessageHandler.h */; }; + 65DBCED0211B448C00E04CB7 /* ZPCScriptMessageHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 65DBCECE211B448C00E04CB7 /* ZPCScriptMessageHandler.m */; }; + 65DBCED5211B745600E04CB7 /* ZPCBannerMessage.h in Headers */ = {isa = PBXBuildFile; fileRef = 65DBCED3211B745600E04CB7 /* ZPCBannerMessage.h */; }; + 65DBCED6211B745600E04CB7 /* ZPCBannerMessage.m in Sources */ = {isa = PBXBuildFile; fileRef = 65DBCED4211B745600E04CB7 /* ZPCBannerMessage.m */; }; + 65DE749B213685DE0075CF59 /* ZPCNotificationManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 65DE7499213685DE0075CF59 /* ZPCNotificationManager.h */; }; + 65DE749C213685DE0075CF59 /* ZPCNotificationManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 65DE749A213685DE0075CF59 /* ZPCNotificationManager.m */; }; + 65E0921C213064D8005B9F6D /* ZPCImageUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = 65E0921A213064D8005B9F6D /* ZPCImageUtils.h */; }; + 65E0921D213064D8005B9F6D /* ZPCImageUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 65E0921B213064D8005B9F6D /* ZPCImageUtils.m */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ - 654BF0B51F27F48700E0B9BC /* PBXContainerItemProxy */ = { + 65202DD820F7ED8C00BFF532 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; - containerPortal = 651AED981F16C49900986F05 /* Project object */; + containerPortal = 65202DC420F7ED8C00BFF532 /* Project object */; proxyType = 1; - remoteGlobalIDString = 651AEDA01F16C49900986F05; + remoteGlobalIDString = 65202DCC20F7ED8C00BFF532; remoteInfo = Zapic; }; /* End PBXContainerItemProxy section */ -/* Begin PBXCopyFilesBuildPhase section */ - 65DA7F921F908E4D00C28025 /* CopyFiles */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = ""; - dstSubfolderSpec = 10; - files = ( - 65DA7F931F908E6F00C28025 /* Nimble.framework in CopyFiles */, - 65DA7F941F908E6F00C28025 /* Quick.framework in CopyFiles */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXCopyFilesBuildPhase section */ - /* Begin PBXFileReference section */ - 650116051F23141300CED7B2 /* ZapicUtils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ZapicUtils.swift; sourceTree = ""; }; - 651AEDA11F16C49900986F05 /* Zapic.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Zapic.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 651AEDA41F16C49900986F05 /* Zapic.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Zapic.h; sourceTree = ""; }; - 651AEDA51F16C49900986F05 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 651AEDB11F16C49900986F05 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 651AEDBC1F16C4CE00986F05 /* CInterface.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CInterface.h; sourceTree = ""; }; - 651AEDBD1F16C4CE00986F05 /* CInterface.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = CInterface.mm; sourceTree = ""; }; - 651AEDBE1F16C4CE00986F05 /* GameCenterHelper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GameCenterHelper.swift; sourceTree = ""; }; - 651AEDC21F16C4CE00986F05 /* Zapic.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Zapic.swift; sourceTree = ""; }; - 651AEDC31F16C4CE00986F05 /* ZapicAssets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = ZapicAssets.xcassets; sourceTree = ""; }; - 651AEDC41F16C4CE00986F05 /* ZapicColors.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ZapicColors.swift; path = ../ZapicColors.swift; sourceTree = ""; }; - 651AEDC51F16C4CE00986F05 /* ZapicController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ZapicController.swift; sourceTree = ""; }; - 6543B5DE1F96C35500D3767A /* ZapicDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ZapicDelegate.swift; sourceTree = ""; }; - 6543B5E01F96C43F00D3767A /* ZapicWebClient.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ZapicWebClient.swift; sourceTree = ""; }; - 654BF0AF1F27F48700E0B9BC /* ZapicTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ZapicTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - 654BF0E41F27FA6E00E0B9BC /* Nimble.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Nimble.framework; path = Carthage/Build/iOS/Nimble.framework; sourceTree = ""; }; - 654BF0E51F27FA6E00E0B9BC /* Quick.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Quick.framework; path = Carthage/Build/iOS/Quick.framework; sourceTree = ""; }; - 654E3D701F47400700FCD261 /* ZapicCore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ZapicCore.swift; sourceTree = ""; }; - 654E3D721F4743D200FCD261 /* ContactManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContactManager.swift; sourceTree = ""; }; - 6551B0D91FA44B44003CE65F /* StringExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StringExtensions.swift; sourceTree = ""; }; - 6551B0DB1FA44BD9003CE65F /* StringExtensionsSpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StringExtensionsSpec.swift; sourceTree = ""; }; - 6578105C1F8F50D900684DD7 /* ZapicUtilsSpec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ZapicUtilsSpec.swift; sourceTree = ""; }; - 65AC48DA1F1D40CD0026E101 /* ZapicSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ZapicSpec.swift; sourceTree = ""; }; - 65E050CA1F326DCB005985F6 /* ZLog.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ZLog.swift; sourceTree = ""; }; - 65E050D01F331201005985F6 /* LoadingView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoadingView.swift; sourceTree = ""; }; - 65E050D11F331201005985F6 /* OfflineView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OfflineView.swift; sourceTree = ""; }; - 65E050D21F331201005985F6 /* ZapicView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ZapicView.swift; sourceTree = ""; }; - 65E050D31F331201005985F6 /* ZapicWebView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ZapicWebView.swift; sourceTree = ""; }; - 65E050DD1F33150C005985F6 /* Banner.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Banner.swift; path = Zapic/Banner.swift; sourceTree = SOURCE_ROOT; }; - 65E050DE1F33150C005985F6 /* BannerContent.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = BannerContent.swift; path = Zapic/BannerContent.swift; sourceTree = SOURCE_ROOT; }; - 65E050E11F331553005985F6 /* ColorBar.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ColorBar.swift; sourceTree = ""; }; - 65E050E31F336E0E005985F6 /* Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Extensions.swift; sourceTree = ""; }; - 65E050E51F33BAE4005985F6 /* Queue.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Queue.swift; sourceTree = ""; }; - 65FD63171F97E40C00C5CC8D /* InjectedJS.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InjectedJS.swift; sourceTree = ""; }; + 651BF25A212A719900067178 /* ZPCQueue.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ZPCQueue.h; sourceTree = ""; }; + 651BF25B212A719900067178 /* ZPCQueue.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ZPCQueue.m; sourceTree = ""; }; + 651BF25E212B0AE100067178 /* ZPCPlayer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ZPCPlayer.h; sourceTree = ""; }; + 651BF25F212B0AE100067178 /* ZPCPlayer.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ZPCPlayer.m; sourceTree = ""; }; + 651BF29B212B55C100067178 /* ZPCPlayerManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ZPCPlayerManager.h; sourceTree = ""; }; + 651BF29C212B55C100067178 /* ZPCPlayerManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ZPCPlayerManager.m; sourceTree = ""; }; + 651BF2AA212B6F8D00067178 /* ZPCSafariManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ZPCSafariManager.h; sourceTree = ""; }; + 651BF2AB212B6F8D00067178 /* ZPCSafariManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ZPCSafariManager.m; sourceTree = ""; }; + 65202DCD20F7ED8C00BFF532 /* Zapic.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Zapic.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 65202DD020F7ED8C00BFF532 /* Zapic.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Zapic.h; sourceTree = ""; }; + 65202DD120F7ED8C00BFF532 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 65202DD620F7ED8C00BFF532 /* ZapicTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ZapicTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 65202DDB20F7ED8C00BFF532 /* ZapicTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ZapicTests.m; sourceTree = ""; }; + 65202DDD20F7ED8C00BFF532 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 6527F3A92140724400DB41AA /* ZPCStatistic.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ZPCStatistic.h; sourceTree = ""; }; + 6527F3AA2140724400DB41AA /* ZPCStatistic.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ZPCStatistic.m; sourceTree = ""; }; + 6527F3AD21408F3100DB41AA /* ZPCChallenge.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ZPCChallenge.h; sourceTree = ""; }; + 6527F3AE21408F3100DB41AA /* ZPCChallenge.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ZPCChallenge.m; sourceTree = ""; }; + 65548F62215AF16F00FF91B9 /* ZPCErrors.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ZPCErrors.h; sourceTree = ""; }; + 65548F66215AF2C400FF91B9 /* ZPCErrors.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ZPCErrors.m; sourceTree = ""; }; + 655812672139C60A00E855A9 /* ZPCCompetition.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ZPCCompetition.h; sourceTree = ""; }; + 655812682139C60A00E855A9 /* ZPCCompetition.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ZPCCompetition.m; sourceTree = ""; }; + 6558126B2139E36B00E855A9 /* ZPCQueryManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ZPCQueryManager.h; sourceTree = ""; }; + 6558126C2139E36B00E855A9 /* ZPCQueryManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ZPCQueryManager.m; sourceTree = ""; }; + 656754622120CB2A009F8917 /* ZPCBanner.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ZPCBanner.h; sourceTree = ""; }; + 656754632120CB2A009F8917 /* ZPCBanner.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ZPCBanner.m; sourceTree = ""; }; + 656754662120DD17009F8917 /* ZPCGradientBar.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ZPCGradientBar.h; sourceTree = ""; }; + 656754672120DD17009F8917 /* ZPCGradientBar.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ZPCGradientBar.m; sourceTree = ""; }; + 656F3CE820F7F51600AB8B1A /* Zapic.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = Zapic.m; sourceTree = ""; }; + 656F3CEA20F7F7A400AB8B1A /* ZPCLog.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ZPCLog.h; sourceTree = ""; }; + 656F3CEB20F7F7A400AB8B1A /* ZPCLog.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ZPCLog.m; sourceTree = ""; }; + 656F3CF020F8099500AB8B1A /* ZPCCore.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ZPCCore.h; sourceTree = ""; }; + 656F3CF120F8099500AB8B1A /* ZPCCore.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ZPCCore.m; sourceTree = ""; }; + 656F3CF420F80D1B00AB8B1A /* ZPCWebApp.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ZPCWebApp.h; sourceTree = ""; }; + 656F3CF520F80D1B00AB8B1A /* ZPCWebApp.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ZPCWebApp.m; sourceTree = ""; }; + 656F3CF820F8102500AB8B1A /* ZPCBackgroundView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ZPCBackgroundView.h; sourceTree = ""; }; + 656F3CF920F8102500AB8B1A /* ZPCBackgroundView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ZPCBackgroundView.m; sourceTree = ""; }; + 656F3CFC20F81AC600AB8B1A /* ZPCInjectedJS.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ZPCInjectedJS.h; sourceTree = ""; }; + 656F3CFD20F81AC600AB8B1A /* ZPCInjectedJS.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ZPCInjectedJS.m; sourceTree = ""; }; + 657132AF2135A67E00F79C70 /* module.modulemap */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.module-map"; path = module.modulemap; sourceTree = ""; }; + 657132B42136223D00F79C70 /* ZPCSelectorHelpers.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ZPCSelectorHelpers.h; sourceTree = ""; }; + 657132B52136223D00F79C70 /* ZPCSelectorHelpers.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ZPCSelectorHelpers.m; sourceTree = ""; }; + 6576A706212DC4B1000D55B4 /* ZPCLoadingView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ZPCLoadingView.h; sourceTree = ""; }; + 6576A707212DC4B1000D55B4 /* ZPCLoadingView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ZPCLoadingView.m; sourceTree = ""; }; + 6576A70A212DC712000D55B4 /* ZPCErrorView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ZPCErrorView.h; sourceTree = ""; }; + 6576A70B212DC712000D55B4 /* ZPCErrorView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ZPCErrorView.m; sourceTree = ""; }; + 6576A70E212DCA29000D55B4 /* ZPCBaseView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ZPCBaseView.h; sourceTree = ""; }; + 6576A70F212DCA29000D55B4 /* ZPCBaseView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ZPCBaseView.m; sourceTree = ""; }; + 6578A65E212F052A00A8C4A7 /* ZPCShareManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ZPCShareManager.h; sourceTree = ""; }; + 6578A65F212F052A00A8C4A7 /* ZPCShareManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ZPCShareManager.m; sourceTree = ""; }; + 6578A662212F0B9D00A8C4A7 /* ZPCStorage.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ZPCStorage.h; sourceTree = ""; }; + 6578A663212F0B9D00A8C4A7 /* ZPCStorage.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ZPCStorage.m; sourceTree = ""; }; + 65904512212F018400AB9D0B /* ZPCShareMessage.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ZPCShareMessage.h; sourceTree = ""; }; + 65904513212F018400AB9D0B /* ZPCShareMessage.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ZPCShareMessage.m; sourceTree = ""; }; + 65AB333C211BC02D00D54953 /* ZPCBannerManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ZPCBannerManager.h; sourceTree = ""; }; + 65AB333D211BC02D00D54953 /* ZPCBannerManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ZPCBannerManager.m; sourceTree = ""; }; + 65C05A7221272FE000BEB7EB /* ZPCAppStatusMessage.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ZPCAppStatusMessage.h; sourceTree = ""; }; + 65C05A7321272FE000BEB7EB /* ZPCAppStatusMessage.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ZPCAppStatusMessage.m; sourceTree = ""; }; + 65C05A7621274BC400BEB7EB /* ZPCUtils.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ZPCUtils.h; sourceTree = ""; }; + 65C05A7721274BC400BEB7EB /* ZPCUtils.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ZPCUtils.m; sourceTree = ""; }; + 65C05A7A2127524100BEB7EB /* ZPCMessageQueue.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ZPCMessageQueue.h; sourceTree = ""; }; + 65C05A7B2127524100BEB7EB /* ZPCMessageQueue.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ZPCMessageQueue.m; sourceTree = ""; }; + 65DBCECD211B448C00E04CB7 /* ZPCScriptMessageHandler.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ZPCScriptMessageHandler.h; sourceTree = ""; }; + 65DBCECE211B448C00E04CB7 /* ZPCScriptMessageHandler.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ZPCScriptMessageHandler.m; sourceTree = ""; }; + 65DBCED3211B745600E04CB7 /* ZPCBannerMessage.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ZPCBannerMessage.h; sourceTree = ""; }; + 65DBCED4211B745600E04CB7 /* ZPCBannerMessage.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ZPCBannerMessage.m; sourceTree = ""; }; + 65DE7499213685DE0075CF59 /* ZPCNotificationManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ZPCNotificationManager.h; sourceTree = ""; }; + 65DE749A213685DE0075CF59 /* ZPCNotificationManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ZPCNotificationManager.m; sourceTree = ""; }; + 65E0921A213064D8005B9F6D /* ZPCImageUtils.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ZPCImageUtils.h; sourceTree = ""; }; + 65E0921B213064D8005B9F6D /* ZPCImageUtils.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ZPCImageUtils.m; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ - 651AED9D1F16C49900986F05 /* Frameworks */ = { + 65202DC920F7ED8C00BFF532 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; - 654BF0AC1F27F48700E0B9BC /* Frameworks */ = { + 65202DD320F7ED8C00BFF532 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 654BF0E61F27FA6E00E0B9BC /* Nimble.framework in Frameworks */, - 654BF0E71F27FA6E00E0B9BC /* Quick.framework in Frameworks */, - 654BF0B41F27F48700E0B9BC /* Zapic.framework in Frameworks */, + 65202DD720F7ED8C00BFF532 /* Zapic.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ - 651AED971F16C49900986F05 = { + 65202DC320F7ED8C00BFF532 = { isa = PBXGroup; children = ( - 651AEDA31F16C49900986F05 /* Zapic */, - 651AEDAE1F16C49900986F05 /* ZapicTests */, - 651AEDA21F16C49900986F05 /* Products */, - 651AEDD11F16C7AD00986F05 /* Frameworks */, + 657132AE2135A66D00F79C70 /* Modules */, + 65DBCED2211B739400E04CB7 /* Controllers */, + 65DBCED1211B738100E04CB7 /* Models */, + 65202DCE20F7ED8C00BFF532 /* Products */, + 656F3CEE20F7F7EC00AB8B1A /* Utilities */, + 656F3CEF20F8095900AB8B1A /* Views */, + 65202DCF20F7ED8C00BFF532 /* Zapic */, + 65202DDA20F7ED8C00BFF532 /* ZapicTests */, ); sourceTree = ""; }; - 651AEDA21F16C49900986F05 /* Products */ = { + 65202DCE20F7ED8C00BFF532 /* Products */ = { isa = PBXGroup; children = ( - 651AEDA11F16C49900986F05 /* Zapic.framework */, - 654BF0AF1F27F48700E0B9BC /* ZapicTests.xctest */, + 65202DCD20F7ED8C00BFF532 /* Zapic.framework */, + 65202DD620F7ED8C00BFF532 /* ZapicTests.xctest */, ); name = Products; sourceTree = ""; }; - 651AEDA31F16C49900986F05 /* Zapic */ = { + 65202DCF20F7ED8C00BFF532 /* Zapic */ = { isa = PBXGroup; children = ( - 651AEDBC1F16C4CE00986F05 /* CInterface.h */, - 651AEDA41F16C49900986F05 /* Zapic.h */, - 651AEDBD1F16C4CE00986F05 /* CInterface.mm */, - 651AEDA51F16C49900986F05 /* Info.plist */, - 65FD63171F97E40C00C5CC8D /* InjectedJS.swift */, - 654E3D721F4743D200FCD261 /* ContactManager.swift */, - 651AEDC21F16C4CE00986F05 /* Zapic.swift */, - 651AEDC51F16C4CE00986F05 /* ZapicController.swift */, - 654E3D701F47400700FCD261 /* ZapicCore.swift */, - 6543B5DE1F96C35500D3767A /* ZapicDelegate.swift */, - 6543B5E01F96C43F00D3767A /* ZapicWebClient.swift */, - 651AEDC31F16C4CE00986F05 /* ZapicAssets.xcassets */, - 65E050CE1F3310D0005985F6 /* Utilities */, - 651AEDD81F16C7F600986F05 /* Views */, + 65202DD120F7ED8C00BFF532 /* Info.plist */, ); path = Zapic; sourceTree = ""; }; - 651AEDAE1F16C49900986F05 /* ZapicTests */ = { + 65202DDA20F7ED8C00BFF532 /* ZapicTests */ = { isa = PBXGroup; children = ( - 65AC48DA1F1D40CD0026E101 /* ZapicSpec.swift */, - 651AEDB11F16C49900986F05 /* Info.plist */, - 6578105C1F8F50D900684DD7 /* ZapicUtilsSpec.swift */, - 6551B0DB1FA44BD9003CE65F /* StringExtensionsSpec.swift */, + 65202DDB20F7ED8C00BFF532 /* ZapicTests.m */, + 65202DDD20F7ED8C00BFF532 /* Info.plist */, ); path = ZapicTests; sourceTree = ""; }; - 651AEDD11F16C7AD00986F05 /* Frameworks */ = { + 656F3CEE20F7F7EC00AB8B1A /* Utilities */ = { isa = PBXGroup; children = ( - 654BF0E41F27FA6E00E0B9BC /* Nimble.framework */, - 654BF0E51F27FA6E00E0B9BC /* Quick.framework */, + 656F3CEA20F7F7A400AB8B1A /* ZPCLog.h */, + 656F3CEB20F7F7A400AB8B1A /* ZPCLog.m */, + 656F3CFC20F81AC600AB8B1A /* ZPCInjectedJS.h */, + 656F3CFD20F81AC600AB8B1A /* ZPCInjectedJS.m */, + 65C05A7621274BC400BEB7EB /* ZPCUtils.h */, + 65C05A7721274BC400BEB7EB /* ZPCUtils.m */, + 651BF25A212A719900067178 /* ZPCQueue.h */, + 651BF25B212A719900067178 /* ZPCQueue.m */, + 6578A662212F0B9D00A8C4A7 /* ZPCStorage.h */, + 6578A663212F0B9D00A8C4A7 /* ZPCStorage.m */, + 65E0921A213064D8005B9F6D /* ZPCImageUtils.h */, + 65E0921B213064D8005B9F6D /* ZPCImageUtils.m */, + 657132B42136223D00F79C70 /* ZPCSelectorHelpers.h */, + 657132B52136223D00F79C70 /* ZPCSelectorHelpers.m */, ); - name = Frameworks; + name = Utilities; + path = Zapic/Utilities; sourceTree = ""; }; - 651AEDD81F16C7F600986F05 /* Views */ = { + 656F3CEF20F8095900AB8B1A /* Views */ = { isa = PBXGroup; children = ( - 65E050E11F331553005985F6 /* ColorBar.swift */, - 65E050D01F331201005985F6 /* LoadingView.swift */, - 65E050D11F331201005985F6 /* OfflineView.swift */, - 651AEDC41F16C4CE00986F05 /* ZapicColors.swift */, - 65E050D21F331201005985F6 /* ZapicView.swift */, - 65E050D31F331201005985F6 /* ZapicWebView.swift */, - 65E050D81F331454005985F6 /* Banners */, - ); - path = Views; + 656F3CF820F8102500AB8B1A /* ZPCBackgroundView.h */, + 656F3CF920F8102500AB8B1A /* ZPCBackgroundView.m */, + 656754622120CB2A009F8917 /* ZPCBanner.h */, + 656754632120CB2A009F8917 /* ZPCBanner.m */, + 656754662120DD17009F8917 /* ZPCGradientBar.h */, + 656754672120DD17009F8917 /* ZPCGradientBar.m */, + 656F3CF420F80D1B00AB8B1A /* ZPCWebApp.h */, + 656F3CF520F80D1B00AB8B1A /* ZPCWebApp.m */, + 6576A706212DC4B1000D55B4 /* ZPCLoadingView.h */, + 6576A707212DC4B1000D55B4 /* ZPCLoadingView.m */, + 6576A70A212DC712000D55B4 /* ZPCErrorView.h */, + 6576A70B212DC712000D55B4 /* ZPCErrorView.m */, + 6576A70E212DCA29000D55B4 /* ZPCBaseView.h */, + 6576A70F212DCA29000D55B4 /* ZPCBaseView.m */, + ); + name = Views; + path = Zapic/Views; sourceTree = ""; }; - 65E050CE1F3310D0005985F6 /* Utilities */ = { + 657132AE2135A66D00F79C70 /* Modules */ = { isa = PBXGroup; children = ( - 651AEDBE1F16C4CE00986F05 /* GameCenterHelper.swift */, - 650116051F23141300CED7B2 /* ZapicUtils.swift */, - 65E050CA1F326DCB005985F6 /* ZLog.swift */, - 65E050E31F336E0E005985F6 /* Extensions.swift */, - 65E050E51F33BAE4005985F6 /* Queue.swift */, - 6551B0D91FA44B44003CE65F /* StringExtensions.swift */, + 657132AF2135A67E00F79C70 /* module.modulemap */, ); - name = Utilities; + path = Modules; sourceTree = ""; }; - 65E050D81F331454005985F6 /* Banners */ = { + 65DBCED1211B738100E04CB7 /* Models */ = { isa = PBXGroup; children = ( - 65E050DD1F33150C005985F6 /* Banner.swift */, - 65E050DE1F33150C005985F6 /* BannerContent.swift */, + 65DBCED3211B745600E04CB7 /* ZPCBannerMessage.h */, + 65DBCED4211B745600E04CB7 /* ZPCBannerMessage.m */, + 65C05A7221272FE000BEB7EB /* ZPCAppStatusMessage.h */, + 65C05A7321272FE000BEB7EB /* ZPCAppStatusMessage.m */, + 651BF25E212B0AE100067178 /* ZPCPlayer.h */, + 651BF25F212B0AE100067178 /* ZPCPlayer.m */, + 65904512212F018400AB9D0B /* ZPCShareMessage.h */, + 65904513212F018400AB9D0B /* ZPCShareMessage.m */, + 655812672139C60A00E855A9 /* ZPCCompetition.h */, + 655812682139C60A00E855A9 /* ZPCCompetition.m */, + 6527F3A92140724400DB41AA /* ZPCStatistic.h */, + 6527F3AA2140724400DB41AA /* ZPCStatistic.m */, + 6527F3AD21408F3100DB41AA /* ZPCChallenge.h */, + 6527F3AE21408F3100DB41AA /* ZPCChallenge.m */, ); - path = Banners; + name = Models; + path = Zapic/Models; + sourceTree = ""; + }; + 65DBCED2211B739400E04CB7 /* Controllers */ = { + isa = PBXGroup; + children = ( + 65202DD020F7ED8C00BFF532 /* Zapic.h */, + 656F3CE820F7F51600AB8B1A /* Zapic.m */, + 656F3CF020F8099500AB8B1A /* ZPCCore.h */, + 656F3CF120F8099500AB8B1A /* ZPCCore.m */, + 65DBCECD211B448C00E04CB7 /* ZPCScriptMessageHandler.h */, + 65DBCECE211B448C00E04CB7 /* ZPCScriptMessageHandler.m */, + 65AB333C211BC02D00D54953 /* ZPCBannerManager.h */, + 65AB333D211BC02D00D54953 /* ZPCBannerManager.m */, + 65C05A7A2127524100BEB7EB /* ZPCMessageQueue.h */, + 65C05A7B2127524100BEB7EB /* ZPCMessageQueue.m */, + 651BF29B212B55C100067178 /* ZPCPlayerManager.h */, + 651BF29C212B55C100067178 /* ZPCPlayerManager.m */, + 651BF2AA212B6F8D00067178 /* ZPCSafariManager.h */, + 651BF2AB212B6F8D00067178 /* ZPCSafariManager.m */, + 6578A65E212F052A00A8C4A7 /* ZPCShareManager.h */, + 6578A65F212F052A00A8C4A7 /* ZPCShareManager.m */, + 65DE7499213685DE0075CF59 /* ZPCNotificationManager.h */, + 65DE749A213685DE0075CF59 /* ZPCNotificationManager.m */, + 6558126B2139E36B00E855A9 /* ZPCQueryManager.h */, + 6558126C2139E36B00E855A9 /* ZPCQueryManager.m */, + 65548F62215AF16F00FF91B9 /* ZPCErrors.h */, + 65548F66215AF2C400FF91B9 /* ZPCErrors.m */, + ); + name = Controllers; + path = Zapic/Controllers; sourceTree = ""; }; /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ - 651AED9E1F16C49900986F05 /* Headers */ = { + 65202DCA20F7ED8C00BFF532 /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( - 651AEDC71F16C4CE00986F05 /* CInterface.h in Headers */, - 651AEDB21F16C49900986F05 /* Zapic.h in Headers */, + 656F3CF620F80D1B00AB8B1A /* ZPCWebApp.h in Headers */, + 651BF260212B0AE100067178 /* ZPCPlayer.h in Headers */, + 655812692139C60A00E855A9 /* ZPCCompetition.h in Headers */, + 6527F3AB2140724400DB41AA /* ZPCStatistic.h in Headers */, + 6527F3AF21408F3100DB41AA /* ZPCChallenge.h in Headers */, + 65548F64215AF16F00FF91B9 /* ZPCErrors.h in Headers */, + 656F3CEC20F7F7A400AB8B1A /* ZPCLog.h in Headers */, + 656F3CFE20F81AC600AB8B1A /* ZPCInjectedJS.h in Headers */, + 65DBCED5211B745600E04CB7 /* ZPCBannerMessage.h in Headers */, + 65DE749B213685DE0075CF59 /* ZPCNotificationManager.h in Headers */, + 651BF29D212B55C100067178 /* ZPCPlayerManager.h in Headers */, + 65C05A7821274BC400BEB7EB /* ZPCUtils.h in Headers */, + 6578A664212F0B9D00A8C4A7 /* ZPCStorage.h in Headers */, + 651BF2AC212B6F8D00067178 /* ZPCSafariManager.h in Headers */, + 65C05A7C2127524100BEB7EB /* ZPCMessageQueue.h in Headers */, + 65904514212F018400AB9D0B /* ZPCShareMessage.h in Headers */, + 6576A70C212DC712000D55B4 /* ZPCErrorView.h in Headers */, + 6558126D2139E36B00E855A9 /* ZPCQueryManager.h in Headers */, + 65E0921C213064D8005B9F6D /* ZPCImageUtils.h in Headers */, + 6578A660212F052A00A8C4A7 /* ZPCShareManager.h in Headers */, + 656F3CFA20F8102500AB8B1A /* ZPCBackgroundView.h in Headers */, + 65C05A7421272FE000BEB7EB /* ZPCAppStatusMessage.h in Headers */, + 65AB333E211BC02D00D54953 /* ZPCBannerManager.h in Headers */, + 65202DDE20F7ED8C00BFF532 /* Zapic.h in Headers */, + 6576A708212DC4B1000D55B4 /* ZPCLoadingView.h in Headers */, + 6576A710212DCA29000D55B4 /* ZPCBaseView.h in Headers */, + 656F3CF220F8099500AB8B1A /* ZPCCore.h in Headers */, + 657132B62136223D00F79C70 /* ZPCSelectorHelpers.h in Headers */, + 651BF25C212A719900067178 /* ZPCQueue.h in Headers */, + 656754682120DD17009F8917 /* ZPCGradientBar.h in Headers */, + 656754642120CB2A009F8917 /* ZPCBanner.h in Headers */, + 65DBCECF211B448C00E04CB7 /* ZPCScriptMessageHandler.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXHeadersBuildPhase section */ /* Begin PBXNativeTarget section */ - 651AEDA01F16C49900986F05 /* Zapic */ = { + 65202DCC20F7ED8C00BFF532 /* Zapic */ = { isa = PBXNativeTarget; - buildConfigurationList = 651AEDB51F16C49900986F05 /* Build configuration list for PBXNativeTarget "Zapic" */; + buildConfigurationList = 65202DE120F7ED8C00BFF532 /* Build configuration list for PBXNativeTarget "Zapic" */; buildPhases = ( - 651AED9C1F16C49900986F05 /* Sources */, - 651AED9D1F16C49900986F05 /* Frameworks */, - 651AED9E1F16C49900986F05 /* Headers */, - 651AED9F1F16C49900986F05 /* Resources */, - 65E050CC1F327877005985F6 /* ShellScript */, + 65202DC820F7ED8C00BFF532 /* Sources */, + 65202DC920F7ED8C00BFF532 /* Frameworks */, + 65202DCA20F7ED8C00BFF532 /* Headers */, + 65202DCB20F7ED8C00BFF532 /* Resources */, + 6558126F213B9ABB00E855A9 /* Format Code */, ); buildRules = ( ); @@ -251,80 +382,72 @@ ); name = Zapic; productName = Zapic; - productReference = 651AEDA11F16C49900986F05 /* Zapic.framework */; + productReference = 65202DCD20F7ED8C00BFF532 /* Zapic.framework */; productType = "com.apple.product-type.framework"; }; - 654BF0AE1F27F48700E0B9BC /* ZapicTests */ = { + 65202DD520F7ED8C00BFF532 /* ZapicTests */ = { isa = PBXNativeTarget; - buildConfigurationList = 654BF0B71F27F48700E0B9BC /* Build configuration list for PBXNativeTarget "ZapicTests" */; + buildConfigurationList = 65202DE420F7ED8C00BFF532 /* Build configuration list for PBXNativeTarget "ZapicTests" */; buildPhases = ( - 654BF0AB1F27F48700E0B9BC /* Sources */, - 654BF0AC1F27F48700E0B9BC /* Frameworks */, - 654BF0AD1F27F48700E0B9BC /* Resources */, - 65DA7F921F908E4D00C28025 /* CopyFiles */, + 6D4F17675DBABE55161D75CE /* [CP] Check Pods Manifest.lock */, + 65202DD220F7ED8C00BFF532 /* Sources */, + 65202DD320F7ED8C00BFF532 /* Frameworks */, + 65202DD420F7ED8C00BFF532 /* Resources */, ); buildRules = ( ); dependencies = ( - 654BF0B61F27F48700E0B9BC /* PBXTargetDependency */, + 65202DD920F7ED8C00BFF532 /* PBXTargetDependency */, ); name = ZapicTests; productName = ZapicTests; - productReference = 654BF0AF1F27F48700E0B9BC /* ZapicTests.xctest */; + productReference = 65202DD620F7ED8C00BFF532 /* ZapicTests.xctest */; productType = "com.apple.product-type.bundle.unit-test"; }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ - 651AED981F16C49900986F05 /* Project object */ = { + 65202DC420F7ED8C00BFF532 /* Project object */ = { isa = PBXProject; attributes = { - LastSwiftUpdateCheck = 0830; - LastUpgradeCheck = 0900; - ORGANIZATIONNAME = zapic; + LastUpgradeCheck = 0940; + ORGANIZATIONNAME = Zapic; TargetAttributes = { - 651AEDA01F16C49900986F05 = { - CreatedOnToolsVersion = 8.3.3; - DevelopmentTeam = 24QXGCES3E; - LastSwiftMigration = 0900; - ProvisioningStyle = Automatic; + 65202DCC20F7ED8C00BFF532 = { + CreatedOnToolsVersion = 9.4.1; }; - 654BF0AE1F27F48700E0B9BC = { - CreatedOnToolsVersion = 8.3.3; - DevelopmentTeam = 24QXGCES3E; - LastSwiftMigration = 0900; - ProvisioningStyle = Automatic; + 65202DD520F7ED8C00BFF532 = { + CreatedOnToolsVersion = 9.4.1; }; }; }; - buildConfigurationList = 651AED9B1F16C49900986F05 /* Build configuration list for PBXProject "Zapic" */; - compatibilityVersion = "Xcode 3.2"; - developmentRegion = English; + buildConfigurationList = 65202DC720F7ED8C00BFF532 /* Build configuration list for PBXProject "Zapic" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( en, ); - mainGroup = 651AED971F16C49900986F05; - productRefGroup = 651AEDA21F16C49900986F05 /* Products */; + mainGroup = 65202DC320F7ED8C00BFF532; + productRefGroup = 65202DCE20F7ED8C00BFF532 /* Products */; projectDirPath = ""; projectRoot = ""; targets = ( - 651AEDA01F16C49900986F05 /* Zapic */, - 654BF0AE1F27F48700E0B9BC /* ZapicTests */, + 65202DCC20F7ED8C00BFF532 /* Zapic */, + 65202DD520F7ED8C00BFF532 /* ZapicTests */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ - 651AED9F1F16C49900986F05 /* Resources */ = { + 65202DCB20F7ED8C00BFF532 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - 651AEDCE1F16C4CE00986F05 /* ZapicAssets.xcassets in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; - 654BF0AD1F27F48700E0B9BC /* Resources */ = { + 65202DD420F7ED8C00BFF532 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( @@ -334,86 +457,115 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ - 65E050CC1F327877005985F6 /* ShellScript */ = { + 6558126F213B9ABB00E855A9 /* Format Code */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Format Code"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "chmod u+x $SRCROOT/Scripts/format-code.sh\nsleep 0.6 ### during save Xcode stops listening for file changes\n$SRCROOT/Scripts/format-code.sh"; + }; + 6D4F17675DBABE55161D75CE /* [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-ZapicTests-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "if which swiftlint >/dev/null; then\nswiftlint autocorrect\nswiftlint\nelse\necho \"warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint\"\nfi"; + 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; }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ - 651AED9C1F16C49900986F05 /* Sources */ = { + 65202DC820F7ED8C00BFF532 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 651AEDC91F16C4CE00986F05 /* GameCenterHelper.swift in Sources */, - 65E050D51F331201005985F6 /* OfflineView.swift in Sources */, - 654E3D711F47400700FCD261 /* ZapicCore.swift in Sources */, - 6543B5E11F96C43F00D3767A /* ZapicWebClient.swift in Sources */, - 65FD63181F97E40C00C5CC8D /* InjectedJS.swift in Sources */, - 65E050DF1F33150C005985F6 /* Banner.swift in Sources */, - 6551B0DA1FA44B44003CE65F /* StringExtensions.swift in Sources */, - 65E050D71F331201005985F6 /* ZapicWebView.swift in Sources */, - 651AEDCD1F16C4CE00986F05 /* Zapic.swift in Sources */, - 65E050D41F331201005985F6 /* LoadingView.swift in Sources */, - 65E050CB1F326DCB005985F6 /* ZLog.swift in Sources */, - 654E3D731F4743D200FCD261 /* ContactManager.swift in Sources */, - 65E050E01F33150C005985F6 /* BannerContent.swift in Sources */, - 650116061F23141300CED7B2 /* ZapicUtils.swift in Sources */, - 651AEDD01F16C4CE00986F05 /* ZapicController.swift in Sources */, - 6543B5DF1F96C35500D3767A /* ZapicDelegate.swift in Sources */, - 65E050E41F336E0E005985F6 /* Extensions.swift in Sources */, - 651AEDCF1F16C4CE00986F05 /* ZapicColors.swift in Sources */, - 65E050D61F331201005985F6 /* ZapicView.swift in Sources */, - 65E050E61F33BAE4005985F6 /* Queue.swift in Sources */, - 651AEDC81F16C4CE00986F05 /* CInterface.mm in Sources */, - 65E050E21F331553005985F6 /* ColorBar.swift in Sources */, + 65DBCED6211B745600E04CB7 /* ZPCBannerMessage.m in Sources */, + 65DE749C213685DE0075CF59 /* ZPCNotificationManager.m in Sources */, + 6527F3B021408F3100DB41AA /* ZPCChallenge.m in Sources */, + 656F3CF320F8099500AB8B1A /* ZPCCore.m in Sources */, + 65E0921D213064D8005B9F6D /* ZPCImageUtils.m in Sources */, + 651BF2AD212B6F8D00067178 /* ZPCSafariManager.m in Sources */, + 65C05A7521272FE000BEB7EB /* ZPCAppStatusMessage.m in Sources */, + 657132B72136223D00F79C70 /* ZPCSelectorHelpers.m in Sources */, + 6578A661212F052A00A8C4A7 /* ZPCShareManager.m in Sources */, + 65C05A7921274BC400BEB7EB /* ZPCUtils.m in Sources */, + 65C05A7D2127524100BEB7EB /* ZPCMessageQueue.m in Sources */, + 65AB333F211BC02D00D54953 /* ZPCBannerManager.m in Sources */, + 651BF25D212A719900067178 /* ZPCQueue.m in Sources */, + 6558126E2139E36B00E855A9 /* ZPCQueryManager.m in Sources */, + 656F3CF720F80D1B00AB8B1A /* ZPCWebApp.m in Sources */, + 6527F3AC2140724400DB41AA /* ZPCStatistic.m in Sources */, + 65548F67215AF2C400FF91B9 /* ZPCErrors.m in Sources */, + 6558126A2139C60A00E855A9 /* ZPCCompetition.m in Sources */, + 656F3CFB20F8102500AB8B1A /* ZPCBackgroundView.m in Sources */, + 6576A709212DC4B1000D55B4 /* ZPCLoadingView.m in Sources */, + 65DBCED0211B448C00E04CB7 /* ZPCScriptMessageHandler.m in Sources */, + 6576A711212DCA29000D55B4 /* ZPCBaseView.m in Sources */, + 656754692120DD17009F8917 /* ZPCGradientBar.m in Sources */, + 651BF261212B0AE100067178 /* ZPCPlayer.m in Sources */, + 6576A70D212DC712000D55B4 /* ZPCErrorView.m in Sources */, + 656F3CE920F7F51600AB8B1A /* Zapic.m in Sources */, + 656F3CED20F7F7A400AB8B1A /* ZPCLog.m in Sources */, + 656754652120CB2A009F8917 /* ZPCBanner.m in Sources */, + 6578A665212F0B9D00A8C4A7 /* ZPCStorage.m in Sources */, + 651BF29E212B55C100067178 /* ZPCPlayerManager.m in Sources */, + 65904515212F018400AB9D0B /* ZPCShareMessage.m in Sources */, + 656F3CFF20F81AC600AB8B1A /* ZPCInjectedJS.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; - 654BF0AB1F27F48700E0B9BC /* Sources */ = { + 65202DD220F7ED8C00BFF532 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 6551B0DC1FA44BD9003CE65F /* StringExtensionsSpec.swift in Sources */, - 6578105D1F8F50D900684DD7 /* ZapicUtilsSpec.swift in Sources */, - 654BF0E91F27FB9400E0B9BC /* ZapicSpec.swift in Sources */, + 65202DDC20F7ED8C00BFF532 /* ZapicTests.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ - 654BF0B61F27F48700E0B9BC /* PBXTargetDependency */ = { + 65202DD920F7ED8C00BFF532 /* PBXTargetDependency */ = { isa = PBXTargetDependency; - target = 651AEDA01F16C49900986F05 /* Zapic */; - targetProxy = 654BF0B51F27F48700E0B9BC /* PBXContainerItemProxy */; + target = 65202DCC20F7ED8C00BFF532 /* Zapic */; + targetProxy = 65202DD820F7ED8C00BFF532 /* PBXContainerItemProxy */; }; /* End PBXTargetDependency section */ /* Begin XCBuildConfiguration section */ - 651AEDB31F16C49900986F05 /* Debug */ = { + 65202DDF20F7ED8C00BFF532 /* 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++0x"; + 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; @@ -421,20 +573,22 @@ 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[sdk=iphoneos*]" = "iPhone Developer"; + CODE_SIGN_IDENTITY = "iPhone Developer"; COPY_PHASE_STRIP = NO; CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_C_LANGUAGE_STANDARD = gnu11; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; GCC_OPTIMIZATION_LEVEL = 0; @@ -448,32 +602,31 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 10.3; + IPHONEOS_DEPLOYMENT_TARGET = 11.4; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; name = Debug; }; - 651AEDB41F16C49900986F05 /* Release */ = { + 65202DE020F7ED8C00BFF532 /* 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++0x"; + 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; @@ -481,20 +634,22 @@ 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[sdk=iphoneos*]" = "iPhone Developer"; + CODE_SIGN_IDENTITY = "iPhone Developer"; COPY_PHASE_STRIP = NO; CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; + 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; @@ -502,136 +657,130 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 10.3; + IPHONEOS_DEPLOYMENT_TARGET = 11.4; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; - SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; - TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; name = Release; }; - 651AEDB61F16C49900986F05 /* Debug */ = { + 65202DE220F7ED8C00BFF532 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = ""; + CODE_SIGN_STYLE = Automatic; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = 24QXGCES3E; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Carthage/Build/iOS", - ); INFOPLIST_FILE = Zapic/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 9.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - ONLY_ACTIVE_ARCH = NO; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); PRODUCT_BUNDLE_IDENTIFIER = com.zapic.Zapic; - PRODUCT_NAME = "$(TARGET_NAME)"; - SKIP_INSTALL = NO; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_SWIFT3_OBJC_INFERENCE = Default; - SWIFT_VERSION = 4.0; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; }; - 651AEDB71F16C49900986F05 /* Release */ = { + 65202DE320F7ED8C00BFF532 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = ""; + CODE_SIGN_STYLE = Automatic; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = 24QXGCES3E; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PROJECT_DIR)/Carthage/Build/iOS", - ); INFOPLIST_FILE = Zapic/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 9.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); PRODUCT_BUNDLE_IDENTIFIER = com.zapic.Zapic; - PRODUCT_NAME = "$(TARGET_NAME)"; - SKIP_INSTALL = NO; - SWIFT_SWIFT3_OBJC_INFERENCE = Default; - SWIFT_VERSION = 4.0; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; }; name = Release; }; - 654BF0B81F27F48700E0B9BC /* Debug */ = { + 65202DE520F7ED8C00BFF532 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = 24QXGCES3E; - FRAMEWORK_SEARCH_PATHS = ( - "$(SRCROOT)/Carthage/Build/iOS", - "$(PROJECT_DIR)/Carthage/Build/iOS", - ); INFOPLIST_FILE = ZapicTests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); PRODUCT_BUNDLE_IDENTIFIER = com.zapic.ZapicTests; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_SWIFT3_OBJC_INFERENCE = Default; - SWIFT_VERSION = 4.0; + TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; }; - 654BF0B91F27F48700E0B9BC /* Release */ = { + 65202DE620F7ED8C00BFF532 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = 24QXGCES3E; - FRAMEWORK_SEARCH_PATHS = ( - "$(SRCROOT)/Carthage/Build/iOS", - "$(PROJECT_DIR)/Carthage/Build/iOS", - ); INFOPLIST_FILE = ZapicTests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); PRODUCT_BUNDLE_IDENTIFIER = com.zapic.ZapicTests; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_SWIFT3_OBJC_INFERENCE = Default; - SWIFT_VERSION = 4.0; + TARGETED_DEVICE_FAMILY = "1,2"; }; name = Release; }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ - 651AED9B1F16C49900986F05 /* Build configuration list for PBXProject "Zapic" */ = { + 65202DC720F7ED8C00BFF532 /* Build configuration list for PBXProject "Zapic" */ = { isa = XCConfigurationList; buildConfigurations = ( - 651AEDB31F16C49900986F05 /* Debug */, - 651AEDB41F16C49900986F05 /* Release */, + 65202DDF20F7ED8C00BFF532 /* Debug */, + 65202DE020F7ED8C00BFF532 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - 651AEDB51F16C49900986F05 /* Build configuration list for PBXNativeTarget "Zapic" */ = { + 65202DE120F7ED8C00BFF532 /* Build configuration list for PBXNativeTarget "Zapic" */ = { isa = XCConfigurationList; buildConfigurations = ( - 651AEDB61F16C49900986F05 /* Debug */, - 651AEDB71F16C49900986F05 /* Release */, + 65202DE220F7ED8C00BFF532 /* Debug */, + 65202DE320F7ED8C00BFF532 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - 654BF0B71F27F48700E0B9BC /* Build configuration list for PBXNativeTarget "ZapicTests" */ = { + 65202DE420F7ED8C00BFF532 /* Build configuration list for PBXNativeTarget "ZapicTests" */ = { isa = XCConfigurationList; buildConfigurations = ( - 654BF0B81F27F48700E0B9BC /* Debug */, - 654BF0B91F27F48700E0B9BC /* Release */, + 65202DE520F7ED8C00BFF532 /* Debug */, + 65202DE620F7ED8C00BFF532 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; /* End XCConfigurationList section */ }; - rootObject = 651AED981F16C49900986F05 /* Project object */; + rootObject = 65202DC420F7ED8C00BFF532 /* Project object */; } diff --git a/Zapic.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/Zapic.xcodeproj/project.xcworkspace/contents.xcworkspacedata index 800bfaa..919434a 100644 --- a/Zapic.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ b/Zapic.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -2,6 +2,6 @@ + location = "self:"> diff --git a/Zapic.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Zapic.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/Zapic.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/Zapic.xcodeproj/xcshareddata/xcschemes/Zapic.xcscheme b/Zapic.xcodeproj/xcshareddata/xcschemes/Zapic.xcscheme deleted file mode 100644 index c5e9134..0000000 --- a/Zapic.xcodeproj/xcshareddata/xcschemes/Zapic.xcscheme +++ /dev/null @@ -1,120 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Zapic.xcworkspace/contents.xcworkspacedata b/Zapic.xcworkspace/contents.xcworkspacedata index 200ef6e..28dc353 100644 --- a/Zapic.xcworkspace/contents.xcworkspacedata +++ b/Zapic.xcworkspace/contents.xcworkspacedata @@ -2,7 +2,10 @@ + location = "group:ZapicExample-Swift/ZapicExample-Swift.xcodeproj"> + + diff --git a/Zapic.xcworkspace/xcshareddata/IDETemplateMacros.plist b/Zapic.xcworkspace/xcshareddata/IDETemplateMacros.plist new file mode 100644 index 0000000..ac19526 --- /dev/null +++ b/Zapic.xcworkspace/xcshareddata/IDETemplateMacros.plist @@ -0,0 +1,8 @@ + + + + + FILEHEADER + + + diff --git a/Zapic.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Zapic.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/Zapic.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/Zapic/Banner.swift b/Zapic/Banner.swift deleted file mode 100644 index 9e17df8..0000000 --- a/Zapic/Banner.swift +++ /dev/null @@ -1,230 +0,0 @@ -// -// Banner.swift -// Zapic -// -// Created by Daniel Sarfati on 8/2/17. -// Copyright © 2017 zapic. All rights reserved. -// -// -// Banner.swift -// -// Created by Harlan Haskins on 7/27/15. -// Copyright (c) 2015 Bryx. All rights reserved. -// - -import UIKit - -private enum BannerState { - case showing, hidden, gone -} - -/// A level of 'springiness' for Banners. -/// -/// - None: The banner will slide in and not bounce. -/// - Slight: The banner will bounce a little. -/// - Heavy: The banner will bounce a lot. -enum BannerSpringiness { - case none, slight, heavy - fileprivate var springValues: (damping: CGFloat, velocity: CGFloat) { - switch self { - case .none: return (damping: 1.0, velocity: 1.0) - case .slight: return (damping: 0.7, velocity: 1.5) - case .heavy: return (damping: 0.6, velocity: 2.0) - } - } -} - -/// Banner is a dropdown notification view that presents above the main view controller, but below the status bar. -class Banner: UIView { - private func topWindow() -> UIView? { - let root = UIApplication.shared.delegate?.window??.rootViewController - - if let presented = root?.presentedViewController { - return presented.view - } - - return root?.view -// return UIApplication.shared.delegate?.window??.rootViewController?.presentedViewController.view - } - - /// How long the slide down animation should last. - var animationDuration: TimeInterval = 0.4 - - /// How 'springy' the banner should display. Defaults to `.Slight` - var springiness = BannerSpringiness.slight - - /// The height of the banner. - var bannerHeight: CGFloat = 66 - - /// Whether or not the banner should show a shadow when presented. - var hasShadows = true { - didSet { - resetShadows() - } - } - - /// A block to call when the uer taps on the banner. - var didTapBlock: (() -> Void)? - - let bannerContent: UIView - - /// A block to call after the banner has finished dismissing and is off screen. - var didDismissBlock: (() -> Void)? - - /// Whether or not the banner should dismiss itself when the user taps. Defaults to `true`. - var dismissesOnTap = true - - /// Whether or not the banner should dismiss itself when the user swipes up. Defaults to `true`. - var dismissesOnSwipe = true - - private var bannerState = BannerState.hidden { - didSet { - if bannerState != oldValue { - forceUpdates() - } - } - } - - /// A Banner with the provided `title`, `subtitle`, and optional `image`, ready to be presented with `show()`. - /// - /// - parameter title: The title of the banner. Optional. Defaults to nil. - /// - parameter subtitle: The subtitle of the banner. Optional. Defaults to nil. - /// - parameter image: The image on the left of the banner. Optional. Defaults to nil. - /// - parameter backgroundColor: The color of the banner's background view. Defaults to `UIColor.blackColor()`. - /// - parameter didTapBlock: An action to be called when the user taps on the banner. Optional. Defaults to `nil`. - required init(title: String, subtitle: String? = nil, icon: UIImage? = nil, didTapBlock: (() -> Void)? = nil) { - self.didTapBlock = didTapBlock - - if let subText = subtitle { - bannerContent = NotificationBannerView(title: title, text: subText, icon: icon) - } else { - bannerContent = MessageBannerView(title, icon: icon) - } - - super.init(frame: CGRect.zero) - - resetShadows() - addGestureRecognizers() - initializeSubviews() - } - - private func forceUpdates() { - guard let superview = superview, let showingConstraint = showingConstraint, let hiddenConstraint = hiddenConstraint else { return } - switch bannerState { - case .hidden: - superview.removeConstraint(showingConstraint) - superview.addConstraint(hiddenConstraint) - case .showing: - superview.removeConstraint(hiddenConstraint) - superview.addConstraint(showingConstraint) - case .gone: - superview.removeConstraint(hiddenConstraint) - superview.removeConstraint(showingConstraint) - superview.removeConstraints(commonConstraints) - } - setNeedsLayout() - setNeedsUpdateConstraints() - // Managing different -layoutIfNeeded behaviours among iOS versions (for more, read the UIKit iOS 10 release notes) - if #available(iOS 10.0, *) { - superview.layoutIfNeeded() - } else { - layoutIfNeeded() - } - updateConstraintsIfNeeded() - } - - @objc internal func didTap(_ recognizer: UITapGestureRecognizer) { - if dismissesOnTap { - dismiss() - } - didTapBlock?() - } - - @objc internal func didSwipe(_ recognizer: UISwipeGestureRecognizer) { - if dismissesOnSwipe { - dismiss() - } - } - - private func addGestureRecognizers() { - addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(Banner.didTap(_:)))) - let swipe = UISwipeGestureRecognizer(target: self, action: #selector(Banner.didSwipe(_:))) - swipe.direction = .up - addGestureRecognizer(swipe) - } - - private func resetShadows() { - layer.shadowColor = UIColor.black.cgColor - layer.shadowOpacity = self.hasShadows ? 0.5 : 0.0 - layer.shadowOffset = CGSize(width: 0, height: 0) - layer.shadowRadius = 4 - } - - private var contentTopOffsetConstraint: NSLayoutConstraint! - - private func initializeSubviews() { - - self.translatesAutoresizingMaskIntoConstraints = false - self.heightAnchor.constraint(equalToConstant: bannerHeight).isActive = true - - addSubview(bannerContent) - - bannerContent.topAnchor.constraint(equalTo: self.topAnchor).isActive = true - bannerContent.bottomAnchor.constraint(equalTo: self.bottomAnchor).isActive = true - bannerContent.centerXAnchor.constraint(equalTo: self.centerXAnchor).isActive = true - } - - required init?(coder aDecoder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - private var showingConstraint: NSLayoutConstraint? - private var hiddenConstraint: NSLayoutConstraint? - private var commonConstraints = [NSLayoutConstraint]() - - override func didMoveToSuperview() { - super.didMoveToSuperview() - guard let superview = superview, bannerState != .gone else { return } - self.centerXAnchor.constraint(equalTo: superview.centerXAnchor).isActive = true - showingConstraint = self.topAnchor.constraint(equalTo: superview.topAnchor) - let yOffset: CGFloat = -7.0 // Offset the bottom constraint to make room for the shadow to animate off screen. - hiddenConstraint = self.bottomAnchor.constraint(equalTo: superview.topAnchor, constant: yOffset) - } - - override func layoutSubviews() { - super.layoutSubviews() - layoutIfNeeded() - } - - /// Shows the banner. If a view is specified, the banner will be displayed at the top of that view, otherwise at top of the top window. If a `duration` is specified, the banner dismisses itself automatically after that duration elapses. - /// - parameter view: A view the banner will be shown in. Optional. Defaults to 'nil', which in turn means it will be shown in the top window. duration A time interval, after which the banner will dismiss itself. Optional. Defaults to `nil`. - func show(duration: TimeInterval? = nil) { - guard let view = topWindow() else { - ZLog.error("Could not find view. Aborting.") - return - } - view.addSubview(self) - forceUpdates() - let (damping, velocity) = self.springiness.springValues - UIView.animate(withDuration: animationDuration, delay: 0.0, usingSpringWithDamping: damping, initialSpringVelocity: velocity, options: .allowUserInteraction, animations: { - self.bannerState = .showing - }, completion: { _ in - guard let duration = duration else { return } - DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + DispatchTimeInterval.milliseconds(Int(1000.0 * duration))) { - self.dismiss() - } - }) - } - - /// Dismisses the banner. - func dismiss() { - let (damping, velocity) = self.springiness.springValues - UIView.animate(withDuration: animationDuration, delay: 0.0, usingSpringWithDamping: damping, initialSpringVelocity: velocity, options: .allowUserInteraction, animations: { - self.bannerState = .hidden - }, completion: { _ in - self.bannerState = .gone - self.removeFromSuperview() - self.didDismissBlock?() - }) - } -} diff --git a/Zapic/BannerContent.swift b/Zapic/BannerContent.swift deleted file mode 100644 index f27162a..0000000 --- a/Zapic/BannerContent.swift +++ /dev/null @@ -1,139 +0,0 @@ -// -// WelcomeBanner.swift -// Zapic -// -// Created by Daniel Sarfati on 7/7/17. -// Copyright © 2017 Zapic. All rights reserved. -// - -import Foundation -import UIKit - -class ZapicBanner: UIView { - - let content = UIView() - - init(contentRightPadding: Int? = nil, icon: UIImage?) { - - super.init(frame: .zero) - - backgroundColor = UIColor.white - - self.translatesAutoresizingMaskIntoConstraints = false - self.widthAnchor.constraint(equalToConstant: 320).isActive = true - - self.layer.cornerRadius = 5 - - let colorBar = ColorBar() - - //Color bar across the top - self.addSubview(colorBar) - - var img = icon - //Zapic icon - if img == nil { - img = ZapicImages.image(name: "ZapicLogo") - } - - let iconView = UIImageView(image: img) - iconView.contentMode = .scaleAspectFit - iconView.layer.cornerRadius = 5 - iconView.layer.masksToBounds = true - - addSubview(iconView) - - let iconPadding: CGFloat = 8 - iconView.translatesAutoresizingMaskIntoConstraints = false - iconView.topAnchor.constraint(equalTo: colorBar.bottomAnchor, constant: iconPadding ).isActive = true - iconView.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: iconPadding).isActive = true - iconView.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: -iconPadding).isActive = true - iconView.widthAnchor.constraint(equalTo: iconView.heightAnchor).isActive = true - - addSubview(content) - - let contentPadding: CGFloat = 5 - let rightPadding = CGFloat(contentRightPadding ?? 0) - content.translatesAutoresizingMaskIntoConstraints = false - content.topAnchor.constraint(equalTo: colorBar.bottomAnchor, constant: contentPadding).isActive = true - content.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: -contentPadding).isActive = true - content.rightAnchor.constraint(equalTo: self.rightAnchor, constant: -(contentPadding + rightPadding)).isActive = true - content.leftAnchor.constraint(equalTo: iconView.rightAnchor, constant: contentPadding).isActive = true - } - - required init?(coder aDecoder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } -} - -class MessageBannerView: ZapicBanner { - - init(_ text: String, icon: UIImage?) { - - super.init(icon: icon) - - let label = UILabel() - label.font = UIFont.systemFont(ofSize: 16) - label.text = text - label.numberOfLines = 2 - label.textAlignment = .center - - content.addSubview(label) - - label.translatesAutoresizingMaskIntoConstraints = false - label.centerXAnchor.constraint(equalTo: content.centerXAnchor).isActive = true - label.centerYAnchor.constraint(equalTo: content.centerYAnchor).isActive = true - label.widthAnchor.constraint(equalTo: content.widthAnchor).isActive = true - } - - required init?(coder aDecoder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } -} - -class NotificationBannerView: ZapicBanner { - - init(title: String, text: String, icon: UIImage?) { - - super.init(contentRightPadding: 20, icon: icon) - - //Title - let titleLabel = UILabel() - titleLabel.font = UIFont.systemFont(ofSize: 13) - titleLabel.text = title - titleLabel.textAlignment = .center - - content.addSubview(titleLabel) - - titleLabel.translatesAutoresizingMaskIntoConstraints = false - titleLabel.centerXAnchor.constraint(equalTo: content.centerXAnchor).isActive = true - titleLabel.topAnchor.constraint(equalTo: content.topAnchor).isActive = true - titleLabel.widthAnchor.constraint(equalTo: content.widthAnchor).isActive = true - titleLabel.heightAnchor.constraint(equalToConstant: 12).isActive = true - - //Text - let label = UILabel() - label.font = UIFont.systemFont(ofSize: 16) - label.text = text - label.textAlignment = .center - - content.addSubview(label) - - label.translatesAutoresizingMaskIntoConstraints = false - label.centerXAnchor.constraint(equalTo: content.centerXAnchor).isActive = true - label.topAnchor.constraint(equalTo: titleLabel.bottomAnchor).isActive = true - label.bottomAnchor.constraint(equalTo: content.bottomAnchor).isActive = true - label.widthAnchor.constraint(equalTo: content.widthAnchor).isActive = true - } - - required init?(coder aDecoder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } -} - -class ZapicImages { - private static let zBundle = Bundle(for: ZapicImages.self) - - static func image(name: String) -> UIImage? { - return UIImage(named: name, in: zBundle, compatibleWith: nil) - } -} diff --git a/Zapic/CInterface.h b/Zapic/CInterface.h deleted file mode 100644 index 7e69496..0000000 --- a/Zapic/CInterface.h +++ /dev/null @@ -1,20 +0,0 @@ -// -// CInterface.h -// Zapic -// -// Created by Daniel Sarfati on 6/30/17. -// Copyright © 2017 Zapic. All rights reserved. -// - -//#import -// -//#ifdef __cplusplus -//extern “C” { -//#endif -// -// void framework_hello(); -// void framework_message(const char* message); -// -//#ifdef __cplusplus -//} -//#endif diff --git a/Zapic/CInterface.mm b/Zapic/CInterface.mm deleted file mode 100644 index e0d3947..0000000 --- a/Zapic/CInterface.mm +++ /dev/null @@ -1,57 +0,0 @@ -// -// CInterface.m -// Zapic -// -// Created by Daniel Sarfati on 6/30/17. -// Copyright © 2017 Zapic. All rights reserved. -// - -//#import "CInterface.h" -#import -#import - -// Converts C style string to NSString -NSString* CreateNSString (const char* string) -{ - if (string) - return [NSString stringWithUTF8String: string]; - else - return [NSString stringWithUTF8String: ""]; -} - -// Helper method to create C string copy -char* MakeStringCopy (const char* string) -{ - if (string == NULL) - return NULL; - - char* res = (char*)malloc(strlen(string) + 1); - strcpy(res, string); - return res; -} - -extern "C" { - - void z_start(char* version){ - [Zapic start:CreateNSString(version)]; - } - - void z_show(char* viewName){ - [Zapic showWithViewName:CreateNSString(viewName)]; - } - - void z_submitEventWithParams(char* data){ - [Zapic submitEventWithJson:[CreateNSString(data) dataUsingEncoding:NSUTF8StringEncoding]]; - } - - /// Returns the unique player id - const char* z_playerId(){ - - NSUUID* uid = [Zapic playerId]; - - if(uid == NULL) - return NULL; - - return MakeStringCopy([[uid UUIDString] UTF8String]); - } -} diff --git a/Zapic/ContactManager.swift b/Zapic/ContactManager.swift deleted file mode 100644 index 59c3a06..0000000 --- a/Zapic/ContactManager.swift +++ /dev/null @@ -1,136 +0,0 @@ -// -// ContactManager.swift -// Zapic -// -// Created by Daniel Sarfati on 8/18/17. -// Copyright © 2017 zapic. All rights reserved. -// - -import Foundation -import Contacts - -struct ZapicContact { - let firstName: String - let lastName: String - var phoneNumbers = [String]() - var emailAddresses = [String]() - - init(_ contact: CNContact) { - self.firstName = contact.givenName - self.lastName = contact.familyName - - for phone in contact.phoneNumbers { - phoneNumbers.append(phone.value.stringValue) - } - - for email in contact.emailAddresses { - emailAddresses.append(String(email.value)) - } - } -} - -class ContactManager { - - let contactStore = CNContactStore() - - func getContacts(completionHandler: @escaping (_ contacts: [[String: Any]]?) -> Void) { - - requestForAccess { (accessGranted) -> Void in - if accessGranted { - - let contacts = self.retrieveContacts() - let zContacts = self.processContacts(contacts) - - completionHandler(zContacts) - } else { - completionHandler(nil) - } - } - } - - /// Requests access to the contacts - private func requestForAccess(completionHandler: @escaping (_ accessGranted: Bool) -> Void) { - let authorizationStatus = CNContactStore.authorizationStatus(for: CNEntityType.contacts) - - switch authorizationStatus { - case .authorized: - completionHandler(true) - - case .denied, .notDetermined: - self.contactStore.requestAccess(for: CNEntityType.contacts, completionHandler: { (access, _) -> Void in - if access { - completionHandler(access) - } else { - if authorizationStatus == CNAuthorizationStatus.denied { - completionHandler(false) - } - } - }) - - default: - completionHandler(false) - } - } - - private func retrieveContacts() -> [CNContact] { - var results: [CNContact] = [] - - let keysToFetch = [ - CNContactFormatter.descriptorForRequiredKeys(for: .fullName), - CNContactEmailAddressesKey, - CNContactPhoneNumbersKey] as [Any] - - guard let keyDescriptors = keysToFetch as? [CNKeyDescriptor] else { - ZLog.error("Contact key descriptor error") - return results - } - - // Get all the containers - var allContainers: [CNContainer] = [] - do { - allContainers = try contactStore.containers(matching: nil) - } catch { - print("Error fetching containers") - } - - // Iterate all containers and append their contacts to our results array - for container in allContainers { - let fetchPredicate = CNContact.predicateForContactsInContainer(withIdentifier: container.identifier) - - do { - let containerResults = try contactStore.unifiedContacts(matching: fetchPredicate, keysToFetch: keyDescriptors ) - results.append(contentsOf: containerResults) - } catch { - print("Error fetching results for container") - } - } - - return results - } - - private func processContacts(_ input: [CNContact]) -> [[String: Any]] { - var contacts = [[String: Any]]() - for contact in input { - - var phoneNumbers = [String]() - var emailAddresses = [String]() - - for phone in contact.phoneNumbers { - phoneNumbers.append(phone.value.stringValue) - } - - for email in contact.emailAddresses { - emailAddresses.append(String(email.value)) - } - - let dict: [String: Any] = - ["first": contact.givenName, - "last": contact.familyName, - "emails": emailAddresses, - "phones": phoneNumbers] - - contacts.append(dict) - } - return contacts - } -} diff --git a/Zapic/Controllers/ZPCBannerManager.h b/Zapic/Controllers/ZPCBannerManager.h new file mode 100644 index 0000000..79a0d63 --- /dev/null +++ b/Zapic/Controllers/ZPCBannerManager.h @@ -0,0 +1,6 @@ +#import +#import "ZPCScriptMessageHandler.h" + +@interface ZPCBannerManager : NSObject +@property (nonatomic) ZPCScriptMessageHandler *messageHandler; +@end diff --git a/Zapic/Controllers/ZPCBannerManager.m b/Zapic/Controllers/ZPCBannerManager.m new file mode 100644 index 0000000..6ca8e7d --- /dev/null +++ b/Zapic/Controllers/ZPCBannerManager.m @@ -0,0 +1,40 @@ +#import "ZPCBannerManager.h" +#import "ZPCBanner.h" +#import "ZPCImageUtils.h" +#import "ZPCUtils.h" +#import "Zapic.h" + +@implementation ZPCBannerManager + +- (void)setMessageHandler:(ZPCScriptMessageHandler *)messageHandler { + [messageHandler addBannerHandler:^(ZPCBannerMessage *message) { + UIImage *icon = message.icon; + + if (!icon) { + icon = [ZPCImageUtils getZapicLogo]; + } + + ZPCBanner *banner = [[ZPCBanner alloc] initWithTitle:message.title subtitle:message.subtitle image:icon]; + + if (message.data) { + banner.callback = ^{ + [Zapic handleInteraction:message.data]; + }; + } + + [banner show:3.0]; + }]; +} + ++ (UIImage *)imageWithImage:(UIImage *)image scaledToSize:(CGSize)newSize { + //UIGraphicsBeginImageContext(newSize); + // In next line, pass 0.0 to use the current device's pixel scaling factor (and thus account for Retina resolution). + // Pass 1.0 to force exact pixel size. + UIGraphicsBeginImageContextWithOptions(newSize, NO, 0.0); + [image drawInRect:CGRectMake(0, 0, newSize.width, newSize.height)]; + UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + return newImage; +} + +@end diff --git a/Zapic/Controllers/ZPCCore.h b/Zapic/Controllers/ZPCCore.h new file mode 100644 index 0000000..a518e22 --- /dev/null +++ b/Zapic/Controllers/ZPCCore.h @@ -0,0 +1,25 @@ +#import "ZPCBackgroundView.h" +#import "ZPCNotificationManager.h" +#import "ZPCPlayerManager.h" +#import "ZPCQueryManager.h" +#import "ZPCWebApp.h" + +#import + +typedef NS_ENUM(NSUInteger, ZPCEventType) { + ZPCEventTypeGameplay, + ZPCEventTypeInteraction, +}; + +@interface ZPCCore : UIViewController +@property (readonly) ZPCPlayerManager *playerManager; +@property (readonly) ZPCNotificationManager *notificationManager; +@property (readonly, strong) ZPCScriptMessageHandler *messageHandler; +@property (nonnull, readonly) ZPCQueryManager *queryManager; + +- (void)start; +- (void)showPage:(NSString *)pageName; +- (void)showDefaultPage; +- (void)submitEvent:(ZPCEventType)eventType withPayload:(NSObject *)payload; +- (void)closePage; +@end diff --git a/Zapic/Controllers/ZPCCore.m b/Zapic/Controllers/ZPCCore.m new file mode 100644 index 0000000..35f4e8a --- /dev/null +++ b/Zapic/Controllers/ZPCCore.m @@ -0,0 +1,189 @@ +#import "ZPCCore.h" +#import "ZPCBannerManager.h" +#import "ZPCErrorView.h" +#import "ZPCLoadingView.h" +#import "ZPCLog.h" +#import "ZPCMessageQueue.h" +#import "ZPCSafariManager.h" +#import "ZPCShareManager.h" +#import "ZPCUtils.h" + +@interface ZPCCore () +@property BOOL isVisible; +@property BOOL started; +@property BOOL pageReady; +@property (readonly) ZPCErrorView *errorView; +@property (readonly) ZPCLoadingView *loadingView; +@property (readonly, strong) ZPCBannerManager *bannerManager; +@property (readonly, strong) ZPCSafariManager *safariManager; +@property (readonly, strong) ZPCShareManager *shareManager; +@property (readonly, strong) ZPCMessageQueue *messageQueue; +@property (nonatomic, strong) ZPCBackgroundView *backgroundView; +@property (nonatomic, strong) ZPCWebApp *webApp; +@end + +@implementation ZPCCore + +- (instancetype)init { + if (self = [super init]) { + _safariManager = [[ZPCSafariManager alloc] initWithController:self]; + _shareManager = [[ZPCShareManager alloc] init]; + + //Initialize the background view that will hold onto the webview + _backgroundView = [[ZPCBackgroundView alloc] init]; + _backgroundView.backgroundColor = [UIColor clearColor]; + [_backgroundView placeViewToBackground]; + + _messageHandler = [[ZPCScriptMessageHandler alloc] init]; + _messageQueue = [[ZPCMessageQueue alloc] init]; + + //Update the notifications so they can send content to the web app + _notificationManager = [[ZPCNotificationManager alloc] initWithMessageQueue:_messageQueue]; + + //Initialize the loading view + _loadingView = [[ZPCLoadingView alloc] init]; + _loadingView.viewController = self; + + //Initialize the error view + _errorView = [[ZPCErrorView alloc] init]; + _errorView.viewController = self; + + //Initialize the web app + _webApp = [[ZPCWebApp alloc] initWithHandler:_messageHandler]; + _webApp.safariManager = _safariManager; + [_backgroundView addSubview:_webApp]; + + _messageQueue.webApp = _webApp; + + //Initialize the query manager + _queryManager = [[ZPCQueryManager alloc] initWithMessageHandler:_messageHandler messageQueue:_messageQueue]; + + //Setup the banners + _bannerManager = [[ZPCBannerManager alloc] init]; + _bannerManager.messageHandler = _messageHandler; + + _playerManager = [[ZPCPlayerManager alloc] initWithHandler:_messageHandler]; + + __weak ZPCCore *weakSelf = self; + + [_messageHandler addAppStatusHandler:^(ZPCAppStatusMessage *msg) { + if (msg.status == ZPCAppStatusReady) { + [weakSelf.messageQueue sendQueuedMessages]; + self->_pageReady = YES; + } else if (msg.status == ZPCAppStatusFailed) { + //Notify the query manager that things are broken + weakSelf.queryManager.isReady = NO; + } + }]; + + [_messageHandler addClosePageHandler:^{ + [weakSelf closePage]; + }]; + + [_messageHandler addPageReadyHandler:^{ + weakSelf.view = weakSelf.webApp; + }]; + + [_messageHandler addShowPageHandler:^{ + [weakSelf showPage:@"current"]; + }]; + + [_messageHandler addShowShareHandler:^(ZPCShareMessage *msg) { + [weakSelf.shareManager share:msg]; + }]; + + _webApp.loadErrorHandler = ^{ + //Show the error page + weakSelf.view = weakSelf.errorView; + //Notify the query manager that things are broken + weakSelf.queryManager.isReady = NO; + }; + } + return self; +} + +- (void)start { + if (_started) { + [ZPCLog info:@"Zapic is already started. Start should only be called once"]; + return; + } + _started = true; + + [ZPCLog info:@"Starting Zapic"]; + + //Start loading the Zapic web app + [_webApp loadUrl:@"https://app.zapic.net"]; +} + +- (BOOL)prefersStatusBarHidden { + //Only show the status bar on iPhoneX + return UIScreen.mainScreen.nativeBounds.size.height != 2436; +} + +- (void)closePage { + [super dismissViewControllerAnimated:YES + completion:^{ + [self->_messageQueue sendMessage:ZPCWebFunctionClosePage withPayload:@""]; + [self->_backgroundView addSubview:self->_webApp]; + }]; + _isVisible = false; +} + +- (void)showDefaultPage { + [self showPage:@"default"]; +} + +- (void)showPage:(NSString *)pageName { + [ZPCLog info:@"Showing %@ page", pageName]; + + if (_pageReady) { + self.view = _webApp; + } else if (_webApp.errorLoading) { + self.view = _errorView; + } else { + self.view = _loadingView; + } + + //Trigger the web to update + [_messageQueue sendMessage:ZPCWebFunctionOpenPage withPayload:pageName]; + + if (_isVisible) { + [ZPCLog info:@"Zapic already visible"]; + return; + } + + _isVisible = true; + + [self.view.topAnchor constraintEqualToAnchor:self.topLayoutGuide.topAnchor].active = YES; + [self.view.bottomAnchor constraintEqualToAnchor:self.bottomLayoutGuide.bottomAnchor].active = YES; + + self.modalPresentationStyle = UIModalPresentationOverCurrentContext; + + UIViewController *root = [ZPCUtils getTopViewController]; + [root presentViewController:self animated:true completion:nil]; +} + +- (void)submitEvent:(ZPCEventType)eventType withPayload:(NSObject *)payload { + [ZPCLog info:@"Submitting an event to the web client"]; + + NSDictionary *msg = @{ + @"type": [ZPCCore getEventTypeName:eventType], + @"params": payload, + @"timestamp": [ZPCUtils getIsoNow], + }; + + [_messageQueue sendMessage:ZPCWebFunctionSubmitEvent withPayload:msg]; +} + ++ (NSString *)getEventTypeName:(ZPCEventType)eventType { + if (eventType == ZPCEventTypeGameplay) { + return @"gameplay"; + } else if (eventType == ZPCEventTypeInteraction) { + return @"interaction"; + } else { + [ZPCLog error:@"Unknow event type"]; + return @""; + } +} + +@end diff --git a/Zapic/Controllers/ZPCErrors.h b/Zapic/Controllers/ZPCErrors.h new file mode 100644 index 0000000..68c6a89 --- /dev/null +++ b/Zapic/Controllers/ZPCErrors.h @@ -0,0 +1,8 @@ +#import + +FOUNDATION_EXPORT NSInteger const ZPCFailedToStartError; +FOUNDATION_EXPORT NSInteger const ZPCInvalidResponseError; +FOUNDATION_EXPORT NSInteger const ZPCVersionNotSupportedError; +FOUNDATION_EXPORT NSInteger const ZPCInvalidQueryError; +FOUNDATION_EXPORT NSInteger const ZPCNetworkError; +FOUNDATION_EXPORT NSInteger const ZPCLoginRequiredError; diff --git a/Zapic/Controllers/ZPCErrors.m b/Zapic/Controllers/ZPCErrors.m new file mode 100644 index 0000000..57ce67a --- /dev/null +++ b/Zapic/Controllers/ZPCErrors.m @@ -0,0 +1,8 @@ +#import "ZPCErrors.h" + +NSInteger const ZPCFailedToStartError = 2600; +NSInteger const ZPCInvalidResponseError = 2601; +NSInteger const ZPCVersionNotSupportedError = 2650; +NSInteger const ZPCInvalidQueryError = 2651; +NSInteger const ZPCNetworkError = 2652; +NSInteger const ZPCLoginRequiredError = 2653; diff --git a/Zapic/Controllers/ZPCMessageQueue.h b/Zapic/Controllers/ZPCMessageQueue.h new file mode 100644 index 0000000..146e313 --- /dev/null +++ b/Zapic/Controllers/ZPCMessageQueue.h @@ -0,0 +1,17 @@ +#import +#import "ZPCWebApp.h" + +static NSString *const ZPCWebFunctionSubmitEvent = @"SUBMIT_EVENT"; +static NSString *const ZPCWebFunctionOpenPage = @"OPEN_PAGE"; +static NSString *const ZPCWebFunctionClosePage = @"CLOSE_PAGE"; +static NSString *const ZPCWebFunctionSetDeviceToken = @"DEVICE_TOKEN"; +static NSString *const ZPCWebFunctionNotificationOpened = @"NOTIFICATION_OPENED"; +static NSString *const ZPCWebFunctionNotificationReceived = @"NOTIFICATION_RECEIVED"; +static NSString *const ZPCWebFunctionQuery = @"QUERY"; + +@interface ZPCMessageQueue : NSObject +@property (nonatomic, strong) ZPCWebApp *webApp; +- (void)sendMessage:(NSString *)function withPayload:(NSObject *)payload; +- (void)sendMessage:(NSString *)function withPayload:(NSObject *)payload isError:(BOOL)isError; +- (void)sendQueuedMessages; +@end diff --git a/Zapic/Controllers/ZPCMessageQueue.m b/Zapic/Controllers/ZPCMessageQueue.m new file mode 100644 index 0000000..5a81a06 --- /dev/null +++ b/Zapic/Controllers/ZPCMessageQueue.m @@ -0,0 +1,104 @@ +#import "ZPCMessageQueue.h" +#import "ZPCLog.h" +#import "ZPCQueue.h" +#import "ZPCStorage.h" + +@interface ZPCMessageQueue () +@property (strong) ZPCQueue *queue; +@property (readonly) ZPCStorage *storage; +@property NSString *queuedPageEvent; +@property BOOL readyToSend; +@end + +@implementation ZPCMessageQueue + +- (instancetype)init { + if (self = [super init]) { + _queue = [[ZPCQueue alloc] init]; + _storage = [[ZPCStorage alloc] init]; + + NSArray *savedItems = [_storage retrieve]; + + if (savedItems) { + [_queue enqueueMany:savedItems]; + } + } + return self; +} + +- (void)sendMessage:(NSString *)function withPayload:(NSObject *)payload { + [self sendMessage:function withPayload:payload isError:NO]; +} + +- (void)sendMessage:(NSString *)function withPayload:(NSObject *)payload isError:(BOOL)isError { + [ZPCLog info:@"Dispatching JS event type %@", function]; + + NSMutableDictionary *msg = [[NSMutableDictionary alloc] init]; + msg[@"type"] = function; + msg[@"payload"] = payload; + + if (isError) { + msg[@"error"] = @true; + } + + NSData *jsonData = [NSJSONSerialization dataWithJSONObject:msg options:0 error:nil]; + NSString *json = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding]; + NSString *js = [[NSString alloc] initWithFormat:@"zapic.dispatch(%@);", json]; + + //Send the message to the web app if it's ready + if (_readyToSend) { + [self runJavaScript:js]; + } + //Queue up messages + else { + [ZPCLog info:@"Web client is not ready to run JS. Adding to queue"]; + + if (function == ZPCWebFunctionOpenPage) { + _queuedPageEvent = js; + } else if (function == ZPCWebFunctionClosePage) { + _queuedPageEvent = nil; + } else { + [_queue enqueue:js]; + + if (_queue.count > 1000) { + [_queue dequeue]; + } + + //Save the events to storage + [_storage store:_queue.data]; + } + } +} + +- (void)sendQueuedMessages { + _readyToSend = YES; + + //If there is a queued page, send it + if (_queuedPageEvent) { + [ZPCLog info:@"Resending page open request"]; + [self runJavaScript:_queuedPageEvent]; + _queuedPageEvent = nil; + } + + [ZPCLog info:@"Starting to resend %lu events", (unsigned long)_queue.count]; + + //Clears any stored events from disk + [_storage clear]; + + while (_queue.count > 0) { + NSString *jsEvent = [_queue dequeue]; + + [self runJavaScript:jsEvent]; + } + + [ZPCLog info:@"Done resending queued messages"]; +} + +- (void)runJavaScript:(NSString *)js { + if (!js) { + return; + } + [_webApp evaluateJavaScript:js]; +} + +@end diff --git a/Zapic/Controllers/ZPCNotificationManager.h b/Zapic/Controllers/ZPCNotificationManager.h new file mode 100644 index 0000000..d0b37f2 --- /dev/null +++ b/Zapic/Controllers/ZPCNotificationManager.h @@ -0,0 +1,9 @@ +#import +#import "ZPCMessageQueue.h" + +@interface ZPCNotificationManager : NSObject +- (instancetype)initWithMessageQueue:(ZPCMessageQueue *)messageQueue; +- (void)registerForPushNotifications; +- (void)setDeviceToken:(NSData *)deviceToken; +- (void)receivedNotification:(NSDictionary *)userInfo; +@end diff --git a/Zapic/Controllers/ZPCNotificationManager.m b/Zapic/Controllers/ZPCNotificationManager.m new file mode 100644 index 0000000..7006905 --- /dev/null +++ b/Zapic/Controllers/ZPCNotificationManager.m @@ -0,0 +1,104 @@ +#import "ZPCNotificationManager.h" +#import +#import "ZPCLog.h" +#import "Zapic.h" + +static BOOL registered = NO; +static ZPCMessageQueue *_messageQueue; + +@interface ZPCNotificationManager () +@property (readonly) ZPCMessageQueue *messageQueue; +@end + +@implementation ZPCNotificationManager + +- (instancetype)initWithMessageQueue:(ZPCMessageQueue *)messageQueue { + if (self = [super init]) { + _messageQueue = messageQueue; + } + return self; +} + +- (void)registerForPushNotifications { + if (registered) { + [ZPCLog warn:@"Already registered for push notifications, ignoring"]; + return; + } + + registered = YES; + + [ZPCLog info:@"Registering for push notifications"]; + + //iOS 10 and above + if (@available(iOS 10.0, *)) { + id callback = ^(BOOL granted, NSError *_Nullable error) { + //If the user accepts the push notifications + if (granted) { + [ZPCLog info:@"Notifications permission are granted"]; + + [UNUserNotificationCenter.currentNotificationCenter getNotificationSettingsWithCompletionHandler:^(UNNotificationSettings *_Nonnull settings) { + [ZPCLog info:@"Notification authorization status %ld", (long)settings.authorizationStatus]; + + if (settings.authorizationStatus == UNAuthorizationStatusAuthorized) { + dispatch_async(dispatch_get_main_queue(), ^(void) { + [UIApplication.sharedApplication registerForRemoteNotifications]; + }); + } + }]; + } else { + [ZPCLog info:@"Notifications permission are not granted"]; + } + }; + + //Request permission to send push notifications, aka the popup + [UNUserNotificationCenter.currentNotificationCenter requestAuthorizationWithOptions:(UNAuthorizationOptionAlert + UNAuthorizationOptionSound + UNAuthorizationOptionBadge) completionHandler:callback]; + } else { + //iOS 9 + // let settings = UIUserNotificationSettings(forTypes: [.Sound, .Alert, .Badge], categories: nil) + // UIApplication.sharedApplication().registerUserNotificationSettings(settings) + UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:(UIUserNotificationTypeSound | UIUserNotificationTypeAlert | UIUserNotificationTypeBadge) categories:nil]; + [UIApplication.sharedApplication registerUserNotificationSettings:settings]; + + // This is an asynchronous method to retrieve a Device Token + // Callbacks are in AppDelegate.swift + // Success = didRegisterForRemoteNotificationsWithDeviceToken + // Fail = didFailToRegisterForRemoteNotificationsWithError + // UIApplication.sharedApplication().registerForRemoteNotifications() + [UIApplication.sharedApplication registerForRemoteNotifications]; + } +} + +- (void)setDeviceToken:(NSData *)deviceToken { + if (!_messageQueue) { + [ZPCLog error:@"No message queue, unable to update the device token"]; + return; + } + + const unsigned *tokenBytes = [deviceToken bytes]; + + NSString *hexToken = [NSString stringWithFormat:@"%08x%08x%08x%08x%08x%08x%08x%08x", + ntohl(tokenBytes[0]), ntohl(tokenBytes[1]), ntohl(tokenBytes[2]), + ntohl(tokenBytes[3]), ntohl(tokenBytes[4]), ntohl(tokenBytes[5]), + ntohl(tokenBytes[6]), ntohl(tokenBytes[7])]; + + NSDictionary *msg = @{ + @"deviceToken": hexToken, + }; + + [_messageQueue sendMessage:ZPCWebFunctionSetDeviceToken withPayload:msg]; +} + +- (void)receivedNotification:(NSDictionary *)userInfo { + UIApplicationState state = UIApplication.sharedApplication.applicationState; + NSDictionary *aps = [userInfo objectForKey:@"aps"]; + + // user tapped notification while app was in background or closed + if (state == UIApplicationStateInactive || state == UIApplicationStateBackground) { + [_messageQueue sendMessage:ZPCWebFunctionNotificationOpened withPayload:aps]; + } else { + // App is in UIApplicationStateActive (running in foreground) + [_messageQueue sendMessage:ZPCWebFunctionNotificationReceived withPayload:aps]; + } +} + +@end diff --git a/Zapic/Controllers/ZPCPlayerManager.h b/Zapic/Controllers/ZPCPlayerManager.h new file mode 100644 index 0000000..54c418d --- /dev/null +++ b/Zapic/Controllers/ZPCPlayerManager.h @@ -0,0 +1,10 @@ +#import +#import "ZPCPlayer.h" +#import "ZPCScriptMessageHandler.h" + +@interface ZPCPlayerManager : NSObject +@property (readonly) ZPCPlayer *player; +- (instancetype)initWithHandler:(ZPCScriptMessageHandler *)handler; +- (void)addLoginHandler:(void (^)(ZPCPlayer *))handler; +- (void)addLogoutHandler:(void (^)(ZPCPlayer *))handler; +@end diff --git a/Zapic/Controllers/ZPCPlayerManager.m b/Zapic/Controllers/ZPCPlayerManager.m new file mode 100644 index 0000000..7cd68d9 --- /dev/null +++ b/Zapic/Controllers/ZPCPlayerManager.m @@ -0,0 +1,57 @@ +#import "ZPCPlayerManager.h" + +@interface ZPCPlayerManager () +@property (nonatomic, strong) NSMutableArray *loginHandlers; +@property (nonatomic, strong) NSMutableArray *logoutHandlers; +@end + +@implementation ZPCPlayerManager + +- (instancetype)initWithHandler:(ZPCScriptMessageHandler *)handler { + if (self = [super init]) { + _loginHandlers = [[NSMutableArray alloc] init]; + _logoutHandlers = [[NSMutableArray alloc] init]; + + [handler addLoginHandler:^(ZPCPlayer *player) { + //If there is already a player logged in, log them out + if (self->_player) { + [self playerLoggedOut]; + } + + self->_player = player; + [self playerLoggedIn:player]; + }]; + + [handler addLogoutHandler:^{ + [self playerLoggedOut]; + }]; + } + return self; +} + +- (void)playerLoggedIn:(ZPCPlayer *)newPlayer { + for (id (^handler)(ZPCPlayer *) in _loginHandlers) { + handler(newPlayer); + } +} + +- (void)playerLoggedOut { + if (!_player) { + return; + } + + for (id (^handler)(ZPCPlayer *) in _logoutHandlers) { + handler(_player); + } + _player = nil; +} + +- (void)addLoginHandler:(void (^)(ZPCPlayer *))handler { + [_loginHandlers addObject:handler]; +} + +- (void)addLogoutHandler:(void (^)(ZPCPlayer *))handler { + [_logoutHandlers addObject:handler]; +} + +@end diff --git a/Zapic/Controllers/ZPCQueryManager.h b/Zapic/Controllers/ZPCQueryManager.h new file mode 100644 index 0000000..3f8bbef --- /dev/null +++ b/Zapic/Controllers/ZPCQueryManager.h @@ -0,0 +1,41 @@ +#import +#import "ZPCChallenge.h" +#import "ZPCCompetition.h" +#import "ZPCMessageQueue.h" +#import "ZPCScriptMessageHandler.h" +#import "ZPCStatistic.h" + +@interface ZPCQueryManager : NSObject + +@property (nonatomic) BOOL isReady; + +- (instancetype)initWithMessageHandler:(ZPCScriptMessageHandler *)messageHandler messageQueue:(ZPCMessageQueue *)messageQueue; + +/** + Gets the list of competitions. + + @param completionHandler Callback handler. + */ +- (void)getCompetitions:(void (^)(NSArray *competitions, NSError *error))completionHandler; + +/** + Gets the list of competitions. + + @param completionHandler Callback handler. + */ +- (void)getStatistics:(void (^)(NSArray *statistics, NSError *error))completionHandler; + +/** + Gets the list of challenges. + + @param completionHandler Callback handler. + */ +- (void)getChallenges:(void (^)(NSArray *statistics, NSError *error))completionHandler; + +/** + Gets the player. + + @param completionHandler Callback handler. + */ +- (void)getPlayer:(void (^)(ZPCPlayer *player, NSError *error))completionHandler; +@end diff --git a/Zapic/Controllers/ZPCQueryManager.m b/Zapic/Controllers/ZPCQueryManager.m new file mode 100644 index 0000000..ad2dcd1 --- /dev/null +++ b/Zapic/Controllers/ZPCQueryManager.m @@ -0,0 +1,162 @@ +#import "ZPCQueryManager.h" +#import "ZPCErrors.h" +#import "ZPCLog.h" + +typedef void (^ResponseBlock)(id response, NSError *error); + +@interface ZPCQueryManager () +@property (readonly) ZPCScriptMessageHandler *messageHandler; +@property (readonly) ZPCMessageQueue *messageQueue; +@property (nonnull, readonly) NSMutableDictionary *requests; +@property (nonnull, readonly) NSMutableDictionary *requestTypes; +@end + +@implementation ZPCQueryManager +static NSString *const CompetitionQuery = @"competitions"; +static NSString *const StatisticsQuery = @"statistics"; +static NSString *const ChallengeQuery = @"challenges"; +static NSString *const PlayerQuery = @"player"; + +static NSString *const ZPCErrorDomain = @"com.Zapic"; + +- (void)setIsReady:(BOOL)isReady { + if (isReady == _isReady) { + return; + } + + _isReady = isReady; + + //If the manager is not available, close all pending queries + if (!_isReady) { + [self failAllQueries]; + } +} + +- (instancetype)initWithMessageHandler:(ZPCScriptMessageHandler *)messageHandler messageQueue:(ZPCMessageQueue *)messageQueue { + if (self = [super init]) { + _messageQueue = messageQueue; + _messageHandler = messageHandler; + _requests = [[NSMutableDictionary alloc] init]; + _requestTypes = [[NSMutableDictionary alloc] init]; + _isReady = YES; + + __weak ZPCQueryManager *weakSelf = self; + + [_messageHandler addQueryResponseHandler:^(NSDictionary *message) { + [weakSelf handleResponse:message]; + }]; + } + return self; +} + +- (void)handleResponse:(NSDictionary *)data { + NSDictionary *payload = data[@"payload"]; + BOOL error = [data[@"error"] boolValue]; + NSString *requestId = payload[@"requestId"]; + + //Gets the callback for this request + ResponseBlock handler = [_requests objectForKey:requestId]; + NSString *dataType = [_requestTypes objectForKey:requestId]; + + if (!handler) { + [ZPCLog warn:@"Unable to find handler for requestId: %@", requestId]; + return; + } + + [_requests removeObjectForKey:requestId]; + [_requestTypes removeObjectForKey:requestId]; + + //If this is an error response, trigger the callback right away + if (error) { + NSString *msg = payload[@"errorMessage"]; + id codeObj = payload[@"errorCode"]; + NSError *error; + if (codeObj == nil) { + error = [NSError errorWithDomain:ZPCErrorDomain code:ZPCFailedToStartError userInfo:@{@"errorMsg": @"Unknown error"}]; + } else { + error = [NSError errorWithDomain:ZPCErrorDomain code:[codeObj intValue] userInfo:@{@"errorMsg": msg}]; + } + + handler(nil, error); + return; + } + + id response = nil; + id responseData = payload[@"response"]; + + if ([dataType isEqualToString:CompetitionQuery]) { + response = [ZPCCompetition decodeList:responseData]; + } else if ([dataType isEqualToString:StatisticsQuery]) { + response = [ZPCStatistic decodeList:responseData]; + } else if ([dataType isEqualToString:ChallengeQuery]) { + response = [ZPCChallenge decodeList:responseData]; + } else if ([dataType isEqualToString:PlayerQuery]) { + response = [[ZPCPlayer alloc] initWithData:responseData]; + } + + //Trigger the callback with the reponse data + handler(response, nil); +} + +- (void)sendQuery:(NSString *)dataType withCompletionHandler:(ResponseBlock)completionHandler { + //If requests cant be processed now, cancel immediately + if (!_isReady) { + NSError *error = [NSError errorWithDomain:ZPCErrorDomain + code:ZPCFailedToStartError + userInfo:nil]; + + completionHandler(nil, error); + return; + } + + //Generate a new unique id + NSString *requestId = [NSUUID UUID].UUIDString; + + //Save the callback for this request id + [_requests setObject:completionHandler forKey:requestId]; + [_requestTypes setObject:dataType forKey:requestId]; + + NSDictionary *msg = @{ + @"requestId": requestId, + @"dataType": dataType, + @"dataTypeVersion": @1 + }; + + //Send the query to JS + [_messageQueue sendMessage:ZPCWebFunctionQuery withPayload:msg]; +} + +- (void)failAllQueries { + for (NSString *requestId in _requests) { + NSError *error = [NSError errorWithDomain:ZPCErrorDomain + code:ZPCFailedToStartError + userInfo:nil]; + + //Gets the handler + ResponseBlock handler = [_requests objectForKey:requestId]; + + //Trigger the handler with the error + handler(nil, error); + } + + [_requests removeAllObjects]; + [_requestTypes removeAllObjects]; +} + +- (void)getCompetitions:(void (^)(NSArray *competitions, NSError *error))completionHandler { + [self sendQuery:CompetitionQuery withCompletionHandler:completionHandler]; +} + +- (void)getStatistics:(void (^)(NSArray *statistics, NSError *error))completionHandler { + [self sendQuery:StatisticsQuery withCompletionHandler:completionHandler]; +} + +- (void)getChallenges:(void (^)(NSArray *statistics, NSError *error))completionHandler { + [self sendQuery:ChallengeQuery withCompletionHandler:completionHandler]; +} + +- (void)getPlayer:(void (^)(ZPCPlayer *player, NSError *error))completionHandler { + [self sendQuery:PlayerQuery withCompletionHandler:completionHandler]; +} + +@end diff --git a/Zapic/Controllers/ZPCSafariManager.h b/Zapic/Controllers/ZPCSafariManager.h new file mode 100644 index 0000000..45b9022 --- /dev/null +++ b/Zapic/Controllers/ZPCSafariManager.h @@ -0,0 +1,18 @@ +#import + +@interface ZPCSafariManager : NSObject + +/** + Initialize and configure the manager with the root view controller + + @param viewController The view controller that will hold the Safari view. + @return The newly created manager. + */ +- (instancetype)initWithController:(UIViewController *)viewController; + +/** + Opens the given url in an embedded safari window + @param url The url to open. + */ +- (void)openUrl:(NSURL *)url; +@end diff --git a/Zapic/Controllers/ZPCSafariManager.m b/Zapic/Controllers/ZPCSafariManager.m new file mode 100644 index 0000000..6d43d2f --- /dev/null +++ b/Zapic/Controllers/ZPCSafariManager.m @@ -0,0 +1,46 @@ +#import "ZPCSafariManager.h" +#import +#import "ZPCLog.h" + +@interface ZPCSafariManager () +@property (readonly) UIViewController *viewController; +@end + +@implementation ZPCSafariManager + +- (instancetype)initWithController:(UIViewController *)viewController { + if (self = [super init]) { + _viewController = viewController; + } + return self; +} + +- (void)openUrl:(NSURL *)url { + if (![url.scheme isEqualToString:@"http"] && ![url.scheme isEqualToString:@"https"]) { + [ZPCLog warn:@"Unable to open scheme %@ in Safari", url.scheme]; + return; + } + + [ZPCLog info:@"Opening url %@ in safari", url.absoluteString]; + + SFSafariViewController *svc = [[SFSafariViewController alloc] initWithURL:url]; + + if (!svc) { + [ZPCLog error:@"Unable to create SFSafariViewController"]; + return; + } + + [svc setValue:self forKey:@"delegate"]; + [_viewController presentViewController:svc animated:YES completion:nil]; +} + +/** + Callback with the embedded safari view is "Done" + + @param controller The controller that is done + */ +- (void)safariViewControllerDidFinish:(id)controller { + [controller dismissViewControllerAnimated:YES completion:nil]; +} + +@end diff --git a/Zapic/Controllers/ZPCScriptMessageHandler.h b/Zapic/Controllers/ZPCScriptMessageHandler.h new file mode 100644 index 0000000..e5e8f8d --- /dev/null +++ b/Zapic/Controllers/ZPCScriptMessageHandler.h @@ -0,0 +1,21 @@ +#import +#import +#import "ZPCAppStatusMessage.h" +#import "ZPCBannerMessage.h" +#import "ZPCPlayer.h" +#import "ZPCShareMessage.h" + +static NSString *const ZPCScriptMethodName = @"dispatch"; + +@interface ZPCScriptMessageHandler : NSObject +- (void)userContentController:(id)userContentController didReceiveScriptMessage:(id)message; +- (void)addAppStatusHandler:(void (^)(ZPCAppStatusMessage *))handler; +- (void)addBannerHandler:(void (^)(ZPCBannerMessage *))handler; +- (void)addLoginHandler:(void (^)(ZPCPlayer *))handler; +- (void)addLogoutHandler:(void (^)(void))handler; +- (void)addClosePageHandler:(void (^)(void))handler; +- (void)addPageReadyHandler:(void (^)(void))handler; +- (void)addShowPageHandler:(void (^)(void))handler; +- (void)addShowShareHandler:(void (^)(ZPCShareMessage *))handler; +- (void)addQueryResponseHandler:(void (^)(NSDictionary *))handler; +@end diff --git a/Zapic/Controllers/ZPCScriptMessageHandler.m b/Zapic/Controllers/ZPCScriptMessageHandler.m new file mode 100644 index 0000000..9ad53b2 --- /dev/null +++ b/Zapic/Controllers/ZPCScriptMessageHandler.m @@ -0,0 +1,236 @@ +#import "ZPCScriptMessageHandler.h" +#import "ZPCLog.h" +#import "ZPCUtils.h" + +@interface ZPCScriptMessageHandler () + +@property (nonatomic, strong) NSMutableArray *bannerHandlers; +@property (nonatomic, strong) NSMutableArray *statusHandlers; +@property (nonatomic, strong) NSMutableArray *loginHandlers; +@property (nonatomic, strong) NSMutableArray *logoutHandlers; +@property (nonatomic, strong) NSMutableArray *closePageHandlers; +@property (nonatomic, strong) NSMutableArray *pageReadyHandlers; +@property (nonatomic, strong) NSMutableArray *showPageHandlers; +@property (nonatomic, strong) NSMutableArray *showShareHandlers; +@property (nonatomic, strong) NSMutableArray *queryResponseHandlers; +@end + +@implementation ZPCScriptMessageHandler + +static NSString *const AppStarted = @"APP_STARTED"; +static NSString *const AppFailed = @"APP_FAILED"; +static NSString *const ShowBanner = @"SHOW_BANNER"; +static NSString *const ShowPage = @"SHOW_PAGE"; +static NSString *const ShowShare = @"SHOW_SHARE_MENU"; +static NSString *const PageReady = @"PAGE_READY"; +static NSString *const ClosePageRequest = @"CLOSE_PAGE_REQUESTED"; +static NSString *const LoggedIn = @"LOGGED_IN"; +static NSString *const LoggedOut = @"LOGGED_OUT"; +static NSString *const QueryResponse = @"QUERY_RESPONSE"; + +- (instancetype)init { + if (self = [super init]) { + _bannerHandlers = [[NSMutableArray alloc] init]; + _statusHandlers = [[NSMutableArray alloc] init]; + _loginHandlers = [[NSMutableArray alloc] init]; + _logoutHandlers = [[NSMutableArray alloc] init]; + _closePageHandlers = [[NSMutableArray alloc] init]; + _pageReadyHandlers = [[NSMutableArray alloc] init]; + _showPageHandlers = [[NSMutableArray alloc] init]; + _showShareHandlers = [[NSMutableArray alloc] init]; + _queryResponseHandlers = [[NSMutableArray alloc] init]; + } + return self; +} + +- (void)userContentController:(id)userContentController didReceiveScriptMessage:(id)message { + NSString *name = [message valueForKey:@"name"]; + if (![name isEqualToString:ZPCScriptMethodName]) { + [ZPCLog warn:@"Received unknown method from JS"]; + return; + } + + NSDictionary *json = [message valueForKey:@"body"]; + + if (json == nil || ![json isKindOfClass:[NSDictionary class]]) { + [ZPCLog warn:@"Received invalid message format"]; + return; + } + + NSString *type = [[json valueForKey:@"type"] uppercaseString]; + + if (!type.length) { + [ZPCLog warn:@"Received a message with a missing message type"]; + return; + } + + [ZPCUtils cleanDictionary:json]; + + [self handleMessage:type withData:json]; +} + +- (void)addAppStatusHandler:(void (^)(ZPCAppStatusMessage *))handler { + [_statusHandlers addObject:handler]; +} + +- (void)addBannerHandler:(void (^)(ZPCBannerMessage *))handler { + [_bannerHandlers addObject:handler]; +} + +- (void)addClosePageHandler:(void (^)(void))handler { + [_closePageHandlers addObject:handler]; +} + +- (void)addPageReadyHandler:(void (^)(void))handler { + [_pageReadyHandlers addObject:handler]; +} + +- (void)addLoginHandler:(void (^)(ZPCPlayer *))handler { + [_loginHandlers addObject:handler]; +} + +- (void)addLogoutHandler:(void (^)(void))handler { + [_logoutHandlers addObject:handler]; +} + +- (void)addShowPageHandler:(void (^)(void))handler { + [_showPageHandlers addObject:handler]; +} + +- (void)addShowShareHandler:(void (^)(ZPCShareMessage *))handler { + [_showShareHandlers addObject:handler]; +} + +- (void)addQueryResponseHandler:(void (^)(NSDictionary *))handler { + [_queryResponseHandlers addObject:handler]; +} + +- (void)handleMessage:(nonnull NSString *)type + withData:(nonnull NSDictionary *)data { + [ZPCLog info:@"Received %@ from JS", type]; + + if ([type isEqualToString:AppStarted]) { + [self handleStatusUpdated:ZPCAppStatusReady]; + } else if ([type isEqualToString:AppFailed]) { + [self handleStatusUpdated:ZPCAppStatusFailed]; + } else if ([type isEqualToString:ClosePageRequest]) { + [self handleClosePageRequested]; + } else if ([type isEqualToString:LoggedIn]) { + [self handleLogin:data]; + } else if ([type isEqualToString:LoggedOut]) { + [self handleLogout]; + } else if ([type isEqualToString:PageReady]) { + [self handlePageReady]; + } else if ([type isEqualToString:ShowBanner]) { + [self handleBanner:data]; + } else if ([type isEqualToString:ShowPage]) { + [self handleShowPage]; + } else if ([type isEqualToString:ShowShare]) { + [self handleShowShare:data]; + } else if ([type isEqualToString:QueryResponse]) { + [self handleQueryResponse:data]; + } else { + [ZPCLog info:@"Recevied unhandled message type: %@", type]; + } +} + +- (void)handleBanner:(nonnull NSDictionary *)data { + NSDictionary *msg = data[@"payload"]; + NSString *title = msg[@"title"]; + NSString *subtitle = msg[@"subtitle"]; + NSString *metadata = msg[@"data"]; + UIImage *img = [self decodeBase64ToImage:msg[@"icon"]]; + + ZPCBannerMessage *bannerMessage = [ZPCBannerMessage bannerWithTitle:title withSubtitle:subtitle withData:metadata withIcon:img]; + + for (id (^handler)(ZPCBannerMessage *) in _bannerHandlers) { + handler(bannerMessage); + } +} + +- (void)handleShowPage { + for (id (^handler)(void) in _showPageHandlers) { + handler(); + } +} + +- (void)handleShowShare:(nonnull NSDictionary *)data { + NSDictionary *msg = data[@"payload"]; + NSString *text = msg[@"text"]; + NSString *urlStr = msg[@"url"]; + NSString *imgStr = msg[@"image"]; + NSString *target = msg[@"target"]; + NSString *subject = msg[@"subject"]; + + NSURL *url; + UIImage *img; + + if (urlStr && urlStr != (id)[NSNull null]) { + url = [NSURL URLWithString:urlStr]; + } + + if (imgStr) { + img = [self decodeBase64ToImage:imgStr]; + } + + if (!text) { + text = msg[@"body"]; + } + + ZPCShareMessage *shareMsg = [[ZPCShareMessage alloc] initWithText:text target:target subject:subject withImage:img withURL:url]; + + for (id (^handler)(ZPCShareMessage *) in _showShareHandlers) { + handler(shareMsg); + } +} + +- (void)handleLogin:(nonnull NSDictionary *)data { + NSDictionary *msg = data[@"payload"]; + ZPCPlayer *player = [[ZPCPlayer alloc] initWithData:msg]; + + for (id (^handler)(ZPCPlayer *) in _loginHandlers) { + handler(player); + } +} + +- (void)handleLogout { + for (id (^handler)(void) in _logoutHandlers) { + handler(); + } +} + +- (void)handleStatusUpdated:(ZPCAppStatus)status { + ZPCAppStatusMessage *statusMessage = [[ZPCAppStatusMessage alloc] initWithStatus:status]; + + for (id (^handler)(ZPCAppStatusMessage *) in _statusHandlers) { + handler(statusMessage); + } +} + +- (void)handleClosePageRequested { + for (id (^handler)(void) in _closePageHandlers) { + handler(); + } +} + +- (void)handlePageReady { + for (id (^handler)(void) in _pageReadyHandlers) { + handler(); + } +} + +- (void)handleQueryResponse:(nonnull NSDictionary *)data { + for (id (^handler)(NSDictionary *) in _queryResponseHandlers) { + handler(data); + } +} + +- (UIImage *)decodeBase64ToImage:(NSString *)strEncodeData { + if (!strEncodeData.length) { + return nil; + } + NSData *data = [[NSData alloc] initWithBase64EncodedString:strEncodeData options:NSDataBase64DecodingIgnoreUnknownCharacters]; + return [UIImage imageWithData:data]; +} + +@end diff --git a/Zapic/Controllers/ZPCShareManager.h b/Zapic/Controllers/ZPCShareManager.h new file mode 100644 index 0000000..e471c2e --- /dev/null +++ b/Zapic/Controllers/ZPCShareManager.h @@ -0,0 +1,14 @@ +#import +#import +#import "ZPCShareMessage.h" + +@interface ZPCShareManager : NSObject + +/** + Shows the share sheet, allowing the user to share. + + @param message Content to share + */ +- (void)share:(ZPCShareMessage *)message; + +@end diff --git a/Zapic/Controllers/ZPCShareManager.m b/Zapic/Controllers/ZPCShareManager.m new file mode 100644 index 0000000..0b03cbc --- /dev/null +++ b/Zapic/Controllers/ZPCShareManager.m @@ -0,0 +1,101 @@ +#import "ZPCShareManager.h" +#import "ZPCLog.h" +#import "ZPCUtils.h" + +@interface ZPCShareManager () +@end + +@implementation ZPCShareManager + +- (void)share:(ZPCShareMessage *)message { + NSString *target = message.target; + + UIViewController *viewController = [ZPCUtils getTopViewController]; + + if (!target || [target isEqual:@"sheet"]) { + [self showShareSheet:message onView:viewController]; + } else if ([target isEqual:@"sms"]) { + if (![MFMessageComposeViewController canSendText]) { + [ZPCLog error:@"Message services are not available."]; + [self showShareSheet:message onView:viewController]; + return; + } else { + MFMessageComposeViewController *composeVC = [[MFMessageComposeViewController alloc] init]; + composeVC.messageComposeDelegate = self; + + NSString *body = message.text; + + if (message.url) { + body = [body stringByAppendingString:[NSString stringWithFormat:@"\n%@", message.url.absoluteString]]; + } + + if (message.image) { + //Add the image as attachment + NSData *dataImg = UIImagePNGRepresentation(message.image); + [composeVC addAttachmentData:dataImg typeIdentifier:@"public.data" filename:@"Image.png"]; + } + + composeVC.body = body; + composeVC.subject = message.subject; + + // Present the view controller modally. + [viewController presentViewController:composeVC animated:YES completion:nil]; + } + } else if ([target isEqual:@"email"]) { + if (![MFMailComposeViewController canSendMail]) { + [ZPCLog error:@"Mail services are not available."]; + [self showShareSheet:message onView:viewController]; + return; + } else { + MFMailComposeViewController *composeVC = [[MFMailComposeViewController alloc] init]; + composeVC.mailComposeDelegate = self; + + NSString *body = message.text; + + if (message.url) { + body = [body stringByAppendingString:[NSString stringWithFormat:@"\n%@", message.url.absoluteString]]; + } + + [composeVC setSubject:message.subject]; + [composeVC setMessageBody:body isHTML:YES]; + + // Present the view controller modally. + [viewController presentViewController:composeVC animated:YES completion:nil]; + } + } +} + +- (void)showShareSheet:(ZPCShareMessage *)message onView:(UIViewController *)viewController { + NSMutableArray *objectsToShare = [NSMutableArray array]; + + if (message.text) { + [objectsToShare addObject:message.text]; + } + + if (message.image) { + [objectsToShare addObject:message.image]; + } + + if (message.url) { + [objectsToShare addObject:message.url]; + } + + UIActivityViewController *shareController = [[UIActivityViewController alloc] initWithActivityItems:objectsToShare applicationActivities:nil]; + + [viewController presentViewController:shareController animated:YES completion:nil]; +} + +- (void)messageComposeViewController:(MFMessageComposeViewController *)controller + didFinishWithResult:(MessageComposeResult)result { + // Check the result or perform other tasks. // Dismiss the message compose view controller. + [controller dismissViewControllerAnimated:YES completion:nil]; +} + +- (void)mailComposeController:(MFMailComposeViewController *)controller + didFinishWithResult:(MFMailComposeResult)result + error:(NSError *)error { + // Dismiss the mail compose view controller. + [controller dismissViewControllerAnimated:YES completion:nil]; +} + +@end diff --git a/Zapic/Controllers/Zapic.h b/Zapic/Controllers/Zapic.h new file mode 100644 index 0000000..b33ebc2 --- /dev/null +++ b/Zapic/Controllers/Zapic.h @@ -0,0 +1,124 @@ +#import +#import "ZPCChallenge.h" +#import "ZPCCompetition.h" +#import "ZPCErrors.h" +#import "ZPCPlayer.h" +#import "ZPCStatistic.h" + +@interface Zapic : NSObject + +#pragma mark - Page names + +/// The current competition +extern NSString *const ZPCPageCompetition; + +/// The challenges page with a list of all the player's challenges +extern NSString *const ZPCPageChallenges; + +/// The page that allows a player to create a new challenge +extern NSString *const ZPCPageCreateChallenge; + +/// A login and registration page +extern NSString *const ZPCPageLogin; + +/// The user's profile page +extern NSString *const ZPCPageProfile; + +/// The list of all the player's stats +extern NSString *const ZPCPageStats; + +#pragma mark - Login callbacks + +/** + The handler when a player logs in to Zapic. + */ +@property (class, nonatomic, copy, nullable) void (^loginHandler)(ZPCPlayer *); + +/** + The handler when a player logs out of Zapic. + */ +@property (class, nonatomic, copy, nullable) void (^logoutHandler)(ZPCPlayer *); + +/** + Gets the current player. nil if the player is not logged in. + Deprecated in favor of getPlayer:, this will be removed in a future version. + */ +@property (class, nonatomic, copy, nullable, readonly) ZPCPlayer *player DEPRECATED_MSG_ATTRIBUTE("Please use getPlayer:"); + +#pragma mark - Zapic Methods + +/** + Starts Zapic. + @discussion This should be called once from the AppDelegate. + Subsequent calls to this method will be ignored. + */ ++ (void)start; + +/** + Shows the specific Zapic UI page. Please check with https://www.zapic.com/docs + for a complete listing of the available pages. + + @param pageName The page to display. + */ ++ (void)showPage:(NSString *)pageName; + +/** + Show the default Zapic UI page. This should be called + from the Z button on your main menu. + */ ++ (void)showDefaultPage; + +/** + Handles an interaction event. Depending on the event parameters, Zapic may open and show + contextual information related to the specific interaction. + + @param data Dictionary that that contains a "zapic" key. + */ ++ (void)handleInteractionData:(NSDictionary *)data; + +/** + Handles an interaction event. Depending on the event parameters, Zapic may open and show + contextual information related to the specific interaction. + + @param string The zapic data string. + */ ++ (void)handleInteraction:(NSString *)string; + +/** + Handles a gameplay event with 1-N parameters. + + @param parameters Parameters + */ ++ (void)submitEvent:(NSDictionary *)parameters; + +#pragma mark - Data Queries + +/** + Gets the player. + + @param completionHandler The block to be called when the player is retrieved + */ ++ (void)getPlayer:(void (^)(ZPCPlayer *player, NSError *error))completionHandler; + +/** + Gets the list of competitions. + + @param completionHandler The block to be called when the competitions are retrieved + */ ++ (void)getCompetitions:(void (^)(NSArray *competitions, NSError *error))completionHandler; + +/** + Gets the list of statistics. + + @param completionHandler The block to be called when the statistics are retrieved + */ ++ (void)getStatistics:(void (^)(NSArray *statistics, NSError *error))completionHandler; + +/** + Gets the list of challenges. + + @param completionHandler The block to be called when the statistics are retrieved + */ ++ (void)getChallenges:(void (^)(NSArray *challenges, NSError *error))completionHandler; + +@end diff --git a/Zapic/Controllers/Zapic.m b/Zapic/Controllers/Zapic.m new file mode 100644 index 0000000..cc40c36 --- /dev/null +++ b/Zapic/Controllers/Zapic.m @@ -0,0 +1,118 @@ +#import "Zapic.h" +#import +#import "ZPCCore.h" +#import "ZPCLog.h" +#import "ZPCSelectorHelpers.h" + +static ZPCCore *_core; +static void (^_loginHandler)(ZPCPlayer *); +static void (^_logoutHandler)(ZPCPlayer *); + +@implementation Zapic : NSObject + +#pragma mark - Page names + +NSString *const ZPCPageChallenges = @"challenges"; +NSString *const ZPCPageCompetition = @"competition"; +NSString *const ZPCPageCreateChallenge = @"createChallenge"; +NSString *const ZPCPageLogin = @"login"; +NSString *const ZPCPageProfile = @"profile"; +NSString *const ZPCPageStats = @"stats"; + +#pragma mark - Login callbacks + ++ (void (^)(ZPCPlayer *))loginHandler { + return _loginHandler; +} + ++ (void (^)(ZPCPlayer *))logoutHandler { + return _logoutHandler; +} + ++ (void)setLoginHandler:(void (^)(ZPCPlayer *))loginHandler { + _loginHandler = loginHandler; +} + ++ (void)setLogoutHandler:(void (^)(ZPCPlayer *))logoutHandler { + _logoutHandler = logoutHandler; +} + ++ (void)initialize { + if (self == [Zapic self]) { + _core = [[ZPCCore alloc] init]; + + [_core.playerManager addLoginHandler:^(ZPCPlayer *player) { + if (_loginHandler) { + _loginHandler(player); + } + }]; + + [_core.playerManager addLogoutHandler:^(ZPCPlayer *player) { + if (_logoutHandler) { + _logoutHandler(player); + } + }]; + } +} + ++ (ZPCPlayer *)player { + return _core.playerManager.player; +} + +#pragma mark - Zapic Methods + ++ (void)start { + [_core start]; +} + ++ (void)showPage:(NSString *)pageName { + [_core showPage:pageName]; +} + ++ (void)showDefaultPage { + [_core showDefaultPage]; +} + ++ (void)handleInteractionData:(NSDictionary *)data { + if (!data) { + [ZPCLog warn:@"Missing data, unable to handleInteraction"]; + return; + } + + NSString *zapic = [data objectForKey:@"zapic"]; + + [self handleInteraction:zapic]; +} + ++ (void)handleInteraction:(NSString *)string { + if (!string) { + [ZPCLog warn:@"Interaction string must be valid string"]; + return; + } + + [_core submitEvent:ZPCEventTypeInteraction withPayload:string]; +} + ++ (void)submitEvent:(NSDictionary *)parameters { + [_core submitEvent:ZPCEventTypeGameplay withPayload:parameters]; +} + +#pragma mark - Data Queries + ++ (void)getPlayer:(void (^)(ZPCPlayer *, NSError *))completionHandler { + [_core.queryManager getPlayer:completionHandler]; +} + ++ (void)getCompetitions:(void (^)(NSArray *competitions, NSError *error))completionHandler { + [_core.queryManager getCompetitions:completionHandler]; +} + ++ (void)getStatistics:(void (^)(NSArray *statistics, NSError *error))completionHandler { + [_core.queryManager getStatistics:completionHandler]; +} + ++ (void)getChallenges:(void (^)(NSArray *challenges, NSError *error))completionHandler { + [_core.queryManager getChallenges:completionHandler]; +} + +@end diff --git a/Zapic/Extensions.swift b/Zapic/Extensions.swift deleted file mode 100644 index 6b1f1f9..0000000 --- a/Zapic/Extensions.swift +++ /dev/null @@ -1,31 +0,0 @@ -// -// Extensions.swift -// Zapic -// -// Created by Daniel Sarfati on 8/3/17. -// Copyright © 2017 zapic. All rights reserved. -// - -import Foundation - -extension Formatter { - static let iso8601: DateFormatter = { - let formatter = DateFormatter() - formatter.calendar = Calendar(identifier: .iso8601) - formatter.locale = Locale(identifier: "en_US_POSIX") - formatter.timeZone = TimeZone(secondsFromGMT: 0) - formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSXXXXX" - return formatter - }() -} -extension Date { - var iso8601: String { - return Formatter.iso8601.string(from: self) - } -} - -extension String { - var dateFromISO8601: Date? { - return Formatter.iso8601.date(from: self) // "Mar 22, 2017, 10:22 AM" - } -} diff --git a/Zapic/GameCenterHelper.swift b/Zapic/GameCenterHelper.swift deleted file mode 100644 index 51ee535..0000000 --- a/Zapic/GameCenterHelper.swift +++ /dev/null @@ -1,110 +0,0 @@ -// -// GameCenterHelper.swift -// Zapic -// -// Created by Daniel Sarfati on 6/30/17. -// Copyright © 2017 Zapic. All rights reserved. -// - -import Foundation -import GameKit - -class GameCenterHelper { - - /// Generates the current player's GameCenter signature - /// - /// - Parameter completionHandler: Callback when the signature has been generated - static func generateSignature(completionHandler: @escaping ([String: Any]?, Error?) -> Void) { - gameCenterAuthenticate { (player, error) in - guard error == nil else { - ZLog.error("Error authenticaing player") - completionHandler(nil, error) - return - } - - guard let localPlayer = player else { - ZLog.error("Error authenticaing player") - completionHandler(nil, ZapicError.invalidPlayer) - return - } - - //Generate the identity info once the player is authenticated with GameCenter - generateIdentityInfo(player: localPlayer) { (signature, error) in - - guard error == nil else { - completionHandler(nil, error) - return - } - - guard signature != nil else { - completionHandler(nil, ZapicError.invalidAuthSignature) - return - } - - completionHandler(signature, nil) - } - } - } - - private static func gameCenterAuthenticate(completionHandler:@escaping (GKLocalPlayer?, Error?) -> Void) { - - let localPlayer = GKLocalPlayer.localPlayer() - - if localPlayer.isAuthenticated { - completionHandler(localPlayer, nil) - return - } - - localPlayer.authenticateHandler = {(gameCenterVC: UIViewController!, gameCenterError: Error!) -> Void in - - guard gameCenterError == nil else { - completionHandler(nil, gameCenterError) - return - } - - if gameCenterVC != nil { - ZLog.info("Zapic - showing GameCenter View") - - UIApplication.shared.keyWindow?.rootViewController?.present(gameCenterVC, animated: true, completion: nil) - } - - if localPlayer.isAuthenticated { - ZLog.info("Authentication with Game Center success") - - completionHandler(localPlayer, nil) - } - } - } - - private static func generateIdentityInfo(player: GKLocalPlayer, completionHandler: @escaping ([String: Any]?, Error?) -> Void) { - ZLog.info("Generating identity signature") - - player.generateIdentityVerificationSignature { (publicKeyUrl: URL!, signature: Data!, salt: Data!, timestamp: UInt64, error: Error!) -> Void in - - guard error == nil else { - ZLog.error("Error generating verification signature") - completionHandler(nil, error) - return - } - - ZLog.info("Generated identity signature") - - let signatureStr = signature.base64EncodedString() - - let saltStr = salt.base64EncodedString() - - let timestampStr = String(timestamp) - - let dict = ["playerId": player.playerID ?? "", - "displayName": player.alias ?? "", - "bundleId": Bundle.main.bundleIdentifier ?? "", - "publicKeyUrl": publicKeyUrl.absoluteString, - "signature": signatureStr, - "timestamp": timestampStr, - "salt": saltStr - ] - - completionHandler(dict, nil) - } - } -} diff --git a/Zapic/Info.plist b/Zapic/Info.plist index fbe1e6b..12c94a4 100644 --- a/Zapic/Info.plist +++ b/Zapic/Info.plist @@ -3,7 +3,7 @@ CFBundleDevelopmentRegion - en + $(DEVELOPMENT_LANGUAGE) CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 1.0 + 2.0.1 CFBundleVersion $(CURRENT_PROJECT_VERSION) NSPrincipalClass diff --git a/Zapic/InjectedJS.swift b/Zapic/InjectedJS.swift deleted file mode 100644 index d14b849..0000000 --- a/Zapic/InjectedJS.swift +++ /dev/null @@ -1,25 +0,0 @@ -// -// InjectedJS.swift -// Zapic -// -// Created by Daniel Sarfati on 10/18/17. -// Copyright © 2017 zapic. All rights reserved. -// - -import Foundation - -internal let injectedScript = """ -window.zapic = { - environment: 'webview', - version: 1, - onLoaded: (action$, publishAction) => { - window.zapic.dispatch = (action) => { - publishAction(action) - } - - action$.subscribe(action => { - window.webkit.messageHandlers.dispatch.postMessage(action) - }) - } -} -""" diff --git a/Zapic/Models/ZPCAppStatusMessage.h b/Zapic/Models/ZPCAppStatusMessage.h new file mode 100644 index 0000000..a0a03a2 --- /dev/null +++ b/Zapic/Models/ZPCAppStatusMessage.h @@ -0,0 +1,15 @@ +#import + +typedef NS_ENUM(NSInteger, ZPCAppStatus) { + ZPCAppStatusNone, + ZPCAppStatusReady, + ZPCAppStatusFailed, +}; + +@interface ZPCAppStatusMessage : NSObject + +@property (readonly, nonatomic) ZPCAppStatus status; + +- (instancetype)initWithStatus:(ZPCAppStatus)status; + +@end diff --git a/Zapic/Models/ZPCAppStatusMessage.m b/Zapic/Models/ZPCAppStatusMessage.m new file mode 100644 index 0000000..f1a5392 --- /dev/null +++ b/Zapic/Models/ZPCAppStatusMessage.m @@ -0,0 +1,11 @@ +#import "ZPCAppStatusMessage.h" + +@implementation ZPCAppStatusMessage + +- (instancetype)initWithStatus:(ZPCAppStatus)status { + if (self = [super init]) { + _status = status; + } + return self; +} +@end diff --git a/Zapic/Models/ZPCBannerMessage.h b/Zapic/Models/ZPCBannerMessage.h new file mode 100644 index 0000000..dce7705 --- /dev/null +++ b/Zapic/Models/ZPCBannerMessage.h @@ -0,0 +1,20 @@ +#import +#import + +@interface ZPCBannerMessage : NSObject + +@property (readonly, strong) NSString *title; +@property (readonly, strong) NSString *subtitle; +@property (readonly, strong) NSString *data; +@property (readonly, strong) UIImage *icon; + +- (instancetype)initWithTitle:(nonnull NSString *)title + withSubtitle:(nonnull NSString *)subtitle + withData:(nonnull NSString *)data + withIcon:(nonnull UIImage *)icon; + ++ (instancetype)bannerWithTitle:(nonnull NSString *)title + withSubtitle:(nonnull NSString *)subtitle + withData:(nonnull NSString *)data + withIcon:(nonnull UIImage *)icon; +@end diff --git a/Zapic/Models/ZPCBannerMessage.m b/Zapic/Models/ZPCBannerMessage.m new file mode 100644 index 0000000..0e37830 --- /dev/null +++ b/Zapic/Models/ZPCBannerMessage.m @@ -0,0 +1,30 @@ +#import "ZPCBannerMessage.h" + +@implementation ZPCBannerMessage + +- (instancetype)initWithTitle:(NSString *)title + withSubtitle:(nonnull NSString *)subtitle + withData:(nonnull NSString *)data + withIcon:(nonnull UIImage *)icon { + if (self = [super init]) { + _title = title; + _subtitle = subtitle; + _data = data; + _icon = icon; + } + return self; +} + ++ (instancetype)bannerWithTitle:(nonnull NSString *)title + withSubtitle:(nonnull NSString *)subtitle + withData:(nonnull NSString *)data + withIcon:(nonnull UIImage *)icon { + ZPCBannerMessage *message = [[ZPCBannerMessage alloc] initWithTitle:title + withSubtitle:subtitle + withData:data + withIcon:icon]; + + return message; +} + +@end diff --git a/Zapic/Models/ZPCChallenge.h b/Zapic/Models/ZPCChallenge.h new file mode 100644 index 0000000..9e47655 --- /dev/null +++ b/Zapic/Models/ZPCChallenge.h @@ -0,0 +1,104 @@ +#import + +typedef NS_ENUM(NSUInteger, ZPCChallengeStatus) { + ZPCChallengeStatusInvited, + ZPCChallengeStatusIgnored, + ZPCChallengeStatusAccepted, +}; + +@interface ZPCChallenge : NSObject + +/** + The unique id for the statistic. + */ +@property (nonnull, readonly) NSString *identifier; + +/** + The title for the challenge. + */ +@property (nullable, readonly) NSString *title; + +/** + The flag indicating if the challenge is active. + */ +@property (readonly) BOOL active; + +/** + The description. + */ +@property (nullable, readonly) NSString *textDescription; + +/** + When the challenge started. + */ +@property (nullable, readonly) NSDate *start; + +/** + When the competition ends. + */ +@property (nullable, readonly) NSDate *end; + +/** + The current player's score, formatted as defined in the portal. + */ +@property (nullable, readonly) NSString *formattedScore; + +/** + The current player's score. + */ +@property (nullable, readonly) NSNumber *score; + +/** + The developer defined metadata. + */ +@property (nullable, readonly) NSString *metadata; + +/** + The player's rank on the leaderboard (ex. Top 100). If the player + is not on the leaderboard this value will be nil. + */ +@property (nullable, readonly) NSNumber *rank; + +/** + The current player's status. + */ +@property (readonly) ZPCChallengeStatus status; + +/** +The total number of players in the challenge. + */ +@property (nullable, readonly) NSNumber *totalUsers; + +/** + Initialize a new instance from json data + + @param data Data + @return The new instance. + */ +- (instancetype)initWithData:(nonnull NSDictionary *)data; + +/** + Decode a collection of json data. + + @param data Array of json data. + @return The new challenges. + */ ++ (NSArray *)decodeList:(nonnull NSArray *)data; + +/** + Converts the status to a string. + + @param status The status. + @return The string. + */ ++ (NSString *)statusToString:(ZPCChallengeStatus)status; + +/** + Parse a status string + + @param string The string. + @return The status. + */ ++ (ZPCChallengeStatus)stringToStatus:(NSString *)string; + +@end diff --git a/Zapic/Models/ZPCChallenge.m b/Zapic/Models/ZPCChallenge.m new file mode 100644 index 0000000..dede649 --- /dev/null +++ b/Zapic/Models/ZPCChallenge.m @@ -0,0 +1,59 @@ +#import "ZPCChallenge.h" +#import "ZPCLog.h" +#import "ZPCUtils.h" + +@implementation ZPCChallenge + +- (instancetype)initWithData:(nonnull NSDictionary *)data { + if (self = [super init]) { + _identifier = data[@"id"]; + _title = data[@"title"]; + _active = [data[@"active"] boolValue]; + _textDescription = data[@"description"]; + _start = [ZPCUtils parseDateIso:data[@"start"]]; + _end = [ZPCUtils parseDateIso:data[@"end"]]; + _formattedScore = data[@"formattedScore"]; + _score = data[@"score"]; + _status = [ZPCChallenge stringToStatus:data[@"status"]]; + _metadata = data[@"metadata"]; + _rank = data[@"rank"]; + _totalUsers = data[@"totalUsers"]; + } + return self; +} + ++ (NSArray *)decodeList:(nonnull NSArray *)data { + NSMutableArray *challenges = [NSMutableArray arrayWithCapacity:data.count]; + + for (id compData in data) { + ZPCChallenge *challenge = [[ZPCChallenge alloc] initWithData:compData]; + [challenges addObject:challenge]; + } + + return challenges; +} + ++ (NSString *)statusToString:(ZPCChallengeStatus)status { + switch (status) { + case ZPCChallengeStatusIgnored: + return @"ignored"; + case ZPCChallengeStatusAccepted: + return @"ignored"; + default: + return @"invited"; + } +} ++ (ZPCChallengeStatus)stringToStatus:(NSString *)string { + if ([string isEqualToString:@"invited"]) { + return ZPCChallengeStatusInvited; + } else if ([string isEqualToString:@"accepted"]) { + return ZPCChallengeStatusAccepted; + } else if ([string isEqualToString:@"ignored"]) { + return ZPCChallengeStatusIgnored; + } else { + [ZPCLog error:@"Unknown challenge status %@", string]; + return ZPCChallengeStatusInvited; + } +} + +@end diff --git a/Zapic/Models/ZPCCompetition.h b/Zapic/Models/ZPCCompetition.h new file mode 100644 index 0000000..7ea0357 --- /dev/null +++ b/Zapic/Models/ZPCCompetition.h @@ -0,0 +1,121 @@ +#import + +typedef NS_ENUM(NSUInteger, ZPCCompetitionStatus) { + ZPCCompetitionStatusInvited, + ZPCCompetitionStatusIgnored, + ZPCCompetitionStatusAccepted, +}; + +@interface ZPCCompetition : NSObject + +#pragma mark - Competition Definition + +/** + The unique id for the competition. + */ +@property (nonnull, readonly) NSString *identifier; + +/** + The title for the competition. + */ +@property (nullable, readonly) NSString *title; + +/** + The description for the competition. + */ +@property (nullable, readonly) NSString *text; + +/** + The developer defined metadata for this competition. + */ +@property (nullable, readonly) NSString *metadata; + +/** + Flag indicating if the competition is currently active. + */ +@property (readonly) BOOL active; + +/** + When the competition starts. + */ +@property (nullable, readonly) NSDate *start; + +/** + When the competition ends. + */ +@property (nullable, readonly) NSDate *end; + +/** + The total number of users that have joined the competition. + */ +@property (nullable, readonly) NSNumber *totalUsers; + +#pragma mark - Player data + +/** + The current player's score, formatted as defined in the portal. + */ +@property (readonly) ZPCCompetitionStatus status; + +/** + The current player's score, formatted as defined in the portal. + */ +@property (nullable, readonly) NSString *formattedScore; + +/** + The current player's score. + */ +@property (nullable, readonly) NSNumber *score; + +/** + The player's rank on the leaderboard (ex. Top 100). If the player + is not on the leaderboard this value will be nil. + */ +@property (nullable, readonly) NSNumber *leaderboardRank; + +/** + The player's rank in their league. If the player + is not in a league yet this value will be nil. + */ +@property (nullable, readonly) NSNumber *leagueRank; + +- (instancetype)initWithId:(nonnull NSString *)identifier + title:(nullable NSString *)title + text:(nullable NSString *)text + metadata:(nullable NSString *)metadata + active:(BOOL)active + start:(nullable NSDate *)start + end:(nullable NSDate *)end + totalUsers:(nullable NSNumber *)totalUsers + status:(ZPCCompetitionStatus)status + formattedScore:(nullable NSString *)formattedScore + score:(nullable NSNumber *)score + leaderboardRank:(nullable NSNumber *)leaderboardRank + leagueRank:(nullable NSNumber *)leagueRank; + +- (instancetype)initWithData:(NSDictionary *)data; + +/** + Decodes a collection of competitions + + @param data Array of competition data + @return Array of competitions + */ ++ (NSArray *)decodeList:(NSArray *)data; + +/** + Converts the status to a string. + + @param status The status. + @return The string. + */ ++ (NSString *)statusToString:(ZPCCompetitionStatus)status; + +/** + Parse a status string + + @param string The string. + @return The status. + */ ++ (ZPCCompetitionStatus)stringToStatus:(NSString *)string; +@end diff --git a/Zapic/Models/ZPCCompetition.m b/Zapic/Models/ZPCCompetition.m new file mode 100644 index 0000000..41a66b7 --- /dev/null +++ b/Zapic/Models/ZPCCompetition.m @@ -0,0 +1,88 @@ +#import "ZPCCompetition.h" +#import "ZPCLog.h" +#import "ZPCUtils.h" + +@implementation ZPCCompetition + +- (instancetype)initWithId:(nonnull NSString *)identifier + title:(nullable NSString *)title + text:(nullable NSString *)text + metadata:(nullable NSString *)metadata + active:(BOOL)active + start:(nullable NSDate *)start + end:(nullable NSDate *)end + totalUsers:(nullable NSNumber *)totalUsers + status:(ZPCCompetitionStatus)status + formattedScore:(nullable NSString *)formattedScore + score:(nullable NSNumber *)score + leaderboardRank:(nullable NSNumber *)leaderboardRank + leagueRank:(nullable NSNumber *)leagueRank { + if (self = [super init]) { + _identifier = identifier; + _title = title; + _text = text; + _metadata = metadata; + _active = active; + _start = start; + _end = end; + _totalUsers = totalUsers; + _status = status; + _formattedScore = formattedScore; + _score = score; + _leaderboardRank = leaderboardRank; + _leagueRank = leagueRank; + } + return self; +} + +- (instancetype)initWithData:(NSDictionary *)data { + return [self initWithId:data[@"id"] + title:data[@"title"] + text:data[@"description"] + metadata:data[@"metadata"] + active:[data[@"active"] boolValue] + start:[ZPCUtils parseDateIso:data[@"start"]] + end:[ZPCUtils parseDateIso:data[@"end"]] + totalUsers:data[@"totalUsers"] + status:[ZPCCompetition stringToStatus:data[@"status"]] + formattedScore:data[@"formattedScore"] + score:data[@"score"] + leaderboardRank:data[@"leaderboardRank"] + leagueRank:data[@"leagueRank"]]; +} + ++ (NSArray *)decodeList:(NSArray *)data { + NSMutableArray *competitions = [NSMutableArray arrayWithCapacity:data.count]; + + for (id compData in data) { + ZPCCompetition *competition = [[ZPCCompetition alloc] initWithData:compData]; + [competitions addObject:competition]; + } + + return competitions; +} + ++ (NSString *)statusToString:(ZPCCompetitionStatus)status { + switch (status) { + case ZPCCompetitionStatusIgnored: + return @"ignored"; + case ZPCCompetitionStatusAccepted: + return @"ignored"; + default: + return @"invited"; + } +} ++ (ZPCCompetitionStatus)stringToStatus:(NSString *)string { + if ([string isEqualToString:@"invited"]) { + return ZPCCompetitionStatusInvited; + } else if ([string isEqualToString:@"accepted"]) { + return ZPCCompetitionStatusAccepted; + } else if ([string isEqualToString:@"ignored"]) { + return ZPCCompetitionStatusIgnored; + } else { + [ZPCLog error:@"Unknown competition status %@", string]; + return ZPCCompetitionStatusInvited; + } +} + +@end diff --git a/Zapic/Models/ZPCPlayer.h b/Zapic/Models/ZPCPlayer.h new file mode 100644 index 0000000..6a0f799 --- /dev/null +++ b/Zapic/Models/ZPCPlayer.h @@ -0,0 +1,9 @@ +#import + +@interface ZPCPlayer : NSObject +@property (readonly) NSString *identifier; +@property (readonly) NSString *notificationToken; +@property (readonly) NSString *name; +@property (readonly) NSURL *iconUrl; +- (instancetype)initWithData:(NSDictionary *)data; +@end diff --git a/Zapic/Models/ZPCPlayer.m b/Zapic/Models/ZPCPlayer.m new file mode 100644 index 0000000..1daed43 --- /dev/null +++ b/Zapic/Models/ZPCPlayer.m @@ -0,0 +1,15 @@ +#import "ZPCPlayer.h" + +@implementation ZPCPlayer + +- (instancetype)initWithData:(NSDictionary *)data { + if (self = [super init]) { + _identifier = data[@"id"]; + _name = data[@"name"]; + _notificationToken = data[@"notificationToken"]; + _iconUrl = [NSURL URLWithString:data[@"iconUrl"]]; + } + return self; +} + +@end diff --git a/Zapic/Models/ZPCShareMessage.h b/Zapic/Models/ZPCShareMessage.h new file mode 100644 index 0000000..2d601cc --- /dev/null +++ b/Zapic/Models/ZPCShareMessage.h @@ -0,0 +1,16 @@ +#import +#import + +@interface ZPCShareMessage : NSObject +@property (readonly, strong) NSString *text; +@property (readonly, strong) NSString *target; +@property (readonly, strong) NSString *subject; +@property (readonly, strong) NSURL *url; +@property (readonly, strong) UIImage *image; + +- (instancetype)initWithText:(nullable NSString *)text + target:(nullable NSString *)target + subject:(nullable NSString *)subject + withImage:(nullable UIImage *)image + withURL:(nullable NSURL *)url; +@end diff --git a/Zapic/Models/ZPCShareMessage.m b/Zapic/Models/ZPCShareMessage.m new file mode 100644 index 0000000..a326496 --- /dev/null +++ b/Zapic/Models/ZPCShareMessage.m @@ -0,0 +1,19 @@ +#import "ZPCShareMessage.h" + +@implementation ZPCShareMessage + +- (instancetype)initWithText:(nullable NSString *)text + target:(nullable NSString *)target + subject:(nullable NSString *)subject + withImage:(nullable UIImage *)image + withURL:(nullable NSURL *)url { + if (self = [super init]) { + _text = text; + _target = target; + _subject = subject; + _image = image; + _url = url; + } + return self; +} +@end diff --git a/Zapic/Models/ZPCStatistic.h b/Zapic/Models/ZPCStatistic.h new file mode 100644 index 0000000..acda191 --- /dev/null +++ b/Zapic/Models/ZPCStatistic.h @@ -0,0 +1,50 @@ +#import + +@interface ZPCStatistic : NSObject +/** + The unique id for the statistic. + */ +@property (nonnull, readonly) NSString *identifier; + +/** + The title for the statistic. + */ +@property (nullable, readonly) NSString *title; + +/** + The developer defined metadata for this statistic. + */ +@property (nullable, readonly) NSString *metadata; + +/** + The current player's score, formatted as defined in the portal. + */ +@property (nullable, readonly) NSString *formattedScore; + +/** + The current player's score. + */ +@property (nullable, readonly) NSNumber *score; + +/** + The current player's percentile rank + */ +@property (nullable, readonly) NSNumber *percentile; + +/** + Initialize a new statistic with json data + + @param data Data + @return The new instance. + */ +- (instancetype)initWithData:(nonnull NSDictionary *)data; + +/** + Decode a collection of json data. + + @param data Array of statistic data. + @return The new statistics. + */ ++ (NSArray *)decodeList:(nonnull NSArray *)data; + +@end diff --git a/Zapic/Models/ZPCStatistic.m b/Zapic/Models/ZPCStatistic.m new file mode 100644 index 0000000..3fa5ae2 --- /dev/null +++ b/Zapic/Models/ZPCStatistic.m @@ -0,0 +1,28 @@ +#import "ZPCStatistic.h" + +@implementation ZPCStatistic + +- (instancetype)initWithData:(nonnull NSDictionary *)data { + if (self = [super init]) { + _identifier = data[@"id"]; + _title = data[@"title"]; + _metadata = data[@"metadata"]; + _formattedScore = data[@"formattedScore"]; + _score = data[@"score"]; + _percentile = data[@"percentile"]; + } + return self; +} + ++ (NSArray *)decodeList:(nonnull NSArray *)data { + NSMutableArray *stats = [NSMutableArray arrayWithCapacity:data.count]; + + for (id compData in data) { + ZPCStatistic *stat = [[ZPCStatistic alloc] initWithData:compData]; + [stats addObject:stat]; + } + + return stats; +} + +@end diff --git a/Zapic/Queue.swift b/Zapic/Queue.swift deleted file mode 100644 index 02bd0ae..0000000 --- a/Zapic/Queue.swift +++ /dev/null @@ -1,49 +0,0 @@ -// -// Queue.swift -// Zapic -// -// Created by Daniel Sarfati on 8/3/17. -// Copyright © 2017 zapic. All rights reserved. -// - -import Foundation - -struct Queue { - private var array = [T?]() - private var head = 0 - - public var isEmpty: Bool { - return count == 0 - } - - public var count: Int { - return array.count - head - } - - public mutating func enqueue(_ element: T) { - array.append(element) - } - - public mutating func dequeue() -> T? { - guard head < array.count, let element = array[head] else { return nil } - - array[head] = nil - head += 1 - - let percentage = Double(head)/Double(array.count) - if array.count > 50 && percentage > 0.25 { - array.removeFirst(head) - head = 0 - } - - return element - } - - public var front: T? { - if isEmpty { - return nil - } else { - return array[head] - } - } -} diff --git a/Zapic/StringExtensions.swift b/Zapic/StringExtensions.swift deleted file mode 100644 index a94e616..0000000 --- a/Zapic/StringExtensions.swift +++ /dev/null @@ -1,67 +0,0 @@ -// -// StringExtensions.swift -// Zapic -// -// Created by Daniel Sarfati on 10/27/17. -// Copyright © 2017 zapic. All rights reserved. -// - -import Foundation - -extension String { - - func asUUID() -> UUID? { - return UUID(uuidString: self) - } - - func indicesOf(string: String) -> [Int] { - var indices = [Int]() - var searchStartIndex = self.startIndex - - while searchStartIndex < self.endIndex, - let range = self.range(of: string, range: searchStartIndex.. String { - - var outString = "" - var argIndex = 0 - var prevChar: Character = " " - var index = 0 - var skip = true - - for char in self { - - if char == "s" && prevChar == "%"{ - let arg = args[argIndex] - argIndex += 1 - - outString.append(arg) - - skip = true - - } else if index == self.count-1 { - outString.append(prevChar) - outString.append(char) - return outString - } else { - if !skip { - outString.append(prevChar) - } - skip = false - prevChar = char - } - - index += 1 - } - - return outString - } -} diff --git a/Zapic/Utilities/ZPCImageUtils.h b/Zapic/Utilities/ZPCImageUtils.h new file mode 100644 index 0000000..9cc83d6 --- /dev/null +++ b/Zapic/Utilities/ZPCImageUtils.h @@ -0,0 +1,7 @@ +#import +#import + +@interface ZPCImageUtils : NSObject ++ (UIImage *)getZapicLogo; ++ (UIImage *)decodeBase64ToImage:(NSString *)strEncodeData; +@end diff --git a/Zapic/Utilities/ZPCImageUtils.m b/Zapic/Utilities/ZPCImageUtils.m new file mode 100644 index 0000000..b56b966 --- /dev/null +++ b/Zapic/Utilities/ZPCImageUtils.m @@ -0,0 +1,21 @@ +#import "ZPCImageUtils.h" + +@implementation ZPCImageUtils + ++ (UIImage *)decodeBase64ToImage:(NSString *)strEncodeData { + if (!strEncodeData.length) { + return nil; + } + NSData *data = [[NSData alloc] initWithBase64EncodedString:strEncodeData options:NSDataBase64DecodingIgnoreUnknownCharacters]; + return [UIImage imageWithData:data]; +} + ++ (UIImage *)getZapicLogo { + NSString *img64 = @"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"; + + UIImage *logo = [ZPCImageUtils decodeBase64ToImage:img64]; + + return logo; +} + +@end diff --git a/Zapic/Utilities/ZPCInjectedJS.h b/Zapic/Utilities/ZPCInjectedJS.h new file mode 100644 index 0000000..e06942e --- /dev/null +++ b/Zapic/Utilities/ZPCInjectedJS.h @@ -0,0 +1,5 @@ +#import + +@interface ZPCInjectedJS : NSObject ++ (NSString *)getInjectedScript; +@end diff --git a/Zapic/Utilities/ZPCInjectedJS.m b/Zapic/Utilities/ZPCInjectedJS.m new file mode 100644 index 0000000..9ccc6a1 --- /dev/null +++ b/Zapic/Utilities/ZPCInjectedJS.m @@ -0,0 +1,78 @@ +#import "ZPCInjectedJS.h" +#import +#import + +@implementation ZPCInjectedJS + ++ (NSString *)getInjectedScript { + //Gets the info to be injected + NSString *sdkVersion = @"2.0.1"; + NSDictionary *appInfo = NSBundle.mainBundle.infoDictionary; + NSString *appVersion = appInfo[@"CFBundleShortVersionString"]; + NSString *appBuild = appInfo[@"CFBundleVersion"]; + NSString *bundleId = NSBundle.mainBundle.bundleIdentifier; + NSString *deviceId = UIDevice.currentDevice.identifierForVendor.UUIDString; + NSString *iosVersion = UIDevice.currentDevice.systemVersion; + NSString *installId = [self installId]; + const int sdkApiVersion = 4; + const int loadTimeout = 10000; + + NSString *adId = @""; + + if (ASIdentifierManager.sharedManager.isAdvertisingTrackingEnabled) { + adId = ASIdentifierManager.sharedManager.advertisingIdentifier.UUIDString; + } + + return [NSString stringWithFormat: + @"window.iosWebViewWatchdog = window.setTimeout(function () {" + @" window.webkit.messageHandlers.dispatch.postMessage({\"type\":\"APP_FAILED\"});" + "}, %d);" + "window.zapic = {" + "environment: 'webview'," + "version: %d," + "iosVersion: '%@'," + "bundleId: '%@'," + "sdkVersion: '%@'," + "installId: '%@'," + "deviceId: '%@'," + "appVersion: '%@'," + "appBuild: '%@'," + "adId:'%@'," + "onLoaded: function(action$, publishAction) {" + "window.clearTimeout(window.iosWebViewWatchdog);" + "delete window.iosWebViewWatchdog;" + "window.zapic.dispatch = function(action) {" + "publishAction(action)" + "};" + "action$.subscribe(function(action) {" + "window.webkit.messageHandlers.dispatch.postMessage(action)" + "});" + "}" + "}", + loadTimeout, + sdkApiVersion, + iosVersion, + bundleId, + sdkVersion, + installId, + deviceId, + appBuild, + appVersion, + adId]; +} + ++ (NSString *)installId { + NSString *const installKey = @"zapic-install-id"; + NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults]; + NSString *installId = [userDefaults stringForKey:installKey]; + + //If an install id was not found, create one + if (!installId) { + installId = [NSUUID UUID].UUIDString; + [userDefaults setObject:installId forKey:installKey]; + } + + return installId; +} + +@end diff --git a/Zapic/Utilities/ZPCLog.h b/Zapic/Utilities/ZPCLog.h new file mode 100644 index 0000000..2b76f6b --- /dev/null +++ b/Zapic/Utilities/ZPCLog.h @@ -0,0 +1,11 @@ +#import + +@interface ZPCLog : NSObject + +extern bool isEnabled; + ++ (void)error:(NSString *)message, ... NS_FORMAT_FUNCTION(1, 2); ++ (void)warn:(NSString *)message, ... NS_FORMAT_FUNCTION(1, 2); ++ (void)info:(NSString *)format, ... NS_FORMAT_FUNCTION(1, 2); + +@end diff --git a/Zapic/Utilities/ZPCLog.m b/Zapic/Utilities/ZPCLog.m new file mode 100644 index 0000000..a61a16d --- /dev/null +++ b/Zapic/Utilities/ZPCLog.m @@ -0,0 +1,42 @@ +#import "ZPCLog.h" + +@implementation ZPCLog + ++ (void)error:(NSString *)message, ... NS_FORMAT_FUNCTION(1, 2) { + va_list args; + va_start(args, message); + + NSString *s = [[NSString alloc] initWithFormat:message arguments:args]; + + [self writeLog:s withSymbol:@"ERROR"]; + + va_end(args); +} + ++ (void)warn:(NSString *)message, ... NS_FORMAT_FUNCTION(1, 2) { + va_list args; + va_start(args, message); + + NSString *s = [[NSString alloc] initWithFormat:message arguments:args]; + + [self writeLog:s withSymbol:@"WARN"]; + + va_end(args); +} + ++ (void)info:(NSString *)message, ... NS_FORMAT_FUNCTION(1, 2) { + va_list args; + va_start(args, message); + + NSString *s = [[NSString alloc] initWithFormat:message arguments:args]; + + [self writeLog:s withSymbol:@"INFO"]; + + va_end(args); +} + ++ (void)writeLog:(NSString *)message withSymbol:(NSString *)symbol { + NSLog(@"[Zapic][%@]-%@", symbol, message); +} + +@end diff --git a/Zapic/Utilities/ZPCQueue.h b/Zapic/Utilities/ZPCQueue.h new file mode 100644 index 0000000..f42d685 --- /dev/null +++ b/Zapic/Utilities/ZPCQueue.h @@ -0,0 +1,9 @@ +#import + +@interface ZPCQueue : NSObject +@property (strong) NSMutableArray *data; +@property (readonly) NSUInteger count; +- (void)enqueueMany:(NSArray *)source; +- (void)enqueue:(id)anObject; +- (id)dequeue; +@end diff --git a/Zapic/Utilities/ZPCQueue.m b/Zapic/Utilities/ZPCQueue.m new file mode 100644 index 0000000..092a9af --- /dev/null +++ b/Zapic/Utilities/ZPCQueue.m @@ -0,0 +1,32 @@ +#import "ZPCQueue.h" + +@implementation ZPCQueue + +- (NSUInteger)count { + return _data.count; +} + +- (instancetype)init { + if (self = [super init]) { + _data = [[NSMutableArray alloc] init]; + } + return self; +} + +- (void)enqueueMany:(NSArray *)source { + [self.data addObjectsFromArray:source]; +} + +- (void)enqueue:(id)anObject { + [self.data addObject:anObject]; +} + +- (id)dequeue { + id headObject = [self.data objectAtIndex:0]; + if (headObject != nil) { + [self.data removeObjectAtIndex:0]; + } + return headObject; +} + +@end diff --git a/Zapic/Utilities/ZPCSelectorHelpers.h b/Zapic/Utilities/ZPCSelectorHelpers.h new file mode 100644 index 0000000..22540a0 --- /dev/null +++ b/Zapic/Utilities/ZPCSelectorHelpers.h @@ -0,0 +1,7 @@ +#import + +BOOL checkIfInstanceOverridesSelector(Class instance, SEL selector); +Class getClassWithProtocolInHierarchy(Class searchClass, Protocol *protocolToFind); +NSArray *ClassGetSubclasses(Class parentClass); +void injectToProperClass(SEL newSel, SEL makeLikeSel, NSArray *delegateSubclasses, Class myClass, Class delegateClass); +BOOL injectSelector(Class newClass, SEL newSel, Class addToClass, SEL makeLikeSel); diff --git a/Zapic/Utilities/ZPCSelectorHelpers.m b/Zapic/Utilities/ZPCSelectorHelpers.m new file mode 100644 index 0000000..36793c3 --- /dev/null +++ b/Zapic/Utilities/ZPCSelectorHelpers.m @@ -0,0 +1,81 @@ +#import "ZPCSelectorHelpers.h" +#import + +BOOL checkIfInstanceOverridesSelector(Class instance, SEL selector) { + Class instSuperClass = [instance superclass]; + return [instance instanceMethodForSelector:selector] != [instSuperClass instanceMethodForSelector:selector]; +} + +Class getClassWithProtocolInHierarchy(Class searchClass, Protocol *protocolToFind) { + if (!class_conformsToProtocol(searchClass, protocolToFind)) { + if ([searchClass superclass] == nil) + return nil; + Class foundClass = getClassWithProtocolInHierarchy([searchClass superclass], protocolToFind); + if (foundClass) + return foundClass; + return searchClass; + } + return searchClass; +} + +BOOL injectSelector(Class newClass, SEL newSel, Class addToClass, SEL makeLikeSel) { + Method newMeth = class_getInstanceMethod(newClass, newSel); + IMP imp = method_getImplementation(newMeth); + + const char *methodTypeEncoding = method_getTypeEncoding(newMeth); + // Keep - class_getInstanceMethod for existing detection. + // class_addMethod will successfuly add if the addToClass was loaded twice into the runtime. + BOOL existing = class_getInstanceMethod(addToClass, makeLikeSel) != NULL; + + if (existing) { + class_addMethod(addToClass, newSel, imp, methodTypeEncoding); + newMeth = class_getInstanceMethod(addToClass, newSel); + Method orgMeth = class_getInstanceMethod(addToClass, makeLikeSel); + method_exchangeImplementations(orgMeth, newMeth); + } else + class_addMethod(addToClass, makeLikeSel, imp, methodTypeEncoding); + + return existing; +} + +// Try to find out which class to inject to +void injectToProperClass(SEL newSel, SEL makeLikeSel, NSArray *delegateSubclasses, Class myClass, Class delegateClass) { + // Find out if we should inject in delegateClass or one of its subclasses. + // CANNOT use the respondsToSelector method as it returns TRUE to both implementing and inheriting a method + // We need to make sure the class actually implements the method (overrides) and not inherits it to properly perform the call + // Start with subclasses then the delegateClass + + for (Class subclass in delegateSubclasses) { + if (checkIfInstanceOverridesSelector(subclass, makeLikeSel)) { + injectSelector(myClass, newSel, subclass, makeLikeSel); + return; + } + } + + // No subclass overrides the method, try to inject in delegate class + injectSelector(myClass, newSel, delegateClass, makeLikeSel); +} + +NSArray *ClassGetSubclasses(Class parentClass) { + int numClasses = objc_getClassList(NULL, 0); + Class *classes = (Class *)malloc(sizeof(Class) * numClasses); + + objc_getClassList(classes, numClasses); + + NSMutableArray *result = [NSMutableArray array]; + + for (NSInteger i = 0; i < numClasses; i++) { + Class superClass = classes[i]; + + while (superClass && superClass != parentClass) { + superClass = class_getSuperclass(superClass); + } + + if (superClass) + [result addObject:classes[i]]; + } + + free(classes); + + return result; +} diff --git a/Zapic/Utilities/ZPCStorage.h b/Zapic/Utilities/ZPCStorage.h new file mode 100644 index 0000000..c080852 --- /dev/null +++ b/Zapic/Utilities/ZPCStorage.h @@ -0,0 +1,7 @@ +#import + +@interface ZPCStorage : NSObject +- (void)store:(NSArray *)objects; +- (NSArray *)retrieve; +- (void)clear; +@end diff --git a/Zapic/Utilities/ZPCStorage.m b/Zapic/Utilities/ZPCStorage.m new file mode 100644 index 0000000..d1c4252 --- /dev/null +++ b/Zapic/Utilities/ZPCStorage.m @@ -0,0 +1,52 @@ +#import "ZPCStorage.h" +#import "ZPCLog.h" + +@implementation ZPCStorage + +NSString *const filename = @"zapic-events.json"; + +- (void)store:(NSArray *)objects { + NSError *error = nil; + NSData *json = [NSJSONSerialization dataWithJSONObject:objects options:kNilOptions error:&error]; + + NSURL *url = [[self getUrl] URLByAppendingPathComponent:filename]; + + //Delete the existing file + if ([NSFileManager.defaultManager fileExistsAtPath:url.path]) { + [NSFileManager.defaultManager removeItemAtURL:url error:&error]; + } + + [NSFileManager.defaultManager createFileAtPath:url.path contents:json attributes:nil]; +} + +- (NSArray *)retrieve { + NSURL *url = [[self getUrl] URLByAppendingPathComponent:filename]; + + if (![NSFileManager.defaultManager fileExistsAtPath:url.path]) { + return nil; + } + NSError *error = nil; + NSData *data = [NSFileManager.defaultManager contentsAtPath:url.path]; + NSArray *json = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&error]; + + if (![NSFileManager.defaultManager removeItemAtPath:url.path error:&error]) { + [ZPCLog warn:@"Unable to remove file at %@", url.path]; + } + return json; +} + +- (void)clear { + NSURL *url = [[self getUrl] URLByAppendingPathComponent:filename]; + NSError *error = nil; + if (![NSFileManager.defaultManager removeItemAtPath:url.path error:&error]) { + [ZPCLog warn:@"Unable to clear file at %@", url.path]; + } +} + +- (NSURL *)getUrl { + NSString *documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject]; + + return [NSURL URLWithString:documentsPath]; +} + +@end diff --git a/Zapic/Utilities/ZPCUtils.h b/Zapic/Utilities/ZPCUtils.h new file mode 100644 index 0000000..c5c28b3 --- /dev/null +++ b/Zapic/Utilities/ZPCUtils.h @@ -0,0 +1,12 @@ +#import +#import + +@interface ZPCUtils : NSObject ++ (UIViewController *)getTopViewController; ++ (UIView *)getTopView; ++ (NSString *)getIsoNow; ++ (NSString *)toIsoDate:(NSDate *)date; ++ (NSDate *)parseDateIso:(NSString *)dateString; ++ (void)cleanDictionary:(NSDictionary *)dict; ++ (int)notchSize; +@end diff --git a/Zapic/Utilities/ZPCUtils.m b/Zapic/Utilities/ZPCUtils.m new file mode 100644 index 0000000..41af7c2 --- /dev/null +++ b/Zapic/Utilities/ZPCUtils.m @@ -0,0 +1,80 @@ +#import "ZPCUtils.h" +#import "ZPCLog.h" + +@implementation ZPCUtils + ++ (UIViewController *)getTopViewController { + UIViewController *root = UIApplication.sharedApplication.delegate.window.rootViewController; + + if (root.presentedViewController) { + return root.presentedViewController; + } + return root; +} + ++ (UIView *)getTopView { + return [self getTopViewController].view; +} + ++ (NSDateFormatter *)getIsoFormatter { + NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; + NSLocale *enUSPOSIXLocale = [NSLocale localeWithLocaleIdentifier:@"en_US_POSIX"]; + [dateFormatter setLocale:enUSPOSIXLocale]; + [dateFormatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ssZZZZZ"]; + return dateFormatter; +} + ++ (NSString *)getIsoNow { + NSDate *now = [NSDate date]; + return [ZPCUtils toIsoDate:now]; +} + ++ (NSString *)toIsoDate:(NSDate *)date { + NSDateFormatter *dateFormatter = [ZPCUtils getIsoFormatter]; + return [dateFormatter stringFromDate:date]; +} + ++ (NSDate *)parseDateIso:(NSString *)dateString { + NSDateFormatter *dateFormatter = [ZPCUtils getIsoFormatter]; + return [dateFormatter dateFromString:dateString]; +} + ++ (void)cleanDictionary:(NSDictionary *)dict { + NSMutableArray *keys = [NSMutableArray array]; + for (id key in dict) { + id value = dict[key]; + + if ([value isKindOfClass:[NSDictionary class]]) { + [ZPCUtils cleanDictionary:value]; + } else if ([value isEqual:[NSNull null]]) { + [keys addObject:key]; + } else if ([value isKindOfClass:[NSMutableArray class]]) { + [ZPCUtils cleanArray:value]; + } + } + + for (id key in keys) { + [dict setValue:nil forKey:key]; + } +} + ++ (void)cleanArray:(NSMutableArray *)array { + for (id value in array) { + if ([value isKindOfClass:[NSDictionary class]]) { + [ZPCUtils cleanDictionary:value]; + } else if ([value isKindOfClass:[NSArray class]]) { + [ZPCUtils cleanArray:value]; + } + } +} + ++ (int)notchSize { + if (@available(iOS 11.0, *)) { + UIWindow *window = UIApplication.sharedApplication.keyWindow; + return window.safeAreaInsets.top; + } else { + return 0; + } +} + +@end diff --git a/Zapic/Views/ColorBar.swift b/Zapic/Views/ColorBar.swift deleted file mode 100644 index 4c6bf39..0000000 --- a/Zapic/Views/ColorBar.swift +++ /dev/null @@ -1,56 +0,0 @@ -// -// ColorBar.swift -// Zapic -// -// Created by Daniel Sarfati on 8/3/17. -// Copyright © 2017 zapic. All rights reserved. -// - -import Foundation - -class ColorBar: UIView { - - private let gradient = CAGradientLayer () - - init() { - super.init(frame: CGRect.zero) - backgroundColor = .red - self.layer.insertSublayer(gradient, at: 0) - gradient.colors = [ZapicColors.blue.cgColor, ZapicColors.green.cgColor, ZapicColors.blue.cgColor, ZapicColors.green.cgColor] - gradient.startPoint = CGPoint(x: 1.1, y: 0) - gradient.endPoint = CGPoint.zero - gradient.locations = [-2.0, -1.0, 0.0, 1] - } - - required init?(coder aDecoder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - private func animate() { - let animation = CABasicAnimation(keyPath: "locations") - animation.toValue = [0.0, 1.0, 2.0, 3.0] - animation.duration = 3.0 - animation.repeatCount = Float.infinity - gradient.add(animation, forKey: nil) - } - - override func layoutSubviews() { - gradient.frame.size.width = self.frame.width - gradient.frame.size.height = self.frame.height - } - - override func didMoveToWindow() { - animate() - } - - override func didMoveToSuperview() { - super.didMoveToSuperview() - guard let superview = superview else {return} - - self.translatesAutoresizingMaskIntoConstraints = false - self.topAnchor.constraint(equalTo: superview.topAnchor).isActive = true - self.heightAnchor.constraint(equalToConstant: 6).isActive = true - self.leadingAnchor.constraint(equalTo: superview.leadingAnchor).isActive = true - self.trailingAnchor.constraint(equalTo: superview.trailingAnchor).isActive = true - } -} diff --git a/Zapic/Views/LoadingView.swift b/Zapic/Views/LoadingView.swift deleted file mode 100644 index 13830de..0000000 --- a/Zapic/Views/LoadingView.swift +++ /dev/null @@ -1,21 +0,0 @@ -// -// LoadingView.swift -// Zapic -// -// Created by Daniel Sarfati on 7/10/17. -// Copyright © 2017 Zapic. All rights reserved. -// - -import Foundation -import WebKit - -class LoadingView: ZapicView { - - init() { - super.init(text: "Loading...") - } - - required init?(coder aDecoder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } -} diff --git a/Zapic/Views/OfflineView.swift b/Zapic/Views/OfflineView.swift deleted file mode 100644 index b138b60..0000000 --- a/Zapic/Views/OfflineView.swift +++ /dev/null @@ -1,37 +0,0 @@ -// -// OfflineView.swift -// Zapic -// -// Created by Daniel Sarfati on 7/11/17. -// Copyright © 2017 Zapic. All rights reserved. -// - -import Foundation -import WebKit - -class OfflineView: ZapicView { - - init() { - super.init(text: "Could Not Connect") - - //Offline text - let details = UILabel() - details.font = UIFont.systemFont(ofSize: 18) - details.text = "Please check your network connection and try again" - details.textAlignment = .center - details.lineBreakMode = .byWordWrapping - details.numberOfLines = 0 //Unlimited number of lines - details.textColor = ZapicColors.secondaryText - - addSubview(details) - - details.translatesAutoresizingMaskIntoConstraints = false - details.leftAnchor.constraint(equalTo: self.leftAnchor, constant: 50).isActive = true - details.rightAnchor.constraint(equalTo: self.rightAnchor, constant: -50).isActive = true - details.topAnchor.constraint(equalTo: self.title.bottomAnchor, constant: 10).isActive = true - } - - required init?(coder aDecoder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } -} diff --git a/Zapic/Views/ZPCBackgroundView.h b/Zapic/Views/ZPCBackgroundView.h new file mode 100644 index 0000000..407b1cd --- /dev/null +++ b/Zapic/Views/ZPCBackgroundView.h @@ -0,0 +1,5 @@ +#import + +@interface ZPCBackgroundView : UIView +- (void)placeViewToBackground; +@end diff --git a/Zapic/Views/ZPCBackgroundView.m b/Zapic/Views/ZPCBackgroundView.m new file mode 100644 index 0000000..edfad79 --- /dev/null +++ b/Zapic/Views/ZPCBackgroundView.m @@ -0,0 +1,49 @@ +#import "ZPCBackgroundView.h" + +@interface ZPCBackgroundView () +@property (nonatomic, assign) BOOL needsPlacement; +@property (nonatomic, copy) NSArray<__kindof UIView *> *subviews; +@end + +@implementation ZPCBackgroundView + +- (instancetype)initWithFrame:(CGRect)frame { + self.needsPlacement = false; + self.subviews = [[NSArray alloc] init]; + self.accessibilityElementsHidden = true; + return [super initWithFrame:frame]; +} + +- (void)willMoveToSuperview:(UIView *)newSuperview { + [super willMoveToSuperview:newSuperview]; + if (!newSuperview) { + self.needsPlacement = true; + } +} + +- (void)didMoveToSuperview { + [super didMoveToSuperview]; + if (self.needsPlacement) { + [self placeViewToBackground]; + } +} + +- (void)placeViewToBackground { + if (!self.superview) { + [self setHidden:YES]; + [[UIApplication sharedApplication].windows.firstObject addSubview:self]; + + if (self.superview) { + [self.superview sendSubviewToBack:self]; + } + + self.needsPlacement = false; + } +} + +- (void)addSubview:(UIView *)view { + [super addSubview:view]; + self.subviews = [[NSArray alloc] init]; +} + +@end diff --git a/Zapic/Views/ZPCBanner.h b/Zapic/Views/ZPCBanner.h new file mode 100644 index 0000000..e58ac62 --- /dev/null +++ b/Zapic/Views/ZPCBanner.h @@ -0,0 +1,11 @@ +#import +#import + +@interface ZPCBanner : UIView + +@property (nonatomic, copy) void (^callback)(void); + +- (instancetype)initWithTitle:(NSString *)title subtitle:(NSString *)subtitle image:(UIImage *)image; +- (void)show:(NSTimeInterval)duration; + +@end diff --git a/Zapic/Views/ZPCBanner.m b/Zapic/Views/ZPCBanner.m new file mode 100644 index 0000000..6de9acd --- /dev/null +++ b/Zapic/Views/ZPCBanner.m @@ -0,0 +1,204 @@ +#import "ZPCBanner.h" +#import "ZPCGradientBar.h" +#import "ZPCLog.h" +#import "ZPCUtils.h" + +@interface ZPCBanner () +@property (nonatomic, assign) float topPadding; +@property (nonatomic, strong) NSLayoutConstraint *showingConstraint; +@property (nonatomic, strong) NSLayoutConstraint *hiddenConstraint; +@end + +@implementation ZPCBanner + +static CGFloat const bannerHeight = 66; +static CGFloat const animationDuration = 0.4; + +- (instancetype)initWithTitle:(NSString *)title subtitle:(NSString *)subtitle image:(UIImage *)image { + if (self = [super initWithFrame:CGRectZero]) { + _topPadding = [ZPCUtils notchSize]; + + if (![UIApplication sharedApplication].isStatusBarHidden) { + _topPadding = [UIApplication sharedApplication].statusBarFrame.size.height; + } + + self.backgroundColor = UIColor.whiteColor; + self.translatesAutoresizingMaskIntoConstraints = NO; + + float bannerWidth = MIN(375, MIN(UIScreen.mainScreen.bounds.size.width, UIScreen.mainScreen.bounds.size.height)); + [self.widthAnchor constraintEqualToConstant:bannerWidth].active = YES; + [self.heightAnchor constraintEqualToConstant:bannerHeight].active = YES; + + //Add the gradient to the top + ZPCGradientBar *gradient = [[ZPCGradientBar alloc] init]; + [self addSubview:gradient]; + + //Adds the icon + UIImageView *iconView = [[UIImageView alloc] initWithImage:image]; + iconView.translatesAutoresizingMaskIntoConstraints = NO; + iconView.contentMode = UIViewContentModeScaleAspectFit; + iconView.layer.cornerRadius = 5; + iconView.layer.masksToBounds = YES; + + [self addSubview:iconView]; + + CGFloat const iconPadding = 8; + [iconView.topAnchor constraintEqualToAnchor:gradient.bottomAnchor constant:iconPadding].active = YES; + [iconView.leadingAnchor constraintEqualToAnchor:self.leadingAnchor constant:iconPadding].active = YES; + [iconView.bottomAnchor constraintEqualToAnchor:self.bottomAnchor constant:-iconPadding].active = YES; + [iconView.widthAnchor constraintEqualToAnchor:iconView.heightAnchor].active = YES; + + //Add the content container + UIView *content = [[UIView alloc] init]; + content.translatesAutoresizingMaskIntoConstraints = NO; + + [self addSubview:content]; + + static CGFloat const contentPadding = 5; + [content.topAnchor constraintEqualToAnchor:gradient.bottomAnchor constant:contentPadding].active = YES; + [content.bottomAnchor constraintEqualToAnchor:self.bottomAnchor constant:-contentPadding].active = YES; + [content.rightAnchor constraintEqualToAnchor:self.rightAnchor constant:-contentPadding].active = YES; + [content.leftAnchor constraintEqualToAnchor:iconView.rightAnchor constant:contentPadding].active = YES; + + static CGFloat const subtitlePadding = 10; + + //Add the title + UILabel *titleLabel = [[UILabel alloc] init]; + titleLabel.font = [UIFont systemFontOfSize:16]; + titleLabel.text = title; + titleLabel.numberOfLines = 1; + titleLabel.textAlignment = NSTextAlignmentCenter; + + [content addSubview:titleLabel]; + + titleLabel.translatesAutoresizingMaskIntoConstraints = NO; + [titleLabel.centerXAnchor constraintEqualToAnchor:content.centerXAnchor].active = YES; + [titleLabel.centerYAnchor constraintEqualToAnchor:content.centerYAnchor constant:subtitle ? -subtitlePadding : 0].active = YES; + [titleLabel.widthAnchor constraintEqualToAnchor:content.widthAnchor].active = YES; + + if (subtitle) { + UILabel *subtitleLabel = [[UILabel alloc] init]; + subtitleLabel.font = [UIFont systemFontOfSize:14]; + subtitleLabel.textColor = [UIColor.blackColor colorWithAlphaComponent:0.5]; + subtitleLabel.text = subtitle; + subtitleLabel.numberOfLines = 1; + subtitleLabel.textAlignment = NSTextAlignmentCenter; + + [content addSubview:subtitleLabel]; + + subtitleLabel.translatesAutoresizingMaskIntoConstraints = NO; + [subtitleLabel.centerXAnchor constraintEqualToAnchor:content.centerXAnchor].active = YES; + [subtitleLabel.centerYAnchor constraintEqualToAnchor:content.centerYAnchor constant:subtitlePadding].active = YES; + [subtitleLabel.widthAnchor constraintEqualToAnchor:content.widthAnchor].active = YES; + } + + //Adds the shadown to the banner + self.layer.shadowColor = UIColor.blackColor.CGColor; + self.layer.shadowOpacity = 0.5; + self.layer.shadowOffset = CGSizeZero; + self.layer.shadowRadius = 4; + + [self addGestures]; + } + return self; +} + +- (void)didMoveToSuperview { + [super didMoveToSuperview]; + + if (!self.superview) { + return; + } + + [self.centerXAnchor constraintEqualToAnchor:self.superview.centerXAnchor].active = YES; + + _showingConstraint = [self.topAnchor constraintEqualToAnchor:self.superview.topAnchor constant:_topPadding]; + + // Offset the bottom constraint to make room for the shadow to animate off screen. + _hiddenConstraint = [self.bottomAnchor constraintEqualToAnchor:self.superview.topAnchor constant:-7.0]; +} + +- (void)show:(NSTimeInterval)duration { + //Finds the current view controller being shown + UIView *topView = [ZPCUtils getTopView]; + + //Adds the banner to that view + [topView addSubview:self]; + + [self setVisibility:NO]; + + dispatch_async(dispatch_get_main_queue(), ^{ + [UIView animateWithDuration:animationDuration + delay:0.0 + usingSpringWithDamping:0.7 + initialSpringVelocity:1.5 + options:UIViewAnimationOptionAllowUserInteraction + animations:^{ + [self setVisibility:YES]; + } + completion:^(BOOL finished) { + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(duration * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + [self dismiss]; + }); + }]; + }); +} + +- (void)addGestures { + //Tap gesture + [self addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(didTap:)]]; + + //Swipe gesture + UISwipeGestureRecognizer *swipe = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(didSwipe:)]; + swipe.direction = UISwipeGestureRecognizerDirectionUp; + [self addGestureRecognizer:swipe]; +} + +- (void)didTap:(UITapGestureRecognizer *)rec { + [self dismiss]; + + if (self.callback) { + self.callback(); + } +} + +- (void)didSwipe:(UISwipeGestureRecognizer *)rec { + [self dismiss]; +} + +- (void)dismiss { + [UIView animateWithDuration:animationDuration + delay:0.0 + usingSpringWithDamping:0.7 + initialSpringVelocity:1.5 + options:UIViewAnimationOptionAllowUserInteraction + animations:^{ + [self setVisibility:NO]; + } + completion:^(BOOL finished) { + [self removeFromSuperview]; + }]; +} + +- (void)setVisibility:(BOOL)show { + if (show) { + [self.superview removeConstraint:_hiddenConstraint]; + [self.superview addConstraint:_showingConstraint]; + } else { + [self.superview removeConstraint:_showingConstraint]; + [self.superview addConstraint:_hiddenConstraint]; + } + + [self setNeedsLayout]; + [self setNeedsUpdateConstraints]; + + // Managing different -layoutIfNeeded behaviours among iOS versions (for more, read the UIKit iOS 10 release notes) + if (@available(iOS 10.0, *)) { + [self.superview layoutIfNeeded]; + } else { + [self layoutIfNeeded]; + } + [self updateConstraintsIfNeeded]; +} + +@end diff --git a/Zapic/Views/ZPCBaseView.h b/Zapic/Views/ZPCBaseView.h new file mode 100644 index 0000000..3013d4c --- /dev/null +++ b/Zapic/Views/ZPCBaseView.h @@ -0,0 +1,8 @@ +#import +#import "ZPCCore.h" + +@interface ZPCBaseView : UIView +@property (weak) ZPCCore *viewController; +- (instancetype)initWithSpinner; +- (instancetype)initWithText:(NSString *)text subText:(NSString *)subText showSpinner:(BOOL)showSpinner; +@end diff --git a/Zapic/Views/ZPCBaseView.m b/Zapic/Views/ZPCBaseView.m new file mode 100644 index 0000000..4f348dc --- /dev/null +++ b/Zapic/Views/ZPCBaseView.m @@ -0,0 +1,108 @@ +#import "ZPCBaseView.h" +#import "ZPCGradientBar.h" +#import "ZPCImageUtils.h" +#import "ZPCLog.h" +#import "ZPCUtils.h" + +@implementation ZPCBaseView + +- (instancetype)initWithSpinner { + return [self initWithText:nil subText:nil showSpinner:YES]; +} + +- (instancetype)initWithText:(NSString *)text subText:(NSString *)subText showSpinner:(BOOL)showSpinner { + if (self = [super initWithFrame:CGRectZero]) { + //Background color + self.backgroundColor = [UIColor.blackColor colorWithAlphaComponent:0.8]; + + //Add the gradient to the top + ZPCGradientBar *gradient = [[ZPCGradientBar alloc] init]; + [self addSubview:gradient]; + + //Add the zapic logo to the center of the screen + UIImageView *icon = [[UIImageView alloc] initWithImage:[ZPCImageUtils getZapicLogo]]; + icon.contentMode = UIViewContentModeScaleAspectFit; + icon.translatesAutoresizingMaskIntoConstraints = NO; + + [self addSubview:icon]; + + CGFloat const iconSize = 64; + [icon.centerXAnchor constraintEqualToAnchor:self.centerXAnchor].active = YES; + [icon.centerYAnchor constraintEqualToAnchor:self.centerYAnchor constant:-150].active = YES; + [icon.heightAnchor constraintEqualToConstant:iconSize].active = YES; + [icon.widthAnchor constraintEqualToConstant:iconSize].active = YES; + + NSLayoutYAxisAnchor *lastAnchor = icon.bottomAnchor; + + if (showSpinner) { + UIActivityIndicatorView *spinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge]; + spinner.translatesAutoresizingMaskIntoConstraints = NO; + [spinner startAnimating]; + + [self addSubview:spinner]; + + [spinner.topAnchor constraintEqualToAnchor:lastAnchor constant:20].active = YES; + [spinner.centerXAnchor constraintEqualToAnchor:self.centerXAnchor].active = YES; + + lastAnchor = spinner.bottomAnchor; + } + + //Adds the text + if (text) { + UILabel *title = [[UILabel alloc] init]; + title.font = [UIFont systemFontOfSize:22]; + title.text = text; + title.textAlignment = NSTextAlignmentCenter; + title.textColor = UIColor.whiteColor; + title.translatesAutoresizingMaskIntoConstraints = NO; + + [self addSubview:title]; + + [title.centerXAnchor constraintEqualToAnchor:self.centerXAnchor].active = YES; + [title.topAnchor constraintEqualToAnchor:lastAnchor constant:15].active = YES; + + lastAnchor = title.bottomAnchor; + } + + //Adds the subtext + if (subText) { + UILabel *details = [[UILabel alloc] init]; + details.font = [UIFont systemFontOfSize:18]; + details.text = subText; + details.textAlignment = NSTextAlignmentCenter; + details.lineBreakMode = NSLineBreakByWordWrapping; + details.numberOfLines = 0; + details.textColor = [UIColor.whiteColor colorWithAlphaComponent:0.5]; + details.translatesAutoresizingMaskIntoConstraints = NO; + + [self addSubview:details]; + + [details.leftAnchor constraintEqualToAnchor:self.leftAnchor constant:50].active = YES; + [details.rightAnchor constraintEqualToAnchor:self.rightAnchor constant:-50].active = YES; + [details.topAnchor constraintEqualToAnchor:lastAnchor constant:15].active = YES; + } + + UIButton *closeButton = [[UIButton alloc] init]; + [closeButton setTitle:@"Done" forState:UIControlStateNormal]; + [closeButton addTarget:self action:@selector(closeButtonAction:) forControlEvents:UIControlEventTouchUpInside]; + closeButton.translatesAutoresizingMaskIntoConstraints = NO; + + [self addSubview:closeButton]; + + [closeButton.topAnchor constraintEqualToAnchor:gradient.bottomAnchor constant:10].active = YES; + [closeButton.leftAnchor constraintEqualToAnchor:self.leftAnchor constant:10].active = YES; + } + return self; +} + +- (void)closeButtonAction:(UIButton *)sender { + if (!_viewController) { + [ZPCLog error:@"Unable to close the view. No view controller set"]; + return; + } + + [ZPCLog info:@"Close button tapped"]; + [_viewController closePage]; +} + +@end diff --git a/Zapic/Views/ZPCErrorView.h b/Zapic/Views/ZPCErrorView.h new file mode 100644 index 0000000..ccdd4a6 --- /dev/null +++ b/Zapic/Views/ZPCErrorView.h @@ -0,0 +1,6 @@ +#import +#import "ZPCBaseView.h" + +@interface ZPCErrorView : ZPCBaseView + +@end diff --git a/Zapic/Views/ZPCErrorView.m b/Zapic/Views/ZPCErrorView.m new file mode 100644 index 0000000..fffa7bb --- /dev/null +++ b/Zapic/Views/ZPCErrorView.m @@ -0,0 +1,11 @@ +#import "ZPCErrorView.h" + +@implementation ZPCErrorView + +- (instancetype)init { + if (self = [super initWithText:@"Could Not Connect" subText:@"Please check your network connection and try again" showSpinner:NO]) { + } + return self; +} + +@end diff --git a/Zapic/Views/ZPCGradientBar.h b/Zapic/Views/ZPCGradientBar.h new file mode 100644 index 0000000..89b0ba9 --- /dev/null +++ b/Zapic/Views/ZPCGradientBar.h @@ -0,0 +1,5 @@ +#import + +@interface ZPCGradientBar : UIView + +@end diff --git a/Zapic/Views/ZPCGradientBar.m b/Zapic/Views/ZPCGradientBar.m new file mode 100644 index 0000000..694bf8e --- /dev/null +++ b/Zapic/Views/ZPCGradientBar.m @@ -0,0 +1,47 @@ +#import "ZPCGradientBar.h" + +@interface ZPCGradientBar () +@property (nonatomic, strong) CAGradientLayer *gradient; +@end + +@implementation ZPCGradientBar + +- (instancetype)init { + if (self = [super initWithFrame:CGRectZero]) { + self.backgroundColor = UIColor.redColor; + _gradient = [CAGradientLayer layer]; + + _gradient.frame = self.bounds; + + UIColor *zBlue = [UIColor colorWithRed:0.00 green:0.87 blue:0.68 alpha:1.0]; + UIColor *zGreen = [UIColor colorWithRed:0.00 green:0.52 blue:0.89 alpha:1.0]; + _gradient.startPoint = CGPointMake(1, 0); + _gradient.endPoint = CGPointZero; + _gradient.colors = @[(id)zBlue.CGColor, (id)zGreen.CGColor]; + + [self.layer insertSublayer:_gradient atIndex:0]; + } + return self; +} + +- (void)layoutSubviews { + _gradient.frame = self.frame; +} + +- (void)didMoveToSuperview { + [super didMoveToSuperview]; + + UIView *superview = self.superview; + + if (!superview) { + return; + } + + self.translatesAutoresizingMaskIntoConstraints = NO; + [self.topAnchor constraintEqualToAnchor:superview.topAnchor].active = YES; + [self.heightAnchor constraintEqualToConstant:6].active = YES; + [self.leadingAnchor constraintEqualToAnchor:superview.leadingAnchor].active = YES; + [self.trailingAnchor constraintEqualToAnchor:superview.trailingAnchor].active = YES; +} + +@end diff --git a/Zapic/Views/ZPCLoadingView.h b/Zapic/Views/ZPCLoadingView.h new file mode 100644 index 0000000..c1d3941 --- /dev/null +++ b/Zapic/Views/ZPCLoadingView.h @@ -0,0 +1,6 @@ +#import +#import "ZPCBaseView.h" + +@interface ZPCLoadingView : ZPCBaseView + +@end diff --git a/Zapic/Views/ZPCLoadingView.m b/Zapic/Views/ZPCLoadingView.m new file mode 100644 index 0000000..978b8ce --- /dev/null +++ b/Zapic/Views/ZPCLoadingView.m @@ -0,0 +1,11 @@ +#import "ZPCLoadingView.h" + +@implementation ZPCLoadingView + +- (instancetype)init { + if (self = [super initWithSpinner]) { + } + return self; +} + +@end diff --git a/Zapic/Views/ZPCWebApp.h b/Zapic/Views/ZPCWebApp.h new file mode 100644 index 0000000..30d2a0f --- /dev/null +++ b/Zapic/Views/ZPCWebApp.h @@ -0,0 +1,16 @@ +#import +#import +#import "ZPCSafariManager.h" +#import "ZPCScriptMessageHandler.h" + +@interface ZPCWebApp : UIView +@property ZPCSafariManager *safariManager; +@property (readonly) BOOL errorLoading; +/** + The handler when a player logs in to Zapic. + */ +@property (nonatomic, copy, nullable) void (^loadErrorHandler)(void); +- (instancetype)initWithHandler:(nonnull ZPCScriptMessageHandler *)messageHandler; +- (void)loadUrl:(NSString *)url; +- (void)evaluateJavaScript:(NSString *)jsString; +@end diff --git a/Zapic/Views/ZPCWebApp.m b/Zapic/Views/ZPCWebApp.m new file mode 100644 index 0000000..2cab601 --- /dev/null +++ b/Zapic/Views/ZPCWebApp.m @@ -0,0 +1,195 @@ +#import "ZPCWebApp.h" +#import "ZPCInjectedJS.h" +#import "ZPCLog.h" +#import "ZPCUtils.h" + +@interface ZPCWebApp () + +@property (nonatomic, strong) NSString *appUrl; +@property (nonatomic, assign) bool loadSuccessful; +@property (nonatomic, assign) int retryAttempt; +@property (readonly) WKWebView *webView; + +@end + +@implementation ZPCWebApp + +- (instancetype)initWithHandler:(nonnull ZPCScriptMessageHandler *)messageHandler { + if (self = [super initWithFrame:CGRectZero]) { + WKWebViewConfiguration *config = [ZPCWebApp getWebViewConfiguration]; + [config.userContentController addScriptMessageHandler:messageHandler name:ZPCScriptMethodName]; + + _webView = [[WKWebView alloc] initWithFrame:CGRectZero configuration:config]; + + _webView.translatesAutoresizingMaskIntoConstraints = NO; + _webView.autoresizingMask = (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight); + _webView.scrollView.scrollEnabled = NO; + _webView.scrollView.bounces = NO; + _webView.scrollView.delegate = self; + _webView.navigationDelegate = self; + + [self addSubview:_webView]; + [_webView.topAnchor constraintEqualToAnchor:self.topAnchor].active = YES; + [_webView.leftAnchor constraintEqualToAnchor:self.leftAnchor].active = YES; + [_webView.rightAnchor constraintEqualToAnchor:self.rightAnchor].active = YES; + [_webView.bottomAnchor constraintEqualToAnchor:self.bottomAnchor].active = YES; + } + return self; +} + +- (void)evaluateJavaScript:(NSString *)jsString { + dispatch_async(dispatch_get_main_queue(), ^{ + [ZPCLog info:@"Dispatching %@", jsString]; + + if (!self.webView) { + [ZPCLog error:@"Webview has not been set, unable to send"]; + return; + } + + [self.webView evaluateJavaScript:jsString + completionHandler:^(id _Nullable result, NSError *_Nullable error) { + if (error) { + [ZPCLog error:@"JS Error: %@", error]; + } else if (result) { + [ZPCLog info:@"JS Result: %@", result]; + } + }]; + }); +} + +- (void)loadUrl:(NSString *)url { + _appUrl = url; + [self load]; +} + +- (void)load { + NSURLRequest *appRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:_appUrl]]; + + [_webView loadRequest:appRequest]; +} + ++ (WKWebViewConfiguration *)getWebViewConfiguration { + WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init]; + + //Gets the JS code to be injected + NSString *injected = [ZPCInjectedJS getInjectedScript]; + + WKUserScript *script = [[WKUserScript alloc] initWithSource:injected injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:YES]; + + [config.userContentController addUserScript:script]; + + return config; +} + +- (void)retryAfterDelay { + if (_loadSuccessful) { + return; + } + + _retryAttempt += 1; + + static CGFloat const base = 5; + static CGFloat const maxDelay = 300; //5 minutes + + //Calculate the delay before the next retry + float delay = MAX(1, drand48() * MIN(maxDelay, base * pow(2.0, _retryAttempt))); + + [ZPCLog info:@"Will try to reload in %f sec", delay]; + + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delay * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + [self load]; + }); +} + +#pragma mark - WKNavigationDelegate + +- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation { + [ZPCLog info:@"Finished loading web app"]; + _retryAttempt = 0; + _loadSuccessful = YES; + _errorLoading = NO; +} + +/** + Handles any errors loading the web view + + @param webView The web view invoking the delegate method. + @param navigation The navigation object that started to load a page. + @param error The error that occurred. + */ +- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(WKNavigation *)navigation withError:(NSError *)error { + if ([error.domain isEqual:@"WebKitErrorDomain"] && error.code == 102) { + [ZPCLog info:@"Skipping known error message loading url"]; + return; + } + + [ZPCLog warn:@"Error loading Zapic webview"]; + + [self retryAfterDelay]; + + _errorLoading = YES; + + if (_loadErrorHandler) { + _loadErrorHandler(); + } +} + +- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler { + NSURL *url = [navigationAction valueForKeyPath:@"request.URL"]; + + int const cancel = 0; + int const allow = 1; + + //Skip if there is not a valid url + if (!url) { + decisionHandler(cancel); + return; + } + + //Skip if the app has not loaded yet + if (!_appUrl) { + decisionHandler(cancel); + return; + } + + //Allow the webview to open other links that are within our web app. + if ([url.absoluteString hasPrefix:_appUrl]) { + decisionHandler(allow); + return; + } + NSString *scheme = url.scheme; + + if (!scheme) { + decisionHandler(cancel); + return; + } + + //Allow the OS to open the itms links directly into the app store + if ([scheme hasPrefix:@"itms"]) { + [UIApplication.sharedApplication openURL:url]; + decisionHandler(cancel); + return; + } + + //Opens the a safari view with the content + [_safariManager openUrl:url]; + + //Tell the webview not to open the link + decisionHandler(cancel); +} + +#pragma mark - UIScrollViewDelegate + +- (void)scrollViewWillBeginZooming:(UIScrollView *)scrollView withView:(UIView *)view { + scrollView.pinchGestureRecognizer.enabled = NO; + scrollView.panGestureRecognizer.enabled = NO; + [scrollView setZoomScale:1.0 animated:NO]; +} + +- (void)scrollViewDidZoom:(UIScrollView *)scrollView { + if (scrollView.zoomScale == 1) { + return; + } + [scrollView setZoomScale:1.0 animated:NO]; +} +@end diff --git a/Zapic/Views/ZapicView.swift b/Zapic/Views/ZapicView.swift deleted file mode 100644 index 6a9d1ea..0000000 --- a/Zapic/Views/ZapicView.swift +++ /dev/null @@ -1,75 +0,0 @@ -// -// File.swift -// Zapic -// -// Created by Daniel Sarfati on 7/11/17. -// Copyright © 2017 Zapic. All rights reserved. -// - -import Foundation - -class ZapicView: UIView { - - weak var controllerDelegate: ZapicViewControllerDelegate? - - let title = UILabel() - let colorBar = ColorBar() - - init(text: String) { - - super.init(frame: .zero) - - backgroundColor = ZapicColors.background - - //Color bar - - addSubview(colorBar) - - //Zapic icon - let icon = UIImageView(image: ZapicImages.image(name: "ZapicLogo")) - icon.contentMode = .scaleAspectFit - - self.addSubview(icon) - - let iconSize: CGFloat = 64 - icon.translatesAutoresizingMaskIntoConstraints = false - icon.centerXAnchor.constraint(equalTo: self.centerXAnchor).isActive = true - icon.centerYAnchor.constraint(equalTo: self.centerYAnchor, constant: -100).isActive = true - icon.heightAnchor.constraint(equalToConstant: iconSize).isActive = true - icon.widthAnchor.constraint(equalToConstant: iconSize).isActive = true - - //Text label - title.font = UIFont.systemFont(ofSize: 22) - title.text = text - title.textAlignment = .center - title.textColor = ZapicColors.primaryText - - self.addSubview(title) - - title.translatesAutoresizingMaskIntoConstraints = false - title.centerXAnchor.constraint(equalTo: self.centerXAnchor).isActive = true - title.topAnchor.constraint(equalTo: icon.bottomAnchor, constant: 10).isActive = true - - //Close button - let closeButton = UIButton() - closeButton.setTitle("Done", for: .normal) - closeButton.setTitleColor(ZapicColors.primaryText, for: .normal) - - closeButton.addTarget(self, action: #selector(closeButtonAction), for: .touchUpInside) - - self.addSubview(closeButton) - - closeButton.translatesAutoresizingMaskIntoConstraints = false - closeButton.topAnchor.constraint(equalTo: colorBar.bottomAnchor).isActive = true - closeButton.leftAnchor.constraint(equalTo: self.leftAnchor, constant: 10).isActive = true - } - - required init?(coder aDecoder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - @objc func closeButtonAction(sender: UIButton!) { - ZLog.info("Close button tapped") - controllerDelegate?.closePage() - } -} diff --git a/Zapic/Views/ZapicWebView.swift b/Zapic/Views/ZapicWebView.swift deleted file mode 100644 index ef3cae8..0000000 --- a/Zapic/Views/ZapicWebView.swift +++ /dev/null @@ -1,390 +0,0 @@ -// -// ZapicWebView.swift -// Zapic -// -// Created by Daniel Sarfati on 7/10/17. -// Copyright © 2017 Zapic. All rights reserved. -// - -import Foundation -import WebKit - -// Events sent from the web client to the SDK -enum WebEvent: String { - case login = "LOGIN" - //case appLoaded = "APP_LOADED" - case appStarted = "APP_STARTED" - case showBanner = "SHOW_BANNER" - case pageReady = "PAGE_READY" - case closePageRequest = "CLOSE_PAGE_REQUESTED" - case getContacts = "GET_CONTACTS" - case loggedIn = "LOGGED_IN" -} - -// Events sent from the SDK to the web client -enum WebFunction: String { - case setSignature = "LOGIN_WITH_GAME_CENTER" - case submitEvent = "SUBMIT_EVENT" - case openPage = "OPEN_PAGE" - case closePage = "CLOSE_PAGE" - case setContacts = "SET_CONTACTS" -} - -struct Event { - let type: WebFunction - let payload: Any - let isError: Bool - - init(type: WebFunction, payload: Any, isError: Bool) { - self.type = type - self.payload = payload - self.isError = isError - } -} - -protocol ZapicViewControllerDelegate: class { - func onPageReady() - func onAppError(error: Error) - func closePage() -} - -enum WebClientStatus { - case none - case loading - case ready - case error -} - -class ZapicWebView: WKWebView, WKScriptMessageHandler, UIScrollViewDelegate, ZapicWebClient { - - private let appUrl: String = ZapicUtils.appUrl() - - private let events: [String] - - private var eventQueue = Queue() - - private (set) var isPageReady = false - - private(set) var status = WebClientStatus.none - - weak var zapicDelegate: ZapicDelegate? - - weak var controllerDelegate: ZapicViewControllerDelegate? - - init() { - - ZLog.info("Loading webclient from \(appUrl)") - - let config = WKWebViewConfiguration() - let controller = WKUserContentController() - config.userContentController = controller - - events = ["dispatch", "console"] - - let userScript = WKUserScript(source: injectedScript, injectionTime: .atDocumentStart, forMainFrameOnly: true) - controller.addUserScript(userScript) - - super.init(frame: .zero, configuration: config) - - super.navigationDelegate = self - super.scrollView.delegate = self - - for event in events { - controller.add(self, name: event) - } - } - - // Disable zooming in webView - func viewForZooming(scroll: UIScrollView) -> UIView? { - return nil - } - - required init?(coder aDecoder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - func load() { - - if status == .ready { - ZLog.info("Web application is already ready") - return - } - - if status == .loading { - ZLog.info("Web application is already loading") - return - } - - ZLog.info("Loading Zapic web application") - - status = .loading - - guard let myURL = URL(string: appUrl) else { - ZLog.error("Invalid URL for web application") - return - } - - super.load(URLRequest(url: myURL, timeoutInterval: 30)) - } - - func onAppError(error: Error) { - status = .error - controllerDelegate?.onAppError(error: error) - zapicDelegate?.onAppError(error: error) - } - - /// Receive messages from JS code - func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) { - - let methodName = message.name - - switch methodName { - case "dispatch": - handleDispatch(message) - case "console": - handleConsole(message) - default: - ZLog.warn("Received a message to unknown method: \(methodName)") - } - } - - private func handleConsole(_ message: WKScriptMessage) { - - guard let json = message.body as? [String: Any] else { - ZLog.warn("Received invalid message format") - return - } - - guard let levelStr = json["level"] as? String else { - ZLog.warn("Received invalid console message") - return - } - - guard let message = json["message"] as? [String] else { - ZLog.warn("Received invalid console message") - return - } - - var level = ZLogLevel.info - - switch levelStr.uppercased() { - case "ERROR": - level = ZLogLevel.error - case "WARN": - level = ZLogLevel.warn - default: - level = ZLogLevel.info - } - - let text = message[0] - let args = Array(message[2...]) - - let msg = text.replaceSubstrings(string: "%s", args: args) - - ZLog.log(msg, level: level, source: .web) - } - - private func handleDispatch(_ message: WKScriptMessage) { - - guard let json = message.body as? [String: Any] else { - ZLog.warn("Received invalid message format") - return - } - - guard let typeValue = json["type"] as? String else { - ZLog.warn("Received message with missing message type") - return - } - - guard let type = WebEvent(rawValue: typeValue) else { - ZLog.warn("Received unknown message type \(typeValue)") - return - } - - handleMessage(type: type, json: json) - } - - private func handleMessage(type: WebEvent, json: [String: Any]) { - - ZLog.info("Received from JS: \(type) ") - - switch type { - case .login: - zapicDelegate?.getVerificationSignature() - case .getContacts: - zapicDelegate?.getContacts() - case .appStarted: - status = .ready - zapicDelegate?.onAppReady() - case .loggedIn: - loggedIn(json) - case .pageReady: - isPageReady = true - controllerDelegate?.onPageReady() - case .showBanner: - receiveBanner(json) - case .closePageRequest: - controllerDelegate?.closePage() - default: - ZLog.warn("Unhandled message type \(type)") - } - } - - private func decode(base64: String?) -> UIImage? { - - guard let string = base64 else { - return nil - } - - if let dataDecoded = Data(base64Encoded: string, options: NSData.Base64DecodingOptions(rawValue: 0)) { - return UIImage(data: dataDecoded) - } else { - ZLog.warn("Invalid base64 string") - return nil - } - } - - // MARK: - Receive Messages - - private func receiveBanner(_ json: [String: Any]) { - guard let msg = json["payload"] as? [String: Any] else { - ZLog.warn("Received invalid ShowBanner payload") - return - } - - guard let title = msg["title"] as? String else { - ZLog.warn("ShowBanner title is required") - return - } - - let icon: UIImage? = decode(base64: msg["icon"] as? String) - - let subTitle = msg["subtitle"] as? String - - zapicDelegate?.showBanner(title: title, subTitle: subTitle, icon: icon) - } - - private func loggedIn(_ json: [String: Any]) { - - guard let msg = json["payload"] as? [String: Any] else { - ZLog.warn("Received invalid SetPlayerId payload") - return - } - - guard let playerIdString = msg["userId"] as? String else { - ZLog.warn("Invalid/missing value for userId, must be a string") - return - } - - guard let playerId = playerIdString.asUUID() else { - ZLog.warn("Could not convert userId to a valid UUID") - return - } - - zapicDelegate?.setPlayerId(playerId: playerId) - } - - // MARK: - Events - - func dispatchToJS(type: WebFunction, payload: Any) { - dispatchToJS(type: type, payload: payload, isError: false) - } - - func dispatchToJS(type: WebFunction, payload: Any, isError: Bool) { - - //Ensure setSignature is the only method that can be sent before - //the app is ready - if status != .ready && type != .setSignature { - ZLog.info("Web client is not ready to run JS. Adding to queue") - - eventQueue.enqueue(Event(type: type, payload: payload, isError: isError)) - return - } - - ZLog.info("Dispatching JS event \(type.rawValue)") - - var msg = ["type": type.rawValue, "payload": payload] - - if isError { - msg["error"]=true - } - - guard let json = ZapicUtils.serialize(data: msg) else { - return - } - - let js = "zapic.dispatch(\(json))" - - ZLog.info("Dispatching \(js)") - - super.evaluateJavaScript(js) { (result, error) in - if let error = error { - ZLog.error("JS Error \(error)") - } else if let result = result { - ZLog.info("JS Result \(result)") - } - } - } - - func submitEvent(eventType: EventType, params: [String: Any]) { - - if status != .ready { - ZLog.info("Web client is not ready to accept events") - return - } - - ZLog.info("Submitting event to web client") - - let msg: [String: Any] = ["type": eventType.rawValue, - "params": params, - "timestamp": Date().iso8601] - - dispatchToJS(type: .submitEvent, payload: msg) - } - - /// Attempt to resend all events that we unable to send - func resendFailedEvents() { - ZLog.info("Started resending \(eventQueue.count) events") - - while eventQueue.count > 0 { - - guard let event = eventQueue.dequeue() else { - ZLog.warn("Resending invalid event") - break - } - - dispatchToJS(type: event.type, payload: event.payload, isError: event.isError) - } - - ZLog.info("Finished resending events") - - } -} - -extension ZapicWebView: WKNavigationDelegate { - - func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) { - ZLog.info("Finished loading webview content") - } - - /// Handle errors loading web application - func webView(_ webView: WKWebView, didFailProvisionalNavigation navigation: WKNavigation!, withError error: Error) { - ZLog.warn("Error loading Zapic webview") - self.onAppError(error: error) - } - - func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) { - guard let scheme = navigationAction.request.url?.scheme else { - decisionHandler(.cancel) - return - } - - if scheme.starts(with: "itms") { - UIApplication.shared.openURL(navigationAction.request.url!) - decisionHandler(.cancel) - return - } - - decisionHandler(.allow) - } -} diff --git a/Zapic/ZLog.swift b/Zapic/ZLog.swift deleted file mode 100644 index 6f546d9..0000000 --- a/Zapic/ZLog.swift +++ /dev/null @@ -1,56 +0,0 @@ -// -// ZLog.swift -// Zapic -// -// Created by Daniel Sarfati on 8/2/17. -// Copyright © 2017 zapic. All rights reserved. -// - -import Foundation - -public enum ZLogLevel: String { - case error = "💩" - case warn = "❗️" - case info = "📣" -} - -public enum ZLogSource: String { - case sdk = "SDK" - case web = "Web" -} - -@objc(ZLog) -public class ZLog: NSObject { - - @objc public static var isEnabled = true - - private static var dateFormatter: DateFormatter { - let dateFormatter: DateFormatter = DateFormatter() - dateFormatter.dateFormat = "H:mm:ss.SSSS" - return dateFormatter - } - - static func error(_ message: String, source: ZLogSource = .sdk ) { - writeLog(message: message, level: .error, source: source) - } - - static func warn(_ message: String, source: ZLogSource = .sdk) { - writeLog(message: message, level: .warn, source: source) - } - - static func info(_ message: String, source: ZLogSource = .sdk) { - writeLog(message: message, level: .info, source: source) - } - - static func log(_ message: String, level: ZLogLevel, source: ZLogSource = .sdk) { - writeLog(message: message, level: level, source: source) - } - - private static func writeLog(message: String, level: ZLogLevel, source: ZLogSource) { - #if DEBUG - if isEnabled { - print("\(level.rawValue) [Zapic][\(source.rawValue)][\(dateFormatter.string(from: Date()))] : \(message)") - } - #endif - } -} diff --git a/Zapic/Zapic.h b/Zapic/Zapic.h deleted file mode 100644 index c607b6e..0000000 --- a/Zapic/Zapic.h +++ /dev/null @@ -1,19 +0,0 @@ -// -// Zapic.h -// Zapic -// -// Created by Daniel Sarfati on 7/12/17. -// Copyright © 2017 zapic. All rights reserved. -// - -#import - -//! Project version number for Zapic. -FOUNDATION_EXPORT double ZapicVersionNumber; - -//! Project version string for Zapic. -FOUNDATION_EXPORT const unsigned char ZapicVersionString[]; - -// In this header, you should import all the public headers of your framework using statements like #import - - diff --git a/Zapic/Zapic.swift b/Zapic/Zapic.swift deleted file mode 100644 index 09c34eb..0000000 --- a/Zapic/Zapic.swift +++ /dev/null @@ -1,67 +0,0 @@ -// -// Swift.swift -// Zapic -// -// Created by Daniel Sarfati on 6/30/17. -// Copyright © 2017 Zapic. All rights reserved. -// - -import Foundation - -public enum ZapicViews: String { - case main - case profile - case achievements -} - -enum ZapicError: Error { - case unknownError - case invalidCredentials - case invalidPlayer - case invalidAuthSignature -} - -enum EventType: String { - case unknown = "Unknown" - case appStarted = "AppStarted" - case gameplay = "Gameplay" -} - -@objc(Zapic) -public class Zapic: NSObject { - - private static let core = ZapicCore() - - @objc public static var playerId: UUID? { - return core.playerId - } - - @objc public static func start(_ version: String) { - core.start(version: version) - } - - @objc public static func submitEvent(json: Data) { - guard let params = ZapicUtils.deserialize(bodyData: json) else { - ZLog.error("Unable to deserialize event from json data") - return - } - - core.submitEvent(eventType: .gameplay, params: params) - } - - @objc public static func submitEvent(_ params: [String: Any]) { - core.submitEvent(eventType: .gameplay, params: params) - } - - @objc public static func show(viewName: String) { - guard let view = ZapicViews(rawValue: viewName) else { - ZLog.error("Invalid view name \(viewName)") - return - } - show(view: view) - } - - public static func show(view: ZapicViews) { - core.show(view: view) - } -} diff --git a/Zapic/ZapicAssets.xcassets/ZapicLogo.imageset/Contents.json b/Zapic/ZapicAssets.xcassets/ZapicLogo.imageset/Contents.json deleted file mode 100644 index 4c36108..0000000 --- a/Zapic/ZapicAssets.xcassets/ZapicLogo.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "filename" : "IconLarge.png", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/Zapic/ZapicAssets.xcassets/ZapicLogo.imageset/IconLarge.png b/Zapic/ZapicAssets.xcassets/ZapicLogo.imageset/IconLarge.png deleted file mode 100644 index 0a8f63f..0000000 Binary files a/Zapic/ZapicAssets.xcassets/ZapicLogo.imageset/IconLarge.png and /dev/null differ diff --git a/Zapic/ZapicColors.swift b/Zapic/ZapicColors.swift deleted file mode 100644 index c1d33d5..0000000 --- a/Zapic/ZapicColors.swift +++ /dev/null @@ -1,19 +0,0 @@ -// -// ZapicColors.swift -// Zapic -// -// Created by Daniel Sarfati on 7/10/17. -// Copyright © 2017 Zapic. All rights reserved. -// - -import Foundation -import UIKit - -class ZapicColors { - static let blue = UIColor(red: 0.00, green: 0.87, blue: 0.68, alpha: 1.0) - static let green = UIColor(red: 0.00, green: 0.52, blue: 0.89, alpha: 1.0) - static let midPoint = UIColor(red: 0.00, green: 0.69, blue: 0.79, alpha: 1.0) - static let background = UIColor.white// UIColor(red:0.11, green:0.13, blue:0.15, alpha:1.0) - static let primaryText = UIColor.black - static let secondaryText = UIColor.black.withAlphaComponent(0.5) -} diff --git a/Zapic/ZapicController.swift b/Zapic/ZapicController.swift deleted file mode 100644 index 96dc5da..0000000 --- a/Zapic/ZapicController.swift +++ /dev/null @@ -1,91 +0,0 @@ -// -// ZapicPanel.swift -// Zapic -// -// Created by Daniel Sarfati on 7/3/17. -// Copyright © 2017 Zapic. All rights reserved. -// - -import Foundation -import UIKit -import WebKit - -class ZapicController: UIViewController, ZapicViewControllerDelegate { - - let webView: ZapicWebView - private let loading: LoadingView - private let offline: OfflineView - private let mainController: UIViewController - - init() { - webView = ZapicWebView() - loading = LoadingView() - offline = OfflineView() - - if let ctrl = UIApplication.shared.delegate?.window??.rootViewController { - mainController = ctrl - } else { - fatalError("RootViewController not found, ensure this is called at the correct time") - } - - super.init(nibName: nil, bundle: nil) - - loading.controllerDelegate = self - offline.controllerDelegate = self - webView.controllerDelegate = self - - //Trick to keep the webview up and running - mainController.view.addSubview(self.webView) - } - - required init?(coder aDecoder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - func show(view zapicView: ZapicViews) { - - if webView.isPageReady { - view = webView - } else if webView.status == .error { - view = offline - } else { - //Reset the view to loading - view = loading - } - - //Trigger the web to update - webView.dispatchToJS(type: .openPage, payload: zapicView.rawValue) - - //Show the ui - self.mainController.present(self, animated: true, completion: nil) - } - - func closePage() { - self.closeView { - self.webView.dispatchToJS(type: .closePage, payload: "") - } - } - - func onPageReady() { - ZLog.info("Page is ready to be shown to the user") - view = webView - } - - func onAppError(error: Error) { - view = offline - } - - override var prefersStatusBarHidden: Bool { - return true - } - - private func closeView(completion: (() -> Void)?) { - super.dismiss(animated: true, completion: completion) - } - - override func dismiss(animated flag: Bool, completion: (() -> Void)?) { - if self.presentedViewController != nil { - super.dismiss(animated: flag, completion: completion) - } - } -} diff --git a/Zapic/ZapicCore.swift b/Zapic/ZapicCore.swift deleted file mode 100644 index 1206ed0..0000000 --- a/Zapic/ZapicCore.swift +++ /dev/null @@ -1,123 +0,0 @@ -// -// ZapicCore.swift -// Zapic -// -// Created by Daniel Sarfati on 8/18/17. -// Copyright © 2017 zapic. All rights reserved. -// - -import Foundation - -class ZapicCore: ZapicDelegate { - - private var webClient: ZapicWebClient - private let zapicController: ZapicController - private let contactManager = ContactManager() - private var hasStarted = false - private var appVersion = "" - - /// Current retry attempt number. Resets when load is sucessful - private var retryAttempt = 0 - - @objc public private(set) var playerId: UUID? - - init() { - if ZLog.isEnabled { - ZLog.info("Logging is enabled. Disable via ZLog.isEnabled.") - } - - zapicController = ZapicController() - webClient = zapicController.webView - webClient.zapicDelegate = self - } - - init(webClient: ZapicWebClient) { - zapicController = ZapicController() - self.webClient = webClient - self.webClient.zapicDelegate = self - } - - func start(version: String) { - if hasStarted { - ZLog.warn("Zapic already started. Start should only be called once.") - return - } - - ZLog.info("Zapic starting. App version \(version)") - - hasStarted = true - appVersion = version - - webClient.load() - } - - func getContacts() { - contactManager.getContacts {contacts in - - if let contacts = contacts { - self.webClient.dispatchToJS(type: .setContacts, payload: contacts) - } else { - self.webClient.dispatchToJS(type: .setContacts, payload: "Unable to get contacts", isError: true) - } - } - } - - func setPlayerId(playerId: UUID) { - self.playerId = playerId - } - - func submitEvent(eventType: EventType, params: [String: Any]) { - - webClient.submitEvent(eventType: eventType, params: params) - } - - func show(view: ZapicViews) { - ZLog.info("Show \(view.rawValue)") - zapicController.show(view: view) - } - - func getVerificationSignature() { - ZLog.info("Getting verification signature") - - GameCenterHelper.generateSignature { (signature, _) in - - if let sig = signature { - self.webClient.dispatchToJS(type: .setSignature, payload: sig) - } else { - self.webClient.dispatchToJS(type: .setSignature, payload: "Error with Game Center", isError: true) - } - } - } - - /// Trigger when a banner should be shown - func showBanner(title: String, subTitle: String?, icon: UIImage?) { - - let banner = Banner(title: title, subtitle: subTitle, icon: icon) - - banner.dismissesOnTap = true - banner.show(duration: 3.0) - } - - func onAppReady() { - retryAttempt = 0 - webClient.resendFailedEvents() - submitEvent(eventType: .appStarted, params: ["version": appVersion]) - } - - func onAppError(error: Error) { - let base: Double = 5 - //Max delay (s) - let maxDelay: Double = 20 * 60 - - retryAttempt += 1 - - let delay = max(1, drand48() * min(maxDelay, base * pow(2.0, Double(retryAttempt)))) - - ZLog.info("Retrying load in \(delay) sec") - - //Attempt to load the web client again after a delay - DispatchQueue.main.asyncAfter(deadline: .now() + delay) { - self.webClient.load() - } - } -} diff --git a/Zapic/ZapicDelegate.swift b/Zapic/ZapicDelegate.swift deleted file mode 100644 index a0b127b..0000000 --- a/Zapic/ZapicDelegate.swift +++ /dev/null @@ -1,32 +0,0 @@ -// -// ZapicDelegate.swift -// Zapic -// -// Created by Daniel Sarfati on 10/17/17. -// Copyright © 2017 zapic. All rights reserved. -// - -import Foundation - -protocol ZapicDelegate: class { - - /// Triggers the generation of the - /// Game Center verification signature - func getVerificationSignature() - - /// Trigger when the web client is ready for - /// the native client to call it - func onAppReady() - - /// Trigger when the web client has an error - func onAppError(error: Error) - - /// Trigger when a banner should be shown - func showBanner(title: String, subTitle: String?, icon: UIImage?) - - /// Triggers retrieving all contacts from the device - func getContacts() - - /// Trigger when the player id is received for the user - func setPlayerId(playerId: UUID) -} diff --git a/Zapic/ZapicUtils.swift b/Zapic/ZapicUtils.swift deleted file mode 100644 index 7f68fe9..0000000 --- a/Zapic/ZapicUtils.swift +++ /dev/null @@ -1,55 +0,0 @@ -// -// ZapicUtils.swift -// Zapic -// -// Created by Daniel Sarfati on 7/21/17. -// Copyright © 2017 zapic. All rights reserved. -// - -import Foundation - -class ZapicUtils { - - static func serialize(data: Any) -> String? { - - switch data { - case is [String: Any]: - if let jsonData = try? JSONSerialization.data(withJSONObject: data) { - return String(data: jsonData, encoding: .utf8) - } - default: - return String(describing: data) - } - - return nil - } - - static func deserialize(bodyData: Data) -> [String: Any]? { - guard let json = try? JSONSerialization.jsonObject(with: bodyData, options: []), - let payload = json as? [String: Any] else { - return nil - } - return payload - } - - static func formatString(message: String, args: [String]?) -> String { - - guard let args = args else { - return message - } - - if args.count == 0 { - return message - } - - return message.replaceSubstrings(string: "%s", args: args) - } - - static func appUrl() -> String { - if let clientUrl = UserDefaults.standard.string(forKey: "ZAPIC_URL"), !clientUrl.isEmpty { - return clientUrl - } else { - return "https://app.zapic.net" - } - } -} diff --git a/Zapic/ZapicWebClient.swift b/Zapic/ZapicWebClient.swift deleted file mode 100644 index aa069b3..0000000 --- a/Zapic/ZapicWebClient.swift +++ /dev/null @@ -1,22 +0,0 @@ -// -// ZapicWebClient.swift -// Zapic -// -// Created by Daniel Sarfati on 10/17/17. -// Copyright © 2017 zapic. All rights reserved. -// - -import Foundation - -protocol ZapicWebClient { - var zapicDelegate: ZapicDelegate? { get set } - func submitEvent(eventType: EventType, params: [String: Any]) - func dispatchToJS(type: WebFunction, payload: Any) - func dispatchToJS(type: WebFunction, payload: Any, isError: Bool) - - /// Load the web client - func load() - - /// Attempt to resend all events that we unable to send - func resendFailedEvents() -} diff --git a/ZapicExample-ObjC/ZapicExample-ObjC.xcodeproj/project.pbxproj b/ZapicExample-ObjC/ZapicExample-ObjC.xcodeproj/project.pbxproj new file mode 100644 index 0000000..127fa5c --- /dev/null +++ b/ZapicExample-ObjC/ZapicExample-ObjC.xcodeproj/project.pbxproj @@ -0,0 +1,385 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 50; + objects = { + +/* Begin PBXBuildFile section */ + 656F3CC920F7F43300AB8B1A /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 656F3CC820F7F43300AB8B1A /* AppDelegate.m */; }; + 656F3CCC20F7F43300AB8B1A /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 656F3CCB20F7F43300AB8B1A /* ViewController.m */; }; + 656F3CCF20F7F43300AB8B1A /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 656F3CCD20F7F43300AB8B1A /* Main.storyboard */; }; + 656F3CD120F7F43400AB8B1A /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 656F3CD020F7F43400AB8B1A /* Assets.xcassets */; }; + 656F3CD420F7F43400AB8B1A /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 656F3CD220F7F43400AB8B1A /* LaunchScreen.storyboard */; }; + 656F3CD720F7F43400AB8B1A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 656F3CD620F7F43400AB8B1A /* main.m */; }; + 657132C02136260F00F79C70 /* Zapic.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 651BF2A5212B65C400067178 /* Zapic.framework */; }; + 657132C12136260F00F79C70 /* Zapic.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 651BF2A5212B65C400067178 /* Zapic.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; +/* End PBXBuildFile section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 651BF2A9212B65DF00067178 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + 657132C12136260F00F79C70 /* Zapic.framework in Embed Frameworks */, + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 651BF296212B538500067178 /* ZapicExample-ObjC.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "ZapicExample-ObjC.entitlements"; sourceTree = ""; }; + 651BF2A5212B65C400067178 /* Zapic.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Zapic.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 656F3CC420F7F43300AB8B1A /* ZapicExample-ObjC.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "ZapicExample-ObjC.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + 656F3CC720F7F43300AB8B1A /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; + 656F3CC820F7F43300AB8B1A /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; + 656F3CCA20F7F43300AB8B1A /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; + 656F3CCB20F7F43300AB8B1A /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; + 656F3CCE20F7F43300AB8B1A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 656F3CD020F7F43400AB8B1A /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 656F3CD320F7F43400AB8B1A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 656F3CD520F7F43400AB8B1A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 656F3CD620F7F43400AB8B1A /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 656F3CC120F7F43300AB8B1A /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 657132C02136260F00F79C70 /* Zapic.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 656F3CBB20F7F43300AB8B1A = { + isa = PBXGroup; + children = ( + 656F3CC620F7F43300AB8B1A /* ZapicExample-ObjC */, + 656F3CC520F7F43300AB8B1A /* Products */, + 656F3CE620F7F4A600AB8B1A /* Frameworks */, + ); + sourceTree = ""; + }; + 656F3CC520F7F43300AB8B1A /* Products */ = { + isa = PBXGroup; + children = ( + 656F3CC420F7F43300AB8B1A /* ZapicExample-ObjC.app */, + ); + name = Products; + sourceTree = ""; + }; + 656F3CC620F7F43300AB8B1A /* ZapicExample-ObjC */ = { + isa = PBXGroup; + children = ( + 651BF296212B538500067178 /* ZapicExample-ObjC.entitlements */, + 656F3CC720F7F43300AB8B1A /* AppDelegate.h */, + 656F3CC820F7F43300AB8B1A /* AppDelegate.m */, + 656F3CCA20F7F43300AB8B1A /* ViewController.h */, + 656F3CCB20F7F43300AB8B1A /* ViewController.m */, + 656F3CCD20F7F43300AB8B1A /* Main.storyboard */, + 656F3CD020F7F43400AB8B1A /* Assets.xcassets */, + 656F3CD220F7F43400AB8B1A /* LaunchScreen.storyboard */, + 656F3CD520F7F43400AB8B1A /* Info.plist */, + 656F3CD620F7F43400AB8B1A /* main.m */, + ); + path = "ZapicExample-ObjC"; + sourceTree = ""; + }; + 656F3CE620F7F4A600AB8B1A /* Frameworks */ = { + isa = PBXGroup; + children = ( + 651BF2A5212B65C400067178 /* Zapic.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 656F3CC320F7F43300AB8B1A /* ZapicExample-ObjC */ = { + isa = PBXNativeTarget; + buildConfigurationList = 656F3CDA20F7F43400AB8B1A /* Build configuration list for PBXNativeTarget "ZapicExample-ObjC" */; + buildPhases = ( + 656F3CC020F7F43300AB8B1A /* Sources */, + 656F3CC120F7F43300AB8B1A /* Frameworks */, + 656F3CC220F7F43300AB8B1A /* Resources */, + 651BF2A9212B65DF00067178 /* Embed Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "ZapicExample-ObjC"; + productName = "ZapicExample-ObjC"; + productReference = 656F3CC420F7F43300AB8B1A /* ZapicExample-ObjC.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 656F3CBC20F7F43300AB8B1A /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0940; + ORGANIZATIONNAME = Zapic; + TargetAttributes = { + 656F3CC320F7F43300AB8B1A = { + CreatedOnToolsVersion = 9.4.1; + SystemCapabilities = { + com.apple.BackgroundModes = { + enabled = 1; + }; + com.apple.Push = { + enabled = 1; + }; + com.apple.SafariKeychain = { + enabled = 0; + }; + }; + }; + }; + }; + buildConfigurationList = 656F3CBF20F7F43300AB8B1A /* Build configuration list for PBXProject "ZapicExample-ObjC" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 656F3CBB20F7F43300AB8B1A; + productRefGroup = 656F3CC520F7F43300AB8B1A /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 656F3CC320F7F43300AB8B1A /* ZapicExample-ObjC */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 656F3CC220F7F43300AB8B1A /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 656F3CD420F7F43400AB8B1A /* LaunchScreen.storyboard in Resources */, + 656F3CD120F7F43400AB8B1A /* Assets.xcassets in Resources */, + 656F3CCF20F7F43300AB8B1A /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 656F3CC020F7F43300AB8B1A /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 656F3CCC20F7F43300AB8B1A /* ViewController.m in Sources */, + 656F3CD720F7F43400AB8B1A /* main.m in Sources */, + 656F3CC920F7F43300AB8B1A /* AppDelegate.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + 656F3CCD20F7F43300AB8B1A /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 656F3CCE20F7F43300AB8B1A /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + 656F3CD220F7F43400AB8B1A /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 656F3CD320F7F43400AB8B1A /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 656F3CD820F7F43400AB8B1A /* 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_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 = 11.4; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + }; + name = Debug; + }; + 656F3CD920F7F43400AB8B1A /* 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_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 = 11.4; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 656F3CDB20F7F43400AB8B1A /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_ENTITLEMENTS = "ZapicExample-ObjC/ZapicExample-ObjC.entitlements"; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = 24QXGCES3E; + INFOPLIST_FILE = "ZapicExample-ObjC/Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = "com.zapic.iOS-Example"; + PRODUCT_NAME = "$(TARGET_NAME)"; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 656F3CDC20F7F43400AB8B1A /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_ENTITLEMENTS = "ZapicExample-ObjC/ZapicExample-ObjC.entitlements"; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = 24QXGCES3E; + INFOPLIST_FILE = "ZapicExample-ObjC/Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = "com.zapic.iOS-Example"; + PRODUCT_NAME = "$(TARGET_NAME)"; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 656F3CBF20F7F43300AB8B1A /* Build configuration list for PBXProject "ZapicExample-ObjC" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 656F3CD820F7F43400AB8B1A /* Debug */, + 656F3CD920F7F43400AB8B1A /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 656F3CDA20F7F43400AB8B1A /* Build configuration list for PBXNativeTarget "ZapicExample-ObjC" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 656F3CDB20F7F43400AB8B1A /* Debug */, + 656F3CDC20F7F43400AB8B1A /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 656F3CBC20F7F43300AB8B1A /* Project object */; +} diff --git a/Examples/iOS Example/iOS Example.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/ZapicExample-ObjC/ZapicExample-ObjC.xcodeproj/project.xcworkspace/contents.xcworkspacedata similarity index 100% rename from Examples/iOS Example/iOS Example.xcodeproj/project.xcworkspace/contents.xcworkspacedata rename to ZapicExample-ObjC/ZapicExample-ObjC.xcodeproj/project.xcworkspace/contents.xcworkspacedata diff --git a/ZapicExample-ObjC/ZapicExample-ObjC.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/ZapicExample-ObjC/ZapicExample-ObjC.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/ZapicExample-ObjC/ZapicExample-ObjC.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/ZapicExample-ObjC/ZapicExample-ObjC/AppDelegate.h b/ZapicExample-ObjC/ZapicExample-ObjC/AppDelegate.h new file mode 100644 index 0000000..07249d8 --- /dev/null +++ b/ZapicExample-ObjC/ZapicExample-ObjC/AppDelegate.h @@ -0,0 +1,17 @@ +// +// AppDelegate.h +// ZapicExample-ObjC +// +// Created by Daniel Sarfati on 7/12/18. +// Copyright © 2018 Zapic. All rights reserved. +// + +#import + +@interface AppDelegate : UIResponder + +@property (strong, nonatomic) UIWindow *window; + + +@end + diff --git a/ZapicExample-ObjC/ZapicExample-ObjC/AppDelegate.m b/ZapicExample-ObjC/ZapicExample-ObjC/AppDelegate.m new file mode 100644 index 0000000..aa91ab0 --- /dev/null +++ b/ZapicExample-ObjC/ZapicExample-ObjC/AppDelegate.m @@ -0,0 +1,77 @@ +// +// AppDelegate.m +// ZapicExample-ObjC +// +// Created by Daniel Sarfati on 7/12/18. +// Copyright © 2018 Zapic. All rights reserved. +// + +#import "AppDelegate.h" +@import Zapic; + +@interface AppDelegate () + +@end + +@implementation AppDelegate + + +- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { + + Zapic.loginHandler = ^(ZPCPlayer* p) { + NSLog(@"Player logged in"); + //Do stuff here + + [Zapic getChallenges:^(NSArray *challenges, NSError *error) { + if(error){ + NSLog(@"Error getting challenges"); + }else{ + NSLog(@"Found %lu challenges!",(unsigned long)challenges.count); + } + }]; + + [Zapic getStatistics:^(NSArray *statistics, NSError *error) { + if(error){ + NSLog(@"Error getting stats"); + }else{ + NSLog(@"Found %lu stats!",(unsigned long)statistics.count); + } + }]; + + [Zapic getCompetitions:^(NSArray *competitions, NSError *error) { + if(error){ + NSLog(@"Error getting competitions"); + }else{ + NSLog(@"Found %lu competitions!",(unsigned long)competitions.count); + } + }]; + + [Zapic getPlayer:^(ZPCPlayer *player, NSError *error) { + if(error){ + NSLog(@"Error getting player"); + }else{ + NSLog(@"Got the player"); + } + }]; + + }; + + Zapic.logoutHandler = ^(ZPCPlayer* p) { + NSLog(@"Player logged out"); + //Do stuff here + }; + + [Zapic start]; + + /* + Get the current player, this will very likely be nil since + Zapic is still loading the player at this point. + */ + [Zapic getPlayer:^(ZPCPlayer *player, NSError *error) { + NSLog(@"Current player: %@", player); + }]; + + // Override point for customization after application launch. + return YES; +} +@end diff --git a/Examples/iOS Example/iOS Example/Assets.xcassets/AppIcon.appiconset/Contents.json b/ZapicExample-ObjC/ZapicExample-ObjC/Assets.xcassets/AppIcon.appiconset/Contents.json similarity index 100% rename from Examples/iOS Example/iOS Example/Assets.xcassets/AppIcon.appiconset/Contents.json rename to ZapicExample-ObjC/ZapicExample-ObjC/Assets.xcassets/AppIcon.appiconset/Contents.json diff --git a/Examples/iOS Example/iOS Example/Assets.xcassets/Contents.json b/ZapicExample-ObjC/ZapicExample-ObjC/Assets.xcassets/Contents.json similarity index 100% rename from Examples/iOS Example/iOS Example/Assets.xcassets/Contents.json rename to ZapicExample-ObjC/ZapicExample-ObjC/Assets.xcassets/Contents.json diff --git a/Examples/iOS Example/iOS Example/Base.lproj/LaunchScreen.storyboard b/ZapicExample-ObjC/ZapicExample-ObjC/Base.lproj/LaunchScreen.storyboard similarity index 70% rename from Examples/iOS Example/iOS Example/Base.lproj/LaunchScreen.storyboard rename to ZapicExample-ObjC/ZapicExample-ObjC/Base.lproj/LaunchScreen.storyboard index fdf3f97..f83f6fd 100644 --- a/Examples/iOS Example/iOS Example/Base.lproj/LaunchScreen.storyboard +++ b/ZapicExample-ObjC/ZapicExample-ObjC/Base.lproj/LaunchScreen.storyboard @@ -1,7 +1,8 @@ - + - + + @@ -9,14 +10,11 @@ - - - - + diff --git a/ZapicExample-ObjC/ZapicExample-ObjC/Base.lproj/Main.storyboard b/ZapicExample-ObjC/ZapicExample-ObjC/Base.lproj/Main.storyboard new file mode 100644 index 0000000..d7515dc --- /dev/null +++ b/ZapicExample-ObjC/ZapicExample-ObjC/Base.lproj/Main.storyboard @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ZapicExample-ObjC/ZapicExample-ObjC/Info.plist b/ZapicExample-ObjC/ZapicExample-ObjC/Info.plist new file mode 100644 index 0000000..318a1e1 --- /dev/null +++ b/ZapicExample-ObjC/ZapicExample-ObjC/Info.plist @@ -0,0 +1,60 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleURLTypes + + + CFBundleURLName + com.zapic.iOS-Example + CFBundleURLSchemes + + zapicDeepLink + + + + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UIBackgroundModes + + remote-notification + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/ZapicExample-ObjC/ZapicExample-ObjC/ViewController.h b/ZapicExample-ObjC/ZapicExample-ObjC/ViewController.h new file mode 100644 index 0000000..f9c95e5 --- /dev/null +++ b/ZapicExample-ObjC/ZapicExample-ObjC/ViewController.h @@ -0,0 +1,15 @@ +// +// ViewController.h +// ZapicExample-ObjC +// +// Created by Daniel Sarfati on 7/12/18. +// Copyright © 2018 Zapic. All rights reserved. +// + +#import + +@interface ViewController : UIViewController + + +@end + diff --git a/ZapicExample-ObjC/ZapicExample-ObjC/ViewController.m b/ZapicExample-ObjC/ZapicExample-ObjC/ViewController.m new file mode 100644 index 0000000..593c062 --- /dev/null +++ b/ZapicExample-ObjC/ZapicExample-ObjC/ViewController.m @@ -0,0 +1,41 @@ +// +// ViewController.m +// ZapicExample-ObjC +// +// Created by Daniel Sarfati on 7/12/18. +// Copyright © 2018 Zapic. All rights reserved. +// + +#import "ViewController.h" +#import "Zapic/Zapic.h" + +@interface ViewController () +@end + +@implementation ViewController + +- (void)viewDidLoad { + [super viewDidLoad]; + + // Do any additional setup after loading the view, typically from a nib. +} + +- (void) viewDidAppear:(BOOL)animated{ + // [Zapic performSelector:@selector(showDefaultPage) withObject:nil afterDelay:3]; +// [Zapic showDefaultPage]; +// [Zapic submitEvent:@{ @"Distance": @147,@"Score":@22}]; +} + + +- (void)didReceiveMemoryWarning { + [super didReceiveMemoryWarning]; + // Dispose of any resources that can be recreated. +} + +-(void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{ + [Zapic showDefaultPage]; + [Zapic submitEvent:@{ @"Distance": @147,@"Score":@22}]; +} + + +@end diff --git a/ZapicExample-ObjC/ZapicExample-ObjC/ZapicExample-ObjC.entitlements b/ZapicExample-ObjC/ZapicExample-ObjC/ZapicExample-ObjC.entitlements new file mode 100644 index 0000000..903def2 --- /dev/null +++ b/ZapicExample-ObjC/ZapicExample-ObjC/ZapicExample-ObjC.entitlements @@ -0,0 +1,8 @@ + + + + + aps-environment + development + + diff --git a/ZapicExample-ObjC/ZapicExample-ObjC/main.m b/ZapicExample-ObjC/ZapicExample-ObjC/main.m new file mode 100644 index 0000000..7449a68 --- /dev/null +++ b/ZapicExample-ObjC/ZapicExample-ObjC/main.m @@ -0,0 +1,16 @@ +// +// main.m +// ZapicExample-ObjC +// +// Created by Daniel Sarfati on 7/12/18. +// Copyright © 2018 Zapic. All rights reserved. +// + +#import +#import "AppDelegate.h" + +int main(int argc, char * argv[]) { + @autoreleasepool { + return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); + } +} diff --git a/ZapicExample-Swift/ZapicExample-Swift.xcodeproj/project.pbxproj b/ZapicExample-Swift/ZapicExample-Swift.xcodeproj/project.pbxproj new file mode 100644 index 0000000..c52c79a --- /dev/null +++ b/ZapicExample-Swift/ZapicExample-Swift.xcodeproj/project.pbxproj @@ -0,0 +1,353 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 50; + objects = { + +/* Begin PBXBuildFile section */ + 651BF274212B127600067178 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 651BF273212B127600067178 /* AppDelegate.swift */; }; + 651BF276212B127600067178 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 651BF275212B127600067178 /* ViewController.swift */; }; + 651BF279212B127600067178 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 651BF277212B127600067178 /* Main.storyboard */; }; + 651BF27B212B127700067178 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 651BF27A212B127700067178 /* Assets.xcassets */; }; + 651BF27E212B127700067178 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 651BF27C212B127700067178 /* LaunchScreen.storyboard */; }; + 657132B12135A6C700F79C70 /* Zapic.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 657132B02135A6C700F79C70 /* Zapic.framework */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 651BF270212B127600067178 /* ZapicExample-Swift.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "ZapicExample-Swift.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + 651BF273212B127600067178 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 651BF275212B127600067178 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; + 651BF278212B127600067178 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 651BF27A212B127700067178 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 651BF27D212B127700067178 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 651BF27F212B127700067178 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 657132B02135A6C700F79C70 /* Zapic.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Zapic.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 6578A667212F2FDE00A8C4A7 /* Zapic.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Zapic.framework; sourceTree = BUILT_PRODUCTS_DIR; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 651BF26D212B127600067178 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 657132B12135A6C700F79C70 /* Zapic.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 651BF267212B127600067178 = { + isa = PBXGroup; + children = ( + 651BF272212B127600067178 /* ZapicExample-Swift */, + 651BF271212B127600067178 /* Products */, + 6578A666212F2FDE00A8C4A7 /* Frameworks */, + ); + sourceTree = ""; + }; + 651BF271212B127600067178 /* Products */ = { + isa = PBXGroup; + children = ( + 651BF270212B127600067178 /* ZapicExample-Swift.app */, + ); + name = Products; + sourceTree = ""; + }; + 651BF272212B127600067178 /* ZapicExample-Swift */ = { + isa = PBXGroup; + children = ( + 651BF273212B127600067178 /* AppDelegate.swift */, + 651BF275212B127600067178 /* ViewController.swift */, + 651BF277212B127600067178 /* Main.storyboard */, + 651BF27A212B127700067178 /* Assets.xcassets */, + 651BF27C212B127700067178 /* LaunchScreen.storyboard */, + 651BF27F212B127700067178 /* Info.plist */, + ); + path = "ZapicExample-Swift"; + sourceTree = ""; + }; + 6578A666212F2FDE00A8C4A7 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 657132B02135A6C700F79C70 /* Zapic.framework */, + 6578A667212F2FDE00A8C4A7 /* Zapic.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 651BF26F212B127600067178 /* ZapicExample-Swift */ = { + isa = PBXNativeTarget; + buildConfigurationList = 651BF282212B127700067178 /* Build configuration list for PBXNativeTarget "ZapicExample-Swift" */; + buildPhases = ( + 651BF26C212B127600067178 /* Sources */, + 651BF26D212B127600067178 /* Frameworks */, + 651BF26E212B127600067178 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "ZapicExample-Swift"; + productName = "ZapicExample-Swift"; + productReference = 651BF270212B127600067178 /* ZapicExample-Swift.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 651BF268212B127600067178 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 0940; + LastUpgradeCheck = 0940; + ORGANIZATIONNAME = Zapic; + TargetAttributes = { + 651BF26F212B127600067178 = { + CreatedOnToolsVersion = 9.4.1; + }; + }; + }; + buildConfigurationList = 651BF26B212B127600067178 /* Build configuration list for PBXProject "ZapicExample-Swift" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 651BF267212B127600067178; + productRefGroup = 651BF271212B127600067178 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 651BF26F212B127600067178 /* ZapicExample-Swift */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 651BF26E212B127600067178 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 651BF27E212B127700067178 /* LaunchScreen.storyboard in Resources */, + 651BF27B212B127700067178 /* Assets.xcassets in Resources */, + 651BF279212B127600067178 /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 651BF26C212B127600067178 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 651BF276212B127600067178 /* ViewController.swift in Sources */, + 651BF274212B127600067178 /* AppDelegate.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + 651BF277212B127600067178 /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 651BF278212B127600067178 /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + 651BF27C212B127700067178 /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 651BF27D212B127700067178 /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 651BF280212B127700067178 /* 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_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 = 11.4; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 651BF281212B127700067178 /* 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_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 = 11.4; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 651BF283212B127700067178 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = 24QXGCES3E; + INFOPLIST_FILE = "ZapicExample-Swift/Info.plist"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = "com.zapic.iOS-Example"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 4.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 651BF284212B127700067178 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = 24QXGCES3E; + INFOPLIST_FILE = "ZapicExample-Swift/Info.plist"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = "com.zapic.iOS-Example"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 4.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 651BF26B212B127600067178 /* Build configuration list for PBXProject "ZapicExample-Swift" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 651BF280212B127700067178 /* Debug */, + 651BF281212B127700067178 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 651BF282212B127700067178 /* Build configuration list for PBXNativeTarget "ZapicExample-Swift" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 651BF283212B127700067178 /* Debug */, + 651BF284212B127700067178 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 651BF268212B127600067178 /* Project object */; +} diff --git a/Examples/iOS Example/iOS Example/AppDelegate.swift b/ZapicExample-Swift/ZapicExample-Swift/AppDelegate.swift similarity index 90% rename from Examples/iOS Example/iOS Example/AppDelegate.swift rename to ZapicExample-Swift/ZapicExample-Swift/AppDelegate.swift index 5bf201f..bed895c 100644 --- a/Examples/iOS Example/iOS Example/AppDelegate.swift +++ b/ZapicExample-Swift/ZapicExample-Swift/AppDelegate.swift @@ -1,9 +1,9 @@ // // AppDelegate.swift -// iOS Example +// ZapicExample-Swift // -// Created by Daniel Sarfati on 7/12/17. -// Copyright © 2017 zapic. All rights reserved. +// Created by Daniel Sarfati on 8/20/18. +// Copyright © 2018 Zapic. All rights reserved. // import UIKit @@ -11,39 +11,38 @@ import Zapic @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { - + var window: UIWindow? - - + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { - ZLog.isEnabled = true - Zapic.start("0.1") + Zapic.start() + // Override point for customization after application launch. return true } - + 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. } - + func applicationWillTerminate(_ application: UIApplication) { // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. } - - + + } diff --git a/ZapicExample-Swift/ZapicExample-Swift/Assets.xcassets/AppIcon.appiconset/Contents.json b/ZapicExample-Swift/ZapicExample-Swift/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..d8db8d6 --- /dev/null +++ b/ZapicExample-Swift/ZapicExample-Swift/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,98 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "size" : "20x20", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "20x20", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "3x" + }, + { + "idiom" : "ipad", + "size" : "20x20", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "20x20", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "83.5x83.5", + "scale" : "2x" + }, + { + "idiom" : "ios-marketing", + "size" : "1024x1024", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Zapic/ZapicAssets.xcassets/Contents.json b/ZapicExample-Swift/ZapicExample-Swift/Assets.xcassets/Contents.json similarity index 100% rename from Zapic/ZapicAssets.xcassets/Contents.json rename to ZapicExample-Swift/ZapicExample-Swift/Assets.xcassets/Contents.json diff --git a/ZapicExample-Swift/ZapicExample-Swift/Base.lproj/LaunchScreen.storyboard b/ZapicExample-Swift/ZapicExample-Swift/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 0000000..f83f6fd --- /dev/null +++ b/ZapicExample-Swift/ZapicExample-Swift/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ZapicExample-Swift/ZapicExample-Swift/Base.lproj/Main.storyboard b/ZapicExample-Swift/ZapicExample-Swift/Base.lproj/Main.storyboard new file mode 100644 index 0000000..03c13c2 --- /dev/null +++ b/ZapicExample-Swift/ZapicExample-Swift/Base.lproj/Main.storyboard @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Examples/iOS Example/iOS Example/Info.plist b/ZapicExample-Swift/ZapicExample-Swift/Info.plist similarity index 77% rename from Examples/iOS Example/iOS Example/Info.plist rename to ZapicExample-Swift/ZapicExample-Swift/Info.plist index b77eb48..16be3b6 100644 --- a/Examples/iOS Example/iOS Example/Info.plist +++ b/ZapicExample-Swift/ZapicExample-Swift/Info.plist @@ -3,7 +3,7 @@ CFBundleDevelopmentRegion - en + $(DEVELOPMENT_LANGUAGE) CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier @@ -20,15 +20,6 @@ 1 LSRequiresIPhoneOS - NSAppTransportSecurity - - NSAllowsArbitraryLoads - - - NSContactsUsageDescription - We'll use your Contacts to find people you know on Zapic. - NSPhotoLibraryUsageDescription - Zapic will only use the Photos you select. UILaunchStoryboardName LaunchScreen UIMainStoryboardFile @@ -37,11 +28,11 @@ armv7 - UIStatusBarHidden - UISupportedInterfaceOrientations UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight UISupportedInterfaceOrientations~ipad diff --git a/ZapicExample-Swift/ZapicExample-Swift/ViewController.swift b/ZapicExample-Swift/ZapicExample-Swift/ViewController.swift new file mode 100644 index 0000000..9052ae3 --- /dev/null +++ b/ZapicExample-Swift/ZapicExample-Swift/ViewController.swift @@ -0,0 +1,25 @@ +// +// ViewController.swift +// ZapicExample-Swift +// +// Created by Daniel Sarfati on 8/20/18. +// Copyright © 2018 Zapic. All rights reserved. +// + +import UIKit + +class ViewController: UIViewController { + + override func viewDidLoad() { + super.viewDidLoad() + // Do any additional setup after loading the view, typically from a nib. + } + + override func didReceiveMemoryWarning() { + super.didReceiveMemoryWarning() + // Dispose of any resources that can be recreated. + } + + +} + diff --git a/ZapicTests/Info.plist b/ZapicTests/Info.plist index 6c6c23c..6c40a6c 100644 --- a/ZapicTests/Info.plist +++ b/ZapicTests/Info.plist @@ -3,7 +3,7 @@ CFBundleDevelopmentRegion - en + $(DEVELOPMENT_LANGUAGE) CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier diff --git a/ZapicTests/StringExtensionsSpec.swift b/ZapicTests/StringExtensionsSpec.swift deleted file mode 100644 index 2c895af..0000000 --- a/ZapicTests/StringExtensionsSpec.swift +++ /dev/null @@ -1,48 +0,0 @@ -// -// StringExtensionsSpec.swift -// ZapicTests -// -// Created by Daniel Sarfati on 10/27/17. -// Copyright © 2017 zapic. All rights reserved. -// - -import Foundation -import XCTest - -import Quick -import Nimble - -@testable import Zapic - -class StringExtensionsSpec: QuickSpec { - override func spec(){ - describe("strings"){ - context("convert to a UUID"){ - it("is valid format with dashes"){ - - // Arrange - let idString = "9bd76e20-f408-4a8f-af94-c65da4378e66" - - // Act - let result = idString.asUUID() - - // Assert - expect(result).toNot(beNil()) - expect(result?.uuidString.lowercased()).to(equal(idString.lowercased())) - } - - it("is invalid without dashes"){ - - // Arrange - let idString = "9bd76e20f4084a8faf94c65da4378e66" - - // Act - let result = idString.asUUID() - - // Assert - expect(result).to(beNil()) - } - } - } - } -} diff --git a/ZapicTests/ZapicSpec.swift b/ZapicTests/ZapicSpec.swift deleted file mode 100644 index 50522d1..0000000 --- a/ZapicTests/ZapicSpec.swift +++ /dev/null @@ -1,118 +0,0 @@ -// -// ZapicTests.swift -// ZapicTests -// -// Created by Daniel Sarfati on 7/25/17. -// Copyright © 2017 zapic. All rights reserved. -// - -import XCTest - -import Quick -import Nimble - -@testable import Zapic - -class ZapicSpec: QuickSpec { - override func spec(){ - - xdescribe("zapic core"){ - - var webClient: MockWebClient! - var zapic:ZapicCore! - - beforeEach { - webClient = MockWebClient() - zapic = ZapicCore(webClient:webClient) - } - - describe("start"){ - it("should load the web client"){ - zapic.start(version: "1") - - expect(webClient.isLoaded).to(beTrue()) - } - - it("should only load once"){ - zapic.start(version: "1") - zapic.start(version: "1") - - expect(webClient.loadedCount).to(equal(1)) - } - - it("should send the app started event"){ - zapic.start(version: "1") - - expect(webClient.eventTotals).to(equal(1)) - } - } - - describe("events"){ - -// it("should fail with reserved event names"){ -// expect { try zapic.submitEvent(eventId: ZapicEvents.appStarted.rawValue) }.to(throwError(ZapicError.reservedEventId)) -// } -// -// describe("should send events"){ -// xit("with no value"){ -// -//// let eventId = "TEST_EVENT" -//// -//// expect{ try zapic.submitEvent(eventId: eventId) }.toNot(throwError()) -//// -//// expect(webClient.eventCounts[Event.gameplay]).to(equal(1)) -//// expect(webClient.latestValue[eventId]!).to(beNil()) -// } -// -// it("with a value"){ -// -// let eventId = "TEST_EVENT" -// -// expect{ try zapic.submitEvent(eventType: .gameplay, params: [eventId:22]) }.toNot(throwError()) -// -// expect(webClient.eventCounts[.gameplay]).to(equal(1)) -// expect(webClient.latestValue[eventId]!).to(equal(22)) -// } -// } - } - } - } -} - -class MockWebClient: ZapicWebClient{ - - var isLoaded = false - var loadedCount = 0 - - var eventTotals = 0 - var eventCounts = [EventType: Int]() - var latestValue = [String: Any]() - - func load(){ - isLoaded = true - loadedCount += 1 - } - - func submitEvent(eventType: EventType, params: [String : Any]) { - eventTotals += 1 - - if eventCounts[eventType] == nil { - eventCounts[eventType] = 1 - } - else{ - eventCounts[eventType] = eventCounts[eventType]! + 1 - } - - for kvp in params { - latestValue[kvp.key] = kvp.value - } - } - - var zapicDelegate: ZapicDelegate? = nil - - func dispatchToJS(type: WebFunction, payload:Any){} - func dispatchToJS(type: WebFunction, payload:Any, isError: Bool){} - - /// Attempt to resend all events that we unable to send - func resendFailedEvents(){} -} diff --git a/ZapicTests/ZapicTests.m b/ZapicTests/ZapicTests.m new file mode 100644 index 0000000..99ccfa4 --- /dev/null +++ b/ZapicTests/ZapicTests.m @@ -0,0 +1,31 @@ +#import + +@interface ZapicTests : XCTestCase + +@end + +@implementation ZapicTests + +- (void)setUp { + [super setUp]; + // Put setup code here. This method is called before the invocation of each test method in the class. +} + +- (void)tearDown { + // Put teardown code here. This method is called after the invocation of each test method in the class. + [super tearDown]; +} + +- (void)testExample { + // This is an example of a functional test case. + // Use XCTAssert and related functions to verify your tests produce the correct results. +} + +- (void)testPerformanceExample { + // This is an example of a performance test case. + [self measureBlock:^{ + // Put the code you want to measure the time of here. + }]; +} + +@end diff --git a/ZapicTests/ZapicUtilsSpec.swift b/ZapicTests/ZapicUtilsSpec.swift deleted file mode 100644 index 1b2a690..0000000 --- a/ZapicTests/ZapicUtilsSpec.swift +++ /dev/null @@ -1,144 +0,0 @@ -// -// ZapicUtilsSpec.swift -// ZapicTests -// -// Created by Daniel Sarfati on 10/12/17. -// Copyright © 2017 zapic. All rights reserved. -// - -import Foundation -import XCTest - -import Quick -import Nimble - -@testable import Zapic - -class ZapicUtilsSpec: QuickSpec { - override func spec(){ - describe("zapic utilities"){ - context("serialization"){ - it("supports basic key value pairs"){ - - // Arrange - let message = ["a":2] - let json = ZapicUtils.serialize(data: message); - let data = (json?.data(using:.utf8))! - - // Act - let result = ZapicUtils.deserialize(bodyData: data) - - // Assert - expect(result).toNot(beNil()) - } - - it("allow nested dictionary"){ - - // Arrange - let message = ["a":["a1":1]] - let json = ZapicUtils.serialize(data: message); - let data = (json?.data(using:.utf8))! - - // Act - let result = ZapicUtils.deserialize(bodyData: data) - - // Assert - expect(result).toNot(beNil()) - } - } - context("string formatting"){ - it("handles nil args"){ - - // Arrange - let message = "Hello World" - let args:[String]? = nil - - // Act - let result = ZapicUtils.formatString(message: message, args: args) - - // Assert - expect(result).to(equal(message)) - } - - it("handles empty args"){ - - // Arrange - let message = "Hello World" - let args:[String] = [] - - // Act - let result = ZapicUtils.formatString(message: message, args: args) - - // Assert - expect(result).to(equal(message)) - } - - it("handles an ending arg"){ - - // Arrange - let message = "Hello %s" - let args = ["World"] - - // Act - let result = ZapicUtils.formatString(message: message, args: args) - - // Assert - expect(result).to(equal("Hello World")) - } - - it("handles a starting arg"){ - - // Arrange - let message = "%s World" - let args = ["Hello"] - - // Act - let result = ZapicUtils.formatString(message: message, args: args) - - // Assert - expect(result).to(equal("Hello World")) - } - - it("handles multiple arg"){ - - // Arrange - let message = "%s %s" - let args = ["Hello","World"] - - // Act - let result = ZapicUtils.formatString(message: message, args: args) - - // Assert - expect(result).to(equal("Hello World")) - } - - it("handles neighbor character after an arg"){ - - // Arrange - let message = "Hello %s, Again" - let args = ["World"] - - // Act - let result = ZapicUtils.formatString(message: message, args: args) - - // Assert - expect(result).to(equal("Hello World, Again")) - } - - it("handles neighbor character after an arg"){ - - // Arrange - let message = "Hello ,%s Again" - let args = ["World"] - - // Act - let result = ZapicUtils.formatString(message: message, args: args) - - // Assert - expect(result).to(equal("Hello ,World Again")) - } - } - } - } -} -