From fdc70034b3b4f49f58005e515756c1733cecee39 Mon Sep 17 00:00:00 2001 From: Dirk van Oosterbosch Date: Tue, 19 Apr 2016 23:39:45 +0200 Subject: [PATCH 1/5] Add specialized error code for when the version check is postponed, because it already recently performed a check. Also fixed a minor typo (verson instead of version) --- Siren/Siren.swift | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) mode change 100755 => 100644 Siren/Siren.swift diff --git a/Siren/Siren.swift b/Siren/Siren.swift old mode 100755 new mode 100644 index e5451f44..aee35178 --- a/Siren/Siren.swift +++ b/Siren/Siren.swift @@ -96,6 +96,7 @@ public enum SirenLanguageType: String { */ private enum SirenErrorCode: Int { case MalformedURL = 1000 + case RecentlyCheckedAlready case NoUpdateAvailable case AppStoreDataRetrievalFailure case AppStoreJSONParsingFailure @@ -298,7 +299,7 @@ public final class Siren: NSObject { if daysSinceLastVersionCheckDate(lastVersionCheckPerformedOnDate) >= checkType.rawValue { performVersionCheck() } else { - postError(.NoUpdateAvailable, underlyingError: nil) + postError(.RecentlyCheckedAlready, underlyingError: nil) } } } @@ -666,6 +667,8 @@ private extension Siren { switch code { case .MalformedURL: description = "The iTunes URL is malformed. Please leave an issue on http://github.com/ArtSabintsev/Siren with as many details as possible." + case .RecentlyCheckedAlready: + description = "Not checking the version, because it already checked recently." case .NoUpdateAvailable: description = "No new update available." case .AppStoreDataRetrievalFailure: @@ -673,9 +676,9 @@ private extension Siren { case .AppStoreJSONParsingFailure: description = "Error parsing App Store JSON data." case .AppStoreVersionNumberFailure: - description = "Error retrieving App Store verson number as there was no data returned." + description = "Error retrieving App Store version number as there was no data returned." case .AppStoreVersionArrayFailure: - description = "Error retrieving App Store verson number as results[0] does not contain a 'version' key." + description = "Error retrieving App Store version number as results[0] does not contain a 'version' key." } var userInfo: [String: AnyObject] = [NSLocalizedDescriptionKey: description] From 1ade5d1604c0fdf160583973ca2180aa0df462b7 Mon Sep 17 00:00:00 2001 From: Dirk van Oosterbosch Date: Tue, 19 Apr 2016 23:52:04 +0200 Subject: [PATCH 2/5] Require either appID or bundleID before checking the version. Also bundleID (next to, or instead of appID) can now be used to get the info from the App Store. If the bundleID is given and an appID is not supplied, the appID will be read from the App Store json, since it is later needed when attempting to open the App Store link after "Upgrade" --- Siren/Siren.swift | 44 ++++++++++++++++++++++++++++++++++++++------ 1 file changed, 38 insertions(+), 6 deletions(-) diff --git a/Siren/Siren.swift b/Siren/Siren.swift index aee35178..40fc0ac4 100644 --- a/Siren/Siren.swift +++ b/Siren/Siren.swift @@ -102,6 +102,7 @@ private enum SirenErrorCode: Int { case AppStoreJSONParsingFailure case AppStoreVersionNumberFailure case AppStoreVersionArrayFailure + case AppStoreAppIdFailure } /** @@ -109,6 +110,7 @@ private enum SirenErrorCode: Int { */ private enum SirenErrorType: ErrorType { case MalformedURL + case MissingBundleIdOrAppId } /** @@ -220,9 +222,20 @@ public final class Siren: NSObject { // 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. @@ -283,8 +296,8 @@ public final class Siren: NSObject { */ public func checkVersion(checkType: SirenVersionCheckType) { - guard let _ = appID else { - printMessage("Please make sure that you have set 'appID' before calling checkVersion.") + if appID == nil && bundleID == nil { + printMessage("Please make sure that you have set either 'appID' or 'bundleID' before calling checkVersion.") return } @@ -353,8 +366,8 @@ public final class Siren: NSObject { task.resume() - } catch _ { - postError(.MalformedURL, underlyingError: nil) + } catch let error as NSError { + postError(.MalformedURL, underlyingError: error) } } @@ -376,6 +389,15 @@ public final class Siren: NSObject { 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 { @@ -519,8 +541,16 @@ private extension Siren { components.scheme = "https" components.host = "itunes.apple.com" components.path = "/lookup" - - var items: [NSURLQueryItem] = [NSURLQueryItem(name: "id", value: appID)] + + 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)) + } if let countryCode = countryCode { let item = NSURLQueryItem(name: "country", value: countryCode) @@ -679,6 +709,8 @@ private extension Siren { 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." } var userInfo: [String: AnyObject] = [NSLocalizedDescriptionKey: description] From cfd099d7b924e8857e7863072561a8d8b0053880 Mon Sep 17 00:00:00 2001 From: Dirk van Oosterbosch Date: Fri, 22 Apr 2016 15:49:33 +0200 Subject: [PATCH 3/5] Update the README to reflect the optional / required bundleID --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 5fcef456..8c213df1 100755 --- a/README.md +++ b/README.md @@ -75,8 +75,10 @@ func application(application: UIApplication, didFinishLaunchingWithOptions launc // Siren is a singleton let siren = Siren.sharedInstance - // Required: Your app's iTunes App Store ID + // 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#> From 8f5bd92e230440ebf635e2cdfc31317f1bcc5dae Mon Sep 17 00:00:00 2001 From: Dirk van Oosterbosch Date: Fri, 22 Apr 2016 15:34:38 +0200 Subject: [PATCH 4/5] Add `alertType` parameter to `sirenDidShowUpdateDialog` delegate method. And converted the `SirenDelegate` protocol from a @objc protocol to a swift protocol, to allow for enums to be sent to the delegate. Also added empty implementations of the protocol methods in a protocol extension, because it's not possible to use the `optional` keyword with non-@objc protocols. --- Siren/Siren.swift | 39 +++++++++++++++++++++++++-------------- 1 file changed, 25 insertions(+), 14 deletions(-) diff --git a/Siren/Siren.swift b/Siren/Siren.swift index e5451f44..85648511 100755 --- a/Siren/Siren.swift +++ b/Siren/Siren.swift @@ -11,13 +11,24 @@ import UIKit // MARK: - SirenDelegate Protocol -@objc public 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 +} + + +// Empty implementation in an extension on SirenDelegate to allow for optional implemenation of its methods +extension SirenDelegate { + func sirenDidShowUpdateDialog(alertType: SirenAlertType) {} + func sirenUserDidLaunchAppStore() {} + func sirenUserDidSkipVersion() {} + func sirenUserDidCancel() {} + func sirenDidFailVersionCheck(error: NSError) {} + func sirenDidDetectNewVersionWithoutAlert(message: String) {} } @@ -148,7 +159,7 @@ public final class Siren: NSObject { The SirenDelegate variable, which should be set if you'd like to be notified: - When a user views or interacts with the alert - - sirenDidShowUpdateDialog() + - sirenDidShowUpdateDialog(alertType: SirenAlertType) - sirenUserDidLaunchAppStore() - sirenUserDidSkipVersion() - sirenUserDidCancel() @@ -430,12 +441,12 @@ private extension Siren { alertController.addAction(updateAlertAction()) alertController.addAction(skipAlertAction()) case .None: - delegate?.sirenDidDetectNewVersionWithoutAlert?(newVersionMessage) + delegate?.sirenDidDetectNewVersionWithoutAlert(newVersionMessage) } if alertType != .None { alertController.show() - delegate?.sirenDidShowUpdateDialog?() + delegate?.sirenDidShowUpdateDialog(alertType) } } @@ -444,7 +455,7 @@ private extension Siren { let action = UIAlertAction(title: title, style: .Default) { (alert: UIAlertAction) in self.hideWindow() self.launchAppStore() - self.delegate?.sirenUserDidLaunchAppStore?() + self.delegate?.sirenUserDidLaunchAppStore() return } @@ -455,7 +466,7 @@ private extension Siren { let title = localizedNextTimeButtonTitle() let action = UIAlertAction(title: title, style: .Default) { (alert: UIAlertAction) in self.hideWindow() - self.delegate?.sirenUserDidCancel?() + self.delegate?.sirenUserDidCancel() return } @@ -470,7 +481,7 @@ private extension Siren { NSUserDefaults.standardUserDefaults().synchronize() } self.hideWindow() - self.delegate?.sirenUserDidSkipVersion?() + self.delegate?.sirenUserDidSkipVersion() return } @@ -686,7 +697,7 @@ private extension Siren { let error = NSError(domain: SirenErrorDomain, code: code.rawValue, userInfo: userInfo) - delegate?.sirenDidFailVersionCheck?(error) + delegate?.sirenDidFailVersionCheck(error) printMessage(error.localizedDescription) } From 55edc3f5dd73fa7da9747fd31ee28219f8f519ab Mon Sep 17 00:00:00 2001 From: Dirk van Oosterbosch Date: Fri, 22 Apr 2016 22:05:41 +0200 Subject: [PATCH 5/5] Making the extension public --- Siren/Siren.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Siren/Siren.swift b/Siren/Siren.swift index 85648511..d81a4c85 100755 --- a/Siren/Siren.swift +++ b/Siren/Siren.swift @@ -22,7 +22,7 @@ public protocol SirenDelegate: class { // Empty implementation in an extension on SirenDelegate to allow for optional implemenation of its methods -extension SirenDelegate { +public extension SirenDelegate { func sirenDidShowUpdateDialog(alertType: SirenAlertType) {} func sirenUserDidLaunchAppStore() {} func sirenUserDidSkipVersion() {}