diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index b640548b..fdc8bdae 100755 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -17,6 +17,7 @@ - [Vahan Margaryan](https://github.com/VahanMargaryan) for [Pull Request #37](https://github.com/ArtSabintsev/Siren/pull/37) - [Justus Kandzi](https://github.com/jkandzi) for [Pull Request #108 (Harpy)](https://github.com/ArtSabintsev/Harpy/pull/108) - [Maxim-Inv](https://github.com/Maxim-Inv) for [Pull Request #40](https://github.com/ArtSabintsev/Siren/pull/40) +- [Dirk van Oosterbosch](https://github.com/irlabs) for [Pull Request #54](https://github.com/ArtSabintsev/Siren/pull/54) and [Pull Request #55](https://github.com/ArtSabintsev/Siren/pull/55) ### Harpy Project Contributors This repo is a Swift language port of [Harpy](http://github.com/ArtSabintsev/Harpy). We couldn't have built this port without acknowledging the following developers who were instrumental in getting Harpy to v3.2.1, the version of Harpy that Siren was based on. diff --git a/README.md b/README.md index 8c213df1..406e3378 100755 --- a/README.md +++ b/README.md @@ -3,6 +3,7 @@ ### Notify users when a new version of your app is available, and prompt them with the App Store link. --- + ## About **Siren** checks a user's currently installed version of your iOS app against the version that is currently available in the App Store. @@ -14,10 +15,18 @@ If a new version is available, an alert can be presented to the user informing t - Siren also supports four-number versioning (e.g., 1.0.0.0) - Siren is actively maintained by [**Arthur Sabintsev**](http://github.com/ArtSabintsev) and [**Aaron Brager**](http://twitter.com/getaaron) +## Important Notice (as of v0.8.0) + +### TL;DR +Siren uses your app's bundle identifier to perform a version check. The `appID` param is now used by Siren under the hood. It'll throw an error if you try to set it as it's privately scoped. + +### Long Version +As of v0.8.0, Siren is now able to check if your users are using the current version of your app using the app's bundle identifier, which is accessible through the `NSBundle` API. Instead of removing/deprecating `appID`, it is still storing your app's iTunes Connect `appID`, but it is now determined the `appID` dynamically from the results of the iTunes Lookup API call. Once it's determined, the `appID` is stored within Siren and used to launch the App Store page of your app when needed. + ## Ports -- Siren is a Swift language port of [**Harpy**](http://github.com/ArtSabintsev/Harpy), an Objective-C library that achieves the same functionality. -- Siren and Harpy are maintained by the same developers and have full parity with one another. -- This library was the inspiration for [**Egghead Games' Siren library**](https://github.com/eggheadgames/Siren), which achieves the same functionality with the the Google Play store on the Android platform. +- Siren is a Swift language port of [**Harpy**](http://github.com/ArtSabintsev/Harpy), an Objective-C library that achieves the same functionality. +- Siren and Harpy are maintained by the same developers. Siren is more feature-laden than Harpy as of v0.7.0. +- This library was the inspiration for [**Egghead Games' Siren library**](https://github.com/eggheadgames/Siren), which achieves the same functionality with the the Google Play store on the Android platform. ## Features - [x] CocoaPods Support @@ -75,11 +84,6 @@ func application(application: UIApplication, didFinishLaunchingWithOptions launc // Siren is a singleton let siren = Siren.sharedInstance - // Required: Either your app's iTunes App Store ID or the app's bundle identifier - siren.appID = <#Your_App_ID#> - // Or: - siren.bundleID = <#Your_Bundle_ID#> - // Optional: Defaults to .Option siren.alertType = <#SirenAlertType_Enum_Value#> @@ -153,16 +157,16 @@ If you would like to set a different type of alert for revision, patch, minor, a ``` ## Optional Delegate and Delegate Methods -Six delegate methods allow you to handle or track the user's behavior: +Six delegate methods allow you to handle or track the user's behavior. Each method has a default, empty implementation, effectively making each of these methods optional. ``` swift -@objc protocol SirenDelegate { - optional func sirenDidShowUpdateDialog() // User presented with update dialog - optional func sirenUserDidLaunchAppStore() // User did click on button that launched App Store.app - optional func sirenUserDidSkipVersion() // User did click on button that skips version update - optional func sirenUserDidCancel() // User did click on button that cancels update dialog - optional func sirenDidFailVersionCheck(error: NSError) // Siren failed to perform version check (may return system-level error) - optional func sirenDidDetectNewVersionWithoutAlert(message: String) // Siren performed version check and did not display alert +public protocol SirenDelegate: class { + func sirenDidShowUpdateDialog(alertType: SirenAlertType) // User presented with update dialog + func sirenUserDidLaunchAppStore() // User did click on button that launched App Store.app + func sirenUserDidSkipVersion() // User did click on button that skips version update + func sirenUserDidCancel() // User did click on button that cancels update dialog + func sirenDidFailVersionCheck(error: NSError) // Siren failed to perform version check (may return system-level error) + func sirenDidDetectNewVersionWithoutAlert(message: String) // Siren performed version check and did not display alert } ``` diff --git a/Sample App/Sample App.xcodeproj/project.pbxproj b/Sample App/Sample App.xcodeproj/project.pbxproj index 09438be9..063d73e0 100755 --- a/Sample App/Sample App.xcodeproj/project.pbxproj +++ b/Sample App/Sample App.xcodeproj/project.pbxproj @@ -419,7 +419,7 @@ INFOPLIST_FILE = "$(SRCROOT)/Sample App/Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 9.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = "com.sabintsev.$(PRODUCT_NAME:rfc1034identifier)"; + PRODUCT_BUNDLE_IDENTIFIER = com.apple.itunesconnect.mobile; PRODUCT_NAME = "Sample App"; }; name = Debug; @@ -432,7 +432,7 @@ INFOPLIST_FILE = "$(SRCROOT)/Sample App/Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 9.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = "com.sabintsev.$(PRODUCT_NAME:rfc1034identifier)"; + PRODUCT_BUNDLE_IDENTIFIER = com.apple.itunesconnect.mobile; PRODUCT_NAME = "Sample App"; }; name = Release; diff --git a/Sample App/Sample App/AppDelegate.swift b/Sample App/Sample App/AppDelegate.swift index dcd50e4d..c6da880b 100755 --- a/Sample App/Sample App/AppDelegate.swift +++ b/Sample App/Sample App/AppDelegate.swift @@ -26,10 +26,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { func setupSiren() { let siren = Siren.sharedInstance - - // Required - siren.appID = "376771144" // For this example, we're using the iTunes Connect App (https://itunes.apple.com/us/app/itunes-connect/id376771144?mt=8) - + // Optional siren.delegate = self @@ -38,7 +35,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { // Optional - Defaults to .Option // siren.alertType = .Option // or .Force, .Skip, .None - + // Optional - Can set differentiated Alerts for Major, Minor, Patch, and Revision Updates (Must be called AFTER siren.alertType, if you are using siren.alertType) siren.majorUpdateAlertType = .Option siren.minorUpdateAlertType = .Option @@ -65,8 +62,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate { extension AppDelegate: SirenDelegate { - func sirenDidShowUpdateDialog() { - print(#function) + func sirenDidShowUpdateDialog(alertType: SirenAlertType) { + print(#function, alertType) } func sirenUserDidCancel() { diff --git a/Siren.podspec b/Siren.podspec index 6a18bbac..d031602c 100755 --- a/Siren.podspec +++ b/Siren.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "Siren" - s.version = "0.7.0" + s.version = "0.8.0" s.summary = "Notify users when a new version of your iOS app is available, and prompt them with the App Store link.." s.description = <<-DESC diff --git a/Siren/Siren.swift b/Siren/Siren.swift index 2208fdf8..fcbd36dd 100644 --- a/Siren/Siren.swift +++ b/Siren/Siren.swift @@ -21,17 +21,6 @@ public protocol SirenDelegate: class { } -// Empty implementation in an extension on SirenDelegate to allow for optional implemenation of its methods -public extension SirenDelegate { - func sirenDidShowUpdateDialog(alertType: SirenAlertType) {} - func sirenUserDidLaunchAppStore() {} - func sirenUserDidSkipVersion() {} - func sirenUserDidCancel() {} - func sirenDidFailVersionCheck(error: NSError) {} - func sirenDidDetectNewVersionWithoutAlert(message: String) {} -} - - /** Determines the type of alert to present after a successful version check has been performed. @@ -113,7 +102,7 @@ private enum SirenErrorCode: Int { case AppStoreJSONParsingFailure case AppStoreVersionNumberFailure case AppStoreVersionArrayFailure - case AppStoreAppIdFailure + case AppStoreAppIDFailure } /** @@ -229,24 +218,7 @@ public final class Siren: NSObject { See the SirenAlertType enum for full details. */ public lazy var revisionUpdateAlertType = SirenAlertType.Option - - // Required Vars - /** - The App Store / iTunes Connect ID for your app. - - Either this appID or a bundleID is required. - */ - public var appID: String? - - /** - The bundle identifier of your app. - - This can be used as substitute of the appID. - Either this bundleID or an appID is required. - */ - public var bundleID: String? - // Optional Vars /** The name of your app. @@ -282,6 +254,7 @@ public final class Siren: NSObject { public private(set) var currentAppStoreVersion: String? // Private + private var appID: Int? private var lastVersionCheckPerformedOnDate: NSDate? private var updaterWindow: UIWindow? @@ -307,8 +280,8 @@ public final class Siren: NSObject { */ public func checkVersion(checkType: SirenVersionCheckType) { - if appID == nil && bundleID == nil { - printMessage("Please make sure that you have set either 'appID' or 'bundleID' before calling checkVersion.") + guard let _ = NSBundle.bundleID() else { + printMessage("Please make sure that you have set a `Bundle Identifier` in your project.") return } @@ -393,22 +366,21 @@ public final class Siren: NSObject { return } - if results.isEmpty == false { // Conditional that avoids crash when app not in App Store or appID mistyped + if results.isEmpty == false { // Conditional that avoids crash when app not in App Store + + guard let appID = results[0]["trackId"] as? Int else { + self.postError(.AppStoreAppIDFailure, underlyingError: nil) + return + } + + self.appID = appID + currentAppStoreVersion = results[0]["version"] as? String guard let _ = currentAppStoreVersion else { self.postError(.AppStoreVersionArrayFailure, underlyingError: nil) return } - // If the appID was not yet set, we need to get it from the App Store info - if appID == nil { - guard let appStoreAppID = results[0]["trackId"] as? Int else { - self.postError(.AppStoreAppIdFailure, underlyingError: nil) - return - } - appID = String(appStoreAppID) - } - if isAppStoreVersionNewer() { showAlertIfCurrentAppStoreVersionNotSkipped() } else { @@ -552,16 +524,8 @@ private extension Siren { components.scheme = "https" components.host = "itunes.apple.com" components.path = "/lookup" - - var items: [NSURLQueryItem] = [] - if let appID = appID { - items.append(NSURLQueryItem(name: "id", value: appID)) - } else { - guard let bundleID = bundleID else { - throw SirenErrorType.MissingBundleIdOrAppId - } - items.append(NSURLQueryItem(name: "bundleId", value: bundleID)) - } + + var items: [NSURLQueryItem] = [NSURLQueryItem(name: "bundleId", value: NSBundle.bundleID())] if let countryCode = countryCode { let item = NSURLQueryItem(name: "country", value: countryCode) @@ -636,7 +600,11 @@ private extension Siren { } func launchAppStore() { - let iTunesString = "https://itunes.apple.com/app/id\(appID!)" + guard let appID = appID else { + return + } + + let iTunesString = "https://itunes.apple.com/app/id\(appID)" let iTunesURL = NSURL(string: iTunesString) UIApplication.sharedApplication().openURL(iTunesURL!) } @@ -649,7 +617,6 @@ private extension Siren { } - // MARK: - UIAlertController Extensions private extension UIAlertController { @@ -669,6 +636,11 @@ private extension UIAlertController { // MARK: - NSBundle Extension private extension NSBundle { + + class func bundleID() -> String? { + return NSBundle.mainBundle().bundleIdentifier + } + func currentInstalledVersion() -> String? { return NSBundle.mainBundle().objectForInfoDictionaryKey("CFBundleShortVersionString") as? String } @@ -719,9 +691,9 @@ private extension Siren { case .AppStoreVersionNumberFailure: description = "Error retrieving App Store version number as there was no data returned." case .AppStoreVersionArrayFailure: - description = "Error retrieving App Store version number as results[0] does not contain a 'version' key." - case .AppStoreAppIdFailure: - description = "Error retrieving App ID from the App Store App as results[0] does not contain a 'trackId' key." + description = "Error retrieving App Store verson number as results[0] does not contain a 'version' key." + case .AppStoreAppIDFailure: + description = "Error retrieving trackId as results[0] does not contain a 'trackId' key." } var userInfo: [String: AnyObject] = [NSLocalizedDescriptionKey: description] @@ -738,3 +710,17 @@ private extension Siren { } } + + +// MARK: - SirenDelegate + +public extension SirenDelegate { + + func sirenDidShowUpdateDialog(alertType: SirenAlertType) {} + func sirenUserDidLaunchAppStore() {} + func sirenUserDidSkipVersion() {} + func sirenUserDidCancel() {} + func sirenDidFailVersionCheck(error: NSError) {} + func sirenDidDetectNewVersionWithoutAlert(message: String) {} + +}