diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..16a9982 --- /dev/null +++ b/.gitignore @@ -0,0 +1,22 @@ +build/ +*.pbxuser +!default.pbxuser +*.mode1v3 +!default.mode1v3 +*.mode2v3 +!default.mode2v3 +*.perspectivev3 +!default.perspectivev3 +xcuserdata +*.xccheckout +*.moved-aside +DerivedData +*.xcuserstate + +.DS_Store +.AppleDouble +.LSOverride + +# Files that might appear on external disk +.Spotlight-V100 +.Trashes \ No newline at end of file diff --git a/Armchair.xcodeproj/project.pbxproj b/Armchair.xcodeproj/project.pbxproj new file mode 100644 index 0000000..ea545e6 --- /dev/null +++ b/Armchair.xcodeproj/project.pbxproj @@ -0,0 +1,515 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + E6D8B8F119C756A4001AD043 /* Armchair.swift in Sources */ = {isa = PBXBuildFile; fileRef = F897FF4019AA800700AB5182 /* Armchair.swift */; }; + E6D8B8F419C756A4001AD043 /* Armchair.h in Headers */ = {isa = PBXBuildFile; fileRef = F8111E3819A95C8B0040E7D1 /* Armchair.h */; settings = {ATTRIBUTES = (Public, ); }; }; + F8111E3919A95C8B0040E7D1 /* Armchair.h in Headers */ = {isa = PBXBuildFile; fileRef = F8111E3819A95C8B0040E7D1 /* Armchair.h */; settings = {ATTRIBUTES = (Public, ); }; }; + F8111E6119A9674D0040E7D1 /* ArmchairTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8111E5C19A9674D0040E7D1 /* ArmchairTests.swift */; }; + F897FF4119AA800700AB5182 /* Armchair.swift in Sources */ = {isa = PBXBuildFile; fileRef = F897FF4019AA800700AB5182 /* Armchair.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + F8111E6519A967880040E7D1 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F8111E2A19A95C8B0040E7D1 /* Project object */; + proxyType = 1; + remoteGlobalIDString = F8111E3219A95C8B0040E7D1; + remoteInfo = Armchair; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + E6D8B8B519C7274F001AD043 /* Armchair.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Armchair.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + E6D8B8B619C7274F001AD043 /* ArmchairTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ArmchairTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + E6D8B8F919C756A4001AD043 /* ArmchairMac.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ArmchairMac.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + F8111E3719A95C8B0040E7D1 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + F8111E3819A95C8B0040E7D1 /* Armchair.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Armchair.h; sourceTree = ""; }; + F8111E4119A95C8B0040E7D1 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + F8111E5C19A9674D0040E7D1 /* ArmchairTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ArmchairTests.swift; sourceTree = ""; }; + F897FF4019AA800700AB5182 /* Armchair.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Armchair.swift; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + E6D8B8F219C756A4001AD043 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F8111E2F19A95C8B0040E7D1 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F8111E3B19A95C8B0040E7D1 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + F8111E2919A95C8B0040E7D1 = { + isa = PBXGroup; + children = ( + F8111E3519A95C8B0040E7D1 /* Source */, + F8111E3F19A95C8B0040E7D1 /* Tests */, + E6D8B8B519C7274F001AD043 /* Armchair.framework */, + E6D8B8F919C756A4001AD043 /* ArmchairMac.framework */, + E6D8B8B619C7274F001AD043 /* ArmchairTests.xctest */, + ); + sourceTree = ""; + }; + F8111E3519A95C8B0040E7D1 /* Source */ = { + isa = PBXGroup; + children = ( + F8111E3819A95C8B0040E7D1 /* Armchair.h */, + F897FF4019AA800700AB5182 /* Armchair.swift */, + F8111E3619A95C8B0040E7D1 /* Supporting Files */, + ); + path = Source; + sourceTree = ""; + }; + F8111E3619A95C8B0040E7D1 /* Supporting Files */ = { + isa = PBXGroup; + children = ( + F8111E3719A95C8B0040E7D1 /* Info.plist */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; + F8111E3F19A95C8B0040E7D1 /* Tests */ = { + isa = PBXGroup; + children = ( + F8111E5C19A9674D0040E7D1 /* ArmchairTests.swift */, + F8111E4019A95C8B0040E7D1 /* Supporting Files */, + ); + path = Tests; + sourceTree = ""; + }; + F8111E4019A95C8B0040E7D1 /* Supporting Files */ = { + isa = PBXGroup; + children = ( + F8111E4119A95C8B0040E7D1 /* Info.plist */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + E6D8B8F319C756A4001AD043 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + E6D8B8F419C756A4001AD043 /* Armchair.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F8111E3019A95C8B0040E7D1 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + F8111E3919A95C8B0040E7D1 /* Armchair.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + E6D8B8EF19C756A4001AD043 /* ArmchairMac */ = { + isa = PBXNativeTarget; + buildConfigurationList = E6D8B8F619C756A4001AD043 /* Build configuration list for PBXNativeTarget "ArmchairMac" */; + buildPhases = ( + E6D8B8F019C756A4001AD043 /* Sources */, + E6D8B8F219C756A4001AD043 /* Frameworks */, + E6D8B8F319C756A4001AD043 /* Headers */, + E6D8B8F519C756A4001AD043 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = ArmchairMac; + productName = Armchair; + productReference = E6D8B8F919C756A4001AD043 /* ArmchairMac.framework */; + productType = "com.apple.product-type.framework"; + }; + F8111E3219A95C8B0040E7D1 /* Armchair */ = { + isa = PBXNativeTarget; + buildConfigurationList = F8111E4619A95C8B0040E7D1 /* Build configuration list for PBXNativeTarget "Armchair" */; + buildPhases = ( + F8111E2E19A95C8B0040E7D1 /* Sources */, + F8111E2F19A95C8B0040E7D1 /* Frameworks */, + F8111E3019A95C8B0040E7D1 /* Headers */, + F8111E3119A95C8B0040E7D1 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Armchair; + productName = Armchair; + productReference = E6D8B8B519C7274F001AD043 /* Armchair.framework */; + productType = "com.apple.product-type.framework"; + }; + F8111E3D19A95C8B0040E7D1 /* ArmchairTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = F8111E4919A95C8B0040E7D1 /* Build configuration list for PBXNativeTarget "ArmchairTests" */; + buildPhases = ( + F8111E3A19A95C8B0040E7D1 /* Sources */, + F8111E3B19A95C8B0040E7D1 /* Frameworks */, + F8111E3C19A95C8B0040E7D1 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + F8111E6619A967880040E7D1 /* PBXTargetDependency */, + ); + name = ArmchairTests; + productName = ArmchairTests; + productReference = E6D8B8B619C7274F001AD043 /* ArmchairTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + F8111E2A19A95C8B0040E7D1 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0600; + ORGANIZATIONNAME = Armchair; + TargetAttributes = { + F8111E3219A95C8B0040E7D1 = { + CreatedOnToolsVersion = 6.0; + }; + F8111E3D19A95C8B0040E7D1 = { + CreatedOnToolsVersion = 6.0; + }; + }; + }; + buildConfigurationList = F8111E2D19A95C8B0040E7D1 /* Build configuration list for PBXProject "Armchair" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = F8111E2919A95C8B0040E7D1; + productRefGroup = F8111E2919A95C8B0040E7D1; + projectDirPath = ""; + projectRoot = ""; + targets = ( + F8111E3219A95C8B0040E7D1 /* Armchair */, + E6D8B8EF19C756A4001AD043 /* ArmchairMac */, + F8111E3D19A95C8B0040E7D1 /* ArmchairTests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + E6D8B8F519C756A4001AD043 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F8111E3119A95C8B0040E7D1 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F8111E3C19A95C8B0040E7D1 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + E6D8B8F019C756A4001AD043 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + E6D8B8F119C756A4001AD043 /* Armchair.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F8111E2E19A95C8B0040E7D1 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F897FF4119AA800700AB5182 /* Armchair.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F8111E3A19A95C8B0040E7D1 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F8111E6119A9674D0040E7D1 /* ArmchairTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + F8111E6619A967880040E7D1 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = F8111E3219A95C8B0040E7D1 /* Armchair */; + targetProxy = F8111E6519A967880040E7D1 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + E6D8B8F719C756A4001AD043 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = "$(SRCROOT)/Source/Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 7.1; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MACOSX_DEPLOYMENT_TARGET = 10.10; + PRODUCT_NAME = ArmchairMac; + SDKROOT = macosx; + SKIP_INSTALL = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + E6D8B8F819C756A4001AD043 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = "$(SRCROOT)/Source/Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 7.1; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MACOSX_DEPLOYMENT_TARGET = 10.10; + PRODUCT_NAME = ArmchairMac; + SDKROOT = macosx; + SKIP_INSTALL = YES; + }; + name = Release; + }; + F8111E4419A95C8B0040E7D1 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_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 = 7.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + OTHER_SWIFT_FLAGS = "-DDebug"; + SDKROOT = iphoneos; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + F8111E4519A95C8B0040E7D1 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = YES; + CURRENT_PROJECT_VERSION = 1; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + 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 = 7.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + F8111E4719A95C8B0040E7D1 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = "$(SRCROOT)/Source/Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 7.1; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + F8111E4819A95C8B0040E7D1 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = "$(SRCROOT)/Source/Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 7.1; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + }; + name = Release; + }; + F8111E4A19A95C8B0040E7D1 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + FRAMEWORK_SEARCH_PATHS = ( + "$(SDKROOT)/Developer/Library/Frameworks", + "$(inherited)", + ); + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + INFOPLIST_FILE = Tests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + F8111E4B19A95C8B0040E7D1 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + FRAMEWORK_SEARCH_PATHS = ( + "$(SDKROOT)/Developer/Library/Frameworks", + "$(inherited)", + ); + INFOPLIST_FILE = Tests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + E6D8B8F619C756A4001AD043 /* Build configuration list for PBXNativeTarget "ArmchairMac" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + E6D8B8F719C756A4001AD043 /* Debug */, + E6D8B8F819C756A4001AD043 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + F8111E2D19A95C8B0040E7D1 /* Build configuration list for PBXProject "Armchair" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F8111E4419A95C8B0040E7D1 /* Debug */, + F8111E4519A95C8B0040E7D1 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + F8111E4619A95C8B0040E7D1 /* Build configuration list for PBXNativeTarget "Armchair" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F8111E4719A95C8B0040E7D1 /* Debug */, + F8111E4819A95C8B0040E7D1 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + F8111E4919A95C8B0040E7D1 /* Build configuration list for PBXNativeTarget "ArmchairTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F8111E4A19A95C8B0040E7D1 /* Debug */, + F8111E4B19A95C8B0040E7D1 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = F8111E2A19A95C8B0040E7D1 /* Project object */; +} diff --git a/Armchair.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/Armchair.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..7d374f9 --- /dev/null +++ b/Armchair.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/Armchair.xcworkspace/contents.xcworkspacedata b/Armchair.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..7a32bcb --- /dev/null +++ b/Armchair.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,13 @@ + + + + + + + + + diff --git a/Example/AppDelegate.h b/Example/AppDelegate.h new file mode 100644 index 0000000..36d79f2 --- /dev/null +++ b/Example/AppDelegate.h @@ -0,0 +1,15 @@ +// +// AppDelegate.h +// Mac Example +// +// Created by Matt Coneybeare on 9/15/14. +// Copyright (c) 2014 Armchair. All rights reserved. +// + +#import + +@interface AppDelegate : NSObject + + +@end + diff --git a/Example/AppDelegate.m b/Example/AppDelegate.m new file mode 100644 index 0000000..24a3f92 --- /dev/null +++ b/Example/AppDelegate.m @@ -0,0 +1,26 @@ +// +// AppDelegate.m +// Mac Example +// +// Created by Matt Coneybeare on 9/15/14. +// Copyright (c) 2014 Armchair. All rights reserved. +// + +#import "AppDelegate.h" + +@interface AppDelegate () + +@property (weak) IBOutlet NSWindow *window; +@end + +@implementation AppDelegate + +- (void)applicationDidFinishLaunching:(NSNotification *)aNotification { + // Insert code here to initialize your application +} + +- (void)applicationWillTerminate:(NSNotification *)aNotification { + // Insert code here to tear down your application +} + +@end diff --git a/Example/AppDelegate.swift b/Example/AppDelegate.swift new file mode 100644 index 0000000..9a8c8d2 --- /dev/null +++ b/Example/AppDelegate.swift @@ -0,0 +1,58 @@ +// AppDelegate.swift +// +// Copyright (c) 2014 Armchair (http://github.com/UrbanApps/Armchair) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import UIKit +import Armchair + +@UIApplicationMain +class AppDelegate: UIResponder, UIApplicationDelegate { + + // MARK: - UIApplicationDelegate + + override class func initialize() { + AppDelegate.setupArmchair() + } + + class func setupArmchair() { + // Normally, all the setup would be here. + // But, because we are presenting a few different setups in the example, + // The config will be in the view controllers + // Armchair.appID("364709193") // iBooks + // + // It is always best to load Armchair as early as possible + // because it needs to receive application life-cycle notifications + // + // NOTE: The appID call always has to go before any other Armchair calls + Armchair.appID("364709193") + Armchair.debugEnabled(true) + } + + var window: UIWindow? + + // MARK: - UIApplicationDelegate + + func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { + return true + } + +} + diff --git a/Example/Base.lproj/Main.storyboard b/Example/Base.lproj/Main.storyboard new file mode 100644 index 0000000..509da12 --- /dev/null +++ b/Example/Base.lproj/Main.storyboard @@ -0,0 +1,136 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Example/Base.lproj/MainMenu.xib b/Example/Base.lproj/MainMenu.xib new file mode 100644 index 0000000..933177d --- /dev/null +++ b/Example/Base.lproj/MainMenu.xib @@ -0,0 +1,680 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Default + + + + + + + Left to Right + + + + + + + Right to Left + + + + + + + + + + + Default + + + + + + + Left to Right + + + + + + + Right to Left + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Example/Images.xcassets/AppIcon.appiconset/Contents.json b/Example/Images.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..b7f3352 --- /dev/null +++ b/Example/Images.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,58 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "3x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Example/Images.xcassets/LaunchImage.launchimage/Contents.json b/Example/Images.xcassets/LaunchImage.launchimage/Contents.json new file mode 100644 index 0000000..6f870a4 --- /dev/null +++ b/Example/Images.xcassets/LaunchImage.launchimage/Contents.json @@ -0,0 +1,51 @@ +{ + "images" : [ + { + "orientation" : "portrait", + "idiom" : "iphone", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "2x" + }, + { + "orientation" : "portrait", + "idiom" : "iphone", + "subtype" : "retina4", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "2x" + }, + { + "orientation" : "portrait", + "idiom" : "ipad", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "1x" + }, + { + "orientation" : "landscape", + "idiom" : "ipad", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "1x" + }, + { + "orientation" : "portrait", + "idiom" : "ipad", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "2x" + }, + { + "orientation" : "landscape", + "idiom" : "ipad", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Example/Images.xcassets/chair.imageset/Contents.json b/Example/Images.xcassets/chair.imageset/Contents.json new file mode 100644 index 0000000..8f08acb --- /dev/null +++ b/Example/Images.xcassets/chair.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x", + "filename" : "chair.jpg" + }, + { + "idiom" : "universal", + "scale" : "2x", + "filename" : "chair-1.jpg" + }, + { + "idiom" : "universal", + "scale" : "3x", + "filename" : "chair-2.jpg" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Example/Images.xcassets/chair.imageset/chair-1.jpg b/Example/Images.xcassets/chair.imageset/chair-1.jpg new file mode 100644 index 0000000..53310fa Binary files /dev/null and b/Example/Images.xcassets/chair.imageset/chair-1.jpg differ diff --git a/Example/Images.xcassets/chair.imageset/chair-2.jpg b/Example/Images.xcassets/chair.imageset/chair-2.jpg new file mode 100644 index 0000000..53310fa Binary files /dev/null and b/Example/Images.xcassets/chair.imageset/chair-2.jpg differ diff --git a/Example/Images.xcassets/chair.imageset/chair.jpg b/Example/Images.xcassets/chair.imageset/chair.jpg new file mode 100644 index 0000000..53310fa Binary files /dev/null and b/Example/Images.xcassets/chair.imageset/chair.jpg differ diff --git a/Example/Info-Mac.plist b/Example/Info-Mac.plist new file mode 100644 index 0000000..12bc27c --- /dev/null +++ b/Example/Info-Mac.plist @@ -0,0 +1,36 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIconFile + + CFBundleIdentifier + com.armchair.$(PRODUCT_NAME:rfc1034identifier) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 99 + LSApplicationCategoryType + public.app-category.developer-tools + LSMinimumSystemVersion + $(MACOSX_DEPLOYMENT_TARGET) + NSHumanReadableCopyright + Copyright © 2014 Armchair. All rights reserved. + NSMainNibFile + MainMenu + NSPrincipalClass + NSApplication + + diff --git a/Example/Info-iOS.plist b/Example/Info-iOS.plist new file mode 100644 index 0000000..1bd5b8d --- /dev/null +++ b/Example/Info-iOS.plist @@ -0,0 +1,55 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + com.armchair.$(PRODUCT_NAME:rfc1034identifier) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + Armchair + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 99 + LSRequiresIPhoneOS + + UIMainStoryboardFile + Main + UIRequiredDeviceCapabilities + + armv7 + + UIStatusBarTintParameters + + UINavigationBar + + Style + UIBarStyleDefault + Translucent + + + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/Example/ViewController.swift b/Example/ViewController.swift new file mode 100644 index 0000000..eea6fc6 --- /dev/null +++ b/Example/ViewController.swift @@ -0,0 +1,152 @@ +// MasterViewController.swift +// +// Copyright (c) 2014 Armchair (http://github.com/UrbanApps/Armchair) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import UIKit +import Armchair + +class ViewController: UIViewController { + + @IBAction func presentStandardPrompt(AnyObject) { + resetAppReviewManager() + + // The AppID is the only required setup + Armchair.appID("364709193") // iBooks + + // Debug means that it will popup on the next available change + Armchair.debugEnabled(true) + + // true here means it is ok to show, but it doesn't matter because we have debug on. + Armchair.userDidSignificantEvent(true) + } + + @IBAction func presentCustomizedPrompt(AnyObject) { + resetAppReviewManager() + + // The AppID is the only required setup + Armchair.appID("364709193") // iBooks + + // Debug means that it will popup on the next available change + Armchair.debugEnabled(true) + + // This overrides the default value, read from your localized bundle plist + Armchair.appName("Pong") + + // This overrides the default value, read from the UAAppReviewManager bundle plist + Armchair.reviewTitle("Rate This Shiz") + + // This overrides the default value, read from the UAAppReviewManager bundle plist + Armchair.reviewMessage("Yo! I werked rly hard on this shiz yo, hit me up wit some good ratings yo!") + + // This overrides the default value, read from the UAAppReviewManager bundle plist + Armchair.cancelButtonTitle("Nah, fool") + + // This overrides the default value, read from the UAAppReviewManager bundle plist + Armchair.rateButtonTitle("Hell yeah!") + + // This overrides the default value, read from the UAAppReviewManager bundle plist + Armchair.remindButtonTitle("Hit me up later...") + + // This overrides the default value of 30, but it doesn't matter here because of Debug mode enabled + Armchair.daysUntilPrompt(420) + + // This overrides the default value of 1, but it doesn't matter here because of Debug mode enabled + Armchair.daysBeforeReminding(187) + + // This means that the popup won't show if you have already rated any version of the app, but it doesn't matter here because of Debug mode enabled + Armchair.shouldPromptIfRated(false) + + // This overrides the default value of 20, but it doesn't matter here because of Debug mode enabled + Armchair.significantEventsUntilPrompt(99) + + // This means that UAAppReviewManager won't track this version if it hasn't already, but it doesn't matter here because of Debug mode enabled + Armchair.tracksNewVersions(false) + + // UAAppReviewManager comes with standard translations for dozens of Languages. If you want to provide your own translations instead, + // or you change the default title, message or button titles, set this to YES. + Armchair.useMainAppBundleForLocalizations(true) + + // This sets the Affiliate code you want to use, but is not required. + Armchair.affiliateCode("11l7j9") + + // This sets the Affiliate campaign code for tracking, but is not required. + Armchair.affiliateCampaignCode("Armchair-ExampleApp") + + // Armchair is block based, so setup some blocks on events + Armchair.onDidDeclineToRate() { println("[Example App] The user just declined to rate") } + Armchair.onDidDisplayAlert() { println("[Example App] We just displayed the rating prompt") } + Armchair.onDidOptToRate() { println("[Example App] The user just opted to rate") } + Armchair.onDidOptToRemindLater({ println("[Example App] The user just opted to remind later") }) + Armchair.onWillPresentModalView({ println("[Example App] About to present the modal view. Animated: \($0)") }) + Armchair.onDidDismissModalView({ println("[Example App] Just dismissed the modal view. Animated: \($0)") }) + + // Armchair has sensible defaults for the NSUserDefault keys it uses, but you can customize that here + Armchair.setKey("kSettingsSignificantEventTally", ArmchairKey.SignificantEventCount) + + // You can also call it with a block to circumvent any of Armchair should rate logic. + Armchair.userDidSignificantEvent({ + (trackingInfo: ArmchairTrackingInfo) -> (Bool) in + // the trackingInfo.info dictionary has all the keys/value Armchair uses to determine whether or not to show a prompt + + println("[Example App] \(trackingInfo)") + + return true + }) + + // Or you can set a global one to get one last chance to stop the prompt, or do your own logic + Armchair.shouldPromptClosure({ + (trackingInfo: ArmchairTrackingInfo) -> (Bool) in + // the trackingInfo.info dictionary has all the keys/value Armchair uses to determine whether or not to show a prompt + + // This will be called once all other rating conditions have been met, but before the prompt. + // If a local ArmchairShouldPromptClosure is called using the local methods, (like immediately above) + // this will not be called. + // + // Return true to allow the prompt, false to stop the presentation. + return true + }) + } + + @IBAction func presentStoreKitPrompt(AnyObject) { + resetAppReviewManager() + + // The AppID is the only required setup + Armchair.appID("364709193") // iBooks + + // Debug means that it will popup on the next available change + Armchair.debugEnabled(true) + + // This overrides the default of NO and is iOS 6+. Instead of going to the review page in the App Store App, + // the user goes to the main page of the app, in side of this app. Downsides are that it doesn't go directly to + // reviews and doesn't take affiliate codes + Armchair.opensInStoreKit(true) + + // If you are opening in StoreKit, you can change whether or not to animated the push of the View Controller + Armchair.usesAnimation(true) + + // true here means it is ok to show, but it doesn't matter because we have debug on. + Armchair.userDidSignificantEvent(true) + } + + func resetAppReviewManager() { + Armchair.resetDefaults() + } +} diff --git a/Example/main.m b/Example/main.m new file mode 100644 index 0000000..eedd9eb --- /dev/null +++ b/Example/main.m @@ -0,0 +1,13 @@ +// +// main.m +// Mac Example +// +// Created by Matt Coneybeare on 9/15/14. +// Copyright (c) 2014 Armchair. All rights reserved. +// + +#import + +int main(int argc, const char * argv[]) { + return NSApplicationMain(argc, argv); +} diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..008e881 --- /dev/null +++ b/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2014 Armchair (http://github.com/UrbanApps/Armchair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/Mac Example.xcodeproj/project.pbxproj b/Mac Example.xcodeproj/project.pbxproj new file mode 100644 index 0000000..029f9b7 --- /dev/null +++ b/Mac Example.xcodeproj/project.pbxproj @@ -0,0 +1,302 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + E6D8B8CD19C75292001AD043 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = E6D8B8CC19C75292001AD043 /* main.m */; }; + E6D8B8D019C75292001AD043 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = E6D8B8CF19C75292001AD043 /* AppDelegate.m */; }; + E6D8B8D219C75292001AD043 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = E6D8B8D119C75292001AD043 /* Images.xcassets */; }; + E6D8B8D519C75292001AD043 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = E6D8B8D319C75292001AD043 /* MainMenu.xib */; }; + E6D8B91F19C75857001AD043 /* ArmchairMac.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E6D8B91E19C75857001AD043 /* ArmchairMac.framework */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + E6D8B8C719C75292001AD043 /* Mac Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Mac Example.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + E6D8B8CB19C75292001AD043 /* Info-Mac.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; name = "Info-Mac.plist"; path = "Example/Info-Mac.plist"; sourceTree = ""; }; + E6D8B8CC19C75292001AD043 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = main.m; path = Example/main.m; sourceTree = ""; }; + E6D8B8CE19C75292001AD043 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = Example/AppDelegate.h; sourceTree = ""; }; + E6D8B8CF19C75292001AD043 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = AppDelegate.m; path = Example/AppDelegate.m; sourceTree = ""; }; + E6D8B8D119C75292001AD043 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = Example/Images.xcassets; sourceTree = ""; }; + E6D8B8D419C75292001AD043 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; + E6D8B8ED19C75565001AD043 /* Armchair.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Armchair.framework; path = "../../../../Library/Developer/Xcode/DerivedData/Armchair-evfkxrlmesanplcgvhiqbnaxdagl/Build/Products/Debug-iphoneos/Armchair.framework"; sourceTree = ""; }; + E6D8B91E19C75857001AD043 /* ArmchairMac.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ArmchairMac.framework; path = build/Debug/ArmchairMac.framework; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + E6D8B8C419C75292001AD043 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + E6D8B91F19C75857001AD043 /* ArmchairMac.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + E6D8B8BE19C75292001AD043 = { + isa = PBXGroup; + children = ( + E6D8B91E19C75857001AD043 /* ArmchairMac.framework */, + E6D8B8ED19C75565001AD043 /* Armchair.framework */, + E6D8B8C919C75292001AD043 /* Source */, + E6D8B8C819C75292001AD043 /* Products */, + ); + sourceTree = ""; + }; + E6D8B8C819C75292001AD043 /* Products */ = { + isa = PBXGroup; + children = ( + E6D8B8C719C75292001AD043 /* Mac Example.app */, + ); + name = Products; + sourceTree = ""; + }; + E6D8B8C919C75292001AD043 /* Source */ = { + isa = PBXGroup; + children = ( + E6D8B8CE19C75292001AD043 /* AppDelegate.h */, + E6D8B8CF19C75292001AD043 /* AppDelegate.m */, + E6D8B8D119C75292001AD043 /* Images.xcassets */, + E6D8B8D319C75292001AD043 /* MainMenu.xib */, + E6D8B8CA19C75292001AD043 /* Supporting Files */, + ); + name = Source; + path = "Mac Example"; + sourceTree = ""; + }; + E6D8B8CA19C75292001AD043 /* Supporting Files */ = { + isa = PBXGroup; + children = ( + E6D8B8CB19C75292001AD043 /* Info-Mac.plist */, + E6D8B8CC19C75292001AD043 /* main.m */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + E6D8B8C619C75292001AD043 /* Mac Example */ = { + isa = PBXNativeTarget; + buildConfigurationList = E6D8B8E419C75292001AD043 /* Build configuration list for PBXNativeTarget "Mac Example" */; + buildPhases = ( + E6D8B8C319C75292001AD043 /* Sources */, + E6D8B8C419C75292001AD043 /* Frameworks */, + E6D8B8C519C75292001AD043 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "Mac Example"; + productName = "Mac Example"; + productReference = E6D8B8C719C75292001AD043 /* Mac Example.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + E6D8B8BF19C75292001AD043 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0600; + ORGANIZATIONNAME = Armchair; + TargetAttributes = { + E6D8B8C619C75292001AD043 = { + CreatedOnToolsVersion = 6.0; + }; + }; + }; + buildConfigurationList = E6D8B8C219C75292001AD043 /* Build configuration list for PBXProject "Mac Example" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = E6D8B8BE19C75292001AD043; + productRefGroup = E6D8B8C819C75292001AD043 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + E6D8B8C619C75292001AD043 /* Mac Example */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + E6D8B8C519C75292001AD043 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + E6D8B8D219C75292001AD043 /* Images.xcassets in Resources */, + E6D8B8D519C75292001AD043 /* MainMenu.xib in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + E6D8B8C319C75292001AD043 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + E6D8B8D019C75292001AD043 /* AppDelegate.m in Sources */, + E6D8B8CD19C75292001AD043 /* main.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + E6D8B8D319C75292001AD043 /* MainMenu.xib */ = { + isa = PBXVariantGroup; + children = ( + E6D8B8D419C75292001AD043 /* Base */, + ); + name = MainMenu.xib; + path = Example; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + E6D8B8E219C75292001AD043 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.9; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + }; + name = Debug; + }; + E6D8B8E319C75292001AD043 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + 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; + MACOSX_DEPLOYMENT_TARGET = 10.9; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + }; + name = Release; + }; + E6D8B8E519C75292001AD043 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + COMBINE_HIDPI_IMAGES = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/build/Debug", + ); + INFOPLIST_FILE = "$(SRCROOT)/Example/Info-Mac.plist"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; + MACOSX_DEPLOYMENT_TARGET = 10.9; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + E6D8B8E619C75292001AD043 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + COMBINE_HIDPI_IMAGES = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/build/Debug", + ); + INFOPLIST_FILE = "$(SRCROOT)/Example/Info-Mac.plist"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; + MACOSX_DEPLOYMENT_TARGET = 10.9; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + E6D8B8C219C75292001AD043 /* Build configuration list for PBXProject "Mac Example" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + E6D8B8E219C75292001AD043 /* Debug */, + E6D8B8E319C75292001AD043 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + E6D8B8E419C75292001AD043 /* Build configuration list for PBXNativeTarget "Mac Example" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + E6D8B8E519C75292001AD043 /* Debug */, + E6D8B8E619C75292001AD043 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = E6D8B8BF19C75292001AD043 /* Project object */; +} diff --git a/Mac Example.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/Mac Example.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..ffc5080 --- /dev/null +++ b/Mac Example.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/Mac Example.xcodeproj/project.xcworkspace/xcshareddata/Mac Example.xccheckout b/Mac Example.xcodeproj/project.xcworkspace/xcshareddata/Mac Example.xccheckout new file mode 100644 index 0000000..3776d61 --- /dev/null +++ b/Mac Example.xcodeproj/project.xcworkspace/xcshareddata/Mac Example.xccheckout @@ -0,0 +1,41 @@ + + + + + IDESourceControlProjectFavoriteDictionaryKey + + IDESourceControlProjectIdentifier + FB71943B-E489-41AE-8AD8-62BD5AFECCF8 + IDESourceControlProjectName + Mac Example + IDESourceControlProjectOriginsDictionary + + 6094D9F4198D56F78EB1B455EC894F8A44DC0039 + github.com:UrbanApps/Armchair.git + + IDESourceControlProjectPath + Mac Example/Mac Example.xcodeproj + IDESourceControlProjectRelativeInstallPathDictionary + + 6094D9F4198D56F78EB1B455EC894F8A44DC0039 + ../../.. + + IDESourceControlProjectURL + github.com:UrbanApps/Armchair.git + IDESourceControlProjectVersion + 111 + IDESourceControlProjectWCCIdentifier + 6094D9F4198D56F78EB1B455EC894F8A44DC0039 + IDESourceControlProjectWCConfigurations + + + IDESourceControlRepositoryExtensionIdentifierKey + public.vcs.git + IDESourceControlWCCIdentifierKey + 6094D9F4198D56F78EB1B455EC894F8A44DC0039 + IDESourceControlWCCName + Armchair + + + + diff --git a/Mac Example.xcodeproj/project.xcworkspace/xcuserdata/coneybeare.xcuserdatad/UserInterfaceState.xcuserstate b/Mac Example.xcodeproj/project.xcworkspace/xcuserdata/coneybeare.xcuserdatad/UserInterfaceState.xcuserstate new file mode 100644 index 0000000..1f0352a Binary files /dev/null and b/Mac Example.xcodeproj/project.xcworkspace/xcuserdata/coneybeare.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/Mac Example.xcodeproj/xcuserdata/coneybeare.xcuserdatad/xcschemes/Mac Example.xcscheme b/Mac Example.xcodeproj/xcuserdata/coneybeare.xcuserdatad/xcschemes/Mac Example.xcscheme new file mode 100644 index 0000000..1f09266 --- /dev/null +++ b/Mac Example.xcodeproj/xcuserdata/coneybeare.xcuserdatad/xcschemes/Mac Example.xcscheme @@ -0,0 +1,110 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Mac Example.xcodeproj/xcuserdata/coneybeare.xcuserdatad/xcschemes/xcschememanagement.plist b/Mac Example.xcodeproj/xcuserdata/coneybeare.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 0000000..6323dd5 --- /dev/null +++ b/Mac Example.xcodeproj/xcuserdata/coneybeare.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,27 @@ + + + + + SchemeUserState + + Mac Example.xcscheme + + orderHint + 3 + + + SuppressBuildableAutocreation + + E6D8B8C619C75292001AD043 + + primary + + + E6D8B8D919C75292001AD043 + + primary + + + + + diff --git a/Mac Example/Mac Example.xcodeproj/project.pbxproj b/Mac Example/Mac Example.xcodeproj/project.pbxproj new file mode 100644 index 0000000..69deaed --- /dev/null +++ b/Mac Example/Mac Example.xcodeproj/project.pbxproj @@ -0,0 +1,413 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + E6D8B8CD19C75292001AD043 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = E6D8B8CC19C75292001AD043 /* main.m */; }; + E6D8B8D019C75292001AD043 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = E6D8B8CF19C75292001AD043 /* AppDelegate.m */; }; + E6D8B8D219C75292001AD043 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = E6D8B8D119C75292001AD043 /* Images.xcassets */; }; + E6D8B8D519C75292001AD043 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = E6D8B8D319C75292001AD043 /* MainMenu.xib */; }; + E6D8B8E119C75292001AD043 /* Mac_ExampleTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E6D8B8E019C75292001AD043 /* Mac_ExampleTests.m */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + E6D8B8DB19C75292001AD043 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = E6D8B8BF19C75292001AD043 /* Project object */; + proxyType = 1; + remoteGlobalIDString = E6D8B8C619C75292001AD043; + remoteInfo = "Mac Example"; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + E6D8B8C719C75292001AD043 /* Mac Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Mac Example.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + E6D8B8CB19C75292001AD043 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + E6D8B8CC19C75292001AD043 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + E6D8B8CE19C75292001AD043 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; + E6D8B8CF19C75292001AD043 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; + E6D8B8D119C75292001AD043 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; + E6D8B8D419C75292001AD043 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; + E6D8B8DA19C75292001AD043 /* Mac ExampleTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Mac ExampleTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; + E6D8B8DF19C75292001AD043 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + E6D8B8E019C75292001AD043 /* Mac_ExampleTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = Mac_ExampleTests.m; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + E6D8B8C419C75292001AD043 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + E6D8B8D719C75292001AD043 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + E6D8B8BE19C75292001AD043 = { + isa = PBXGroup; + children = ( + E6D8B8C919C75292001AD043 /* Mac Example */, + E6D8B8DD19C75292001AD043 /* Mac ExampleTests */, + E6D8B8C819C75292001AD043 /* Products */, + ); + sourceTree = ""; + }; + E6D8B8C819C75292001AD043 /* Products */ = { + isa = PBXGroup; + children = ( + E6D8B8C719C75292001AD043 /* Mac Example.app */, + E6D8B8DA19C75292001AD043 /* Mac ExampleTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + E6D8B8C919C75292001AD043 /* Mac Example */ = { + isa = PBXGroup; + children = ( + E6D8B8CE19C75292001AD043 /* AppDelegate.h */, + E6D8B8CF19C75292001AD043 /* AppDelegate.m */, + E6D8B8D119C75292001AD043 /* Images.xcassets */, + E6D8B8D319C75292001AD043 /* MainMenu.xib */, + E6D8B8CA19C75292001AD043 /* Supporting Files */, + ); + path = "Mac Example"; + sourceTree = ""; + }; + E6D8B8CA19C75292001AD043 /* Supporting Files */ = { + isa = PBXGroup; + children = ( + E6D8B8CB19C75292001AD043 /* Info.plist */, + E6D8B8CC19C75292001AD043 /* main.m */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; + E6D8B8DD19C75292001AD043 /* Mac ExampleTests */ = { + isa = PBXGroup; + children = ( + E6D8B8E019C75292001AD043 /* Mac_ExampleTests.m */, + E6D8B8DE19C75292001AD043 /* Supporting Files */, + ); + path = "Mac ExampleTests"; + sourceTree = ""; + }; + E6D8B8DE19C75292001AD043 /* Supporting Files */ = { + isa = PBXGroup; + children = ( + E6D8B8DF19C75292001AD043 /* Info.plist */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + E6D8B8C619C75292001AD043 /* Mac Example */ = { + isa = PBXNativeTarget; + buildConfigurationList = E6D8B8E419C75292001AD043 /* Build configuration list for PBXNativeTarget "Mac Example" */; + buildPhases = ( + E6D8B8C319C75292001AD043 /* Sources */, + E6D8B8C419C75292001AD043 /* Frameworks */, + E6D8B8C519C75292001AD043 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "Mac Example"; + productName = "Mac Example"; + productReference = E6D8B8C719C75292001AD043 /* Mac Example.app */; + productType = "com.apple.product-type.application"; + }; + E6D8B8D919C75292001AD043 /* Mac ExampleTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = E6D8B8E719C75292001AD043 /* Build configuration list for PBXNativeTarget "Mac ExampleTests" */; + buildPhases = ( + E6D8B8D619C75292001AD043 /* Sources */, + E6D8B8D719C75292001AD043 /* Frameworks */, + E6D8B8D819C75292001AD043 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + E6D8B8DC19C75292001AD043 /* PBXTargetDependency */, + ); + name = "Mac ExampleTests"; + productName = "Mac ExampleTests"; + productReference = E6D8B8DA19C75292001AD043 /* Mac ExampleTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + E6D8B8BF19C75292001AD043 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0600; + ORGANIZATIONNAME = Armrest; + TargetAttributes = { + E6D8B8C619C75292001AD043 = { + CreatedOnToolsVersion = 6.0; + }; + E6D8B8D919C75292001AD043 = { + CreatedOnToolsVersion = 6.0; + TestTargetID = E6D8B8C619C75292001AD043; + }; + }; + }; + buildConfigurationList = E6D8B8C219C75292001AD043 /* Build configuration list for PBXProject "Mac Example" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = E6D8B8BE19C75292001AD043; + productRefGroup = E6D8B8C819C75292001AD043 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + E6D8B8C619C75292001AD043 /* Mac Example */, + E6D8B8D919C75292001AD043 /* Mac ExampleTests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + E6D8B8C519C75292001AD043 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + E6D8B8D219C75292001AD043 /* Images.xcassets in Resources */, + E6D8B8D519C75292001AD043 /* MainMenu.xib in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + E6D8B8D819C75292001AD043 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + E6D8B8C319C75292001AD043 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + E6D8B8D019C75292001AD043 /* AppDelegate.m in Sources */, + E6D8B8CD19C75292001AD043 /* main.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + E6D8B8D619C75292001AD043 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + E6D8B8E119C75292001AD043 /* Mac_ExampleTests.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + E6D8B8DC19C75292001AD043 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = E6D8B8C619C75292001AD043 /* Mac Example */; + targetProxy = E6D8B8DB19C75292001AD043 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + E6D8B8D319C75292001AD043 /* MainMenu.xib */ = { + isa = PBXVariantGroup; + children = ( + E6D8B8D419C75292001AD043 /* Base */, + ); + name = MainMenu.xib; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + E6D8B8E219C75292001AD043 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.9; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + }; + name = Debug; + }; + E6D8B8E319C75292001AD043 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + 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; + MACOSX_DEPLOYMENT_TARGET = 10.9; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + }; + name = Release; + }; + E6D8B8E519C75292001AD043 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = "Mac Example/Info.plist"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + E6D8B8E619C75292001AD043 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = "Mac Example/Info.plist"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; + E6D8B8E819C75292001AD043 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + COMBINE_HIDPI_IMAGES = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(DEVELOPER_FRAMEWORKS_DIR)", + "$(inherited)", + ); + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + INFOPLIST_FILE = "Mac ExampleTests/Info.plist"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; + PRODUCT_NAME = "$(TARGET_NAME)"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Mac Example.app/Contents/MacOS/Mac Example"; + }; + name = Debug; + }; + E6D8B8E919C75292001AD043 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + COMBINE_HIDPI_IMAGES = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(DEVELOPER_FRAMEWORKS_DIR)", + "$(inherited)", + ); + INFOPLIST_FILE = "Mac ExampleTests/Info.plist"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; + PRODUCT_NAME = "$(TARGET_NAME)"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Mac Example.app/Contents/MacOS/Mac Example"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + E6D8B8C219C75292001AD043 /* Build configuration list for PBXProject "Mac Example" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + E6D8B8E219C75292001AD043 /* Debug */, + E6D8B8E319C75292001AD043 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + E6D8B8E419C75292001AD043 /* Build configuration list for PBXNativeTarget "Mac Example" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + E6D8B8E519C75292001AD043 /* Debug */, + E6D8B8E619C75292001AD043 /* Release */, + ); + defaultConfigurationIsVisible = 0; + }; + E6D8B8E719C75292001AD043 /* Build configuration list for PBXNativeTarget "Mac ExampleTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + E6D8B8E819C75292001AD043 /* Debug */, + E6D8B8E919C75292001AD043 /* Release */, + ); + defaultConfigurationIsVisible = 0; + }; +/* End XCConfigurationList section */ + }; + rootObject = E6D8B8BF19C75292001AD043 /* Project object */; +} diff --git a/Mac Example/Mac Example.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/Mac Example/Mac Example.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..ffc5080 --- /dev/null +++ b/Mac Example/Mac Example.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/Mac Example/Mac Example.xcodeproj/project.xcworkspace/xcuserdata/coneybeare.xcuserdatad/UserInterfaceState.xcuserstate b/Mac Example/Mac Example.xcodeproj/project.xcworkspace/xcuserdata/coneybeare.xcuserdatad/UserInterfaceState.xcuserstate new file mode 100644 index 0000000..7c8e01b Binary files /dev/null and b/Mac Example/Mac Example.xcodeproj/project.xcworkspace/xcuserdata/coneybeare.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/Mac Example/Mac Example.xcodeproj/xcuserdata/coneybeare.xcuserdatad/xcschemes/Mac Example.xcscheme b/Mac Example/Mac Example.xcodeproj/xcuserdata/coneybeare.xcuserdatad/xcschemes/Mac Example.xcscheme new file mode 100644 index 0000000..d2d91ae --- /dev/null +++ b/Mac Example/Mac Example.xcodeproj/xcuserdata/coneybeare.xcuserdatad/xcschemes/Mac Example.xcscheme @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + diff --git a/Mac Example/Mac Example/AppDelegate.h b/Mac Example/Mac Example/AppDelegate.h new file mode 100644 index 0000000..6c91cdf --- /dev/null +++ b/Mac Example/Mac Example/AppDelegate.h @@ -0,0 +1,15 @@ +// +// AppDelegate.h +// Mac Example +// +// Created by Matt Coneybeare on 9/15/14. +// Copyright (c) 2014 Armrest. All rights reserved. +// + +#import + +@interface AppDelegate : NSObject + + +@end + diff --git a/Mac Example/Mac Example/AppDelegate.m b/Mac Example/Mac Example/AppDelegate.m new file mode 100644 index 0000000..cc0d6f2 --- /dev/null +++ b/Mac Example/Mac Example/AppDelegate.m @@ -0,0 +1,26 @@ +// +// AppDelegate.m +// Mac Example +// +// Created by Matt Coneybeare on 9/15/14. +// Copyright (c) 2014 Armrest. All rights reserved. +// + +#import "AppDelegate.h" + +@interface AppDelegate () + +@property (weak) IBOutlet NSWindow *window; +@end + +@implementation AppDelegate + +- (void)applicationDidFinishLaunching:(NSNotification *)aNotification { + // Insert code here to initialize your application +} + +- (void)applicationWillTerminate:(NSNotification *)aNotification { + // Insert code here to tear down your application +} + +@end diff --git a/Mac Example/Mac Example/Base.lproj/MainMenu.xib b/Mac Example/Mac Example/Base.lproj/MainMenu.xib new file mode 100644 index 0000000..933177d --- /dev/null +++ b/Mac Example/Mac Example/Base.lproj/MainMenu.xib @@ -0,0 +1,680 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Default + + + + + + + Left to Right + + + + + + + Right to Left + + + + + + + + + + + Default + + + + + + + Left to Right + + + + + + + Right to Left + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Mac Example/Mac Example/Images.xcassets/AppIcon.appiconset/Contents.json b/Mac Example/Mac Example/Images.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..2db2b1c --- /dev/null +++ b/Mac Example/Mac Example/Images.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,58 @@ +{ + "images" : [ + { + "idiom" : "mac", + "size" : "16x16", + "scale" : "1x" + }, + { + "idiom" : "mac", + "size" : "16x16", + "scale" : "2x" + }, + { + "idiom" : "mac", + "size" : "32x32", + "scale" : "1x" + }, + { + "idiom" : "mac", + "size" : "32x32", + "scale" : "2x" + }, + { + "idiom" : "mac", + "size" : "128x128", + "scale" : "1x" + }, + { + "idiom" : "mac", + "size" : "128x128", + "scale" : "2x" + }, + { + "idiom" : "mac", + "size" : "256x256", + "scale" : "1x" + }, + { + "idiom" : "mac", + "size" : "256x256", + "scale" : "2x" + }, + { + "idiom" : "mac", + "size" : "512x512", + "scale" : "1x" + }, + { + "idiom" : "mac", + "size" : "512x512", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Mac Example/Mac Example/Info.plist b/Mac Example/Mac Example/Info.plist new file mode 100644 index 0000000..2d87a5b --- /dev/null +++ b/Mac Example/Mac Example/Info.plist @@ -0,0 +1,34 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIconFile + + CFBundleIdentifier + com.armrest.$(PRODUCT_NAME:rfc1034identifier) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + LSMinimumSystemVersion + $(MACOSX_DEPLOYMENT_TARGET) + NSHumanReadableCopyright + Copyright © 2014 Armrest. All rights reserved. + NSMainNibFile + MainMenu + NSPrincipalClass + NSApplication + + diff --git a/Mac Example/Mac Example/main.m b/Mac Example/Mac Example/main.m new file mode 100644 index 0000000..b31f721 --- /dev/null +++ b/Mac Example/Mac Example/main.m @@ -0,0 +1,13 @@ +// +// main.m +// Mac Example +// +// Created by Matt Coneybeare on 9/15/14. +// Copyright (c) 2014 Armrest. All rights reserved. +// + +#import + +int main(int argc, const char * argv[]) { + return NSApplicationMain(argc, argv); +} diff --git a/Mac Example/Mac ExampleTests/Info.plist b/Mac Example/Mac ExampleTests/Info.plist new file mode 100644 index 0000000..b55b3ac --- /dev/null +++ b/Mac Example/Mac ExampleTests/Info.plist @@ -0,0 +1,24 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + com.armrest.$(PRODUCT_NAME:rfc1034identifier) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + + diff --git a/Mac Example/Mac ExampleTests/Mac_ExampleTests.m b/Mac Example/Mac ExampleTests/Mac_ExampleTests.m new file mode 100644 index 0000000..81d3eff --- /dev/null +++ b/Mac Example/Mac ExampleTests/Mac_ExampleTests.m @@ -0,0 +1,40 @@ +// +// Mac_ExampleTests.m +// Mac ExampleTests +// +// Created by Matt Coneybeare on 9/15/14. +// Copyright (c) 2014 Armrest. All rights reserved. +// + +#import +#import + +@interface Mac_ExampleTests : XCTestCase + +@end + +@implementation Mac_ExampleTests + +- (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. + XCTAssert(YES, @"Pass"); +} + +- (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/Source/Armchair.h b/Source/Armchair.h new file mode 100644 index 0000000..4f75d77 --- /dev/null +++ b/Source/Armchair.h @@ -0,0 +1,26 @@ +// Armchair.h +// +// Copyright (c) 2014 Armchair (http://github.com/UrbanApps/Armchair) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#import + +FOUNDATION_EXPORT double ArmchairVersionNumber; +FOUNDATION_EXPORT const unsigned char ArmchairVersionString[]; diff --git a/Source/Armchair.swift b/Source/Armchair.swift new file mode 100644 index 0000000..aa97a1b --- /dev/null +++ b/Source/Armchair.swift @@ -0,0 +1,1695 @@ +// Armchair.swift +// +// Copyright (c) 2014 Armchair (http://github.com/UrbanApps/Armchair) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import Foundation +import StoreKit +import SystemConfiguration +import UIKit + +// MARK: - +// MARK: PUBLIC Interface +// MARK: - +// MARK: Properties +/* +* Get/Set your Apple generated software id. +* This is the only required setup value. +* This call needs to be first. No default. +*/ +public var appID: String = "" +public func appID(appID: String) { + Armchair.appID = appID + Manager.defaultManager.appID = appID +} + +/* +* Get/Set the App Name to use in the prompt +* Default value is your localized display name from the info.plist +*/ +public func appName() -> String { + return Manager.defaultManager.appName +} +public func appName(appName: String) { + Manager.defaultManager.appName = appName +} + +/* +* Get/Set the title to use on the review prompt. +* Default value is a localized "Rate " +*/ +public func reviewTitle() -> String { + return Manager.defaultManager.reviewTitle +} +public func reviewTitle(reviewTitle: String) { + Manager.defaultManager.reviewTitle = reviewTitle +} + +/* +* Get/Set the message to use on the review prompt. +* Default value is a localized +* "If you enjoy using , would you mind taking a moment to rate it? It won't take more than a minute. Thanks for your support!" +*/ +public func reviewMessage() -> String { + return Manager.defaultManager.reviewMessage +} +public func reviewMessage(reviewMessage: String) { + Manager.defaultManager.reviewMessage = reviewMessage +} + +/* +* Get/Set the cancel button title to use on the review prompt. +* Default value is a localized "No, Thanks" +*/ +public func cancelButtonTitle() -> String { + return Manager.defaultManager.cancelButtonTitle +} +public func cancelButtonTitle(cancelButtonTitle: String) { + Manager.defaultManager.cancelButtonTitle = cancelButtonTitle +} + +/* +* Get/Set the rate button title to use on the review prompt. +* Default value is a localized "Rate " +*/ +public func rateButtonTitle() -> String { + return Manager.defaultManager.rateButtonTitle +} +public func rateButtonTitle(rateButtonTitle: String) { + Manager.defaultManager.rateButtonTitle = rateButtonTitle +} + +/* +* Get/Set the remind me later button title to use on the review prompt. +* It is optional, so you can set it to nil to hide the remind button from displaying +* Default value is a localized "Remind me later" +*/ +public func remindButtonTitle() -> String? { + return Manager.defaultManager.remindButtonTitle +} +public func remindButtonTitle(remindButtonTitle: String?) { + Manager.defaultManager.remindButtonTitle = remindButtonTitle +} + +/* +* Get/Set the NSUserDefault keys that store the usage data for Armchair +* Default values are in the form of "_Armchair" +*/ +public func keyForArmchairKeyType(keyType: ArmchairKey) -> String { + return Manager.defaultManager.keyForArmchairKeyType(keyType) +} +public func setKey(key: NSString, armchairKeyType: ArmchairKey) { + Manager.defaultManager.setKey(key, armchairKeyType: armchairKeyType) +} + +/* +* Get/Set the prefix to the NSUserDefault keys that store the usage data for Armchair +* Default value is the App ID, and it is prepended to the keys for key type, above +* This prevents different apps using a shared Key/Value store from overwriting each other. +*/ +public func keyPrefix() -> String { + return Manager.defaultManager.keyPrefix +} +public func keyPrefix(keyPrefix: String) { + Manager.defaultManager.keyPrefix = keyPrefix +} + +/* +* Get/Set the object that stores the usage data for Armchair +* it is optional but if you pass nil, Armchair can not run. +* Default value is NSUserDefaults.standardUserDefaults() +*/ +public func userDefaultsObject() -> ArmchairDefaultsObject? { + return Manager.defaultManager.userDefaultsObject +} +public func userDefaultsObject(userDefaultsObject: ArmchairDefaultsObject?) { + Manager.defaultManager.userDefaultsObject = userDefaultsObject +} + +/* +* Users will need to have the same version of your app installed for this many +* days before they will be prompted to rate it. +* Default => 30 +*/ +public func daysUntilPrompt() -> UInt { + return Manager.defaultManager.daysUntilPrompt +} +public func daysUntilPrompt(daysUntilPrompt: UInt) { + Manager.defaultManager.daysUntilPrompt = daysUntilPrompt +} + +/* +* An example of a 'use' would be if the user launched the app. Bringing the app +* into the foreground (on devices that support it) would also be considered +* a 'use'. +* +* Users need to 'use' the same version of the app this many times before +* before they will be prompted to rate it. +* Default => 20 +*/ +public func usesUntilPrompt() -> UInt { + return Manager.defaultManager.usesUntilPrompt +} +public func usesUntilPrompt(usesUntilPrompt: UInt) { + Manager.defaultManager.usesUntilPrompt = usesUntilPrompt +} + +/* +* A significant event can be anything you want to be in your app. In a +* telephone app, a significant event might be placing or receiving a call. +* In a game, it might be beating a level or a boss. This is just another +* layer of filtering that can be used to make sure that only the most +* loyal of your users are being prompted to rate you on the app store. +* If you leave this at a value of 0 (default), then this won't be a criterion +* used for rating. +* +* To tell Armchair that the user has performed +* a significant event, call the method Armchair.userDidSignificantEvent() +* Default => 0 +*/ +public func significantEventsUntilPrompt() -> UInt { + return Manager.defaultManager.significantEventsUntilPrompt +} +public func significantEventsUntilPrompt(significantEventsUntilPrompt: UInt) { + Manager.defaultManager.significantEventsUntilPrompt = significantEventsUntilPrompt +} + +/* +* Once the rating alert is presented to the user, they might select +* 'Remind me later'. This value specifies how many days Armchair +* will wait before reminding them. A value of 0 disables reminders and +* removes the 'Remind me later' button. +* Default => 1 +*/ +public func daysBeforeReminding() -> UInt { + return Manager.defaultManager.daysBeforeReminding +} +public func daysBeforeReminding(daysBeforeReminding: UInt) { + Manager.defaultManager.daysBeforeReminding = daysBeforeReminding +} + +/* +* By default, Armchair tracks all new bundle versions. +* When it detects a new version, it resets the values saved for usage, +* significant events, popup shown, user action etc... +* By setting this to false, Armchair will ONLY track the version it +* was initialized with. If this setting is set to true, Armchair +* will reset after each new version detection. +* Default => true +*/ +public func tracksNewVersions() -> Bool { + return Manager.defaultManager.tracksNewVersions +} +public func tracksNewVersions(tracksNewVersions: Bool) { + Manager.defaultManager.tracksNewVersions = tracksNewVersions +} + +/* +* If the user has rated the app once before, and you don't want it to show on +* a new version, set this to false. This is useful if you release small bugfix +* versions and don't want to pester your users with popups for every minor +* version. For example, you might set this to false for every minor build, then +* when you push a major version upgrade, leave it as true to ask for a rating again. +* Default => true +*/ +public func shouldPromptIfRated() -> Bool { + return Manager.defaultManager.shouldPromptIfRated +} +public func shouldPromptIfRated(shouldPromptIfRated: Bool) { + Manager.defaultManager.shouldPromptIfRated = shouldPromptIfRated +} + +/* +* If set to true, the main bundle will always be used to load localized strings. +* Set this to true if you have provided your own custom localizations in +* ArmchairLocalizable.strings in your main bundle +* Default => false. +*/ +public func useMainAppBundleForLocalizations() -> Bool { + return Manager.defaultManager.useMainAppBundleForLocalizations +} +public func useMainAppBundleForLocalizations(useMainAppBundleForLocalizations: Bool) { + Manager.defaultManager.useMainAppBundleForLocalizations = useMainAppBundleForLocalizations +} + +/* +* If you are an Apple Affiliate, enter your code here. +* If none is set, the author's code will be used as it is better to be set as something +* rather than nothing. If you want to thank me for making Armchair, feel free +* to leave this value at it's default. +*/ +public func affiliateCode() -> String { + return Manager.defaultManager.affiliateCode +} +public func affiliateCode(affiliateCode: String) { + Manager.defaultManager.affiliateCode = affiliateCode +} + +/* +* If you are an Apple Affiliate, enter your campaign code here. +* Default => "Armchair-" +*/ +public func affiliateCampaignCode() -> String { + return Manager.defaultManager.affiliateCampaignCode +} +public func affiliateCampaignCode(affiliateCampaignCode: String) { + Manager.defaultManager.affiliateCampaignCode = affiliateCampaignCode +} + +/* +* 'true' will show the Armchair alert everytime. Useful for testing +* how your message looks and making sure the link to your app's review page works. +* Calling this method in a production build (DEBUG preprocessor macro is not defined) +* has no effect. In app store builds, you don't have to worry about accidentally +* leaving debugEnabled to true +* Default => false +*/ +public func debugEnabled() -> Bool { + return Manager.defaultManager.debugEnabled +} +public func debugEnabled(debugEnabled: Bool) { +#if Debug + Manager.defaultManager.debugEnabled = debugEnabled +#else + println("[Armchair] Debug is disabled on release builds. If you really want to enable debug mode, add \"DEBUG=1\" to your release Preprocessor Macros") +#endif +} + +/* +* +* +*/ +public func resetDefaults() { + Manager.defaultManager.debugEnabled = false + Manager.defaultManager.appName = Manager.defaultManager.defaultAppName() + Manager.defaultManager.reviewTitle = Manager.defaultManager.defaultReviewTitle() + Manager.defaultManager.reviewMessage = Manager.defaultManager.defaultReviewMessage() + Manager.defaultManager.cancelButtonTitle = Manager.defaultManager.defaultCancelButtonTitle() + Manager.defaultManager.rateButtonTitle = Manager.defaultManager.defaultRateButtonTitle() + Manager.defaultManager.remindButtonTitle = Manager.defaultManager.defaultRemindButtonTitle() + Manager.defaultManager.daysUntilPrompt = 30 + Manager.defaultManager.daysBeforeReminding = 1 + Manager.defaultManager.shouldPromptIfRated = true + Manager.defaultManager.significantEventsUntilPrompt = 20 + Manager.defaultManager.tracksNewVersions = true + Manager.defaultManager.useMainAppBundleForLocalizations = false + Manager.defaultManager.affiliateCode = Manager.defaultManager.defaultAffiliateCode() + Manager.defaultManager.affiliateCampaignCode = Manager.defaultManager.defaultAffiliateCampaignCode() + Manager.defaultManager.didDeclineToRateClosure = nil + Manager.defaultManager.didDisplayAlertClosure = nil + Manager.defaultManager.didOptToRateClosure = nil + Manager.defaultManager.didOptToRemindLaterClosure = nil + +#if os(iOS) + Manager.defaultManager.usesAnimation = true + Manager.defaultManager.usesAlertController = false + Manager.defaultManager.opensInStoreKit = Manager.defaultManager.defaultOpensInStoreKit() + Manager.defaultManager.willPresentModalViewClosure = nil + Manager.defaultManager.didDismissModalViewClosure = nil +#endif + + Manager.defaultManager.armchairKeyFirstUseDate = Manager.defaultManager.defaultArmchairKeyFirstUseDate() + Manager.defaultManager.armchairKeyUseCount = Manager.defaultManager.defaultArmchairKeyUseCount() + Manager.defaultManager.armchairKeySignificantEventCount = Manager.defaultManager.defaultArmchairKeySignificantEventCount() + Manager.defaultManager.armchairKeyCurrentVersion = Manager.defaultManager.defaultArmchairKeyCurrentVersion() + Manager.defaultManager.armchairKeyRatedCurrentVersion = Manager.defaultManager.defaultArmchairKeyRatedCurrentVersion() + Manager.defaultManager.armchairKeyDeclinedToRate = Manager.defaultManager.defaultArmchairKeyDeclinedToRate() + Manager.defaultManager.armchairKeyReminderRequestDate = Manager.defaultManager.defaultArmchairKeyReminderRequestDate() + Manager.defaultManager.armchairKeyPreviousVersion = Manager.defaultManager.defaultArmchairKeyPreviousVersion() + Manager.defaultManager.armchairKeyPreviousVersionRated = Manager.defaultManager.defaultArmchairKeyPreviousVersionRated() + Manager.defaultManager.armchairKeyPreviousVersionDeclinedToRate = Manager.defaultManager.defaultArmchairKeyDeclinedToRate() + Manager.defaultManager.armchairKeyRatedAnyVersion = Manager.defaultManager.defaultArmchairKeyRatedAnyVersion() + Manager.defaultManager.armchairKeyAppiraterMigrationCompleted = Manager.defaultManager.defaultArmchairKeyAppiraterMigrationCompleted() + Manager.defaultManager.armchairKeyUAAppReviewManagerMigrationCompleted = Manager.defaultManager.defaultArmchairKeyUAAppReviewManagerMigrationCompleted() + + Manager.defaultManager.keyPrefix = Manager.defaultManager.defaultKeyPrefix() +} + +#if os(iOS) + /* + * Set whether or not Armchair uses animation when pushing modal StoreKit + * view controllers for the app. + * Default => true + */ + public func usesAnimation() -> Bool { + return Manager.defaultManager.usesAnimation + } + public func usesAnimation(usesAnimation: Bool) { + Manager.defaultManager.usesAnimation = usesAnimation + } + + /* + * Set whether or not Armchair uses a UIAlertController when presenting on iOS 8 + * We prefer not to use it so that the Rate button can be on the bottom and the cancel button on the top, + * Something not possible as of iOS 8.0 + * Default => false + */ + public func usesAlertController() -> Bool { + return Manager.defaultManager.usesAlertController + } + public func usesAlertController(usesAlertController: Bool) { + Manager.defaultManager.usesAlertController = usesAlertController + } + + /* + * If set to true, Armchair will open App Store link inside the app using + * SKStoreProductViewController. + * - itunes affiliate codes DO NOT work on iOS 7 inside StoreKit, + * - itunes affiliate codes DO work on iOS 8 inside StoreKit, + * Default => false on iOS 7, true on iOS 8+ + */ + public func opensInStoreKit() -> Bool { + return Manager.defaultManager.opensInStoreKit + } + public func opensInStoreKit(opensInStoreKit: Bool) { + Manager.defaultManager.opensInStoreKit = opensInStoreKit + } +#endif + +// MARK: Events + +/* +* Tells Armchair that the user performed a significant event. +* A significant event is whatever you want it to be. If you're app is used +* to make VoIP calls, then you might want to call this method whenever the +* user places a call. If it's a game, you might want to call this whenever +* the user beats a level boss. +* +* If the user has performed enough significant events and used the app enough, +* you can suppress the rating alert by passing false for canPromptForRating. The +* rating alert will simply be postponed until it is called again with true for +* canPromptForRating. +*/ +public func userDidSignificantEvent(canPromptForRating: Bool) { + Manager.defaultManager.userDidSignificantEvent(canPromptForRating) +} + +/* +* Tells Armchair that the user performed a significant event. +* A significant event is whatever you want it to be. If you're app is used +* to make VoIP calls, then you might want to call this method whenever the +* user places a call. If it's a game, you might want to call this whenever +* the user beats a level boss. +* +* This is similar to the userDidSignificantEvent method, but allows the passing of a +* ArmchairShouldPromptClosure that will be executed before prompting. +* The block passes all the keys and values that Armchair uses to +* determine if it the prompt conditions have been met, and it is up to you +* to use this info and return a Bool on whether or not the prompt should be shown. +* The block is run synchronous and on the main queue, so be sure to handle it appropriately. +* Return true to proceed and show the prompt, return false to kill the pending presentation. +*/ +public func userDidSignificantEvent(shouldPrompt: ArmchairShouldPromptClosure) { + Manager.defaultManager.userDidSignificantEvent(shouldPrompt) +} + +// MARK: Prompts + +/* +* Tells Armchair to show the prompt (a rating alert). The prompt +* will be showed if there is an internet connection available, the user hasn't +* declined to rate, hasn't rated current version and you are tracking new versions. +* +* You could call to show the prompt regardless of Armchair settings, +* for instance, in the case of some special event in your app. +*/ + +public func showPrompt() { + Manager.defaultManager.showPrompt() +} + +/* +* Tells Armchair to show the review prompt alert if all restrictions have been met. +* The prompt will be shown if all restrictions are met, there is an internet connection available, +* the user hasn't declined to rate, hasn't rated current version, and you are tracking new versions. +* +* You could call to show the prompt, for instance, in the case of some special event in your app, +* like a user login. +*/ +public func showPromptIfNecessary() { + Manager.defaultManager.showPrompt(ifNecessary: true) +} + +/* +* Tells Armchair to show the review prompt alert. +* +* This is similar to the showPromptIfNecessary method, but allows the passing of a +* ArmchairShouldPromptClosure that will be executed before prompting. +* The block passes all the keys and values that Armchair uses to +* determine if it the prompt conditions have been met, and it is up to you +* to use this info and return a Bool on whether or not the prompt should be shown. +* The block is run synchronous and on the main queue, so be sure to handle it appropriately. +* Return true to proceed and show the prompt, return false to kill the pending presentation. +*/ +public func showPrompt(shouldPrompt: ArmchairShouldPromptClosure) { + Manager.defaultManager.showPrompt(shouldPrompt) +} + +// MARK: Misc +/* +* This is the review URL string, generated by substituting the appID, affiliate code +* and affilitate campaign code into the template URL. +*/ +public func reviewURLString() -> String { + return Manager.defaultManager.reviewURLString() +} + +/* +* Tells Armchair to open the App Store page where the user can specify a +* rating for the app. Also records the fact that this has happened, so the +* user won't be prompted again to rate the app. +* +* The only case where you should call this directly is if your app has an +* explicit "Rate this app" command somewhere. In all other cases, don't worry +* about calling this -- instead, just call the other functions listed above, +* and let Armchair handle the bookkeeping of deciding when to ask the user +* whether to rate the app. +*/ +public func rateApp() { + Manager.defaultManager.rateApp() +} + +#if os(iOS) + /* + * Tells Armchair to immediately close any open rating modals + * for instance, a StoreKit rating View Controller. + */ + public func closeModalPanel() { + Manager.defaultManager.closeModalPanel() + } +#endif + +// MARK: Closures +/* +* Armchair uses blocks instead of delegate methods for callbacks. +* Default is nil for all of them. +*/ + +public typealias ArmchairClosure = () -> () +public typealias ArmchairAnimateClosure = (Bool) -> () +public typealias ArmchairShouldPromptClosure = (ArmchairTrackingInfo) -> Bool +public typealias ArmchairShouldIncrementClosure = () -> Bool + +public func onDidDisplayAlert(didDisplayAlertClosure: ArmchairClosure?) { + Manager.defaultManager.didDisplayAlertClosure = didDisplayAlertClosure +} +public func onDidDeclineToRate(didDeclineToRateClosure: ArmchairClosure?) { + Manager.defaultManager.didDeclineToRateClosure = didDeclineToRateClosure +} +public func onDidOptToRate(didOptToRateClosure: ArmchairClosure?) { + Manager.defaultManager.didOptToRateClosure = didOptToRateClosure +} +public func onDidOptToRemindLater(didOptToRemindLaterClosure: ArmchairClosure?) { + Manager.defaultManager.didOptToRemindLaterClosure = didOptToRemindLaterClosure +} + +#if os(iOS) + public func onWillPresentModalView(willPresentModalViewClosure: ArmchairAnimateClosure?) { + Manager.defaultManager.willPresentModalViewClosure = willPresentModalViewClosure + } + public func onDidDismissModalView(didDismissModalViewClosure: ArmchairAnimateClosure?) { + Manager.defaultManager.didDismissModalViewClosure = didDismissModalViewClosure + } +#endif + +/* +* The setShouldPromptBlock is called just after all the rating coditions +* have been met and Armchair has decided it should display a prompt, +* but just before the prompt actually displays. +* +* The block passes all the keys and values that Armchair used to +* determine that the prompt conditions had been met, but it is up to you +* to use this info and return a Bool on whether or not the prompt should be shown. +* Return true to proceed and show the prompt, return false to kill the pending presentation. +*/ +public func shouldPromptClosure(shouldPromptClosure: ArmchairShouldPromptClosure?) { + Manager.defaultManager.shouldPromptClosure = shouldPromptClosure +} + +/* +* The setShouldIncrementUseBlock, if valid, is called before incrementing the use count. +* Returning false allows you to ignore a use. This may be usefull in cases such as Facebook login +* where the app is backgrounded momentarily and the resultant enter foreground event should +* not be considered another use. +*/ +public func shouldIncrementUseCountClosure(shouldIncrementUseCountClosure: ArmchairShouldIncrementClosure?) { + Manager.defaultManager.shouldIncrementUseCountClosure = shouldIncrementUseCountClosure +} + +// MARK: - +// MARK: Armchair Defaults Protocol + +@objc public protocol ArmchairDefaultsObject { + func objectForKey(defaultName: String) -> AnyObject? + func setObject(value: AnyObject?, forKey defaultName: String) + func removeObjectForKey(defaultName: String) + + func stringForKey(defaultName: String) -> String? + func integerForKey(defaultName: String) -> Int + func doubleForKey(defaultName: String) -> Double + func boolForKey(defaultName: String) -> Bool + + func setInteger(value: Int, forKey defaultName: String) + func setDouble(value: Double, forKey defaultName: String) + func setBool(value: Bool, forKey defaultName: String) + + func synchronize() -> Bool +} + +public class StandardUserDefaults: ArmchairDefaultsObject { + let defaults = NSUserDefaults.standardUserDefaults() + + public func objectForKey(defaultName: String) -> AnyObject? { return defaults.objectForKey(defaultName) } + public func setObject(value: AnyObject?, forKey defaultName: String) { defaults.setObject(value, forKey: defaultName) } + public func removeObjectForKey(defaultName: String) { defaults.removeObjectForKey(defaultName) } + + public func stringForKey(defaultName: String) -> String? { return defaults.stringForKey(defaultName) } + public func integerForKey(defaultName: String) -> Int { return defaults.integerForKey(defaultName) } + public func doubleForKey(defaultName: String) -> Double { return defaults.doubleForKey(defaultName) } + public func boolForKey(defaultName: String) -> Bool { return defaults.boolForKey(defaultName) } + + public func setInteger(value: Int, forKey defaultName: String) { defaults.setInteger(value, forKey: defaultName) } + public func setDouble(value: Double, forKey defaultName: String) { defaults.setDouble(value, forKey: defaultName) } + public func setBool(value: Bool, forKey defaultName: String) { defaults.setBool(value, forKey: defaultName) } + + public func synchronize() -> Bool { return defaults.synchronize() } +} + +public enum ArmchairKey: String, Printable { + case FirstUseDate = "First Use Date" + case UseCount = "Use Count" + case SignificantEventCount = "Significant Event Count" + case CurrentVersion = "Current Version" + case RatedCurrentVersion = "Rated Current Version" + case DeclinedToRate = "Declined To Rate" + case ReminderRequestDate = "Reminder Request Date" + case PreviousVersion = "Previous Version" + case PreviousVersionRated = "Previous Version Rated" + case PreviousVersionDeclinedToRate = "Previous Version Declined To Rate" + case RatedAnyVersion = "Rated Any Version" + case AppiraterMigrationCompleted = "Appirater Migration Completed" + case UAAppReviewManagerMigrationCompleted = "UAAppReviewManager Migration Completed" + + static let allValues = [FirstUseDate, UseCount, SignificantEventCount, CurrentVersion, RatedCurrentVersion, DeclinedToRate, ReminderRequestDate, PreviousVersion, PreviousVersionRated, PreviousVersionDeclinedToRate, RatedAnyVersion, AppiraterMigrationCompleted, UAAppReviewManagerMigrationCompleted] + + public var description : String { + get { + return self.toRaw() + } + } +} + +public class ArmchairTrackingInfo: Printable { + public let info: Dictionary + + init(info: Dictionary) { + self.info = info + } + + public var description: String { + get { + var description = "ArmchairTrackingInfo\r" + for (key, val) in info { + description += " - \(key): \(val)\r" + } + return description + } + } +} + +public struct AppiraterKey { + static var FirstUseDate = "kAppiraterFirstUseDate" + static var UseCount = "kAppiraterUseCount" + static var SignificantEventCount = "kAppiraterSignificantEventCount" + static var CurrentVersion = "kAppiraterCurrentVersion" + static var RatedCurrentVersion = "kAppiraterRatedCurrentVersion" + static var RatedAnyVersion = "kAppiraterRatedAnyVersion" + static var DeclinedToRate = "kAppiraterDeclinedToRate" + static var ReminderRequestDate = "kAppiraterReminderRequestDate" +} + +// MARK: - +// MARK: PRIVATE Interface + +#if os(iOS) +public class ArmchairManager : NSObject, UIAlertViewDelegate, SKStoreProductViewControllerDelegate { } +#elseif os(OSX) +public class ArmchairManager : NSObject, NSAlertDelegate { } +#else +// Untested, and currently unsupported +#endif + +public class Manager : ArmchairManager { + + private var iOSVersion = NSString(string: UIDevice.currentDevice().systemVersion).doubleValue + + // MARK: - + // MARK: Review Alert & Properties + +#if os(iOS) + private var ratingAlert: UIAlertView? = nil +// private let reviewURLTemplate = "itms-apps://ax.itunes.apple.com/WebObjects/MZStore.woa/wa/viewContentsUserReviews?type=Purple+Software&id=APP_ID&at=AFFILIATE_CODE&ct=AFFILIATE_CAMPAIGN_CODE" + private let reviewURLTemplate = "itms-apps://itunes.apple.com/WebObjects/MZStore.woa/wa/viewContentsUserReviews?type=Purple+Software&onlyLatestVersion=true&pageNumber=0&sortOrdering=1&id=APP_ID&at=AFFILIATE_CODE&ct=AFFILIATE_CAMPAIGN_CODE" +#elseif os(OSX) + private var ratingAlert: NSAlert? = nil + private let reviewURLTemplate = "macappstore://itunes.apple.com/us/app/idAPP_ID?ls=1&mt=12&at=AFFILIATE_CODE&ct=AFFILIATE_CAMPAIGN_CODE" +#else +#endif + + private lazy var appName: String = self.defaultAppName() + private func defaultAppName() -> String { + let mainBundle = NSBundle.mainBundle() + let displayName = mainBundle.objectForInfoDictionaryKey("CFBundleDisplayName") as? String + let bundleNameKey = kCFBundleNameKey as NSString? + let name = mainBundle.objectForInfoDictionaryKey(bundleNameKey as String) as? String + return displayName ?? name ?? "This App" + } + + private lazy var reviewTitle: String = self.defaultReviewTitle() + private func defaultReviewTitle() -> String { + var template = "Rate %@" + // Check for a localized version of the default title + if let bundle = self.bundle() { + template = bundle.localizedStringForKey(template, value:"", table: "ArmchairLocalizable") + } + + return template.stringByReplacingOccurrencesOfString("%@", withString: "\(self.appName)", options: nil, range: nil) + } + + private lazy var reviewMessage: String = self.defaultReviewMessage() + private func defaultReviewMessage() -> String { + var template = "If you enjoy using %@, would you mind taking a moment to rate it? It won't take more than a minute. Thanks for your support!" + // Check for a localized version of the default title + if let bundle = self.bundle() { + template = bundle.localizedStringForKey(template, value:"", table: "ArmchairLocalizable") + } + + return template.stringByReplacingOccurrencesOfString("%@", withString: "\(self.appName)", options: nil, range: nil) + } + + private lazy var cancelButtonTitle: String = self.defaultCancelButtonTitle() + private func defaultCancelButtonTitle() -> String { + var title = "No, Thanks" + // Check for a localized version of the default title + if let bundle = self.bundle() { + title = bundle.localizedStringForKey(title, value:"", table: "ArmchairLocalizable") + } + + return title + } + + private lazy var rateButtonTitle: String = self.defaultRateButtonTitle() + private func defaultRateButtonTitle() -> String { + var template = "Rate %@" + // Check for a localized version of the default title + if let bundle = self.bundle() { + template = bundle.localizedStringForKey(template, value:"", table: "ArmchairLocalizable") + } + + return template.stringByReplacingOccurrencesOfString("%@", withString: "\(self.appName)", options: nil, range: nil) + } + + private lazy var remindButtonTitle: String? = self.defaultRemindButtonTitle() + private func defaultRemindButtonTitle() -> String? { + //if reminders are disabled, return a nil title to supress the button + if self.daysBeforeReminding == 0 { + return nil + } + var title = "Remind me later" + // Check for a localized version of the default title + if let bundle = self.bundle() { + title = bundle.localizedStringForKey(title, value:"", table: "ArmchairLocalizable") + } + + return title + } + + // Tracking Logic / Configuration + private var appID: String = "" { + didSet { + keyPrefix = defaultKeyPrefix() + if affiliateCampaignCode == defaultAffiliateCampaignCode() { + affiliateCampaignCode = affiliateCampaignCode + "-\(appID)" + } + } + } + + // MARK: Properties with sensible defaults + private var daysUntilPrompt: UInt = 30 + private var usesUntilPrompt: UInt = 20 + private var significantEventsUntilPrompt: UInt = 0 + private var daysBeforeReminding: UInt = 1 + private var tracksNewVersions: Bool = true + private var shouldPromptIfRated: Bool = true + private var useMainAppBundleForLocalizations: Bool = false + private var debugEnabled: Bool = false + + // If you aren't going to set an affiliate code yourself, please leave this as is. + // It is my affiliate code. It is better that somebody's code is used rather than nobody's. + private var affiliateCode: String = "11l7j9" + private var affiliateCampaignCode: String = "Armchair" + +#if os(iOS) + private var usesAnimation: Bool = true + private var usesAlertController: Bool = false + private lazy var opensInStoreKit: Bool = self.defaultOpensInStoreKit() + + private func defaultOpensInStoreKit() -> Bool { + return iOSVersion >= 8 + } +#endif + + // MARK: Tracking Keys with sensible defaults + private lazy var armchairKeyFirstUseDate: String = self.defaultArmchairKeyFirstUseDate() + private lazy var armchairKeyUseCount: String = self.defaultArmchairKeyUseCount() + private lazy var armchairKeySignificantEventCount: String = self.defaultArmchairKeySignificantEventCount() + private lazy var armchairKeyCurrentVersion: String = self.defaultArmchairKeyCurrentVersion() + private lazy var armchairKeyRatedCurrentVersion: String = self.defaultArmchairKeyRatedCurrentVersion() + private lazy var armchairKeyDeclinedToRate: String = self.defaultArmchairKeyDeclinedToRate() + private lazy var armchairKeyReminderRequestDate: String = self.defaultArmchairKeyReminderRequestDate() + private lazy var armchairKeyPreviousVersion: String = self.defaultArmchairKeyPreviousVersion() + private lazy var armchairKeyPreviousVersionRated: String = self.defaultArmchairKeyPreviousVersionRated() + private lazy var armchairKeyPreviousVersionDeclinedToRate: String = self.defaultArmchairKeyPreviousVersionDeclinedToRate() + private lazy var armchairKeyRatedAnyVersion: String = self.defaultArmchairKeyRatedAnyVersion() + private lazy var armchairKeyAppiraterMigrationCompleted: String = self.defaultArmchairKeyAppiraterMigrationCompleted() + private lazy var armchairKeyUAAppReviewManagerMigrationCompleted: String = self.defaultArmchairKeyUAAppReviewManagerMigrationCompleted() + + private func defaultArmchairKeyFirstUseDate() -> String { return "ArmchairFirstUseDate" } + private func defaultArmchairKeyUseCount() -> String { return "ArmchairUseCount" } + private func defaultArmchairKeySignificantEventCount() -> String { return "ArmchairSignificantEventCount" } + private func defaultArmchairKeyCurrentVersion() -> String { return "ArmchairKeyCurrentVersion" } + private func defaultArmchairKeyRatedCurrentVersion() -> String { return "ArmchairRatedCurrentVersion" } + private func defaultArmchairKeyDeclinedToRate() -> String { return "ArmchairKeyDeclinedToRate" } + private func defaultArmchairKeyReminderRequestDate() -> String { return "ArmchairReminderRequestDate" } + private func defaultArmchairKeyPreviousVersion() -> String { return "ArmchairPreviousVersion" } + private func defaultArmchairKeyPreviousVersionRated() -> String { return "ArmchairPreviousVersionRated" } + private func defaultArmchairKeyPreviousVersionDeclinedToRate() -> String { return "ArmchairPreviousVersionDeclinedToRate" } + private func defaultArmchairKeyRatedAnyVersion() -> String { return "ArmchairKeyRatedAnyVersion" } + private func defaultArmchairKeyAppiraterMigrationCompleted() -> String { return "ArmchairAppiraterMigrationCompleted" } + private func defaultArmchairKeyUAAppReviewManagerMigrationCompleted() -> String { return "ArmchairUAAppReviewManagerMigrationCompleted" } + + + private lazy var keyPrefix: String = self.defaultKeyPrefix() + private func defaultKeyPrefix() -> String { + if !self.appID.isEmpty { + return self.appID + "_" + } else { + return "_" + } + } + + private var userDefaultsObject:ArmchairDefaultsObject? = StandardUserDefaults() + + // MARK: Optional Closures + var didDisplayAlertClosure: ArmchairClosure? + var didDeclineToRateClosure: ArmchairClosure? + var didOptToRateClosure: ArmchairClosure? + var didOptToRemindLaterClosure: ArmchairClosure? + +#if os(iOS) + var willPresentModalViewClosure: ArmchairAnimateClosure? + var didDismissModalViewClosure: ArmchairAnimateClosure? +#endif + var shouldPromptClosure: ArmchairShouldPromptClosure? + var shouldIncrementUseCountClosure: ArmchairShouldIncrementClosure? + + // MARK: State Vars + private var modalPanelOpen: Bool = false +#if os(iOS) + private lazy var currentStatusBarStyle: UIStatusBarStyle = { + return UIApplication.sharedApplication().statusBarStyle + }() +#endif + + // MARK: - + // MARK: PRIVATE Methods + + private func userDidSignificantEvent(canPromptForRating: Bool) { + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0)) { + self.incrementSignificantEventAndRate(canPromptForRating) + } + } + + private func userDidSignificantEvent(shouldPrompt: ArmchairShouldPromptClosure) { + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0)) { + self.incrementSignificantEventAndRate(shouldPrompt) + } + } + + // MARK: - + // MARK: PRIVATE Rating Helpers + + private func incrementAndRate(canPromptForRating: Bool) { + migrateKeysIfNecessary() + incrementUseCount() + showPrompt(ifNecessary: canPromptForRating) + } + + private func incrementAndRate(shouldPrompt: ArmchairShouldPromptClosure) { + migrateKeysIfNecessary() + incrementUseCount() + showPrompt(shouldPrompt) + } + + private func incrementSignificantEventAndRate(canPromptForRating: Bool) { + migrateKeysIfNecessary() + incrementSignificantEventCount() + showPrompt(ifNecessary: canPromptForRating) + } + + private func incrementSignificantEventAndRate(shouldPrompt: ArmchairShouldPromptClosure) { + migrateKeysIfNecessary() + incrementSignificantEventCount() + showPrompt(shouldPrompt) + } + + private func incrementUseCount() { + var shouldIncrement = true + if let closure = shouldIncrementUseCountClosure { + shouldIncrement = closure() + } + + if shouldIncrement { + _incrementCountForKeyType(ArmchairKey.UseCount) + } + } + + private func incrementSignificantEventCount() { + _incrementCountForKeyType(ArmchairKey.SignificantEventCount) + } + + private func _incrementCountForKeyType(incrementKeyType: ArmchairKey) { + var incrementKey = keyForArmchairKeyType(incrementKeyType) + + // App's version. Not settable as the other ivars because that would be crazy. + var currentVersion: String = NSBundle.mainBundle().infoDictionary[kCFBundleVersionKey] as NSString + + // Get the version number that we've been tracking thus far + var currentVersionKey = keyForArmchairKeyType(ArmchairKey.CurrentVersion) + var trackingVersion: String? = userDefaultsObject?.stringForKey(currentVersionKey) + // New install, or changed keys + if let version = trackingVersion { + } else { + trackingVersion = currentVersion + userDefaultsObject?.setObject(currentVersion, forKey: currentVersionKey) + } + + debugLog("Tracking version: \(trackingVersion!)") + + if trackingVersion == currentVersion { + // Check if the first use date has been set. if not, set it. + var firstUseDateKey = keyForArmchairKeyType(ArmchairKey.FirstUseDate) + var timeInterval: Double? = userDefaultsObject?.doubleForKey(firstUseDateKey) + if 0 == timeInterval { + timeInterval = NSDate().timeIntervalSince1970 + userDefaultsObject?.setObject(NSNumber.numberWithDouble(timeInterval!), forKey: firstUseDateKey) + } + + // Increment the key's count + var incrementKeyCount = userDefaultsObject?.integerForKey(incrementKey) + userDefaultsObject?.setInteger(++incrementKeyCount!, forKey:incrementKey) + + debugLog("Incremented \(incrementKeyType) count: \(incrementKeyCount)") + + } else if tracksNewVersions { + // it's a new version of the app, so restart tracking + userDefaultsObject?.setObject(trackingVersion, forKey: keyForArmchairKeyType(ArmchairKey.PreviousVersion)) + userDefaultsObject?.setObject(userDefaultsObject?.objectForKey(keyForArmchairKeyType(ArmchairKey.RatedCurrentVersion)), forKey: keyForArmchairKeyType(ArmchairKey.PreviousVersionRated)) + userDefaultsObject?.setObject(userDefaultsObject?.objectForKey(keyForArmchairKeyType(ArmchairKey.DeclinedToRate)), forKey: keyForArmchairKeyType(ArmchairKey.PreviousVersionDeclinedToRate)) + + userDefaultsObject?.setObject(currentVersion, forKey: currentVersionKey) + userDefaultsObject?.setObject(NSNumber.numberWithDouble(NSDate().timeIntervalSince1970), forKey: keyForArmchairKeyType(ArmchairKey.FirstUseDate)) + userDefaultsObject?.setObject(NSNumber.numberWithInteger(1), forKey: keyForArmchairKeyType(ArmchairKey.UseCount)) + userDefaultsObject?.setObject(NSNumber.numberWithInteger(0), forKey: keyForArmchairKeyType(ArmchairKey.SignificantEventCount)) + userDefaultsObject?.setObject(NSNumber.numberWithBool(false), forKey: keyForArmchairKeyType(ArmchairKey.RatedCurrentVersion)) + userDefaultsObject?.setObject(NSNumber.numberWithBool(false), forKey: keyForArmchairKeyType(ArmchairKey.DeclinedToRate)) + userDefaultsObject?.setObject(NSNumber.numberWithDouble(0), forKey: keyForArmchairKeyType(ArmchairKey.ReminderRequestDate)) + } + + userDefaultsObject?.synchronize() + } + + private func showPrompt(ifNecessary canPromptForRating: Bool) { + if canPromptForRating && connectedToNetwork() && ratingConditionsHaveBeenMet() { + var shouldPrompt: Bool = true + + if let closure = shouldPromptClosure { + if NSThread.isMainThread() { + shouldPrompt = closure(trackingInfo()) + } else { + dispatch_sync(dispatch_get_main_queue()) { + shouldPrompt = closure(self.trackingInfo()) + } + } + } + + if shouldPrompt { + dispatch_async(dispatch_get_main_queue()) { + self.showRatingAlert() + } + } + } + } + + private func showPrompt(shouldPrompt: ArmchairShouldPromptClosure) { + var shouldPromptVal = false + + if NSThread.isMainThread() { + shouldPromptVal = shouldPrompt(trackingInfo()) + } else { + dispatch_sync(dispatch_get_main_queue()) { + shouldPromptVal = shouldPrompt(self.trackingInfo()) + } + } + + + if (shouldPromptVal) { + dispatch_async(dispatch_get_main_queue()) { + self.showRatingAlert() + } + } + } + + private func showPrompt() { + if !appID.isEmpty && connectedToNetwork() && !userHasDeclinedToRate() && !userHasRatedCurrentVersion() { + showRatingAlert() + } + } + + private func ratingConditionsHaveBeenMet() -> Bool { + if debugEnabled { + return true + } + + if appID.isEmpty { + return false + } + + // check if the app has been used long enough + var timeIntervalOfFirstLaunch = userDefaultsObject?.doubleForKey(keyForArmchairKeyType(ArmchairKey.FirstUseDate)) + if let timeInterval = timeIntervalOfFirstLaunch { + var dateOfFirstLaunch = NSDate(timeIntervalSince1970: timeInterval) + var timeSinceFirstLaunch = NSDate().timeIntervalSinceDate(dateOfFirstLaunch) + var timeUntilRate: NSTimeInterval = 60 * 60 * 24 * Double(daysUntilPrompt) + if timeSinceFirstLaunch < timeUntilRate { + return false + } + } else { + return false + } + + // check if the app has been used enough times + var useCount = userDefaultsObject?.integerForKey(keyForArmchairKeyType(ArmchairKey.UseCount)) + if let count = useCount { + if UInt(count) <= usesUntilPrompt { + return false + } + } else { + return false + } + + // check if the user has done enough significant events + var significantEventCount = userDefaultsObject?.integerForKey(keyForArmchairKeyType(ArmchairKey.SignificantEventCount)) + if let count = significantEventCount { + if UInt(count) < significantEventsUntilPrompt { + return false + } + } else { + return false + } + + // Check if the user previously has declined to rate this version of the app + if userHasDeclinedToRate() { + return false + } + + // Check if the user has already rated the app? + if userHasRatedCurrentVersion() { + return false + } + + // If the user wanted to be reminded later, has enough time passed? + var timeIntervalOfReminder = userDefaultsObject?.doubleForKey(keyForArmchairKeyType(ArmchairKey.ReminderRequestDate)) + if let timeInterval = timeIntervalOfReminder { + var reminderRequestDate = NSDate(timeIntervalSince1970: timeInterval) + var timeSinceReminderRequest = NSDate().timeIntervalSinceDate(reminderRequestDate) + var timeUntilReminder: NSTimeInterval = 60 * 60 * 24 * Double(daysBeforeReminding) + if timeSinceReminderRequest < timeUntilReminder { + return false + } + } else { + return false + } + + // if we have a global set to not show if the end-user has already rated once, and the developer has not opted out of displaying on minor updates + var ratedAnyVersion = userDefaultsObject?.boolForKey(keyForArmchairKeyType(ArmchairKey.RatedAnyVersion)) + if let ratedAlready = ratedAnyVersion { + if (!shouldPromptIfRated && ratedAlready) { + return false + } + } + + return true + } + + private func userHasDeclinedToRate() -> Bool { + if let declined = userDefaultsObject?.boolForKey(keyForArmchairKeyType(ArmchairKey.DeclinedToRate)) { + return declined + } else { + return false + } + } + + private func userHasRatedCurrentVersion() -> Bool { + if let ratedCurrentVersion = userDefaultsObject?.boolForKey(keyForArmchairKeyType(ArmchairKey.RatedCurrentVersion)) { + return ratedCurrentVersion + } else { + return false + } + } + + private func showsRemindButton() -> Bool { + if let title = remindButtonTitle { + return true + } else { + return false + } + } + + private func showRatingAlert() { +#if os(iOS) + if iOSVersion >= 8 && usesAlertController { + /* iOS 8 uses new UIAlertController API*/ + var alertView : UIAlertController = UIAlertController(title: reviewTitle, message: reviewMessage, preferredStyle: UIAlertControllerStyle.Alert) + alertView.addAction(UIAlertAction(title: cancelButtonTitle, style:UIAlertActionStyle.Cancel, handler: { + (alert: UIAlertAction!) in + self.dontRate() + })) + if (showsRemindButton()) { + alertView.addAction(UIAlertAction(title: remindButtonTitle!, style:UIAlertActionStyle.Default, handler: { + (alert: UIAlertAction!) in + self.remindMeLater() + })) + } + alertView.addAction(UIAlertAction(title: rateButtonTitle, style:UIAlertActionStyle.Default, handler: { + (alert: UIAlertAction!) in + self._rateApp() + })) + + // get the top most controller (= the StoreKit Controller) and dismiss it + if let presentingController = UIApplication.sharedApplication().keyWindow.rootViewController { + if let topController = topMostViewController(presentingController) { + topController.presentViewController(alertView, animated: usesAnimation) { + println("presentViewController() completed") + } + } + } + + } else { + /* Otherwise we use UIAlertView still */ + var alertView: UIAlertView + if (showsRemindButton()) { + alertView = UIAlertView(title: reviewTitle, message: reviewMessage, delegate: self, cancelButtonTitle: cancelButtonTitle, otherButtonTitles: remindButtonTitle!, rateButtonTitle) + } else { + alertView = UIAlertView(title: reviewTitle, message: reviewMessage, delegate: self, cancelButtonTitle: cancelButtonTitle, otherButtonTitles: rateButtonTitle) + } + // If we have a remind button, show it first. Otherwise show the rate button + // If we have a remind button, show the rate button next. Otherwise stop adding buttons. + + alertView.cancelButtonIndex = -1 + ratingAlert = alertView + alertView.show() + + if let closure = didDisplayAlertClosure { + closure() + } + } + +#elseif os(OSX) +// NSAlert *alert = [NSAlert alertWithMessageText:self.reviewTitle +// defaultButton:self.rateButtonTitle +// alternateButton:self.cancelButtonTitle +// otherButton:self.remindButtonTitle +// informativeTextWithFormat:@"%@",self.reviewMessage]; +// self.ratingAlert = alert; +// +// NSWindow *window = [[NSApplication sharedApplication] keyWindow]; +// if (window) { +// [alert beginSheetModalForWindow:[[NSApplication sharedApplication] keyWindow] +// modalDelegate:self +// didEndSelector:@selector(alertDidEnd:returnCode:contextInfo:) +// contextInfo:nil]; +// } else { +// NSInteger returnCode = [alert runModal]; +// [self handleNSAlertReturnCode:returnCode]; +// } +// +// if (self.didDisplayAlertBlock) +// self.didDisplayAlertBlock(); +#else + println("OMG, it's that mythical new Apple product!!!") +#endif + } + + // MARK: - + // MARK: PRIVATE Alert View / StoreKit Delegate Methods + +#if os(iOS) + public func alertView(alertView: UIAlertView, didDismissWithButtonIndex buttonIndex: Int) { + // cancelButtonIndex is set to -1 to show the cancel button up top, but a tap on it ends up here with index 0 + if (alertView.cancelButtonIndex == buttonIndex || 0 == buttonIndex) { + // they don't want to rate it + dontRate() + } else if (showsRemindButton() && alertView.firstOtherButtonIndex == buttonIndex) { + // remind them later + remindMeLater() + } else { + // they want to rate it + _rateApp() + } + } + + //Delegate call from the StoreKit view. + public func productViewControllerDidFinish(viewController: SKStoreProductViewController!) { + closeModalPanel() + } + + //Close the in-app rating (StoreKit) view and restore the previous status bar style. + private func closeModalPanel() { + if modalPanelOpen { + UIApplication.sharedApplication().setStatusBarStyle(currentStatusBarStyle, animated:usesAnimation) + let usedAnimation = usesAnimation + modalPanelOpen = false + + // get the top most controller (= the StoreKit Controller) and dismiss it + if let presentingController = UIApplication.sharedApplication().keyWindow.rootViewController { + if let topController = topMostViewController(presentingController) { + topController.dismissViewControllerAnimated(usesAnimation) { + if let closure = self.didDismissModalViewClosure { + closure(usedAnimation) + } + } + currentStatusBarStyle = UIStatusBarStyle.Default + } + } + } + } + +#elseif os(OSX) + + private func handleNSAlertReturnCode(returnCode: NSInteger) { + switch (returnCode) { + case NSAlertAlternateReturn: + // they don't want to rate it + dontRate() + case NSAlertDefaultReturn: + // they want to rate it + _rateApp() + case NSAlertOtherReturn: + // remind them later + remindMeLater() + } + } + + private func alertDidEnd(alert: NSAlert, returnCode: NSInteger, contextInfo: void) { + handleNSAlertReturnCode(returnCode) + } + +#else +#endif + + private func dontRate() { + userDefaultsObject?.setBool(true, forKey: keyForArmchairKeyType(ArmchairKey.DeclinedToRate)) + userDefaultsObject?.synchronize() + if let closure = didDeclineToRateClosure { + closure() + } + } + + private func remindMeLater() { + userDefaultsObject?.setDouble(NSDate().timeIntervalSince1970, forKey: keyForArmchairKeyType(ArmchairKey.ReminderRequestDate)) + userDefaultsObject?.synchronize() + if let closure = didOptToRemindLaterClosure { + closure() + } + } + + private func _rateApp() { + rateApp() + if let closure = didOptToRateClosure { + closure() + } + } + + private func rateApp() { + + userDefaultsObject?.setBool(true, forKey: keyForArmchairKeyType(ArmchairKey.RatedCurrentVersion)) + userDefaultsObject?.setBool(true, forKey: keyForArmchairKeyType(ArmchairKey.RatedAnyVersion)) + userDefaultsObject?.synchronize() + +#if os(iOS) + // Use the in-app StoreKit view if set, available (iOS 6+) and imported This works in the simulator. + if opensInStoreKit { + + let storeViewController = SKStoreProductViewController() + let appIDVal: Int? = appID.toInt() + + if let appIDInt = appIDVal { + var productParameters: [NSObject:AnyObject]! = [SKStoreProductParameterITunesItemIdentifier : NSNumber(integer: appIDInt)] + + if (iOSVersion >= 8) { + productParameters[SKStoreProductParameterAffiliateToken] = affiliateCode + productParameters[SKStoreProductParameterCampaignToken] = affiliateCampaignCode + } + + storeViewController.loadProductWithParameters(productParameters, completionBlock: nil) + storeViewController.delegate = self + + if let closure = willPresentModalViewClosure { + closure(usesAnimation) + } + + + if let rootController = getRootViewController() { + rootController.presentViewController(storeViewController, animated: usesAnimation) { + self.modalPanelOpen = true + + //Temporarily use a status bar to match the StoreKit view. + self.currentStatusBarStyle = UIApplication.sharedApplication().statusBarStyle + UIApplication.sharedApplication().setStatusBarStyle(UIStatusBarStyle.Default, animated: self.usesAnimation) + } + } + } else { + debugLog("Can't parse \(appID) into an Int for the SKStoreProductViewController") + } + + //Use the standard openUrl method + } else { + UIApplication.sharedApplication().openURL(NSURL(string: reviewURLString())) + } + + if UIDevice.currentDevice().model.rangeOfString("Simulator") != nil { + debugLog("iTunes App Store is not supported on the iOS simulator.") + debugLog(" - We would have went to \(reviewURLString()).") + debugLog(" - Try running on a test-device") + let fakeURL = reviewURLString().stringByReplacingOccurrencesOfString("itms-apps", withString:"http") + debugLog(" - Or try copy/pasting \(fakeURL) into a browser on your computer.") + } +#elseif os(OSX) + NSWorkspace.sharedWorkspace().openURL(NSURL(reviewURLString)) +#else +#endif + } + + private func reviewURLString() -> String { + var template = reviewURLTemplate + var reviewURL = template.stringByReplacingOccurrencesOfString("APP_ID", withString: "\(appID)") + reviewURL = reviewURL.stringByReplacingOccurrencesOfString("AFFILIATE_CODE", withString: "\(affiliateCode)") + reviewURL = reviewURL.stringByReplacingOccurrencesOfString("AFFILIATE_CAMPAIGN_CODE", withString: "\(affiliateCampaignCode)") + return reviewURL + } + + // Mark: - + // Mark: PRIVATE Key Helpers + + private func trackingInfo() -> ArmchairTrackingInfo { + var trackingInfo: Dictionary = [:] + + for keyType in ArmchairKey.allValues { + var obj: AnyObject? = userDefaultsObject?.objectForKey(keyForArmchairKeyType(keyType)) + if let val = obj as? NSObject { + trackingInfo[keyType] = val + } else { + trackingInfo[keyType] = NSNull() + } + } + + return ArmchairTrackingInfo(info: trackingInfo) + } + + private func keyForArmchairKeyType(keyType: ArmchairKey) -> String { + switch (keyType) { + case .FirstUseDate: + return keyPrefix + armchairKeyFirstUseDate + case .UseCount: + return keyPrefix + armchairKeyUseCount + case .SignificantEventCount: + return keyPrefix + armchairKeySignificantEventCount + case .CurrentVersion: + return keyPrefix + armchairKeyCurrentVersion + case .RatedCurrentVersion: + return keyPrefix + armchairKeyRatedCurrentVersion + case .DeclinedToRate: + return keyPrefix + armchairKeyDeclinedToRate + case .ReminderRequestDate: + return keyPrefix + armchairKeyReminderRequestDate + case .PreviousVersion: + return keyPrefix + armchairKeyPreviousVersion + case .PreviousVersionRated: + return keyPrefix + armchairKeyPreviousVersionRated + case .PreviousVersionDeclinedToRate: + return keyPrefix + armchairKeyPreviousVersionDeclinedToRate + case .RatedAnyVersion: + return keyPrefix + armchairKeyRatedAnyVersion + case .AppiraterMigrationCompleted: + return keyPrefix + armchairKeyAppiraterMigrationCompleted + case .UAAppReviewManagerMigrationCompleted: + return keyPrefix + armchairKeyUAAppReviewManagerMigrationCompleted + } + } + + private func setKey(key: NSString, armchairKeyType: ArmchairKey) { + switch armchairKeyType { + case .FirstUseDate: + armchairKeyFirstUseDate = key + case .UseCount: + armchairKeyUseCount = key + case .SignificantEventCount: + armchairKeySignificantEventCount = key + case .CurrentVersion: + armchairKeyCurrentVersion = key + case .RatedCurrentVersion: + armchairKeyRatedCurrentVersion = key + case .DeclinedToRate: + armchairKeyDeclinedToRate = key + case .ReminderRequestDate: + armchairKeyReminderRequestDate = key + case .PreviousVersion: + armchairKeyPreviousVersion = key + case .PreviousVersionRated: + armchairKeyPreviousVersionRated = key + case .PreviousVersionDeclinedToRate: + armchairKeyPreviousVersionDeclinedToRate = key + case .RatedAnyVersion: + armchairKeyRatedAnyVersion = key + case .AppiraterMigrationCompleted: + armchairKeyAppiraterMigrationCompleted = key + case .UAAppReviewManagerMigrationCompleted: + armchairKeyUAAppReviewManagerMigrationCompleted = key + } + } + + private func armchairKeyForAppiraterKey(appiraterKey: String) -> String { + switch appiraterKey { + case AppiraterKey.FirstUseDate: + return keyForArmchairKeyType(ArmchairKey.FirstUseDate) + case AppiraterKey.UseCount: + return keyForArmchairKeyType(ArmchairKey.UseCount) + case AppiraterKey.SignificantEventCount: + return keyForArmchairKeyType(ArmchairKey.SignificantEventCount) + case AppiraterKey.CurrentVersion: + return keyForArmchairKeyType(ArmchairKey.CurrentVersion) + case AppiraterKey.RatedCurrentVersion: + return keyForArmchairKeyType(ArmchairKey.RatedCurrentVersion) + case AppiraterKey.DeclinedToRate: + return keyForArmchairKeyType(ArmchairKey.DeclinedToRate) + case AppiraterKey.ReminderRequestDate: + return keyForArmchairKeyType(ArmchairKey.ReminderRequestDate) + case AppiraterKey.RatedAnyVersion: + return keyForArmchairKeyType(ArmchairKey.RatedAnyVersion) + default: + return "" + } + } + + private func migrateAppiraterKeysIfNecessary() { + + let appiraterAlreadyCompletedKey: NSString = keyForArmchairKeyType(.AppiraterMigrationCompleted) + var appiraterMigrationAlreadyCompleted = userDefaultsObject?.boolForKey(appiraterAlreadyCompletedKey) + + if let completed = appiraterMigrationAlreadyCompleted { + if completed { + return + } + } + + var oldKeys: [String] = [AppiraterKey.FirstUseDate, + AppiraterKey.UseCount, + AppiraterKey.SignificantEventCount, + AppiraterKey.CurrentVersion, + AppiraterKey.RatedCurrentVersion, + AppiraterKey.RatedAnyVersion, + AppiraterKey.DeclinedToRate, + AppiraterKey.ReminderRequestDate] + for oldKey in oldKeys { + var oldValue: NSObject? = userDefaultsObject?.objectForKey(oldKey) as? NSObject + if let val = oldValue { + let newKey = armchairKeyForAppiraterKey(oldKey) + userDefaultsObject?.setObject(val, forKey: newKey) + userDefaultsObject?.removeObjectForKey(oldKey) + } + } + + userDefaultsObject?.setObject(NSNumber.numberWithBool(true), forKey: appiraterAlreadyCompletedKey) + userDefaultsObject?.synchronize() + } + + // This only supports the default UAAppReviewManager keys. If you customized them, you will have to manually migrate your values over. + private func migrateUAAppReviewManagerKeysIfNecessary() { + let appReviewManagerAlreadyCompletedKey: NSString = keyForArmchairKeyType(.UAAppReviewManagerMigrationCompleted) + var appReviewManagerMigrationAlreadyCompleted = userDefaultsObject?.boolForKey(appReviewManagerAlreadyCompletedKey) + + if let completed = appReviewManagerMigrationAlreadyCompleted { + if completed { + return + } + } + + // By default, UAAppReviewManager keys are in the format _UAAppReviewManagerKey + var oldKeys: [String:ArmchairKey] = ["\(appID)_UAAppReviewManagerKeyFirstUseDate" : ArmchairKey.FirstUseDate, + "\(appID)_UAAppReviewManagerKeyUseCount" : ArmchairKey.UseCount, + "\(appID)_UAAppReviewManagerKeySignificantEventCount" : ArmchairKey.SignificantEventCount, + "\(appID)_UAAppReviewManagerKeyCurrentVersion" : ArmchairKey.CurrentVersion, + "\(appID)_UAAppReviewManagerKeyRatedCurrentVersion" : ArmchairKey.RatedCurrentVersion, + "\(appID)_UAAppReviewManagerKeyDeclinedToRate" : ArmchairKey.DeclinedToRate, + "\(appID)_UAAppReviewManagerKeyReminderRequestDate" : ArmchairKey.ReminderRequestDate, + "\(appID)_UAAppReviewManagerKeyPreviousVersion" : ArmchairKey.PreviousVersion, + "\(appID)_UAAppReviewManagerKeyPreviousVersionRated" : ArmchairKey.PreviousVersionRated, + "\(appID)_UAAppReviewManagerKeyPreviousVersionDeclinedToRate" : ArmchairKey.PreviousVersionDeclinedToRate, + "\(appID)_UAAppReviewManagerKeyRatedAnyVersion" : ArmchairKey.RatedAnyVersion] + for (oldKey, newKeyType) in oldKeys { + var oldValue: NSObject? = userDefaultsObject?.objectForKey(oldKey) as? NSObject + if let val = oldValue { + userDefaultsObject?.setObject(val, forKey: keyForArmchairKeyType(newKeyType)) + userDefaultsObject?.removeObjectForKey(oldKey) + } + } + + userDefaultsObject?.setObject(NSNumber.numberWithBool(true), forKey: appReviewManagerAlreadyCompletedKey) + userDefaultsObject?.synchronize() + } + + private func migrateKeysIfNecessary() { + migrateAppiraterKeysIfNecessary() + migrateUAAppReviewManagerKeysIfNecessary() + } + + // MARK: - + // MARK: Internet Connectivity + + private func connectedToNetwork() -> Bool { + var zeroAddress = sockaddr_in(sin_len: 0, sin_family: 0, sin_port: 0, sin_addr: in_addr(s_addr: 0), sin_zero: (0, 0, 0, 0, 0, 0, 0, 0)) + zeroAddress.sin_len = UInt8(sizeofValue(zeroAddress)) + zeroAddress.sin_family = sa_family_t(AF_INET) + + let defaultRouteReachability = withUnsafePointer(&zeroAddress) { + SCNetworkReachabilityCreateWithAddress(nil, UnsafePointer($0)).takeRetainedValue() + } + + var flags : SCNetworkReachabilityFlags = 0 + if SCNetworkReachabilityGetFlags(defaultRouteReachability, &flags) == 0 { + return false + } + + let isReachable = (flags & UInt32(kSCNetworkFlagsReachable)) != 0 + let needsConnection = (flags & UInt32(kSCNetworkFlagsConnectionRequired)) != 0 + return (isReachable && !needsConnection) + } + + // MARK: - + // MARK: PRIVATE Misc Helpers + + private func bundle() -> NSBundle? { + var bundle: NSBundle? = nil + if useMainAppBundleForLocalizations { + bundle = NSBundle.mainBundle() + } else { + // These bundles are exactly the same, but splitting them by target makes Cocoapods happy. +#if os(iOS) + let armchairBundleURL: NSURL? = NSBundle.mainBundle().URLForResource("Armchair-iOS", withExtension: "bundle") +#elseif os(OSX) + let armchairBundleURL: NSURL? = NSBundle.mainBundle().URLForResource("Armchair-OSX", withExtension: "bundle") +#else + let armchairBundleURL: NSURL? = nil +#endif + if let url = armchairBundleURL { + // Armchair-[iOS|OSX].bundle will likely only exist when used via CocoaPods + bundle = NSBundle(URL: url) + } else { + bundle = NSBundle.mainBundle() + } + } + + return bundle + } + +#if os(iOS) + private func topMostViewController(controller: UIViewController?) -> UIViewController? { + var isPresenting: Bool = false + var topController: UIViewController? = controller + do { + // this path is called only on iOS 6+, so -presentedViewController is fine here. + if let controller = topController { + var presented: UIViewController? = controller.presentedViewController + if let presentedController = presented { + isPresenting = true + topController = presented + } else { + isPresenting = false + } + } + } while isPresenting + + return topController + } + + private func getRootViewController() -> UIViewController? { + var window: UIWindow = UIApplication.sharedApplication().keyWindow + if window.windowLevel != UIWindowLevelNormal { + var windows: NSArray = UIApplication.sharedApplication().windows + for window in windows { + if window.windowLevel == UIWindowLevelNormal { + break + } + } + } + + for subView in window.subviews { + var nextResponder: UIResponder? = subView.nextResponder() + if let responder = nextResponder { + if responder.isKindOfClass(UIViewController) { + return topMostViewController(responder as? UIViewController) + } + + } + } + + return nil + } +#endif + + private func hideRatingAlert() { + if let alert = ratingAlert { + debugLog("Hiding Alert") +#if os(iOS) + if alert.visible { + alert.dismissWithClickedButtonIndex(alert.cancelButtonIndex, animated: false) + } +#elseif os(OSX) + NSApp.endSheet(window: NSApplication.sharedApplication().keyWindow) +#else +#endif + ratingAlert = nil + } + } + + private func defaultAffiliateCode() -> String { + return "11l7j9" + } + + private func defaultAffiliateCampaignCode() -> String { + return "Armchair" + } + + // MARK: - + // MARK: Notification Handlers + + public func appWillResignActive(notification: NSNotification) { + debugLog("appWillResignActive:") + hideRatingAlert() + } + + public func applicationDidFinishLaunching(notification: NSNotification) { + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0)) { + self.debugLog("applicationDidFinishLaunching:") + self.migrateKeysIfNecessary() + self.incrementUseCount() + } + } + + public func applicationWillEnterForeground(notification: NSNotification) { + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0)) { + self.debugLog("applicationWillEnterForeground:") + self.migrateKeysIfNecessary() + self.incrementUseCount() + } + } + + // MARK: - + // MARK: Singleton + public class var defaultManager: Manager { + assert(Armchair.appID != "", "Armchair.appID(appID: String) has to be the first Armchair call made.") + struct Singleton { + static let instance: Manager = Manager(appID: Armchair.appID) + } + return Singleton.instance + } + + init(appID: String) { + super.init() + setupNotifications() + } + + // MARK: Singleton Instance Setup + + private func setupNotifications() { +#if os(iOS) + NSNotificationCenter.defaultCenter().addObserver(self, selector: "appWillResignActive:", name: UIApplicationWillResignActiveNotification, object: nil) + NSNotificationCenter.defaultCenter().addObserver(self, selector: "applicationDidFinishLaunching:", name: UIApplicationDidFinishLaunchingNotification, object: nil) + NSNotificationCenter.defaultCenter().addObserver(self, selector: "applicationWillEnterForeground:", name: UIApplicationWillEnterForegroundNotification, object: nil) +#elseif os(OSX) + NSNotificationCenter.defaultCenter().addObserver(self, selector: "appWillResignActive:", name: NSApplicationWillResignActiveNotification, object: nil) + NSNotificationCenter.defaultCenter().addObserver(self, selector: "applicationDidFinishLaunching:", name: NSApplicationDidFinishLaunchingNotification, object: nil) + NSNotificationCenter.defaultCenter().addObserver(self, selector: "applicationWillEnterForeground:", name: NSApplicationWillBecomeActiveNotification, object: nil) +#else +#endif + } + + // MARK: - + // MARK: Printable + override public var debugDescription: String { + get { + return "Armchair: appID=\(Armchair.appID)" + } + } + + // MARK: - + // MARK: Debug + + let lockQueue = dispatch_queue_create("com.armchair.lockqueue", nil) + + private func debugLog(log: String) { + if debugEnabled { + dispatch_sync(lockQueue, { + println("[Armchair] \(log)") + }) + } + } +} diff --git a/Source/Info.plist b/Source/Info.plist new file mode 100644 index 0000000..c1a3778 --- /dev/null +++ b/Source/Info.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + com.armchair.$(PRODUCT_NAME:rfc1034identifier) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + NSPrincipalClass + + + diff --git a/Tests/ArmchairTests.swift b/Tests/ArmchairTests.swift new file mode 100644 index 0000000..cf6aba3 --- /dev/null +++ b/Tests/ArmchairTests.swift @@ -0,0 +1,41 @@ +// ParameterEncodingTests.swift +// +// Copyright (c) 2014 Armchair (http://github.com/UrbanApps/Armchair) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import Foundation +import Armchair +import XCTest + +class ArmchairTestCase: XCTestCase { + + var appID: String! = "987654321" + + override func setUp() { + super.setUp() + + Armchair.appID(appID) + } + // MARK: - + + func testAppIDSetProperly() { + XCTAssertEqual(Armchair.appID(), appID, "appID should be as set") + } +} diff --git a/Tests/Info.plist b/Tests/Info.plist new file mode 100644 index 0000000..d71ae81 --- /dev/null +++ b/Tests/Info.plist @@ -0,0 +1,24 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + com.armchair.$(PRODUCT_NAME:rfc1034identifier) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + + diff --git a/iOS Example.xcodeproj/project.pbxproj b/iOS Example.xcodeproj/project.pbxproj new file mode 100644 index 0000000..125f4c9 --- /dev/null +++ b/iOS Example.xcodeproj/project.pbxproj @@ -0,0 +1,367 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + E6D8B8EC19C75559001AD043 /* Armchair.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E6D8B8BB19C7382B001AD043 /* Armchair.framework */; }; + F8111E0B19A951050040E7D1 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8111E0A19A951050040E7D1 /* AppDelegate.swift */; }; + F8111E0D19A951050040E7D1 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8111E0C19A951050040E7D1 /* ViewController.swift */; }; + F8111E1219A951050040E7D1 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F8111E1019A951050040E7D1 /* Main.storyboard */; }; + F8111E1419A951050040E7D1 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = F8111E1319A951050040E7D1 /* Images.xcassets */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + E6C9055D19C6433500F41311 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F8111E4E19A95D7C0040E7D1 /* Armchair.xcodeproj */; + proxyType = 1; + remoteGlobalIDString = F8111E3219A95C8B0040E7D1; + remoteInfo = Armchair; + }; + E6D8B8BA19C7382B001AD043 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F8111E4E19A95D7C0040E7D1 /* Armchair.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = E6D8B8B519C7274F001AD043; + remoteInfo = Armchair; + }; + E6D8B8BC19C7382B001AD043 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F8111E4E19A95D7C0040E7D1 /* Armchair.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = E6D8B8B619C7274F001AD043; + remoteInfo = ArmchairTests; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + E6C9055419C63AAF00F41311 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + E6C49DC519C23C91006A4544 /* iOS Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "iOS Example.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + F8111E0919A951050040E7D1 /* Info-iOS.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "Info-iOS.plist"; sourceTree = ""; }; + F8111E0A19A951050040E7D1 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + F8111E0C19A951050040E7D1 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; + F8111E1119A951050040E7D1 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + F8111E1319A951050040E7D1 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; + F8111E4E19A95D7C0040E7D1 /* Armchair.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; path = Armchair.xcodeproj; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + F8111E0219A951050040E7D1 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + E6D8B8EC19C75559001AD043 /* Armchair.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + E6C9055519C6432200F41311 /* Products */ = { + isa = PBXGroup; + children = ( + E6D8B8BB19C7382B001AD043 /* Armchair.framework */, + E6D8B8BD19C7382B001AD043 /* ArmchairTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + F8111DFC19A951050040E7D1 = { + isa = PBXGroup; + children = ( + F8111E0719A951050040E7D1 /* Source */, + F8111E4E19A95D7C0040E7D1 /* Armchair.xcodeproj */, + E6C49DC519C23C91006A4544 /* iOS Example.app */, + ); + sourceTree = ""; + }; + F8111E0719A951050040E7D1 /* Source */ = { + isa = PBXGroup; + children = ( + F8111E0A19A951050040E7D1 /* AppDelegate.swift */, + F8111E0C19A951050040E7D1 /* ViewController.swift */, + F8111E1019A951050040E7D1 /* Main.storyboard */, + F8111E1319A951050040E7D1 /* Images.xcassets */, + F8111E0819A951050040E7D1 /* Supporting Files */, + ); + name = Source; + path = Example; + sourceTree = ""; + }; + F8111E0819A951050040E7D1 /* Supporting Files */ = { + isa = PBXGroup; + children = ( + F8111E0919A951050040E7D1 /* Info-iOS.plist */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + F8111E0419A951050040E7D1 /* iOS Example */ = { + isa = PBXNativeTarget; + buildConfigurationList = F8111E2319A951050040E7D1 /* Build configuration list for PBXNativeTarget "iOS Example" */; + buildPhases = ( + F8111E0119A951050040E7D1 /* Sources */, + F8111E0219A951050040E7D1 /* Frameworks */, + F8111E0319A951050040E7D1 /* Resources */, + E6C9055419C63AAF00F41311 /* Embed Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + E6C9055E19C6433500F41311 /* PBXTargetDependency */, + ); + name = "iOS Example"; + productName = Armchair; + productReference = E6C49DC519C23C91006A4544 /* iOS Example.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + F8111DFD19A951050040E7D1 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0600; + ORGANIZATIONNAME = Armchair; + TargetAttributes = { + F8111E0419A951050040E7D1 = { + CreatedOnToolsVersion = 6.0; + }; + }; + }; + buildConfigurationList = F8111E0019A951050040E7D1 /* Build configuration list for PBXProject "iOS Example" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = F8111DFC19A951050040E7D1; + productRefGroup = F8111DFC19A951050040E7D1; + projectDirPath = ""; + projectReferences = ( + { + ProductGroup = E6C9055519C6432200F41311 /* Products */; + ProjectRef = F8111E4E19A95D7C0040E7D1 /* Armchair.xcodeproj */; + }, + ); + projectRoot = ""; + targets = ( + F8111E0419A951050040E7D1 /* iOS Example */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXReferenceProxy section */ + E6D8B8BB19C7382B001AD043 /* Armchair.framework */ = { + isa = PBXReferenceProxy; + fileType = wrapper.framework; + path = Armchair.framework; + remoteRef = E6D8B8BA19C7382B001AD043 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + E6D8B8BD19C7382B001AD043 /* ArmchairTests.xctest */ = { + isa = PBXReferenceProxy; + fileType = wrapper.cfbundle; + path = ArmchairTests.xctest; + remoteRef = E6D8B8BC19C7382B001AD043 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; +/* End PBXReferenceProxy section */ + +/* Begin PBXResourcesBuildPhase section */ + F8111E0319A951050040E7D1 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F8111E1219A951050040E7D1 /* Main.storyboard in Resources */, + F8111E1419A951050040E7D1 /* Images.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + F8111E0119A951050040E7D1 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F8111E0D19A951050040E7D1 /* ViewController.swift in Sources */, + F8111E0B19A951050040E7D1 /* AppDelegate.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + E6C9055E19C6433500F41311 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = Armchair; + targetProxy = E6C9055D19C6433500F41311 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + F8111E1019A951050040E7D1 /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + F8111E1119A951050040E7D1 /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + F8111E2119A951050040E7D1 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_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 = 7.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + F8111E2219A951050040E7D1 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = YES; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + 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 = 7.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + F8111E2419A951050040E7D1 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; + CODE_SIGN_IDENTITY = "iPhone Developer"; + GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)"; + INFOPLIST_FILE = "$(SRCROOT)/Example/Info-iOS.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 7.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + OTHER_SWIFT_FLAGS = ""; + PRODUCT_NAME = "iOS Example"; + }; + name = Debug; + }; + F8111E2519A951050040E7D1 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; + CODE_SIGN_IDENTITY = "iPhone Developer"; + INFOPLIST_FILE = "$(SRCROOT)/Example/Info-iOS.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 7.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_NAME = "iOS Example"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + F8111E0019A951050040E7D1 /* Build configuration list for PBXProject "iOS Example" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F8111E2119A951050040E7D1 /* Debug */, + F8111E2219A951050040E7D1 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + F8111E2319A951050040E7D1 /* Build configuration list for PBXNativeTarget "iOS Example" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F8111E2419A951050040E7D1 /* Debug */, + F8111E2519A951050040E7D1 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = F8111DFD19A951050040E7D1 /* Project object */; +} diff --git a/iOS Example.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/iOS Example.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..bfe77a2 --- /dev/null +++ b/iOS Example.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + +