Skip to content

Commit

Permalink
Merge pull request #56 from ArtSabintsev/feature/irlabs-inspired-changes
Browse files Browse the repository at this point in the history
Work in Progress for v0.8.0
  • Loading branch information
ArtSabintsev committed Apr 27, 2016
2 parents 3ec8d95 + 78dd7ea commit 3a21914
Show file tree
Hide file tree
Showing 6 changed files with 70 additions and 82 deletions.
1 change: 1 addition & 0 deletions CONTRIBUTORS.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
36 changes: 20 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand All @@ -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
Expand Down Expand Up @@ -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#>

Expand Down Expand Up @@ -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
}
```

Expand Down
4 changes: 2 additions & 2 deletions Sample App/Sample App.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down
11 changes: 4 additions & 7 deletions Sample App/Sample App/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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
Expand All @@ -65,8 +62,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate {

extension AppDelegate: SirenDelegate
{
func sirenDidShowUpdateDialog() {
print(#function)
func sirenDidShowUpdateDialog(alertType: SirenAlertType) {
print(#function, alertType)
}

func sirenUserDidCancel() {
Expand Down
2 changes: 1 addition & 1 deletion Siren.podspec
Original file line number Diff line number Diff line change
@@ -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
Expand Down
98 changes: 42 additions & 56 deletions Siren/Siren.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -113,7 +102,7 @@ private enum SirenErrorCode: Int {
case AppStoreJSONParsingFailure
case AppStoreVersionNumberFailure
case AppStoreVersionArrayFailure
case AppStoreAppIdFailure
case AppStoreAppIDFailure
}

/**
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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?

Expand All @@ -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
}

Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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!)
}
Expand All @@ -649,7 +617,6 @@ private extension Siren {

}


// MARK: - UIAlertController Extensions

private extension UIAlertController {
Expand All @@ -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
}
Expand Down Expand Up @@ -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]
Expand All @@ -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) {}

}

0 comments on commit 3a21914

Please sign in to comment.