diff --git a/Armchair.xcodeproj/project.pbxproj b/Armchair.xcodeproj/project.pbxproj index 75b4e3c..fb1ae98 100644 --- a/Armchair.xcodeproj/project.pbxproj +++ b/Armchair.xcodeproj/project.pbxproj @@ -318,7 +318,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0700; - LastUpgradeCheck = 0700; + LastUpgradeCheck = 0800; ORGANIZATIONNAME = Armchair; TargetAttributes = { E6A0AF6419C9CFF400C3A7DC = { @@ -326,6 +326,7 @@ }; F8111E3219A95C8B0040E7D1 = { CreatedOnToolsVersion = 6.0; + LastSwiftMigration = 0800; }; }; }; @@ -527,7 +528,7 @@ DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = "$(SRCROOT)/Source/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 7.1; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; MACOSX_DEPLOYMENT_TARGET = 10.10; PRODUCT_BUNDLE_IDENTIFIER = "com.armchair.$(PRODUCT_NAME:rfc1034identifier)"; @@ -552,7 +553,7 @@ DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = "$(SRCROOT)/Source/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 7.1; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; MACOSX_DEPLOYMENT_TARGET = 10.10; PRODUCT_BUNDLE_IDENTIFIER = "com.armchair.$(PRODUCT_NAME:rfc1034identifier)"; @@ -616,8 +617,10 @@ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; @@ -627,6 +630,7 @@ ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", @@ -665,8 +669,10 @@ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; @@ -675,6 +681,7 @@ ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; @@ -685,6 +692,7 @@ MACOSX_DEPLOYMENT_TARGET = 10.9; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; VERSIONING_SYSTEM = "apple-generic"; @@ -696,6 +704,7 @@ isa = XCBuildConfiguration; buildSettings = { CLANG_ENABLE_MODULES = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; @@ -709,6 +718,7 @@ SKIP_INSTALL = YES; SWIFT_OBJC_BRIDGING_HEADER = ""; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 3.0; }; name = Debug; }; @@ -716,6 +726,7 @@ isa = XCBuildConfiguration; buildSettings = { CLANG_ENABLE_MODULES = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; @@ -728,6 +739,7 @@ PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; SWIFT_OBJC_BRIDGING_HEADER = ""; + SWIFT_VERSION = 3.0; }; name = Release; }; diff --git a/Armchair.xcodeproj/xcshareddata/xcschemes/Armchair.xcscheme b/Armchair.xcodeproj/xcshareddata/xcschemes/Armchair.xcscheme index 74ef91f..53b90e2 100644 --- a/Armchair.xcodeproj/xcshareddata/xcschemes/Armchair.xcscheme +++ b/Armchair.xcodeproj/xcshareddata/xcschemes/Armchair.xcscheme @@ -1,6 +1,6 @@ String { return Manager.defaultManager.appName } -public func appName(appName: String) { +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 " -*/ + * 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) { +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!" -*/ + * 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) { +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" -*/ + * 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) { +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 " -*/ + * 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) { +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" -*/ + * 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?) { +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 { + * 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) { +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. -*/ + * 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) { +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() -*/ + * 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?) { +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 -*/ + * 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) { +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 -*/ + * 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) { +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 -*/ + * 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) { +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 -*/ + * 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) { +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 -*/ + * 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) { +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 -*/ + * 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) { +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. -*/ + * 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) { +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. -*/ + * 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) { +public func affiliateCode(_ affiliateCode: String) { Manager.defaultManager.affiliateCode = affiliateCode } /* -* If you are an Apple Affiliate, enter your campaign code here. -* Default => "Armchair-" -*/ + * 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) { +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 -*/ + * '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 - print("[Armchair] Debug is disabled on release builds.") - print("[Armchair] If you really want to enable debug mode,") - print("[Armchair] add \"-DDebug\" to your Swift Compiler - Custom Flags") - print("[Armchair] section in the target's build settings for release") -#endif +public func debugEnabled(_ debugEnabled: Bool) { + #if Debug + Manager.defaultManager.debugEnabled = debugEnabled + #else + print("[Armchair] Debug is disabled on release builds.") + print("[Armchair] If you really want to enable debug mode,") + print("[Armchair] add \"-DDebug\" to your Swift Compiler - Custom Flags") + print("[Armchair] section in the target's build settings for release") + #endif } /** Reset all counters manually. This resets UseCount, SignificantEventCount and FirstUseDate (daysUntilPrompt) */ public func resetUsageCounters() { - StandardUserDefaults().setObject(NSNumber(double: NSDate().timeIntervalSince1970), forKey: keyForArmchairKeyType(ArmchairKey.FirstUseDate)) - StandardUserDefaults().setObject(NSNumber(integer: 1), forKey: keyForArmchairKeyType(ArmchairKey.UseCount)) - StandardUserDefaults().setObject(NSNumber(integer: 0), forKey: keyForArmchairKeyType(ArmchairKey.SignificantEventCount)) + StandardUserDefaults().setObject(NSNumber(value: Date().timeIntervalSince1970), forKey: keyForArmchairKeyType(ArmchairKey.FirstUseDate)) + StandardUserDefaults().setObject(NSNumber(value: 1), forKey: keyForArmchairKeyType(ArmchairKey.UseCount)) + StandardUserDefaults().setObject(NSNumber(value: 0), forKey: keyForArmchairKeyType(ArmchairKey.SignificantEventCount)) StandardUserDefaults().synchronize() } @@ -319,23 +319,23 @@ public func resetAllCounters() { let currentVersionKey = keyForArmchairKeyType(ArmchairKey.CurrentVersion) let trackingVersion: String? = StandardUserDefaults().stringForKey(currentVersionKey) let bundleVersionKey = kCFBundleVersionKey as String - let currentVersion = NSBundle.mainBundle().objectForInfoDictionaryKey(bundleVersionKey) as? String + let currentVersion = Bundle.main.object(forInfoDictionaryKey: bundleVersionKey) as? String - StandardUserDefaults().setObject(trackingVersion, forKey: keyForArmchairKeyType(ArmchairKey.PreviousVersion)) + StandardUserDefaults().setObject(trackingVersion as AnyObject?, forKey: keyForArmchairKeyType(ArmchairKey.PreviousVersion)) StandardUserDefaults().setObject(StandardUserDefaults().objectForKey(keyForArmchairKeyType(ArmchairKey.RatedCurrentVersion)), forKey: keyForArmchairKeyType(ArmchairKey.PreviousVersionRated)) StandardUserDefaults().setObject(StandardUserDefaults().objectForKey(keyForArmchairKeyType(ArmchairKey.DeclinedToRate)), forKey: keyForArmchairKeyType(ArmchairKey.PreviousVersionDeclinedToRate)) - StandardUserDefaults().setObject(currentVersion, forKey: currentVersionKey) + StandardUserDefaults().setObject(currentVersion as AnyObject?, forKey: currentVersionKey) resetUsageCounters() - StandardUserDefaults().setObject(NSNumber(bool: false), forKey: keyForArmchairKeyType(ArmchairKey.RatedCurrentVersion)) - StandardUserDefaults().setObject(NSNumber(bool: false), forKey: keyForArmchairKeyType(ArmchairKey.DeclinedToRate)) - StandardUserDefaults().setObject(NSNumber(double: 0), forKey: keyForArmchairKeyType(ArmchairKey.ReminderRequestDate)) + StandardUserDefaults().setObject(NSNumber(value: false), forKey: keyForArmchairKeyType(ArmchairKey.RatedCurrentVersion)) + StandardUserDefaults().setObject(NSNumber(value: false), forKey: keyForArmchairKeyType(ArmchairKey.DeclinedToRate)) + StandardUserDefaults().setObject(NSNumber(value: 0), forKey: keyForArmchairKeyType(ArmchairKey.ReminderRequestDate)) StandardUserDefaults().synchronize() } /* -* -* -*/ + * + * + */ public func resetDefaults() { Manager.defaultManager.debugEnabled = false Manager.defaultManager.appName = Manager.defaultManager.defaultAppName() @@ -356,15 +356,15 @@ public func resetDefaults() { Manager.defaultManager.didDisplayAlertClosure = nil Manager.defaultManager.didOptToRateClosure = nil Manager.defaultManager.didOptToRemindLaterClosure = nil - -#if os(iOS) - Manager.defaultManager.usesAnimation = true - Manager.defaultManager.usesAlertController = Manager.defaultManager.defaultUsesAlertController() - Manager.defaultManager.opensInStoreKit = Manager.defaultManager.defaultOpensInStoreKit() - Manager.defaultManager.willPresentModalViewClosure = nil - Manager.defaultManager.didDismissModalViewClosure = nil -#endif - + + #if os(iOS) + Manager.defaultManager.usesAnimation = true + Manager.defaultManager.usesAlertController = Manager.defaultManager.defaultUsesAlertController() + 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() @@ -378,47 +378,47 @@ public func resetDefaults() { 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 - */ + * 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) { + 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 - */ + * 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) { + 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+ - */ + * 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) { + public func opensInStoreKit(_ opensInStoreKit: Bool) { Manager.defaultManager.opensInStoreKit = opensInStoreKit } #endif @@ -426,79 +426,79 @@ public func resetDefaults() { // 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) { + * 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 closure 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 closure 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) { + * 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 closure 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 closure 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: @escaping 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. -*/ + * 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. -*/ + * 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 closure 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 closure 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) { + * 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 closure 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 closure 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) } @@ -511,33 +511,33 @@ public func ratingConditionsHaveBeenMet() -> Bool { // MARK: Misc /* -* This is the review URL string, generated by substituting the appID, affiliate code -* and affilitate campaign code into the template URL. -*/ + * 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. -*/ + * 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. - */ + * Tells Armchair to immediately close any open rating modals + * for instance, a StoreKit rating View Controller. + */ public func closeModalPanel() { Manager.defaultManager.closeModalPanel() } @@ -545,70 +545,70 @@ public func rateApp() { // MARK: Closures /* -* Armchair uses closures instead of delegate methods for callbacks. -* Default is nil for all of them. -*/ + * Armchair uses closures 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?) { +public func onDidDisplayAlert(_ didDisplayAlertClosure: ArmchairClosure?) { Manager.defaultManager.didDisplayAlertClosure = didDisplayAlertClosure } -public func onDidDeclineToRate(didDeclineToRateClosure: ArmchairClosure?) { +public func onDidDeclineToRate(_ didDeclineToRateClosure: ArmchairClosure?) { Manager.defaultManager.didDeclineToRateClosure = didDeclineToRateClosure } -public func onDidOptToRate(didOptToRateClosure: ArmchairClosure?) { +public func onDidOptToRate(_ didOptToRateClosure: ArmchairClosure?) { Manager.defaultManager.didOptToRateClosure = didOptToRateClosure } -public func onDidOptToRemindLater(didOptToRemindLaterClosure: ArmchairClosure?) { +public func onDidOptToRemindLater(_ didOptToRemindLaterClosure: ArmchairClosure?) { Manager.defaultManager.didOptToRemindLaterClosure = didOptToRemindLaterClosure } #if os(iOS) - public func onWillPresentModalView(willPresentModalViewClosure: ArmchairAnimateClosure?) { + public func onWillPresentModalView(_ willPresentModalViewClosure: ArmchairAnimateClosure?) { Manager.defaultManager.willPresentModalViewClosure = willPresentModalViewClosure } - public func onDidDismissModalView(didDismissModalViewClosure: ArmchairAnimateClosure?) { + public func onDidDismissModalView(_ didDismissModalViewClosure: ArmchairAnimateClosure?) { Manager.defaultManager.didDismissModalViewClosure = didDismissModalViewClosure } #endif /* -* The setShouldPromptClosure 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 closure 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?) { + * The setShouldPromptClosure 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 closure 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 setShouldIncrementUseClosure, 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?) { + * The setShouldIncrementUseClosure, 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: Armchair Logger Protocol -public typealias ArmchairLogger = (Manager, log: String, file: StaticString, function: StaticString, line: UInt) -> Void +public typealias ArmchairLogger = (Manager, _ log: String, _ file: StaticString, _ function: StaticString, _ line: UInt) -> Void /* -* Set a closure to capture debug log and to plug in the desired logging framework. -*/ -public func logger(logger: ArmchairLogger) { + * Set a closure to capture debug log and to plug in the desired logging framework. + */ +public func logger(_ logger: @escaping ArmchairLogger) { Manager.defaultManager.logger = logger } @@ -616,39 +616,41 @@ public func logger(logger: ArmchairLogger) { // 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 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) + + @discardableResult func synchronize() -> Bool } -public class StandardUserDefaults: ArmchairDefaultsObject { - let defaults = NSUserDefaults.standardUserDefaults() - - @objc public func objectForKey(defaultName: String) -> AnyObject? { return defaults.objectForKey(defaultName) } - @objc public func setObject(value: AnyObject?, forKey defaultName: String) { defaults.setObject(value, forKey: defaultName) } - @objc public func removeObjectForKey(defaultName: String) { defaults.removeObjectForKey(defaultName) } - - @objc public func stringForKey(defaultName: String) -> String? { return defaults.stringForKey(defaultName) } - @objc public func integerForKey(defaultName: String) -> Int { return defaults.integerForKey(defaultName) } - @objc public func doubleForKey(defaultName: String) -> Double { return defaults.doubleForKey(defaultName) } - @objc public func boolForKey(defaultName: String) -> Bool { return defaults.boolForKey(defaultName) } - - @objc public func setInteger(value: Int, forKey defaultName: String) { defaults.setInteger(value, forKey: defaultName) } - @objc public func setDouble(value: Double, forKey defaultName: String) { defaults.setDouble(value, forKey: defaultName) } - @objc public func setBool(value: Bool, forKey defaultName: String) { defaults.setBool(value, forKey: defaultName) } - - @objc public func synchronize() -> Bool { return defaults.synchronize() } +open class StandardUserDefaults: ArmchairDefaultsObject { + let defaults = UserDefaults.standard + + @objc open func objectForKey(_ defaultName: String) -> AnyObject? { return defaults.object(forKey: defaultName) as AnyObject? } + @objc open func setObject(_ value: AnyObject?, forKey defaultName: String) { defaults.set(value, forKey: defaultName) } + @objc open func removeObjectForKey(_ defaultName: String) { defaults.removeObject(forKey: defaultName) } + + @objc open func stringForKey(_ defaultName: String) -> String? { return defaults.string(forKey: defaultName) } + @objc open func integerForKey(_ defaultName: String) -> Int { return defaults.integer(forKey: defaultName) } + @objc open func doubleForKey(_ defaultName: String) -> Double { return defaults.double(forKey: defaultName) } + @objc open func boolForKey(_ defaultName: String) -> Bool { return defaults.bool(forKey: defaultName) } + + @objc open func setInteger(_ value: Int, forKey defaultName: String) { defaults.set(value, forKey: defaultName) } + @objc open func setDouble(_ value: Double, forKey defaultName: String) { defaults.set(value, forKey: defaultName) } + @objc open func setBool(_ value: Bool, forKey defaultName: String) { defaults.set(value, forKey: defaultName) } + + @discardableResult + @objc open func synchronize() -> Bool { return defaults.synchronize() } } public enum ArmchairKey: String, CustomStringConvertible { @@ -665,9 +667,9 @@ public enum ArmchairKey: String, CustomStringConvertible { 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.rawValue @@ -675,14 +677,14 @@ public enum ArmchairKey: String, CustomStringConvertible { } } -public class ArmchairTrackingInfo: CustomStringConvertible { - public let info: Dictionary - +open class ArmchairTrackingInfo: CustomStringConvertible { + open let info: Dictionary + init(info: Dictionary) { self.info = info } - - public var description: String { + + open var description: String { get { var description = "ArmchairTrackingInfo\r" for (key, val) in info { @@ -708,97 +710,97 @@ public struct AppiraterKey { // MARK: PRIVATE Interface #if os(iOS) -public class ArmchairManager : NSObject, UIAlertViewDelegate, SKStoreProductViewControllerDelegate { } + open class ArmchairManager : NSObject, UIAlertViewDelegate, SKStoreProductViewControllerDelegate { } #elseif os(OSX) -public class ArmchairManager : NSObject, NSAlertDelegate { } + public class ArmchairManager : NSObject, NSAlertDelegate { } #else -// Untested, and currently unsupported + // Untested, and currently unsupported #endif -public class Manager : ArmchairManager { - -#if os(iOS) - private var operatingSystemVersion = NSString(string: UIDevice.currentDevice().systemVersion).doubleValue -#elseif os(OSX) +open class Manager : ArmchairManager { + + #if os(iOS) + fileprivate var operatingSystemVersion = NSString(string: UIDevice.current.systemVersion).doubleValue + #elseif os(OSX) private var operatingSystemVersion = Double(NSProcessInfo.processInfo().operatingSystemVersion.majorVersion) -#else -#endif + #else + #endif // MARK: - // MARK: Review Alert & Properties - -#if os(iOS) - private var ratingAlert: UIAlertView? = nil - 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) + + #if os(iOS) + fileprivate var ratingAlert: UIAlertView? = nil + fileprivate 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 + #else + #endif + + fileprivate lazy var appName: String = self.defaultAppName() + fileprivate func defaultAppName() -> String { + let mainBundle = Bundle.main + let displayName = mainBundle.object(forInfoDictionaryKey: "CFBundleDisplayName") as? String let bundleNameKey = kCFBundleNameKey as String - let name = mainBundle.objectForInfoDictionaryKey(bundleNameKey) as? String + let name = mainBundle.object(forInfoDictionaryKey: bundleNameKey) as? String return displayName ?? name ?? "This App" } - private lazy var reviewTitle: String = self.defaultReviewTitle() - private func defaultReviewTitle() -> String { + fileprivate lazy var reviewTitle: String = self.defaultReviewTitle() + fileprivate 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: bundle.localizedStringForKey(template, value:"", table: nil), - table: "ArmchairLocalizable") + template = bundle.localizedString(forKey: template, + value: bundle.localizedString(forKey: template, value:"", table: nil), + table: "ArmchairLocalizable") } - - return template.stringByReplacingOccurrencesOfString("%@", withString: "\(self.appName)", options: NSStringCompareOptions(rawValue: 0), range: nil) + + return template.replacingOccurrences(of: "%@", with: "\(self.appName)", options: NSString.CompareOptions(rawValue: 0), range: nil) } - - private lazy var reviewMessage: String = self.defaultReviewMessage() - private func defaultReviewMessage() -> String { + + fileprivate lazy var reviewMessage: String = self.defaultReviewMessage() + fileprivate 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: bundle.localizedStringForKey(template, value:"", table: nil), - table: "ArmchairLocalizable") + template = bundle.localizedString(forKey: template, + value: bundle.localizedString(forKey: template, value:"", table: nil), + table: "ArmchairLocalizable") } - - return template.stringByReplacingOccurrencesOfString("%@", withString: "\(self.appName)", options: NSStringCompareOptions(rawValue: 0), range: nil) + + return template.replacingOccurrences(of: "%@", with: "\(self.appName)", options: NSString.CompareOptions(rawValue: 0), range: nil) } - - private lazy var cancelButtonTitle: String = self.defaultCancelButtonTitle() - private func defaultCancelButtonTitle() -> String { + + fileprivate lazy var cancelButtonTitle: String = self.defaultCancelButtonTitle() + fileprivate 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: bundle.localizedStringForKey(title, value:"", table: nil), - table: "ArmchairLocalizable") + title = bundle.localizedString(forKey: title, + value: bundle.localizedString(forKey: title, value:"", table: nil), + table: "ArmchairLocalizable") } - + return title } - - private lazy var rateButtonTitle: String = self.defaultRateButtonTitle() - private func defaultRateButtonTitle() -> String { + + fileprivate lazy var rateButtonTitle: String = self.defaultRateButtonTitle() + fileprivate 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: bundle.localizedStringForKey(template, value:"", table: nil), - table: "ArmchairLocalizable") + template = bundle.localizedString(forKey: template, + value: bundle.localizedString(forKey: template, value:"", table: nil), + table: "ArmchairLocalizable") } - - return template.stringByReplacingOccurrencesOfString("%@", withString: "\(self.appName)", options: NSStringCompareOptions(rawValue: 0), range: nil) + + return template.replacingOccurrences(of: "%@", with: "\(self.appName)", options: NSString.CompareOptions(rawValue: 0), range: nil) } - - private lazy var remindButtonTitle: String? = self.defaultRemindButtonTitle() - private func defaultRemindButtonTitle() -> String? { + + fileprivate lazy var remindButtonTitle: String? = self.defaultRemindButtonTitle() + fileprivate func defaultRemindButtonTitle() -> String? { //if reminders are disabled, return a nil title to supress the button if self.daysBeforeReminding == 0 { return nil @@ -806,16 +808,16 @@ public class Manager : ArmchairManager { var title = "Remind me later" // Check for a localized version of the default title if let bundle = self.bundle() { - title = bundle.localizedStringForKey(title, - value: bundle.localizedStringForKey(title, value:"", table: nil), - table: "ArmchairLocalizable") + title = bundle.localizedString(forKey: title, + value: bundle.localizedString(forKey: title, value:"", table: nil), + table: "ArmchairLocalizable") } - + return title } - + // Tracking Logic / Configuration - private var appID: String = "" { + fileprivate var appID: String = "" { didSet { keyPrefix = defaultKeyPrefix() if affiliateCampaignCode == defaultAffiliateCampaignCode() { @@ -823,275 +825,277 @@ public class Manager : ArmchairManager { } } } - + // 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 { + fileprivate var daysUntilPrompt: UInt = 30 + fileprivate var usesUntilPrompt: UInt = 20 + fileprivate var significantEventsUntilPrompt: UInt = 0 + fileprivate var daysBeforeReminding: UInt = 1 + fileprivate var tracksNewVersions: Bool = true + fileprivate var shouldPromptIfRated: Bool = true + fileprivate var useMainAppBundleForLocalizations: Bool = false + fileprivate var debugEnabled: Bool = false { didSet { if self.debugEnabled { debugLog("Debug enabled for app: \(appID)") } } } - + // 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 lazy var usesAlertController: Bool = self.defaultUsesAlertController() - private lazy var opensInStoreKit: Bool = self.defaultOpensInStoreKit() - - private func defaultOpensInStoreKit() -> Bool { + fileprivate var affiliateCode: String = "11l7j9" + fileprivate var affiliateCampaignCode: String = "Armchair" + + #if os(iOS) + fileprivate var usesAnimation: Bool = true + fileprivate lazy var usesAlertController: Bool = self.defaultUsesAlertController() + fileprivate lazy var opensInStoreKit: Bool = self.defaultOpensInStoreKit() + + fileprivate func defaultOpensInStoreKit() -> Bool { return operatingSystemVersion >= 8 } - private func defaultUsesAlertController() -> Bool { + fileprivate func defaultUsesAlertController() -> Bool { return operatingSystemVersion >= 9 } -#endif - + #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 { + fileprivate lazy var armchairKeyFirstUseDate: String = self.defaultArmchairKeyFirstUseDate() + fileprivate lazy var armchairKeyUseCount: String = self.defaultArmchairKeyUseCount() + fileprivate lazy var armchairKeySignificantEventCount: String = self.defaultArmchairKeySignificantEventCount() + fileprivate lazy var armchairKeyCurrentVersion: String = self.defaultArmchairKeyCurrentVersion() + fileprivate lazy var armchairKeyRatedCurrentVersion: String = self.defaultArmchairKeyRatedCurrentVersion() + fileprivate lazy var armchairKeyDeclinedToRate: String = self.defaultArmchairKeyDeclinedToRate() + fileprivate lazy var armchairKeyReminderRequestDate: String = self.defaultArmchairKeyReminderRequestDate() + fileprivate lazy var armchairKeyPreviousVersion: String = self.defaultArmchairKeyPreviousVersion() + fileprivate lazy var armchairKeyPreviousVersionRated: String = self.defaultArmchairKeyPreviousVersionRated() + fileprivate lazy var armchairKeyPreviousVersionDeclinedToRate: String = self.defaultArmchairKeyPreviousVersionDeclinedToRate() + fileprivate lazy var armchairKeyRatedAnyVersion: String = self.defaultArmchairKeyRatedAnyVersion() + fileprivate lazy var armchairKeyAppiraterMigrationCompleted: String = self.defaultArmchairKeyAppiraterMigrationCompleted() + fileprivate lazy var armchairKeyUAAppReviewManagerMigrationCompleted: String = self.defaultArmchairKeyUAAppReviewManagerMigrationCompleted() + + fileprivate func defaultArmchairKeyFirstUseDate() -> String { return "ArmchairFirstUseDate" } + fileprivate func defaultArmchairKeyUseCount() -> String { return "ArmchairUseCount" } + fileprivate func defaultArmchairKeySignificantEventCount() -> String { return "ArmchairSignificantEventCount" } + fileprivate func defaultArmchairKeyCurrentVersion() -> String { return "ArmchairKeyCurrentVersion" } + fileprivate func defaultArmchairKeyRatedCurrentVersion() -> String { return "ArmchairRatedCurrentVersion" } + fileprivate func defaultArmchairKeyDeclinedToRate() -> String { return "ArmchairKeyDeclinedToRate" } + fileprivate func defaultArmchairKeyReminderRequestDate() -> String { return "ArmchairReminderRequestDate" } + fileprivate func defaultArmchairKeyPreviousVersion() -> String { return "ArmchairPreviousVersion" } + fileprivate func defaultArmchairKeyPreviousVersionRated() -> String { return "ArmchairPreviousVersionRated" } + fileprivate func defaultArmchairKeyPreviousVersionDeclinedToRate() -> String { return "ArmchairPreviousVersionDeclinedToRate" } + fileprivate func defaultArmchairKeyRatedAnyVersion() -> String { return "ArmchairKeyRatedAnyVersion" } + fileprivate func defaultArmchairKeyAppiraterMigrationCompleted() -> String { return "ArmchairAppiraterMigrationCompleted" } + fileprivate func defaultArmchairKeyUAAppReviewManagerMigrationCompleted() -> String { return "ArmchairUAAppReviewManagerMigrationCompleted" } + + + fileprivate lazy var keyPrefix: String = self.defaultKeyPrefix() + fileprivate func defaultKeyPrefix() -> String { if !self.appID.isEmpty { return self.appID + "_" } else { return "_" } } - - private var userDefaultsObject:ArmchairDefaultsObject? = StandardUserDefaults() - + + fileprivate var userDefaultsObject:ArmchairDefaultsObject? = StandardUserDefaults() + // MARK: Optional Closures var didDisplayAlertClosure: ArmchairClosure? var didDeclineToRateClosure: ArmchairClosure? var didOptToRateClosure: ArmchairClosure? var didOptToRemindLaterClosure: ArmchairClosure? - -#if os(iOS) + + #if os(iOS) var willPresentModalViewClosure: ArmchairAnimateClosure? var didDismissModalViewClosure: ArmchairAnimateClosure? -#endif + #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 + fileprivate var modalPanelOpen: Bool = false + #if os(iOS) + fileprivate lazy var currentStatusBarStyle: UIStatusBarStyle = { + return UIApplication.shared.statusBarStyle }() -#endif - + #endif + // MARK: - // MARK: PRIVATE Methods - - private func userDidSignificantEvent(canPromptForRating: Bool) { - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0)) { + + fileprivate func userDidSignificantEvent(_ canPromptForRating: Bool) { + DispatchQueue.global(qos: .background).async { self.incrementSignificantEventAndRate(canPromptForRating) } } - - private func userDidSignificantEvent(shouldPrompt: ArmchairShouldPromptClosure) { - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0)) { + + fileprivate func userDidSignificantEvent(_ shouldPrompt: @escaping ArmchairShouldPromptClosure) { + DispatchQueue.global(qos: .background).async { self.incrementSignificantEventAndRate(shouldPrompt) } } - + // MARK: - // MARK: PRIVATE Rating Helpers - - private func incrementAndRate(canPromptForRating: Bool) { + + fileprivate func incrementAndRate(_ canPromptForRating: Bool) { migrateKeysIfNecessary() incrementUseCount() showPrompt(ifNecessary: canPromptForRating) } - - private func incrementAndRate(shouldPrompt: ArmchairShouldPromptClosure) { + + fileprivate func incrementAndRate(_ shouldPrompt: ArmchairShouldPromptClosure) { migrateKeysIfNecessary() incrementUseCount() showPrompt(shouldPrompt) } - - private func incrementSignificantEventAndRate(canPromptForRating: Bool) { + + fileprivate func incrementSignificantEventAndRate(_ canPromptForRating: Bool) { migrateKeysIfNecessary() incrementSignificantEventCount() showPrompt(ifNecessary: canPromptForRating) } - - private func incrementSignificantEventAndRate(shouldPrompt: ArmchairShouldPromptClosure) { + + fileprivate func incrementSignificantEventAndRate(_ shouldPrompt: ArmchairShouldPromptClosure) { migrateKeysIfNecessary() incrementSignificantEventCount() showPrompt(shouldPrompt) } - - private func incrementUseCount() { + + fileprivate func incrementUseCount() { var shouldIncrement = true if let closure = shouldIncrementUseCountClosure { shouldIncrement = closure() } - + if shouldIncrement { _incrementCountForKeyType(ArmchairKey.UseCount) } } - - private func incrementSignificantEventCount() { + + fileprivate func incrementSignificantEventCount() { _incrementCountForKeyType(ArmchairKey.SignificantEventCount) } - - private func _incrementCountForKeyType(incrementKeyType: ArmchairKey) { + + fileprivate func _incrementCountForKeyType(_ incrementKeyType: ArmchairKey) { let incrementKey = keyForArmchairKeyType(incrementKeyType) - + let bundleVersionKey = kCFBundleVersionKey as String // App's version. Not settable as the other ivars because that would be crazy. - let currentVersion = NSBundle.mainBundle().objectForInfoDictionaryKey(bundleVersionKey) as? String + let currentVersion = Bundle.main.object(forInfoDictionaryKey: bundleVersionKey) as? String if currentVersion == nil { assertionFailure("Could not read kCFBundleVersionKey from InfoDictionary") return } - + // Get the version number that we've been tracking thus far let currentVersionKey = keyForArmchairKeyType(ArmchairKey.CurrentVersion) var trackingVersion: String? = userDefaultsObject?.stringForKey(currentVersionKey) // New install, or changed keys if trackingVersion == nil { trackingVersion = currentVersion - userDefaultsObject?.setObject(currentVersion, forKey: currentVersionKey) + userDefaultsObject?.setObject(currentVersion as AnyObject?, forKey: currentVersionKey) } - + debugLog("Tracking version: \(trackingVersion!)") - + if trackingVersion == currentVersion { // Check if the first use date has been set. if not, set it. let firstUseDateKey = keyForArmchairKeyType(ArmchairKey.FirstUseDate) var timeInterval: Double? = userDefaultsObject?.doubleForKey(firstUseDateKey) if 0 == timeInterval { - timeInterval = NSDate().timeIntervalSince1970 - userDefaultsObject?.setObject(NSNumber(double: timeInterval!), forKey: firstUseDateKey) + timeInterval = Date().timeIntervalSince1970 + userDefaultsObject?.setObject(NSNumber(value: timeInterval!), forKey: firstUseDateKey) } - + // Increment the key's count - var incrementKeyCount = userDefaultsObject?.integerForKey(incrementKey) - userDefaultsObject?.setInteger(++incrementKeyCount!, forKey:incrementKey) - - debugLog("Incremented \(incrementKeyType): \(incrementKeyCount!)") - + var incrementKeyCount = userDefaultsObject!.integerForKey(incrementKey) + incrementKeyCount += 1 + + userDefaultsObject?.setInteger(incrementKeyCount, forKey:incrementKey) + + debugLog("Incremented \(incrementKeyType): \(incrementKeyCount)") + } else if tracksNewVersions { // it's a new version of the app, so restart tracking resetAllCounters() debugLog("Reset Tracking Version to: \(trackingVersion!)") } - + userDefaultsObject?.synchronize() } - - private func showPrompt(ifNecessary canPromptForRating: Bool) { + + fileprivate func showPrompt(ifNecessary canPromptForRating: Bool) { if canPromptForRating && connectedToNetwork() && ratingConditionsHaveBeenMet() { var shouldPrompt: Bool = true if let closure = shouldPromptClosure { - if NSThread.isMainThread() { + if Thread.isMainThread { shouldPrompt = closure(trackingInfo()) } else { - dispatch_sync(dispatch_get_main_queue()) { + DispatchQueue.main.sync { shouldPrompt = closure(self.trackingInfo()) } } } - + if shouldPrompt { - dispatch_async(dispatch_get_main_queue()) { + DispatchQueue.main.async { self.showRatingAlert() } } } } - - private func showPrompt(shouldPrompt: ArmchairShouldPromptClosure) { + + fileprivate func showPrompt(_ shouldPrompt: ArmchairShouldPromptClosure) { var shouldPromptVal = false - - if NSThread.isMainThread() { + + if Thread.isMainThread { shouldPromptVal = shouldPrompt(trackingInfo()) } else { - dispatch_sync(dispatch_get_main_queue()) { + DispatchQueue.main.sync { shouldPromptVal = shouldPrompt(self.trackingInfo()) } } - - + + if (shouldPromptVal) { - dispatch_async(dispatch_get_main_queue()) { + DispatchQueue.main.async { self.showRatingAlert() } } } - - private func showPrompt() { + + fileprivate func showPrompt() { if !appID.isEmpty && connectedToNetwork() && !userHasDeclinedToRate() && !userHasRatedCurrentVersion() { showRatingAlert() } } - - private func ratingConditionsHaveBeenMet() -> Bool { + + fileprivate func ratingConditionsHaveBeenMet() -> Bool { if debugEnabled { return true } - + if appID.isEmpty { return false } - + // check if the app has been used long enough let timeIntervalOfFirstLaunch = userDefaultsObject?.doubleForKey(keyForArmchairKeyType(ArmchairKey.FirstUseDate)) if let timeInterval = timeIntervalOfFirstLaunch { - let dateOfFirstLaunch = NSDate(timeIntervalSince1970: timeInterval) - let timeSinceFirstLaunch = NSDate().timeIntervalSinceDate(dateOfFirstLaunch) - let timeUntilRate: NSTimeInterval = 60 * 60 * 24 * Double(daysUntilPrompt) + let dateOfFirstLaunch = Date(timeIntervalSince1970: timeInterval) + let timeSinceFirstLaunch = Date().timeIntervalSince(dateOfFirstLaunch) + let timeUntilRate: TimeInterval = 60 * 60 * 24 * Double(daysUntilPrompt) if timeSinceFirstLaunch < timeUntilRate { return false } } else { return false } - + // check if the app has been used enough times let useCount = userDefaultsObject?.integerForKey(keyForArmchairKeyType(ArmchairKey.UseCount)) if let count = useCount { @@ -1101,7 +1105,7 @@ public class Manager : ArmchairManager { } else { return false } - + // check if the user has done enough significant events let significantEventCount = userDefaultsObject?.integerForKey(keyForArmchairKeyType(ArmchairKey.SignificantEventCount)) if let count = significantEventCount { @@ -1111,30 +1115,30 @@ public class Manager : ArmchairManager { } 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? let timeIntervalOfReminder = userDefaultsObject?.doubleForKey(keyForArmchairKeyType(ArmchairKey.ReminderRequestDate)) if let timeInterval = timeIntervalOfReminder { - let reminderRequestDate = NSDate(timeIntervalSince1970: timeInterval) - let timeSinceReminderRequest = NSDate().timeIntervalSinceDate(reminderRequestDate) - let timeUntilReminder: NSTimeInterval = 60 * 60 * 24 * Double(daysBeforeReminding) + let reminderRequestDate = Date(timeIntervalSince1970: timeInterval) + let timeSinceReminderRequest = Date().timeIntervalSince(reminderRequestDate) + let timeUntilReminder: TimeInterval = 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 let ratedAnyVersion = userDefaultsObject?.boolForKey(keyForArmchairKeyType(ArmchairKey.RatedAnyVersion)) if let ratedAlready = ratedAnyVersion { @@ -1142,113 +1146,113 @@ public class Manager : ArmchairManager { return false } } - + return true } - - private func userHasDeclinedToRate() -> Bool { + + fileprivate func userHasDeclinedToRate() -> Bool { if let declined = userDefaultsObject?.boolForKey(keyForArmchairKeyType(ArmchairKey.DeclinedToRate)) { return declined } else { return false } } - - private func userHasRatedCurrentVersion() -> Bool { + + fileprivate func userHasRatedCurrentVersion() -> Bool { if let ratedCurrentVersion = userDefaultsObject?.boolForKey(keyForArmchairKeyType(ArmchairKey.RatedCurrentVersion)) { return ratedCurrentVersion } else { return false } } - - private func showsRemindButton() -> Bool { + + fileprivate func showsRemindButton() -> Bool { return (daysBeforeReminding > 0 && remindButtonTitle != nil) } - - private func showRatingAlert() { -#if os(iOS) - if (operatingSystemVersion >= 8 && usesAlertController) || operatingSystemVersion >= 9 { - /* iOS 8 uses new UIAlertController API*/ - let 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: { + + fileprivate func showRatingAlert() { + #if os(iOS) + if (operatingSystemVersion >= 8 && usesAlertController) || operatingSystemVersion >= 9 { + /* iOS 8 uses new UIAlertController API*/ + let alertView : UIAlertController = UIAlertController(title: reviewTitle, message: reviewMessage, preferredStyle: UIAlertControllerStyle.alert) + alertView.addAction(UIAlertAction(title: cancelButtonTitle, style:UIAlertActionStyle.cancel, handler: { (alert: UIAlertAction!) in - self.remindMeLater() + self.dontRate() })) - } - 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) { - print("presentViewController() completed") + 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.shared.keyWindow?.rootViewController { + if let topController = topMostViewController(presentingController) { + topController.present(alertView, animated: usesAnimation) { + print("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) + /* 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() + } } - // 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 { + + #elseif os(OSX) + + let alert: NSAlert = NSAlert() + alert.messageText = reviewTitle + alert.informativeText = reviewMessage + alert.addButtonWithTitle(rateButtonTitle) + if showsRemindButton() { + alert.addButtonWithTitle(remindButtonTitle!) + } + alert.addButtonWithTitle(cancelButtonTitle) + ratingAlert = alert + + if let window = NSApplication.sharedApplication().keyWindow { + alert.beginSheetModalForWindow(window) { + (response: NSModalResponse) in + self.handleNSAlertReturnCode(response) + } + } else { + let returnCode = alert.runModal() + handleNSAlertReturnCode(returnCode) + } + + if let closure = self.didDisplayAlertClosure { closure() } - } - -#elseif os(OSX) - - let alert: NSAlert = NSAlert() - alert.messageText = reviewTitle - alert.informativeText = reviewMessage - alert.addButtonWithTitle(rateButtonTitle) - if showsRemindButton() { - alert.addButtonWithTitle(remindButtonTitle!) - } - alert.addButtonWithTitle(cancelButtonTitle) - ratingAlert = alert - - if let window = NSApplication.sharedApplication().keyWindow { - alert.beginSheetModalForWindow(window) { - (response: NSModalResponse) in - self.handleNSAlertReturnCode(response) - } - } else { - let returnCode = alert.runModal() - handleNSAlertReturnCode(returnCode) - } - - if let closure = self.didDisplayAlertClosure { - closure() - } -#else -#endif + #else + #endif } - + // MARK: - // MARK: PRIVATE Alert View / StoreKit Delegate Methods - -#if os(iOS) - public func alertView(alertView: UIAlertView, didDismissWithButtonIndex buttonIndex: Int) { + + #if os(iOS) + open 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 @@ -1261,58 +1265,58 @@ public class Manager : ArmchairManager { _rateApp() } } - + //Delegate call from the StoreKit view. - public func productViewControllerDidFinish(viewController: SKStoreProductViewController!) { + open func productViewControllerDidFinish(_ viewController: SKStoreProductViewController!) { closeModalPanel() } - + //Close the in-app rating (StoreKit) view and restore the previous status bar style. - private func closeModalPanel() { + fileprivate func closeModalPanel() { if modalPanelOpen { - UIApplication.sharedApplication().setStatusBarStyle(currentStatusBarStyle, animated:usesAnimation) + UIApplication.shared.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 presentingController = UIApplication.shared.keyWindow?.rootViewController { if let topController = topMostViewController(presentingController) { - topController.dismissViewControllerAnimated(usesAnimation) { + topController.dismiss(animated: usesAnimation) { if let closure = self.didDismissModalViewClosure { closure(usedAnimation) } } - currentStatusBarStyle = UIStatusBarStyle.Default + currentStatusBarStyle = UIStatusBarStyle.default } } } } - -#elseif os(OSX) - + + #elseif os(OSX) + private func handleNSAlertReturnCode(returnCode: NSInteger) { - switch (returnCode) { - case NSAlertFirstButtonReturn: - // they want to rate it - _rateApp() - case NSAlertSecondButtonReturn: - // remind them later or cancel - if showsRemindButton() { - remindMeLater() - } else { - dontRate() - } - case NSAlertThirdButtonReturn: - // they don't want to rate it - dontRate() - default: - return - } + switch (returnCode) { + case NSAlertFirstButtonReturn: + // they want to rate it + _rateApp() + case NSAlertSecondButtonReturn: + // remind them later or cancel + if showsRemindButton() { + remindMeLater() + } else { + dontRate() } - -#else -#endif - + case NSAlertThirdButtonReturn: + // they don't want to rate it + dontRate() + default: + return + } + } + + #else + #endif + private func dontRate() { userDefaultsObject?.setBool(true, forKey: keyForArmchairKeyType(ArmchairKey.DeclinedToRate)) userDefaultsObject?.synchronize() @@ -1320,98 +1324,98 @@ public class Manager : ArmchairManager { closure() } } - + private func remindMeLater() { - userDefaultsObject?.setDouble(NSDate().timeIntervalSince1970, forKey: keyForArmchairKeyType(ArmchairKey.ReminderRequestDate)) + userDefaultsObject?.setDouble(Date().timeIntervalSince1970, forKey: keyForArmchairKeyType(ArmchairKey.ReminderRequestDate)) userDefaultsObject?.synchronize() if let closure = didOptToRemindLaterClosure { closure() } } - + private func _rateApp() { rateApp() if let closure = didOptToRateClosure { closure() } } - - private func rateApp() { - + + fileprivate 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 7+) and imported. This works in the simulator. - if opensInStoreKit { - - let storeViewController = SKStoreProductViewController() - - var productParameters: [String:AnyObject]! = [SKStoreProductParameterITunesItemIdentifier : appID] - - if (operatingSystemVersion >= 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) + + #if os(iOS) + // Use the in-app StoreKit view if set, available (iOS 7+) and imported. This works in the simulator. + if opensInStoreKit { + + let storeViewController = SKStoreProductViewController() + + var productParameters: [String:AnyObject]! = [SKStoreProductParameterITunesItemIdentifier : appID as AnyObject] + + if (operatingSystemVersion >= 8) { + productParameters[SKStoreProductParameterAffiliateToken] = affiliateCode as AnyObject? + productParameters[SKStoreProductParameterCampaignToken] = affiliateCampaignCode as AnyObject? + } + + storeViewController.loadProduct(withParameters: productParameters, completionBlock: nil) + storeViewController.delegate = self + + if let closure = willPresentModalViewClosure { + closure(usesAnimation) + } + + + if let rootController = getRootViewController() { + rootController.present(storeViewController, animated: usesAnimation) { + self.modalPanelOpen = true + + //Temporarily use a status bar to match the StoreKit view. + self.currentStatusBarStyle = UIApplication.shared.statusBarStyle + UIApplication.shared.setStatusBarStyle(UIStatusBarStyle.default, animated: self.usesAnimation) + } + } + + //Use the standard openUrl method + } else { + if let url = URL(string: reviewURLString()) { + UIApplication.shared.openURL(url) } } - - //Use the standard openUrl method - } else { + + if UIDevice.current.model.range(of: "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().replacingOccurrences(of: "itms-apps", with:"http") + debugLog(" - Or try copy/pasting \(fakeURL) into a browser on your computer.") + } + #elseif os(OSX) if let url = NSURL(string: reviewURLString()) { - UIApplication.sharedApplication().openURL(url) + let opened = NSWorkspace.sharedWorkspace().openURL(url) + if !opened { + debugLog("Failed to open \(url)") + } } - } - - 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) - if let url = NSURL(string: reviewURLString()) { - let opened = NSWorkspace.sharedWorkspace().openURL(url) - if !opened { - debugLog("Failed to open \(url)") - } - } -#else -#endif + #else + #endif } - - private func reviewURLString() -> String { + + fileprivate func reviewURLString() -> String { let 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)") + var reviewURL = template.replacingOccurrences(of: "APP_ID", with: "\(appID)") + reviewURL = reviewURL.replacingOccurrences(of: "AFFILIATE_CODE", with: "\(affiliateCode)") + reviewURL = reviewURL.replacingOccurrences(of: "AFFILIATE_CAMPAIGN_CODE", with: "\(affiliateCampaignCode)") return reviewURL } - + // Mark: - // Mark: PRIVATE Key Helpers - + private func trackingInfo() -> ArmchairTrackingInfo { var trackingInfo: Dictionary = [:] - + for keyType in ArmchairKey.allValues { let obj: AnyObject? = userDefaultsObject?.objectForKey(keyForArmchairKeyType(keyType)) if let val = obj as? NSObject { @@ -1420,11 +1424,11 @@ public class Manager : ArmchairManager { trackingInfo[keyType] = NSNull() } } - + return ArmchairTrackingInfo(info: trackingInfo) } - - private func keyForArmchairKeyType(keyType: ArmchairKey) -> String { + + fileprivate func keyForArmchairKeyType(_ keyType: ArmchairKey) -> String { switch (keyType) { case .FirstUseDate: return keyPrefix + armchairKeyFirstUseDate @@ -1454,8 +1458,8 @@ public class Manager : ArmchairManager { return keyPrefix + armchairKeyUAAppReviewManagerMigrationCompleted } } - - private func setKey(key: NSString, armchairKeyType: ArmchairKey) { + + fileprivate func setKey(_ key: NSString, armchairKeyType: ArmchairKey) { switch armchairKeyType { case .FirstUseDate: armchairKeyFirstUseDate = key as String @@ -1485,8 +1489,8 @@ public class Manager : ArmchairManager { armchairKeyUAAppReviewManagerMigrationCompleted = key as String } } - - private func armchairKeyForAppiraterKey(appiraterKey: String) -> String { + + private func armchairKeyForAppiraterKey(_ appiraterKey: String) -> String { switch appiraterKey { case AppiraterKey.FirstUseDate: return keyForArmchairKeyType(ArmchairKey.FirstUseDate) @@ -1508,18 +1512,18 @@ public class Manager : ArmchairManager { return "" } } - + private func migrateAppiraterKeysIfNecessary() { - - let appiraterAlreadyCompletedKey: NSString = keyForArmchairKeyType(.AppiraterMigrationCompleted) + + let appiraterAlreadyCompletedKey: NSString = keyForArmchairKeyType(.AppiraterMigrationCompleted) as NSString let appiraterMigrationAlreadyCompleted = userDefaultsObject?.boolForKey(appiraterAlreadyCompletedKey as String) - + if let completed = appiraterMigrationAlreadyCompleted { if completed { return } } - + let oldKeys: [String] = [AppiraterKey.FirstUseDate, AppiraterKey.UseCount, AppiraterKey.SignificantEventCount, @@ -1536,34 +1540,34 @@ public class Manager : ArmchairManager { userDefaultsObject?.removeObjectForKey(oldKey) } } - - userDefaultsObject?.setObject(NSNumber(bool: true), forKey: appiraterAlreadyCompletedKey as String) + + userDefaultsObject?.setObject(NSNumber(value: true), forKey: appiraterAlreadyCompletedKey as String) 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) + let appReviewManagerAlreadyCompletedKey: NSString = keyForArmchairKeyType(.UAAppReviewManagerMigrationCompleted) as NSString let appReviewManagerMigrationAlreadyCompleted = userDefaultsObject?.boolForKey(appReviewManagerAlreadyCompletedKey as String) - + if let completed = appReviewManagerMigrationAlreadyCompleted { if completed { return } } - + // By default, UAAppReviewManager keys are in the format _UAAppReviewManagerKey let 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] + "\(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 { let oldValue: NSObject? = userDefaultsObject?.objectForKey(oldKey) as? NSObject if let val = oldValue { @@ -1571,62 +1575,64 @@ public class Manager : ArmchairManager { userDefaultsObject?.removeObjectForKey(oldKey) } } - - userDefaultsObject?.setObject(NSNumber(bool: true), forKey: appReviewManagerAlreadyCompletedKey as String) + + userDefaultsObject?.setObject(NSNumber(value: true), forKey: appReviewManagerAlreadyCompletedKey as String) userDefaultsObject?.synchronize() } - + private func migrateKeysIfNecessary() { migrateAppiraterKeysIfNecessary() migrateUAAppReviewManagerKeysIfNecessary() } - + // MARK: - // MARK: Internet Connectivity - + private func connectedToNetwork() -> Bool { var zeroAddress = sockaddr_in() - zeroAddress.sin_len = UInt8(sizeofValue(zeroAddress)) + zeroAddress.sin_len = UInt8(MemoryLayout.size(ofValue: zeroAddress)) zeroAddress.sin_family = sa_family_t(AF_INET) - - guard let defaultRouteReachability = withUnsafePointer(&zeroAddress, { - SCNetworkReachabilityCreateWithAddress(nil, UnsafePointer($0)) + + guard let defaultRouteReachability = withUnsafePointer(to: &zeroAddress, { + $0.withMemoryRebound(to: sockaddr.self, capacity: 1) { + SCNetworkReachabilityCreateWithAddress(nil, $0) + } }) else { return false } - + var flags : SCNetworkReachabilityFlags = [] if !SCNetworkReachabilityGetFlags(defaultRouteReachability, &flags) { return false } - - let isReachable = flags.contains(.Reachable) - let needsConnection = flags.contains(.ConnectionRequired) + + let isReachable = flags.contains(.reachable) + let needsConnection = flags.contains(.connectionRequired) return (isReachable && !needsConnection) } - + // MARK: - // MARK: PRIVATE Misc Helpers - - private func bundle() -> NSBundle? { - var bundle: NSBundle? = nil + + private func bundle() -> Bundle? { + var bundle: Bundle? = nil if useMainAppBundleForLocalizations { - bundle = NSBundle.mainBundle() + bundle = Bundle.main } else { - let armchairBundleURL: NSURL? = NSBundle.mainBundle().URLForResource("Armchair", withExtension: "bundle") + let armchairBundleURL: URL? = Bundle.main.url(forResource: "Armchair", withExtension: "bundle") if let url = armchairBundleURL { - bundle = NSBundle(URL: url) + bundle = Bundle(url: url) } else { - bundle = NSBundle(forClass: self.dynamicType) + bundle = Bundle(for: type(of: self)) } } - + return bundle } - -#if os(iOS) - private func topMostViewController(controller: UIViewController?) -> UIViewController? { + + #if os(iOS) + private func topMostViewController(_ controller: UIViewController?) -> UIViewController? { var isPresenting: Bool = false var topController: UIViewController? = controller repeat { @@ -1640,15 +1646,15 @@ public class Manager : ArmchairManager { } } } while isPresenting - + return topController } - + private func getRootViewController() -> UIViewController? { - if var window = UIApplication.sharedApplication().keyWindow { - + if var window = UIApplication.shared.keyWindow { + if window.windowLevel != UIWindowLevelNormal { - let windows: NSArray = UIApplication.sharedApplication().windows + let windows: NSArray = UIApplication.shared.windows as NSArray for candidateWindow in windows { if let candidateWindow = candidateWindow as? UIWindow { if candidateWindow.windowLevel == UIWindowLevelNormal { @@ -1658,73 +1664,73 @@ public class Manager : ArmchairManager { } } } - + for subView in window.subviews { - if let responder = subView.nextResponder() { - if responder.isKindOfClass(UIViewController) { + if let responder = subView.next { + if responder.isKind(of: UIViewController.self) { return topMostViewController(responder as? UIViewController) } - + } } } - + return nil } -#endif - + #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) - if let window = NSApplication.sharedApplication().keyWindow { - if let parent = window.sheetParent { - parent.endSheet(window) + #if os(iOS) + if alert.isVisible { + alert.dismiss(withClickedButtonIndex: alert.cancelButtonIndex, animated: false) } - } - - #else -#endif + #elseif os(OSX) + if let window = NSApplication.sharedApplication().keyWindow { + if let parent = window.sheetParent { + parent.endSheet(window) + } + } + + #else + #endif ratingAlert = nil } } - - private func defaultAffiliateCode() -> String { + + fileprivate func defaultAffiliateCode() -> String { return "11l7j9" } - - private func defaultAffiliateCampaignCode() -> String { + + fileprivate func defaultAffiliateCampaignCode() -> String { return "Armchair" } - + // MARK: - // MARK: Notification Handlers - - public func appWillResignActive(notification: NSNotification) { + + public func appWillResignActive(_ notification: Notification) { debugLog("appWillResignActive:") hideRatingAlert() } - - public func applicationDidFinishLaunching(notification: NSNotification) { - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0)) { + + public func applicationDidFinishLaunching(_ notification: Notification) { + DispatchQueue.global(qos: .background).async { self.debugLog("applicationDidFinishLaunching:") self.migrateKeysIfNecessary() self.incrementUseCount() } } - - public func applicationWillEnterForeground(notification: NSNotification) { - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0)) { + + public func applicationWillEnterForeground(_ notification: Notification) { + DispatchQueue.global(qos: .background).async { self.debugLog("applicationWillEnterForeground:") self.migrateKeysIfNecessary() self.incrementUseCount() } } - + // MARK: - // MARK: Singleton public class var defaultManager: Manager { @@ -1734,49 +1740,49 @@ public class Manager : ArmchairManager { } 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 + + fileprivate func setupNotifications() { + #if os(iOS) + NotificationCenter.default.addObserver(self, selector: #selector(Manager.appWillResignActive(_:)), name: NSNotification.Name.UIApplicationWillResignActive, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(UIApplicationDelegate.applicationDidFinishLaunching(_:)), name: NSNotification.Name.UIApplicationDidFinishLaunching, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(UIApplicationDelegate.applicationWillEnterForeground(_:)), name: NSNotification.Name.UIApplicationWillEnterForeground, 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 { + override open var debugDescription: String { get { return "Armchair: appID=\(Armchair.appID)" } } - + // MARK: - // MARK: Debug - - let lockQueue = dispatch_queue_create("com.armchair.lockqueue", nil) - + + let lockQueue = DispatchQueue(label: "com.armchair.lockqueue") + public var logger: ArmchairLogger = { manager, log, file, function, line in if manager.debugEnabled { - dispatch_sync(manager.lockQueue, { + manager.lockQueue.sync(execute: { print("[Armchair] \(log)") }) } } - private func debugLog(log: String, file: StaticString = __FILE__, function: StaticString = __FUNCTION__, line: UInt = __LINE__) { - logger(self, log: log, file: file, function: function, line: line) + fileprivate func debugLog(_ log: String, file: StaticString = #file, function: StaticString = #function, line: UInt = #line) { + logger(self, log, file, function, line) } - + }