From dbfe20032f6d88ff3bf5c7edb64eab8d07d0af29 Mon Sep 17 00:00:00 2001 From: Arthur Ariel Sabintsev Date: Wed, 26 Dec 2018 20:47:03 -0500 Subject: [PATCH] Siren 4.0.0 (#250) * Scoping changes to most utility classes * Updated docs * Continued code clean up and file renaming * Metadata cleanup * Updated docs * Updated Siren.podspec to 4.0.0 * Massive overhaul of the localization logic * Fixed tests * Updated docs * Minor cleanup * Added new Configuration type but have not hooked it up * Non-working commit for Configuration * Continued configuration by thinning down Siren's main interface * Removed singleton and improved window presenting logic * Removed dead code. Confined utility methods to proper locations * Updated some documentation * Rebuilt version checking and alert presentation logic, however skip alert presentation logic is broken * Fixed skip version logic * Added default settings * Removed delegates and added completion handler * Changed initialization scheme * Minor changes to initialization * Fixed tests and error logic * Minor changes to sample project * Renamed all managers * Improved Errors * Continued cleanup * Mor cleanup * Continued cleanup * Removed Log struct as it is no longer needed. Removed unused helpers. Merged remaining helpers into mainline Siren.swift file * Began adding more documentation * Renamed alertManager to presentationManager * Removed redundant comments * Changed alphabetical ordering * Added some more built in rules * minor improvemeent to VersionCheckFrequency * Code cleanup * Added some missing errors * More robust error handling * Added more rules * Changed start to wail * Added deviceLanguageLocalization * Added documentation to BundleExtension * Added DateExtension documentation * Added UIAlertController extension documentation * Added UserDefaultsExtension documentation * Added APIManager documentation * Added PresentationManager documentation and began adding RulesManager documentation * Added AlertAction documentation * Added Results.swift documentation * Added Rules.swift documentation * Added documentation to VersionParser and SirenViewController * Some metadata cleanup * Lots more documentation * Moved networking code in APIManagr.swift * Moved lots of logic out of siren and into PresentationManager and Localization * More abstraction * Cleaned up alert presentation * More cleanup * minor changres * App now has less properties, improved uni-directional flow, and tests that account for that * Continued code modification * More documentation changes * More scope changes * More scope changes * More documentation * Fixed app store version null bug * More changes * Reorganized code in presentationmanager * Reorganized code in presentationmanager * Improved cache handling * Added a lot more documentation and removed some unnecessary optionality * Continued adding documentation * Fixed bug with layering of alert. Tons more documentation * Finished documenting all functions and properties * Updated jazzy docs * Added missing documentation * Removed superfluous files * Fixed bugs around prompt frequency. Updated code and documentation around prompt frequency * Reached 100% of private function and beyond documentation * Fixed bug arond presenting localized strings vs custom strings * Added lots of examples to AppDelegate. Updated README. Removed attributed string settings since they never worked * Documentation updated. Added tons of examples to AppDelegate.swift with comments * Updated README * Updated README.md * Updated README.md * Updated README.md * Removed random fragment in readme * Updated README * More README updates * More README updates * Updated README * More metadata updates * Update metadata * Regenerated jazzy docs --- .github/ISSUE_TEMPLATE.md | 2 +- .ruby-version | 1 - .swiftlint.yml | 6 +- CONTRIBUTORS.md | 83 -- Example/Example.xcodeproj/project.pbxproj | 128 +- Example/Example/AppDelegate.swift | 271 +++- Example/Tests/SirenTests.swift | 601 ++++---- Gemfile | 1 - README.md | 243 +--- README.zh_CN.md | 232 --- Siren.podspec | 4 +- Sources/Extensions/BundleExtension.swift | 111 ++ ...ateExtension.swift => DateExtension.swift} | 15 +- Sources/Extensions/SirenBundleExtension.swift | 55 - .../SirenLocalizationExtension.swift | 48 - .../SirenUIAlertControllerExtension.swift | 25 - .../UIAlertControllerExtension.swift | 28 + ...sion.swift => UserDefaultsExtension.swift} | 32 +- Sources/Managers/APIManager.swift | 131 ++ Sources/Managers/PresentationManager.swift | 219 +++ Sources/Managers/RulesManager.swift | 125 ++ Sources/Models/AlertAction.swift | 21 + Sources/Models/AlertConstants.swift | 27 + Sources/Models/Localization.swift | 166 +++ ...renLookupModel.swift => LookupModel.swift} | 16 +- Sources/Models/Results.swift | 25 + Sources/Models/Rules.swift | 86 ++ Sources/Models/SirenAlertMessaging.swift | 64 - Sources/Protocols/SirenDelegate.swift | 110 -- Sources/Siren.swift | 465 ++---- Sources/Utilities/DataParser.swift | 92 ++ Sources/Utilities/KnownError.swift | 78 + Sources/Utilities/SirenConstants.swift | 129 -- Sources/Utilities/SirenError.swift | 86 -- Sources/Utilities/SirenHelpers.swift | 83 -- Sources/Utilities/SirenLog.swift | 18 - .../SirenViewController.swift | 10 +- docs/Classes.html | 117 +- docs/Classes/Siren.html | 505 ++++--- docs/Classes/Siren/AlertType.html | 30 +- docs/Classes/Siren/LanguageType.html | 182 +-- docs/Classes/Siren/VersionCheckType.html | 26 +- docs/Classes/SirenViewController.html | 185 +++ docs/Enums.html | 129 +- docs/Enums/AlertAction.html | 266 ++++ docs/Enums/KnownError.html | 563 +++++++ docs/Enums/UpdateType.html | 34 +- docs/Extensions.html | 272 ++++ docs/Extensions/Bundle.html | 433 ++++++ docs/Extensions/Bundle/Constants.html | 320 ++++ docs/Extensions/Date.html | 257 ++++ docs/Extensions/UIAlertController.html | 249 ++++ docs/Extensions/UserDefaults.html | 266 ++++ docs/Extensions/UserDefaults/SirenKeys.html | 240 +++ docs/Protocols.html | 49 +- docs/Protocols/SirenDelegate.html | 71 +- docs/Structs.html | 328 ++++- docs/Structs/APIManager.html | 494 +++++++ docs/Structs/APIManager/Constants.html | 212 +++ docs/Structs/AlertConstants.html | 293 ++++ docs/Structs/AlertMessaging.html | 250 ++++ docs/Structs/AlertMessaging/Constants.html | 252 ++++ docs/Structs/CapturedError.html | 158 ++ docs/Structs/CapturedError/Known.html | 400 +++++ docs/Structs/Constants.html | 211 +++ docs/Structs/Constants/AlertType.html | 226 +++ docs/Structs/Constants/UpdateType.html | 262 ++++ .../Constants/VersionCheckFrequency.html | 198 +++ docs/Structs/DataParser.html | 385 +++++ docs/Structs/Localization.html | 458 ++++++ docs/Structs/Localization/Language.html | 1295 +++++++++++++++++ docs/Structs/LookupModel.html | 241 +++ docs/Structs/LookupModel/CodingKeys.html | 185 +++ docs/Structs/LookupModel/Results.html | 321 ++++ .../LookupModel/Results/CodingKeys.html | 293 ++++ docs/Structs/PresentationManager.html | 838 +++++++++++ docs/Structs/Results.html | 267 ++++ docs/Structs/Rules.html | 472 ++++++ docs/Structs/Rules/AlertType.html | 267 ++++ docs/Structs/Rules/UpdatePrompFrequency.html | 237 +++ docs/Structs/Rules/UpdatePromptFrequency.html | 240 +++ docs/Structs/RulesManager.html | 570 ++++++++ docs/Structs/RulesManager/UpdateType.html | 302 ++++ docs/Structs/SirenAlertMessaging.html | 6 +- .../SirenAlertMessaging/Constants.html | 2 +- docs/Structs/SirenError.html | 2 +- docs/Structs/SirenError/Known.html | 46 +- docs/Structs/SirenLookupModel.html | 2 +- docs/Structs/SirenLookupModel/Results.html | 2 +- .../Contents/Resources/Documents/Classes.html | 117 +- .../Resources/Documents/Classes/Siren.html | 505 ++++--- .../Documents/Classes/Siren/AlertType.html | 30 +- .../Documents/Classes/Siren/LanguageType.html | 182 +-- .../Classes/Siren/VersionCheckType.html | 26 +- .../Classes/SirenViewController.html | 185 +++ .../Contents/Resources/Documents/Enums.html | 129 +- .../Documents/Enums/AlertAction.html | 266 ++++ .../Resources/Documents/Enums/KnownError.html | 563 +++++++ .../Resources/Documents/Enums/UpdateType.html | 34 +- .../Resources/Documents/Extensions.html | 272 ++++ .../Documents/Extensions/Bundle.html | 433 ++++++ .../Extensions/Bundle/Constants.html | 320 ++++ .../Resources/Documents/Extensions/Date.html | 257 ++++ .../Extensions/UIAlertController.html | 249 ++++ .../Documents/Extensions/UserDefaults.html | 266 ++++ .../Extensions/UserDefaults/SirenKeys.html | 240 +++ .../Resources/Documents/Protocols.html | 49 +- .../Documents/Protocols/SirenDelegate.html | 71 +- .../Contents/Resources/Documents/Structs.html | 328 ++++- .../Documents/Structs/APIManager.html | 494 +++++++ .../Structs/APIManager/Constants.html | 212 +++ .../Documents/Structs/AlertConstants.html | 293 ++++ .../Documents/Structs/AlertMessaging.html | 250 ++++ .../Structs/AlertMessaging/Constants.html | 252 ++++ .../Documents/Structs/CapturedError.html | 158 ++ .../Structs/CapturedError/Known.html | 400 +++++ .../Documents/Structs/Constants.html | 211 +++ .../Structs/Constants/AlertType.html | 226 +++ .../Structs/Constants/UpdateType.html | 262 ++++ .../Constants/VersionCheckFrequency.html | 198 +++ .../Documents/Structs/DataParser.html | 385 +++++ .../Documents/Structs/Localization.html | 458 ++++++ .../Structs/Localization/Language.html | 1295 +++++++++++++++++ .../Documents/Structs/LookupModel.html | 241 +++ .../Structs/LookupModel/CodingKeys.html | 185 +++ .../Structs/LookupModel/Results.html | 321 ++++ .../LookupModel/Results/CodingKeys.html | 293 ++++ .../Structs/PresentationManager.html | 838 +++++++++++ .../Resources/Documents/Structs/Results.html | 267 ++++ .../Resources/Documents/Structs/Rules.html | 472 ++++++ .../Documents/Structs/Rules/AlertType.html | 267 ++++ .../Structs/Rules/UpdatePrompFrequency.html | 237 +++ .../Structs/Rules/UpdatePromptFrequency.html | 240 +++ .../Documents/Structs/RulesManager.html | 570 ++++++++ .../Structs/RulesManager/UpdateType.html | 302 ++++ .../Structs/SirenAlertMessaging.html | 6 +- .../SirenAlertMessaging/Constants.html | 2 +- .../Documents/Structs/SirenError.html | 2 +- .../Documents/Structs/SirenError/Known.html | 46 +- .../Documents/Structs/SirenLookupModel.html | 2 +- .../Structs/SirenLookupModel/Results.html | 2 +- .../Contents/Resources/Documents/index.html | 318 ++-- .../Contents/Resources/Documents/search.json | 2 +- .../Contents/Resources/docSet.dsidx | Bin 36864 -> 73728 bytes docs/docsets/Siren.tgz | Bin 72982 -> 115721 bytes docs/index.html | 318 ++-- docs/search.json | 2 +- 147 files changed, 28871 insertions(+), 3424 deletions(-) delete mode 100644 .ruby-version delete mode 100755 CONTRIBUTORS.md delete mode 100644 README.zh_CN.md create mode 100644 Sources/Extensions/BundleExtension.swift rename Sources/Extensions/{SirenDateExtension.swift => DateExtension.swift} (62%) delete mode 100644 Sources/Extensions/SirenBundleExtension.swift delete mode 100644 Sources/Extensions/SirenLocalizationExtension.swift delete mode 100644 Sources/Extensions/SirenUIAlertControllerExtension.swift create mode 100644 Sources/Extensions/UIAlertControllerExtension.swift rename Sources/Extensions/{SirenUserDefaultsExtension.swift => UserDefaultsExtension.swift} (51%) create mode 100644 Sources/Managers/APIManager.swift create mode 100644 Sources/Managers/PresentationManager.swift create mode 100644 Sources/Managers/RulesManager.swift create mode 100644 Sources/Models/AlertAction.swift create mode 100644 Sources/Models/AlertConstants.swift create mode 100644 Sources/Models/Localization.swift rename Sources/Models/{SirenLookupModel.swift => LookupModel.swift} (65%) create mode 100644 Sources/Models/Results.swift create mode 100644 Sources/Models/Rules.swift delete mode 100644 Sources/Models/SirenAlertMessaging.swift delete mode 100644 Sources/Protocols/SirenDelegate.swift create mode 100644 Sources/Utilities/DataParser.swift create mode 100644 Sources/Utilities/KnownError.swift delete mode 100644 Sources/Utilities/SirenConstants.swift delete mode 100644 Sources/Utilities/SirenError.swift delete mode 100644 Sources/Utilities/SirenHelpers.swift delete mode 100644 Sources/Utilities/SirenLog.swift create mode 100644 docs/Classes/SirenViewController.html create mode 100644 docs/Enums/AlertAction.html create mode 100644 docs/Enums/KnownError.html create mode 100644 docs/Extensions.html create mode 100644 docs/Extensions/Bundle.html create mode 100644 docs/Extensions/Bundle/Constants.html create mode 100644 docs/Extensions/Date.html create mode 100644 docs/Extensions/UIAlertController.html create mode 100644 docs/Extensions/UserDefaults.html create mode 100644 docs/Extensions/UserDefaults/SirenKeys.html create mode 100644 docs/Structs/APIManager.html create mode 100644 docs/Structs/APIManager/Constants.html create mode 100644 docs/Structs/AlertConstants.html create mode 100644 docs/Structs/AlertMessaging.html create mode 100644 docs/Structs/AlertMessaging/Constants.html create mode 100644 docs/Structs/CapturedError.html create mode 100644 docs/Structs/CapturedError/Known.html create mode 100644 docs/Structs/Constants.html create mode 100644 docs/Structs/Constants/AlertType.html create mode 100644 docs/Structs/Constants/UpdateType.html create mode 100644 docs/Structs/Constants/VersionCheckFrequency.html create mode 100644 docs/Structs/DataParser.html create mode 100644 docs/Structs/Localization.html create mode 100644 docs/Structs/Localization/Language.html create mode 100644 docs/Structs/LookupModel.html create mode 100644 docs/Structs/LookupModel/CodingKeys.html create mode 100644 docs/Structs/LookupModel/Results.html create mode 100644 docs/Structs/LookupModel/Results/CodingKeys.html create mode 100644 docs/Structs/PresentationManager.html create mode 100644 docs/Structs/Results.html create mode 100644 docs/Structs/Rules.html create mode 100644 docs/Structs/Rules/AlertType.html create mode 100644 docs/Structs/Rules/UpdatePrompFrequency.html create mode 100644 docs/Structs/Rules/UpdatePromptFrequency.html create mode 100644 docs/Structs/RulesManager.html create mode 100644 docs/Structs/RulesManager/UpdateType.html create mode 100644 docs/docsets/Siren.docset/Contents/Resources/Documents/Classes/SirenViewController.html create mode 100644 docs/docsets/Siren.docset/Contents/Resources/Documents/Enums/AlertAction.html create mode 100644 docs/docsets/Siren.docset/Contents/Resources/Documents/Enums/KnownError.html create mode 100644 docs/docsets/Siren.docset/Contents/Resources/Documents/Extensions.html create mode 100644 docs/docsets/Siren.docset/Contents/Resources/Documents/Extensions/Bundle.html create mode 100644 docs/docsets/Siren.docset/Contents/Resources/Documents/Extensions/Bundle/Constants.html create mode 100644 docs/docsets/Siren.docset/Contents/Resources/Documents/Extensions/Date.html create mode 100644 docs/docsets/Siren.docset/Contents/Resources/Documents/Extensions/UIAlertController.html create mode 100644 docs/docsets/Siren.docset/Contents/Resources/Documents/Extensions/UserDefaults.html create mode 100644 docs/docsets/Siren.docset/Contents/Resources/Documents/Extensions/UserDefaults/SirenKeys.html create mode 100644 docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/APIManager.html create mode 100644 docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/APIManager/Constants.html create mode 100644 docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/AlertConstants.html create mode 100644 docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/AlertMessaging.html create mode 100644 docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/AlertMessaging/Constants.html create mode 100644 docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/CapturedError.html create mode 100644 docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/CapturedError/Known.html create mode 100644 docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/Constants.html create mode 100644 docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/Constants/AlertType.html create mode 100644 docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/Constants/UpdateType.html create mode 100644 docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/Constants/VersionCheckFrequency.html create mode 100644 docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/DataParser.html create mode 100644 docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/Localization.html create mode 100644 docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/Localization/Language.html create mode 100644 docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/LookupModel.html create mode 100644 docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/LookupModel/CodingKeys.html create mode 100644 docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/LookupModel/Results.html create mode 100644 docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/LookupModel/Results/CodingKeys.html create mode 100644 docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/PresentationManager.html create mode 100644 docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/Results.html create mode 100644 docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/Rules.html create mode 100644 docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/Rules/AlertType.html create mode 100644 docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/Rules/UpdatePrompFrequency.html create mode 100644 docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/Rules/UpdatePromptFrequency.html create mode 100644 docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/RulesManager.html create mode 100644 docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/RulesManager/UpdateType.html diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index a230c84c..51f45890 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -4,7 +4,7 @@ - **What is your app's Bundle ID?**: - **When was the latest version of your app published to the App Store?**: - **Is your app published in the US App Store? If not, what App Store is it published in?**: -- **Does Siren work if you plugin your app's BundleID (and CountryCode, if necessary) into the Example App?**: +- **Does Siren work if you plugin your app's `BundleID` (and `countryCode`, if necessary) into the Example app?**: --- diff --git a/.ruby-version b/.ruby-version deleted file mode 100644 index 25c81fe3..00000000 --- a/.ruby-version +++ /dev/null @@ -1 +0,0 @@ -ruby-2.5.1 \ No newline at end of file diff --git a/.swiftlint.yml b/.swiftlint.yml index 4b98e5a3..027a24a0 100644 --- a/.swiftlint.yml +++ b/.swiftlint.yml @@ -5,13 +5,11 @@ included: disabled_rules: - cyclomatic_complexity + - line_length - nesting - unused_optional_binding - variable_name # Specialized Rules file_length: - - 1000 - -line_length: - - 200 + - 500 diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md deleted file mode 100755 index d07f23f7..00000000 --- a/CONTRIBUTORS.md +++ /dev/null @@ -1,83 +0,0 @@ -### Creators -[Arthur Ariel Sabintsev](http://www.sabintsev.com/) and [Aaron Brager](https://twitter.com/GetAaron) - -### Siren Project Contributors -- [Dmitry Bespalov](https://github.com/diamondsky) for [Pull Request #7](https://github.com/ArtSabintsev/Siren/pull/7) -- [Daniel Bauke](https://github.com/bonkey) for [Pull Request #8](https://github.com/ArtSabintsev/Siren/pull/8) -- [ipedro](https://github.com/ipedro) for [Pull Request #76 (Harpy)](https://github.com/ArtSabintsev/Harpy/pull/76) -- [Michael Graff](https://github.com/skandragon) for [Pull Request #15](https://github.com/ArtSabintsev/Siren/pull/15) -- [Jaroslav_](https://github.com/jaroslavas) for [Pull Request #83 (Harpy)](https://github.com/ArtSabintsev/Harpy/pull/83) -- [Dylan Bettermann](https://github.com/dbettermann) for [Pull Request #18](https://github.com/ArtSabintsev/Siren/pull/18) -- [Daniel](https://github.com/Daniel) for [Pull Request #21](https://github.com/ArtSabintsev/Siren/pull/21) -- [nagaho](https://github.com/nagaho) for [Pull Request #22](https://github.com/ArtSabintsev/Siren/pull/22) -- [Parnsind Hantrakool](https://github.com/kong707) for [Pull Request #91 (Harpy)](https://github.com/ArtSabintsev/Harpy/issues/91) -- [Tibor Molnár](https://github.com/fatalaa) for [Pull Request #96 (Harpy)](https://github.com/ArtSabintsev/Harpy/issues/96) -- [Tanel Suurhans](https://github.com/tanelsuurhans) and [Jaroslav_](https://github.com/jaroslavas) for [Pull Request #99 (Harpy)](https://github.com/ArtSabintsev/Harpy/issues/99) -- [Zaid M. Said](https://github.com/SentulAsia) for [Pull Request #36](https://github.com/ArtSabintsev/Siren/pull/36) -- [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) -- [pavankataria](https://github.com/pavankataria) for [Pull Request #63](https://github.com/ArtSabintsev/Siren/pull/63) -- [attilat85](https://github.com/attilat85) for [Pull Request #124 (Harpy)](https://github.com/ArtSabintsev/Harpy/pull/124) -- [Vahan Margaryan](https://github.com/VahanMargaryan) for [Pull Request #71](https://github.com/ArtSabintsev/Siren/pull/71) -- [Josip Injic](https://github.com/jinjic) for [Pull Request #73](https://github.com/ArtSabintsev/Siren/pull/73) -- [Thi](https://github.com/thii) for [Pull Request #78](https://github.com/ArtSabintsev/Siren/pull/78) -- [Ilija Puaca](https://github.com/ilijapuaca) for [Pull Request #141 (Harpy)](https://github.com/ArtSabintsev/Harpy/pull/141) -- [thii](https://github.com/ilijapuaca) for [Pull Request #83](https://github.com/ArtSabintsev/Siren/pull/83) -- [Kristaps Grinbergs](https://github.com/fassko) for [Pull Request #90](https://github.com/ArtSabintsev/Siren/pull/90) -- [Luciano Nascimento](https://github.com/@lucianocn) for [Pull Request #146 (Harpy)](https://github.com/ArtSabintsev/Harpy/pull/146) -- [Stefan Kieleithner](https://github.com/steviki) for [Pull Request #97](https://github.com/ArtSabintsev/Siren/pull/97) -- [Vladislav Jevremović](https://github.com/VladislavJevremovic) for [Pull Request #98](https://github.com/ArtSabintsev/Siren/pull/98) -- [Stefan Kieleithner](https://github.com/steviki) for [Pull Request #99](https://github.com/ArtSabintsev/Siren/pull/99) -- [Konstantinos N.](https://github.com/kwstasna) for [Pull Request #101](https://github.com/ArtSabintsev/Siren/pull/101) -- [Christoph Mantler](https://github.com/ChrisixFlash) for [Pull Request #103](https://github.com/ArtSabintsev/Siren/pull/103) and [Pull Request #106](https://github.com/ArtSabintsev/Siren/pull/106) -- [xedla](https://github.com/xedla) for [Pull Request #107](https://github.com/ArtSabintsev/Siren/pull/107) -- [Daniel Hu](https://github.com/zongmumask) for [Pull Request #116](https://github.com/ArtSabintsev/Siren/pull/116) -- [Jakob Krigovsky](https://github.com/sonicdoe) for [Pull Request #118](https://github.com/ArtSabintsev/Siren/pull/118) -- [germ4n](https://github.com/glm4) for [Pull Request #122](https://github.com/ArtSabintsev/Siren/pull/122) -- [Txai Wieser](https://github.com/txaiwieser) for [Pull Request #139](https://github.com/ArtSabintsev/Siren/pull/139) and [Pull Request #140](https://github.com/ArtSabintsev/Siren/pull/140) -- [premyslvlcek](https://github.com/premyslvlcek) for [Pull Request #143](https://github.com/ArtSabintsev/Siren/pull/143) -- [Seyed Mojtaba Hosseini Zeidabadi](https://github.com/MojtabaHs) for [Pull Request #144](https://github.com/ArtSabintsev/Siren/pull/143) -- [Chanchal Raj](https://github.com/RajChanchal) for [Pull Request #171 (Harpy)](https://github.com/ArtSabintsev/harpy/pull/171) -- [Ryoh Tsukahara](https://github.com/nixnoughtnothing) for [Pull Request #150](https://github.com/ArtSabintsev/Siren/pull/150) and [Pull Request #159](https://github.com/ArtSabintsev/Siren/pull/159) -- [Jason Wade](https://github.com/iJasonWade) for [Pull Request #162](https://github.com/ArtSabintsev/Siren/pull/162) -- [JussiSuojanen](https://github.com/JussiSuojanen) for [Pull Request #171](https://github.com/ArtSabintsev/Siren/pull/171) -- [Dmytro Cheverda](https://github.com/dimacheverda) for [Pull Request #175](https://github.com/ArtSabintsev/Siren/pull/175) -- [Vladislav Jevremovic](https://github.com/VladislavJevremovic) for [Pull Request #176](https://github.com/ArtSabintsev/Siren/pull/176) -- [NSemakov](https://github.com/NSemakov) for [Pull Request #179](https://github.com/ArtSabintsev/Siren/pull/179) -- [Attia Mo](https://github.com/attiamo) for [Pull Request #182](https://github.com/ArtSabintsev/Siren/pull/182) -- [wbison](https://github.com/wbison) for [Pull Request #185 (Harpy)](https://github.com/ArtSabintsev/Harpy/pull/185) -- [Balázs Vincze](https://github.com/vinczebalazs) for [Pull Request #200](https://github.com/ArtSabintsev/siren/pull/200) -- [Txai Wieser](https://github.com/txaiwieser) for [Pull Request #201](https://github.com/ArtSabintsev/siren/pull/200) -- [Gabriel Martelo](https://github.com/gabmarfer) for [Pull Request #214](https://github.com/ArtSabintsev/Siren/pull/214) -- [Ryan](ryanthon) for [Pull Request #216](https://github.com/ArtSabintsev/Siren/pull/216) -- [Titouan Van Belle](TitouanVanBelle) for [Pull Request #218](https://github.com/ArtSabintsev/Siren/pull/218) -- [Ilija Puaca](https://github.com/ilijapuaca) for [Pull Request #247](https://github.com/ArtSabintsev/Siren/pull/247) - -### Harpy Project Contributors -This repo is a Swift language port of [Harpy](https://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. - -A huge **Thank You** to the following developers: - -- [Borut Tomažin](https://github.com/borut-t) -- [Bertie Liu](https://github.com/https://github.com/aceisScope) -- [Burakkilic](https://github.com/burakkilic) -- [Claas Lange](https://github.com/claaslange) -- [Daniel](https://github.com/danieltskv) -- [David Keegan](https://github.com/kgn) -- [Erick](https://github.com/dexcell0) -- [Ercillagorka](https://github.com/ercillagorka) -- [Jamie Ly](https://github,com/jamiely) -- [Jon Andersen](https://github.com/jonandersen) -- [Josh T. Brown](https://github.com/joshuatbrown) -- [Mark Rickert](https://github.com/markrickert) -- [Patrick Debois](https://github.com/jedi4ever) -- [Pius Uzamere](https://github.com/pius) -- [Rahul Jiresal](https://github.com/rahuljiresal) -- [Rui Peres](https://github.com/RuiAAPeres) -- [Thomas Hempel](https://github.com/thomashempel) -- [TrentW](https://github.com/trentw) - -### Special Thanks -Finally, a massive **Thank You** to [Aaron Brager](https://twitter.com/GetAaron) for the dev-work and issue-moderation he's done on Harpy since 2012. A Swift port could not have happened without him. diff --git a/Example/Example.xcodeproj/project.pbxproj b/Example/Example.xcodeproj/project.pbxproj index c80f0b82..f0cc2b65 100755 --- a/Example/Example.xcodeproj/project.pbxproj +++ b/Example/Example.xcodeproj/project.pbxproj @@ -7,29 +7,33 @@ objects = { /* Begin PBXBuildFile section */ + 4D729C4421A20EDC002F73AB /* Siren.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 55EC365E1E6BB99B00726F13 /* Siren.bundle */; }; + 4D729C4621A213B4002F73AB /* Rules.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4D729C4521A213B4002F73AB /* Rules.swift */; }; 55EC364B1E6BB98A00726F13 /* Siren.h in Headers */ = {isa = PBXBuildFile; fileRef = 55EC36491E6BB98A00726F13 /* Siren.h */; settings = {ATTRIBUTES = (Public, ); }; }; 55EC36601E6BB99B00726F13 /* Siren.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 55EC365E1E6BB99B00726F13 /* Siren.bundle */; }; 55EC36611E6BB99B00726F13 /* Siren.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55EC365F1E6BB99B00726F13 /* Siren.swift */; }; + 8E01817921B379AF006DED05 /* Results.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8E01817821B379AF006DED05 /* Results.swift */; }; + 8E01817E21B379DD006DED05 /* PresentationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8E01817B21B379DD006DED05 /* PresentationManager.swift */; }; + 8E01818021B379DD006DED05 /* RulesManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8E01817D21B379DD006DED05 /* RulesManager.swift */; }; + 8E01818221B37B43006DED05 /* AlertAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8E01818121B37B43006DED05 /* AlertAction.swift */; }; 8E1635A91E6A0B9C0060CE27 /* SirenTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8EE6C74C1E6A0AE100DBE454 /* SirenTests.swift */; }; - 8E171DDE215B26B4006FBBCC /* SirenBundleExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8E171DD1215B26B4006FBBCC /* SirenBundleExtension.swift */; }; - 8E171DDF215B26B4006FBBCC /* SirenDateExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8E171DD2215B26B4006FBBCC /* SirenDateExtension.swift */; }; - 8E171DE1215B26B4006FBBCC /* SirenAlertMessaging.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8E171DD6215B26B4006FBBCC /* SirenAlertMessaging.swift */; }; - 8E171DE2215B26B4006FBBCC /* SirenLookupModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8E171DD7215B26B4006FBBCC /* SirenLookupModel.swift */; }; - 8E171DE3215B26B4006FBBCC /* SirenUIAlertControllerExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8E171DD8215B26B4006FBBCC /* SirenUIAlertControllerExtension.swift */; }; + 8E171DDE215B26B4006FBBCC /* BundleExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8E171DD1215B26B4006FBBCC /* BundleExtension.swift */; }; + 8E171DDF215B26B4006FBBCC /* DateExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8E171DD2215B26B4006FBBCC /* DateExtension.swift */; }; + 8E171DE2215B26B4006FBBCC /* LookupModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8E171DD7215B26B4006FBBCC /* LookupModel.swift */; }; + 8E171DE3215B26B4006FBBCC /* UIAlertControllerExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8E171DD8215B26B4006FBBCC /* UIAlertControllerExtension.swift */; }; 8E171DE4215B26B4006FBBCC /* SirenViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8E171DDA215B26B4006FBBCC /* SirenViewController.swift */; }; - 8E171DE6215B2701006FBBCC /* SirenLocalizationExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8E171DE5215B2701006FBBCC /* SirenLocalizationExtension.swift */; }; - 8E171DEE215B2D09006FBBCC /* SirenUserDefaultsExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8E171DED215B2D09006FBBCC /* SirenUserDefaultsExtension.swift */; }; + 8E171DE6215B2701006FBBCC /* Localization.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8E171DE5215B2701006FBBCC /* Localization.swift */; }; + 8E171DEE215B2D09006FBBCC /* UserDefaultsExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8E171DED215B2D09006FBBCC /* UserDefaultsExtension.swift */; }; + 8E5061EB21C9CDF000A28DE0 /* AlertConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8E5061EA21C9CDF000A28DE0 /* AlertConstants.swift */; }; 8E641D2520C8B44B00908555 /* Siren.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 55EC36471E6BB98A00726F13 /* Siren.framework */; }; 8EACA9711F380294003134CA /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 8EACA9671F37F2D3003134CA /* LaunchScreen.xib */; }; 8EACA9721F380294003134CA /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 8EACA9691F37F2D3003134CA /* Main.storyboard */; }; 8EACA9731F380294003134CA /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 8EACA96B1F37F2D3003134CA /* Images.xcassets */; }; 8EACA9741F38029B003134CA /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8EACA9661F37F2D3003134CA /* AppDelegate.swift */; }; 8EACA9751F38029B003134CA /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8EACA96D1F37F2D3003134CA /* ViewController.swift */; }; - 8ED733FC215DBE4900447E3B /* SirenDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8E171DD4215B26B4006FBBCC /* SirenDelegate.swift */; }; - 8ED733FD215DBE4900447E3B /* SirenConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8E171DE7215B27C2006FBBCC /* SirenConstants.swift */; }; - 8ED733FE215DBE4900447E3B /* SirenError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8E171DCD215B26B4006FBBCC /* SirenError.swift */; }; - 8ED733FF215DBE4900447E3B /* SirenLog.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8E171DCE215B26B4006FBBCC /* SirenLog.swift */; }; - 8ED73400215DBE4900447E3B /* SirenHelpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8E171DEB215B28EC006FBBCC /* SirenHelpers.swift */; }; + 8EE1FAF321B4DC0F009112A8 /* KnownError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8EE1FAF221B4DC0F009112A8 /* KnownError.swift */; }; + 8EE1FAF521B4E6B8009112A8 /* APIManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8EE1FAF421B4E6B8009112A8 /* APIManager.swift */; }; + 8EEF4AB121AA6DDF00C83AAA /* DataParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8EEF4AB021AA6DDF00C83AAA /* DataParser.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -43,25 +47,25 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + 4D729C4521A213B4002F73AB /* Rules.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Rules.swift; sourceTree = ""; }; 55EC36471E6BB98A00726F13 /* Siren.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Siren.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 55EC36491E6BB98A00726F13 /* Siren.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Siren.h; sourceTree = ""; }; 55EC364A1E6BB98A00726F13 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = ../../Siren/Info.plist; sourceTree = ""; }; 55EC365E1E6BB99B00726F13 /* Siren.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; name = Siren.bundle; path = ../../Sources/Siren.bundle; sourceTree = ""; }; 55EC365F1E6BB99B00726F13 /* Siren.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Siren.swift; path = ../../Sources/Siren.swift; sourceTree = ""; }; - 8E171DCD215B26B4006FBBCC /* SirenError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SirenError.swift; sourceTree = ""; }; - 8E171DCE215B26B4006FBBCC /* SirenLog.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SirenLog.swift; sourceTree = ""; }; - 8E171DD1215B26B4006FBBCC /* SirenBundleExtension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SirenBundleExtension.swift; sourceTree = ""; }; - 8E171DD2215B26B4006FBBCC /* SirenDateExtension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SirenDateExtension.swift; sourceTree = ""; }; - 8E171DD4215B26B4006FBBCC /* SirenDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SirenDelegate.swift; sourceTree = ""; }; - 8E171DD6215B26B4006FBBCC /* SirenAlertMessaging.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SirenAlertMessaging.swift; sourceTree = ""; }; - 8E171DD7215B26B4006FBBCC /* SirenLookupModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SirenLookupModel.swift; sourceTree = ""; }; - 8E171DD8215B26B4006FBBCC /* SirenUIAlertControllerExtension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SirenUIAlertControllerExtension.swift; sourceTree = ""; }; + 8E01817821B379AF006DED05 /* Results.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Results.swift; sourceTree = ""; }; + 8E01817B21B379DD006DED05 /* PresentationManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PresentationManager.swift; sourceTree = ""; }; + 8E01817D21B379DD006DED05 /* RulesManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RulesManager.swift; sourceTree = ""; }; + 8E01818121B37B43006DED05 /* AlertAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlertAction.swift; sourceTree = ""; }; + 8E171DD1215B26B4006FBBCC /* BundleExtension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BundleExtension.swift; sourceTree = ""; }; + 8E171DD2215B26B4006FBBCC /* DateExtension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DateExtension.swift; sourceTree = ""; }; + 8E171DD7215B26B4006FBBCC /* LookupModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LookupModel.swift; sourceTree = ""; }; + 8E171DD8215B26B4006FBBCC /* UIAlertControllerExtension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIAlertControllerExtension.swift; sourceTree = ""; }; 8E171DDA215B26B4006FBBCC /* SirenViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SirenViewController.swift; sourceTree = ""; }; - 8E171DE5215B2701006FBBCC /* SirenLocalizationExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SirenLocalizationExtension.swift; sourceTree = ""; }; - 8E171DE7215B27C2006FBBCC /* SirenConstants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SirenConstants.swift; sourceTree = ""; }; - 8E171DEB215B28EC006FBBCC /* SirenHelpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SirenHelpers.swift; sourceTree = ""; }; - 8E171DED215B2D09006FBBCC /* SirenUserDefaultsExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SirenUserDefaultsExtension.swift; sourceTree = ""; }; + 8E171DE5215B2701006FBBCC /* Localization.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Localization.swift; sourceTree = ""; }; + 8E171DED215B2D09006FBBCC /* UserDefaultsExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserDefaultsExtension.swift; sourceTree = ""; }; 8E3A6C041D07CB6F00A8B7CF /* Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Tests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 8E5061EA21C9CDF000A28DE0 /* AlertConstants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlertConstants.swift; sourceTree = ""; }; 8EACA9661F37F2D3003134CA /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 8EACA9681F37F2D3003134CA /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = ""; }; 8EACA96A1F37F2D3003134CA /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; @@ -69,8 +73,11 @@ 8EACA96C1F37F2D3003134CA /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 8EACA96D1F37F2D3003134CA /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 8EC391811A58B465001C121E /* Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Example.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 8EE1FAF221B4DC0F009112A8 /* KnownError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KnownError.swift; sourceTree = ""; }; + 8EE1FAF421B4E6B8009112A8 /* APIManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = APIManager.swift; sourceTree = ""; }; 8EE6C74B1E6A0AE100DBE454 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 8EE6C74C1E6A0AE100DBE454 /* SirenTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SirenTests.swift; sourceTree = ""; }; + 8EEF4AB021AA6DDF00C83AAA /* DataParser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataParser.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -106,21 +113,30 @@ 55EC365F1E6BB99B00726F13 /* Siren.swift */, 55EC365E1E6BB99B00726F13 /* Siren.bundle */, 8E171DD0215B26B4006FBBCC /* Extensions */, + 8E01817A21B379DD006DED05 /* Managers */, 8E171DD5215B26B4006FBBCC /* Models */, - 8E171DD3215B26B4006FBBCC /* Protocols */, 8E171DCC215B26B4006FBBCC /* Utilities */, 8E171DD9215B26B4006FBBCC /* View Controllers */, ); path = Siren; sourceTree = ""; }; + 8E01817A21B379DD006DED05 /* Managers */ = { + isa = PBXGroup; + children = ( + 8EE1FAF421B4E6B8009112A8 /* APIManager.swift */, + 8E01817B21B379DD006DED05 /* PresentationManager.swift */, + 8E01817D21B379DD006DED05 /* RulesManager.swift */, + ); + name = Managers; + path = ../../Sources/Managers; + sourceTree = ""; + }; 8E171DCC215B26B4006FBBCC /* Utilities */ = { isa = PBXGroup; children = ( - 8E171DE7215B27C2006FBBCC /* SirenConstants.swift */, - 8E171DCD215B26B4006FBBCC /* SirenError.swift */, - 8E171DCE215B26B4006FBBCC /* SirenLog.swift */, - 8E171DEB215B28EC006FBBCC /* SirenHelpers.swift */, + 8EEF4AB021AA6DDF00C83AAA /* DataParser.swift */, + 8EE1FAF221B4DC0F009112A8 /* KnownError.swift */, ); name = Utilities; path = ../../Sources/Utilities; @@ -129,30 +145,24 @@ 8E171DD0215B26B4006FBBCC /* Extensions */ = { isa = PBXGroup; children = ( - 8E171DD1215B26B4006FBBCC /* SirenBundleExtension.swift */, - 8E171DD2215B26B4006FBBCC /* SirenDateExtension.swift */, - 8E171DE5215B2701006FBBCC /* SirenLocalizationExtension.swift */, - 8E171DD8215B26B4006FBBCC /* SirenUIAlertControllerExtension.swift */, - 8E171DED215B2D09006FBBCC /* SirenUserDefaultsExtension.swift */, + 8E171DD1215B26B4006FBBCC /* BundleExtension.swift */, + 8E171DD2215B26B4006FBBCC /* DateExtension.swift */, + 8E171DD8215B26B4006FBBCC /* UIAlertControllerExtension.swift */, + 8E171DED215B2D09006FBBCC /* UserDefaultsExtension.swift */, ); name = Extensions; path = ../../Sources/Extensions; sourceTree = ""; }; - 8E171DD3215B26B4006FBBCC /* Protocols */ = { - isa = PBXGroup; - children = ( - 8E171DD4215B26B4006FBBCC /* SirenDelegate.swift */, - ); - name = Protocols; - path = ../../Sources/Protocols; - sourceTree = ""; - }; 8E171DD5215B26B4006FBBCC /* Models */ = { isa = PBXGroup; children = ( - 8E171DD6215B26B4006FBBCC /* SirenAlertMessaging.swift */, - 8E171DD7215B26B4006FBBCC /* SirenLookupModel.swift */, + 8E01818121B37B43006DED05 /* AlertAction.swift */, + 8E5061EA21C9CDF000A28DE0 /* AlertConstants.swift */, + 8E171DE5215B2701006FBBCC /* Localization.swift */, + 8E171DD7215B26B4006FBBCC /* LookupModel.swift */, + 8E01817821B379AF006DED05 /* Results.swift */, + 4D729C4521A213B4002F73AB /* Rules.swift */, ); name = Models; path = ../../Sources/Models; @@ -367,6 +377,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 4D729C4421A20EDC002F73AB /* Siren.bundle in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -404,20 +415,23 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 8E171DE1215B26B4006FBBCC /* SirenAlertMessaging.swift in Sources */, - 8E171DEE215B2D09006FBBCC /* SirenUserDefaultsExtension.swift in Sources */, - 8E171DE2215B26B4006FBBCC /* SirenLookupModel.swift in Sources */, - 8ED73400215DBE4900447E3B /* SirenHelpers.swift in Sources */, + 8E01818021B379DD006DED05 /* RulesManager.swift in Sources */, + 8E01818221B37B43006DED05 /* AlertAction.swift in Sources */, + 8E171DEE215B2D09006FBBCC /* UserDefaultsExtension.swift in Sources */, + 8E171DE2215B26B4006FBBCC /* LookupModel.swift in Sources */, 55EC36611E6BB99B00726F13 /* Siren.swift in Sources */, - 8ED733FC215DBE4900447E3B /* SirenDelegate.swift in Sources */, - 8ED733FF215DBE4900447E3B /* SirenLog.swift in Sources */, + 8E5061EB21C9CDF000A28DE0 /* AlertConstants.swift in Sources */, + 8E01817921B379AF006DED05 /* Results.swift in Sources */, + 8EEF4AB121AA6DDF00C83AAA /* DataParser.swift in Sources */, + 4D729C4621A213B4002F73AB /* Rules.swift in Sources */, 8E171DE4215B26B4006FBBCC /* SirenViewController.swift in Sources */, - 8E171DE6215B2701006FBBCC /* SirenLocalizationExtension.swift in Sources */, - 8E171DDE215B26B4006FBBCC /* SirenBundleExtension.swift in Sources */, - 8ED733FD215DBE4900447E3B /* SirenConstants.swift in Sources */, - 8E171DE3215B26B4006FBBCC /* SirenUIAlertControllerExtension.swift in Sources */, - 8ED733FE215DBE4900447E3B /* SirenError.swift in Sources */, - 8E171DDF215B26B4006FBBCC /* SirenDateExtension.swift in Sources */, + 8E171DE6215B2701006FBBCC /* Localization.swift in Sources */, + 8EE1FAF521B4E6B8009112A8 /* APIManager.swift in Sources */, + 8E171DDE215B26B4006FBBCC /* BundleExtension.swift in Sources */, + 8E01817E21B379DD006DED05 /* PresentationManager.swift in Sources */, + 8E171DE3215B26B4006FBBCC /* UIAlertControllerExtension.swift in Sources */, + 8EE1FAF321B4DC0F009112A8 /* KnownError.swift in Sources */, + 8E171DDF215B26B4006FBBCC /* DateExtension.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Example/Example/AppDelegate.swift b/Example/Example/AppDelegate.swift index a7e8177e..82132f2c 100755 --- a/Example/Example/AppDelegate.swift +++ b/Example/Example/AppDelegate.swift @@ -6,108 +6,237 @@ // Copyright (c) 2015 Sabintsev iOS Projects. All rights reserved. // -import UIKit import Siren +import UIKit @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { - var window: UIWindow? func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool { window?.makeKeyAndVisible() - setupSiren() + /// - Warning: + /// Siren should ONLY be placed in UIApplication.didFinishLaunchingWithOptionsand only after the `window?.makeKeyAndVisible()` call. + /// Siren initializes a listener on `didBecomeActiveNotification` to perform version checks. + +// defaultExample() + defaultExampleUsingCompletionHandler() +// minimalCustomizationPresentationExample() +// forceLocalizationCustomizationPresentationExample() +// customMessagingPresentationExample() +// annoyingRuleExample() +// hyperCriticalRulesExample() +// updateSpecificRulesExample() +// customAlertRulesExample() +// appStoreCountryChangeExample() +// complexExample() return true } - - func setupSiren() { - let siren = Siren.shared - - // Optional - siren.delegate = self - - // Optional - siren.debugEnabled = true - - // Optional - Change the name of your app. Useful if you have a long app name and want to display a shortened version in the update dialog (e.g., the UIAlertController). -// siren.appName = "Test App Name" - - // Optional - Change the various UIAlertController and UIAlertAction messaging. One or more values can be changes. If only a subset of values are changed, the defaults with which Siren comes with will be used. -// siren.alertMessaging = SirenAlertMessaging(updateTitle: NSAttributedString(string: "New Fancy Title"), -// updateMessage: NSAttributedString(string: "New message goes here!"), -// updateButtonMessage: NSAttributedString(string: "Update Now, Plz!?"), -// nextTimeButtonMessage: NSAttributedString(string: "OK, next time it is!"), -// skipVersionButtonMessage: NSAttributedString(string: "Please don't push skip, please don't!")) - - // 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 - siren.patchUpdateAlertType = .option - siren.revisionUpdateAlertType = .option - - // Optional - Sets all messages to appear in Russian. Siren supports many other languages, not just English and Russian. -// siren.forceLanguageLocalization = .russian +} - // Optional - Set this variable if your app is not available in the U.S. App Store. List of codes: https://developer.apple.com/library/content/documentation/LanguagesUtilities/Conceptual/iTunesConnect_Guide/Chapters/AppStoreTerritories.html -// siren.countryCode = "" +// Examples on how to use Siren - // Optional - Set this variable if you would only like to show an alert if your app has been available on the store for a few days. - // This default value is set to 1 to avoid this issue: https://github.com/ArtSabintsev/Siren#words-of-caution - // To show the update immediately after Apple has updated their JSON, set this value to 0. Not recommended due to aforementioned reason in https://github.com/ArtSabintsev/Siren#words-of-caution. - siren.showAlertAfterCurrentVersionHasBeenReleasedForDays = 0 +private extension AppDelegate { - // Optional (Only do this if you don't call checkVersion in didBecomeActive) -// siren.checkVersion(checkType: .immediately) + /// The simplest implementation of Siren. + /// All default rules are implemented and the + /// results of the completion handler are ignored. + func defaultExample() { + Siren.shared.wail() } - func applicationWillEnterForeground(_ application: UIApplication) { - // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. - Siren.shared.checkVersion(checkType: .daily) + /// The simplest implementation of Siren. + /// All default rules are implemented and the + /// results of the completion handler are returned or an error is returned. + func defaultExampleUsingCompletionHandler() { + Siren.shared.wail { (results, error) in + if let results = results { + print("AlertAction ", results.alertAction) + print("Localization ", results.localization) + print("LookupModel ", results.lookupModel) + print("UpdateType ", results.updateType) + } else if let error = error { + print(error.localizedDescription) + } + } } - func applicationDidBecomeActive(_ application: UIApplication) { - // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. - Siren.shared.checkVersion(checkType: .daily) + /// Minor customization to Siren's update alert presentation. + func minimalCustomizationPresentationExample() { + let siren = Siren.shared + siren.presentationManager = PresentationManager(alertTintColor: .purple, + appName: "Siren Example App Override!") + siren.wail { (results, error) in + if let results = results { + print("AlertAction ", results.alertAction) + print("Localization ", results.localization) + print("LookupModel ", results.lookupModel) + print("UpdateType ", results.updateType) + } else if let error = error { + print(error.localizedDescription) + } + } } -} -extension AppDelegate: SirenDelegate -{ - func sirenDidShowUpdateDialog(alertType: Siren.AlertType) { - print(#function, alertType) + /// Forcing the language of the update alert to a specific localization (e.g., Russian is force in this function. + func forceLocalizationCustomizationPresentationExample() { + let siren = Siren.shared + siren.presentationManager = PresentationManager(forceLanguageLocalization: .russian) + siren.wail { (results, error) in + if let results = results { + print("AlertAction ", results.alertAction) + print("Localization ", results.localization) + print("LookupModel ", results.lookupModel) + print("UpdateType ", results.updateType) + } else if let error = error { + print(error.localizedDescription) + } + } } - - func sirenUserDidCancel() { - print(#function) + + /// Example on how to change specific strings in the update alert. + func customMessagingPresentationExample() { + let siren = Siren.shared + siren.presentationManager = PresentationManager(alertTitle: "Update Now, OK?", + nextTimeButtonTitle: "Next time, please!?") + siren.wail { (results, error) in + if let results = results { + print("AlertAction ", results.alertAction) + print("Localization ", results.localization) + print("LookupModel ", results.lookupModel) + print("UpdateType ", results.updateType) + } else if let error = error { + print(error.localizedDescription) + } + } } - - func sirenUserDidSkipVersion() { - print(#function) + + /// How to present an alert every time the app is foregrounded. + func annoyingRuleExample() { + let siren = Siren.shared + siren.rulesManager = RulesManager(globalRules: .annoying) + + siren.wail { (results, error) in + if let results = results { + print("AlertAction ", results.alertAction) + print("Localization ", results.localization) + print("LookupModel ", results.lookupModel) + print("UpdateType ", results.updateType) + } else if let error = error { + print(error.localizedDescription) + } + } } - - func sirenUserDidLaunchAppStore() { - print(#function) + + /// How to present an alert every time the app is foregrounded. + /// This will block the user from using the app until they update the app. + /// Setting `showAlertAfterCurrentVersionHasBeenReleasedForDays` to `0` IS NOT RECOMMENDED + /// as it will cause the user to go into an endless loop to the App Store if the JSON results + /// update faster than the App Store CDN. + /// + /// The `0` value is illustrated in this app as an example on how to change how quickly an alert is presented. + func hyperCriticalRulesExample() { + let siren = Siren.shared + siren.rulesManager = RulesManager(globalRules: .critical, + showAlertAfterCurrentVersionHasBeenReleasedForDays: 0) + + siren.wail { (results, error) in + if let results = results { + print("AlertAction ", results.alertAction) + print("Localization ", results.localization) + print("LookupModel ", results.lookupModel) + print("UpdateType ", results.updateType) + } else if let error = error { + print(error.localizedDescription) + } + } } - func sirenDidFailVersionCheck(error: Error) { - print(#function, error) + /// Major, Minor, Patch, and Revision specific rules implementations. + func updateSpecificRulesExample() { + let siren = Siren.shared + siren.rulesManager = RulesManager(majorUpdateRules: .annoying, + minorUpdateRules: .default, + patchUpdateRules: .critical, + revisionUpdateRules: Rules(promptFrequency: .weekly, forAlertType: .option)) + + siren.wail { (results, error) in + if let results = results { + print("AlertAction ", results.alertAction) + print("Localization ", results.localization) + print("LookupModel ", results.lookupModel) + print("UpdateType ", results.updateType) + } else if let error = error { + print(error.localizedDescription) + } + } } - func sirenLatestVersionInstalled() { - print(#function, "Latest version of app is installed") + /// An example on how to present your own custom alert using Siren's localized Strings and version checking cadence. + func customAlertRulesExample() { + let siren = Siren.shared + // The key for using custom alerts is to set the `alertType` to `.none`. + // The `Results` type will return localized strings for your app's custom modal presentation. + // The `promptFrequency` allows you to customize how often Siren performs the version check before returning a non-error result back into your app, prompting your custom alert functionality. + let rules = Rules(promptFrequency: .immediately, forAlertType: .none) + siren.rulesManager = RulesManager(globalRules: rules) + + siren.wail { (results, error) in + if let results = results { + print("USE THE VALUES FROM THE `RESULTS` DATA STRUCTURE TO BUILD YOUR UPDATE ALERT WITH LOCALIZED STRINGS.") + print("AlertAction ", results.alertAction) + print("Localization ", results.localization) + print("LookupModel ", results.lookupModel) + print("UpdateType ", results.updateType) + } else if let error = error { + print(error.localizedDescription) + } + } } - func sirenNetworkCallDidReturnWithNewVersionInformation(lookupModel: SirenLookupModel) { - print(#function, "\(lookupModel)") + /// An example on how to change the App Store region that your app in which your app is available. + // This should only be used if your app is not available in the US App Store. + // This example function illustrates how this can be done by checking against the Russian App Store. + func appStoreCountryChangeExample() { + let siren = Siren.shared + siren.apiManager = APIManager(countryCode: "RU") + + siren.wail { (results, error) in + if let results = results { + print("AlertAction ", results.alertAction) + print("Localization ", results.localization) + print("LookupModel ", results.lookupModel) + print("UpdateType ", results.updateType) + } else if let error = error { + print(error.localizedDescription) + } + } } - // This delegate method is only hit when alertType is initialized to .none - func sirenDidDetectNewVersionWithoutAlert(title: String, message: String, updateType: UpdateType) { - print(#function, "\n\(title)\n\(message).\nRelease type: \(updateType.rawValue.capitalized)") + /// An example on how to customize multiple managers at once. + func complexExample() { + let siren = Siren.shared + siren.presentationManager = PresentationManager(alertTintColor: .brown, + appName: "Siren's Complex Rule Example App", + alertTitle: "Please, Update Now!", + skipButtonTitle: "Click here to skip!", + forceLanguageLocalization: .spanish) + siren.rulesManager = RulesManager(majorUpdateRules: .annoying, + minorUpdateRules: .default, + patchUpdateRules: .critical, + revisionUpdateRules: .relaxed) + + siren.wail { (results, error) in + if let results = results { + print("AlertAction ", results.alertAction) + print("Localization ", results.localization) + print("LookupModel ", results.lookupModel) + print("UpdateType ", results.updateType) + } else if let error = error { + print(error.localizedDescription) + } + } } } diff --git a/Example/Tests/SirenTests.swift b/Example/Tests/SirenTests.swift index 7c53a42e..652b4980 100644 --- a/Example/Tests/SirenTests.swift +++ b/Example/Tests/SirenTests.swift @@ -9,9 +9,9 @@ import XCTest @testable import Siren -class SirenTests: XCTestCase { +final class SirenTests: XCTestCase { - let siren = Siren.shared + var siren: Siren = Siren.shared } @@ -20,819 +20,776 @@ class SirenTests: XCTestCase { extension SirenTests { func testSingleDigitVersionUpdate() { - siren.testSetCurrentInstalledVersion(version: "1") + siren.currentInstalledVersion = "1" - siren.testSetAppStoreVersion(version: "2") - XCTAssertTrue(siren.isAppStoreVersionNewer()) + XCTAssertTrue(DataParser.isAppStoreVersionNewer(installedVersion: siren.currentInstalledVersion, + appStoreVersion: "2")) - siren.testSetAppStoreVersion(version: "2.0") - XCTAssertTrue(siren.isAppStoreVersionNewer()) + XCTAssertTrue(DataParser.isAppStoreVersionNewer(installedVersion: siren.currentInstalledVersion, + appStoreVersion: "2.0")) - siren.testSetAppStoreVersion(version: "2.0.0") - XCTAssertTrue(siren.isAppStoreVersionNewer()) + XCTAssertTrue(DataParser.isAppStoreVersionNewer(installedVersion: siren.currentInstalledVersion, + appStoreVersion: "2.0.0")) - siren.testSetAppStoreVersion(version: "2.0.0.0") - XCTAssertTrue(siren.isAppStoreVersionNewer()) + XCTAssertTrue(DataParser.isAppStoreVersionNewer(installedVersion: siren.currentInstalledVersion, + appStoreVersion: "2.0.0.0")) - siren.testSetAppStoreVersion(version: "0") - XCTAssertFalse(siren.isAppStoreVersionNewer()) + XCTAssertFalse(DataParser.isAppStoreVersionNewer(installedVersion: siren.currentInstalledVersion, + appStoreVersion: "0")) - siren.testSetAppStoreVersion(version: "0.9") - XCTAssertFalse(siren.isAppStoreVersionNewer()) + XCTAssertFalse(DataParser.isAppStoreVersionNewer(installedVersion: siren.currentInstalledVersion, + appStoreVersion: "0.9")) - siren.testSetAppStoreVersion(version: "0.0.9") - XCTAssertFalse(siren.isAppStoreVersionNewer()) + XCTAssertFalse(DataParser.isAppStoreVersionNewer(installedVersion: siren.currentInstalledVersion, + appStoreVersion: "0.0.9")) - siren.testSetAppStoreVersion(version: "0.0.0.9") - XCTAssertFalse(siren.isAppStoreVersionNewer()) + XCTAssertFalse(DataParser.isAppStoreVersionNewer(installedVersion: siren.currentInstalledVersion, + appStoreVersion: "0.0.0.9")) } func testDoubleDigitVersionUpdate() { - siren.testSetCurrentInstalledVersion(version: "1.0") + siren.currentInstalledVersion = "1.0" - siren.testSetAppStoreVersion(version: "2") - XCTAssertTrue(siren.isAppStoreVersionNewer()) + XCTAssertTrue(DataParser.isAppStoreVersionNewer(installedVersion: siren.currentInstalledVersion, + appStoreVersion: "2")) - siren.testSetAppStoreVersion(version: "2.0") - XCTAssertTrue(siren.isAppStoreVersionNewer()) + XCTAssertTrue(DataParser.isAppStoreVersionNewer(installedVersion: siren.currentInstalledVersion, + appStoreVersion: "2.0")) - siren.testSetAppStoreVersion(version: "2.0.0") - XCTAssertTrue(siren.isAppStoreVersionNewer()) + XCTAssertTrue(DataParser.isAppStoreVersionNewer(installedVersion: siren.currentInstalledVersion, + appStoreVersion: "2.0.0")) - siren.testSetAppStoreVersion(version: "2.0.0.0") - XCTAssertTrue(siren.isAppStoreVersionNewer()) + XCTAssertTrue(DataParser.isAppStoreVersionNewer(installedVersion: siren.currentInstalledVersion, + appStoreVersion: "2.0.0.0")) - siren.testSetAppStoreVersion(version: "0") - XCTAssertFalse(siren.isAppStoreVersionNewer()) + XCTAssertFalse(DataParser.isAppStoreVersionNewer(installedVersion: siren.currentInstalledVersion, + appStoreVersion: "0")) - siren.testSetAppStoreVersion(version: "0.9") - XCTAssertFalse(siren.isAppStoreVersionNewer()) + XCTAssertFalse(DataParser.isAppStoreVersionNewer(installedVersion: siren.currentInstalledVersion, + appStoreVersion: "0.9")) - siren.testSetAppStoreVersion(version: "0.0.9") - XCTAssertFalse(siren.isAppStoreVersionNewer()) + XCTAssertFalse(DataParser.isAppStoreVersionNewer(installedVersion: siren.currentInstalledVersion, + appStoreVersion: "0.0.9")) - siren.testSetAppStoreVersion(version: "0.0.0.9") - XCTAssertFalse(siren.isAppStoreVersionNewer()) + XCTAssertFalse(DataParser.isAppStoreVersionNewer(installedVersion: siren.currentInstalledVersion, + appStoreVersion: "0.0.0.9")) } func testTripleDigitVersionUpdate() { - siren.testSetCurrentInstalledVersion(version: "1.0.0") + siren.currentInstalledVersion = "1.0.0" - siren.testSetAppStoreVersion(version: "2") - XCTAssertTrue(siren.isAppStoreVersionNewer()) + XCTAssertTrue(DataParser.isAppStoreVersionNewer(installedVersion: siren.currentInstalledVersion, + appStoreVersion: "2")) - siren.testSetAppStoreVersion(version: "2.0") - XCTAssertTrue(siren.isAppStoreVersionNewer()) + XCTAssertTrue(DataParser.isAppStoreVersionNewer(installedVersion: siren.currentInstalledVersion, + appStoreVersion: "2.0")) - siren.testSetAppStoreVersion(version: "2.0.0") - XCTAssertTrue(siren.isAppStoreVersionNewer()) + XCTAssertTrue(DataParser.isAppStoreVersionNewer(installedVersion: siren.currentInstalledVersion, + appStoreVersion: "2.0.0")) - siren.testSetAppStoreVersion(version: "2.0.0.0") - XCTAssertTrue(siren.isAppStoreVersionNewer()) + XCTAssertTrue(DataParser.isAppStoreVersionNewer(installedVersion: siren.currentInstalledVersion, + appStoreVersion: "2.0.0.0")) - siren.testSetAppStoreVersion(version: "0") - XCTAssertFalse(siren.isAppStoreVersionNewer()) + XCTAssertFalse(DataParser.isAppStoreVersionNewer(installedVersion: siren.currentInstalledVersion, + appStoreVersion: "0")) - siren.testSetAppStoreVersion(version: "0.9") - XCTAssertFalse(siren.isAppStoreVersionNewer()) + XCTAssertFalse(DataParser.isAppStoreVersionNewer(installedVersion: siren.currentInstalledVersion, + appStoreVersion: "0.9")) - siren.testSetAppStoreVersion(version: "0.0.9") - XCTAssertFalse(siren.isAppStoreVersionNewer()) + XCTAssertFalse(DataParser.isAppStoreVersionNewer(installedVersion: siren.currentInstalledVersion, + appStoreVersion: "0.0.9")) - siren.testSetAppStoreVersion(version: "0.0.0.9") - XCTAssertFalse(siren.isAppStoreVersionNewer()) + XCTAssertFalse(DataParser.isAppStoreVersionNewer(installedVersion: siren.currentInstalledVersion, + appStoreVersion: "0.0.0.9")) } func testQuadrupleDigitVersionUpdate() { - siren.testSetCurrentInstalledVersion(version: "1.0.0") + siren.currentInstalledVersion = "1.0.0" - siren.testSetAppStoreVersion(version: "2") - XCTAssertTrue(siren.isAppStoreVersionNewer()) + XCTAssertTrue(DataParser.isAppStoreVersionNewer(installedVersion: siren.currentInstalledVersion, + appStoreVersion: "2")) - siren.testSetAppStoreVersion(version: "2.0") - XCTAssertTrue(siren.isAppStoreVersionNewer()) + XCTAssertTrue(DataParser.isAppStoreVersionNewer(installedVersion: siren.currentInstalledVersion, + appStoreVersion: "2.0")) - siren.testSetAppStoreVersion(version: "2.0.0") - XCTAssertTrue(siren.isAppStoreVersionNewer()) + XCTAssertTrue(DataParser.isAppStoreVersionNewer(installedVersion: siren.currentInstalledVersion, + appStoreVersion: "2.0.0")) - siren.testSetAppStoreVersion(version: "2.0.0.0") - XCTAssertTrue(siren.isAppStoreVersionNewer()) + XCTAssertTrue(DataParser.isAppStoreVersionNewer(installedVersion: siren.currentInstalledVersion, + appStoreVersion: "2.0.0.0")) - siren.testSetAppStoreVersion(version: "0") - XCTAssertFalse(siren.isAppStoreVersionNewer()) + XCTAssertFalse(DataParser.isAppStoreVersionNewer(installedVersion: siren.currentInstalledVersion, + appStoreVersion: "0")) - siren.testSetAppStoreVersion(version: "0.9") - XCTAssertFalse(siren.isAppStoreVersionNewer()) + XCTAssertFalse(DataParser.isAppStoreVersionNewer(installedVersion: siren.currentInstalledVersion, + appStoreVersion: "0.9")) - siren.testSetAppStoreVersion(version: "0.0.9") - XCTAssertFalse(siren.isAppStoreVersionNewer()) + XCTAssertFalse(DataParser.isAppStoreVersionNewer(installedVersion: siren.currentInstalledVersion, + appStoreVersion: "0.0.9")) - siren.testSetAppStoreVersion(version: "0.0.0.9") - XCTAssertFalse(siren.isAppStoreVersionNewer()) + XCTAssertFalse(DataParser.isAppStoreVersionNewer(installedVersion: siren.currentInstalledVersion, + appStoreVersion: "0.0.0.9")) } - } - // MARK: - Localization extension SirenTests { func testArabicLocalization() { - let language: Siren.LanguageType = .arabic - siren.forceLanguageLocalization = language + let language: Localization.Language = .arabic // Update Available - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Update Available", forceLanguageLocalization: language), "التحديث متوفر") + XCTAssertEqual(Bundle.localizedString(forKey: "Update Available", andForceLocalization: language), "التحديث متوفر") // Next time - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Next time", forceLanguageLocalization: language), "المرة التالية") + XCTAssertEqual(Bundle.localizedString(forKey: "Next time", andForceLocalization: language), "المرة التالية") // Skip this version - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Skip this version", forceLanguageLocalization: language), "تخطى عن هذه النسخة") + XCTAssertEqual(Bundle.localizedString(forKey: "Skip this version", andForceLocalization: language), "تخطى عن هذه النسخة") // Update - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Update", forceLanguageLocalization: language), "تحديث") + XCTAssertEqual(Bundle.localizedString(forKey: "Update", andForceLocalization: language), "تحديث") } func testArmenianLocalization() { - let language: Siren.LanguageType = .armenian - siren.forceLanguageLocalization = language + let language: Localization.Language = .armenian // Update Available - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Update Available", forceLanguageLocalization: language), "Թարմացումը հասանելի Է") + XCTAssertEqual(Bundle.localizedString(forKey: "Update Available", andForceLocalization: language), "Թարմացումը հասանելի Է") // Next time - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Next time", forceLanguageLocalization: language), "Հաջորդ անգամ") + XCTAssertEqual(Bundle.localizedString(forKey: "Next time", andForceLocalization: language), "Հաջորդ անգամ") // Skip this version - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Skip this version", forceLanguageLocalization: language), "Բաց թողնել այս տարբերակը") + XCTAssertEqual(Bundle.localizedString(forKey: "Skip this version", andForceLocalization: language), "Բաց թողնել այս տարբերակը") // Update - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Update", forceLanguageLocalization: language), "Թարմացնել") + XCTAssertEqual(Bundle.localizedString(forKey: "Update", andForceLocalization: language), "Թարմացնել") } func testBasqueLocalization() { - let language: Siren.LanguageType = .basque - siren.forceLanguageLocalization = language + let language: Localization.Language = .basque // Update Available - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Update Available", forceLanguageLocalization: language), "Eguneratzea erabilgarri") + XCTAssertEqual(Bundle.localizedString(forKey: "Update Available", andForceLocalization: language), "Eguneratzea erabilgarri") // Next time - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Next time", forceLanguageLocalization: language), "Hurrengo batean") + XCTAssertEqual(Bundle.localizedString(forKey: "Next time", andForceLocalization: language), "Hurrengo batean") // Skip this version - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Skip this version", forceLanguageLocalization: language), "Bertsio honetatik jauzi egin") + XCTAssertEqual(Bundle.localizedString(forKey: "Skip this version", andForceLocalization: language), "Bertsio honetatik jauzi egin") // Update - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Update", forceLanguageLocalization: language), "Eguneratu") + XCTAssertEqual(Bundle.localizedString(forKey: "Update", andForceLocalization: language), "Eguneratu") } func testChineseSimplifiedLocalization() { - let language: Siren.LanguageType = .chineseSimplified - siren.forceLanguageLocalization = language + let language: Localization.Language = .chineseSimplified // Update Available - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Update Available", forceLanguageLocalization: language), "更新可用") + XCTAssertEqual(Bundle.localizedString(forKey: "Update Available", andForceLocalization: language), "更新可用") // Next time - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Next time", forceLanguageLocalization: language), "下一次") + XCTAssertEqual(Bundle.localizedString(forKey: "Next time", andForceLocalization: language), "下一次") // Skip this version - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Skip this version", forceLanguageLocalization: language), "跳过此版本") + XCTAssertEqual(Bundle.localizedString(forKey: "Skip this version", andForceLocalization: language), "跳过此版本") // Update - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Update", forceLanguageLocalization: language), "更新") + XCTAssertEqual(Bundle.localizedString(forKey: "Update", andForceLocalization: language), "更新") } func testChineseTraditionalLocalization() { - let language: Siren.LanguageType = .chineseTraditional - siren.forceLanguageLocalization = language + let language: Localization.Language = .chineseTraditional // Update Available - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Update Available", forceLanguageLocalization: language), "有更新可用") + XCTAssertEqual(Bundle.localizedString(forKey: "Update Available", andForceLocalization: language), "有更新可用") // Next time - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Next time", forceLanguageLocalization: language), "下次") + XCTAssertEqual(Bundle.localizedString(forKey: "Next time", andForceLocalization: language), "下次") // Skip this version - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Skip this version", forceLanguageLocalization: language), "跳過此版本") + XCTAssertEqual(Bundle.localizedString(forKey: "Skip this version", andForceLocalization: language), "跳過此版本") // Update - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Update", forceLanguageLocalization: language), "更新") + XCTAssertEqual(Bundle.localizedString(forKey: "Update", andForceLocalization: language), "更新") } func testCroatianLocalization() { - let language: Siren.LanguageType = .croatian - siren.forceLanguageLocalization = language + let language: Localization.Language = .croatian // Update Available - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Update Available", forceLanguageLocalization: language), "Nova ažuriranje je stigla") + XCTAssertEqual(Bundle.localizedString(forKey: "Update Available", andForceLocalization: language), "Nova ažuriranje je stigla") // Next time - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Next time", forceLanguageLocalization: language), "Sljedeći put") + XCTAssertEqual(Bundle.localizedString(forKey: "Next time", andForceLocalization: language), "Sljedeći put") // Skip this version - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Skip this version", forceLanguageLocalization: language), "Preskoči ovu verziju") + XCTAssertEqual(Bundle.localizedString(forKey: "Skip this version", andForceLocalization: language), "Preskoči ovu verziju") // Update - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Update", forceLanguageLocalization: language), "Ažuriraj") + XCTAssertEqual(Bundle.localizedString(forKey: "Update", andForceLocalization: language), "Ažuriraj") } func testCzechLocalization() { - let language: Siren.LanguageType = .czech - siren.forceLanguageLocalization = language + let language: Localization.Language = .czech // Update Available - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Update Available", forceLanguageLocalization: language), "Aktualizace dostupná") + XCTAssertEqual(Bundle.localizedString(forKey: "Update Available", andForceLocalization: language), "Aktualizace dostupná") // Next time - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Next time", forceLanguageLocalization: language), "Příště") + XCTAssertEqual(Bundle.localizedString(forKey: "Next time", andForceLocalization: language), "Příště") // Skip this version - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Skip this version", forceLanguageLocalization: language), "Přeskočit tuto verzi") + XCTAssertEqual(Bundle.localizedString(forKey: "Skip this version", andForceLocalization: language), "Přeskočit tuto verzi") // Update - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Update", forceLanguageLocalization: language), "Aktualizovat") + XCTAssertEqual(Bundle.localizedString(forKey: "Update", andForceLocalization: language), "Aktualizovat") } func testDanishLocalization() { - let language: Siren.LanguageType = .danish - siren.forceLanguageLocalization = language + let language: Localization.Language = .danish // Update Available - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Update Available", forceLanguageLocalization: language), "Tilgængelig opdatering") + XCTAssertEqual(Bundle.localizedString(forKey: "Update Available", andForceLocalization: language), "Tilgængelig opdatering") // Next time - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Next time", forceLanguageLocalization: language), "Næste gang") + XCTAssertEqual(Bundle.localizedString(forKey: "Next time", andForceLocalization: language), "Næste gang") // Skip this version - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Skip this version", forceLanguageLocalization: language), "Spring denne version over") + XCTAssertEqual(Bundle.localizedString(forKey: "Skip this version", andForceLocalization: language), "Spring denne version over") // Update - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Update", forceLanguageLocalization: language), "Opdater") + XCTAssertEqual(Bundle.localizedString(forKey: "Update", andForceLocalization: language), "Opdater") } func testDutchLocalization() { - let language: Siren.LanguageType = .dutch - siren.forceLanguageLocalization = language + let language: Localization.Language = .dutch // Update Available - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Update Available", forceLanguageLocalization: language), "Update beschikbaar") + XCTAssertEqual(Bundle.localizedString(forKey: "Update Available", andForceLocalization: language), "Update beschikbaar") // Next time - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Next time", forceLanguageLocalization: language), "Volgende keer") + XCTAssertEqual(Bundle.localizedString(forKey: "Next time", andForceLocalization: language), "Volgende keer") // Skip this version - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Skip this version", forceLanguageLocalization: language), "Sla deze versie over") + XCTAssertEqual(Bundle.localizedString(forKey: "Skip this version", andForceLocalization: language), "Sla deze versie over") // Update - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Update", forceLanguageLocalization: language), "Updaten") + XCTAssertEqual(Bundle.localizedString(forKey: "Update", andForceLocalization: language), "Updaten") } func testEstonianLocalization() { - let language: Siren.LanguageType = .estonian - siren.forceLanguageLocalization = language + let language: Localization.Language = .estonian // Update Available - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Update Available", forceLanguageLocalization: language), "Uuendus saadaval") + XCTAssertEqual(Bundle.localizedString(forKey: "Update Available", andForceLocalization: language), "Uuendus saadaval") // Next time - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Next time", forceLanguageLocalization: language), "Järgmisel korral") + XCTAssertEqual(Bundle.localizedString(forKey: "Next time", andForceLocalization: language), "Järgmisel korral") // Skip this version - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Skip this version", forceLanguageLocalization: language), "Jäta see version vahele") + XCTAssertEqual(Bundle.localizedString(forKey: "Skip this version", andForceLocalization: language), "Jäta see version vahele") // Update - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Update", forceLanguageLocalization: language), "Uuenda") + XCTAssertEqual(Bundle.localizedString(forKey: "Update", andForceLocalization: language), "Uuenda") } func testFinnishLocalization() { - let language: Siren.LanguageType = .finnish - siren.forceLanguageLocalization = language + let language: Localization.Language = .finnish // Update Available - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Update Available", forceLanguageLocalization: language), "Päivitys saatavilla") + XCTAssertEqual(Bundle.localizedString(forKey: "Update Available", andForceLocalization: language), "Päivitys saatavilla") // Next time - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Next time", forceLanguageLocalization: language), "Ensi kerralla") + XCTAssertEqual(Bundle.localizedString(forKey: "Next time", andForceLocalization: language), "Ensi kerralla") // Skip this version - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Skip this version", forceLanguageLocalization: language), "Jätä tämä versio väliin") + XCTAssertEqual(Bundle.localizedString(forKey: "Skip this version", andForceLocalization: language), "Jätä tämä versio väliin") // Update - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Update", forceLanguageLocalization: language), "Päivitys") + XCTAssertEqual(Bundle.localizedString(forKey: "Update", andForceLocalization: language), "Päivitys") } func testFrenchLocalization() { - let language: Siren.LanguageType = .french - siren.forceLanguageLocalization = language + let language: Localization.Language = .french // Update Available - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Update Available", forceLanguageLocalization: language), "Mise à jour disponible") + XCTAssertEqual(Bundle.localizedString(forKey: "Update Available", andForceLocalization: language), "Mise à jour disponible") // Next time - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Next time", forceLanguageLocalization: language), "La prochaine fois") + XCTAssertEqual(Bundle.localizedString(forKey: "Next time", andForceLocalization: language), "La prochaine fois") // Skip this version - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Skip this version", forceLanguageLocalization: language), "Sauter cette version") + XCTAssertEqual(Bundle.localizedString(forKey: "Skip this version", andForceLocalization: language), "Sauter cette version") // Update - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Update", forceLanguageLocalization: language), "Mettre à jour") + XCTAssertEqual(Bundle.localizedString(forKey: "Update", andForceLocalization: language), "Mettre à jour") } func testGermanLocalization() { - let language: Siren.LanguageType = .german - siren.forceLanguageLocalization = language + let language: Localization.Language = .german // Update Available - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Update Available", forceLanguageLocalization: language), "Update erhältlich") + XCTAssertEqual(Bundle.localizedString(forKey: "Update Available", andForceLocalization: language), "Update erhältlich") // Next time - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Next time", forceLanguageLocalization: language), "Später") + XCTAssertEqual(Bundle.localizedString(forKey: "Next time", andForceLocalization: language), "Später") // Skip this version - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Skip this version", forceLanguageLocalization: language), "Diese Version überspringen") + XCTAssertEqual(Bundle.localizedString(forKey: "Skip this version", andForceLocalization: language), "Diese Version überspringen") // Update - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Update", forceLanguageLocalization: language), "Update") + XCTAssertEqual(Bundle.localizedString(forKey: "Update", andForceLocalization: language), "Update") } func testGreekLocalization() { - let language: Siren.LanguageType = .greek - siren.forceLanguageLocalization = language - + let language: Localization.Language = .greek + // Update Available - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Update Available", forceLanguageLocalization: language), "Διαθέσιμη Ενημέρωση") + XCTAssertEqual(Bundle.localizedString(forKey: "Update Available", andForceLocalization: language), "Διαθέσιμη Ενημέρωση") // Next time - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Next time", forceLanguageLocalization: language), "Άλλη φορά") + XCTAssertEqual(Bundle.localizedString(forKey: "Next time", andForceLocalization: language), "Άλλη φορά") // Skip this version - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Skip this version", forceLanguageLocalization: language), "Αγνόησε αυτήν την έκδοση") + XCTAssertEqual(Bundle.localizedString(forKey: "Skip this version", andForceLocalization: language), "Αγνόησε αυτήν την έκδοση") // Update - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Update", forceLanguageLocalization: language), "Αναβάθμιση") + XCTAssertEqual(Bundle.localizedString(forKey: "Update", andForceLocalization: language), "Αναβάθμιση") } func testHebrewLocalization() { - let language: Siren.LanguageType = .hebrew - siren.forceLanguageLocalization = language + let language: Localization.Language = .hebrew // Update Available - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Update Available", forceLanguageLocalization: language), "עדכון זמין") + XCTAssertEqual(Bundle.localizedString(forKey: "Update Available", andForceLocalization: language), "עדכון זמין") // Next time - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Next time", forceLanguageLocalization: language), "בפעם הבאה") + XCTAssertEqual(Bundle.localizedString(forKey: "Next time", andForceLocalization: language), "בפעם הבאה") // Skip this version - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Skip this version", forceLanguageLocalization: language), "דלג על גרסה זו") + XCTAssertEqual(Bundle.localizedString(forKey: "Skip this version", andForceLocalization: language), "דלג על גרסה זו") // Update - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Update", forceLanguageLocalization: language), "עדכן") + XCTAssertEqual(Bundle.localizedString(forKey: "Update", andForceLocalization: language), "עדכן") } func testHungarianLocalization() { - let language: Siren.LanguageType = .hungarian - siren.forceLanguageLocalization = language + let language: Localization.Language = .hungarian // Update Available - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Update Available", forceLanguageLocalization: language), "Új frissítés érhető el") + XCTAssertEqual(Bundle.localizedString(forKey: "Update Available", andForceLocalization: language), "Új frissítés érhető el") // Next time - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Next time", forceLanguageLocalization: language), "Később") + XCTAssertEqual(Bundle.localizedString(forKey: "Next time", andForceLocalization: language), "Később") // Skip this version - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Skip this version", forceLanguageLocalization: language), "Ennél a verziónál ne figyelmeztessen") + XCTAssertEqual(Bundle.localizedString(forKey: "Skip this version", andForceLocalization: language), "Ennél a verziónál ne figyelmeztessen") // Update - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Update", forceLanguageLocalization: language), "Frissítés") + XCTAssertEqual(Bundle.localizedString(forKey: "Update", andForceLocalization: language), "Frissítés") } func testIndonesianLocalization() { - let language: Siren.LanguageType = .indonesian - siren.forceLanguageLocalization = language + let language: Localization.Language = .indonesian // Update Available - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Update Available", forceLanguageLocalization: language), "Pembaruan Tersedia") + XCTAssertEqual(Bundle.localizedString(forKey: "Update Available", andForceLocalization: language), "Pembaruan Tersedia") // Next time - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Next time", forceLanguageLocalization: language), "Lain kali") + XCTAssertEqual(Bundle.localizedString(forKey: "Next time", andForceLocalization: language), "Lain kali") // Skip this version - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Skip this version", forceLanguageLocalization: language), "Lewati versi ini") + XCTAssertEqual(Bundle.localizedString(forKey: "Skip this version", andForceLocalization: language), "Lewati versi ini") // Update - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Update", forceLanguageLocalization: language), "Perbarui") + XCTAssertEqual(Bundle.localizedString(forKey: "Update", andForceLocalization: language), "Perbarui") } func testItalianLocalization() { - let language: Siren.LanguageType = .italian - siren.forceLanguageLocalization = language + let language: Localization.Language = .italian // Update Available - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Update Available", forceLanguageLocalization: language), "Aggiornamento disponibile") + XCTAssertEqual(Bundle.localizedString(forKey: "Update Available", andForceLocalization: language), "Aggiornamento disponibile") // Next time - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Next time", forceLanguageLocalization: language), "La prossima volta") + XCTAssertEqual(Bundle.localizedString(forKey: "Next time", andForceLocalization: language), "La prossima volta") // Skip this version - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Skip this version", forceLanguageLocalization: language), "Salta questa versione") + XCTAssertEqual(Bundle.localizedString(forKey: "Skip this version", andForceLocalization: language), "Salta questa versione") // Update - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Update", forceLanguageLocalization: language), "Aggiorna") + XCTAssertEqual(Bundle.localizedString(forKey: "Update", andForceLocalization: language), "Aggiorna") } func testJapaneseLocalization() { - let language: Siren.LanguageType = .japanese - siren.forceLanguageLocalization = language + let language: Localization.Language = .japanese // Update Available - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Update Available", forceLanguageLocalization: language), "アップデートのお知らせ") + XCTAssertEqual(Bundle.localizedString(forKey: "Update Available", andForceLocalization: language), "アップデートのお知らせ") // Next time - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Next time", forceLanguageLocalization: language), "次回") + XCTAssertEqual(Bundle.localizedString(forKey: "Next time", andForceLocalization: language), "次回") // Skip this version - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Skip this version", forceLanguageLocalization: language), "このバージョンをスキップ") + XCTAssertEqual(Bundle.localizedString(forKey: "Skip this version", andForceLocalization: language), "このバージョンをスキップ") // Update - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Update", forceLanguageLocalization: language), "アップデート") + XCTAssertEqual(Bundle.localizedString(forKey: "Update", andForceLocalization: language), "アップデート") } func testKoreanLocalization() { - let language: Siren.LanguageType = .korean - siren.forceLanguageLocalization = language + let language: Localization.Language = .korean // Update Available - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Update Available", forceLanguageLocalization: language), "업데이트 가능") + XCTAssertEqual(Bundle.localizedString(forKey: "Update Available", andForceLocalization: language), "업데이트 가능") // Next time - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Next time", forceLanguageLocalization: language), "다음에") + XCTAssertEqual(Bundle.localizedString(forKey: "Next time", andForceLocalization: language), "다음에") // Skip this version - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Skip this version", forceLanguageLocalization: language), "이 버전 건너뜀") + XCTAssertEqual(Bundle.localizedString(forKey: "Skip this version", andForceLocalization: language), "이 버전 건너뜀") // Update - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Update", forceLanguageLocalization: language), "업데이트") + XCTAssertEqual(Bundle.localizedString(forKey: "Update", andForceLocalization: language), "업데이트") } func testLatvianLocalization() { - let language: Siren.LanguageType = .latvian - siren.forceLanguageLocalization = language + let language: Localization.Language = .latvian // Update Available - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Update Available", forceLanguageLocalization: language), "Atjauninājums pieejams") + XCTAssertEqual(Bundle.localizedString(forKey: "Update Available", andForceLocalization: language), "Atjauninājums pieejams") // Next time - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Next time", forceLanguageLocalization: language), "Nākamreiz") + XCTAssertEqual(Bundle.localizedString(forKey: "Next time", andForceLocalization: language), "Nākamreiz") // Skip this version - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Skip this version", forceLanguageLocalization: language), "Izlaist šo versiju") + XCTAssertEqual(Bundle.localizedString(forKey: "Skip this version", andForceLocalization: language), "Izlaist šo versiju") // Update - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Update", forceLanguageLocalization: language), "Atjaunināt") + XCTAssertEqual(Bundle.localizedString(forKey: "Update", andForceLocalization: language), "Atjaunināt") } func testLithuanianLocalization() { - let language: Siren.LanguageType = .lithuanian - siren.forceLanguageLocalization = language + let language: Localization.Language = .lithuanian // Update Available - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Update Available", forceLanguageLocalization: language), "Atnaujinimas") + XCTAssertEqual(Bundle.localizedString(forKey: "Update Available", andForceLocalization: language), "Atnaujinimas") // Next time - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Next time", forceLanguageLocalization: language), "Kitą kartą") + XCTAssertEqual(Bundle.localizedString(forKey: "Next time", andForceLocalization: language), "Kitą kartą") // Skip this version - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Skip this version", forceLanguageLocalization: language), "Praleisti šią versiją") + XCTAssertEqual(Bundle.localizedString(forKey: "Skip this version", andForceLocalization: language), "Praleisti šią versiją") // Update - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Update", forceLanguageLocalization: language), "Atnaujinti") + XCTAssertEqual(Bundle.localizedString(forKey: "Update", andForceLocalization: language), "Atnaujinti") } func testMalayLocalization() { - let language: Siren.LanguageType = .malay - siren.forceLanguageLocalization = language + let language: Localization.Language = .malay // Update Available - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Update Available", forceLanguageLocalization: language), "Versi Terkini") + XCTAssertEqual(Bundle.localizedString(forKey: "Update Available", andForceLocalization: language), "Versi Terkini") // Next time - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Next time", forceLanguageLocalization: language), "Lain kali") + XCTAssertEqual(Bundle.localizedString(forKey: "Next time", andForceLocalization: language), "Lain kali") // Skip this version - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Skip this version", forceLanguageLocalization: language), "Langkau versi ini") + XCTAssertEqual(Bundle.localizedString(forKey: "Skip this version", andForceLocalization: language), "Langkau versi ini") // Update - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Update", forceLanguageLocalization: language), "Muat turun") + XCTAssertEqual(Bundle.localizedString(forKey: "Update", andForceLocalization: language), "Muat turun") } func testNorwegianLocalization() { - let language: Siren.LanguageType = .norwegian - siren.forceLanguageLocalization = language + let language: Localization.Language = .norwegian // Update Available - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Update Available", forceLanguageLocalization: language), "Oppdatering tilgjengelig") + XCTAssertEqual(Bundle.localizedString(forKey: "Update Available", andForceLocalization: language), "Oppdatering tilgjengelig") // Next time - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Next time", forceLanguageLocalization: language), "Neste gang") + XCTAssertEqual(Bundle.localizedString(forKey: "Next time", andForceLocalization: language), "Neste gang") // Skip this version - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Skip this version", forceLanguageLocalization: language), "Hopp over denne versjonen") + XCTAssertEqual(Bundle.localizedString(forKey: "Skip this version", andForceLocalization: language), "Hopp over denne versjonen") // Update - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Update", forceLanguageLocalization: language), "Oppdater") + XCTAssertEqual(Bundle.localizedString(forKey: "Update", andForceLocalization: language), "Oppdater") } func testPersianLocalization() { - let language: Siren.LanguageType = .persian - siren.forceLanguageLocalization = language - + let language: Localization.Language = .persian + // Update Available - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Update Available", forceLanguageLocalization: language), "بروزرسانی در دسترس") + XCTAssertEqual(Bundle.localizedString(forKey: "Update Available", andForceLocalization: language), "بروزرسانی در دسترس") // Next time - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Next time", forceLanguageLocalization: language), "دفعه بعد") + XCTAssertEqual(Bundle.localizedString(forKey: "Next time", andForceLocalization: language), "دفعه بعد") // Skip this version - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Skip this version", forceLanguageLocalization: language), "رد این نسخه") + XCTAssertEqual(Bundle.localizedString(forKey: "Skip this version", andForceLocalization: language), "رد این نسخه") // Update - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Update", forceLanguageLocalization: language), "بروزرسانی") + XCTAssertEqual(Bundle.localizedString(forKey: "Update", andForceLocalization: language), "بروزرسانی") } func testPersianAfghanistanLocalization() { - let language: Siren.LanguageType = .persianAfghanistan - siren.forceLanguageLocalization = language + let language: Localization.Language = .persianAfghanistan // Update Available - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Update Available", forceLanguageLocalization: language), "بروزرسانی در دسترس") + XCTAssertEqual(Bundle.localizedString(forKey: "Update Available", andForceLocalization: language), "بروزرسانی در دسترس") // Next time - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Next time", forceLanguageLocalization: language), "دگر بار") + XCTAssertEqual(Bundle.localizedString(forKey: "Next time", andForceLocalization: language), "دگر بار") // Skip this version - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Skip this version", forceLanguageLocalization: language), "رد این نسخه") + XCTAssertEqual(Bundle.localizedString(forKey: "Skip this version", andForceLocalization: language), "رد این نسخه") // Update - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Update", forceLanguageLocalization: language), "بروزرسانی") + XCTAssertEqual(Bundle.localizedString(forKey: "Update", andForceLocalization: language), "بروزرسانی") } func testPersianIranLocalization() { - let language: Siren.LanguageType = .persianIran - siren.forceLanguageLocalization = language - + let language: Localization.Language = .persianIran + // Update Available - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Update Available", forceLanguageLocalization: language), "بروزرسانی در دسترس") + XCTAssertEqual(Bundle.localizedString(forKey: "Update Available", andForceLocalization: language), "بروزرسانی در دسترس") // Next time - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Next time", forceLanguageLocalization: language), "دفعه بعد") + XCTAssertEqual(Bundle.localizedString(forKey: "Next time", andForceLocalization: language), "دفعه بعد") // Skip this version - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Skip this version", forceLanguageLocalization: language), "رد این نسخه") + XCTAssertEqual(Bundle.localizedString(forKey: "Skip this version", andForceLocalization: language), "رد این نسخه") // Update - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Update", forceLanguageLocalization: language), "بروزرسانی") + XCTAssertEqual(Bundle.localizedString(forKey: "Update", andForceLocalization: language), "بروزرسانی") } func testPolishLocalization() { - let language: Siren.LanguageType = .polish - siren.forceLanguageLocalization = language + let language: Localization.Language = .polish // Update Available - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Update Available", forceLanguageLocalization: language), "Aktualizacja dostępna") + XCTAssertEqual(Bundle.localizedString(forKey: "Update Available", andForceLocalization: language), "Aktualizacja dostępna") // Next time - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Next time", forceLanguageLocalization: language), "Następnym razem") + XCTAssertEqual(Bundle.localizedString(forKey: "Next time", andForceLocalization: language), "Następnym razem") // Skip this version - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Skip this version", forceLanguageLocalization: language), "Pomiń wersję") + XCTAssertEqual(Bundle.localizedString(forKey: "Skip this version", andForceLocalization: language), "Pomiń wersję") // Update - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Update", forceLanguageLocalization: language), "Zaktualizuj") + XCTAssertEqual(Bundle.localizedString(forKey: "Update", andForceLocalization: language), "Zaktualizuj") } func testPortugueseBrazilLocalization() { - let language: Siren.LanguageType = .portugueseBrazil - siren.forceLanguageLocalization = language + let language: Localization.Language = .portugueseBrazil // Update Available - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Update Available", forceLanguageLocalization: language), "Atualização disponível") + XCTAssertEqual(Bundle.localizedString(forKey: "Update Available", andForceLocalization: language), "Atualização disponível") // Next time - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Next time", forceLanguageLocalization: language), "Próxima vez") + XCTAssertEqual(Bundle.localizedString(forKey: "Next time", andForceLocalization: language), "Próxima vez") // Skip this version - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Skip this version", forceLanguageLocalization: language), "Ignorar esta versão") + XCTAssertEqual(Bundle.localizedString(forKey: "Skip this version", andForceLocalization: language), "Ignorar esta versão") // Update - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Update", forceLanguageLocalization: language), "Atualizar") + XCTAssertEqual(Bundle.localizedString(forKey: "Update", andForceLocalization: language), "Atualizar") } func testPortuguesePortugalLocalization() { - let language: Siren.LanguageType = .portuguesePortugal - siren.forceLanguageLocalization = language + let language: Localization.Language = .portuguesePortugal // Update Available - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Update Available", forceLanguageLocalization: language), "Nova actualização disponível") + XCTAssertEqual(Bundle.localizedString(forKey: "Update Available", andForceLocalization: language), "Nova actualização disponível") // Next time - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Next time", forceLanguageLocalization: language), "Próxima vez") + XCTAssertEqual(Bundle.localizedString(forKey: "Next time", andForceLocalization: language), "Próxima vez") // Skip this version - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Skip this version", forceLanguageLocalization: language), "Ignorar esta versão") + XCTAssertEqual(Bundle.localizedString(forKey: "Skip this version", andForceLocalization: language), "Ignorar esta versão") // Update - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Update", forceLanguageLocalization: language), "Actualizar") + XCTAssertEqual(Bundle.localizedString(forKey: "Update", andForceLocalization: language), "Actualizar") } func testRussianLocalization() { - let language: Siren.LanguageType = .russian - siren.forceLanguageLocalization = language + let language: Localization.Language = .russian // Update Available - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Update Available", forceLanguageLocalization: language), "Доступно обновление") + XCTAssertEqual(Bundle.localizedString(forKey: "Update Available", andForceLocalization: language), "Доступно обновление") // Next time - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Next time", forceLanguageLocalization: language), "В следующий раз") + XCTAssertEqual(Bundle.localizedString(forKey: "Next time", andForceLocalization: language), "В следующий раз") // Skip this version - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Skip this version", forceLanguageLocalization: language), "Пропустить эту версию") + XCTAssertEqual(Bundle.localizedString(forKey: "Skip this version", andForceLocalization: language), "Пропустить эту версию") // Update - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Update", forceLanguageLocalization: language), "Обновить") + XCTAssertEqual(Bundle.localizedString(forKey: "Update", andForceLocalization: language), "Обновить") } func testSerbianCyrillicLocalization() { - let language: Siren.LanguageType = .serbianCyrillic - siren.forceLanguageLocalization = language + let language: Localization.Language = .serbianCyrillic // Update Available - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Update Available", forceLanguageLocalization: language), "Ажурирање доступно") + XCTAssertEqual(Bundle.localizedString(forKey: "Update Available", andForceLocalization: language), "Ажурирање доступно") // Next time - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Next time", forceLanguageLocalization: language), "Следећи пут") + XCTAssertEqual(Bundle.localizedString(forKey: "Next time", andForceLocalization: language), "Следећи пут") // Skip this version - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Skip this version", forceLanguageLocalization: language), "Прескочи ову верзију") + XCTAssertEqual(Bundle.localizedString(forKey: "Skip this version", andForceLocalization: language), "Прескочи ову верзију") // Update - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Update", forceLanguageLocalization: language), "Ажурирај") + XCTAssertEqual(Bundle.localizedString(forKey: "Update", andForceLocalization: language), "Ажурирај") } func testSerbianLatinLocalization() { - let language: Siren.LanguageType = .serbianLatin - siren.forceLanguageLocalization = language + let language: Localization.Language = .serbianLatin // Update Available - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Update Available", forceLanguageLocalization: language), "Ažuriranje dostupno") + XCTAssertEqual(Bundle.localizedString(forKey: "Update Available", andForceLocalization: language), "Ažuriranje dostupno") // Next time - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Next time", forceLanguageLocalization: language), "Sledeći put") + XCTAssertEqual(Bundle.localizedString(forKey: "Next time", andForceLocalization: language), "Sledeći put") // Skip this version - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Skip this version", forceLanguageLocalization: language), "Preskoči ovu verziju") + XCTAssertEqual(Bundle.localizedString(forKey: "Skip this version", andForceLocalization: language), "Preskoči ovu verziju") // Update - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Update", forceLanguageLocalization: language), "Ažuriraj") + XCTAssertEqual(Bundle.localizedString(forKey: "Update", andForceLocalization: language), "Ažuriraj") } func testSlovenianLocalization() { - let language: Siren.LanguageType = .slovenian - siren.forceLanguageLocalization = language + let language: Localization.Language = .slovenian // Update Available - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Update Available", forceLanguageLocalization: language), "Posodobitev aplikacije") + XCTAssertEqual(Bundle.localizedString(forKey: "Update Available", andForceLocalization: language), "Posodobitev aplikacije") // Next time - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Next time", forceLanguageLocalization: language), "Naslednjič") + XCTAssertEqual(Bundle.localizedString(forKey: "Next time", andForceLocalization: language), "Naslednjič") // Skip this version - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Skip this version", forceLanguageLocalization: language), "Ne želim") + XCTAssertEqual(Bundle.localizedString(forKey: "Skip this version", andForceLocalization: language), "Ne želim") // Update - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Update", forceLanguageLocalization: language), "Namesti") + XCTAssertEqual(Bundle.localizedString(forKey: "Update", andForceLocalization: language), "Namesti") } func testSpanishLocalization() { - let language: Siren.LanguageType = .spanish - siren.forceLanguageLocalization = language + let language: Localization.Language = .spanish // Update Available - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Update Available", forceLanguageLocalization: language), "Actualización disponible") + XCTAssertEqual(Bundle.localizedString(forKey: "Update Available", andForceLocalization: language), "Actualización disponible") // Next time - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Next time", forceLanguageLocalization: language), "La próxima vez") + XCTAssertEqual(Bundle.localizedString(forKey: "Next time", andForceLocalization: language), "La próxima vez") // Skip this version - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Skip this version", forceLanguageLocalization: language), "Saltar esta versión") + XCTAssertEqual(Bundle.localizedString(forKey: "Skip this version", andForceLocalization: language), "Saltar esta versión") // Update - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Update", forceLanguageLocalization: language), "Actualizar") + XCTAssertEqual(Bundle.localizedString(forKey: "Update", andForceLocalization: language), "Actualizar") } func testSwedishLocalization() { - let language: Siren.LanguageType = .swedish - siren.forceLanguageLocalization = language + let language: Localization.Language = .swedish // Update Available - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Update Available", forceLanguageLocalization: language), "Tillgänglig uppdatering") + XCTAssertEqual(Bundle.localizedString(forKey: "Update Available", andForceLocalization: language), "Tillgänglig uppdatering") // Next time - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Next time", forceLanguageLocalization: language), "Nästa gång") + XCTAssertEqual(Bundle.localizedString(forKey: "Next time", andForceLocalization: language), "Nästa gång") // Skip this version - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Skip this version", forceLanguageLocalization: language), "Hoppa över den här versionen") + XCTAssertEqual(Bundle.localizedString(forKey: "Skip this version", andForceLocalization: language), "Hoppa över den här versionen") // Update - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Update", forceLanguageLocalization: language), "Uppdatera") + XCTAssertEqual(Bundle.localizedString(forKey: "Update", andForceLocalization: language), "Uppdatera") } func testThaiLocalization() { - let language: Siren.LanguageType = .thai - siren.forceLanguageLocalization = language + let language: Localization.Language = .thai // Update Available - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Update Available", forceLanguageLocalization: language), "มีการอัพเดท") + XCTAssertEqual(Bundle.localizedString(forKey: "Update Available", andForceLocalization: language), "มีการอัพเดท") // Next time - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Next time", forceLanguageLocalization: language), "ไว้คราวหน้า") + XCTAssertEqual(Bundle.localizedString(forKey: "Next time", andForceLocalization: language), "ไว้คราวหน้า") // Skip this version - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Skip this version", forceLanguageLocalization: language), "ข้ามเวอร์ชั่นนี้") + XCTAssertEqual(Bundle.localizedString(forKey: "Skip this version", andForceLocalization: language), "ข้ามเวอร์ชั่นนี้") // Update - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Update", forceLanguageLocalization: language), "อัพเดท") + XCTAssertEqual(Bundle.localizedString(forKey: "Update", andForceLocalization: language), "อัพเดท") } func testTurkishLocalization() { - let language: Siren.LanguageType = .turkish - siren.forceLanguageLocalization = language + let language: Localization.Language = .turkish // Update Available - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Update Available", forceLanguageLocalization: language), "Güncelleme Mevcut") + XCTAssertEqual(Bundle.localizedString(forKey: "Update Available", andForceLocalization: language), "Güncelleme Mevcut") // Next time - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Next time", forceLanguageLocalization: language), "Daha sonra") + XCTAssertEqual(Bundle.localizedString(forKey: "Next time", andForceLocalization: language), "Daha sonra") // Skip this version - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Skip this version", forceLanguageLocalization: language), "Boşver") + XCTAssertEqual(Bundle.localizedString(forKey: "Skip this version", andForceLocalization: language), "Boşver") // Update - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Update", forceLanguageLocalization: language), "Güncelle") + XCTAssertEqual(Bundle.localizedString(forKey: "Update", andForceLocalization: language), "Güncelle") } func testUkrainianLocalization() { - let language: Siren.LanguageType = .ukrainian - siren.forceLanguageLocalization = language - + let language: Localization.Language = .ukrainian + // Update Available - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Update Available", forceLanguageLocalization: language), "Доступне Оновлення") + XCTAssertEqual(Bundle.localizedString(forKey: "Update Available", andForceLocalization: language), "Доступне Оновлення") // Next time - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Next time", forceLanguageLocalization: language), "Наступного разу") + XCTAssertEqual(Bundle.localizedString(forKey: "Next time", andForceLocalization: language), "Наступного разу") // Skip this version - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Skip this version", forceLanguageLocalization: language), "Пропустити версію") + XCTAssertEqual(Bundle.localizedString(forKey: "Skip this version", andForceLocalization: language), "Пропустити версію") // Update - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Update", forceLanguageLocalization: language), "Оновити") + XCTAssertEqual(Bundle.localizedString(forKey: "Update", andForceLocalization: language), "Оновити") } func testUrduLocalization() { - let language: Siren.LanguageType = .urdu - siren.forceLanguageLocalization = language + let language: Localization.Language = .urdu // Update Available - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Update Available", forceLanguageLocalization: language), "نیا اپڈیٹ") + XCTAssertEqual(Bundle.localizedString(forKey: "Update Available", andForceLocalization: language), "نیا اپڈیٹ") // Next time - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Next time", forceLanguageLocalization: language), "اگلی مرتبہ") + XCTAssertEqual(Bundle.localizedString(forKey: "Next time", andForceLocalization: language), "اگلی مرتبہ") // Skip this version - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Skip this version", forceLanguageLocalization: language), "اس ورزن کو چھوڑ دیں") + XCTAssertEqual(Bundle.localizedString(forKey: "Skip this version", andForceLocalization: language), "اس ورزن کو چھوڑ دیں") // Update - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Update", forceLanguageLocalization: language), "اپڈیٹ کریں") + XCTAssertEqual(Bundle.localizedString(forKey: "Update", andForceLocalization: language), "اپڈیٹ کریں") } func testVietnameseLocalization() { - let language: Siren.LanguageType = .vietnamese - siren.forceLanguageLocalization = language + let language: Localization.Language = .vietnamese // Update Available - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Update Available", forceLanguageLocalization: language), "Cập nhật mới") + XCTAssertEqual(Bundle.localizedString(forKey: "Update Available", andForceLocalization: language), "Cập nhật mới") // Next time - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Next time", forceLanguageLocalization: language), "Lần tới") + XCTAssertEqual(Bundle.localizedString(forKey: "Next time", andForceLocalization: language), "Lần tới") // Skip this version - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Skip this version", forceLanguageLocalization: language), "Bỏ qua phiên bản này") + XCTAssertEqual(Bundle.localizedString(forKey: "Skip this version", andForceLocalization: language), "Bỏ qua phiên bản này") // Update - XCTAssertEqual(Bundle().testLocalizedString(forKey: "Update", forceLanguageLocalization: language), "Cập nhật") + XCTAssertEqual(Bundle.localizedString(forKey: "Update", andForceLocalization: language), "Cập nhật") } } diff --git a/Gemfile b/Gemfile index d62d861b..0d8c69d2 100644 --- a/Gemfile +++ b/Gemfile @@ -1,4 +1,3 @@ -# A sample Gemfile source "https://rubygems.org" gem "cocoapods" diff --git a/README.md b/README.md index 44995f03..6e405fd4 100755 --- a/README.md +++ b/README.md @@ -11,9 +11,7 @@ - [Features](https://github.com/ArtSabintsev/Siren#features) - [Screenshots](https://github.com/ArtSabintsev/Siren#screenshots) - [Installation Instructions](https://github.com/ArtSabintsev/Siren#installation-instructions) -- [Example Code](https://github.com/ArtSabintsev/Siren#example-code) -- [Granular/Differentiated Version Management](https://github.com/ArtSabintsev/Siren#granular-version-update-management) -- [Delegates (Optional)](https://github.com/ArtSabintsev/Siren#optional-delegate-and-delegate-methods) +- [Implementation Examples](https://github.com/ArtSabintsev/Siren#example-code) - [Localization](https://github.com/ArtSabintsev/Siren#localization) - [Device Compatibility](https://github.com/ArtSabintsev/Siren#device-compatibility) - [Testing Siren](https://github.com/ArtSabintsev/Siren#testing-siren) @@ -21,6 +19,7 @@ - [Phased Releases](https://github.com/ArtSabintsev/Siren#phased-releases) - [Words of Caution](https://github.com/ArtSabintsev/Siren#words-of-caution) - [Ports](https://github.com/ArtSabintsev/Siren#ports) +- [Shout-Out and Gratitude](https://github.com/ArtSabintsev/Siren#shout-out-and-gratitude) - [Attribution](https://github.com/ArtSabintsev/Siren#created-and-maintained-by) --- @@ -31,29 +30,30 @@ If a new version is available, an alert can be presented to the user informing them of the newer version, and giving them the option to update the application. Alternatively, Siren can notify your app programmatically, enabling you to inform the user through alternative means, such as a custom interface. - Siren is built to work with the [**Semantic Versioning**](https://semver.org/) system. - - Semantic Versioning is a three number versioning system (e.g., 1.0.0) + - Canonical Semantic Versioning uses a three number versioning system (e.g., 1.0.0) - Siren also supports two-number versioning (e.g., 1.0) and four-number versioning (e.g., 1.0.0.0) -- Siren is actively maintained by [**Arthur Sabintsev**](https://github.com/ArtSabintsev) and [**Aaron Brager**](https://twitter.com/getaaron) - -### README Translations -- [**简体中文**](README.zh_CN.md) (by [**Daniel Hu**](https://www.jianshu.com/u/d8bbc4831623)) ## Features -- [x] CocoaPods Support -- [x] Carthage Support -- [x] Swift Package Manager Support -- [x] Localized for 30+ languages (see [Localization](https://github.com/ArtSabintsev/Siren#localization)) -- [x] Pre-Update Device Compatibility Check (see [Device Compatibility](https://github.com/ArtSabintsev/Siren#device-compatibility)) -- [x] Three types of alerts (see [Screenshots](https://github.com/ArtSabintsev/Siren#screenshots)) -- [x] Optional delegate methods (see [Delegates (Optional)](https://github.com/ArtSabintsev/Siren#optional-delegate-and-delegate-methods)) -- [x] Unit Tests -- [x] Documentation can be found at http://sabintsev.com/Siren. + +### Current Features +- [x] CocoaPods, Carthage, and Swift Package Manager Support +- [x] Three Types of Alerts (see [Screenshots](https://github.com/ArtSabintsev/Siren#screenshots)) +- [x] Highly Customizable Presentation Rules [Implementation Examples](https://github.com/ArtSabintsev/Siren#implementation-examples)) +- [x] Localized for 40+ Languages (see [Localization](https://github.com/ArtSabintsev/Siren#localization)) +- [x] Device Compatibility Check (see [Device Compatibility](https://github.com/ArtSabintsev/Siren#device-compatibility)) +- [x] 100% Documentation Coverage + +### Future Features +- [ ] Present prompt only on WiFi if app is over the OTA limit. +- [ ] Support for Third-/Homegrown Update Servers (not including TestFlight). +- [ ] Increase code coverage with more unit tests and UI tests. + ## Screenshots - The **left picture** forces the user to update the app. - The **center picture** gives the user the option to update the app. - The **right picture** gives the user the option to skip the current update. -- These options are controlled by the `Siren.AlertType` enum. +- These options are controlled by the `Rules.AlertType` enum. @@ -88,199 +88,56 @@ github "ArtSabintsev/Siren" "swift2.3" // Swift 2.3 ### Swift Package Manager ```swift -.Package(url: "https://github.com/ArtSabintsev/Siren.git", majorVersion: 3) +.Package(url: "https://github.com/ArtSabintsev/Siren.git", majorVersion: 4) ``` -## Example Code - -Below is some commented sample code. Adapt this to meet your app's needs. - -For a full list of optional settings/preferences, please refer to https://github.com/ArtSabintsev/Siren/blob/master/Example/Example/AppDelegate.swift in the Sample Project. - -```Swift -func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { - /* Siren code should go below window?.makeKeyAndVisible() */ - - // Siren is a singleton - let siren = Siren.shared - - // Optional: Defaults to .option - siren.alertType = <#Siren.AlertType_Enum_Value#> - - // Optional: Change the various UIAlertController and UIAlertAction messaging. One or more values can be changes. If only a subset of values are changed, the defaults with which Siren comes with will be used. - siren.alertMessaging = SirenAlertMessaging(updateTitle: NSAttributedString(string: "New Fancy Title"), - updateMessage: NSAttributedString(string: "New message goes here!"), - updateButtonMessage: NSAttributedString(string: "Update Now, Plz!?"), - nextTimeButtonMessage: NSAttributedString(string: "OK, next time it is!"), - skipVersionButtonMessage: NSAttributedString(string: "Please don't push skip, please don't!")) - +## Implementation Examples +Implementing Siren is as easy as adding two line of code to your app. - // Optional: Set this variable if you would only like to show an alert if your app has been available on the store for a few days. - // This default value is set to 1 to avoid this issue: https://github.com/ArtSabintsev/Siren#words-of-caution - // To show the update immediately after Apple has updated their JSON, set this value to 0. Not recommended due to aforementioned reason in https://github.com/ArtSabintsev/Siren#words-of-caution. - siren.showAlertAfterCurrentVersionHasBeenReleasedForDays = 3 - - // Replace .immediately with .daily or .weekly to specify a maximum daily or weekly frequency for version checks. - // DO NOT CALL THIS METHOD IN didFinishLaunchingWithOptions IF YOU ALSO PLAN TO CALL IT IN applicationDidBecomeActive. - siren.checkVersion(checkType: .immediately) - - return true -} - -func applicationDidBecomeActive(application: UIApplication) { - /* - Perform daily (.daily) or weekly (.weekly) checks for new version of your app. - Useful if user returns to your app from the background after extended period of time. - Place in applicationDidBecomeActive(_:). - */ +```swift +import Siren // Line 1 +import UIKit - Siren.shared.checkVersion(checkType: .daily) -} +@UIApplicationMain +final class AppDelegate: UIResponder, UIApplicationDelegate { + var window: UIWindow? -func applicationWillEnterForeground(application: UIApplication) { - /* - Useful if user returns to your app from the background after being sent to the - App Store, but doesn't update their app before coming back to your app. + func application(_ application: UIApplication, + didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool { + window?.makeKeyAndVisible() - ONLY USE WITH Siren.AlertType.immediately - */ + Siren.shared.wail() // Line 2 - Siren.shared.checkVersion(checkType: .immediately) + return true + } } ``` -And you're all set! - -### Prompting for Updates without Alerts - -Some developers may want to display a less obtrusive custom interface, like a banner or small icon. To accomplish this, you can disable alert presentation by doing the following: +Siren also has plenty of customization options. All examples can be found in the Example Project's [**AppDelegate**](https://github.com/ArtSabintsev/Siren/blob/master/Example/Example/AppDelegate.swift) file. Uncomment the example you'd like to test. + -```swift -func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { - ... - siren.delegate = self - siren.alertType = .none - ... -} +**WARNING**: Siren should ONLY be placed in [UIApplication.didFinishLaunchingWithOptions](https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1622921-application) and only after the `window?.makeKeyAndVisible()` call. Siren initializes a listener on [didBecomeActiveNotification](https://developer.apple.com/reference/foundation/nsnotification.name/1622953-uiapplicationdidbecomeactive) to perform version checks. -extension AppDelegate: SirenDelegate { - // Returns a localized message to this delegate method upon performing a successful version check - func sirenDidDetectNewVersionWithoutAlert(message: String, updateType: UpdateType) { - print("\(message)") - } -} -``` +## Localization +Siren is localized for the following languages: -Siren will call the `sirenDidDetectNewVersionWithoutAlert(message: String)` delegate method, passing a localized, suggested update string suitable for display. Implement this method to display your own messaging, optionally using `message`. +Arabic, Armenian, Basque, Chinese (Simplified and Traditional), Croatian, Czech, Danish, Dutch, English, Estonian, Finnish, French, German, Greek, Hebrew, Hungarian, Indonesian, Italian, Japanese, Korean, Latvian, Lithuanian, Malay, Norwegian (Bokmål), Persian (Afghanistan, Iran, Persian), Polish, Portuguese (Brazil and Portugal), Russian, Serbian (Cyrillic and Latin), Slovenian, Spanish, Swedish, Thai, Turkish, Ukrainian, Urdu, Vietnamese -## Granular Version Update Management -If you would like to set a different type of alert for revision, patch, minor, and/or major updates, simply add one or all of the following *optional* lines to your setup *before* calling the `checkVersion()` method: +You may want the update dialog to *always* appear in a certain language, ignoring the user's device-specific setting. You can enable it like so: ```swift -/* Siren defaults to Siren.AlertType.option for all updates */ -siren.shared.revisionUpdateAlertType = <#Siren.AlertType_Enum_Value#> -siren.shared.patchUpdateAlertType = <#Siren.AlertType_Enum_Value#> -siren.shared.minorUpdateAlertType = <#Siren.AlertType_Enum_Value#> -siren.shared.majorUpdateAlertType = <#Siren.AlertType_Enum_Value#> +// In this example, we force the `russian` language. +Siren.shared.presentationManager = PresentationManager(forceLanguageLocalization: .russian) ``` -## Optional Delegate and Delegate Methods -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 -public protocol SirenDelegate: NSObjectProtocol { - /// Siren performed version check and did not display alert. - func sirenDidDetectNewVersionWithoutAlert(message: String, updateType: UpdateType) - - /// Siren failed to perform version check. - /// - /// - Note: - /// Depending on the reason for failure, - /// a system-level error may be returned. - func sirenDidFailVersionCheck(error: Error) - - /// User presented with update dialog. - func sirenDidShowUpdateDialog(alertType: Siren.AlertType) - - /// Siren performed a version check and latest version is installed. - func sirenLatestVersionInstalled() - - /// Provides the decoded JSON information from a successful version check call. - /// - /// - SeeAlso: - /// SirenLookupModel.swift - /// - /// - Parameter lookupModel: The `Decodable` model representing the JSON results from the iTunes Lookup API. - func sirenNetworkCallDidReturnWithNewVersionInformation(lookupModel: SirenLookupModel) - - /// User did click on button that cancels update dialog. - func sirenUserDidCancel() - - /// User did click on button that launched "App Store.app". - func sirenUserDidLaunchAppStore() - - /// User did click on button that skips version update. - func sirenUserDidSkipVersion() -} -``` - -## Localization -Siren is localized for -- Arabic -- Armenian -- Basque -- Chinese (Simplified and Traditional) -- Croatian -- Czech -- Danish -- Dutch -- English -- Estonian -- Finnish -- French -- German -- Greek -- Hebrew -- Hungarian -- Indonesian -- Italian -- Japanese -- Korean -- Latvian -- Lithuanian -- Malay -- Norwegian (Bokmål) -- Persian (Afghanistan, Iran, Persian) -- Polish -- Portuguese (Brazil and Portugal) -- Russian -- Serbian (Cyrillic and Latin) -- Slovenian -- Spanish -- Swedish -- Thai -- Turkish -- Ukrainian -- Urdu -- Vietnamese - -You may want the update dialog to *always* appear in a certain language, ignoring iOS's language setting (e.g. apps released in a specific country). - -You can enable it like so: - -```swift -Siren.shared.forceLanguageLocalization = Siren.LanguageType.<#Siren.LanguageType_Enum_Value#> -``` ## Device Compatibility -If an app update is available, Siren checks to make sure that the version of iOS on the user's device is compatible with the one that is required by the app update. For example, if a user has iOS 10 installed on their device, but the app update requires iOS 11, an alert will not be shown. This takes care of the *false positive* case regarding app updating. +If an app update is available, Siren checks to make sure that the version of iOS on the user's device is compatible with the one that is required by the app update. For example, if a user has iOS 11 installed on their device, but the app update requires iOS 12, an alert will not be shown. This takes care of the *false positive* case regarding app updating. ## Testing Siren -Temporarily change the version string in Xcode (within the `.xcodeproj`) to an older version than the one that's currently available in the App Store. Afterwards, build and run your app, and you should see the alert. +Temporarily change the version string in Xcode (within the `.xcodeproj` file) to an older version than the one that's currently available in the App Store. Afterwards, build and run your app, and you should see the alert. If you currently don't have an app in the store, change your bundleID to one that is already in the store. In the sample app packaged with this library, we use the [App Store Connect](https://itunes.apple.com/app/id1234793120) app's bundleID: `com.apple.AppStoreConnect`. -For your convenience, you may turn on debugging statements by setting `self.debugEnabled = true` before calling the `checkVersion()` method. - ## App Store Submissions The App Store reviewer will **not** see the alert. The version in the App Store will always be older than the version being reviewed. @@ -288,7 +145,7 @@ The App Store reviewer will **not** see the alert. The version in the App Store In 2017, Apple announced the [ability to rollout app updates gradually (a.k.a. Phased Releases)](https://itunespartner.apple.com/en/apps/faq/Managing%20Your%20Apps_Submission%20Process). Siren will continue to work as it has in the past, presenting an update modal to _all_ users. If you opt-in to a phased rollout for a specific version, you have a few choices: - You can leave Siren configured as normal. Phased rollout will continue to auto-update apps. Since all users can still manually update your app directly from the App Store, Siren will ignore the phased rollout and will prompt users to update. -- You can set `showAlertAfterCurrentVersionHasBeenReleasedForDays` to `7`, and Siren will not prompt any users until the latest version is 7 days old, after phased rollout is complete. +- You can set `showAlertAfterCurrentVersionHasBeenReleasedForDays` to `7`, and Siren will not prompt any users until the latest version is 7 days old, after the phased rollout is complete. - You can remotely disable Siren until the rollout is done using your own API / backend logic. ## Words of Caution @@ -298,6 +155,7 @@ Occasionally, the iTunes JSON will update faster than the App Store CDN, meaning - **Objective-C (iOS)** - [**Harpy**](https://github.com/ArtSabintsev/Harpy) - Siren was ported _from_ Harpy, as Siren and Harpy are maintained by the same developer. + - As of December 2018, Harpy has been deprecated in favor of Siren. - **Java (Android)** - [**Egghead Games' Siren library**](https://github.com/eggheadgames/Siren) - The Siren Swift library inspired the Java library. @@ -305,5 +163,12 @@ Occasionally, the iTunes JSON will update faster than the App Store CDN, meaning - [**Gant Laborde's Siren library**](https://github.com/GantMan/react-native-siren) - The Siren Swift library inspired the React Native library. +## Shout-Out and Gratitude +A massive shout-out and thank you goes to the following folks: + +- [Aaron Brager](https://twitter.com/@getaaron) for motivating me and assisting me in building the initial proof-of-concept of Siren (based on [Harpy](https:github.com/ArtSabintsev/Harpy)) back in 2015. Without him, Siren may never have been built. +- All of [Harpy's Consitrbutors](https://github.com/ArtSabintsev/Harpy/graphs/contributors) for helping building the feature set from 2012-2015 that was used as the basis for the first version of Siren. +- All of [Siren's Contributors](https://github.com/ArtSabintsev/Siren/graphs/contributors) for helping make Siren as powerful and bug-free as it currently is today. + ## Created and maintained by -[Arthur Ariel Sabintsev](http://www.sabintsev.com/) & [Aaron Brager](https://twitter.com/getaaron) +[Arthur Ariel Sabintsev](http://www.sabintsev.com/) diff --git a/README.zh_CN.md b/README.zh_CN.md deleted file mode 100644 index d43e71b2..00000000 --- a/README.zh_CN.md +++ /dev/null @@ -1,232 +0,0 @@ -# Siren [English](README.md) - -### 当您的 app 有新版本可用时提示用户进行更新。 - -[![Travis-CI](https://travis-ci.org/ArtSabintsev/Siren.svg?branch=master)](https://travis-ci.org/ArtSabintsev/Siren) [![CocoaPods](https://img.shields.io/cocoapods/v/Siren.svg)](https://cocoapods.org/pods/Siren) [![Carthage Compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) [![SwiftPM Compatible](https://img.shields.io/badge/SwiftPM-Compatible-brightgreen.svg)](https://swift.org/package-manager/) [![CocoaPods](https://img.shields.io/cocoapods/dt/Siren.svg)](https://cocoapods.org/pods/Siren) [![CocoaPods](https://img.shields.io/cocoapods/dm/Siren.svg)](https://cocoapods.org/pods/Siren) ---- - -## 关于 -**Siren** 用于检测用户当前安装版本是否是 App Store 上的最新可用版本。 - -当 app 有更新时,Siren 会弹出提示框,用户可根据提示框提供的选项进行更新。或者您也可以根据 Siren 发出的消息来自定义通知用户的方式,比如您可以提供一个自定义的提示框。 - -- Siren 可配合 [**Semantic Versioning**](https://semver.org/) 系统使用 - - Semantic 版本系统由三位数字标识 (比如,1.0.0) - - Siren 同时支持两位数字标识 (比如,1.0) - - Siren 同时支持四位数字标识 (比如,1.0.0.0) -- Siren 当前处于活跃维护状态,由[**Arthur Sabintsev**](https://github.com/ArtSabintsev) 和 [**Aaron Brager**](https://twitter.com/getaaron) 进行维护。 - - -## Ports -- [**Harpy**](https://github.com/ArtSabintsev/Harpy) 是 Objective-C 实现的版本更新检查库,Siren 是 Harpy 的 swift 版本。 -- Siren 和 Harpy 是由相同的开发者维护。 -- 安卓平台 Play Store 上的 [**Egghead Games' Siren library**](https://github.com/eggheadgames/Siren) 库使用了和 Siren 相同的原理实现了版本更新检测。 -- 针对 React Native 项目 (iOS/Android) 的 [**Gant Laborde's Siren library**](https://github.com/GantMan/react-native-siren) 库使用了和 Siren 相同的原理实现了版本更新检测。 - -## 特点 -- [x] 支持 Cocoapods -- [x] 支持 Carthage -- [x] 支持 Swift 包管理器 -- [x] 30+ 语言本地化 (查看**本地化**) -- [x] 设备兼容性检测 (查看**设备兼容性**) -- [x] 三种类型的弹出提示框 (查看**截图**) -- [x] 可选代理方法 (查看**可选代理**) -- [x] 单元测试! - -## 截图 - -- **左图** 强制用户更新 -- **中间** 给用户提供更新选项 -- **右图** 给用户提供更新和跳过更新选项 -- 这些选项对应着 `SirenAlertType` 枚举类型 - - - - - -## 安装指南 - -### CocoaPods -Swift 3 版本: -```ruby -pod 'Siren' -``` - -Swift 2.3 版本: - -```ruby -pod 'Siren', :git => 'https://github.com/ArtSabintsev/Siren.git', :branch => 'swift2.3' -``` - -Swift 2.2 版本: - -```ruby -pod 'Siren', '0.9.5' -``` - -### Carthage -FSwift 3 版本: - -``` swift -github "ArtSabintsev/Siren" -``` - -Swift 2.3 版本: - -``` swift -github "ArtSabintsev/Siren" "swift2.3" -``` - -### Swift 包管理器 -```swift -.Package(url: "https://github.com/ArtSabintsev/Siren.git", majorVersion: 1) -``` - -## 使用 -下面是一些示例代码。请根据您的需求进行修改。更详细的使用说明请参考示例项目中的 https://github.com/ArtSabintsev/Siren/blob/master/Sample%20App/Sample%20App/AppDelegate.swift。 - -```Swift -func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { - /* Siren code should go below window?.makeKeyAndVisible() */ - - // Siren is a singleton - let siren = Siren.sharedInstance - - // Optional: Defaults to .Option - siren.alertType = <#SirenAlertType_Enum_Value#> - - /* - Replace .Immediately with .Daily or .Weekly to specify a maximum daily or weekly frequency for version - checks. - */ - siren.checkVersion(checkType: .immediately) - - return true -} - -func applicationDidBecomeActive(application: UIApplication) { - /* - Perform daily (.Daily) or weekly (.Weekly) checks for new version of your app. - Useful if user returns to your app from the background after extended period of time. - Place in applicationDidBecomeActive(_:). */ - - Siren.sharedInstance.checkVersion(checkType: .daily) -} - -func applicationWillEnterForeground(application: UIApplication) { - /* - Useful if user returns to your app from the background after being sent to the - App Store, but doesn't update their app before coming back to your app. - - ONLY USE WITH SirenAlertType.Force - */ - - Siren.sharedInstance.checkVersion(checkType: .immediately) -} -``` - -### 使用非弹出提示框进行提示 - -您可以使用顶部条幅等更友好的方式进行提示。首先,您需要通过下面代码禁用弹出提示框: - -```swift -func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { - ... - siren.delegate = self - siren.alertType = .None - ... -} - -extension AppDelegate: SirenDelegate { - // 当检测到有更新可用时向该代理方法传递一个本地化的提示信息 - func sirenDidDetectNewVersionWithoutAlert(message: String) { - print("\(message)") - } -} -``` -Siren 会调用 `sirenDidDetectNewVersionWithoutAlert(message: String)` 代理方法,该方法传递了一个本地化的更新提示信息作为参数。您可以使用该参数作为提示信息,也可以使用自定义的提示信息。 - -## 为修订版,补丁,小版本,大版本设置不同的提示框类型 -您可以为修订版,补丁,小版本,大版本等设置不同提示框类型,只需要在 `checkVersion()` 方法前调用进行如下设置即可: - -```swift - /* Siren defaults to SirenAlertType.Option for all updates */ - siren.sharedInstance().revisionUpdateAlertType = <#SirenAlertType_Enum_Value#> - siren.sharedInstance().patchUpdateAlertType = <#SirenAlertType_Enum_Value#> - siren.sharedInstance().minorUpdateAlertType = <#SirenAlertType_Enum_Value#> - siren.sharedInstance().majorUpdateAlertType = <#SirenAlertType_Enum_Value#> -``` - -##可选代理和代理方法 -您可以通过下面六个代理方法跟踪用户进行的操作。 - -``` swift -public protocol SirenDelegate: class { - func sirenDidShowUpdateDialog(alertType: SirenAlertType) // 弹出更新提示框 - func sirenUserDidLaunchAppStore() // 用户点击去 app store 更新 - func sirenUserDidSkipVersion() // 用户点击跳过此次更新 - func sirenUserDidCancel() // 用户点击取消更新 - func sirenDidFailVersionCheck(error: NSError) // 检查更新失败(可能返回系统级别的错误) - func sirenDidDetectNewVersionWithoutAlert(message: String) // 检测到更新但不弹出提示框 -} -``` - -## 本地化 -Siren 为以下国家做了本地化 -- Arabic -- Armenian -- Basque -- Chinese (Simplified and Traditional) -- Danish -- Dutch -- English -- Estonian -- Finnish -- French -- German -- Greek -- Hebrew -- Hungarian -- Indonesian -- Italian -- Japanese -- Korean -- Latvian -- Lithuanian -- Malay -- Norwegian (Bokmål) -- Polish -- Portuguese (Brazil and Portugal) -- Russian -- Serbian (Cyrillic and Latin) -- Slovenian -- Swedish -- Spanish -- Thai -- Turkish -- Vietnamese - -您可以通过以下代码忽略 iOS 系统语言设置,为弹出框设置固定语言。 - -```swift -Siren.sharedInstance.forceLanguageLocalization = SirenLanguageType.<#SirenLanguageType_Enum_Value#> -``` - -## 设备兼容性 -当有更新可用时,Siren 会检测用户的 iOS 版本号是否符合更新需求。比如,用户的系统是 iOS 9,但此次更新只针对 iOS 10,这时是不会出现弹出提示框。 - -## 测试 -测试时,需要暂时将 Xcode 里(`.xcodeproj` 文件) 的版本号修改为比当前苹果商店中的可用版本号大。这样编译运行 app 时,您就可以看到弹出提示框。 - -如果您尚未发布过 App,把 bundleID 修改为一个 app store 已经存在的 bundleID。在示例项目中,我们使用了 [iTunes Connect Mobile](https://itunes.apple.com/us/app/itunes-connect/id376771144?mt=8) 的 bundleID:`com.apple.itunesconnect.mobile`。 - -为方便调试,您可以在调用 `checkVersion()` 方法前通过 `self.debugEnabled = true` 来开启调试模式。 - -## 提交至 App Store -因为商店里的可用版本总是比提交审核的版本老,所以苹果商店审核人员在审核时是**不会**弹出提示框的。 - -##创建维护人员 -[Arthur Ariel Sabintsev](http://www.sabintsev.com/) & [Aaron Brager](https://twitter.com/getaaron) - -## 翻译人员 -[Daniel Hu](https://www.jianshu.com/u/d8bbc4831623) \ No newline at end of file diff --git a/Siren.podspec b/Siren.podspec index 9708fa73..f9c7322f 100755 --- a/Siren.podspec +++ b/Siren.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| # Version - s.version = "3.9.2" + s.version = "4.0.0" s.swift_version = '4.2' # Meta @@ -8,7 +8,7 @@ Pod::Spec.new do |s| s.summary = "Notify users that a new version of your iOS app is available, and prompt them with the App Store link." s.homepage = "https://github.com/ArtSabintsev/Siren" s.license = "MIT" - s.authors = { "Arthur Ariel Sabintsev" => "arthur@sabintsev.com", "Aaron Brager" => "getaaron@gmail.com" } + s.authors = { "Arthur Ariel Sabintsev" => "arthur@sabintsev.com" } s.description = <<-DESC Notify your users when a new version of your iOS app is available, and prompt them with the App Store link. DESC diff --git a/Sources/Extensions/BundleExtension.swift b/Sources/Extensions/BundleExtension.swift new file mode 100644 index 00000000..84c09e78 --- /dev/null +++ b/Sources/Extensions/BundleExtension.swift @@ -0,0 +1,111 @@ +// +// BundleExtension.swift +// Siren +// +// Created by Arthur Sabintsev on 3/17/17. +// Copyright © 2017 Sabintsev iOS Projects. All rights reserved. +// + +import Foundation + +// `Bundle` Extension for Siren. +extension Bundle { + /// Constants used in the `Bundle` extension. + struct Constants { + /// Constant for the `.bundle` file extension. + static let bundleExtension = "bundle" + /// Constant for `CFBundleDisplayName`. + static let displayName = "CFBundleDisplayName" + /// Constant for the default US English localization. + static let englishLocalization = "en" + /// Constant for the project file extension. + static let projectExtension = "lproj" + /// Constant for `CFBundleShortVersionString`. + static let shortVersionString = "CFBundleShortVersionString" + /// Constant for the localization table. + static let table = "SirenLocalizable" + } + + /// Fetches the current verison of the app. + /// + /// - Returns: The current installed version of the app. + final class func version() -> String? { + return Bundle.main.object(forInfoDictionaryKey: Constants.shortVersionString) as? String + } + + /// Returns the localized string for a given default string. + /// + /// By default, the English language localization is used. + /// If the device's localization is set to another locale, that local's language is used if it's supported by Siren. + /// If `forcedLanguage` is set to `true`, the chosen language is shown for all devices, irrespective of their device's localization. + /// + /// + /// - Parameters: + /// - key: The default string used to search the localization table for a specific translation. + /// - forcedLanguage: Returns + /// - Returns: The localized string for a given key. + final class func localizedString(forKey key: String, andForceLocalization forcedLanguage: Localization.Language?) -> String { + guard var path = sirenBundlePath() else { + return key + } + + if let deviceLangauge = deviceLanguage(), + let devicePath = sirenForcedBundlePath(forceLanguageLocalization: deviceLangauge) { + path = devicePath + } + + if let forcedLanguage = forcedLanguage, + let forcedPath = sirenForcedBundlePath(forceLanguageLocalization: forcedLanguage) { + path = forcedPath + } + + return Bundle(path: path)?.localizedString(forKey: key, value: key, table: Constants.table) ?? key + } + + /// The appropriate name for the app to be displayed in the update alert. + /// + /// Siren checks `CFBundleDisplayName` first. It then falls back to + /// to `kCFBundleNameKey` and ultimately to an empty string + /// if the aforementioned values are nil. + /// + /// - Returns: The name of the app. + final class func bestMatchingAppName() -> String { + let bundleDisplayName = Bundle.main.object(forInfoDictionaryKey: Constants.displayName) as? String + let bundleName = Bundle.main.object(forInfoDictionaryKey: kCFBundleNameKey as String) as? String + + return bundleDisplayName ?? bundleName ?? "" + } +} + +private extension Bundle { + /// The path to Siren's localization `Bundle`. + /// + /// - Returns: The bundle's path or `nil`. + final class func sirenBundlePath() -> String? { + return Bundle(for: Siren.self).path(forResource: "\(Siren.self)", ofType: Constants.bundleExtension) + } + + /// The path for a particular language localizationin Siren's localization `Bundle`. + /// + /// - Parameter forceLanguageLocalization: The language localization that should be searched for in Siren's localization `bundle`. + /// - Returns: The path to the forced language localization. + final class func sirenForcedBundlePath(forceLanguageLocalization: Localization.Language) -> String? { + guard let path = sirenBundlePath() else { return nil } + let name = forceLanguageLocalization.rawValue + + return Bundle(path: path)?.path(forResource: name, ofType: Constants.projectExtension) + } + + /// The user's preferred language based on their device's localization. + /// + /// - Returns: The user's preferred language. + final class func deviceLanguage() -> Localization.Language? { + guard let preferredLocalization = Bundle.main.preferredLocalizations.first, + preferredLocalization != Constants.englishLocalization, + let preferredLanguage = Localization.Language(rawValue: preferredLocalization) else { + return nil + } + + return preferredLanguage + } +} diff --git a/Sources/Extensions/SirenDateExtension.swift b/Sources/Extensions/DateExtension.swift similarity index 62% rename from Sources/Extensions/SirenDateExtension.swift rename to Sources/Extensions/DateExtension.swift index a4f2d529..105feaae 100644 --- a/Sources/Extensions/SirenDateExtension.swift +++ b/Sources/Extensions/DateExtension.swift @@ -1,6 +1,6 @@ // -// SirenDateExtension.swift -// SirenExample +// DateExtension.swift +// Siren // // Created by Arthur Sabintsev on 3/21/17. // Copyright © 2017 Sabintsev iOS Projects. All rights reserved. @@ -8,15 +8,22 @@ import Foundation -// MARK: - Date Extension for Siren - +// `Date` Extension for Siren. extension Date { + /// The amount of days passed from a specific source date. + /// + /// - Parameter date: The source date. + /// - Returns: The amount of days passed since the source date. static func days(since date: Date) -> Int { let calendar = Calendar.current let components = calendar.dateComponents([.day], from: date, to: Date()) return components.day ?? 0 } + /// The amount of days passed from a specific source date string. + /// + /// - Parameter dateString: The source date string. + /// - Returns: The amount of days passed since the source date. static func days(since dateString: String) -> Int? { let dateformatter = DateFormatter() dateformatter.locale = Locale(identifier: "en_US_POSIX") diff --git a/Sources/Extensions/SirenBundleExtension.swift b/Sources/Extensions/SirenBundleExtension.swift deleted file mode 100644 index 79adbe74..00000000 --- a/Sources/Extensions/SirenBundleExtension.swift +++ /dev/null @@ -1,55 +0,0 @@ -// -// SirenBundleExtension.swift -// SirenExample -// -// Created by Arthur Sabintsev on 3/17/17. -// Copyright © 2017 Sabintsev iOS Projects. All rights reserved. -// - -import Foundation - -// MARK: - Bundle Extension for Siren - -extension Bundle { - final class func bundleID() -> String? { - return Bundle.main.bundleIdentifier - } - - final class func sirenBundlePath() -> String? { - return Bundle(for: Siren.self).path(forResource: "Siren", ofType: "bundle") - } - - final class func sirenForcedBundlePath(forceLanguageLocalization: Siren.LanguageType) -> String? { - guard let path = sirenBundlePath() else { return nil } - let name = forceLanguageLocalization.rawValue - - return Bundle(path: path)?.path(forResource: name, ofType: "lproj") - } - - final class func localizedString(forKey key: String, forceLanguageLocalization: Siren.LanguageType?) -> String? { - guard var path = sirenBundlePath() else { return nil } - let table = "SirenLocalizable" - - if let forceLanguageLocalization = forceLanguageLocalization, - let forcedPath = sirenForcedBundlePath(forceLanguageLocalization: forceLanguageLocalization) { - path = forcedPath - } - - return Bundle(path: path)?.localizedString(forKey: key, value: key, table: table) - } - - final class func bestMatchingAppName() -> String { - let bundleDisplayName = Bundle.main.object(forInfoDictionaryKey: "CFBundleDisplayName") as? String - let bundleName = Bundle.main.object(forInfoDictionaryKey: kCFBundleNameKey as String) as? String - - return bundleDisplayName ?? bundleName ?? "" - } -} - -// MARK: - Bundle Extension for Testing Siren - -extension Bundle { - func testLocalizedString(forKey key: String, forceLanguageLocalization: Siren.LanguageType?) -> String? { - return Bundle.localizedString(forKey: key, forceLanguageLocalization: forceLanguageLocalization) - } -} diff --git a/Sources/Extensions/SirenLocalizationExtension.swift b/Sources/Extensions/SirenLocalizationExtension.swift deleted file mode 100644 index 2d01fb49..00000000 --- a/Sources/Extensions/SirenLocalizationExtension.swift +++ /dev/null @@ -1,48 +0,0 @@ -// -// SirenLocalizationExtension.swift -// Siren -// -// Created by Arthur Sabintsev on 9/25/18. -// Copyright © 2018 Sabintsev iOS Projects. All rights reserved. -// - -import Foundation - -// MARK: - Helpers (Localization) - -extension Siren { - func localizedUpdateTitle() -> String { - return Bundle.localizedString(forKey: alertMessaging.updateTitle.string, forceLanguageLocalization: forceLanguageLocalization) - ?? alertMessaging.updateTitle.string - } - - func localizedNewVersionMessage() -> String { - let newVersionMessage = Bundle.localizedString(forKey: alertMessaging.updateMessage.string, - forceLanguageLocalization: forceLanguageLocalization) - ?? alertMessaging.updateMessage.string - - guard let currentAppStoreVersion = currentAppStoreVersion else { - return String(format: newVersionMessage, appName, "Unknown") - } - - return String(format: newVersionMessage, appName, currentAppStoreVersion) - } - - func localizedUpdateButtonTitle() -> String? { - return Bundle.localizedString(forKey: alertMessaging.updateButtonMessage.string, - forceLanguageLocalization: forceLanguageLocalization) - ?? alertMessaging.updateButtonMessage.string - } - - func localizedNextTimeButtonTitle() -> String? { - return Bundle.localizedString(forKey: alertMessaging.nextTimeButtonMessage.string, - forceLanguageLocalization: forceLanguageLocalization) - ?? alertMessaging.nextTimeButtonMessage.string - } - - func localizedSkipButtonTitle() -> String? { - return Bundle.localizedString(forKey: alertMessaging.skipVersionButtonMessage.string, - forceLanguageLocalization: forceLanguageLocalization) - ?? alertMessaging.skipVersionButtonMessage.string - } -} diff --git a/Sources/Extensions/SirenUIAlertControllerExtension.swift b/Sources/Extensions/SirenUIAlertControllerExtension.swift deleted file mode 100644 index c824b506..00000000 --- a/Sources/Extensions/SirenUIAlertControllerExtension.swift +++ /dev/null @@ -1,25 +0,0 @@ -// -// SirenUIAlertControllerExtension.swift -// SirenExample -// -// Created by Arthur Sabintsev on 3/17/17. -// Copyright © 2017 Sabintsev iOS Projects. All rights reserved. -// - -import Foundation -import UIKit - -// MARK: - UIAlertController Extension for Siren - -extension UIAlertController { - func show() { - let window = UIWindow(frame: UIScreen.main.bounds) - window.rootViewController = SirenViewController() - window.windowLevel = UIWindow.Level.alert + 1 - - Siren.shared.updaterWindow = window - - window.makeKeyAndVisible() - window.rootViewController?.present(self, animated: true, completion: nil) - } -} diff --git a/Sources/Extensions/UIAlertControllerExtension.swift b/Sources/Extensions/UIAlertControllerExtension.swift new file mode 100644 index 00000000..5dcdec20 --- /dev/null +++ b/Sources/Extensions/UIAlertControllerExtension.swift @@ -0,0 +1,28 @@ +// +// UIAlertControllerExtension.swift +// Siren +// +// Created by Arthur Sabintsev on 3/17/17. +// Copyright © 2017 Sabintsev iOS Projects. All rights reserved. +// + +import Foundation +import UIKit + +// `UIAlertController` Extension for Siren. +extension UIAlertController { + /// Presents Siren's `UIAlertController` in a new `UIWindow`. + /// + /// - Parameter window: The `UIWindow` that _should_ reference Siren's `UIAlertController`. + func show(window: UIWindow) { + window.makeKeyAndVisible() + window.rootViewController?.present(self, animated: true, completion: nil) + } + + /// Hides Siren's `UIAlertController` within a given window. + /// + /// - Parameter window: The `UIWindow` that references Siren's `UIAlertController`. + func hide(window: UIWindow) { + window.isHidden = true + } +} diff --git a/Sources/Extensions/SirenUserDefaultsExtension.swift b/Sources/Extensions/UserDefaultsExtension.swift similarity index 51% rename from Sources/Extensions/SirenUserDefaultsExtension.swift rename to Sources/Extensions/UserDefaultsExtension.swift index f806f540..a92d7c91 100644 --- a/Sources/Extensions/SirenUserDefaultsExtension.swift +++ b/Sources/Extensions/UserDefaultsExtension.swift @@ -1,5 +1,5 @@ // -// SirenUserDefaultsExtension.swift +// UserDefaultsExtension.swift // Siren // // Created by Arthur Sabintsev on 9/25/18. @@ -8,22 +8,22 @@ import Foundation -/// Siren-specific UserDefaults Keys -private enum SirenKeys: String { - /// Key that notifies Siren to perform a version check and present - /// the Siren alert the next time the user launches the app. - case PerformVersionCheckOnSubsequentLaunch - - /// Key that stores the timestamp of the last version check in UserDefaults. - case StoredVersionCheckDate +// `UserDefaults` Extension for Siren. +extension UserDefaults { + /// Siren-specific `UserDefaults` Keys + private enum SirenKeys: String { + /// Key that notifies Siren to perform a version check and present + /// the Siren alert the next time the user launches the app. + case PerformVersionCheckOnSubsequentLaunch - /// Key that stores the version that a user decided to skip in UserDefaults. - case StoredSkippedVersion -} + /// Key that stores the timestamp of the last version check. + case StoredVersionCheckDate -// MARK: - UserDefaults Extension for Siren + /// Key that stores the version that a user decided to skip. + case StoredSkippedVersion + } -extension UserDefaults { + /// Sets and Gets a `UserDefault` around performing a version check on a subsequent launch. static var shouldPerformVersionCheckOnSubsequentLaunch: Bool { get { return standard.bool(forKey: SirenKeys.PerformVersionCheckOnSubsequentLaunch.rawValue) @@ -32,6 +32,7 @@ extension UserDefaults { } } + /// Sets and Gets a `UserDefault` around storing a version that the user wants to skip updating. static var storedSkippedVersion: String? { get { return standard.string(forKey: SirenKeys.StoredSkippedVersion.rawValue) @@ -40,7 +41,8 @@ extension UserDefaults { } } - static var storedVersionCheckDate: Date? { + /// Sets and Gets a `UserDefault` around the last time the user was presented a version update alert. + static var alertPresentationDate: Date? { get { return standard.object(forKey: SirenKeys.StoredVersionCheckDate.rawValue) as? Date } set { diff --git a/Sources/Managers/APIManager.swift b/Sources/Managers/APIManager.swift new file mode 100644 index 00000000..5225630d --- /dev/null +++ b/Sources/Managers/APIManager.swift @@ -0,0 +1,131 @@ +// +// APIManager.swift +// Siren +// +// Created by Arthur Sabintsev on 11/24/18. +// Copyright © 2018 Sabintsev iOS Projects. All rights reserved. +// + +import Foundation + +/// APIManager for Siren +public struct APIManager { + /// Constants used in the `APIManager`. + private struct Constants { + /// Constant for the `bundleId` parameter in the iTunes Lookup API request. + static let bundleID = "bundleId" + /// Constant for the `country` parameter in the iTunes Lookup API request. + static let country = "country" + } + + /// Return results or errors obtained from performing a version check with Siren. + typealias CompletionHandler = (LookupModel?, KnownError?) -> Void + + /// The region or country of an App Store in which the app is available. + /// By default, all version check requests are performed against the US App Store. + /// If the app is not available in the US App Store, set it to the identifier of at least one App Store region within which it is available. + /// + /// [List of country codes](https://help.apple.com/app-store-connect/#/dev997f9cf7c) + /// + let countryCode: String? + + /// Initializes `APIManager` to the region or country of an App Store in which the app is available. + /// By default, all version check requests are performed against the US App Store. + /// If the app is not available in the US App Store, set it to the identifier of at least one App Store region within which it is available. + /// + /// [List of country codes](https://help.apple.com/app-store-connect/#/dev997f9cf7c) + /// + /// - Parameter countryCode: The country code for the App Store in which the app is availabe. Defaults to nil (e.g., the US App Store) + public init(countryCode: String? = nil) { + self.countryCode = countryCode + } + + /// The default `APIManager`. + /// + /// The version check is performed against the US App Store. + public static let `default` = APIManager() +} + +extension APIManager { + /// Creates and performs a URLRequest against the iTunes Lookup API. + /// + /// - Parameter handler: The completion handler for the iTunes Lookup API request. + func performVersionCheckRequest(completion handler: CompletionHandler?) { + guard Bundle.main.bundleIdentifier != nil else { + handler?(nil, .missingBundleID) + return + } + + do { + let url = try makeITunesURL() + let request = URLRequest(url: url, cachePolicy: .reloadIgnoringLocalAndRemoteCacheData, timeoutInterval: 30) + URLSession.shared.dataTask(with: request) { (data, response, error) in + URLCache.shared.removeCachedResponse(for: request) + self.processVersionCheckResults(withData: data, response: response, error: error, completion: handler) + }.resume() + } catch { + handler?(nil, .malformedURL) + } + } + + /// Parses and maps the the results from the iTunes Lookup API request. + /// + /// - Parameters: + /// - data: The JSON data returned from the request. + /// - response: The response metadata returned from the request. + /// - error: The error returned from the request. + /// - handler: The completion handler to call once the results of the request has been processed. + private func processVersionCheckResults(withData data: Data?, + response: URLResponse?, + error: Error?, + completion handler: CompletionHandler?) { + if let error = error { + handler?(nil, .appStoreDataRetrievalFailure(underlyingError: error)) + } else { + guard let data = data else { + handler?(nil, .appStoreDataRetrievalFailure(underlyingError: nil)) + return + } + do { + let lookupModel = try JSONDecoder().decode(LookupModel.self, from: data) + + guard !lookupModel.results.isEmpty else { + handler?(nil, .appStoreDataRetrievalEmptyResults) + return + } + + DispatchQueue.main.async { + handler?(lookupModel, nil) + } + } catch { + handler?(nil, .appStoreJSONParsingFailure(underlyingError: error)) + } + } + } + + /// Creates the URL that points to the iTunes Lookup API. + /// + /// - Returns: The iTunes Lookup API URL. + /// - Throws: An error if the URL cannot be created. + private func makeITunesURL() throws -> URL { + var components = URLComponents() + components.scheme = "https" + components.host = "itunes.apple.com" + components.path = "/lookup" + + var items: [URLQueryItem] = [URLQueryItem(name: Constants.bundleID, value: Bundle.main.bundleIdentifier)] + + if let countryCode = countryCode { + let item = URLQueryItem(name: Constants.country, value: countryCode) + items.append(item) + } + + components.queryItems = items + + guard let url = components.url, !url.absoluteString.isEmpty else { + throw KnownError.malformedURL + } + + return url + } +} diff --git a/Sources/Managers/PresentationManager.swift b/Sources/Managers/PresentationManager.swift new file mode 100644 index 00000000..903f4ff3 --- /dev/null +++ b/Sources/Managers/PresentationManager.swift @@ -0,0 +1,219 @@ +// +// PresentationManager.swift +// Siren +// +// Created by Arthur Sabintsev on 12/6/17. +// Copyright © 2017 Sabintsev iOS Projects. All rights reserved. +// + +import Foundation + +/// PresentationManager for Siren +public struct PresentationManager { + /// Return results or errors obtained from performing a version check with Siren. + typealias CompletionHandler = (AlertAction) -> Void + + /// The localization data structure that will be used to construct localized strings for the update alert. + let localization: Localization + + /// The tint color of the `UIAlertController` buttons. + let tintColor: UIColor? + + /// The descriptive update message of the `UIAlertController`. + let alertMessage: String + + /// The main message of the `UIAlertController`. + let alertTitle: String + + /// The "Next time" button text of the `UIAlertController`. + let nextTimeButtonTitle: String + + /// The "Skip this version" button text of the `UIAlertController`. + let skipButtonTitle: String + + /// The "Update" button text of the `UIAlertController`. + let updateButtonTitle: String + + /// The instance of the `UIAlertController` used to present the update alert. + private var alertController: UIAlertController? + + /// The `UIWindow` instance that presents the `SirenViewController`. + private var updaterWindow: UIWindow { + let window = UIWindow(frame: UIScreen.main.bounds) + window.rootViewController = SirenViewController() + window.windowLevel = UIWindow.Level.alert + 1 + return window + } + + /// `PresentationManager`'s public initializer. + /// + /// - Parameters: + /// - tintColor: The alert's tintColor. Settings this to `nil` defaults to the system default color. + /// - appName: The name of the app (overrides the default/bundled name). + /// - alertTitle: The title field of the `UIAlertController`. + /// - alertMessage: The `message` field of the `UIAlertController`. + /// - nextTimeButtonTitle: The `title` field of the Next Time Button `UIAlertAction`. + /// - skipButtonTitle: The `title` field of the Skip Button `UIAlertAction`. + /// - updateButtonTitle: The `title` field of the Update Button `UIAlertAction`. + /// - forceLanguage: The language the alert to which the alert should be set. If `nil`, it falls back to the device's preferred locale. + public init(alertTintColor tintColor: UIColor? = nil, + appName: String? = nil, + alertTitle: String = AlertConstants.alertTitle, + alertMessage: String = AlertConstants.alertMessage, + updateButtonTitle: String = AlertConstants.updateButtonTitle, + nextTimeButtonTitle: String = AlertConstants.nextTimeButtonTitle, + skipButtonTitle: String = AlertConstants.skipButtonTitle, + forceLanguageLocalization forceLanguage: Localization.Language? = nil) { + self.alertTitle = alertTitle + self.alertMessage = alertMessage + self.localization = Localization(appName: appName, andForceLanguageLocalization: forceLanguage) + self.nextTimeButtonTitle = nextTimeButtonTitle + self.updateButtonTitle = updateButtonTitle + self.skipButtonTitle = skipButtonTitle + self.tintColor = tintColor + } + + /// The default `PresentationManager`. + /// + /// By default: + /// - There is no tint color (defaults to Apple's system `blue` color.) + /// - The name of the app is equal to the name that appears in `Info.plist`. + /// - The strings are all set to that of the user's device localization (if supported) or it falls back to English. + public static let `default` = PresentationManager() +} + +extension PresentationManager { + + /// Constructs the localized update alert `UIAlertController` object. + /// + /// - Parameters: + /// - rules: The rules that are used to define the type of alert that should be presented. + /// - currentAppStoreVersion: The current version of the app in the App Store. + /// - handler: The completion handler that returns the an `AlertAction` depending on the type of action the end-user took. + mutating func presentAlert(withRules rules: Rules, + forCurrentAppStoreVersion currentAppStoreVersion: String, + completion handler: CompletionHandler?) { + UserDefaults.alertPresentationDate = Date() + + // Alert Title + let alertTitle: String + if self.alertTitle == AlertConstants.alertTitle { + alertTitle = localization.alertTitle() + } else { + alertTitle = self.alertTitle + } + + // Alert Message + let alertMessage: String + if self.alertMessage == AlertConstants.alertMessage { + alertMessage = localization.alertMessage(forCurrentAppStoreVersion: currentAppStoreVersion) + } else { + alertMessage = self.alertMessage + } + + alertController = UIAlertController(title: alertTitle, + message: alertMessage, + preferredStyle: .alert) + + if let tintColor = tintColor { + alertController?.view.tintColor = tintColor + } + + switch rules.alertType { + case .force: + alertController?.addAction(updateAlertAction(completion: handler)) + case .option: + + alertController?.addAction(updateAlertAction(completion: handler)) + alertController?.addAction(nextTimeAlertAction(completion: handler)) + case .skip: + alertController?.addAction(updateAlertAction(completion: handler)) + alertController?.addAction(nextTimeAlertAction(completion: handler)) + alertController?.addAction(skipAlertAction(forCurrentAppStoreVersion: currentAppStoreVersion, completion: handler)) + case .none: + handler?(.unknown) + } + + // If the alertType is .none, an alert will not be presneted. + // If the `updaterWindow` is not hidden, than an alert is already presented. + // The latter prevents `UIAlertControllers` from appearing on top of each other. + if rules.alertType != .none && updaterWindow.isHidden { + alertController?.show(window: updaterWindow) + } + } + + /// The `UIAlertAction` that is executed when the `Update` option is selected. + /// + /// - Parameters: + /// - handler: The completion handler that returns the `.update` option. + /// - Returns: The `Update` alert action. + private func updateAlertAction(completion handler: CompletionHandler?) -> UIAlertAction { + let title: String + if self.updateButtonTitle == AlertConstants.updateButtonTitle { + title = localization.updateButtonTitle() + } else { + title = self.updateButtonTitle + } + + let action = UIAlertAction(title: title, style: .default) { _ in + self.alertController?.hide(window: self.updaterWindow) + Siren.shared.launchAppStore() + + handler?(.appStore) + return + } + + return action + } + + /// The `UIAlertAction` that is executed when the `Next time` option is selected. + /// + /// - Parameters: + /// - handler: The completion handler that returns the `.nextTime` option. + /// - Returns: The `Next time` alert action. + private func nextTimeAlertAction(completion handler: CompletionHandler?) -> UIAlertAction { + let title: String + if self.nextTimeButtonTitle == AlertConstants.nextTimeButtonTitle { + title = localization.nextTimeButtonTitle() + } else { + title = self.nextTimeButtonTitle + } + + let action = UIAlertAction(title: title, style: .default) { _ in + self.alertController?.hide(window: self.updaterWindow) + UserDefaults.shouldPerformVersionCheckOnSubsequentLaunch = true + + handler?(.nextTime) + return + } + + return action + } + + /// The `UIAlertAction` that is executed when the `Skip this version` option is selected. + /// + /// - Parameters: + /// - currentAppStoreVersion: The current version of the app in the App Store. + /// - handler: The completion handler that returns the `.skip` option. + /// - Returns: The `Skip this version` alert action. + private func skipAlertAction(forCurrentAppStoreVersion currentAppStoreVersion: String, completion handler: CompletionHandler?) -> UIAlertAction { + let title: String + if self.skipButtonTitle == AlertConstants.skipButtonTitle { + title = localization.skipButtonTitle() + } else { + title = self.skipButtonTitle + } + + let action = UIAlertAction(title: title, style: .default) { _ in + UserDefaults.storedSkippedVersion = currentAppStoreVersion + UserDefaults.standard.synchronize() + + self.alertController?.hide(window: self.updaterWindow) + + handler?(.skip) + return + } + + return action + } +} diff --git a/Sources/Managers/RulesManager.swift b/Sources/Managers/RulesManager.swift new file mode 100644 index 00000000..6c8f9699 --- /dev/null +++ b/Sources/Managers/RulesManager.swift @@ -0,0 +1,125 @@ +// +// RulesManager.swift +// Siren +// +// Created by Arthur Sabintsev on 12/1/18. +// Copyright © 2018 Sabintsev iOS Projects. All rights reserved. +// + +import Foundation + +/// RulesManager for Siren +public struct RulesManager { + /// The alert will only show up if the current version has already been released for X days. + /// + /// This value defaults to 1 day (in `RulesManager`'s initializer) to avoid an issue where + /// Apple updates the JSON faster than the app binary propogates to the App Store. + let releasedForDays: Int + + /// The `Rules` that should be used when the App Store version of the app signifies that it is a **major** version update (A.b.c.d). + var majorUpdateRules: Rules + + /// The `Rules` that should be used when the App Store version of the app signifies that it is a **minor** version update (a.B.c.d). + var minorUpdateRules: Rules + + /// The `Rules` that should be used when the App Store version of the app signifies that it is a **patch** version update (a.b.C.d). + var patchUpdateRules: Rules + + /// The `Rules` that should be used when the App Store version of the app signifies that it is a **revision** version update (a.b.c.D). + var revisionUpdateRules: Rules + + /// Initializer that sets update-specific `Rules` for all updates (e.g., major, minor, patch, revision). + /// This means that each of the four update types can have their own specific update rules. + /// + /// By default, the `releasedForDays` parameter delays the update alert from being presented for _1 day_ + /// to avoid an issue where the _iTunes Lookup_ API response is updated faster than the time it takes for the binary + /// to become available on App Store CDNs across all regions. Usually it takes 6-24 hours, hence the _1 day_ delay. + /// + /// - Warning: Setting `releasedForDays` to _0 days_ causes the alert to appear right away, even if the binary isn't available. + /// If this value is set to _0 days_, and an `AlertType` of type `.force` is set, it will cause your app to infinitely send the + /// end-user to the App Store to download a version that's not there and lock them out of your application until the binary is + /// is available to be downloaded. + /// + /// - Parameters: + /// - rules: The rules that should be set for all version updates. + /// - releasedForDays: The amount of time (in days) that the app should delay before presenting the user + public init(majorUpdateRules: Rules = .default, + minorUpdateRules: Rules = .default, + patchUpdateRules: Rules = .default, + revisionUpdateRules: Rules = .default, + showAlertAfterCurrentVersionHasBeenReleasedForDays releasedForDays: Int = 1) { + self.majorUpdateRules = majorUpdateRules + self.minorUpdateRules = minorUpdateRules + self.patchUpdateRules = patchUpdateRules + self.revisionUpdateRules = revisionUpdateRules + self.releasedForDays = releasedForDays + } + + /// Initializer that sets the same update `Rules` for all types of updates (e.g., major, minor, patch, revision). + /// This means that all four update types will use the same presentation rules. + /// + /// By default, the `releasedForDays` parameter delays the update alert from being presented for _1 day_ + /// to avoid an issue where the _iTunes Lookup_ API response is updated faster than the time it takes for the binary + /// to become available on App Store CDNs across all regions. Usually it takes 6-24 hours, hence the _1 day_ delay. + /// + /// - Warning: Setting `releasedForDays` to _0 days_ causes the alert to appear right away, even if the binary isn't available. + /// If this value is set to _0 days_, and an `AlertType` of type `.force` is set, it will cause your app to infinitely send the + /// end-user to the App Store to download a version that's not there and lock them out of your application until the binary is + /// is available to be downloaded. + /// + /// - Parameters: + /// - rules: The rules that should be set for all version updates. + /// - releasedForDays: The amount of time (in days) that the app should delay before presenting the user + public init(globalRules rules: Rules = .default, + showAlertAfterCurrentVersionHasBeenReleasedForDays releasedForDays: Int = 1) { + self.init(majorUpdateRules: rules, + minorUpdateRules: rules, + patchUpdateRules: rules, + revisionUpdateRules: rules, + showAlertAfterCurrentVersionHasBeenReleasedForDays: releasedForDays) + } + + /// Returns the appropriate update rules based on the type of version that is returned from the API. + /// + /// - Parameter type: The type of app update. + /// - Returns: The appropriate rule based on the type of app update that is returned by the API. + func loadRulesForUpdateType(_ type: UpdateType) -> Rules { + switch type { + case .major: return majorUpdateRules + case .minor: return minorUpdateRules + case .patch: return patchUpdateRules + case .revision: return revisionUpdateRules + case .unknown: return majorUpdateRules + } + } + + /// The default `RulesManager`. + /// + /// By default, the `Rules.default` rule is used for all update typs. + public static let `default` = RulesManager(globalRules: .default) +} + +// MARK: - RulesManager-related Constants + +extension RulesManager { + /// Informs Siren of the type of update that is available so that + /// the appropriate ruleset is used to present the update alert. + /// + /// - major: Major release available: A.b.c.d + /// - minor: Minor release available: a.B.c.d + /// - patch: Patch release available: a.b.C.d + /// - revision: Revision release available: a.b.c.D + /// - unknown: No information available about the update. + public enum UpdateType: String { + /// Major release available: A.b.c.d + case major + /// Minor release available: a.B.c.d + case minor + /// Patch release available: a.b.C.d + case patch + /// Revision release available: a.b.c.D + case revision + /// No information available about the update. + case unknown + } +} diff --git a/Sources/Models/AlertAction.swift b/Sources/Models/AlertAction.swift new file mode 100644 index 00000000..8ac5b224 --- /dev/null +++ b/Sources/Models/AlertAction.swift @@ -0,0 +1,21 @@ +// +// AlertAction.swift +// Siren +// +// Created by Arthur Sabintsev on 12/1/18. +// Copyright © 2018 Sabintsev iOS Projects. All rights reserved. +// + +import Foundation + +/// The `UIAlertController` button that was pressed upon being presented an update alert. +public enum AlertAction { + /// The user clicked on the `Update` option, which took them to the app's App Store page. + case appStore + /// The user clicked on the `Next Time` option, which dismissed the alert. + case nextTime + /// The user clicked on the `Skip this version` option, which dismissed the alert. + case skip + /// (Default) The user never chose an option. This is returned when an error is thrown by Siren. + case unknown +} diff --git a/Sources/Models/AlertConstants.swift b/Sources/Models/AlertConstants.swift new file mode 100644 index 00000000..91c6a65b --- /dev/null +++ b/Sources/Models/AlertConstants.swift @@ -0,0 +1,27 @@ +// +// AlertConstants.swift +// Siren +// +// Created by Arthur Sabintsev on 12/18/18. +// Copyright © 2018 Sabintsev iOS Projects. All rights reserved. +// + +import Foundation + +/// The default constants used for the update alert's messaging. +public struct AlertConstants { + /// The text that conveys the message that there is an app update available + public static let alertMessage = "A new version of %@ is available. Please update to version %@ now." + + /// The alert title which defaults to *Update Available*. + public static let alertTitle = "Update Available" + + /// The button text that conveys the message that the user should be prompted to update next time the app launches. + public static let nextTimeButtonTitle = "Next time" + + /// The text that conveys the message that the the user wants to skip this verison update. + public static let skipButtonTitle = "Skip this version" + + /// The button text that conveys the message that the user would like to update the app right away. + public static let updateButtonTitle = "Update" +} diff --git a/Sources/Models/Localization.swift b/Sources/Models/Localization.swift new file mode 100644 index 00000000..a58907e6 --- /dev/null +++ b/Sources/Models/Localization.swift @@ -0,0 +1,166 @@ +// +// Localization.swift +// Siren +// +// Created by Arthur Sabintsev on 9/25/18. +// Copyright © 2018 Sabintsev iOS Projects. All rights reserved. +// + +import Foundation + +/// Localization information and strings for Siren. +public struct Localization { + /// Determines the available languages in which the update message and alert button titles should appear. + /// + /// By default, the operating system's default lanuage setting is used. However, you can force a specific language + /// by setting the forceLanguageLocalization property before calling checkVersion() + public enum Language: String { + /// Arabic Language Localization + case arabic = "ar" + /// Armenian Language Localization + case armenian = "hy" + /// Basque Language Localization + case basque = "eu" + /// Simplified Chinese Language Localization + case chineseSimplified = "zh-Hans" + /// Traditional Chinese Localization Localization + case chineseTraditional = "zh-Hant" + /// Croatian Language Localization + case croatian = "hr" + /// Czech Language Localization + case czech = "cs" + /// Danish Language Localization + case danish = "da" + /// Dutch Language Localization + case dutch = "nl" + /// English Language Localization + case english = "en" + /// Estonian Language Localization + case estonian = "et" + /// Finnish Language Localization + case finnish = "fi" + /// French Language Localization + case french = "fr" + /// German Language Localization + case german = "de" + /// Greek Language Localization + case greek = "el" + /// Hebrew Language Localization + case hebrew = "he" + /// Hungarian Language Localization + case hungarian = "hu" + /// Indonesian Language Localization + case indonesian = "id" + /// Italian Language Localization + case italian = "it" + /// Japanese Language Localization + case japanese = "ja" + /// Korean Language Localization + case korean = "ko" + /// Latvian Language Localization + case latvian = "lv" + /// Lithuanian Language Localization + case lithuanian = "lt" + /// Malay Language Localization + case malay = "ms" + /// Norwegian Language Localization + case norwegian = "nb-NO" + /// Persian Language Localization + case persian = "fa" + /// Persian (Afghanistan) Language Localization + case persianAfghanistan = "fa-AF" + /// Persian (Iran) Language Localization + case persianIran = "fa-IR" + /// Polish Language Localization + case polish = "pl" + /// Brazilian Portuguese Language Localization + case portugueseBrazil = "pt" + /// Portugal's Portuguese Language Localization + case portuguesePortugal = "pt-PT" + /// Russian Language Localization + case russian = "ru" + /// Serbian (Cyrillic) Language Localization + case serbianCyrillic = "sr-Cyrl" + /// Serbian (Latin) Language Localization + case serbianLatin = "sr-Latn" + /// Slovenian Language Localization + case slovenian = "sl" + /// Spanish Language Localization + case spanish = "es" + /// Swedish Language Localization + case swedish = "sv" + /// Thai Language Localization + case thai = "th" + /// Turkish Language Localization + case turkish = "tr" + /// Urdu Language Localization + case urdu = "ur" + /// Ukranian Language Localization + case ukrainian = "uk" + /// Vietnamese Language Localization + case vietnamese = "vi" + } + + /// The name of the app as defined by the `Info.plist`. + private var appName: String = Bundle.bestMatchingAppName() + + /// Overrides the default localization of a user's device when presenting the update message and button titles in the alert. + /// + /// See the Siren.Localization.Language enum for more details. + private let forceLanguage: Language? + + /// Initializes + /// + /// - Parameters: + /// - appName: Overrides the default name of the app. This is optional and defaults to the app that is defined in the `Info.plist`. + /// - forceLanguage: The language the alert to which the alert should be set. If `nil`, it falls back to the device's preferred locale. + init(appName: String?, andForceLanguageLocalization forceLanguage: Language?) { + if let appName = appName { + self.appName = appName + } + + self.forceLanguage = forceLanguage + } + + /// The localized string for the `UIAlertController`'s message field. . + /// + /// - Returns: A localized string for the update message. + public func alertMessage(forCurrentAppStoreVersion currentAppStoreVersion: String) -> String { + let message = Bundle.localizedString(forKey: AlertConstants.alertMessage, + andForceLocalization: forceLanguage) + + return String(format: message, appName, currentAppStoreVersion) + } + + /// The localized string for the `UIAlertController`'s title field. . + /// + /// - Returns: A localized string for the phrase "Update Available". + public func alertTitle() -> String { + return Bundle.localizedString(forKey: AlertConstants.alertTitle, + andForceLocalization: forceLanguage) + } + + /// The localized string for the "Next time" `UIAlertAction`. + /// + /// - Returns: A localized string for the phrase "Next time". + public func nextTimeButtonTitle() -> String { + return Bundle.localizedString(forKey: AlertConstants.nextTimeButtonTitle, + andForceLocalization: forceLanguage) + } + + /// The localized string for the "Skip this version" `UIAlertAction`. + /// + /// - Returns: A localized string for the phrase "Skip this version". + public func skipButtonTitle() -> String { + return Bundle.localizedString(forKey: AlertConstants.skipButtonTitle, + andForceLocalization: forceLanguage) + } + + /// The localized string for the "Update" `UIAlertAction`. + /// + /// - Returns: A localized string for the phrase "Update". + public func updateButtonTitle() -> String { + return Bundle.localizedString(forKey: AlertConstants.updateButtonTitle, + andForceLocalization: forceLanguage) + } +} diff --git a/Sources/Models/SirenLookupModel.swift b/Sources/Models/LookupModel.swift similarity index 65% rename from Sources/Models/SirenLookupModel.swift rename to Sources/Models/LookupModel.swift index 58a02c6e..85cc6454 100644 --- a/Sources/Models/SirenLookupModel.swift +++ b/Sources/Models/LookupModel.swift @@ -1,5 +1,5 @@ // -// SirenLookupModel.swift +// LookupModel.swift // Siren // // Created by Arthur Sabintsev on 8/6/17. @@ -8,11 +8,11 @@ import Foundation -// MARK: - Model representing a selection of results from the iTunes Lookup API - -/// MARK: Siren extension used to parse and map the iTunes JSON results into a model represented in Swift. -public struct SirenLookupModel: Decodable { +/// Model representing a selection of results from the iTunes Lookup API. +public struct LookupModel: Decodable { + /// Codable Coding Keys for the Top-Level iTunes Lookup API JSON response. private enum CodingKeys: String, CodingKey { + /// The results JSON key. case results } @@ -21,11 +21,17 @@ public struct SirenLookupModel: Decodable { /// The Results object from the the iTunes Lookup API. public struct Results: Decodable { + /// Codable Coding Keys for the Results array in the iTunes Lookup API JSON response. private enum CodingKeys: String, CodingKey { + /// The appID JSON key. case appID = "trackId" + /// The current version release date JSON key. case currentVersionReleaseDate + /// The minimum device iOS version compatibility JSON key. case minimumOSVersion = "minimumOsVersion" + /// The release notes JSON key. case releaseNotes + /// The current App Store version JSON key. case version } diff --git a/Sources/Models/Results.swift b/Sources/Models/Results.swift new file mode 100644 index 00000000..57cb6f3e --- /dev/null +++ b/Sources/Models/Results.swift @@ -0,0 +1,25 @@ +// +// Results.swift +// Siren +// +// Created by Arthur Sabintsev on 12/1/18. +// Copyright © 2018 Sabintsev iOS Projects. All rights reserved. +// + +import Foundation + +/// The relevant metadata returned from Siren upon completing a successful version check. +public struct Results { + /// The `UIAlertAction` the user chose upon being presented with the update alert. + /// Defaults to `unknown` until an alert is actually presented. + public var alertAction: AlertAction = .unknown + + /// The Siren-supported locale that was used for the string in the update alert. + public var localization: Localization + + /// The Swift-mapped API model, if a successful version check was performed. + public var lookupModel: LookupModel + + /// The type of update that was returned for the API. + public var updateType: RulesManager.UpdateType = .unknown +} diff --git a/Sources/Models/Rules.swift b/Sources/Models/Rules.swift new file mode 100644 index 00000000..e01cc282 --- /dev/null +++ b/Sources/Models/Rules.swift @@ -0,0 +1,86 @@ +// +// Rules.swift +// Siren +// +// Created by Sabintsev, Arthur on 11/18/18. +// Copyright © 2018 Sabintsev iOS Projects. All rights reserved. +// + +import Foundation + +/// Alert Presentation Rules for Siren. +public struct Rules { + /// The type of alert that should be presented. + let alertType: AlertType + + /// The frequency in which a the user is prompted to update the app + /// once a new version is available in the App Store and if they have not updated yet. + let frequency: UpdatePromptFrequency + + /// Initializes the alert presentation rules. + /// + /// - Parameters: + /// - frequency: How often a user should be prompted to update the app once a new version is available in the App Store. + /// - alertType: The type of alert that should be presented. + public init(promptFrequency frequency: UpdatePromptFrequency, + forAlertType alertType: AlertType) { + self.frequency = frequency + self.alertType = alertType + } + + /// Performs a version check immediately, but allows the user to skip updating the app until the next time the app becomes active. + public static var annoying: Rules { + return Rules(promptFrequency: .immediately, forAlertType: .option) + } + + /// Performs a version check immediately and forces the user to update the app. + public static var critical: Rules { + return Rules(promptFrequency: .immediately, forAlertType: .force) + } + + /// Performs a version check once a day, but allows the user to skip updating the app until + /// the next time the app becomes active or skipping the update all together until another version is released. + /// + /// This is the default setting. + public static var `default`: Rules { + return Rules(promptFrequency: .daily, forAlertType: .skip) + } + + /// Performs a version check daily, but allows the user to skip updating the app until the next time the app becomes active. + public static var persistent: Rules { + return Rules(promptFrequency: .daily, forAlertType: .option) + } + + /// Performs a version check weekly, but allows the user to skip updating the app until + /// the next time the app becomes active or skipping the update all together until another version is released. + public static var relaxed: Rules { + return Rules(promptFrequency: .weekly, forAlertType: .skip) + } +} + +// Rules-related Constants +public extension Rules { + /// Determines the type of alert to present after a successful version check has been performed. + public enum AlertType { + /// Forces the user to update your app (1 button alert). + case force + /// Presents the user with option to update app now or at next launch (2 button alert). + case option + /// Presents the user with option to update the app now, at next launch, or to skip this version all together (3 button alert). + case skip + /// Doesn't present the alert. + /// Use this option if you would like to present a custom alert to the end-user. + case none + } + + /// Determines the frequency in which the user is prompted to update the app + /// once a new version is available in the App Store and if they have not updated yet. + public enum UpdatePromptFrequency: UInt { + /// Version check performed every time the app is launched. + case immediately = 0 + /// Version check performed once a day. + case daily = 1 + /// Version check performed once a week. + case weekly = 7 + } +} diff --git a/Sources/Models/SirenAlertMessaging.swift b/Sources/Models/SirenAlertMessaging.swift deleted file mode 100644 index ca27dbc8..00000000 --- a/Sources/Models/SirenAlertMessaging.swift +++ /dev/null @@ -1,64 +0,0 @@ -// -// SirenAlertMessaging.swift -// Siren -// -// Created by Arthur Sabintsev on 12/6/17. -// Copyright © 2017 Sabintsev iOS Projects. All rights reserved. -// - -import Foundation - -// MARK: - Siren Alert Messaging Customization - -/// Allows the overriding of all the `UIAlertController` and `UIActionSheet` Strings to which Siren defaults. -/// -/// - Warning: Overriding any of these keys will result in the loss of the built-in internationalization that Siren provides. -/// -/// As `SirenAlertMessaging` is a Struct, one _or_ more keys can be modified. Overriding only one string will result in the other keys retaining their default (and internationalizable) values. -public struct SirenAlertMessaging { - - /// The default constants used for the alert messaging. - public struct Constants { - - /// The button text that conveys the message that the user should be prompted to update next time the app launches. - public static let nextTime = NSAttributedString(string: "Next time") - - /// The text that conveys the message that the the user wants to skip this verison update. - public static let skipVersion = NSAttributedString(string: "Skip this version") - - /// The text that conveys the message that there is an app update available - public static let updateMessage = NSAttributedString(string: "A new version of %@ is available. Please update to version %@ now.") - - /// The alert title which defaults to *Update Available*. - public static let updateTitle = NSAttributedString(string: "Update Available") - - /// The button text that conveys the message that the user would like to update the app right away. - public static let updateNow = NSAttributedString(string: "Update") - } - - let nextTimeButtonMessage: NSAttributedString - let skipVersionButtonMessage: NSAttributedString - let updateButtonMessage: NSAttributedString - let updateMessage: NSAttributedString - let updateTitle: NSAttributedString - - /// The public initializer - /// - /// - Parameters: - /// - title: The title field of the `UIAlertController`. - /// - message: The `message` field of the `UIAlertController`. - /// - updateButtonMessage: The `title` field of the Update Button `UIAlertAction`. - /// - nextTimeButtonMessage: The `title` field of the Next Time Button `UIAlertAction`. - /// - skipVersionButtonMessage: The `title` field of the Skip Button `UIAlertAction`. - public init(updateTitle title: NSAttributedString = Constants.updateTitle, - updateMessage message: NSAttributedString = Constants.updateMessage, - updateButtonMessage: NSAttributedString = Constants.updateNow, - nextTimeButtonMessage: NSAttributedString = Constants.nextTime, - skipVersionButtonMessage: NSAttributedString = Constants.skipVersion) { - self.updateTitle = title - self.nextTimeButtonMessage = nextTimeButtonMessage - self.updateButtonMessage = updateButtonMessage - self.updateMessage = message - self.skipVersionButtonMessage = skipVersionButtonMessage - } -} diff --git a/Sources/Protocols/SirenDelegate.swift b/Sources/Protocols/SirenDelegate.swift deleted file mode 100644 index d7902f58..00000000 --- a/Sources/Protocols/SirenDelegate.swift +++ /dev/null @@ -1,110 +0,0 @@ -// -// SirenDelegate.swift -// SirenExample -// -// Created by Arthur Sabintsev on 4/8/17. -// Copyright © 2017 Sabintsev iOS Projects. All rights reserved. -// - -import Foundation - -/// MARK - Siren UpdateType - -/// `UpdateType` defines what kind of update is available. -/// It is used as parameter if user wants to use -/// custom alert to inform the user about an update. -/// -/// - major: Major release available: A.b.c.d -/// - minor: Minor release available: a.B.c.d -/// - patch: Patch release available: a.b.C.d -/// - revision: Revision release available: a.b.c.D -/// - unknown: No information available about the update. -public enum UpdateType: String { - /// Major release available: A.b.c.d - case major - /// Minor release available: a.B.c.d - case minor - /// Patch release available: a.b.C.d - case patch - /// Revision release available: a.b.c.D - case revision - /// No information available about the update. - case unknown -} - -// MARK: - SirenDelegate Protocol - -/// Delegate that handles all codepaths for Siren upon version check completion. -public protocol SirenDelegate: NSObjectProtocol { - /// Siren performed a version check and did not display an alert. - func sirenDidDetectNewVersionWithoutAlert(title: String, message: String, updateType: UpdateType) - - /// Siren failed to perform version check. - /// - /// - Note: - /// Depending on the reason for failure, - /// a system-level error may be returned. - func sirenDidFailVersionCheck(error: Error) - - /// User presented with an update dialog. - /// - /// - Parameter alertType: The type of alert that was presented. - func sirenDidShowUpdateDialog(alertType: Siren.AlertType) - - /// Siren performed a version check and the latest version was already installed. - func sirenLatestVersionInstalled() - - /// Provides the decoded JSON information from a successful version check call. - /// - /// - Parameter lookupModel: The `Decodable` model representing the JSON results from the iTunes Lookup API. - func sirenNetworkCallDidReturnWithNewVersionInformation(lookupModel: SirenLookupModel) - - /// User did click on button that cancels update dialog. - func sirenUserDidCancel() - - /// User did click on button that launched "App Store.app". - func sirenUserDidLaunchAppStore() - - /// User did click on button that skips version update. - func sirenUserDidSkipVersion() -} - -// MARK: - SirenDelegate Protocol Extension - -public extension SirenDelegate { - func sirenDidDetectNewVersionWithoutAlert(title: String, message: String, updateType: UpdateType) { - printMessage() - } - - func sirenDidFailVersionCheck(error: Error) { - printMessage() - } - - func sirenDidShowUpdateDialog(alertType: Siren.AlertType) { - printMessage() - } - - func sirenLatestVersionInstalled() { - printMessage() - } - - func sirenUserDidCancel() { - printMessage() - } - - func sirenUserDidLaunchAppStore() { - printMessage() - } - - func sirenNetworkCallDidReturnWithNewVersionInformation(lookupModel: SirenLookupModel) { - printMessage() - } - - func sirenUserDidSkipVersion() { - printMessage() - } - - private func printMessage(_ function: String = #function) { - SirenLog("The default implementation of \(function) is being called. You can ignore this message if you do not care to implement this method in your `SirenDelegate` conforming structure.") - } -} diff --git a/Sources/Siren.swift b/Sources/Siren.swift index 7b49d499..84ec9b70 100644 --- a/Sources/Siren.swift +++ b/Sources/Siren.swift @@ -8,144 +8,79 @@ import UIKit -// MARK: - Siren - -/// The Siren Class. A singleton that is initialized using the `shared` constant. +/// The Siren Class. public final class Siren: NSObject { + /// Return results or errors obtained from performing a version check with Siren. + public typealias ResultsHandler = (Results?, KnownError?) -> Void - /// Current installed version of your app. - internal var currentInstalledVersion: String? = { - return Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as? String - }() - - /// The error domain for all errors created by Siren. - public let SirenErrorDomain = "Siren Error Domain" - - /// The `SirenDelegate` variable, which should be set if you'd like to be notified of any of specific user interactions or API success/failures. - /// Also set this variable if you'd like to use custom UI for presesnting the update notification. - public weak var delegate: SirenDelegate? + /// The Siren singleton. The main point of entry to the Siren library. + public static let shared = Siren() /// The debug flag, which is disabled by default. - /// When enabled, a stream of print() statements are logged to your console when a version check is performed. - public lazy var debugEnabled = false - - /// Determines the type of alert that should be shown. - /// See the Siren.AlertType enum for full details. - public var alertType: AlertType = .option { - didSet { - majorUpdateAlertType = alertType - minorUpdateAlertType = alertType - patchUpdateAlertType = alertType - revisionUpdateAlertType = alertType - } - } - - /// Determines the type of alert that should be shown for major version updates: A.b.c - /// Defaults to Siren.AlertType.option. - /// See the Siren.AlertType enum for full details. - public lazy var majorUpdateAlertType: AlertType = .option - - /// Determines the type of alert that should be shown for minor version updates: a.B.c - /// Defaults to Siren.AlertType.option. - /// See the Siren.AlertType enum for full details. - public lazy var minorUpdateAlertType: AlertType = .option - - /// Determines the type of alert that should be shown for minor patch updates: a.b.C - /// Defaults to Siren.AlertType.option. - /// See the Siren.AlertType enum for full details. - public lazy var patchUpdateAlertType: AlertType = .option - - /// Determines the type of alert that should be shown for revision updates: a.b.c.D - /// Defaults to Siren.AlertType.option. - /// See the Siren.AlertType enum for full details. - public lazy var revisionUpdateAlertType: AlertType = .option - - /// The name of your app. - /// By default, it's set to the name of the app that's stored in your plist. - public lazy var appName = Bundle.bestMatchingAppName() - - /// Overrides all the Strings to which Siren defaults. - /// Defaults to the values defined in `SirenAlertMessaging.Constants` - public var alertMessaging = SirenAlertMessaging() - - /// The region or country of an App Store in which your app is available. - /// By default, all version checks are performed against the US App Store. - /// If your app is not available in the US App Store, set it to the identifier of at least one App Store within which it is available. - public var countryCode: String? - - /// Overrides the default localization of a user's device when presenting the update message and button titles in the alert. - /// See the Siren.LanguageType enum for more details. - public var forceLanguageLocalization: Siren.LanguageType? + /// When enabled, a stream of `print()` statements are logged to your console when a version check is performed. + public lazy var debugEnabled: Bool = false - /// Overrides the tint color for UIAlertController. - public var alertControllerTintColor: UIColor? + /// The manager that controls the App Store API that is + /// used to fetch the latest version of the app. + /// + /// Defaults to the US App Store. + public lazy var apiManager: APIManager = .default - /// When this is set, the alert will only show up if the current version has already been released for X days. - /// Defaults to 1 day to avoid an issue where Apple updates the JSON faster than the app binary propogates to the App Store. - public var showAlertAfterCurrentVersionHasBeenReleasedForDays: Int = 1 + /// The manager that controls the update alert's string localization and tint color. + /// + /// Defaults the string's lange localization to the user's device localization. + public lazy var presentationManager: PresentationManager = .default - /// The current version of your app that is available for download on the App Store - public internal(set) var currentAppStoreVersion: String? + /// The manager that controls the type of alert that should be displayed + /// and how often an alert should be displayed dpeneding on the type + /// of update that is available relative to the installed version of the app + /// (e.g., different rules for major, minor, patch and revision updated can be used). + /// + /// Defaults to performing a version check once a day with an alert that allows + /// the user to skip updating the app until the next time the app becomes active or + /// skipping the update all together until another version is released. + public lazy var rulesManager: RulesManager = .default - /// The `UIWindow` instance that presents the `SirenAlertViewController`. - var updaterWindow: UIWindow? + /// The current installed version of your app. + lazy var currentInstalledVersion: String? = Bundle.version() - /// The last Date that a version check was performed. - var lastVersionCheckPerformedOnDate: Date? + /// The retained `NotificationCenter` observer that listens for `UIApplication.didBecomeActiveNotification` notifications. + var didBecomeActiveObserver: NSObjectProtocol? - fileprivate var appID: Int? - fileprivate lazy var alertViewIsVisible: Bool = false + /// The last date that an alert was presented to the user. + private var alertPresentationDate: Date? - /// Type of the available update - fileprivate var updateType: UpdateType = .unknown + /// The App Store's unique identifier for an app. + private var appID: Int? - /// The App's Singleton - public static let shared = Siren() + /// The completion handler used to return the results or errors returned by Siren. + private var resultsHandler: ResultsHandler? - override init() { - lastVersionCheckPerformedOnDate = UserDefaults.storedVersionCheckDate + /// The initialization method. + private override init() { + alertPresentationDate = UserDefaults.alertPresentationDate } +} - /// Checks the currently installed version of your app against the App Store. - /// The default check is against the US App Store, but if your app is not listed in the US, - /// you should set the `countryCode` property before calling this method. Please refer to the countryCode property for more information. - /// - /// - Parameters: - /// - checkType: The frequency in days in which you want a check to be performed. Please refer to the Siren.VersionCheckType enum for more details. - public func checkVersion(checkType: VersionCheckType) { - updateType = .unknown - - guard Bundle.bundleID() != nil else { - printMessage("Please make sure that you have set a `Bundle Identifier` in your project.") - return - } - - if checkType == .immediately { - performVersionCheck() - } else if UserDefaults.shouldPerformVersionCheckOnSubsequentLaunch { - UserDefaults.shouldPerformVersionCheckOnSubsequentLaunch = false - performVersionCheck() - } else { - guard let lastVersionCheckPerformedOnDate = lastVersionCheckPerformedOnDate else { - performVersionCheck() - return - } +// MARK: - Public Functionality - if Date.days(since: lastVersionCheckPerformedOnDate) >= checkType.rawValue { - performVersionCheck() - } else { - postError(.recentlyCheckedAlready) - } - } +public extension Siren { + /// + /// + /// - Parameter handler: + func wail(completion handler: ResultsHandler? = nil) { + resultsHandler = handler + addObservers() } - /// Launches the AppStore in two situations: - /// - /// - User clicked the `Update` button in the UIAlertController modal. - /// - Developer built a custom alert modal and needs to be able to call this function when the user chooses to update the app in the aforementioned custom modal. - public func launchAppStore() { + /// Launches the AppStore in two situations when the user clicked the `Update` button in the UIAlertController modal. + /// + /// This function is marked `public` as a convenience for those developers who decide to build a custom alert modal + /// instead of using Siren's prebuilt update alert. + func launchAppStore() { guard let appID = appID, let url = URL(string: "https://itunes.apple.com/app/id\(appID)") else { + resultsHandler?(nil, .malformedURL) return } @@ -159,233 +94,145 @@ public final class Siren: NSObject { } } -// MARK: - Networking +// MARK: - Private Functionality -private extension Siren { +extension Siren { + /// Initiates the uni-directional version checking flow. func performVersionCheck() { - do { - let url = try iTunesURLFromString() - let request = URLRequest(url: url, cachePolicy: .reloadIgnoringLocalAndRemoteCacheData, timeoutInterval: 30) - URLCache.shared.removeCachedResponse(for: request) - URLSession.shared.dataTask(with: request, completionHandler: { [weak self] (data, response, error) in - self?.processResults(withData: data, response: response, error: error) - }).resume() - } catch { - postError(.malformedURL) - } - } - - func processResults(withData data: Data?, response: URLResponse?, error: Error?) { - if let error = error { - postError(.appStoreDataRetrievalFailure(underlyingError: error)) - } else { - guard let data = data else { - return postError(.appStoreDataRetrievalFailure(underlyingError: nil)) - } - do { - let decodedData = try JSONDecoder().decode(SirenLookupModel.self, from: data) - - guard !decodedData.results.isEmpty else { - return postError(.appStoreDataRetrievalEmptyResults) - } - - DispatchQueue.main.async { [weak self] in - guard let self = self else { return } - - self.printMessage("Decoded JSON results: \(decodedData)") - self.delegate?.sirenNetworkCallDidReturnWithNewVersionInformation(lookupModel: decodedData) - self.processVersionCheck(with: decodedData) - } - } catch { - postError(.appStoreJSONParsingFailure(underlyingError: error)) + alertPresentationDate = UserDefaults.alertPresentationDate + apiManager.performVersionCheckRequest { [weak self] (lookupModel, error) in + guard let self = self else { return } + guard let lookupModel = lookupModel, error == nil else { + self.resultsHandler?(nil, error) + return } + + self.validate(model: lookupModel) } } - func processVersionCheck(with model: SirenLookupModel) { - guard isUpdateCompatibleWithDeviceOS(for: model) else { return } + /// Validates the parsed and mapped iTunes Lookup Model + /// to guarantee all the relevant data was returned before + /// attempting to present an alert. + /// + /// - Parameter model: The iTunes Lookup Model. + func validate(model: LookupModel) { + // Check if the latest version is compatible with current device's version of iOS. + guard DataParser.isUpdateCompatibleWithDeviceOS(for: model) else { + resultsHandler?(nil, .appStoreOSVersionUnsupported) + return + } + // Check and store the App ID . guard let appID = model.results.first?.appID else { - return postError(.appStoreAppIDFailure) + resultsHandler?(nil, .appStoreAppIDFailure) + return } - self.appID = appID + // Check and store the current App Store version. guard let currentAppStoreVersion = model.results.first?.version else { - return postError(.appStoreVersionArrayFailure) + resultsHandler?(nil, .appStoreVersionArrayFailure) + return } - self.currentAppStoreVersion = currentAppStoreVersion - - guard isAppStoreVersionNewer() else { - delegate?.sirenLatestVersionInstalled() - postError(.noUpdateAvailable) + // Check if the App Store version is newer than the currently installed version. + guard DataParser.isAppStoreVersionNewer(installedVersion: currentInstalledVersion, + appStoreVersion: currentAppStoreVersion) else { + resultsHandler?(nil, .noUpdateAvailable) return } + // Check the release date of the current version. guard let currentVersionReleaseDate = model.results.first?.currentVersionReleaseDate, let daysSinceRelease = Date.days(since: currentVersionReleaseDate) else { - return - } - - guard daysSinceRelease >= showAlertAfterCurrentVersionHasBeenReleasedForDays else { - let message = "Your app has been released for \(daysSinceRelease) days, but Siren cannot prompt the user until \(showAlertAfterCurrentVersionHasBeenReleasedForDays) days have passed." - self.printMessage(message) - return - } - - showAlertIfCurrentAppStoreVersionNotSkipped() - } - - func iTunesURLFromString() throws -> URL { - var components = URLComponents() - components.scheme = "https" - components.host = "itunes.apple.com" - components.path = "/lookup" - - var items: [URLQueryItem] = [URLQueryItem(name: "bundleId", value: Bundle.bundleID())] - - if let countryCode = countryCode { - let item = URLQueryItem(name: "country", value: countryCode) - items.append(item) - } - - components.queryItems = items - - guard let url = components.url, !url.absoluteString.isEmpty else { - throw SirenError.Known.malformedURL + resultsHandler?(nil, .currentVersionReleaseDate) + return } - return url - } -} - -// MARK: - Alert - -private extension Siren { - func showAlertIfCurrentAppStoreVersionNotSkipped() { - alertType = setAlertType() - - guard let previouslySkippedVersion = UserDefaults.storedSkippedVersion else { - showAlert() + // Check if applicaiton has been released for the amount of days defined by the app consuming Siren. + guard daysSinceRelease >= rulesManager.releasedForDays else { + resultsHandler?(nil, .releasedTooSoon(daysSinceRelease: daysSinceRelease, + releasedForDays: rulesManager.releasedForDays)) return } - if let currentAppStoreVersion = currentAppStoreVersion, currentAppStoreVersion != previouslySkippedVersion { - showAlert() - } - } - - func showAlert() { - storeVersionCheckDate() - - let updateAvailableMessage = localizedUpdateTitle() - let newVersionMessage = localizedNewVersionMessage() - - let alertController = UIAlertController(title: updateAvailableMessage, message: newVersionMessage, preferredStyle: .alert) - - if let alertControllerTintColor = alertControllerTintColor { - alertController.view.tintColor = alertControllerTintColor - } - - switch alertType { - case .force: - alertController.addAction(updateAlertAction()) - case .option: - alertController.addAction(nextTimeAlertAction()) - alertController.addAction(updateAlertAction()) - case .skip: - alertController.addAction(nextTimeAlertAction()) - alertController.addAction(updateAlertAction()) - alertController.addAction(skipAlertAction()) - case .none: - let updateTitle = localizedUpdateTitle() - delegate?.sirenDidDetectNewVersionWithoutAlert(title: updateTitle, message: newVersionMessage, updateType: updateType) - } - - if alertType != .none && !alertViewIsVisible { - alertController.show() - alertViewIsVisible = true - delegate?.sirenDidShowUpdateDialog(alertType: alertType) - } + determineIfAlertPresentationRulesAreSatisfied(forCurrentAppStoreVersion: currentAppStoreVersion, andLookupModel: model) } - func updateAlertAction() -> UIAlertAction { - let title = localizedUpdateButtonTitle() - let action = UIAlertAction(title: title, style: .default) { [weak self] _ in - guard let self = self else { return } - - self.hideWindow() - self.launchAppStore() - self.delegate?.sirenUserDidLaunchAppStore() - self.alertViewIsVisible = false - return + /// Determines if the update alert can be presented based on the + /// rules set in the `RulesManager` and the the skip version settings. + /// + /// - Parameters: + /// - currentAppStoreVersion: The curren version of the app in the App Store. + /// - model: The iTunes Lookup Model. + func determineIfAlertPresentationRulesAreSatisfied(forCurrentAppStoreVersion currentAppStoreVersion: String, andLookupModel model: LookupModel) { + // Did the user: + // - request to skip being prompted with version update alerts for a specific version + // - and is the latest App Store update the same version that was requested? + if let previouslySkippedVersion = UserDefaults.storedSkippedVersion, + let currentInstalledVersion = currentInstalledVersion, + !currentAppStoreVersion.isEmpty, + currentAppStoreVersion != previouslySkippedVersion { + resultsHandler?(nil, .skipVersionUpdate(installedVersion: currentInstalledVersion, appStoreVersion: currentAppStoreVersion)) + return } - return action - } + let updateType = DataParser.parseForUpdate(forInstalledVersion: currentInstalledVersion, + andAppStoreVersion: currentAppStoreVersion) + let rules = rulesManager.loadRulesForUpdateType(updateType) - func nextTimeAlertAction() -> UIAlertAction { - let title = localizedNextTimeButtonTitle() - let action = UIAlertAction(title: title, style: .default) { [weak self] _ in - guard let self = self else { return } - - self.hideWindow() - self.delegate?.sirenUserDidCancel() - self.alertViewIsVisible = false - UserDefaults.shouldPerformVersionCheckOnSubsequentLaunch = true - return + if rules.frequency == .immediately { + presentAlert(withRules: rules, forCurrentAppStoreVersion: currentAppStoreVersion, model: model, andUpdateType: updateType) + } else if UserDefaults.shouldPerformVersionCheckOnSubsequentLaunch { + UserDefaults.shouldPerformVersionCheckOnSubsequentLaunch = false + presentAlert(withRules: rules, forCurrentAppStoreVersion: currentAppStoreVersion, model: model, andUpdateType: updateType) + } else { + guard let alertPresentationDate = alertPresentationDate else { + presentAlert(withRules: rules, forCurrentAppStoreVersion: currentAppStoreVersion, model: model, andUpdateType: updateType) + return + } + if Date.days(since: alertPresentationDate) >= rules.frequency.rawValue { + presentAlert(withRules: rules, forCurrentAppStoreVersion: currentAppStoreVersion, model: model, andUpdateType: updateType) + } else { + resultsHandler?(nil, .recentlyPrompted) + } } - - return action } - func skipAlertAction() -> UIAlertAction { - let title = localizedSkipButtonTitle() - let action = UIAlertAction(title: title, style: .default) { [weak self] _ in + /// Presents the update alert to the end user. + /// Upon tapping a value on the alert view, a completion handler will return all relevant metadata to the app. + /// + /// - Parameters: + /// - rules: The rules for how to present the alert. + /// - currentAppStoreVersion: The current version of the app in the App Store. + /// - model: The iTunes Lookup Model. + /// - updateType: The type of update that is available based on the version found in the App Store. + func presentAlert(withRules rules: Rules, + forCurrentAppStoreVersion currentAppStoreVersion: String, + model: LookupModel, + andUpdateType updateType: RulesManager.UpdateType) { + presentationManager.presentAlert(withRules: rules, forCurrentAppStoreVersion: currentAppStoreVersion) { [weak self] alertAction in guard let self = self else { return } - - if let currentAppStoreVersion = self.currentAppStoreVersion { - UserDefaults.storedSkippedVersion = currentAppStoreVersion - UserDefaults.standard.synchronize() - } - - self.hideWindow() - self.delegate?.sirenUserDidSkipVersion() - self.alertViewIsVisible = false - return + let results = Results(alertAction: alertAction, + localization: self.presentationManager.localization, + lookupModel: model, + updateType: updateType) + self.resultsHandler?(results, nil) } - - return action } - func setAlertType() -> Siren.AlertType { - guard let currentInstalledVersion = currentInstalledVersion, - let currentAppStoreVersion = currentAppStoreVersion else { - return .option - } - - let oldVersion = (currentInstalledVersion).lazy.split {$0 == "."}.map { String($0) }.map {Int($0) ?? 0} - let newVersion = (currentAppStoreVersion).lazy.split {$0 == "."}.map { String($0) }.map {Int($0) ?? 0} - - guard let newVersionFirst = newVersion.first, let oldVersionFirst = oldVersion.first else { - return alertType // Default value is .Option + /// Add an observer that listens for app launching/relaunching + /// (e.g., calls to `UIApplication`'s `didBecomeActive` function). + func addObservers() { + guard didBecomeActiveObserver == nil else { return } + didBecomeActiveObserver = NotificationCenter + .default + .addObserver(forName: UIApplication.didBecomeActiveNotification, + object: nil, + queue: nil) { [weak self] _ in + guard let self = self else { return } + self.performVersionCheck() } - - if newVersionFirst > oldVersionFirst { // A.b.c.d - alertType = majorUpdateAlertType - updateType = .major - } else if newVersion.count > 1 && (oldVersion.count <= 1 || newVersion[1] > oldVersion[1]) { // a.B.c.d - alertType = minorUpdateAlertType - updateType = .minor - } else if newVersion.count > 2 && (oldVersion.count <= 2 || newVersion[2] > oldVersion[2]) { // a.b.C.d - alertType = patchUpdateAlertType - updateType = .patch - } else if newVersion.count > 3 && (oldVersion.count <= 3 || newVersion[3] > oldVersion[3]) { // a.b.c.D - alertType = revisionUpdateAlertType - updateType = .revision - } - - return alertType } } diff --git a/Sources/Utilities/DataParser.swift b/Sources/Utilities/DataParser.swift new file mode 100644 index 00000000..849238da --- /dev/null +++ b/Sources/Utilities/DataParser.swift @@ -0,0 +1,92 @@ +// +// DataParser.swift +// Siren +// +// Created by Arthur Sabintsev on 11/25/18. +// Copyright © 2018 Sabintsev iOS Projects. All rights reserved. +// + +import Foundation + +/// Version parsing functions for Siren. +struct DataParser { + /// Checks to see if the App Store version of the app is newer than the installed version. + /// + /// - Parameters: + /// - installedVersion: The installed version of the app. + /// - appStoreVersion: The App Store version of the app. + /// - Returns: `true` if the App Store version is newer. Otherwise, `false`. + static func isAppStoreVersionNewer(installedVersion: String?, appStoreVersion: String?) -> Bool { + guard let installedVersion = installedVersion, + let appStoreVersion = appStoreVersion, + (installedVersion.compare(appStoreVersion, options: .numeric) == .orderedAscending) else { + return false + } + + return true + } + + /// Validates that the latest version in the App Store is compatible with the device's current version of iOS. + /// + /// - Parameter model: The iTunes Lookup Model. + /// - Returns: `true` if the latest version is compatible with the device's current version of iOS. Otherwise, `false`. + static func isUpdateCompatibleWithDeviceOS(for model: LookupModel) -> Bool { + guard let requiredOSVersion = model.results.first?.minimumOSVersion else { + return false + } + + let systemVersion = UIDevice.current.systemVersion + + guard systemVersion.compare(requiredOSVersion, options: .numeric) == .orderedDescending || + systemVersion.compare(requiredOSVersion, options: .numeric) == .orderedSame else { + return false + } + + return true + } + + /// The type of update that is returned from the API in relation to the verison of the app that is installed. + /// + /// - Parameters: + /// - installedVersion: The installed version of the app. + /// - appStoreVersion: The App Store version of the app. + /// - Returns: The type of update in relation to the verison of the app that is installed. + static func parseForUpdate(forInstalledVersion installedVersion: String?, + andAppStoreVersion appStoreVersion: String?) -> RulesManager.UpdateType { + guard let installedVersion = installedVersion, + let appStoreVersion = appStoreVersion else { + return .unknown + } + + let oldVersion = split(version: installedVersion) + let newVersion = split(version: appStoreVersion) + + guard let newVersionFirst = newVersion.first, + let oldVersionFirst = oldVersion.first else { + return .unknown + } + + if newVersionFirst > oldVersionFirst { // A.b.c.d + return .major + } else if newVersion.count > 1 && (oldVersion.count <= 1 || newVersion[1] > oldVersion[1]) { // a.B.c.d + return .minor + } else if newVersion.count > 2 && (oldVersion.count <= 2 || newVersion[2] > oldVersion[2]) { // a.b.C.d + return .patch + } else if newVersion.count > 3 && (oldVersion.count <= 3 || newVersion[3] > oldVersion[3]) { // a.b.c.D + return .revision + } else { + return .unknown + } + } + + /// Splits a version-formatted `String into an `[Int]`. + /// + /// Converts `"a.b.c.d"` into `[a, b, c, d]`. + /// + /// - Parameter version: The version formatted `String`. + /// + /// - Returns: An array of integers representing a version of the app. + private static func split(version: String) -> [Int] { + return version.lazy.split {$0 == "."}.map { String($0) }.map {Int($0) ?? 0} + } +} diff --git a/Sources/Utilities/KnownError.swift b/Sources/Utilities/KnownError.swift new file mode 100644 index 00000000..da1fce32 --- /dev/null +++ b/Sources/Utilities/KnownError.swift @@ -0,0 +1,78 @@ +// +// KnownError.swift +// Siren +// +// Created by Arthur Sabintsev on 8/6/17. +// Copyright © 2017 Sabintsev iOS Projects. All rights reserved. +// + +import Foundation + +/// Enumerates all potentials errors that Siren can handle. +public enum KnownError: LocalizedError { + /// Error retrieving trackId as the JSON does not contain a 'trackId' key. + case appStoreAppIDFailure + /// Error retrieving App Store data as JSON results were empty. Is your app available in the US? If not, change the `countryCode` variable to fix this error. + case appStoreDataRetrievalEmptyResults + /// Error retrieving App Store data as an error was returned. + case appStoreDataRetrievalFailure(underlyingError: Error?) + /// Error parsing App Store JSON data. + case appStoreJSONParsingFailure(underlyingError: Error) + /// The version of iOS on the device is lower than that of the one required by the app verison update. + case appStoreOSVersionUnsupported + /// Error retrieving App Store verson number as the JSON does not contain a `version` key. + case appStoreVersionArrayFailure + /// The `currentVersionReleaseDate` key is missing in the JSON payload. Please leave an issue on https://github.com/ArtSabintsev/Siren with as many details as possible. + case currentVersionReleaseDate + /// One of the iTunes URLs used in Siren is malformed. Please leave an issue on https://github.com/ArtSabintsev/Siren with as many details as possible. + case malformedURL + /// Please make sure that you have set a `Bundle Identifier` in your project. + case missingBundleID + /// No new update available. + case noUpdateAvailable + /// Siren will not present an update alert if it performed one too recently. If you would like to present an alert every time Siren is called, please consider setting the `UpdatePromptFrequency.immediately` rule in `RulesManager` + case recentlyPrompted + /// The app has been released for X days, but Siren cannot prompt the user until Y (where Y > X) days have passed. + case releasedTooSoon(daysSinceRelease: Int, releasedForDays: Int) + /// The user has opted to skip updating their current version of the app to the current App Store version. + case skipVersionUpdate(installedVersion: String, appStoreVersion: String) + + /// The localized description for each error handled by Siren. + public var localizedDescription: String { + switch self { + case .appStoreAppIDFailure: + return "\(KnownError.sirenError) Error retrieving trackId as the JSON does not contain a `trackId` key." + case .appStoreDataRetrievalFailure(let error?): + return "\(KnownError.sirenError) Error retrieving App Store data as an error was returned\nAlso, the following system level error was returned: \(error)" + case .appStoreDataRetrievalEmptyResults: + return "\(KnownError.sirenError) Error retrieving App Store data as the JSON results were empty. Is your app available in the US? If not, change the `countryCode` variable to fix this error." + case .appStoreDataRetrievalFailure(.none): + return "\(KnownError.sirenError) Error retrieving App Store data as an error was returned." + case .appStoreJSONParsingFailure(let error): + return "\(KnownError.sirenError) Error parsing App Store JSON data.\nAlso, the following system level error was returned: \(error)" + case .appStoreOSVersionUnsupported: + return "\(KnownError.sirenError) The version of iOS on the device is lower than that of the one required by the app verison update." + case .appStoreVersionArrayFailure: + return "\(KnownError.sirenError) Error retrieving App Store verson number as the JSON does not contain a `version` key." + case .currentVersionReleaseDate: + return "\(KnownError.sirenError) The `currentVersionReleaseDate` key is missing in the JSON payload. Please leave an issue on https://github.com/ArtSabintsev/Siren with as many details as possible." + case .malformedURL: + return "\(KnownError.sirenError) One of the iTunes URLs used in Siren is malformed. Please leave an issue on https://github.com/ArtSabintsev/Siren with as many details as possible." + case .missingBundleID: + return "\(KnownError.sirenError) Please make sure that you have set a `Bundle Identifier` in your project." + case .noUpdateAvailable: + return "\(KnownError.sirenError) No new update available." + case .recentlyPrompted: + return "\(KnownError.sirenError) Siren will not present an update alert if it performed one too recently. If you would like to present an alert every time Siren is called, please consider setting the `\(Rules.UpdatePromptFrequency.self).immediately` rule in `\(RulesManager.self)`" + case .releasedTooSoon(let daysSinceRelease, let releasedForDays): + return "\(KnownError.sirenError) The app has been released for \(daysSinceRelease) days, but Siren cannot prompt the user until \(releasedForDays) days have passed." + case .skipVersionUpdate(let installedVersion, let appStoreVersion): + return "\(KnownError.sirenError) The user has opted to skip updating their current version of the app (\(installedVersion)) to the current App Store version (\(appStoreVersion))." + } + } + + /// An easily identifiable prefix for all errors thrown by Siren. + private static var sirenError: String { + return "[Siren Error]" + } +} diff --git a/Sources/Utilities/SirenConstants.swift b/Sources/Utilities/SirenConstants.swift deleted file mode 100644 index 06b7803c..00000000 --- a/Sources/Utilities/SirenConstants.swift +++ /dev/null @@ -1,129 +0,0 @@ -// -// SirenConstants.swift -// Siren -// -// Created by Arthur Sabintsev on 9/25/18. -// Copyright © 2018 Sabintsev iOS Projects. All rights reserved. -// - -import Foundation - -// MARK: - Enumerated Types - -// MARK: Siren extension dealing with enumerated types and constants. -public extension Siren { - /// Determines the type of alert to present after a successful version check has been performed. - enum AlertType { - /// Forces user to update your app (1 button alert). - case force - /// (DEFAULT) Presents user with option to update app now or at next launch (2 button alert). - case option - /// Presents user with option to update the app now, at next launch, or to skip this version all together (3 button alert). - case skip - /// Doesn't show the alert, but instead returns a localized message - /// for use in a custom UI within the sirenDidDetectNewVersionWithoutAlert() delegate method. - case none - } - - /// Determines the frequency in which the the version check is performed and the user is prompted to update the app. - /// - enum VersionCheckType: Int { - /// Version check performed every time the app is launched. - case immediately = 0 - /// Version check performed once a day. - case daily = 1 - /// Version check performed once a week. - case weekly = 7 - } - - /// Determines the available languages in which the update message and alert button titles should appear. - /// - /// By default, the operating system's default lanuage setting is used. However, you can force a specific language - /// by setting the forceLanguageLocalization property before calling checkVersion() - enum LanguageType: String { - /// Arabic - case arabic = "ar" - /// Armenian - case armenian = "hy" - /// Basque - case basque = "eu" - /// Simplified Chinese - case chineseSimplified = "zh-Hans" - /// Traditional Chinese - case chineseTraditional = "zh-Hant" - /// Croatian - case croatian = "hr" - /// Czech - case czech = "cs" - /// Danish - case danish = "da" - /// Dutch - case dutch = "nl" - /// English - case english = "en" - /// Estonian - case estonian = "et" - /// Finnish - case finnish = "fi" - /// French - case french = "fr" - /// German - case german = "de" - /// Greek - case greek = "el" - /// Hebrew - case hebrew = "he" - /// Hungarian - case hungarian = "hu" - /// Indonesian - case indonesian = "id" - /// Italian - case italian = "it" - /// Japanese - case japanese = "ja" - /// Korean - case korean = "ko" - /// Latvian - case latvian = "lv" - /// Lithuanian - case lithuanian = "lt" - /// Malaysian - case malay = "ms" - /// Norwegian - case norwegian = "nb-NO" - /// Persian - case persian = "fa" - /// Persian (Afghanistan) - case persianAfghanistan = "fa-AF" - /// Persian (Iran) - case persianIran = "fa-IR" - /// Polish - case polish = "pl" - /// Portuguese (Brazil) - case portugueseBrazil = "pt" - /// Portuguese (Portugal) - case portuguesePortugal = "pt-PT" - /// Russian - case russian = "ru" - /// Serbian (Cyrillic) - case serbianCyrillic = "sr-Cyrl" - /// Serbian (Latin) - case serbianLatin = "sr-Latn" - /// Slovenian - case slovenian = "sl" - /// Spanish - case spanish = "es" - /// Swedish - case swedish = "sv" - /// Thai - case thai = "th" - /// Turkish - case turkish = "tr" - /// Urdu - case urdu = "ur" - /// Ukranian - case ukrainian = "uk" - /// Vietnamese - case vietnamese = "vi" - } -} diff --git a/Sources/Utilities/SirenError.swift b/Sources/Utilities/SirenError.swift deleted file mode 100644 index 95b3d008..00000000 --- a/Sources/Utilities/SirenError.swift +++ /dev/null @@ -1,86 +0,0 @@ -// -// SirenError.swift -// Siren -// -// Created by Arthur Sabintsev on 8/6/17. -// Copyright © 2017 Sabintsev iOS Projects. All rights reserved. -// - -import Foundation - -// MARK: - Siren Error Handling - -/// Data structure used to build Siren specific Errors. -public struct SirenError: LocalizedError { - /// Enumerates all potentials errors that Siren can handle. - /// - /// - appStoreAppIDFailure: Error retrieving trackId as the JSON does not contain a 'trackId' key. - /// - appStoreDataRetrievalFailure: Error retrieving App Store data as an error was returned. - /// - appStoreJSONParsingFailure: Error parsing App Store JSON data. - /// - appStoreDataRetrievalEmptyResults: Error retrieving App Store data as JSON results were empty. Is your app available in the US? If not, change the `countryCode` variable to fix this error. - /// - appStoreOSVersionNumberFailure: Error retrieving iOS version number as there was no data returned. - /// - appStoreOSVersionUnsupported: The version of iOS on the device is lower than that of the one required by the app verison update. - /// - appStoreVersionArrayFailure: Error retrieving App Store verson number as the JSON does not contain a 'version' key. - /// - malformedURL: The iTunes URL is malformed. Please leave an issue on https://github.com/ArtSabintsev/Siren with as many details as possible. - /// - noUpdateAvailable: No new update available. - /// - recentlyCheckedAlready: Not checking the version, because it was already checked recently. - public enum Known: Error { - /// Error retrieving trackId as the JSON does not contain a 'trackId' key. - case appStoreAppIDFailure - /// Error retrieving App Store data as an error was returned. - case appStoreDataRetrievalFailure(underlyingError: Error?) - /// Error parsing App Store JSON data. - case appStoreJSONParsingFailure(underlyingError: Error) - /// Error retrieving App Store data as JSON results were empty. Is your app available in the US? If not, change the `countryCode` variable to fix this error. - case appStoreDataRetrievalEmptyResults - /// Error retrieving iOS version number as there was no data returned. - case appStoreOSVersionNumberFailure - /// The version of iOS on the device is lower than that of the one required by the app verison update. - case appStoreOSVersionUnsupported - /// Error retrieving App Store verson number as the JSON does not contain a 'version' key. - case appStoreVersionArrayFailure - /// The iTunes URL is malformed. Please leave an issue on https://github.com/ArtSabintsev/Siren with as many details as possible. - case malformedURL - /// No new update available. - case noUpdateAvailable - /// Not checking the version, because it was already checked recently. - case recentlyCheckedAlready - - /// The localized description for each error handled by Siren. - var localizedDescription: String { - switch self { - case .appStoreAppIDFailure: - return "Error retrieving trackId as the JSON does not contain a 'trackId' key." - case .appStoreDataRetrievalFailure(let error?): - return "Error retrieving App Store data as an error was returned\nAlso, the following system level error was returned: \(error)" - case .appStoreDataRetrievalFailure(.none): - return "Error retrieving App Store data as an error was returned." - case .appStoreDataRetrievalEmptyResults: - return "Error retrieving App Store data as the JSON results were empty. Is your app available in the US? If not, change the `countryCode` variable to fix this error." - case .appStoreJSONParsingFailure(let error): - return "Error parsing App Store JSON data.\nAlso, the following system level error was returned: \(error)" - case .appStoreOSVersionNumberFailure: - return "Error retrieving iOS version number as there was no data returned." - case .appStoreOSVersionUnsupported: - return "The version of iOS on the device is lower than that of the one required by the app verison update." - case .appStoreVersionArrayFailure: - return "Error retrieving App Store verson number as the JSON does not contain a 'version' key." - case .malformedURL: - return "The iTunes URL is malformed. Please leave an issue on https://github.com/ArtSabintsev/Siren with as many details as possible." - case .noUpdateAvailable: - return "No new update available." - case .recentlyCheckedAlready: - return "Not checking the version, because it was already checked recently." - } - } - } -} - -// MARK: - Error Handling - -extension Siren { - func postError(_ error: SirenError.Known) { - delegate?.sirenDidFailVersionCheck(error: error) - printMessage(error.localizedDescription) - } -} diff --git a/Sources/Utilities/SirenHelpers.swift b/Sources/Utilities/SirenHelpers.swift deleted file mode 100644 index 112e43fa..00000000 --- a/Sources/Utilities/SirenHelpers.swift +++ /dev/null @@ -1,83 +0,0 @@ -// -// SirenHelpers.swift -// Siren -// -// Created by Arthur Sabintsev on 9/25/18. -// Copyright © 2018 Sabintsev iOS Projects. All rights reserved. -// - -import Foundation - -// MARK: - Version - -extension Siren { - func isAppStoreVersionNewer() -> Bool { - var newVersionExists = false - - if let currentInstalledVersion = currentInstalledVersion, - let currentAppStoreVersion = currentAppStoreVersion, - (currentInstalledVersion.compare(currentAppStoreVersion, options: .numeric) == .orderedAscending) { - - newVersionExists = true - } - - return newVersionExists - } - - func storeVersionCheckDate() { - lastVersionCheckPerformedOnDate = Date() - if let lastVersionCheckPerformedOnDate = lastVersionCheckPerformedOnDate { - UserDefaults.storedVersionCheckDate = lastVersionCheckPerformedOnDate - UserDefaults.standard.synchronize() - } - } -} - -// MARK: - Miscellaneous - -extension Siren { - func isUpdateCompatibleWithDeviceOS(for model: SirenLookupModel) -> Bool { - guard let requiredOSVersion = model.results.first?.minimumOSVersion else { - postError(.appStoreOSVersionNumberFailure) - return false - } - - let systemVersion = UIDevice.current.systemVersion - - guard systemVersion.compare(requiredOSVersion, options: .numeric) == .orderedDescending || - systemVersion.compare(requiredOSVersion, options: .numeric) == .orderedSame else { - postError(.appStoreOSVersionUnsupported) - return false - } - - return true - } - - func hideWindow() { - if let updaterWindow = updaterWindow { - updaterWindow.isHidden = true - self.updaterWindow = nil - } - } - - /// Routes a console-bound message to the `SirenLog` struct, which decorates the log message. - /// - /// - Parameter message: The message to decorate and log to the console. - func printMessage(_ message: String) { - if debugEnabled { - SirenLog(message) - } - } -} - -// MARK: - Test Target - -extension Siren { - func testSetCurrentInstalledVersion(version: String) { - currentInstalledVersion = version - } - - func testSetAppStoreVersion(version: String) { - currentAppStoreVersion = version - } -} diff --git a/Sources/Utilities/SirenLog.swift b/Sources/Utilities/SirenLog.swift deleted file mode 100644 index a64d45bf..00000000 --- a/Sources/Utilities/SirenLog.swift +++ /dev/null @@ -1,18 +0,0 @@ -// -// SirenLog.swift -// SirenExample -// -// Created by Arthur Sabintsev on 8/5/17. -// Copyright © 2017 Sabintsev iOS Projects. All rights reserved. -// - -import Foundation - -// MARK: - Log and decorate Siren-specific messages to the console. - -struct SirenLog { - @discardableResult - init(_ message: String) { - print("[Siren] \(message)") - } -} diff --git a/Sources/View Controllers/SirenViewController.swift b/Sources/View Controllers/SirenViewController.swift index b9712d13..a0a47dd3 100644 --- a/Sources/View Controllers/SirenViewController.swift +++ b/Sources/View Controllers/SirenViewController.swift @@ -1,6 +1,6 @@ // // SirenViewController.swift -// SirenExample +// Siren // // Created by Arthur Sabintsev on 3/17/17. // Copyright © 2017 Sabintsev iOS Projects. All rights reserved. @@ -9,8 +9,10 @@ import Foundation import UIKit -// MARK: - UIViewController Extension for Siren - +/// `UIViewController` Extension for Siren final class SirenViewController: UIViewController { - override var preferredStatusBarStyle: UIStatusBarStyle { return UIApplication.shared.statusBarStyle } + /// `UIStatusBarStyle` override. + override var preferredStatusBarStyle: UIStatusBarStyle { + return UIApplication.shared.statusBarStyle + } } diff --git a/docs/Classes.html b/docs/Classes.html index 48c529f0..6dd105a4 100644 --- a/docs/Classes.html +++ b/docs/Classes.html @@ -34,13 +34,7 @@ Siren - - @@ -48,15 +42,33 @@ Enumerations @@ -64,22 +76,52 @@ Structures @@ -94,13 +136,6 @@

Classes

-
- - - -

Siren

-
-
  • @@ -115,7 +150,7 @@

    Siren

    -

    The Siren Class. A singleton that is initialized using the shared constant.

    +

    The Siren Class.

    See more
    @@ -132,10 +167,42 @@

    Declaration

+
+
    +
  • +
    + + + + SirenViewController + +
    +
    +
    +
    +
    +
    +

    UIViewController Extension for Siren

    + + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    final class SirenViewController : UIViewController
    + +
    +
    +
    +
    +
  • +
+
diff --git a/docs/Classes/Siren.html b/docs/Classes/Siren.html index 66663cda..8441ed14 100644 --- a/docs/Classes/Siren.html +++ b/docs/Classes/Siren.html @@ -34,13 +34,7 @@ Siren - - @@ -48,15 +42,33 @@ Enumerations @@ -64,22 +76,52 @@ Structures @@ -95,7 +137,7 @@

Siren

-

The Siren Class. A singleton that is initialized using the shared constant.

+

The Siren Class.

@@ -104,9 +146,9 @@

Siren

  • @@ -114,14 +156,14 @@

    Siren

    -

    The error domain for all errors created by Siren.

    +

    Return results or errors obtained from performing a version check with Siren.

    Declaration

    Swift

    -
    public let SirenErrorDomain: String
    +
    public typealias ResultsHandler = (Results?, KnownError?) -> Void
    @@ -131,9 +173,9 @@

    Declaration

  • - - - delegate + + + shared
    @@ -141,15 +183,14 @@

    Declaration

    -

    The SirenDelegate variable, which should be set if you’d like to be notified of any of specific user interactions or API success/failures. -Also set this variable if you’d like to use custom UI for presesnting the update notification.

    +

    The Siren singleton. The main point of entry to the Siren library.

    Declaration

    Swift

    -
    public weak var delegate: SirenDelegate?
    +
    public static let shared: Siren
    @@ -170,7 +211,7 @@

    Declaration

    The debug flag, which is disabled by default. -When enabled, a stream of print() statements are logged to your console when a version check is performed.

    +When enabled, a stream of print() statements are logged to your console when a version check is performed.

    @@ -187,9 +228,9 @@

    Declaration

  • - - - alertType + + + apiManager
    @@ -197,44 +238,17 @@

    Declaration

    -

    Determines the type of alert that should be shown. -See the Siren.AlertType enum for full details.

    +

    The manager that controls the App Store API that is +used to fetch the latest version of the app.

    -
    -
    -

    Declaration

    -
    -

    Swift

    -
    public var alertType: Siren.AlertType { get set }
    - -
    -
    -
    -
    -
  • -
  • -
    - - - - majorUpdateAlertType - -
    -
    -
    -
    -
    -
    -

    Determines the type of alert that should be shown for major version updates: A.b.c -Defaults to Siren.AlertType.option. -See the Siren.AlertType enum for full details.

    +

    Defaults to the US App Store.

    Declaration

    Swift

    -
    public lazy var majorUpdateAlertType: AlertType { get set }
    +
    public lazy var apiManager: APIManager { get set }
    @@ -244,9 +258,9 @@

    Declaration

  • @@ -254,16 +268,16 @@

    Declaration

    -

    Determines the type of alert that should be shown for minor version updates: a.B.c -Defaults to Siren.AlertType.option. -See the Siren.AlertType enum for full details.

    +

    The manager that controls the update alert’s string localization and tint color.

    + +

    Defaults the string’s lange localization to the user’s device localization.

    Declaration

    Swift

    -
    public lazy var minorUpdateAlertType: AlertType { get set }
    +
    public lazy var presentationManager: PresentationManager { get set }
    @@ -273,9 +287,9 @@

    Declaration

  • @@ -283,16 +297,21 @@

    Declaration

    -

    Determines the type of alert that should be shown for minor patch updates: a.b.C -Defaults to Siren.AlertType.option. -See the Siren.AlertType enum for full details.

    +

    The manager that controls the type of alert that should be displayed +and how often an alert should be displayed dpeneding on the type +of update that is available relative to the installed version of the app +(e.g., different rules for major, minor, patch and revision updated can be used).

    + +

    Defaults to performing a version check once a day with an alert that allows +the user to skip updating the app until the next time the app becomes active or +skipping the update all together until another version is released.

    Declaration

    Swift

    -
    public lazy var patchUpdateAlertType: AlertType { get set }
    +
    public lazy var rulesManager: RulesManager { get set }
    @@ -302,9 +321,9 @@

    Declaration

  • @@ -312,16 +331,14 @@

    Declaration

    -

    Determines the type of alert that should be shown for revision updates: a.b.c.D -Defaults to Siren.AlertType.option. -See the Siren.AlertType enum for full details.

    +

    The current installed version of your app.

    Declaration

    Swift

    -
    public lazy var revisionUpdateAlertType: AlertType { get set }
    +
    lazy var currentInstalledVersion: String? { get set }
    @@ -331,9 +348,9 @@

    Declaration

  • @@ -341,15 +358,14 @@

    Declaration

    -

    The name of your app. -By default, it’s set to the name of the app that’s stored in your plist.

    +

    The retained NotificationCenter observer that listens for UIApplication.didBecomeActiveNotification notifications.

    Declaration

    Swift

    -
    public lazy var appName: String { get set }
    +
    var didBecomeActiveObserver: NSObjectProtocol?
    @@ -359,9 +375,9 @@

    Declaration

  • @@ -369,15 +385,14 @@

    Declaration

    -

    Overrides all the Strings to which Siren defaults. -Defaults to the values defined in SirenAlertMessaging.Constants

    +

    The last date that an alert was presented to the user.

    Declaration

    Swift

    -
    public var alertMessaging: SirenAlertMessaging
    +
    private var alertPresentationDate: Date?
    @@ -387,9 +402,9 @@

    Declaration

  • - - - countryCode + + + appID
    @@ -397,16 +412,14 @@

    Declaration

    -

    The region or country of an App Store in which your app is available. -By default, all version checks are performed against the US App Store. -If your app is not available in the US App Store, set it to the identifier of at least one App Store within which it is available.

    +

    The App Store’s unique identifier for an app.

    Declaration

    Swift

    -
    public var countryCode: String?
    +
    private var appID: Int?
    @@ -416,9 +429,9 @@

    Declaration

  • @@ -426,15 +439,14 @@

    Declaration

    -

    Overrides the default localization of a user’s device when presenting the update message and button titles in the alert. -See the Siren.LanguageType enum for more details.

    +

    The completion handler used to return the results or errors returned by Siren.

    Declaration

    Swift

    -
    public var forceLanguageLocalization: Siren.LanguageType?
    +
    private var resultsHandler: Siren.ResultsHandler?
    @@ -444,9 +456,9 @@

    Declaration

  • @@ -454,26 +466,37 @@

    Declaration

    -

    Overrides the tint color for UIAlertController.

    +

    The initialization method.

    Declaration

    Swift

    -
    public var alertControllerTintColor: UIColor?
    +
    private override init()
  • + +
    +
    + +
    • @@ -481,27 +504,44 @@

      Declaration

      -

      When this is set, the alert will only show up if the current version has already been released for X days. -Defaults to 1 day to avoid an issue where Apple updates the JSON faster than the app binary propogates to the App Store.

      - +

      Declaration

      Swift

      -
      public var showAlertAfterCurrentVersionHasBeenReleasedForDays: Int
      +
      func wail(completion handler: ResultsHandler? = nil)
      +
      +

      Parameters

      + + + + + + + +
      + + handler + + +
      +

      +
      +
      +
    • @@ -509,26 +549,40 @@

      Declaration

      -

      The current version of your app that is available for download on the App Store

      +

      Launches the AppStore in two situations when the user clicked the Update button in the UIAlertController modal.

      + +

      This function is marked public as a convenience for those developers who decide to build a custom alert modal +instead of using Siren’s prebuilt update alert.

      Declaration

      Swift

      -
      public internal(set) var currentAppStoreVersion: String?
      +
      func launchAppStore()
    • +
    +
    +
    + +
    • @@ -536,14 +590,14 @@

      Declaration

      -

      The App’s Singleton

      +

      Initiates the uni-directional version checking flow.

      Declaration

      Swift

      -
      public static let shared: Siren
      +
      func performVersionCheck()
      @@ -553,9 +607,9 @@

      Declaration

    • @@ -563,16 +617,16 @@

      Declaration

      -

      Checks the currently installed version of your app against the App Store. -The default check is against the US App Store, but if your app is not listed in the US, -you should set the countryCode property before calling this method. Please refer to the countryCode property for more information.

      +

      Validates the parsed and mapped iTunes Lookup Model +to guarantee all the relevant data was returned before +attempting to present an alert.

      Declaration

      Swift

      -
      public func checkVersion(checkType: VersionCheckType)
      +
      func validate(model: LookupModel)
      @@ -583,12 +637,12 @@

      Parameters

      - checkType + model
      -

      The frequency in days in which you want a check to be performed. Please refer to the Siren.VersionCheckType enum for more details.

      +

      The iTunes Lookup Model.

      @@ -601,9 +655,9 @@

      Parameters

    • @@ -611,60 +665,48 @@

      Parameters

      -

      Launches the AppStore in two situations:

      - -
        -
      • User clicked the Update button in the UIAlertController modal.
      • -
      • Developer built a custom alert modal and needs to be able to call this function when the user chooses to update the app in the aforementioned custom modal.
      • -
      +

      Determines if the update alert can be presented based on the +rules set in the RulesManager and the the skip version settings.

      Declaration

      Swift

      -
      public func launchAppStore()
      +
      func determineIfAlertPresentationRulesAreSatisfied(forCurrentAppStoreVersion currentAppStoreVersion: String, andLookupModel model: LookupModel)
      -
      -
      -
    • -
    -
    -
    - -
      -
    • -
      - - - - AlertType - -
      -
      -
      -
      -
      -
      -

      Determines the type of alert to present after a successful version check has been performed.

      - - See more -
      -
      -

      Declaration

      -
      -

      Swift

      -
      enum AlertType
      - -
      +
      +

      Parameters

      + + + + + + + + + + + +
      + + currentAppStoreVersion + + +
      +

      The curren version of the app in the App Store.

      +
      +
      + + model + + +
      +

      The iTunes Lookup Model.

      +
      +
      @@ -672,9 +714,9 @@

      Declaration

    • @@ -682,27 +724,85 @@

      Declaration

      -

      Determines the frequency in which the the version check is performed and the user is prompted to update the app.

      +

      Presents the update alert to the end user. +Upon tapping a value on the alert view, a completion handler will return all relevant metadata to the app.

      - See more

      Declaration

      Swift

      -
      enum VersionCheckType : Int
      +
      func presentAlert(withRules rules: Rules,
      +                  forCurrentAppStoreVersion currentAppStoreVersion: String,
      +                  model: LookupModel,
      +                  andUpdateType updateType: RulesManager.UpdateType)
      +
      +

      Parameters

      + + + + + + + + + + + + + + + + + + + +
      + + rules + + +
      +

      The rules for how to present the alert.

      +
      +
      + + currentAppStoreVersion + + +
      +

      The current version of the app in the App Store.

      +
      +
      + + model + + +
      +

      The iTunes Lookup Model.

      +
      +
      + + updateType + + +
      +

      The type of update that is available based on the version found in the App Store.

      +
      +
      +
    • @@ -710,18 +810,15 @@

      Declaration

      -

      Determines the available languages in which the update message and alert button titles should appear.

      - -

      By default, the operating system’s default lanuage setting is used. However, you can force a specific language -by setting the forceLanguageLocalization property before calling checkVersion()

      +

      Add an observer that listens for app launching/relaunching +(e.g., calls to UIApplication‘s didBecomeActive function).

      - See more

      Declaration

      Swift

      -
      enum LanguageType : String
      +
      func addObservers()
      @@ -733,7 +830,7 @@

      Declaration

  • diff --git a/docs/Classes/Siren/AlertType.html b/docs/Classes/Siren/AlertType.html index 414365b5..dac6a08f 100644 --- a/docs/Classes/Siren/AlertType.html +++ b/docs/Classes/Siren/AlertType.html @@ -64,22 +64,22 @@ Structures
  • @@ -104,9 +104,9 @@

    AlertType

  • - + - force + force
    @@ -131,9 +131,9 @@

    Declaration

  • - + - option + option
    @@ -158,9 +158,9 @@

    Declaration

  • - + - skip + skip
    @@ -185,9 +185,9 @@

    Declaration

  • - + - none + none
    @@ -215,7 +215,7 @@

    Declaration

  • diff --git a/docs/Classes/Siren/LanguageType.html b/docs/Classes/Siren/LanguageType.html index 0802a3cd..1c849a98 100644 --- a/docs/Classes/Siren/LanguageType.html +++ b/docs/Classes/Siren/LanguageType.html @@ -64,22 +64,22 @@ Structures
  • @@ -107,9 +107,9 @@

    LanguageType

  • - + - arabic + arabic
    @@ -134,9 +134,9 @@

    Declaration

  • - + - armenian + armenian
    @@ -161,9 +161,9 @@

    Declaration

  • - + - basque + basque
    @@ -188,9 +188,9 @@

    Declaration

  • @@ -215,9 +215,9 @@

    Declaration

  • @@ -242,9 +242,9 @@

    Declaration

  • - + - croatian + croatian
    @@ -269,9 +269,9 @@

    Declaration

  • - + - czech + czech
    @@ -296,9 +296,9 @@

    Declaration

  • - + - danish + danish
    @@ -323,9 +323,9 @@

    Declaration

  • - + - dutch + dutch
    @@ -350,9 +350,9 @@

    Declaration

  • - + - english + english
    @@ -377,9 +377,9 @@

    Declaration

  • - + - estonian + estonian
    @@ -404,9 +404,9 @@

    Declaration

  • - + - finnish + finnish
    @@ -431,9 +431,9 @@

    Declaration

  • - + - french + french
    @@ -458,9 +458,9 @@

    Declaration

  • - + - german + german
    @@ -485,9 +485,9 @@

    Declaration

  • - + - greek + greek
    @@ -512,9 +512,9 @@

    Declaration

  • - + - hebrew + hebrew
    @@ -539,9 +539,9 @@

    Declaration

  • @@ -566,9 +566,9 @@

    Declaration

  • @@ -593,9 +593,9 @@

    Declaration

  • - + - italian + italian
    @@ -620,9 +620,9 @@

    Declaration

  • - + - japanese + japanese
    @@ -647,9 +647,9 @@

    Declaration

  • - + - korean + korean
    @@ -674,9 +674,9 @@

    Declaration

  • - + - latvian + latvian
    @@ -701,9 +701,9 @@

    Declaration

  • @@ -728,9 +728,9 @@

    Declaration

  • - + - malay + malay
    @@ -755,9 +755,9 @@

    Declaration

  • @@ -782,9 +782,9 @@

    Declaration

  • - + - persian + persian
    @@ -809,9 +809,9 @@

    Declaration

  • @@ -836,9 +836,9 @@

    Declaration

  • @@ -863,9 +863,9 @@

    Declaration

  • - + - polish + polish
    @@ -890,9 +890,9 @@

    Declaration

  • @@ -917,9 +917,9 @@

    Declaration

  • @@ -944,9 +944,9 @@

    Declaration

  • - + - russian + russian
    @@ -971,9 +971,9 @@

    Declaration

  • @@ -998,9 +998,9 @@

    Declaration

  • @@ -1025,9 +1025,9 @@

    Declaration

  • @@ -1052,9 +1052,9 @@

    Declaration

  • - + - spanish + spanish
    @@ -1079,9 +1079,9 @@

    Declaration

  • - + - swedish + swedish
    @@ -1106,9 +1106,9 @@

    Declaration

  • - + - thai + thai
    @@ -1133,9 +1133,9 @@

    Declaration

  • - + - turkish + turkish
    @@ -1160,9 +1160,9 @@

    Declaration

  • - + - urdu + urdu
    @@ -1187,9 +1187,9 @@

    Declaration

  • @@ -1214,9 +1214,9 @@

    Declaration

  • @@ -1243,7 +1243,7 @@

    Declaration

  • diff --git a/docs/Classes/Siren/VersionCheckType.html b/docs/Classes/Siren/VersionCheckType.html index 5a289556..548a463d 100644 --- a/docs/Classes/Siren/VersionCheckType.html +++ b/docs/Classes/Siren/VersionCheckType.html @@ -64,22 +64,22 @@ Structures
  • @@ -104,9 +104,9 @@

    VersionCheckType

  • @@ -131,9 +131,9 @@

    Declaration

  • - + - daily + daily
    @@ -158,9 +158,9 @@

    Declaration

  • - + - weekly + weekly
    @@ -187,7 +187,7 @@

    Declaration

  • diff --git a/docs/Classes/SirenViewController.html b/docs/Classes/SirenViewController.html new file mode 100644 index 00000000..543f954f --- /dev/null +++ b/docs/Classes/SirenViewController.html @@ -0,0 +1,185 @@ + + + + SirenViewController Class Reference + + + + + + + + + + +
    +
    +

    Siren Docs (100% documented)

    +
    +
    +
    + +
    +
    + +
    +
    +
    +

    SirenViewController

    +
    +
    +
    final class SirenViewController : UIViewController
    + +
    +
    +

    UIViewController Extension for Siren

    + +
    +
    +
    +
      +
    • + +
      +
      +
      +
      +
      +

      UIStatusBarStyle override.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      override var preferredStatusBarStyle: UIStatusBarStyle { get }
      + +
      +
      +
      +
      +
    • +
    +
    +
    +
    + +
    +
    + +
    + diff --git a/docs/Enums.html b/docs/Enums.html index bcf77e18..5e6c536b 100644 --- a/docs/Enums.html +++ b/docs/Enums.html @@ -34,13 +34,7 @@ Siren
  • - -
  • @@ -48,15 +42,33 @@ Enumerations
  • @@ -64,22 +76,52 @@ Structures @@ -98,9 +140,9 @@

    Enumerations

  • - - - UpdateType + + + AlertAction
    @@ -108,26 +150,47 @@

    Enumerations

    -

    MARK - Siren UpdateType -UpdateType defines what kind of update is available. -It is used as parameter if user wants to use -custom alert to inform the user about an update.

    +

    The UIAlertController button that was pressed upon being presented an update alert.

    -
      -
    • major: Major release available: A.b.c.d
    • -
    • minor: Minor release available: a.B.c.d
    • -
    • patch: Patch release available: a.b.C.d
    • -
    • revision: Revision release available: a.b.c.D
    • -
    • unknown: No information available about the update.
    • -
    + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public enum AlertAction
    + +
    +
    +
    +
    +
  • + + +
    +
      +
    • +
      + + + + KnownError + +
      +
      +
      +
      +
      +
      +

      Enumerates all potentials errors that Siren can handle.

      - See more + See more

      Declaration

      Swift

      -
      public enum UpdateType : String
      +
      public enum KnownError : LocalizedError
      @@ -139,7 +202,7 @@

      Declaration

    diff --git a/docs/Enums/AlertAction.html b/docs/Enums/AlertAction.html new file mode 100644 index 00000000..df23cf7a --- /dev/null +++ b/docs/Enums/AlertAction.html @@ -0,0 +1,266 @@ + + + + AlertAction Enumeration Reference + + + + + + + + + + +
    +
    +

    Siren Docs (100% documented)

    +
    +
    +
    + +
    +
    + +
    +
    +
    +

    AlertAction

    +
    +
    +
    public enum AlertAction
    + +
    +
    +

    The UIAlertController button that was pressed upon being presented an update alert.

    + +
    +
    +
    +
      +
    • +
      + + + + appStore + +
      +
      +
      +
      +
      +
      +

      The user clicked on the Update option, which took them to the app’s App Store page.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case appStore
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + nextTime + +
      +
      +
      +
      +
      +
      +

      The user clicked on the Next Time option, which dismissed the alert.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case nextTime
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + skip + +
      +
      +
      +
      +
      +
      +

      The user clicked on the Skip this version option, which dismissed the alert.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case skip
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + unknown + +
      +
      +
      +
      +
      +
      +

      (Default) The user never chose an option. This is returned when an error is thrown by Siren.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case unknown
      + +
      +
      +
      +
      +
    • +
    +
    +
    +
    + +
    +
    + + + diff --git a/docs/Enums/KnownError.html b/docs/Enums/KnownError.html new file mode 100644 index 00000000..e60c3168 --- /dev/null +++ b/docs/Enums/KnownError.html @@ -0,0 +1,563 @@ + + + + KnownError Enumeration Reference + + + + + + + + + + +
    +
    +

    Siren Docs (100% documented)

    +
    +
    +
    + +
    +
    + +
    +
    +
    +

    KnownError

    +
    +
    +
    public enum KnownError : LocalizedError
    + +
    +
    +

    Enumerates all potentials errors that Siren can handle.

    + +
    +
    +
    +
      +
    • +
      + + + + appStoreAppIDFailure + +
      +
      +
      +
      +
      +
      +

      Error retrieving trackId as the JSON does not contain a ‘trackId’ key.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case appStoreAppIDFailure
      + +
      +
      +
      +
      +
    • +
    • + +
      +
      +
      +
      +
      +

      Error retrieving App Store data as JSON results were empty. Is your app available in the US? If not, change the countryCode variable to fix this error.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case appStoreDataRetrievalEmptyResults
      + +
      +
      +
      +
      +
    • +
    • + +
      +
      +
      +
      +
      +

      Error retrieving App Store data as an error was returned.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case appStoreDataRetrievalFailure(underlyingError: Error?)
      + +
      +
      +
      +
      +
    • +
    • + +
      +
      +
      +
      +
      +

      Error parsing App Store JSON data.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case appStoreJSONParsingFailure(underlyingError: Error)
      + +
      +
      +
      +
      +
    • +
    • + +
      +
      +
      +
      +
      +

      The version of iOS on the device is lower than that of the one required by the app verison update.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case appStoreOSVersionUnsupported
      + +
      +
      +
      +
      +
    • +
    • + +
      +
      +
      +
      +
      +

      Error retrieving App Store verson number as the JSON does not contain a version key.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case appStoreVersionArrayFailure
      + +
      +
      +
      +
      +
    • +
    • + +
      +
      +
      +
      +
      +

      The currentVersionReleaseDate key is missing in the JSON payload. Please leave an issue on https://github.com/ArtSabintsev/Siren with as many details as possible.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case currentVersionReleaseDate
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + malformedURL + +
      +
      +
      +
      +
      +
      +

      One of the iTunes URLs used in Siren is malformed. Please leave an issue on https://github.com/ArtSabintsev/Siren with as many details as possible.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case malformedURL
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + missingBundleID + +
      +
      +
      +
      +
      +
      +

      Please make sure that you have set a Bundle Identifier in your project.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case missingBundleID
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + noUpdateAvailable + +
      +
      +
      +
      +
      +
      +

      No new update available.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case noUpdateAvailable
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + recentlyPrompted + +
      +
      +
      +
      +
      +
      +

      Siren will not present an update alert if it performed one too recently. If you would like to present an alert every time Siren is called, please consider setting the UpdatePromptFrequency.immediately rule in RulesManager

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case recentlyPrompted
      + +
      +
      +
      +
      +
    • +
    • + +
      +
      +
      +
      +
      +

      The app has been released for X days, but Siren cannot prompt the user until Y (where Y > X) days have passed.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case releasedTooSoon(daysSinceRelease: Int, releasedForDays: Int)
      + +
      +
      +
      +
      +
    • +
    • + +
      +
      +
      +
      +
      +

      The user has opted to skip updating their current version of the app to the current App Store version.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case skipVersionUpdate(installedVersion: String, appStoreVersion: String)
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + localizedDescription + +
      +
      +
      +
      +
      +
      +

      The localized description for each error handled by Siren.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public var localizedDescription: String { get }
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + sirenError + +
      +
      +
      +
      +
      +
      +

      An easily identifiable prefix for all errors thrown by Siren.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      private static var sirenError: String { get }
      + +
      +
      +
      +
      +
    • +
    +
    +
    +
    + +
    +
    + + + diff --git a/docs/Enums/UpdateType.html b/docs/Enums/UpdateType.html index 3a7b62d6..194d915c 100644 --- a/docs/Enums/UpdateType.html +++ b/docs/Enums/UpdateType.html @@ -64,22 +64,22 @@ Structures @@ -115,9 +115,9 @@

    UpdateType

  • - + - major + major
    @@ -142,9 +142,9 @@

    Declaration

  • - + - minor + minor
    @@ -169,9 +169,9 @@

    Declaration

  • - + - patch + patch
    @@ -196,9 +196,9 @@

    Declaration

  • - + - revision + revision
    @@ -223,9 +223,9 @@

    Declaration

  • - + - unknown + unknown
    @@ -252,7 +252,7 @@

    Declaration

    diff --git a/docs/Extensions.html b/docs/Extensions.html new file mode 100644 index 00000000..b3f816c0 --- /dev/null +++ b/docs/Extensions.html @@ -0,0 +1,272 @@ + + + + Extensions Reference + + + + + + + + + + +
    +
    +

    Siren Docs (100% documented)

    +
    +
    +
    + +
    +
    + +
    +
    +
    +

    Extensions

    +

    The following extensions are available globally.

    + +
    +
    +
    +
      +
    • +
      + + + + Bundle + +
      +
      +
      +
      +
      +
      + + See more +
      +
      +

      Declaration

      +
      +

      Swift

      +
      class Bundle : NSObject
      + +
      +
      +
      +
      +
    • +
    +
    +
    +
      +
    • +
      + + + + Date + +
      +
      +
      +
      +
      +
      + + See more +
      +
      +

      Declaration

      +
      +

      Swift

      +
      struct Date : ReferenceConvertible, Comparable, Equatable
      + +
      +
      +
      +
      +
    • +
    +
    +
    +
      +
    • +
      + + + + UIAlertController + +
      +
      +
      +
      +
      +
      + + See more +
      +
      +

      Declaration

      +
      +

      Swift

      +
      class UIAlertController : UIViewController
      + +
      +
      +
      +
      +
    • +
    +
    +
    +
      +
    • +
      + + + + UserDefaults + +
      +
      +
      +
      +
      +
      + + See more +
      +
      +

      Declaration

      +
      +

      Swift

      +
      class UserDefaults : NSObject
      + +
      +
      +
      +
      +
    • +
    +
    +
    +
    + +
    +
    + +
    + diff --git a/docs/Extensions/Bundle.html b/docs/Extensions/Bundle.html new file mode 100644 index 00000000..a200bd70 --- /dev/null +++ b/docs/Extensions/Bundle.html @@ -0,0 +1,433 @@ + + + + Bundle Extension Reference + + + + + + + + + + +
    +
    +

    Siren Docs (100% documented)

    +
    +
    +
    + +
    +
    + +
    +
    +
    +

    Bundle

    +
    +
    +
    class Bundle : NSObject
    + +
    +
    + +
    +
    +
    +
      +
    • +
      + + + + Constants + +
      +
      +
      +
      +
      +
      +

      Constants used in the Bundle extension.

      + + See more +
      +
      +

      Declaration

      +
      +

      Swift

      +
      struct Constants
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + version() + +
      +
      +
      +
      +
      +
      +

      Fetches the current verison of the app.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      final class func version() -> String?
      + +
      +
      +
      +

      Return Value

      +

      The current installed version of the app.

      +
      +
      +
      +
    • +
    • + +
      +
      +
      +
      +
      +

      Returns the localized string for a given default string.

      + +

      By default, the English language localization is used. +If the device’s localization is set to another locale, that local’s language is used if it’s supported by Siren. +If forcedLanguage is set to true, the chosen language is shown for all devices, irrespective of their device’s localization.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      final class func localizedString(forKey key: String, andForceLocalization forcedLanguage: Localization.Language?) -> String
      + +
      +
      +
      +

      Parameters

      + + + + + + + + + + + +
      + + key + + +
      +

      The default string used to search the localization table for a specific translation.

      +
      +
      + + forcedLanguage + + +
      +

      Returns

      +
      +
      +
      +
      +

      Return Value

      +

      The localized string for a given key.

      +
      +
      +
      +
    • +
    • +
      + + + + bestMatchingAppName() + +
      +
      +
      +
      +
      +
      +

      The appropriate name for the app to be displayed in the update alert.

      + +

      Siren checks CFBundleDisplayName first. It then falls back to +to kCFBundleNameKey and ultimately to an empty string +if the aforementioned values are nil.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      final class func bestMatchingAppName() -> String
      + +
      +
      +
      +

      Return Value

      +

      The name of the app.

      +
      +
      +
      +
    • +
    +
    +
    +
      +
    • +
      + + + + sirenBundlePath() + +
      +
      +
      +
      +
      +
      +

      The path to Siren’s localization Bundle.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      final class func sirenBundlePath() -> String?
      + +
      +
      +
      +

      Return Value

      +

      The bundle’s path or nil.

      +
      +
      +
      +
    • +
    • + +
      +
      +
      +
      +
      +

      The path for a particular language localizationin Siren’s localization Bundle.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      final class func sirenForcedBundlePath(forceLanguageLocalization: Localization.Language) -> String?
      + +
      +
      +
      +

      Parameters

      + + + + + + + +
      + + forceLanguageLocalization + + +
      +

      The language localization that should be searched for in Siren’s localization bundle.

      +
      +
      +
      +
      +

      Return Value

      +

      The path to the forced language localization.

      +
      +
      +
      +
    • +
    • +
      + + + + deviceLanguage() + +
      +
      +
      +
      +
      +
      +

      The user’s preferred language based on their device’s localization.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      final class func deviceLanguage() -> Localization.Language?
      + +
      +
      +
      +

      Return Value

      +

      The user’s preferred language.

      +
      +
      +
      +
    • +
    +
    +
    +
    + +
    +
    + + + diff --git a/docs/Extensions/Bundle/Constants.html b/docs/Extensions/Bundle/Constants.html new file mode 100644 index 00000000..744910b9 --- /dev/null +++ b/docs/Extensions/Bundle/Constants.html @@ -0,0 +1,320 @@ + + + + Constants Structure Reference + + + + + + + + + + +
    +
    +

    Siren Docs (100% documented)

    +
    +
    +
    + +
    +
    + +
    +
    +
    +

    Constants

    +
    +
    +
    struct Constants
    + +
    +
    +

    Constants used in the Bundle extension.

    + +
    +
    +
    +
      +
    • +
      + + + + bundleExtension + +
      +
      +
      +
      +
      +
      +

      Constant for the .bundle file extension.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      static let bundleExtension: String
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + displayName + +
      +
      +
      +
      +
      +
      +

      Constant for CFBundleDisplayName.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      static let displayName: String
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + englishLocalization + +
      +
      +
      +
      +
      +
      +

      Constant for the default US English localization.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      static let englishLocalization: String
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + projectExtension + +
      +
      +
      +
      +
      +
      +

      Constant for the project file extension.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      static let projectExtension: String
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + shortVersionString + +
      +
      +
      +
      +
      +
      +

      Constant for CFBundleShortVersionString.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      static let shortVersionString: String
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + table + +
      +
      +
      +
      +
      +
      +

      Constant for the localization table.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      static let table: String
      + +
      +
      +
      +
      +
    • +
    +
    +
    +
    + +
    +
    + + + diff --git a/docs/Extensions/Date.html b/docs/Extensions/Date.html new file mode 100644 index 00000000..c827524a --- /dev/null +++ b/docs/Extensions/Date.html @@ -0,0 +1,257 @@ + + + + Date Extension Reference + + + + + + + + + + +
    +
    +

    Siren Docs (100% documented)

    +
    +
    +
    + +
    +
    + +
    +
    +
    +

    Date

    +
    +
    +
    struct Date : ReferenceConvertible, Comparable, Equatable
    + +
    +
    + +
    +
    +
    +
      +
    • +
      + + + + days(since:) + +
      +
      +
      +
      +
      +
      +

      The amount of days passed from a specific source date.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      static func days(since date: Date) -> Int
      + +
      +
      +
      +

      Parameters

      + + + + + + + +
      + + date + + +
      +

      The source date.

      +
      +
      +
      +
      +

      Return Value

      +

      The amount of days passed since the source date.

      +
      +
      +
      +
    • +
    • +
      + + + + days(since:) + +
      +
      +
      +
      +
      +
      +

      The amount of days passed from a specific source date string.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      static func days(since dateString: String) -> Int?
      + +
      +
      +
      +

      Parameters

      + + + + + + + +
      + + dateString + + +
      +

      The source date string.

      +
      +
      +
      +
      +

      Return Value

      +

      The amount of days passed since the source date.

      +
      +
      +
      +
    • +
    +
    +
    +
    + +
    +
    + + + diff --git a/docs/Extensions/UIAlertController.html b/docs/Extensions/UIAlertController.html new file mode 100644 index 00000000..41651dd0 --- /dev/null +++ b/docs/Extensions/UIAlertController.html @@ -0,0 +1,249 @@ + + + + UIAlertController Extension Reference + + + + + + + + + + +
    +
    +

    Siren Docs (100% documented)

    +
    +
    +
    + +
    +
    + +
    +
    +
    +

    UIAlertController

    +
    +
    +
    class UIAlertController : UIViewController
    + +
    +
    + +
    +
    +
    +
      +
    • +
      + + + + show(window:) + +
      +
      +
      +
      +
      +
      +

      Presents Siren’s UIAlertController in a new UIWindow.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      func show(window: UIWindow)
      + +
      +
      +
      +

      Parameters

      + + + + + + + +
      + + window + + +
      +

      The UIWindow that should reference Siren’s UIAlertController.

      +
      +
      +
      +
      +
      +
    • +
    • +
      + + + + hide(window:) + +
      +
      +
      +
      +
      +
      +

      Hides Siren’s UIAlertController within a given window.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      func hide(window: UIWindow)
      + +
      +
      +
      +

      Parameters

      + + + + + + + +
      + + window + + +
      +

      The UIWindow that references Siren’s UIAlertController.

      +
      +
      +
      +
      +
      +
    • +
    +
    +
    +
    + +
    +
    + + + diff --git a/docs/Extensions/UserDefaults.html b/docs/Extensions/UserDefaults.html new file mode 100644 index 00000000..1c64d3cd --- /dev/null +++ b/docs/Extensions/UserDefaults.html @@ -0,0 +1,266 @@ + + + + UserDefaults Extension Reference + + + + + + + + + + +
    +
    +

    Siren Docs (100% documented)

    +
    +
    +
    + +
    +
    + +
    +
    +
    +

    UserDefaults

    +
    +
    +
    class UserDefaults : NSObject
    + +
    +
    + +
    +
    +
    +
      +
    • +
      + + + + SirenKeys + +
      +
      +
      +
      +
      +
      +

      Siren-specific UserDefaults Keys

      + + See more +
      +
      +

      Declaration

      +
      +

      Swift

      +
      private enum SirenKeys : String
      + +
      +
      +
      +
      +
    • +
    • + +
      +
      +
      +
      +
      +

      Sets and Gets a UserDefault around performing a version check on a subsequent launch.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      static var shouldPerformVersionCheckOnSubsequentLaunch: Bool { get set }
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + storedSkippedVersion + +
      +
      +
      +
      +
      +
      +

      Sets and Gets a UserDefault around storing a version that the user wants to skip updating.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      static var storedSkippedVersion: String? { get set }
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + alertPresentationDate + +
      +
      +
      +
      +
      +
      +

      Sets and Gets a UserDefault around the last time the user was presented a version update alert.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      static var alertPresentationDate: Date? { get set }
      + +
      +
      +
      +
      +
    • +
    +
    +
    +
    + +
    +
    + + + diff --git a/docs/Extensions/UserDefaults/SirenKeys.html b/docs/Extensions/UserDefaults/SirenKeys.html new file mode 100644 index 00000000..6983054f --- /dev/null +++ b/docs/Extensions/UserDefaults/SirenKeys.html @@ -0,0 +1,240 @@ + + + + SirenKeys Enumeration Reference + + + + + + + + + + +
    +
    +

    Siren Docs (100% documented)

    +
    +
    +
    + +
    +
    + +
    +
    +
    +

    SirenKeys

    +
    +
    +
    private enum SirenKeys : String
    + +
    +
    +

    Siren-specific UserDefaults Keys

    + +
    +
    +
    +
      +
    • + +
      +
      +
      +
      +
      +

      Key that notifies Siren to perform a version check and present +the Siren alert the next time the user launches the app.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case PerformVersionCheckOnSubsequentLaunch
      + +
      +
      +
      +
      +
    • +
    • + +
      +
      +
      +
      +
      +

      Key that stores the timestamp of the last version check.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case StoredVersionCheckDate
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + StoredSkippedVersion + +
      +
      +
      +
      +
      +
      +

      Key that stores the version that a user decided to skip.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case StoredSkippedVersion
      + +
      +
      +
      +
      +
    • +
    +
    +
    +
    + +
    +
    + + + diff --git a/docs/Protocols.html b/docs/Protocols.html index ccb40a40..17d6198c 100644 --- a/docs/Protocols.html +++ b/docs/Protocols.html @@ -33,23 +33,6 @@
  • - - - - - - @@ -135,7 +136,7 @@

    Declaration

    diff --git a/docs/Protocols/SirenDelegate.html b/docs/Protocols/SirenDelegate.html index edf9991e..59df3c7a 100644 --- a/docs/Protocols/SirenDelegate.html +++ b/docs/Protocols/SirenDelegate.html @@ -33,23 +33,6 @@ - - - - - - @@ -104,9 +105,9 @@

    SirenDelegate

  • - + - sirenDidDetectNewVersionWithoutAlert(title:message:updateType:) + sirenDidDetectNewVersionWithoutAlert(title:message:updateType:) Default implementation @@ -128,7 +129,7 @@

    Default Implementation

    Declaration

    Swift

    -
    func sirenDidDetectNewVersionWithoutAlert(title: String, message: String, updateType: UpdateType)
    +
    func sirenDidDetectNewVersionWithoutAlert(title: String, message: String, updateType: Constants.UpdateType)
    @@ -138,9 +139,9 @@

    Declaration

  • - + - sirenDidFailVersionCheck(error:) + sirenDidFailVersionCheck(error:) Default implementation @@ -178,9 +179,9 @@

    Declaration

  • - + - sirenDidShowUpdateDialog(alertType:) + sirenDidShowUpdateDialog(alertType:) Default implementation @@ -202,7 +203,7 @@

    Default Implementation

    Declaration

    Swift

    -
    func sirenDidShowUpdateDialog(alertType: Siren.AlertType)
    +
    func sirenDidShowUpdateDialog(alertType: Constants.AlertType)
    @@ -265,9 +266,9 @@

    Declaration

  • - + - sirenNetworkCallDidReturnWithNewVersionInformation(lookupModel:) + sirenNetworkCallDidReturnWithNewVersionInformation(lookupModel:) Default implementation @@ -289,7 +290,7 @@

    Default Implementation

    Declaration

    Swift

    -
    func sirenNetworkCallDidReturnWithNewVersionInformation(lookupModel: SirenLookupModel)
    +
    func sirenNetworkCallDidReturnWithNewVersionInformation(lookupModel: LookupModel)
    @@ -422,7 +423,7 @@

    Declaration

    diff --git a/docs/Structs.html b/docs/Structs.html index e8b05e98..c0ef5159 100644 --- a/docs/Structs.html +++ b/docs/Structs.html @@ -34,13 +34,7 @@ Siren
  • - - @@ -48,15 +42,33 @@ Enumerations @@ -64,22 +76,52 @@ Structures @@ -94,20 +136,13 @@

    Structures

    -
    • @@ -115,22 +150,207 @@

      Siren Alert Messaging Customization

      -

      Allows the overriding of all the UIAlertController and UIActionSheet Strings to which Siren defaults.

      -
      -

      Warning

      - Overriding any of these keys will result in the loss of the built-in internationalization that Siren provides. +

      APIManager for Siren

      -
      + See more +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public struct APIManager
      + +
      +
      +
      +
      +
    • +
    +
    +
    +
      +
    • +
      + + + + PresentationManager + +
      +
      +
      +
      +
      +
      +

      PresentationManager for Siren

      + + See more +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public struct PresentationManager
      + +
      +
      +
      +
      +
    • +
    +
    +
    +
      +
    • +
      + + + + RulesManager + +
      +
      +
      +
      +
      +
      +

      RulesManager for Siren

      + + See more +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public struct RulesManager
      -

      As SirenAlertMessaging is a Struct, one or more keys can be modified. Overriding only one string will result in the other keys retaining their default (and internationalizable) values.

      +
      +
      +
      +
      +
    • +
    +
    +
    +
      +
    • +
      + + + + AlertConstants + +
      +
      +
      +
      +
      +
      +

      The default constants used for the update alert’s messaging.

      + + See more +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public struct AlertConstants
      + +
      +
      +
      +
      +
    • +
    +
    +
    +
      +
    • +
      + + + + Localization + +
      +
      +
      +
      +
      +
      +

      Localization information and strings for Siren.

      + + See more +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public struct Localization
      + +
      +
      +
      +
      +
    • +
    +
    +
    +
      +
    • +
      + + + + LookupModel + +
      +
      +
      +
      +
      +
      +

      Model representing a selection of results from the iTunes Lookup API.

      + + See more +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public struct LookupModel : Decodable
      + +
      +
      +
      +
      +
    • +
    +
    +
    +
      +
    • +
      + + + + Results + +
      +
      +
      +
      +
      +
      +

      The relevant metadata returned from Siren upon completing a successful version check.

      - See more + See more

      Declaration

      Swift

      -
      public struct SirenAlertMessaging
      +
      public struct Results
      @@ -140,20 +360,13 @@

      Declaration

    -
    • - - - SirenLookupModel + + + Rules
      @@ -161,15 +374,15 @@

      Model representing a selection of results from the iTun
      -

      MARK: Siren extension used to parse and map the iTunes JSON results into a model represented in Swift.

      +

      Alert Presentation Rules for Siren.

      - See more + See more

      Declaration

      Swift

      -
      public struct SirenLookupModel : Decodable
      +
      public struct Rules
      @@ -179,20 +392,13 @@

      Declaration

    -
    • - - - SirenError + + + DataParser
      @@ -200,15 +406,15 @@

      Siren Error Handling

      -

      Data structure used to build Siren specific Errors.

      +

      Version parsing functions for Siren.

      - See more + See more

      Declaration

      Swift

      -
      public struct SirenError : LocalizedError
      +
      struct DataParser
      @@ -220,7 +426,7 @@

      Declaration

    diff --git a/docs/Structs/APIManager.html b/docs/Structs/APIManager.html new file mode 100644 index 00000000..57385729 --- /dev/null +++ b/docs/Structs/APIManager.html @@ -0,0 +1,494 @@ + + + + APIManager Structure Reference + + + + + + + + + + +
    +
    +

    Siren Docs (100% documented)

    +
    +
    +
    + +
    +
    + +
    +
    +
    +

    APIManager

    +
    +
    +
    public struct APIManager
    + +
    +
    +

    APIManager for Siren

    + +
    +
    +
    +
      +
    • +
      + + + + Constants + +
      +
      +
      +
      +
      +
      +

      Constants used in the APIManager.

      + + See more +
      +
      +

      Declaration

      +
      +

      Swift

      +
      private struct Constants
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + CompletionHandler + +
      +
      +
      +
      +
      +
      +

      Return results or errors obtained from performing a version check with Siren.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      typealias CompletionHandler = (LookupModel?, KnownError?) -> Void
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + countryCode + +
      +
      +
      +
      +
      +
      +

      The region or country of an App Store in which the app is available. +By default, all version check requests are performed against the US App Store. +If the app is not available in the US App Store, set it to the identifier of at least one App Store region within which it is available.

      + +

      List of country codes

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      let countryCode: String?
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + init(countryCode:) + +
      +
      +
      +
      +
      +
      +

      Initializes APIManager to the region or country of an App Store in which the app is available. +By default, all version check requests are performed against the US App Store. +If the app is not available in the US App Store, set it to the identifier of at least one App Store region within which it is available.

      + +

      List of country codes

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public init(countryCode: String? = nil)
      + +
      +
      +
      +

      Parameters

      + + + + + + + +
      + + countryCode + + +
      +

      The country code for the App Store in which the app is availabe. Defaults to nil (e.g., the US App Store)

      +
      +
      +
      +
      +
      +
    • +
    • +
      + + + + default + +
      +
      +
      +
      +
      +
      +

      The default APIManager.

      + +

      The version check is performed against the US App Store.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public static let `default`: APIManager
      + +
      +
      +
      +
      +
    • +
    +
    +
    +
      +
    • + +
      +
      +
      +
      +
      +

      Creates and performs a URLRequest against the iTunes Lookup API.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      func performVersionCheckRequest(completion handler: CompletionHandler?)
      + +
      +
      +
      +

      Parameters

      + + + + + + + +
      + + handler + + +
      +

      The completion handler for the iTunes Lookup API request.

      +
      +
      +
      +
      +
      +
    • +
    • + +
      +
      +
      +
      +
      +

      Parses and maps the the results from the iTunes Lookup API request.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      private func processVersionCheckResults(withData data: Data?,
      +                                        response: URLResponse?,
      +                                        error: Error?,
      +                                        completion handler: CompletionHandler?)
      + +
      +
      +
      +

      Parameters

      + + + + + + + + + + + + + + + + + + + +
      + + data + + +
      +

      The JSON data returned from the request.

      +
      +
      + + response + + +
      +

      The response metadata returned from the request.

      +
      +
      + + error + + +
      +

      The error returned from the request.

      +
      +
      + + handler + + +
      +

      The completion handler to call once the results of the request has been processed.

      +
      +
      +
      +
      +
      +
    • +
    • +
      + + + + makeITunesURL() + +
      +
      +
      +
      +
      +
      +

      Creates the URL that points to the iTunes Lookup API.

      +
      +

      Throws

      + An error if the URL cannot be created. + +
      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      private func makeITunesURL() throws -> URL
      + +
      +
      +
      +

      Return Value

      +

      The iTunes Lookup API URL.

      +
      +
      +
      +
    • +
    +
    +
    +
    + +
    +
    + + + diff --git a/docs/Structs/APIManager/Constants.html b/docs/Structs/APIManager/Constants.html new file mode 100644 index 00000000..645afd45 --- /dev/null +++ b/docs/Structs/APIManager/Constants.html @@ -0,0 +1,212 @@ + + + + Constants Structure Reference + + + + + + + + + + +
    +
    +

    Siren Docs (100% documented)

    +
    +
    +
    + +
    +
    + +
    +
    +
    +

    Constants

    +
    +
    +
    private struct Constants
    + +
    +
    +

    Constants used in the APIManager.

    + +
    +
    +
    +
      +
    • +
      + + + + bundleID + +
      +
      +
      +
      +
      +
      +

      Constant for the bundleId parameter in the iTunes Lookup API request.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      static let bundleID: String
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + country + +
      +
      +
      +
      +
      +
      +

      Constant for the country parameter in the iTunes Lookup API request.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      static let country: String
      + +
      +
      +
      +
      +
    • +
    +
    +
    +
    + +
    +
    + + + diff --git a/docs/Structs/AlertConstants.html b/docs/Structs/AlertConstants.html new file mode 100644 index 00000000..2717d6dc --- /dev/null +++ b/docs/Structs/AlertConstants.html @@ -0,0 +1,293 @@ + + + + AlertConstants Structure Reference + + + + + + + + + + +
    +
    +

    Siren Docs (100% documented)

    +
    +
    +
    + +
    +
    + +
    +
    +
    +

    AlertConstants

    +
    +
    +
    public struct AlertConstants
    + +
    +
    +

    The default constants used for the update alert’s messaging.

    + +
    +
    +
    +
      +
    • +
      + + + + alertMessage + +
      +
      +
      +
      +
      +
      +

      The text that conveys the message that there is an app update available

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public static let alertMessage: String
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + alertTitle + +
      +
      +
      +
      +
      +
      +

      The alert title which defaults to Update Available.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public static let alertTitle: String
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + nextTimeButtonTitle + +
      +
      +
      +
      +
      +
      +

      The button text that conveys the message that the user should be prompted to update next time the app launches.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public static let nextTimeButtonTitle: String
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + skipButtonTitle + +
      +
      +
      +
      +
      +
      +

      The text that conveys the message that the the user wants to skip this verison update.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public static let skipButtonTitle: String
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + updateButtonTitle + +
      +
      +
      +
      +
      +
      +

      The button text that conveys the message that the user would like to update the app right away.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public static let updateButtonTitle: String
      + +
      +
      +
      +
      +
    • +
    +
    +
    +
    + +
    +
    + + + diff --git a/docs/Structs/AlertMessaging.html b/docs/Structs/AlertMessaging.html new file mode 100644 index 00000000..4984c1c1 --- /dev/null +++ b/docs/Structs/AlertMessaging.html @@ -0,0 +1,250 @@ + + + + AlertMessaging Structure Reference + + + + + + + + + + +
    +
    +

    Siren Docs (100% documented)

    +
    +
    +
    + +
    +
    + +
    +
    +
    +

    AlertMessaging

    +
    +
    +
    public struct AlertMessaging
    + +
    +
    +

    Allows the overriding of all the UIAlertController and UIActionSheet Strings to which Siren defaults.

    +
    +

    Warning

    + Overriding any of these keys will result in the loss of the built-in internationalization that Siren provides. + +
    + +

    As SirenAlertMessaging is a Struct, one or more keys can be modified. Overriding only one string will result in the other keys retaining their default (and internationalizable) values.

    + +
    +
    +
    +
      +
    • +
      + + + + Constants + +
      +
      +
      +
      +
      +
      +

      The default constants used for the alert messaging.

      + + See more +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public struct Constants
      + +
      +
      +
      +
      +
    • +
    • + +
      +
      +
      +
      +
      +

      The public initializer

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public init(updateTitle title: NSAttributedString  = Constants.alertTitle,
      +            updateMessage message: NSAttributedString  = Constants.alertMessage,
      +            updateButtonMessage: NSAttributedString  = Constants.updateButtonTitle,
      +            nextTimeButtonMessage: NSAttributedString  = Constants.nextTimeButtonTitle,
      +            skipVersionButtonMessage: NSAttributedString  = Constants.skipButtonTitle)
      + +
      +
      +
      +

      Parameters

      + + + + + + + + + + + + + + + + + + + + + + + +
      + + title + + +
      +

      The title field of the UIAlertController.

      +
      +
      + + message + + +
      +

      The message field of the UIAlertController.

      +
      +
      + + updateButtonMessage + + +
      +

      The title field of the Update Button UIAlertAction.

      +
      +
      + + nextTimeButtonMessage + + +
      +

      The title field of the Next Time Button UIAlertAction.

      +
      +
      + + skipVersionButtonMessage + + +
      +

      The title field of the Skip Button UIAlertAction.

      +
      +
      +
      +
      +
      +
    • +
    +
    +
    +
    + +
    +
    + + + diff --git a/docs/Structs/AlertMessaging/Constants.html b/docs/Structs/AlertMessaging/Constants.html new file mode 100644 index 00000000..5b9d5c7f --- /dev/null +++ b/docs/Structs/AlertMessaging/Constants.html @@ -0,0 +1,252 @@ + + + + Constants Structure Reference + + + + + + + + + + +
    +
    +

    Siren Docs (100% documented)

    +
    +
    +
    + +
    +
    + +
    +
    +
    +

    Constants

    +
    +
    +
    public struct Constants
    + +
    +
    +

    The default constants used for the alert messaging.

    + +
    +
    +
    +
      +
    • +
      + + + + alertMessage + +
      +
      +
      +
      +
      +
      +

      The text that conveys the message that there is an app update available

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public static let alertMessage: NSAttributedString
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + alertTitle + +
      +
      +
      +
      +
      +
      +

      The alert title which defaults to Update Available.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public static let alertTitle: NSAttributedString
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + nextTimeButtonTitle + +
      +
      +
      +
      +
      +
      +

      The button text that conveys the message that the user should be prompted to update next time the app launches.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public static let nextTimeButtonTitle: NSAttributedString
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + skipButtonTitle + +
      +
      +
      +
      +
      +
      +

      The text that conveys the message that the the user wants to skip this verison update.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public static let skipButtonTitle: NSAttributedString
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + updateButtonTitle + +
      +
      +
      +
      +
      +
      +

      The button text that conveys the message that the user would like to update the app right away.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public static let updateButtonTitle: NSAttributedString
      + +
      +
      +
      +
      +
    • +
    +
    +
    +
    + +
    +
    + + + diff --git a/docs/Structs/CapturedError.html b/docs/Structs/CapturedError.html new file mode 100644 index 00000000..bce35f56 --- /dev/null +++ b/docs/Structs/CapturedError.html @@ -0,0 +1,158 @@ + + + + CapturedError Structure Reference + + + + + + + + + + +
    +
    +

    Siren Docs (100% documented)

    +
    +
    +
    + +
    +
    + +
    +
    +
    +

    CapturedError

    +
    +
    +
    public struct CapturedError : LocalizedError
    + +
    +
    +

    Siren extension dealing with Siren-specific errors.

    + +
    +
    +
    +
      +
    • +
      + + + + Known + +
      +
      +
      +
      +
      +
      +

      Enumerates all potentials errors that Siren can handle.

      + +
        +
      • appStoreAppIDFailure: Error retrieving trackId as the JSON does not contain a ‘trackId’ key.
      • +
      • appStoreDataRetrievalFailure: Error retrieving App Store data as an error was returned.
      • +
      • appStoreJSONParsingFailure: Error parsing App Store JSON data.
      • +
      • appStoreDataRetrievalEmptyResults: Error retrieving App Store data as JSON results were empty. Is your app available in the US? If not, change the countryCode variable to fix this error.
      • +
      • appStoreOSVersionNumberFailure: Error retrieving iOS version number as there was no data returned.
      • +
      • appStoreOSVersionUnsupported: The version of iOS on the device is lower than that of the one required by the app verison update.
      • +
      • appStoreVersionArrayFailure: Error retrieving App Store verson number as the JSON does not contain a ‘version’ key.
      • +
      • malformedURL: The iTunes URL is malformed. Please leave an issue on https://github.com/ArtSabintsev/Siren with as many details as possible.
      • +
      • noUpdateAvailable: No new update available.
      • +
      • recentlyCheckedAlready: Not checking the version, because it was already checked recently.
      • +
      + + See more +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public enum Known : Error
      + +
      +
      +
      +
      +
    • +
    +
    +
    +
    + +
    +
    + + + diff --git a/docs/Structs/CapturedError/Known.html b/docs/Structs/CapturedError/Known.html new file mode 100644 index 00000000..dc1c8aaa --- /dev/null +++ b/docs/Structs/CapturedError/Known.html @@ -0,0 +1,400 @@ + + + + Known Enumeration Reference + + + + + + + + + + +
    +
    +

    Siren Docs (100% documented)

    +
    +
    +
    + +
    +
    + +
    +
    +
    +

    Known

    +
    +
    +
    public enum Known : Error
    + +
    +
    +

    Enumerates all potentials errors that Siren can handle.

    + +
      +
    • appStoreAppIDFailure: Error retrieving trackId as the JSON does not contain a ‘trackId’ key.
    • +
    • appStoreDataRetrievalFailure: Error retrieving App Store data as an error was returned.
    • +
    • appStoreJSONParsingFailure: Error parsing App Store JSON data.
    • +
    • appStoreDataRetrievalEmptyResults: Error retrieving App Store data as JSON results were empty. Is your app available in the US? If not, change the countryCode variable to fix this error.
    • +
    • appStoreOSVersionNumberFailure: Error retrieving iOS version number as there was no data returned.
    • +
    • appStoreOSVersionUnsupported: The version of iOS on the device is lower than that of the one required by the app verison update.
    • +
    • appStoreVersionArrayFailure: Error retrieving App Store verson number as the JSON does not contain a ‘version’ key.
    • +
    • malformedURL: The iTunes URL is malformed. Please leave an issue on https://github.com/ArtSabintsev/Siren with as many details as possible.
    • +
    • noUpdateAvailable: No new update available.
    • +
    • recentlyCheckedAlready: Not checking the version, because it was already checked recently.
    • +
    + +
    +
    +
    +
      +
    • +
      + + + + appStoreAppIDFailure + +
      +
      +
      +
      +
      +
      +

      Error retrieving trackId as the JSON does not contain a ‘trackId’ key.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case appStoreAppIDFailure
      + +
      +
      +
      +
      +
    • +
    • + +
      +
      +
      +
      +
      +

      Error retrieving App Store data as an error was returned.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case appStoreDataRetrievalFailure(underlyingError: Error?)
      + +
      +
      +
      +
      +
    • +
    • + +
      +
      +
      +
      +
      +

      Error parsing App Store JSON data.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case appStoreJSONParsingFailure(underlyingError: Error)
      + +
      +
      +
      +
      +
    • +
    • + +
      +
      +
      +
      +
      +

      Error retrieving App Store data as JSON results were empty. Is your app available in the US? If not, change the countryCode variable to fix this error.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case appStoreDataRetrievalEmptyResults
      + +
      +
      +
      +
      +
    • +
    • + +
      +
      +
      +
      +
      +

      Error retrieving iOS version number as there was no data returned.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case appStoreOSVersionNumberFailure
      + +
      +
      +
      +
      +
    • +
    • + +
      +
      +
      +
      +
      +

      The version of iOS on the device is lower than that of the one required by the app verison update.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case appStoreOSVersionUnsupported
      + +
      +
      +
      +
      +
    • +
    • + +
      +
      +
      +
      +
      +

      Error retrieving App Store verson number as the JSON does not contain a ‘version’ key.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case appStoreVersionArrayFailure
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + malformedURL + +
      +
      +
      +
      +
      +
      +

      The iTunes URL is malformed. Please leave an issue on https://github.com/ArtSabintsev/Siren with as many details as possible.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case malformedURL
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + noUpdateAvailable + +
      +
      +
      +
      +
      +
      +

      No new update available.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case noUpdateAvailable
      + +
      +
      +
      +
      +
    • +
    • + +
      +
      +
      +
      +
      +

      Not checking the version, because it was already checked recently.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case recentlyCheckedAlready
      + +
      +
      +
      +
      +
    • +
    +
    +
    +
    + +
    +
    + + + diff --git a/docs/Structs/Constants.html b/docs/Structs/Constants.html new file mode 100644 index 00000000..d85e5cfa --- /dev/null +++ b/docs/Structs/Constants.html @@ -0,0 +1,211 @@ + + + + Constants Structure Reference + + + + + + + + + + +
    +
    +

    Siren Docs (100% documented)

    +
    +
    +
    + +
    +
    + +
    +
    +
    +

    Constants

    +
    +
    +
    public struct Constants
    + +
    +
    +

    Lists all enumerated types that are used to configure the library.

    + +
    +
    +
    +
      +
    • +
      + + + + AlertType + +
      +
      +
      +
      +
      +
      +

      Determines the type of alert to present after a successful version check has been performed.

      + + See more +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public enum AlertType
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + UpdateType + +
      +
      +
      +
      +
      +
      +

      UpdateType defines what kind of update is available. +It is used as a parameter if the user wants to use +a custom alert to inform the user about an update.

      + +
        +
      • major: Major release available: A.b.c.d
      • +
      • minor: Minor release available: a.B.c.d
      • +
      • patch: Patch release available: a.b.C.d
      • +
      • revision: Revision release available: a.b.c.D
      • +
      • unknown: No information available about the update.
      • +
      + + See more +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public enum UpdateType : String
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + VersionCheckFrequency + +
      +
      +
      +
      +
      +
      +

      Determines the frequency in which the the version check is performed and the user is prompted to update the app.

      + + See more +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public enum VersionCheckFrequency : Int
      + +
      +
      +
      +
      +
    • +
    +
    +
    +
    + +
    +
    + + + diff --git a/docs/Structs/Constants/AlertType.html b/docs/Structs/Constants/AlertType.html new file mode 100644 index 00000000..a24bea17 --- /dev/null +++ b/docs/Structs/Constants/AlertType.html @@ -0,0 +1,226 @@ + + + + AlertType Enumeration Reference + + + + + + + + + + +
    +
    +

    Siren Docs (100% documented)

    +
    +
    +
    + +
    +
    + +
    +
    +
    +

    AlertType

    +
    +
    +
    public enum AlertType
    + +
    +
    +

    Determines the type of alert to present after a successful version check has been performed.

    + +
    +
    +
    +
      +
    • +
      + + + + force + +
      +
      +
      +
      +
      +
      +

      Forces user to update your app (1 button alert).

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case force
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + option + +
      +
      +
      +
      +
      +
      +

      (DEFAULT) Presents user with option to update app now or at next launch (2 button alert).

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case option
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + skip + +
      +
      +
      +
      +
      +
      +

      Presents user with option to update the app now, at next launch, or to skip this version all together (3 button alert).

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case skip
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + none + +
      +
      +
      +
      +
      +
      +

      Doesn’t show the alert, but instead returns a localized message +for use in a custom UI within the sirenDidDetectNewVersionWithoutAlert() delegate method.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case none
      + +
      +
      +
      +
      +
    • +
    +
    +
    +
    + +
    +
    + + + diff --git a/docs/Structs/Constants/UpdateType.html b/docs/Structs/Constants/UpdateType.html new file mode 100644 index 00000000..7633d0ac --- /dev/null +++ b/docs/Structs/Constants/UpdateType.html @@ -0,0 +1,262 @@ + + + + UpdateType Enumeration Reference + + + + + + + + + + +
    +
    +

    Siren Docs (100% documented)

    +
    +
    +
    + +
    +
    + +
    +
    +
    +

    UpdateType

    +
    +
    +
    public enum UpdateType : String
    + +
    +
    +

    UpdateType defines what kind of update is available. +It is used as a parameter if the user wants to use +a custom alert to inform the user about an update.

    + +
      +
    • major: Major release available: A.b.c.d
    • +
    • minor: Minor release available: a.B.c.d
    • +
    • patch: Patch release available: a.b.C.d
    • +
    • revision: Revision release available: a.b.c.D
    • +
    • unknown: No information available about the update.
    • +
    + +
    +
    +
    +
      +
    • +
      + + + + major + +
      +
      +
      +
      +
      +
      +

      Major release available: A.b.c.d

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case major
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + minor + +
      +
      +
      +
      +
      +
      +

      Minor release available: a.B.c.d

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case minor
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + patch + +
      +
      +
      +
      +
      +
      +

      Patch release available: a.b.C.d

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case patch
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + revision + +
      +
      +
      +
      +
      +
      +

      Revision release available: a.b.c.D

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case revision
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + unknown + +
      +
      +
      +
      +
      +
      +

      No information available about the update.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case unknown
      + +
      +
      +
      +
      +
    • +
    +
    +
    +
    + +
    +
    + + + diff --git a/docs/Structs/Constants/VersionCheckFrequency.html b/docs/Structs/Constants/VersionCheckFrequency.html new file mode 100644 index 00000000..c1c179fc --- /dev/null +++ b/docs/Structs/Constants/VersionCheckFrequency.html @@ -0,0 +1,198 @@ + + + + VersionCheckFrequency Enumeration Reference + + + + + + + + + + +
    +
    +

    Siren Docs (100% documented)

    +
    +
    +
    + +
    +
    + +
    +
    +
    +

    VersionCheckFrequency

    +
    +
    +
    public enum VersionCheckFrequency : Int
    + +
    +
    +

    Determines the frequency in which the the version check is performed and the user is prompted to update the app.

    + +
    +
    +
    +
      +
    • +
      + + + + immediately + +
      +
      +
      +
      +
      +
      +

      Version check performed every time the app is launched.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case immediately = 0
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + daily + +
      +
      +
      +
      +
      +
      +

      Version check performed once a day.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case daily = 1
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + weekly + +
      +
      +
      +
      +
      +
      +

      Version check performed once a week.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case weekly = 7
      + +
      +
      +
      +
      +
    • +
    +
    +
    +
    + +
    +
    + + + diff --git a/docs/Structs/DataParser.html b/docs/Structs/DataParser.html new file mode 100644 index 00000000..739b616c --- /dev/null +++ b/docs/Structs/DataParser.html @@ -0,0 +1,385 @@ + + + + DataParser Structure Reference + + + + + + + + + + +
    +
    +

    Siren Docs (100% documented)

    +
    +
    +
    + +
    +
    + +
    +
    +
    +

    DataParser

    +
    +
    +
    struct DataParser
    + +
    +
    +

    Version parsing functions for Siren.

    + +
    +
    +
    +
      +
    • + +
      +
      +
      +
      +
      +

      Checks to see if the App Store version of the app is newer than the installed version.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      static func isAppStoreVersionNewer(installedVersion: String?, appStoreVersion: String?) -> Bool
      + +
      +
      +
      +

      Parameters

      + + + + + + + + + + + +
      + + installedVersion + + +
      +

      The installed version of the app.

      +
      +
      + + appStoreVersion + + +
      +

      The App Store version of the app.

      +
      +
      +
      +
      +

      Return Value

      +

      true if the App Store version is newer. Otherwise, false.

      +
      +
      +
      +
    • +
    • + +
      +
      +
      +
      +
      +

      Validates that the latest version in the App Store is compatible with the device’s current version of iOS.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      static func isUpdateCompatibleWithDeviceOS(for model: LookupModel) -> Bool
      + +
      +
      +
      +

      Parameters

      + + + + + + + +
      + + model + + +
      +

      The iTunes Lookup Model.

      +
      +
      +
      +
      +

      Return Value

      +

      true if the latest version is compatible with the device’s current version of iOS. Otherwise, false.

      +
      +
      +
      +
    • +
    • + +
      +
      +
      +
      +
      +

      The type of update that is returned from the API in relation to the verison of the app that is installed.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      static func parseForUpdate(forInstalledVersion installedVersion: String?,
      +                           andAppStoreVersion appStoreVersion: String?) -> RulesManager.UpdateType
      + +
      +
      +
      +

      Parameters

      + + + + + + + + + + + +
      + + installedVersion + + +
      +

      The installed version of the app.

      +
      +
      + + appStoreVersion + + +
      +

      The App Store version of the app.

      +
      +
      +
      +
      +

      Return Value

      +

      The type of update in relation to the verison of the app that is installed.

      +
      +
      +
      +
    • +
    • +
      + + + + split(version:) + +
      +
      +
      +
      +
      +
      +

      Splits a version-formatted String into an[Int]`.

      + +

      Converts "a.b.c.d" into [a, b, c, d].

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      private static func split(version: String) -> [Int]
      + +
      +
      +
      +

      Parameters

      + + + + + + + +
      + + version + + +
      +

      The version formatted String.

      +
      +
      +
      +
      +

      Return Value

      +

      An array of integers representing a version of the app.

      +
      +
      +
      +
    • +
    +
    +
    +
    + +
    +
    + + + diff --git a/docs/Structs/Localization.html b/docs/Structs/Localization.html new file mode 100644 index 00000000..78a6c884 --- /dev/null +++ b/docs/Structs/Localization.html @@ -0,0 +1,458 @@ + + + + Localization Structure Reference + + + + + + + + + + +
    +
    +

    Siren Docs (100% documented)

    +
    +
    +
    + +
    +
    + +
    +
    +
    +

    Localization

    +
    +
    +
    public struct Localization
    + +
    +
    +

    Localization information and strings for Siren.

    + +
    +
    +
    +
      +
    • +
      + + + + Language + +
      +
      +
      +
      +
      +
      +

      Determines the available languages in which the update message and alert button titles should appear.

      + +

      By default, the operating system’s default lanuage setting is used. However, you can force a specific language +by setting the forceLanguageLocalization property before calling checkVersion()

      + + See more +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public enum Language : String
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + appName + +
      +
      +
      +
      +
      +
      +

      The name of the app as defined by the Info.plist.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      private var appName: String
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + forceLanguage + +
      +
      +
      +
      +
      +
      +

      Overrides the default localization of a user’s device when presenting the update message and button titles in the alert.

      + +

      See the Siren.Localization.Language enum for more details.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      private let forceLanguage: Language?
      + +
      +
      +
      +
      +
    • +
    • + +
      +
      +
      +
      +
      +

      Initializes

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      init(appName: String?, andForceLanguageLocalization forceLanguage: Language?)
      + +
      +
      +
      +

      Parameters

      + + + + + + + + + + + +
      + + appName + + +
      +

      Overrides the default name of the app. This is optional and defaults to the app that is defined in the Info.plist.

      +
      +
      + + forceLanguage + + +
      +

      The language the alert to which the alert should be set. If nil, it falls back to the device’s preferred locale.

      +
      +
      +
      +
      +
      +
    • +
    • + +
      +
      +
      +
      +
      +

      The localized string for the UIAlertController‘s message field. .

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public func alertMessage(forCurrentAppStoreVersion currentAppStoreVersion: String) -> String
      + +
      +
      +
      +

      Return Value

      +

      A localized string for the update message.

      +
      +
      +
      +
    • +
    • +
      + + + + alertTitle() + +
      +
      +
      +
      +
      +
      +

      The localized string for the UIAlertController‘s title field. .

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public func alertTitle() -> String
      + +
      +
      +
      +

      Return Value

      +

      A localized string for the phrase Update Available.

      +
      +
      +
      +
    • +
    • +
      + + + + nextTimeButtonTitle() + +
      +
      +
      +
      +
      +
      +

      The localized string for the Next time UIAlertAction.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public func nextTimeButtonTitle() -> String
      + +
      +
      +
      +

      Return Value

      +

      A localized string for the phrase Next time.

      +
      +
      +
      +
    • +
    • +
      + + + + skipButtonTitle() + +
      +
      +
      +
      +
      +
      +

      The localized string for the Skip this version UIAlertAction.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public func skipButtonTitle() -> String
      + +
      +
      +
      +

      Return Value

      +

      A localized string for the phrase Skip this version.

      +
      +
      +
      +
    • +
    • +
      + + + + updateButtonTitle() + +
      +
      +
      +
      +
      +
      +

      The localized string for the Update UIAlertAction.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public func updateButtonTitle() -> String
      + +
      +
      +
      +

      Return Value

      +

      A localized string for the phrase Update.

      +
      +
      +
      +
    • +
    +
    +
    +
    + +
    +
    + + + diff --git a/docs/Structs/Localization/Language.html b/docs/Structs/Localization/Language.html new file mode 100644 index 00000000..a1e50087 --- /dev/null +++ b/docs/Structs/Localization/Language.html @@ -0,0 +1,1295 @@ + + + + Language Enumeration Reference + + + + + + + + + + +
    +
    +

    Siren Docs (100% documented)

    +
    +
    +
    + +
    +
    + +
    +
    +
    +

    Language

    +
    +
    +
    public enum Language : String
    + +
    +
    +

    Determines the available languages in which the update message and alert button titles should appear.

    + +

    By default, the operating system’s default lanuage setting is used. However, you can force a specific language +by setting the forceLanguageLocalization property before calling checkVersion()

    + +
    +
    +
    +
      +
    • +
      + + + + arabic + +
      +
      +
      +
      +
      +
      +

      Arabic Language Localization

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case arabic = "ar"
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + armenian + +
      +
      +
      +
      +
      +
      +

      Armenian Language Localization

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case armenian = "hy"
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + basque + +
      +
      +
      +
      +
      +
      +

      Basque Language Localization

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case basque = "eu"
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + chineseSimplified + +
      +
      +
      +
      +
      +
      +

      Simplified Chinese Language Localization

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case chineseSimplified = "zh-Hans"
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + chineseTraditional + +
      +
      +
      +
      +
      +
      +

      Traditional Chinese Localization Localization

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case chineseTraditional = "zh-Hant"
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + croatian + +
      +
      +
      +
      +
      +
      +

      Croatian Language Localization

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case croatian = "hr"
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + czech + +
      +
      +
      +
      +
      +
      +

      Czech Language Localization

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case czech = "cs"
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + danish + +
      +
      +
      +
      +
      +
      +

      Danish Language Localization

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case danish = "da"
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + dutch + +
      +
      +
      +
      +
      +
      +

      Dutch Language Localization

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case dutch = "nl"
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + english + +
      +
      +
      +
      +
      +
      +

      English Language Localization

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case english = "en"
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + estonian + +
      +
      +
      +
      +
      +
      +

      Estonian Language Localization

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case estonian = "et"
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + finnish + +
      +
      +
      +
      +
      +
      +

      Finnish Language Localization

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case finnish = "fi"
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + french + +
      +
      +
      +
      +
      +
      +

      French Language Localization

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case french = "fr"
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + german + +
      +
      +
      +
      +
      +
      +

      German Language Localization

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case german = "de"
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + greek + +
      +
      +
      +
      +
      +
      +

      Greek Language Localization

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case greek = "el"
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + hebrew + +
      +
      +
      +
      +
      +
      +

      Hebrew Language Localization

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case hebrew = "he"
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + hungarian + +
      +
      +
      +
      +
      +
      +

      Hungarian Language Localization

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case hungarian = "hu"
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + indonesian + +
      +
      +
      +
      +
      +
      +

      Indonesian Language Localization

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case indonesian = "id"
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + italian + +
      +
      +
      +
      +
      +
      +

      Italian Language Localization

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case italian = "it"
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + japanese + +
      +
      +
      +
      +
      +
      +

      Japanese Language Localization

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case japanese = "ja"
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + korean + +
      +
      +
      +
      +
      +
      +

      Korean Language Localization

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case korean = "ko"
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + latvian + +
      +
      +
      +
      +
      +
      +

      Latvian Language Localization

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case latvian = "lv"
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + lithuanian + +
      +
      +
      +
      +
      +
      +

      Lithuanian Language Localization

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case lithuanian = "lt"
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + malay + +
      +
      +
      +
      +
      +
      +

      Malay Language Localization

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case malay = "ms"
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + norwegian + +
      +
      +
      +
      +
      +
      +

      Norwegian Language Localization

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case norwegian = "nb-NO"
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + persian + +
      +
      +
      +
      +
      +
      +

      Persian Language Localization

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case persian = "fa"
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + persianAfghanistan + +
      +
      +
      +
      +
      +
      +

      Persian (Afghanistan) Language Localization

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case persianAfghanistan = "fa-AF"
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + persianIran + +
      +
      +
      +
      +
      +
      +

      Persian (Iran) Language Localization

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case persianIran = "fa-IR"
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + polish + +
      +
      +
      +
      +
      +
      +

      Polish Language Localization

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case polish = "pl"
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + portugueseBrazil + +
      +
      +
      +
      +
      +
      +

      Brazilian Portuguese Language Localization

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case portugueseBrazil = "pt"
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + portuguesePortugal + +
      +
      +
      +
      +
      +
      +

      Portugal’s Portuguese Language Localization

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case portuguesePortugal = "pt-PT"
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + russian + +
      +
      +
      +
      +
      +
      +

      Russian Language Localization

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case russian = "ru"
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + serbianCyrillic + +
      +
      +
      +
      +
      +
      +

      Serbian (Cyrillic) Language Localization

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case serbianCyrillic = "sr-Cyrl"
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + serbianLatin + +
      +
      +
      +
      +
      +
      +

      Serbian (Latin) Language Localization

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case serbianLatin = "sr-Latn"
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + slovenian + +
      +
      +
      +
      +
      +
      +

      Slovenian Language Localization

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case slovenian = "sl"
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + spanish + +
      +
      +
      +
      +
      +
      +

      Spanish Language Localization

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case spanish = "es"
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + swedish + +
      +
      +
      +
      +
      +
      +

      Swedish Language Localization

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case swedish = "sv"
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + thai + +
      +
      +
      +
      +
      +
      +

      Thai Language Localization

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case thai = "th"
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + turkish + +
      +
      +
      +
      +
      +
      +

      Turkish Language Localization

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case turkish = "tr"
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + urdu + +
      +
      +
      +
      +
      +
      +

      Urdu Language Localization

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case urdu = "ur"
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + ukrainian + +
      +
      +
      +
      +
      +
      +

      Ukranian Language Localization

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case ukrainian = "uk"
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + vietnamese + +
      +
      +
      +
      +
      +
      +

      Vietnamese Language Localization

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case vietnamese = "vi"
      + +
      +
      +
      +
      +
    • +
    +
    +
    +
    + +
    +
    + + + diff --git a/docs/Structs/LookupModel.html b/docs/Structs/LookupModel.html new file mode 100644 index 00000000..ad08f39d --- /dev/null +++ b/docs/Structs/LookupModel.html @@ -0,0 +1,241 @@ + + + + LookupModel Structure Reference + + + + + + + + + + +
    +
    +

    Siren Docs (100% documented)

    +
    +
    +
    + +
    +
    + +
    +
    +
    +

    LookupModel

    +
    +
    +
    public struct LookupModel : Decodable
    + +
    +
    +

    Model representing a selection of results from the iTunes Lookup API.

    + +
    +
    +
    +
      +
    • +
      + + + + CodingKeys + +
      +
      +
      +
      +
      +
      +

      Codable Coding Keys for the Top-Level iTunes Lookup API JSON response.

      + + See more +
      +
      +

      Declaration

      +
      +

      Swift

      +
      private enum CodingKeys : String, CodingKey
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + results + +
      +
      +
      +
      +
      +
      +

      The array of results objects from the iTunes Lookup API.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public let results: [Results]
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + Results + +
      +
      +
      +
      +
      +
      +

      The Results object from the the iTunes Lookup API.

      + + See more +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public struct Results : Decodable
      + +
      +
      +
      +
      +
    • +
    +
    +
    +
    + +
    +
    + + + diff --git a/docs/Structs/LookupModel/CodingKeys.html b/docs/Structs/LookupModel/CodingKeys.html new file mode 100644 index 00000000..8bae8fe4 --- /dev/null +++ b/docs/Structs/LookupModel/CodingKeys.html @@ -0,0 +1,185 @@ + + + + CodingKeys Enumeration Reference + + + + + + + + + + +
    +
    +

    Siren Docs (100% documented)

    +
    +
    +
    + +
    +
    + +
    +
    +
    +

    CodingKeys

    +
    +
    +
    private enum CodingKeys : String, CodingKey
    + +
    +
    +

    Codable Coding Keys for the Top-Level iTunes Lookup API JSON response.

    + +
    +
    +
    +
      +
    • +
      + + + + results + +
      +
      +
      +
      +
      +
      +

      The results JSON key.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case results
      + +
      +
      +
      +
      +
    • +
    +
    +
    +
    + +
    +
    + + + diff --git a/docs/Structs/LookupModel/Results.html b/docs/Structs/LookupModel/Results.html new file mode 100644 index 00000000..6158798d --- /dev/null +++ b/docs/Structs/LookupModel/Results.html @@ -0,0 +1,321 @@ + + + + Results Structure Reference + + + + + + + + + + +
    +
    +

    Siren Docs (100% documented)

    +
    +
    +
    + +
    +
    + +
    +
    +
    +

    Results

    +
    +
    +
    public struct Results : Decodable
    + +
    +
    +

    The Results object from the the iTunes Lookup API.

    + +
    +
    +
    +
      +
    • +
      + + + + CodingKeys + +
      +
      +
      +
      +
      +
      +

      Codable Coding Keys for the Results array in the iTunes Lookup API JSON response.

      + + See more +
      +
      +

      Declaration

      +
      +

      Swift

      +
      private enum CodingKeys : String, CodingKey
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + appID + +
      +
      +
      +
      +
      +
      +

      The app’s App ID.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public let appID: Int
      + +
      +
      +
      +
      +
    • +
    • + +
      +
      +
      +
      +
      +

      The release date for the latest verison of the app.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public let currentVersionReleaseDate: String
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + minimumOSVersion + +
      +
      +
      +
      +
      +
      +

      The minimum verison of iOS that the current verison of the app requires.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public let minimumOSVersion: String
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + releaseNotes + +
      +
      +
      +
      +
      +
      +

      The releases notes from the latest version of the app.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public let releaseNotes: String?
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + version + +
      +
      +
      +
      +
      +
      +

      The latest version of the app.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public let version: String
      + +
      +
      +
      +
      +
    • +
    +
    +
    +
    + +
    +
    + + + diff --git a/docs/Structs/LookupModel/Results/CodingKeys.html b/docs/Structs/LookupModel/Results/CodingKeys.html new file mode 100644 index 00000000..0560df9b --- /dev/null +++ b/docs/Structs/LookupModel/Results/CodingKeys.html @@ -0,0 +1,293 @@ + + + + CodingKeys Enumeration Reference + + + + + + + + + + +
    +
    +

    Siren Docs (100% documented)

    +
    +
    +
    + +
    +
    + +
    +
    +
    +

    CodingKeys

    +
    +
    +
    private enum CodingKeys : String, CodingKey
    + +
    +
    +

    Codable Coding Keys for the Results array in the iTunes Lookup API JSON response.

    + +
    +
    +
    +
      +
    • +
      + + + + appID + +
      +
      +
      +
      +
      +
      +

      The appID JSON key.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case appID = "trackId"
      + +
      +
      +
      +
      +
    • +
    • + +
      +
      +
      +
      +
      +

      The current version release date JSON key.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case currentVersionReleaseDate
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + minimumOSVersion + +
      +
      +
      +
      +
      +
      +

      The minimum device iOS version compatibility JSON key.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case minimumOSVersion = "minimumOsVersion"
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + releaseNotes + +
      +
      +
      +
      +
      +
      +

      The release notes JSON key.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case releaseNotes
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + version + +
      +
      +
      +
      +
      +
      +

      The current App Store version JSON key.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case version
      + +
      +
      +
      +
      +
    • +
    +
    +
    +
    + +
    +
    + + + diff --git a/docs/Structs/PresentationManager.html b/docs/Structs/PresentationManager.html new file mode 100644 index 00000000..36ed26af --- /dev/null +++ b/docs/Structs/PresentationManager.html @@ -0,0 +1,838 @@ + + + + PresentationManager Structure Reference + + + + + + + + + + +
    +
    +

    Siren Docs (100% documented)

    +
    +
    +
    + +
    +
    + +
    +
    +
    +

    PresentationManager

    +
    +
    +
    public struct PresentationManager
    + +
    +
    +

    PresentationManager for Siren

    + +
    +
    +
    +
      +
    • +
      + + + + CompletionHandler + +
      +
      +
      +
      +
      +
      +

      Return results or errors obtained from performing a version check with Siren.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      typealias CompletionHandler = (AlertAction) -> Void
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + localization + +
      +
      +
      +
      +
      +
      +

      The localization data structure that will be used to construct localized strings for the update alert.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      let localization: Localization
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + tintColor + +
      +
      +
      +
      +
      +
      +

      The tint color of the UIAlertController buttons.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      let tintColor: UIColor?
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + alertMessage + +
      +
      +
      +
      +
      +
      +

      The descriptive update message of the UIAlertController.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      let alertMessage: String
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + alertTitle + +
      +
      +
      +
      +
      +
      +

      The main message of the UIAlertController.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      let alertTitle: String
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + nextTimeButtonTitle + +
      +
      +
      +
      +
      +
      +

      The Next time button text of the UIAlertController.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      let nextTimeButtonTitle: String
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + skipButtonTitle + +
      +
      +
      +
      +
      +
      +

      The Skip this version button text of the UIAlertController.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      let skipButtonTitle: String
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + updateButtonTitle + +
      +
      +
      +
      +
      +
      +

      The Update button text of the UIAlertController.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      let updateButtonTitle: String
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + alertController + +
      +
      +
      +
      +
      +
      +

      The instance of the UIAlertController used to present the update alert.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      private var alertController: UIAlertController?
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + updaterWindow + +
      +
      +
      +
      +
      +
      +

      The UIWindow instance that presents the SirenViewController.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      private var updaterWindow: UIWindow { get }
      + +
      +
      +
      +
      +
    • +
    • + +
      +
      +
      +
      +
      +

      PresentationManager‘s public initializer.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public init(alertTintColor tintColor: UIColor? = nil,
      +            appName: String? = nil,
      +            alertTitle: String  = AlertConstants.alertTitle,
      +            alertMessage: String  = AlertConstants.alertMessage,
      +            updateButtonTitle: String  = AlertConstants.updateButtonTitle,
      +            nextTimeButtonTitle: String  = AlertConstants.nextTimeButtonTitle,
      +            skipButtonTitle: String  = AlertConstants.skipButtonTitle,
      +            forceLanguageLocalization forceLanguage: Localization.Language? = nil)
      + +
      +
      +
      +

      Parameters

      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      + + tintColor + + +
      +

      The alert’s tintColor. Settings this to nil defaults to the system default color.

      +
      +
      + + appName + + +
      +

      The name of the app (overrides the default/bundled name).

      +
      +
      + + alertTitle + + +
      +

      The title field of the UIAlertController.

      +
      +
      + + alertMessage + + +
      +

      The message field of the UIAlertController.

      +
      +
      + + nextTimeButtonTitle + + +
      +

      The title field of the Next Time Button UIAlertAction.

      +
      +
      + + skipButtonTitle + + +
      +

      The title field of the Skip Button UIAlertAction.

      +
      +
      + + updateButtonTitle + + +
      +

      The title field of the Update Button UIAlertAction.

      +
      +
      + + forceLanguage + + +
      +

      The language the alert to which the alert should be set. If nil, it falls back to the device’s preferred locale.

      +
      +
      +
      +
      +
      +
    • +
    • +
      + + + + default + +
      +
      +
      +
      +
      +
      +

      The default PresentationManager.

      + +

      By default:

      + +
        +
      • There is no tint color (defaults to Apple’s system blue color.)
      • +
      • The name of the app is equal to the name that appears in Info.plist.
      • +
      • The strings are all set to that of the user’s device localization (if supported) or it falls back to English.
      • +
      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public static let `default`: PresentationManager
      + +
      +
      +
      +
      +
    • +
    +
    +
    +
      +
    • + +
      +
      +
      +
      +
      +

      Constructs the localized update alert UIAlertController object.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      mutating func presentAlert(withRules rules: Rules,
      +                           forCurrentAppStoreVersion currentAppStoreVersion: String,
      +                           completion handler: CompletionHandler?)
      + +
      +
      +
      +

      Parameters

      + + + + + + + + + + + + + + + +
      + + rules + + +
      +

      The rules that are used to define the type of alert that should be presented.

      +
      +
      + + currentAppStoreVersion + + +
      +

      The current version of the app in the App Store.

      +
      +
      + + handler + + +
      +

      The completion handler that returns the an AlertAction depending on the type of action the end-user took.

      +
      +
      +
      +
      +
      +
    • +
    • + +
      +
      +
      +
      +
      +

      The UIAlertAction that is executed when the Update option is selected.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      private func updateAlertAction(completion handler: CompletionHandler?) -> UIAlertAction
      + +
      +
      +
      +

      Parameters

      + + + + + + + +
      + + handler + + +
      +

      The completion handler that returns the .update option.

      +
      +
      +
      +
      +

      Return Value

      +

      The Update alert action.

      +
      +
      +
      +
    • +
    • + +
      +
      +
      +
      +
      +

      The UIAlertAction that is executed when the Next time option is selected.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      private func nextTimeAlertAction(completion handler: CompletionHandler?) -> UIAlertAction
      + +
      +
      +
      +

      Parameters

      + + + + + + + +
      + + handler + + +
      +

      The completion handler that returns the .nextTime option.

      +
      +
      +
      +
      +

      Return Value

      +

      The Next time alert action.

      +
      +
      +
      +
    • +
    • + +
      +
      +
      +
      +
      +

      The UIAlertAction that is executed when the Skip this version option is selected.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      private func skipAlertAction(forCurrentAppStoreVersion currentAppStoreVersion: String, completion handler: CompletionHandler?) -> UIAlertAction
      + +
      +
      +
      +

      Parameters

      + + + + + + + + + + + +
      + + currentAppStoreVersion + + +
      +

      The current version of the app in the App Store.

      +
      +
      + + handler + + +
      +

      The completion handler that returns the .skip option.

      +
      +
      +
      +
      +

      Return Value

      +

      The Skip this version alert action.

      +
      +
      +
      +
    • +
    +
    +
    +
    + +
    +
    + + + diff --git a/docs/Structs/Results.html b/docs/Structs/Results.html new file mode 100644 index 00000000..b2deed5f --- /dev/null +++ b/docs/Structs/Results.html @@ -0,0 +1,267 @@ + + + + Results Structure Reference + + + + + + + + + + +
    +
    +

    Siren Docs (100% documented)

    +
    +
    +
    + +
    +
    + +
    +
    +
    +

    Results

    +
    +
    +
    public struct Results
    + +
    +
    +

    The relevant metadata returned from Siren upon completing a successful version check.

    + +
    +
    +
    +
      +
    • +
      + + + + alertAction + +
      +
      +
      +
      +
      +
      +

      The UIAlertAction the user chose upon being presented with the update alert. +Defaults to unknown until an alert is actually presented.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public var alertAction: AlertAction
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + localization + +
      +
      +
      +
      +
      +
      +

      The Siren-supported locale that was used for the string in the update alert.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public var localization: Localization
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + lookupModel + +
      +
      +
      +
      +
      +
      +

      The Swift-mapped API model, if a successful version check was performed.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public var lookupModel: LookupModel
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + updateType + +
      +
      +
      +
      +
      +
      +

      The type of update that was returned for the API.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public var updateType: RulesManager.UpdateType
      + +
      +
      +
      +
      +
    • +
    +
    +
    +
    + +
    +
    + + + diff --git a/docs/Structs/Rules.html b/docs/Structs/Rules.html new file mode 100644 index 00000000..8d6d2ae2 --- /dev/null +++ b/docs/Structs/Rules.html @@ -0,0 +1,472 @@ + + + + Rules Structure Reference + + + + + + + + + + +
    +
    +

    Siren Docs (100% documented)

    +
    +
    +
    + +
    +
    + +
    +
    +
    +

    Rules

    +
    +
    +
    public struct Rules
    + +
    +
    +

    Alert Presentation Rules for Siren.

    + +
    +
    +
    +
      +
    • +
      + + + + alertType + +
      +
      +
      +
      +
      +
      +

      The type of alert that should be presented.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      let alertType: AlertType
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + frequency + +
      +
      +
      +
      +
      +
      +

      The frequency in which a the user is prompted to update the app +once a new version is available in the App Store and if they have not updated yet.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      let frequency: UpdatePromptFrequency
      + +
      +
      +
      +
      +
    • +
    • + +
      +
      +
      +
      +
      +

      Initializes the alert presentation rules.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public init(promptFrequency frequency: UpdatePromptFrequency,
      +            forAlertType alertType: AlertType)
      + +
      +
      +
      +

      Parameters

      + + + + + + + + + + + +
      + + frequency + + +
      +

      How often a user should be prompted to update the app once a new version is available in the App Store.

      +
      +
      + + alertType + + +
      +

      The type of alert that should be presented.

      +
      +
      +
      +
      +
      +
    • +
    • +
      + + + + annoying + +
      +
      +
      +
      +
      +
      +

      Performs a version check immediately, but allows the user to skip updating the app until the next time the app becomes active.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public static var annoying: Rules { get }
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + critical + +
      +
      +
      +
      +
      +
      +

      Performs a version check immediately and forces the user to update the app.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public static var critical: Rules { get }
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + default + +
      +
      +
      +
      +
      +
      +

      Performs a version check once a day, but allows the user to skip updating the app until +the next time the app becomes active or skipping the update all together until another version is released.

      + +

      This is the default setting.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public static var `default`: Rules { get }
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + persistent + +
      +
      +
      +
      +
      +
      +

      Performs a version check daily, but allows the user to skip updating the app until the next time the app becomes active.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public static var persistent: Rules { get }
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + relaxed + +
      +
      +
      +
      +
      +
      +

      Performs a version check weekly, but allows the user to skip updating the app until +the next time the app becomes active or skipping the update all together until another version is released.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public static var relaxed: Rules { get }
      + +
      +
      +
      +
      +
    • +
    +
    +
    +
      +
    • +
      + + + + AlertType + +
      +
      +
      +
      +
      +
      +

      Determines the type of alert to present after a successful version check has been performed.

      + + See more +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public enum AlertType
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + UpdatePromptFrequency + +
      +
      +
      +
      +
      +
      +

      Determines the frequency in which the user is prompted to update the app +once a new version is available in the App Store and if they have not updated yet.

      + + See more +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public enum UpdatePromptFrequency : UInt
      + +
      +
      +
      +
      +
    • +
    +
    +
    +
    + +
    +
    + + + diff --git a/docs/Structs/Rules/AlertType.html b/docs/Structs/Rules/AlertType.html new file mode 100644 index 00000000..a339ed4b --- /dev/null +++ b/docs/Structs/Rules/AlertType.html @@ -0,0 +1,267 @@ + + + + AlertType Enumeration Reference + + + + + + + + + + +
    +
    +

    Siren Docs (100% documented)

    +
    +
    +
    + +
    +
    + +
    +
    +
    +

    AlertType

    +
    +
    +
    public enum AlertType
    + +
    +
    +

    Determines the type of alert to present after a successful version check has been performed.

    + +
    +
    +
    +
      +
    • +
      + + + + force + +
      +
      +
      +
      +
      +
      +

      Forces the user to update your app (1 button alert).

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case force
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + option + +
      +
      +
      +
      +
      +
      +

      Presents the user with option to update app now or at next launch (2 button alert).

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case option
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + skip + +
      +
      +
      +
      +
      +
      +

      Presents the user with option to update the app now, at next launch, or to skip this version all together (3 button alert).

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case skip
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + none + +
      +
      +
      +
      +
      +
      +

      Doesn’t present the alert. +Use this option if you would like to present a custom alert to the end-user.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case none
      + +
      +
      +
      +
      +
    • +
    +
    +
    +
    + +
    +
    + + + diff --git a/docs/Structs/Rules/UpdatePrompFrequency.html b/docs/Structs/Rules/UpdatePrompFrequency.html new file mode 100644 index 00000000..85e98ed6 --- /dev/null +++ b/docs/Structs/Rules/UpdatePrompFrequency.html @@ -0,0 +1,237 @@ + + + + UpdatePrompFrequency Enumeration Reference + + + + + + + + + + +
    +
    +

    Siren Docs (66% documented)

    +
    +
    +
    + +
    +
    + +
    +
    +
    +

    UpdatePrompFrequency

    +
    +
    +
    public enum UpdatePrompFrequency : UInt
    + +
    +
    +

    Determines the frequency in which the user is prompted to update the app +once a new version is available in the App Store and if they have not updated yet.

    + +
    +
    +
    +
      +
    • +
      + + + + immediately + +
      +
      +
      +
      +
      +
      +

      Version check performed every time the app is launched.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case immediately = 0
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + daily + +
      +
      +
      +
      +
      +
      +

      Version check performed once a day.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case daily = 1
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + weekly + +
      +
      +
      +
      +
      +
      +

      Version check performed once a week.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case weekly = 7
      + +
      +
      +
      +
      +
    • +
    +
    +
    +
    + +
    +
    + + + diff --git a/docs/Structs/Rules/UpdatePromptFrequency.html b/docs/Structs/Rules/UpdatePromptFrequency.html new file mode 100644 index 00000000..d04dfa75 --- /dev/null +++ b/docs/Structs/Rules/UpdatePromptFrequency.html @@ -0,0 +1,240 @@ + + + + UpdatePromptFrequency Enumeration Reference + + + + + + + + + + +
    +
    +

    Siren Docs (100% documented)

    +
    +
    +
    + +
    +
    + +
    +
    +
    +

    UpdatePromptFrequency

    +
    +
    +
    public enum UpdatePromptFrequency : UInt
    + +
    +
    +

    Determines the frequency in which the user is prompted to update the app +once a new version is available in the App Store and if they have not updated yet.

    + +
    +
    +
    +
      +
    • +
      + + + + immediately + +
      +
      +
      +
      +
      +
      +

      Version check performed every time the app is launched.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case immediately = 0
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + daily + +
      +
      +
      +
      +
      +
      +

      Version check performed once a day.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case daily = 1
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + weekly + +
      +
      +
      +
      +
      +
      +

      Version check performed once a week.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case weekly = 7
      + +
      +
      +
      +
      +
    • +
    +
    +
    +
    + +
    +
    + + + diff --git a/docs/Structs/RulesManager.html b/docs/Structs/RulesManager.html new file mode 100644 index 00000000..b9b2be68 --- /dev/null +++ b/docs/Structs/RulesManager.html @@ -0,0 +1,570 @@ + + + + RulesManager Structure Reference + + + + + + + + + + +
    +
    +

    Siren Docs (100% documented)

    +
    +
    +
    + +
    +
    + +
    +
    +
    +

    RulesManager

    +
    +
    +
    public struct RulesManager
    + +
    +
    +

    RulesManager for Siren

    + +
    +
    +
    +
      +
    • +
      + + + + releasedForDays + +
      +
      +
      +
      +
      +
      +

      The alert will only show up if the current version has already been released for X days.

      + +

      This value defaults to 1 day (in RulesManager‘s initializer) to avoid an issue where +Apple updates the JSON faster than the app binary propogates to the App Store.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      let releasedForDays: Int
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + majorUpdateRules + +
      +
      +
      +
      +
      +
      +

      The Rules that should be used when the App Store version of the app signifies that it is a major version update (A.b.c.d).

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      var majorUpdateRules: Rules
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + minorUpdateRules + +
      +
      +
      +
      +
      +
      +

      The Rules that should be used when the App Store version of the app signifies that it is a minor version update (a.B.c.d).

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      var minorUpdateRules: Rules
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + patchUpdateRules + +
      +
      +
      +
      +
      +
      +

      The Rules that should be used when the App Store version of the app signifies that it is a patch version update (a.b.C.d).

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      var patchUpdateRules: Rules
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + revisionUpdateRules + +
      +
      +
      +
      +
      +
      +

      The Rules that should be used when the App Store version of the app signifies that it is a revision version update (a.b.c.D).

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      var revisionUpdateRules: Rules
      + +
      +
      +
      +
      +
    • +
    • + +
      +
      +
      +
      +
      +

      Initializer that sets update-specific Rules for all updates (e.g., major, minor, patch, revision). +This means that each of the four update types can have their own specific update rules.

      + +

      By default, the releasedForDays parameter delays the update alert from being presented for 1 day +to avoid an issue where the iTunes Lookup API response is updated faster than the time it takes for the binary +to become available on App Store CDNs across all regions. Usually it takes 6-24 hours, hence the 1 day delay.

      +
      +

      Warning

      +

      Setting releasedForDays to 0 days causes the alert to appear right away, even if the binary isn’t available. +If this value is set to 0 days, and an AlertType of type .force is set, it will cause your app to infinitely send the +end-user to the App Store to download a version that’s not there and lock them out of your application until the binary is +is available to be downloaded.

      + +
      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public init(majorUpdateRules: Rules = .default,
      +            minorUpdateRules: Rules = .default,
      +            patchUpdateRules: Rules = .default,
      +            revisionUpdateRules: Rules = .default,
      +            showAlertAfterCurrentVersionHasBeenReleasedForDays releasedForDays: Int = 1)
      + +
      +
      +
      +

      Parameters

      + + + + + + + + + + + +
      + + rules + + +
      +

      The rules that should be set for all version updates.

      +
      +
      + + releasedForDays + + +
      +

      The amount of time (in days) that the app should delay before presenting the user

      +
      +
      +
      +
      +
      +
    • +
    • + +
      +
      +
      +
      +
      +

      Initializer that sets the same update Rules for all types of updates (e.g., major, minor, patch, revision). +This means that all four update types will use the same presentation rules.

      + +

      By default, the releasedForDays parameter delays the update alert from being presented for 1 day +to avoid an issue where the iTunes Lookup API response is updated faster than the time it takes for the binary +to become available on App Store CDNs across all regions. Usually it takes 6-24 hours, hence the 1 day delay.

      +
      +

      Warning

      +

      Setting releasedForDays to 0 days causes the alert to appear right away, even if the binary isn’t available. +If this value is set to 0 days, and an AlertType of type .force is set, it will cause your app to infinitely send the +end-user to the App Store to download a version that’s not there and lock them out of your application until the binary is +is available to be downloaded.

      + +
      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public init(globalRules rules: Rules = .default,
      +            showAlertAfterCurrentVersionHasBeenReleasedForDays releasedForDays: Int = 1)
      + +
      +
      +
      +

      Parameters

      + + + + + + + + + + + +
      + + rules + + +
      +

      The rules that should be set for all version updates.

      +
      +
      + + releasedForDays + + +
      +

      The amount of time (in days) that the app should delay before presenting the user

      +
      +
      +
      +
      +
      +
    • +
    • + +
      +
      +
      +
      +
      +

      Returns the appropriate update rules based on the type of version that is returned from the API.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      func loadRulesForUpdateType(_ type: UpdateType) -> Rules
      + +
      +
      +
      +

      Parameters

      + + + + + + + +
      + + type + + +
      +

      The type of app update.

      +
      +
      +
      +
      +

      Return Value

      +

      The appropriate rule based on the type of app update that is returned by the API.

      +
      +
      +
      +
    • +
    • +
      + + + + default + +
      +
      +
      +
      +
      +
      +

      The default RulesManager.

      + +

      By default, the Rules.default rule is used for all update typs.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public static let `default`: RulesManager
      + +
      +
      +
      +
      +
    • +
    +
    +
    + +
      +
    • +
      + + + + UpdateType + +
      +
      +
      +
      +
      +
      +

      Informs Siren of the type of update that is available so that +the appropriate ruleset is used to present the update alert.

      + +
        +
      • major: Major release available: A.b.c.d
      • +
      • minor: Minor release available: a.B.c.d
      • +
      • patch: Patch release available: a.b.C.d
      • +
      • revision: Revision release available: a.b.c.D
      • +
      • unknown: No information available about the update.
      • +
      + + See more +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public enum UpdateType : String
      + +
      +
      +
      +
      +
    • +
    +
    +
    +
    + +
    +
    + + + diff --git a/docs/Structs/RulesManager/UpdateType.html b/docs/Structs/RulesManager/UpdateType.html new file mode 100644 index 00000000..c061dcb7 --- /dev/null +++ b/docs/Structs/RulesManager/UpdateType.html @@ -0,0 +1,302 @@ + + + + UpdateType Enumeration Reference + + + + + + + + + + +
    +
    +

    Siren Docs (100% documented)

    +
    +
    +
    + +
    +
    + +
    +
    +
    +

    UpdateType

    +
    +
    +
    public enum UpdateType : String
    + +
    +
    +

    Informs Siren of the type of update that is available so that +the appropriate ruleset is used to present the update alert.

    + +
      +
    • major: Major release available: A.b.c.d
    • +
    • minor: Minor release available: a.B.c.d
    • +
    • patch: Patch release available: a.b.C.d
    • +
    • revision: Revision release available: a.b.c.D
    • +
    • unknown: No information available about the update.
    • +
    + +
    +
    +
    +
      +
    • +
      + + + + major + +
      +
      +
      +
      +
      +
      +

      Major release available: A.b.c.d

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case major
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + minor + +
      +
      +
      +
      +
      +
      +

      Minor release available: a.B.c.d

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case minor
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + patch + +
      +
      +
      +
      +
      +
      +

      Patch release available: a.b.C.d

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case patch
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + revision + +
      +
      +
      +
      +
      +
      +

      Revision release available: a.b.c.D

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case revision
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + unknown + +
      +
      +
      +
      +
      +
      +

      No information available about the update.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case unknown
      + +
      +
      +
      +
      +
    • +
    +
    +
    +
    + +
    +
    + + + diff --git a/docs/Structs/SirenAlertMessaging.html b/docs/Structs/SirenAlertMessaging.html index 8afae6bf..912a86f1 100644 --- a/docs/Structs/SirenAlertMessaging.html +++ b/docs/Structs/SirenAlertMessaging.html @@ -139,9 +139,9 @@

    Declaration

  • @@ -239,7 +239,7 @@

    Parameters

    diff --git a/docs/Structs/SirenAlertMessaging/Constants.html b/docs/Structs/SirenAlertMessaging/Constants.html index 9aa14fd1..a0c6d8aa 100644 --- a/docs/Structs/SirenAlertMessaging/Constants.html +++ b/docs/Structs/SirenAlertMessaging/Constants.html @@ -241,7 +241,7 @@

    Declaration

    diff --git a/docs/Structs/SirenError.html b/docs/Structs/SirenError.html index 22344b0a..72b4ead7 100644 --- a/docs/Structs/SirenError.html +++ b/docs/Structs/SirenError.html @@ -147,7 +147,7 @@

    Declaration

    diff --git a/docs/Structs/SirenError/Known.html b/docs/Structs/SirenError/Known.html index b318c9d3..89f03cbc 100644 --- a/docs/Structs/SirenError/Known.html +++ b/docs/Structs/SirenError/Known.html @@ -117,9 +117,9 @@

    Known

  • @@ -144,9 +144,9 @@

    Declaration

  • @@ -171,9 +171,9 @@

    Declaration

  • @@ -198,9 +198,9 @@

    Declaration

  • @@ -225,9 +225,9 @@

    Declaration

  • @@ -252,9 +252,9 @@

    Declaration

  • @@ -279,9 +279,9 @@

    Declaration

  • @@ -306,9 +306,9 @@

    Declaration

  • @@ -333,9 +333,9 @@

    Declaration

  • @@ -360,9 +360,9 @@

    Declaration

  • @@ -389,7 +389,7 @@

    Declaration

    diff --git a/docs/Structs/SirenLookupModel.html b/docs/Structs/SirenLookupModel.html index 16e627fd..4be7a899 100644 --- a/docs/Structs/SirenLookupModel.html +++ b/docs/Structs/SirenLookupModel.html @@ -161,7 +161,7 @@

    Declaration

    diff --git a/docs/Structs/SirenLookupModel/Results.html b/docs/Structs/SirenLookupModel/Results.html index fb1d7aa5..245d7b7a 100644 --- a/docs/Structs/SirenLookupModel/Results.html +++ b/docs/Structs/SirenLookupModel/Results.html @@ -241,7 +241,7 @@

    Declaration

    diff --git a/docs/docsets/Siren.docset/Contents/Resources/Documents/Classes.html b/docs/docsets/Siren.docset/Contents/Resources/Documents/Classes.html index 48c529f0..6dd105a4 100644 --- a/docs/docsets/Siren.docset/Contents/Resources/Documents/Classes.html +++ b/docs/docsets/Siren.docset/Contents/Resources/Documents/Classes.html @@ -34,13 +34,7 @@ Siren
  • - - @@ -48,15 +42,33 @@ Enumerations @@ -64,22 +76,52 @@ Structures @@ -94,13 +136,6 @@

    Classes

    -
    - - - -

    Siren

    -
    -
    • @@ -115,7 +150,7 @@

      Siren

      -

      The Siren Class. A singleton that is initialized using the shared constant.

      +

      The Siren Class.

      See more
      @@ -132,10 +167,42 @@

      Declaration

    +
    +
      +
    • +
      + + + + SirenViewController + +
      +
      +
      +
      +
      +
      +

      UIViewController Extension for Siren

      + + See more +
      +
      +

      Declaration

      +
      +

      Swift

      +
      final class SirenViewController : UIViewController
      + +
      +
      +
      +
      +
    • +
    +
    diff --git a/docs/docsets/Siren.docset/Contents/Resources/Documents/Classes/Siren.html b/docs/docsets/Siren.docset/Contents/Resources/Documents/Classes/Siren.html index 66663cda..8441ed14 100644 --- a/docs/docsets/Siren.docset/Contents/Resources/Documents/Classes/Siren.html +++ b/docs/docsets/Siren.docset/Contents/Resources/Documents/Classes/Siren.html @@ -34,13 +34,7 @@ Siren - - @@ -48,15 +42,33 @@ Enumerations @@ -64,22 +76,52 @@ Structures @@ -95,7 +137,7 @@

    Siren

    -

    The Siren Class. A singleton that is initialized using the shared constant.

    +

    The Siren Class.

    @@ -104,9 +146,9 @@

    Siren

  • @@ -114,14 +156,14 @@

    Siren

    -

    The error domain for all errors created by Siren.

    +

    Return results or errors obtained from performing a version check with Siren.

    Declaration

    Swift

    -
    public let SirenErrorDomain: String
    +
    public typealias ResultsHandler = (Results?, KnownError?) -> Void
    @@ -131,9 +173,9 @@

    Declaration

  • - - - delegate + + + shared
    @@ -141,15 +183,14 @@

    Declaration

    -

    The SirenDelegate variable, which should be set if you’d like to be notified of any of specific user interactions or API success/failures. -Also set this variable if you’d like to use custom UI for presesnting the update notification.

    +

    The Siren singleton. The main point of entry to the Siren library.

    Declaration

    Swift

    -
    public weak var delegate: SirenDelegate?
    +
    public static let shared: Siren
    @@ -170,7 +211,7 @@

    Declaration

    The debug flag, which is disabled by default. -When enabled, a stream of print() statements are logged to your console when a version check is performed.

    +When enabled, a stream of print() statements are logged to your console when a version check is performed.

    @@ -187,9 +228,9 @@

    Declaration

  • - - - alertType + + + apiManager
    @@ -197,44 +238,17 @@

    Declaration

    -

    Determines the type of alert that should be shown. -See the Siren.AlertType enum for full details.

    +

    The manager that controls the App Store API that is +used to fetch the latest version of the app.

    -
    -
    -

    Declaration

    -
    -

    Swift

    -
    public var alertType: Siren.AlertType { get set }
    - -
    -
    -
    -
    -
  • -
  • -
    - - - - majorUpdateAlertType - -
    -
    -
    -
    -
    -
    -

    Determines the type of alert that should be shown for major version updates: A.b.c -Defaults to Siren.AlertType.option. -See the Siren.AlertType enum for full details.

    +

    Defaults to the US App Store.

    Declaration

    Swift

    -
    public lazy var majorUpdateAlertType: AlertType { get set }
    +
    public lazy var apiManager: APIManager { get set }
    @@ -244,9 +258,9 @@

    Declaration

  • @@ -254,16 +268,16 @@

    Declaration

    -

    Determines the type of alert that should be shown for minor version updates: a.B.c -Defaults to Siren.AlertType.option. -See the Siren.AlertType enum for full details.

    +

    The manager that controls the update alert’s string localization and tint color.

    + +

    Defaults the string’s lange localization to the user’s device localization.

    Declaration

    Swift

    -
    public lazy var minorUpdateAlertType: AlertType { get set }
    +
    public lazy var presentationManager: PresentationManager { get set }
    @@ -273,9 +287,9 @@

    Declaration

  • @@ -283,16 +297,21 @@

    Declaration

    -

    Determines the type of alert that should be shown for minor patch updates: a.b.C -Defaults to Siren.AlertType.option. -See the Siren.AlertType enum for full details.

    +

    The manager that controls the type of alert that should be displayed +and how often an alert should be displayed dpeneding on the type +of update that is available relative to the installed version of the app +(e.g., different rules for major, minor, patch and revision updated can be used).

    + +

    Defaults to performing a version check once a day with an alert that allows +the user to skip updating the app until the next time the app becomes active or +skipping the update all together until another version is released.

    Declaration

    Swift

    -
    public lazy var patchUpdateAlertType: AlertType { get set }
    +
    public lazy var rulesManager: RulesManager { get set }
    @@ -302,9 +321,9 @@

    Declaration

  • @@ -312,16 +331,14 @@

    Declaration

    -

    Determines the type of alert that should be shown for revision updates: a.b.c.D -Defaults to Siren.AlertType.option. -See the Siren.AlertType enum for full details.

    +

    The current installed version of your app.

    Declaration

    Swift

    -
    public lazy var revisionUpdateAlertType: AlertType { get set }
    +
    lazy var currentInstalledVersion: String? { get set }
    @@ -331,9 +348,9 @@

    Declaration

  • @@ -341,15 +358,14 @@

    Declaration

    -

    The name of your app. -By default, it’s set to the name of the app that’s stored in your plist.

    +

    The retained NotificationCenter observer that listens for UIApplication.didBecomeActiveNotification notifications.

    Declaration

    Swift

    -
    public lazy var appName: String { get set }
    +
    var didBecomeActiveObserver: NSObjectProtocol?
    @@ -359,9 +375,9 @@

    Declaration

  • @@ -369,15 +385,14 @@

    Declaration

    -

    Overrides all the Strings to which Siren defaults. -Defaults to the values defined in SirenAlertMessaging.Constants

    +

    The last date that an alert was presented to the user.

    Declaration

    Swift

    -
    public var alertMessaging: SirenAlertMessaging
    +
    private var alertPresentationDate: Date?
    @@ -387,9 +402,9 @@

    Declaration

  • - - - countryCode + + + appID
    @@ -397,16 +412,14 @@

    Declaration

    -

    The region or country of an App Store in which your app is available. -By default, all version checks are performed against the US App Store. -If your app is not available in the US App Store, set it to the identifier of at least one App Store within which it is available.

    +

    The App Store’s unique identifier for an app.

    Declaration

    Swift

    -
    public var countryCode: String?
    +
    private var appID: Int?
    @@ -416,9 +429,9 @@

    Declaration

  • @@ -426,15 +439,14 @@

    Declaration

    -

    Overrides the default localization of a user’s device when presenting the update message and button titles in the alert. -See the Siren.LanguageType enum for more details.

    +

    The completion handler used to return the results or errors returned by Siren.

    Declaration

    Swift

    -
    public var forceLanguageLocalization: Siren.LanguageType?
    +
    private var resultsHandler: Siren.ResultsHandler?
    @@ -444,9 +456,9 @@

    Declaration

  • @@ -454,26 +466,37 @@

    Declaration

    -

    Overrides the tint color for UIAlertController.

    +

    The initialization method.

    Declaration

    Swift

    -
    public var alertControllerTintColor: UIColor?
    +
    private override init()
  • + +
    +
    + +
    • @@ -481,27 +504,44 @@

      Declaration

      -

      When this is set, the alert will only show up if the current version has already been released for X days. -Defaults to 1 day to avoid an issue where Apple updates the JSON faster than the app binary propogates to the App Store.

      - +

      Declaration

      Swift

      -
      public var showAlertAfterCurrentVersionHasBeenReleasedForDays: Int
      +
      func wail(completion handler: ResultsHandler? = nil)
      +
      +

      Parameters

      + + + + + + + +
      + + handler + + +
      +

      +
      +
      +
    • @@ -509,26 +549,40 @@

      Declaration

      -

      The current version of your app that is available for download on the App Store

      +

      Launches the AppStore in two situations when the user clicked the Update button in the UIAlertController modal.

      + +

      This function is marked public as a convenience for those developers who decide to build a custom alert modal +instead of using Siren’s prebuilt update alert.

      Declaration

      Swift

      -
      public internal(set) var currentAppStoreVersion: String?
      +
      func launchAppStore()
    • +
    +
    +
    + +
    • @@ -536,14 +590,14 @@

      Declaration

      -

      The App’s Singleton

      +

      Initiates the uni-directional version checking flow.

      Declaration

      Swift

      -
      public static let shared: Siren
      +
      func performVersionCheck()
      @@ -553,9 +607,9 @@

      Declaration

    • @@ -563,16 +617,16 @@

      Declaration

      -

      Checks the currently installed version of your app against the App Store. -The default check is against the US App Store, but if your app is not listed in the US, -you should set the countryCode property before calling this method. Please refer to the countryCode property for more information.

      +

      Validates the parsed and mapped iTunes Lookup Model +to guarantee all the relevant data was returned before +attempting to present an alert.

      Declaration

      Swift

      -
      public func checkVersion(checkType: VersionCheckType)
      +
      func validate(model: LookupModel)
      @@ -583,12 +637,12 @@

      Parameters

      - checkType + model
      -

      The frequency in days in which you want a check to be performed. Please refer to the Siren.VersionCheckType enum for more details.

      +

      The iTunes Lookup Model.

      @@ -601,9 +655,9 @@

      Parameters

    • @@ -611,60 +665,48 @@

      Parameters

      -

      Launches the AppStore in two situations:

      - -
        -
      • User clicked the Update button in the UIAlertController modal.
      • -
      • Developer built a custom alert modal and needs to be able to call this function when the user chooses to update the app in the aforementioned custom modal.
      • -
      +

      Determines if the update alert can be presented based on the +rules set in the RulesManager and the the skip version settings.

      Declaration

      Swift

      -
      public func launchAppStore()
      +
      func determineIfAlertPresentationRulesAreSatisfied(forCurrentAppStoreVersion currentAppStoreVersion: String, andLookupModel model: LookupModel)
      -
      -
      -
    • -
    -
    -
    - -
      -
    • -
      - - - - AlertType - -
      -
      -
      -
      -
      -
      -

      Determines the type of alert to present after a successful version check has been performed.

      - - See more -
      -
      -

      Declaration

      -
      -

      Swift

      -
      enum AlertType
      - -
      +
      +

      Parameters

      + + + + + + + + + + + +
      + + currentAppStoreVersion + + +
      +

      The curren version of the app in the App Store.

      +
      +
      + + model + + +
      +

      The iTunes Lookup Model.

      +
      +
      @@ -672,9 +714,9 @@

      Declaration

    • @@ -682,27 +724,85 @@

      Declaration

      -

      Determines the frequency in which the the version check is performed and the user is prompted to update the app.

      +

      Presents the update alert to the end user. +Upon tapping a value on the alert view, a completion handler will return all relevant metadata to the app.

      - See more

      Declaration

      Swift

      -
      enum VersionCheckType : Int
      +
      func presentAlert(withRules rules: Rules,
      +                  forCurrentAppStoreVersion currentAppStoreVersion: String,
      +                  model: LookupModel,
      +                  andUpdateType updateType: RulesManager.UpdateType)
      +
      +

      Parameters

      + + + + + + + + + + + + + + + + + + + +
      + + rules + + +
      +

      The rules for how to present the alert.

      +
      +
      + + currentAppStoreVersion + + +
      +

      The current version of the app in the App Store.

      +
      +
      + + model + + +
      +

      The iTunes Lookup Model.

      +
      +
      + + updateType + + +
      +

      The type of update that is available based on the version found in the App Store.

      +
      +
      +
    • @@ -710,18 +810,15 @@

      Declaration

      -

      Determines the available languages in which the update message and alert button titles should appear.

      - -

      By default, the operating system’s default lanuage setting is used. However, you can force a specific language -by setting the forceLanguageLocalization property before calling checkVersion()

      +

      Add an observer that listens for app launching/relaunching +(e.g., calls to UIApplication‘s didBecomeActive function).

      - See more

      Declaration

      Swift

      -
      enum LanguageType : String
      +
      func addObservers()
      @@ -733,7 +830,7 @@

      Declaration

  • diff --git a/docs/docsets/Siren.docset/Contents/Resources/Documents/Classes/Siren/AlertType.html b/docs/docsets/Siren.docset/Contents/Resources/Documents/Classes/Siren/AlertType.html index 414365b5..dac6a08f 100644 --- a/docs/docsets/Siren.docset/Contents/Resources/Documents/Classes/Siren/AlertType.html +++ b/docs/docsets/Siren.docset/Contents/Resources/Documents/Classes/Siren/AlertType.html @@ -64,22 +64,22 @@ Structures
  • @@ -104,9 +104,9 @@

    AlertType

  • - + - force + force
    @@ -131,9 +131,9 @@

    Declaration

  • - + - option + option
    @@ -158,9 +158,9 @@

    Declaration

  • - + - skip + skip
    @@ -185,9 +185,9 @@

    Declaration

  • - + - none + none
    @@ -215,7 +215,7 @@

    Declaration

  • diff --git a/docs/docsets/Siren.docset/Contents/Resources/Documents/Classes/Siren/LanguageType.html b/docs/docsets/Siren.docset/Contents/Resources/Documents/Classes/Siren/LanguageType.html index 0802a3cd..1c849a98 100644 --- a/docs/docsets/Siren.docset/Contents/Resources/Documents/Classes/Siren/LanguageType.html +++ b/docs/docsets/Siren.docset/Contents/Resources/Documents/Classes/Siren/LanguageType.html @@ -64,22 +64,22 @@ Structures
  • @@ -107,9 +107,9 @@

    LanguageType

  • - + - arabic + arabic
    @@ -134,9 +134,9 @@

    Declaration

  • - + - armenian + armenian
    @@ -161,9 +161,9 @@

    Declaration

  • - + - basque + basque
    @@ -188,9 +188,9 @@

    Declaration

  • @@ -215,9 +215,9 @@

    Declaration

  • @@ -242,9 +242,9 @@

    Declaration

  • - + - croatian + croatian
    @@ -269,9 +269,9 @@

    Declaration

  • - + - czech + czech
    @@ -296,9 +296,9 @@

    Declaration

  • - + - danish + danish
    @@ -323,9 +323,9 @@

    Declaration

  • - + - dutch + dutch
    @@ -350,9 +350,9 @@

    Declaration

  • - + - english + english
    @@ -377,9 +377,9 @@

    Declaration

  • - + - estonian + estonian
    @@ -404,9 +404,9 @@

    Declaration

  • - + - finnish + finnish
    @@ -431,9 +431,9 @@

    Declaration

  • - + - french + french
    @@ -458,9 +458,9 @@

    Declaration

  • - + - german + german
    @@ -485,9 +485,9 @@

    Declaration

  • - + - greek + greek
    @@ -512,9 +512,9 @@

    Declaration

  • - + - hebrew + hebrew
    @@ -539,9 +539,9 @@

    Declaration

  • @@ -566,9 +566,9 @@

    Declaration

  • @@ -593,9 +593,9 @@

    Declaration

  • - + - italian + italian
    @@ -620,9 +620,9 @@

    Declaration

  • - + - japanese + japanese
    @@ -647,9 +647,9 @@

    Declaration

  • - + - korean + korean
    @@ -674,9 +674,9 @@

    Declaration

  • - + - latvian + latvian
    @@ -701,9 +701,9 @@

    Declaration

  • @@ -728,9 +728,9 @@

    Declaration

  • - + - malay + malay
    @@ -755,9 +755,9 @@

    Declaration

  • @@ -782,9 +782,9 @@

    Declaration

  • - + - persian + persian
    @@ -809,9 +809,9 @@

    Declaration

  • @@ -836,9 +836,9 @@

    Declaration

  • @@ -863,9 +863,9 @@

    Declaration

  • - + - polish + polish
    @@ -890,9 +890,9 @@

    Declaration

  • @@ -917,9 +917,9 @@

    Declaration

  • @@ -944,9 +944,9 @@

    Declaration

  • - + - russian + russian
    @@ -971,9 +971,9 @@

    Declaration

  • @@ -998,9 +998,9 @@

    Declaration

  • @@ -1025,9 +1025,9 @@

    Declaration

  • @@ -1052,9 +1052,9 @@

    Declaration

  • - + - spanish + spanish
    @@ -1079,9 +1079,9 @@

    Declaration

  • - + - swedish + swedish
    @@ -1106,9 +1106,9 @@

    Declaration

  • - + - thai + thai
    @@ -1133,9 +1133,9 @@

    Declaration

  • - + - turkish + turkish
    @@ -1160,9 +1160,9 @@

    Declaration

  • - + - urdu + urdu
    @@ -1187,9 +1187,9 @@

    Declaration

  • @@ -1214,9 +1214,9 @@

    Declaration

  • @@ -1243,7 +1243,7 @@

    Declaration

  • diff --git a/docs/docsets/Siren.docset/Contents/Resources/Documents/Classes/Siren/VersionCheckType.html b/docs/docsets/Siren.docset/Contents/Resources/Documents/Classes/Siren/VersionCheckType.html index 5a289556..548a463d 100644 --- a/docs/docsets/Siren.docset/Contents/Resources/Documents/Classes/Siren/VersionCheckType.html +++ b/docs/docsets/Siren.docset/Contents/Resources/Documents/Classes/Siren/VersionCheckType.html @@ -64,22 +64,22 @@ Structures
  • @@ -104,9 +104,9 @@

    VersionCheckType

  • @@ -131,9 +131,9 @@

    Declaration

  • - + - daily + daily
    @@ -158,9 +158,9 @@

    Declaration

  • - + - weekly + weekly
    @@ -187,7 +187,7 @@

    Declaration

  • diff --git a/docs/docsets/Siren.docset/Contents/Resources/Documents/Classes/SirenViewController.html b/docs/docsets/Siren.docset/Contents/Resources/Documents/Classes/SirenViewController.html new file mode 100644 index 00000000..543f954f --- /dev/null +++ b/docs/docsets/Siren.docset/Contents/Resources/Documents/Classes/SirenViewController.html @@ -0,0 +1,185 @@ + + + + SirenViewController Class Reference + + + + + + + + + + +
    +
    +

    Siren Docs (100% documented)

    +
    +
    +
    + +
    +
    + +
    +
    +
    +

    SirenViewController

    +
    +
    +
    final class SirenViewController : UIViewController
    + +
    +
    +

    UIViewController Extension for Siren

    + +
    +
    +
    +
      +
    • + +
      +
      +
      +
      +
      +

      UIStatusBarStyle override.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      override var preferredStatusBarStyle: UIStatusBarStyle { get }
      + +
      +
      +
      +
      +
    • +
    +
    +
    +
    + +
    +
    + +
    + diff --git a/docs/docsets/Siren.docset/Contents/Resources/Documents/Enums.html b/docs/docsets/Siren.docset/Contents/Resources/Documents/Enums.html index bcf77e18..5e6c536b 100644 --- a/docs/docsets/Siren.docset/Contents/Resources/Documents/Enums.html +++ b/docs/docsets/Siren.docset/Contents/Resources/Documents/Enums.html @@ -34,13 +34,7 @@ Siren
  • - -
  • @@ -48,15 +42,33 @@ Enumerations
  • @@ -64,22 +76,52 @@ Structures @@ -98,9 +140,9 @@

    Enumerations

  • - - - UpdateType + + + AlertAction
    @@ -108,26 +150,47 @@

    Enumerations

    -

    MARK - Siren UpdateType -UpdateType defines what kind of update is available. -It is used as parameter if user wants to use -custom alert to inform the user about an update.

    +

    The UIAlertController button that was pressed upon being presented an update alert.

    -
      -
    • major: Major release available: A.b.c.d
    • -
    • minor: Minor release available: a.B.c.d
    • -
    • patch: Patch release available: a.b.C.d
    • -
    • revision: Revision release available: a.b.c.D
    • -
    • unknown: No information available about the update.
    • -
    + See more +
    +
    +

    Declaration

    +
    +

    Swift

    +
    public enum AlertAction
    + +
    +
    +
    +
    +
  • + + +
    +
      +
    • +
      + + + + KnownError + +
      +
      +
      +
      +
      +
      +

      Enumerates all potentials errors that Siren can handle.

      - See more + See more

      Declaration

      Swift

      -
      public enum UpdateType : String
      +
      public enum KnownError : LocalizedError
      @@ -139,7 +202,7 @@

      Declaration

    diff --git a/docs/docsets/Siren.docset/Contents/Resources/Documents/Enums/AlertAction.html b/docs/docsets/Siren.docset/Contents/Resources/Documents/Enums/AlertAction.html new file mode 100644 index 00000000..df23cf7a --- /dev/null +++ b/docs/docsets/Siren.docset/Contents/Resources/Documents/Enums/AlertAction.html @@ -0,0 +1,266 @@ + + + + AlertAction Enumeration Reference + + + + + + + + + + +
    +
    +

    Siren Docs (100% documented)

    +
    +
    +
    + +
    +
    + +
    +
    +
    +

    AlertAction

    +
    +
    +
    public enum AlertAction
    + +
    +
    +

    The UIAlertController button that was pressed upon being presented an update alert.

    + +
    +
    +
    +
      +
    • +
      + + + + appStore + +
      +
      +
      +
      +
      +
      +

      The user clicked on the Update option, which took them to the app’s App Store page.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case appStore
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + nextTime + +
      +
      +
      +
      +
      +
      +

      The user clicked on the Next Time option, which dismissed the alert.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case nextTime
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + skip + +
      +
      +
      +
      +
      +
      +

      The user clicked on the Skip this version option, which dismissed the alert.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case skip
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + unknown + +
      +
      +
      +
      +
      +
      +

      (Default) The user never chose an option. This is returned when an error is thrown by Siren.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case unknown
      + +
      +
      +
      +
      +
    • +
    +
    +
    +
    + +
    +
    + + + diff --git a/docs/docsets/Siren.docset/Contents/Resources/Documents/Enums/KnownError.html b/docs/docsets/Siren.docset/Contents/Resources/Documents/Enums/KnownError.html new file mode 100644 index 00000000..e60c3168 --- /dev/null +++ b/docs/docsets/Siren.docset/Contents/Resources/Documents/Enums/KnownError.html @@ -0,0 +1,563 @@ + + + + KnownError Enumeration Reference + + + + + + + + + + +
    +
    +

    Siren Docs (100% documented)

    +
    +
    +
    + +
    +
    + +
    +
    +
    +

    KnownError

    +
    +
    +
    public enum KnownError : LocalizedError
    + +
    +
    +

    Enumerates all potentials errors that Siren can handle.

    + +
    +
    +
    +
      +
    • +
      + + + + appStoreAppIDFailure + +
      +
      +
      +
      +
      +
      +

      Error retrieving trackId as the JSON does not contain a ‘trackId’ key.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case appStoreAppIDFailure
      + +
      +
      +
      +
      +
    • +
    • + +
      +
      +
      +
      +
      +

      Error retrieving App Store data as JSON results were empty. Is your app available in the US? If not, change the countryCode variable to fix this error.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case appStoreDataRetrievalEmptyResults
      + +
      +
      +
      +
      +
    • +
    • + +
      +
      +
      +
      +
      +

      Error retrieving App Store data as an error was returned.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case appStoreDataRetrievalFailure(underlyingError: Error?)
      + +
      +
      +
      +
      +
    • +
    • + +
      +
      +
      +
      +
      +

      Error parsing App Store JSON data.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case appStoreJSONParsingFailure(underlyingError: Error)
      + +
      +
      +
      +
      +
    • +
    • + +
      +
      +
      +
      +
      +

      The version of iOS on the device is lower than that of the one required by the app verison update.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case appStoreOSVersionUnsupported
      + +
      +
      +
      +
      +
    • +
    • + +
      +
      +
      +
      +
      +

      Error retrieving App Store verson number as the JSON does not contain a version key.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case appStoreVersionArrayFailure
      + +
      +
      +
      +
      +
    • +
    • + +
      +
      +
      +
      +
      +

      The currentVersionReleaseDate key is missing in the JSON payload. Please leave an issue on https://github.com/ArtSabintsev/Siren with as many details as possible.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case currentVersionReleaseDate
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + malformedURL + +
      +
      +
      +
      +
      +
      +

      One of the iTunes URLs used in Siren is malformed. Please leave an issue on https://github.com/ArtSabintsev/Siren with as many details as possible.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case malformedURL
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + missingBundleID + +
      +
      +
      +
      +
      +
      +

      Please make sure that you have set a Bundle Identifier in your project.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case missingBundleID
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + noUpdateAvailable + +
      +
      +
      +
      +
      +
      +

      No new update available.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case noUpdateAvailable
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + recentlyPrompted + +
      +
      +
      +
      +
      +
      +

      Siren will not present an update alert if it performed one too recently. If you would like to present an alert every time Siren is called, please consider setting the UpdatePromptFrequency.immediately rule in RulesManager

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case recentlyPrompted
      + +
      +
      +
      +
      +
    • +
    • + +
      +
      +
      +
      +
      +

      The app has been released for X days, but Siren cannot prompt the user until Y (where Y > X) days have passed.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case releasedTooSoon(daysSinceRelease: Int, releasedForDays: Int)
      + +
      +
      +
      +
      +
    • +
    • + +
      +
      +
      +
      +
      +

      The user has opted to skip updating their current version of the app to the current App Store version.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case skipVersionUpdate(installedVersion: String, appStoreVersion: String)
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + localizedDescription + +
      +
      +
      +
      +
      +
      +

      The localized description for each error handled by Siren.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public var localizedDescription: String { get }
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + sirenError + +
      +
      +
      +
      +
      +
      +

      An easily identifiable prefix for all errors thrown by Siren.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      private static var sirenError: String { get }
      + +
      +
      +
      +
      +
    • +
    +
    +
    +
    + +
    +
    + + + diff --git a/docs/docsets/Siren.docset/Contents/Resources/Documents/Enums/UpdateType.html b/docs/docsets/Siren.docset/Contents/Resources/Documents/Enums/UpdateType.html index 3a7b62d6..194d915c 100644 --- a/docs/docsets/Siren.docset/Contents/Resources/Documents/Enums/UpdateType.html +++ b/docs/docsets/Siren.docset/Contents/Resources/Documents/Enums/UpdateType.html @@ -64,22 +64,22 @@ Structures @@ -115,9 +115,9 @@

    UpdateType

  • - + - major + major
    @@ -142,9 +142,9 @@

    Declaration

  • - + - minor + minor
    @@ -169,9 +169,9 @@

    Declaration

  • - + - patch + patch
    @@ -196,9 +196,9 @@

    Declaration

  • - + - revision + revision
    @@ -223,9 +223,9 @@

    Declaration

  • - + - unknown + unknown
    @@ -252,7 +252,7 @@

    Declaration

    diff --git a/docs/docsets/Siren.docset/Contents/Resources/Documents/Extensions.html b/docs/docsets/Siren.docset/Contents/Resources/Documents/Extensions.html new file mode 100644 index 00000000..b3f816c0 --- /dev/null +++ b/docs/docsets/Siren.docset/Contents/Resources/Documents/Extensions.html @@ -0,0 +1,272 @@ + + + + Extensions Reference + + + + + + + + + + +
    +
    +

    Siren Docs (100% documented)

    +
    +
    +
    + +
    +
    + +
    +
    +
    +

    Extensions

    +

    The following extensions are available globally.

    + +
    +
    +
    +
      +
    • +
      + + + + Bundle + +
      +
      +
      +
      +
      +
      + + See more +
      +
      +

      Declaration

      +
      +

      Swift

      +
      class Bundle : NSObject
      + +
      +
      +
      +
      +
    • +
    +
    +
    +
      +
    • +
      + + + + Date + +
      +
      +
      +
      +
      +
      + + See more +
      +
      +

      Declaration

      +
      +

      Swift

      +
      struct Date : ReferenceConvertible, Comparable, Equatable
      + +
      +
      +
      +
      +
    • +
    +
    +
    +
      +
    • +
      + + + + UIAlertController + +
      +
      +
      +
      +
      +
      + + See more +
      +
      +

      Declaration

      +
      +

      Swift

      +
      class UIAlertController : UIViewController
      + +
      +
      +
      +
      +
    • +
    +
    +
    +
      +
    • +
      + + + + UserDefaults + +
      +
      +
      +
      +
      +
      + + See more +
      +
      +

      Declaration

      +
      +

      Swift

      +
      class UserDefaults : NSObject
      + +
      +
      +
      +
      +
    • +
    +
    +
    +
    + +
    +
    + +
    + diff --git a/docs/docsets/Siren.docset/Contents/Resources/Documents/Extensions/Bundle.html b/docs/docsets/Siren.docset/Contents/Resources/Documents/Extensions/Bundle.html new file mode 100644 index 00000000..a200bd70 --- /dev/null +++ b/docs/docsets/Siren.docset/Contents/Resources/Documents/Extensions/Bundle.html @@ -0,0 +1,433 @@ + + + + Bundle Extension Reference + + + + + + + + + + +
    +
    +

    Siren Docs (100% documented)

    +
    +
    +
    + +
    +
    + +
    +
    +
    +

    Bundle

    +
    +
    +
    class Bundle : NSObject
    + +
    +
    + +
    +
    +
    +
      +
    • +
      + + + + Constants + +
      +
      +
      +
      +
      +
      +

      Constants used in the Bundle extension.

      + + See more +
      +
      +

      Declaration

      +
      +

      Swift

      +
      struct Constants
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + version() + +
      +
      +
      +
      +
      +
      +

      Fetches the current verison of the app.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      final class func version() -> String?
      + +
      +
      +
      +

      Return Value

      +

      The current installed version of the app.

      +
      +
      +
      +
    • +
    • + +
      +
      +
      +
      +
      +

      Returns the localized string for a given default string.

      + +

      By default, the English language localization is used. +If the device’s localization is set to another locale, that local’s language is used if it’s supported by Siren. +If forcedLanguage is set to true, the chosen language is shown for all devices, irrespective of their device’s localization.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      final class func localizedString(forKey key: String, andForceLocalization forcedLanguage: Localization.Language?) -> String
      + +
      +
      +
      +

      Parameters

      + + + + + + + + + + + +
      + + key + + +
      +

      The default string used to search the localization table for a specific translation.

      +
      +
      + + forcedLanguage + + +
      +

      Returns

      +
      +
      +
      +
      +

      Return Value

      +

      The localized string for a given key.

      +
      +
      +
      +
    • +
    • +
      + + + + bestMatchingAppName() + +
      +
      +
      +
      +
      +
      +

      The appropriate name for the app to be displayed in the update alert.

      + +

      Siren checks CFBundleDisplayName first. It then falls back to +to kCFBundleNameKey and ultimately to an empty string +if the aforementioned values are nil.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      final class func bestMatchingAppName() -> String
      + +
      +
      +
      +

      Return Value

      +

      The name of the app.

      +
      +
      +
      +
    • +
    +
    +
    +
      +
    • +
      + + + + sirenBundlePath() + +
      +
      +
      +
      +
      +
      +

      The path to Siren’s localization Bundle.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      final class func sirenBundlePath() -> String?
      + +
      +
      +
      +

      Return Value

      +

      The bundle’s path or nil.

      +
      +
      +
      +
    • +
    • + +
      +
      +
      +
      +
      +

      The path for a particular language localizationin Siren’s localization Bundle.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      final class func sirenForcedBundlePath(forceLanguageLocalization: Localization.Language) -> String?
      + +
      +
      +
      +

      Parameters

      + + + + + + + +
      + + forceLanguageLocalization + + +
      +

      The language localization that should be searched for in Siren’s localization bundle.

      +
      +
      +
      +
      +

      Return Value

      +

      The path to the forced language localization.

      +
      +
      +
      +
    • +
    • +
      + + + + deviceLanguage() + +
      +
      +
      +
      +
      +
      +

      The user’s preferred language based on their device’s localization.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      final class func deviceLanguage() -> Localization.Language?
      + +
      +
      +
      +

      Return Value

      +

      The user’s preferred language.

      +
      +
      +
      +
    • +
    +
    +
    +
    + +
    +
    + + + diff --git a/docs/docsets/Siren.docset/Contents/Resources/Documents/Extensions/Bundle/Constants.html b/docs/docsets/Siren.docset/Contents/Resources/Documents/Extensions/Bundle/Constants.html new file mode 100644 index 00000000..744910b9 --- /dev/null +++ b/docs/docsets/Siren.docset/Contents/Resources/Documents/Extensions/Bundle/Constants.html @@ -0,0 +1,320 @@ + + + + Constants Structure Reference + + + + + + + + + + +
    +
    +

    Siren Docs (100% documented)

    +
    +
    +
    + +
    +
    + +
    +
    +
    +

    Constants

    +
    +
    +
    struct Constants
    + +
    +
    +

    Constants used in the Bundle extension.

    + +
    +
    +
    +
      +
    • +
      + + + + bundleExtension + +
      +
      +
      +
      +
      +
      +

      Constant for the .bundle file extension.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      static let bundleExtension: String
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + displayName + +
      +
      +
      +
      +
      +
      +

      Constant for CFBundleDisplayName.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      static let displayName: String
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + englishLocalization + +
      +
      +
      +
      +
      +
      +

      Constant for the default US English localization.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      static let englishLocalization: String
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + projectExtension + +
      +
      +
      +
      +
      +
      +

      Constant for the project file extension.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      static let projectExtension: String
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + shortVersionString + +
      +
      +
      +
      +
      +
      +

      Constant for CFBundleShortVersionString.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      static let shortVersionString: String
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + table + +
      +
      +
      +
      +
      +
      +

      Constant for the localization table.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      static let table: String
      + +
      +
      +
      +
      +
    • +
    +
    +
    +
    + +
    +
    + + + diff --git a/docs/docsets/Siren.docset/Contents/Resources/Documents/Extensions/Date.html b/docs/docsets/Siren.docset/Contents/Resources/Documents/Extensions/Date.html new file mode 100644 index 00000000..c827524a --- /dev/null +++ b/docs/docsets/Siren.docset/Contents/Resources/Documents/Extensions/Date.html @@ -0,0 +1,257 @@ + + + + Date Extension Reference + + + + + + + + + + +
    +
    +

    Siren Docs (100% documented)

    +
    +
    +
    + +
    +
    + +
    +
    +
    +

    Date

    +
    +
    +
    struct Date : ReferenceConvertible, Comparable, Equatable
    + +
    +
    + +
    +
    +
    +
      +
    • +
      + + + + days(since:) + +
      +
      +
      +
      +
      +
      +

      The amount of days passed from a specific source date.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      static func days(since date: Date) -> Int
      + +
      +
      +
      +

      Parameters

      + + + + + + + +
      + + date + + +
      +

      The source date.

      +
      +
      +
      +
      +

      Return Value

      +

      The amount of days passed since the source date.

      +
      +
      +
      +
    • +
    • +
      + + + + days(since:) + +
      +
      +
      +
      +
      +
      +

      The amount of days passed from a specific source date string.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      static func days(since dateString: String) -> Int?
      + +
      +
      +
      +

      Parameters

      + + + + + + + +
      + + dateString + + +
      +

      The source date string.

      +
      +
      +
      +
      +

      Return Value

      +

      The amount of days passed since the source date.

      +
      +
      +
      +
    • +
    +
    +
    +
    + +
    +
    + + + diff --git a/docs/docsets/Siren.docset/Contents/Resources/Documents/Extensions/UIAlertController.html b/docs/docsets/Siren.docset/Contents/Resources/Documents/Extensions/UIAlertController.html new file mode 100644 index 00000000..41651dd0 --- /dev/null +++ b/docs/docsets/Siren.docset/Contents/Resources/Documents/Extensions/UIAlertController.html @@ -0,0 +1,249 @@ + + + + UIAlertController Extension Reference + + + + + + + + + + +
    +
    +

    Siren Docs (100% documented)

    +
    +
    +
    + +
    +
    + +
    +
    +
    +

    UIAlertController

    +
    +
    +
    class UIAlertController : UIViewController
    + +
    +
    + +
    +
    +
    +
      +
    • +
      + + + + show(window:) + +
      +
      +
      +
      +
      +
      +

      Presents Siren’s UIAlertController in a new UIWindow.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      func show(window: UIWindow)
      + +
      +
      +
      +

      Parameters

      + + + + + + + +
      + + window + + +
      +

      The UIWindow that should reference Siren’s UIAlertController.

      +
      +
      +
      +
      +
      +
    • +
    • +
      + + + + hide(window:) + +
      +
      +
      +
      +
      +
      +

      Hides Siren’s UIAlertController within a given window.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      func hide(window: UIWindow)
      + +
      +
      +
      +

      Parameters

      + + + + + + + +
      + + window + + +
      +

      The UIWindow that references Siren’s UIAlertController.

      +
      +
      +
      +
      +
      +
    • +
    +
    +
    +
    + +
    +
    + + + diff --git a/docs/docsets/Siren.docset/Contents/Resources/Documents/Extensions/UserDefaults.html b/docs/docsets/Siren.docset/Contents/Resources/Documents/Extensions/UserDefaults.html new file mode 100644 index 00000000..1c64d3cd --- /dev/null +++ b/docs/docsets/Siren.docset/Contents/Resources/Documents/Extensions/UserDefaults.html @@ -0,0 +1,266 @@ + + + + UserDefaults Extension Reference + + + + + + + + + + +
    +
    +

    Siren Docs (100% documented)

    +
    +
    +
    + +
    +
    + +
    +
    +
    +

    UserDefaults

    +
    +
    +
    class UserDefaults : NSObject
    + +
    +
    + +
    +
    +
    +
      +
    • +
      + + + + SirenKeys + +
      +
      +
      +
      +
      +
      +

      Siren-specific UserDefaults Keys

      + + See more +
      +
      +

      Declaration

      +
      +

      Swift

      +
      private enum SirenKeys : String
      + +
      +
      +
      +
      +
    • +
    • + +
      +
      +
      +
      +
      +

      Sets and Gets a UserDefault around performing a version check on a subsequent launch.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      static var shouldPerformVersionCheckOnSubsequentLaunch: Bool { get set }
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + storedSkippedVersion + +
      +
      +
      +
      +
      +
      +

      Sets and Gets a UserDefault around storing a version that the user wants to skip updating.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      static var storedSkippedVersion: String? { get set }
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + alertPresentationDate + +
      +
      +
      +
      +
      +
      +

      Sets and Gets a UserDefault around the last time the user was presented a version update alert.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      static var alertPresentationDate: Date? { get set }
      + +
      +
      +
      +
      +
    • +
    +
    +
    +
    + +
    +
    + + + diff --git a/docs/docsets/Siren.docset/Contents/Resources/Documents/Extensions/UserDefaults/SirenKeys.html b/docs/docsets/Siren.docset/Contents/Resources/Documents/Extensions/UserDefaults/SirenKeys.html new file mode 100644 index 00000000..6983054f --- /dev/null +++ b/docs/docsets/Siren.docset/Contents/Resources/Documents/Extensions/UserDefaults/SirenKeys.html @@ -0,0 +1,240 @@ + + + + SirenKeys Enumeration Reference + + + + + + + + + + +
    +
    +

    Siren Docs (100% documented)

    +
    +
    +
    + +
    +
    + +
    +
    +
    +

    SirenKeys

    +
    +
    +
    private enum SirenKeys : String
    + +
    +
    +

    Siren-specific UserDefaults Keys

    + +
    +
    +
    +
      +
    • + +
      +
      +
      +
      +
      +

      Key that notifies Siren to perform a version check and present +the Siren alert the next time the user launches the app.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case PerformVersionCheckOnSubsequentLaunch
      + +
      +
      +
      +
      +
    • +
    • + +
      +
      +
      +
      +
      +

      Key that stores the timestamp of the last version check.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case StoredVersionCheckDate
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + StoredSkippedVersion + +
      +
      +
      +
      +
      +
      +

      Key that stores the version that a user decided to skip.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case StoredSkippedVersion
      + +
      +
      +
      +
      +
    • +
    +
    +
    +
    + +
    +
    + + + diff --git a/docs/docsets/Siren.docset/Contents/Resources/Documents/Protocols.html b/docs/docsets/Siren.docset/Contents/Resources/Documents/Protocols.html index ccb40a40..17d6198c 100644 --- a/docs/docsets/Siren.docset/Contents/Resources/Documents/Protocols.html +++ b/docs/docsets/Siren.docset/Contents/Resources/Documents/Protocols.html @@ -33,23 +33,6 @@
  • - - - - - - @@ -135,7 +136,7 @@

    Declaration

    diff --git a/docs/docsets/Siren.docset/Contents/Resources/Documents/Protocols/SirenDelegate.html b/docs/docsets/Siren.docset/Contents/Resources/Documents/Protocols/SirenDelegate.html index edf9991e..59df3c7a 100644 --- a/docs/docsets/Siren.docset/Contents/Resources/Documents/Protocols/SirenDelegate.html +++ b/docs/docsets/Siren.docset/Contents/Resources/Documents/Protocols/SirenDelegate.html @@ -33,23 +33,6 @@ - - - - - - @@ -104,9 +105,9 @@

    SirenDelegate

  • - + - sirenDidDetectNewVersionWithoutAlert(title:message:updateType:) + sirenDidDetectNewVersionWithoutAlert(title:message:updateType:) Default implementation @@ -128,7 +129,7 @@

    Default Implementation

    Declaration

    Swift

    -
    func sirenDidDetectNewVersionWithoutAlert(title: String, message: String, updateType: UpdateType)
    +
    func sirenDidDetectNewVersionWithoutAlert(title: String, message: String, updateType: Constants.UpdateType)
    @@ -138,9 +139,9 @@

    Declaration

  • - + - sirenDidFailVersionCheck(error:) + sirenDidFailVersionCheck(error:) Default implementation @@ -178,9 +179,9 @@

    Declaration

  • - + - sirenDidShowUpdateDialog(alertType:) + sirenDidShowUpdateDialog(alertType:) Default implementation @@ -202,7 +203,7 @@

    Default Implementation

    Declaration

    Swift

    -
    func sirenDidShowUpdateDialog(alertType: Siren.AlertType)
    +
    func sirenDidShowUpdateDialog(alertType: Constants.AlertType)
    @@ -265,9 +266,9 @@

    Declaration

  • - + - sirenNetworkCallDidReturnWithNewVersionInformation(lookupModel:) + sirenNetworkCallDidReturnWithNewVersionInformation(lookupModel:) Default implementation @@ -289,7 +290,7 @@

    Default Implementation

    Declaration

    Swift

    -
    func sirenNetworkCallDidReturnWithNewVersionInformation(lookupModel: SirenLookupModel)
    +
    func sirenNetworkCallDidReturnWithNewVersionInformation(lookupModel: LookupModel)
    @@ -422,7 +423,7 @@

    Declaration

    diff --git a/docs/docsets/Siren.docset/Contents/Resources/Documents/Structs.html b/docs/docsets/Siren.docset/Contents/Resources/Documents/Structs.html index e8b05e98..c0ef5159 100644 --- a/docs/docsets/Siren.docset/Contents/Resources/Documents/Structs.html +++ b/docs/docsets/Siren.docset/Contents/Resources/Documents/Structs.html @@ -34,13 +34,7 @@ Siren
  • - - @@ -48,15 +42,33 @@ Enumerations @@ -64,22 +76,52 @@ Structures @@ -94,20 +136,13 @@

    Structures

    -
    • @@ -115,22 +150,207 @@

      Siren Alert Messaging Customization

      -

      Allows the overriding of all the UIAlertController and UIActionSheet Strings to which Siren defaults.

      -
      -

      Warning

      - Overriding any of these keys will result in the loss of the built-in internationalization that Siren provides. +

      APIManager for Siren

      -
      + See more +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public struct APIManager
      + +
      +
      +
      +
      +
    • +
    +
    +
    +
      +
    • +
      + + + + PresentationManager + +
      +
      +
      +
      +
      +
      +

      PresentationManager for Siren

      + + See more +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public struct PresentationManager
      + +
      +
      +
      +
      +
    • +
    +
    +
    +
      +
    • +
      + + + + RulesManager + +
      +
      +
      +
      +
      +
      +

      RulesManager for Siren

      + + See more +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public struct RulesManager
      -

      As SirenAlertMessaging is a Struct, one or more keys can be modified. Overriding only one string will result in the other keys retaining their default (and internationalizable) values.

      +
      +
      +
      +
      +
    • +
    +
    +
    +
      +
    • +
      + + + + AlertConstants + +
      +
      +
      +
      +
      +
      +

      The default constants used for the update alert’s messaging.

      + + See more +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public struct AlertConstants
      + +
      +
      +
      +
      +
    • +
    +
    +
    +
      +
    • +
      + + + + Localization + +
      +
      +
      +
      +
      +
      +

      Localization information and strings for Siren.

      + + See more +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public struct Localization
      + +
      +
      +
      +
      +
    • +
    +
    +
    +
      +
    • +
      + + + + LookupModel + +
      +
      +
      +
      +
      +
      +

      Model representing a selection of results from the iTunes Lookup API.

      + + See more +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public struct LookupModel : Decodable
      + +
      +
      +
      +
      +
    • +
    +
    +
    +
      +
    • +
      + + + + Results + +
      +
      +
      +
      +
      +
      +

      The relevant metadata returned from Siren upon completing a successful version check.

      - See more + See more

      Declaration

      Swift

      -
      public struct SirenAlertMessaging
      +
      public struct Results
      @@ -140,20 +360,13 @@

      Declaration

    -
    • - - - SirenLookupModel + + + Rules
      @@ -161,15 +374,15 @@

      Model representing a selection of results from the iTun
      -

      MARK: Siren extension used to parse and map the iTunes JSON results into a model represented in Swift.

      +

      Alert Presentation Rules for Siren.

      - See more + See more

      Declaration

      Swift

      -
      public struct SirenLookupModel : Decodable
      +
      public struct Rules
      @@ -179,20 +392,13 @@

      Declaration

    -
    • - - - SirenError + + + DataParser
      @@ -200,15 +406,15 @@

      Siren Error Handling

      -

      Data structure used to build Siren specific Errors.

      +

      Version parsing functions for Siren.

      - See more + See more

      Declaration

      Swift

      -
      public struct SirenError : LocalizedError
      +
      struct DataParser
      @@ -220,7 +426,7 @@

      Declaration

    diff --git a/docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/APIManager.html b/docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/APIManager.html new file mode 100644 index 00000000..57385729 --- /dev/null +++ b/docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/APIManager.html @@ -0,0 +1,494 @@ + + + + APIManager Structure Reference + + + + + + + + + + +
    +
    +

    Siren Docs (100% documented)

    +
    +
    +
    + +
    +
    + +
    +
    +
    +

    APIManager

    +
    +
    +
    public struct APIManager
    + +
    +
    +

    APIManager for Siren

    + +
    +
    +
    +
      +
    • +
      + + + + Constants + +
      +
      +
      +
      +
      +
      +

      Constants used in the APIManager.

      + + See more +
      +
      +

      Declaration

      +
      +

      Swift

      +
      private struct Constants
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + CompletionHandler + +
      +
      +
      +
      +
      +
      +

      Return results or errors obtained from performing a version check with Siren.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      typealias CompletionHandler = (LookupModel?, KnownError?) -> Void
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + countryCode + +
      +
      +
      +
      +
      +
      +

      The region or country of an App Store in which the app is available. +By default, all version check requests are performed against the US App Store. +If the app is not available in the US App Store, set it to the identifier of at least one App Store region within which it is available.

      + +

      List of country codes

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      let countryCode: String?
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + init(countryCode:) + +
      +
      +
      +
      +
      +
      +

      Initializes APIManager to the region or country of an App Store in which the app is available. +By default, all version check requests are performed against the US App Store. +If the app is not available in the US App Store, set it to the identifier of at least one App Store region within which it is available.

      + +

      List of country codes

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public init(countryCode: String? = nil)
      + +
      +
      +
      +

      Parameters

      + + + + + + + +
      + + countryCode + + +
      +

      The country code for the App Store in which the app is availabe. Defaults to nil (e.g., the US App Store)

      +
      +
      +
      +
      +
      +
    • +
    • +
      + + + + default + +
      +
      +
      +
      +
      +
      +

      The default APIManager.

      + +

      The version check is performed against the US App Store.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public static let `default`: APIManager
      + +
      +
      +
      +
      +
    • +
    +
    +
    +
      +
    • + +
      +
      +
      +
      +
      +

      Creates and performs a URLRequest against the iTunes Lookup API.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      func performVersionCheckRequest(completion handler: CompletionHandler?)
      + +
      +
      +
      +

      Parameters

      + + + + + + + +
      + + handler + + +
      +

      The completion handler for the iTunes Lookup API request.

      +
      +
      +
      +
      +
      +
    • +
    • + +
      +
      +
      +
      +
      +

      Parses and maps the the results from the iTunes Lookup API request.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      private func processVersionCheckResults(withData data: Data?,
      +                                        response: URLResponse?,
      +                                        error: Error?,
      +                                        completion handler: CompletionHandler?)
      + +
      +
      +
      +

      Parameters

      + + + + + + + + + + + + + + + + + + + +
      + + data + + +
      +

      The JSON data returned from the request.

      +
      +
      + + response + + +
      +

      The response metadata returned from the request.

      +
      +
      + + error + + +
      +

      The error returned from the request.

      +
      +
      + + handler + + +
      +

      The completion handler to call once the results of the request has been processed.

      +
      +
      +
      +
      +
      +
    • +
    • +
      + + + + makeITunesURL() + +
      +
      +
      +
      +
      +
      +

      Creates the URL that points to the iTunes Lookup API.

      +
      +

      Throws

      + An error if the URL cannot be created. + +
      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      private func makeITunesURL() throws -> URL
      + +
      +
      +
      +

      Return Value

      +

      The iTunes Lookup API URL.

      +
      +
      +
      +
    • +
    +
    +
    +
    + +
    +
    + + + diff --git a/docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/APIManager/Constants.html b/docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/APIManager/Constants.html new file mode 100644 index 00000000..645afd45 --- /dev/null +++ b/docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/APIManager/Constants.html @@ -0,0 +1,212 @@ + + + + Constants Structure Reference + + + + + + + + + + +
    +
    +

    Siren Docs (100% documented)

    +
    +
    +
    + +
    +
    + +
    +
    +
    +

    Constants

    +
    +
    +
    private struct Constants
    + +
    +
    +

    Constants used in the APIManager.

    + +
    +
    +
    +
      +
    • +
      + + + + bundleID + +
      +
      +
      +
      +
      +
      +

      Constant for the bundleId parameter in the iTunes Lookup API request.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      static let bundleID: String
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + country + +
      +
      +
      +
      +
      +
      +

      Constant for the country parameter in the iTunes Lookup API request.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      static let country: String
      + +
      +
      +
      +
      +
    • +
    +
    +
    +
    + +
    +
    + + + diff --git a/docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/AlertConstants.html b/docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/AlertConstants.html new file mode 100644 index 00000000..2717d6dc --- /dev/null +++ b/docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/AlertConstants.html @@ -0,0 +1,293 @@ + + + + AlertConstants Structure Reference + + + + + + + + + + +
    +
    +

    Siren Docs (100% documented)

    +
    +
    +
    + +
    +
    + +
    +
    +
    +

    AlertConstants

    +
    +
    +
    public struct AlertConstants
    + +
    +
    +

    The default constants used for the update alert’s messaging.

    + +
    +
    +
    +
      +
    • +
      + + + + alertMessage + +
      +
      +
      +
      +
      +
      +

      The text that conveys the message that there is an app update available

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public static let alertMessage: String
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + alertTitle + +
      +
      +
      +
      +
      +
      +

      The alert title which defaults to Update Available.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public static let alertTitle: String
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + nextTimeButtonTitle + +
      +
      +
      +
      +
      +
      +

      The button text that conveys the message that the user should be prompted to update next time the app launches.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public static let nextTimeButtonTitle: String
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + skipButtonTitle + +
      +
      +
      +
      +
      +
      +

      The text that conveys the message that the the user wants to skip this verison update.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public static let skipButtonTitle: String
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + updateButtonTitle + +
      +
      +
      +
      +
      +
      +

      The button text that conveys the message that the user would like to update the app right away.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public static let updateButtonTitle: String
      + +
      +
      +
      +
      +
    • +
    +
    +
    +
    + +
    +
    + + + diff --git a/docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/AlertMessaging.html b/docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/AlertMessaging.html new file mode 100644 index 00000000..4984c1c1 --- /dev/null +++ b/docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/AlertMessaging.html @@ -0,0 +1,250 @@ + + + + AlertMessaging Structure Reference + + + + + + + + + + +
    +
    +

    Siren Docs (100% documented)

    +
    +
    +
    + +
    +
    + +
    +
    +
    +

    AlertMessaging

    +
    +
    +
    public struct AlertMessaging
    + +
    +
    +

    Allows the overriding of all the UIAlertController and UIActionSheet Strings to which Siren defaults.

    +
    +

    Warning

    + Overriding any of these keys will result in the loss of the built-in internationalization that Siren provides. + +
    + +

    As SirenAlertMessaging is a Struct, one or more keys can be modified. Overriding only one string will result in the other keys retaining their default (and internationalizable) values.

    + +
    +
    +
    +
      +
    • +
      + + + + Constants + +
      +
      +
      +
      +
      +
      +

      The default constants used for the alert messaging.

      + + See more +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public struct Constants
      + +
      +
      +
      +
      +
    • +
    • + +
      +
      +
      +
      +
      +

      The public initializer

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public init(updateTitle title: NSAttributedString  = Constants.alertTitle,
      +            updateMessage message: NSAttributedString  = Constants.alertMessage,
      +            updateButtonMessage: NSAttributedString  = Constants.updateButtonTitle,
      +            nextTimeButtonMessage: NSAttributedString  = Constants.nextTimeButtonTitle,
      +            skipVersionButtonMessage: NSAttributedString  = Constants.skipButtonTitle)
      + +
      +
      +
      +

      Parameters

      + + + + + + + + + + + + + + + + + + + + + + + +
      + + title + + +
      +

      The title field of the UIAlertController.

      +
      +
      + + message + + +
      +

      The message field of the UIAlertController.

      +
      +
      + + updateButtonMessage + + +
      +

      The title field of the Update Button UIAlertAction.

      +
      +
      + + nextTimeButtonMessage + + +
      +

      The title field of the Next Time Button UIAlertAction.

      +
      +
      + + skipVersionButtonMessage + + +
      +

      The title field of the Skip Button UIAlertAction.

      +
      +
      +
      +
      +
      +
    • +
    +
    +
    +
    + +
    +
    + + + diff --git a/docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/AlertMessaging/Constants.html b/docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/AlertMessaging/Constants.html new file mode 100644 index 00000000..5b9d5c7f --- /dev/null +++ b/docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/AlertMessaging/Constants.html @@ -0,0 +1,252 @@ + + + + Constants Structure Reference + + + + + + + + + + +
    +
    +

    Siren Docs (100% documented)

    +
    +
    +
    + +
    +
    + +
    +
    +
    +

    Constants

    +
    +
    +
    public struct Constants
    + +
    +
    +

    The default constants used for the alert messaging.

    + +
    +
    +
    +
      +
    • +
      + + + + alertMessage + +
      +
      +
      +
      +
      +
      +

      The text that conveys the message that there is an app update available

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public static let alertMessage: NSAttributedString
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + alertTitle + +
      +
      +
      +
      +
      +
      +

      The alert title which defaults to Update Available.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public static let alertTitle: NSAttributedString
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + nextTimeButtonTitle + +
      +
      +
      +
      +
      +
      +

      The button text that conveys the message that the user should be prompted to update next time the app launches.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public static let nextTimeButtonTitle: NSAttributedString
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + skipButtonTitle + +
      +
      +
      +
      +
      +
      +

      The text that conveys the message that the the user wants to skip this verison update.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public static let skipButtonTitle: NSAttributedString
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + updateButtonTitle + +
      +
      +
      +
      +
      +
      +

      The button text that conveys the message that the user would like to update the app right away.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public static let updateButtonTitle: NSAttributedString
      + +
      +
      +
      +
      +
    • +
    +
    +
    +
    + +
    +
    + + + diff --git a/docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/CapturedError.html b/docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/CapturedError.html new file mode 100644 index 00000000..bce35f56 --- /dev/null +++ b/docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/CapturedError.html @@ -0,0 +1,158 @@ + + + + CapturedError Structure Reference + + + + + + + + + + +
    +
    +

    Siren Docs (100% documented)

    +
    +
    +
    + +
    +
    + +
    +
    +
    +

    CapturedError

    +
    +
    +
    public struct CapturedError : LocalizedError
    + +
    +
    +

    Siren extension dealing with Siren-specific errors.

    + +
    +
    +
    +
      +
    • +
      + + + + Known + +
      +
      +
      +
      +
      +
      +

      Enumerates all potentials errors that Siren can handle.

      + +
        +
      • appStoreAppIDFailure: Error retrieving trackId as the JSON does not contain a ‘trackId’ key.
      • +
      • appStoreDataRetrievalFailure: Error retrieving App Store data as an error was returned.
      • +
      • appStoreJSONParsingFailure: Error parsing App Store JSON data.
      • +
      • appStoreDataRetrievalEmptyResults: Error retrieving App Store data as JSON results were empty. Is your app available in the US? If not, change the countryCode variable to fix this error.
      • +
      • appStoreOSVersionNumberFailure: Error retrieving iOS version number as there was no data returned.
      • +
      • appStoreOSVersionUnsupported: The version of iOS on the device is lower than that of the one required by the app verison update.
      • +
      • appStoreVersionArrayFailure: Error retrieving App Store verson number as the JSON does not contain a ‘version’ key.
      • +
      • malformedURL: The iTunes URL is malformed. Please leave an issue on https://github.com/ArtSabintsev/Siren with as many details as possible.
      • +
      • noUpdateAvailable: No new update available.
      • +
      • recentlyCheckedAlready: Not checking the version, because it was already checked recently.
      • +
      + + See more +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public enum Known : Error
      + +
      +
      +
      +
      +
    • +
    +
    +
    +
    + +
    +
    + + + diff --git a/docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/CapturedError/Known.html b/docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/CapturedError/Known.html new file mode 100644 index 00000000..dc1c8aaa --- /dev/null +++ b/docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/CapturedError/Known.html @@ -0,0 +1,400 @@ + + + + Known Enumeration Reference + + + + + + + + + + +
    +
    +

    Siren Docs (100% documented)

    +
    +
    +
    + +
    +
    + +
    +
    +
    +

    Known

    +
    +
    +
    public enum Known : Error
    + +
    +
    +

    Enumerates all potentials errors that Siren can handle.

    + +
      +
    • appStoreAppIDFailure: Error retrieving trackId as the JSON does not contain a ‘trackId’ key.
    • +
    • appStoreDataRetrievalFailure: Error retrieving App Store data as an error was returned.
    • +
    • appStoreJSONParsingFailure: Error parsing App Store JSON data.
    • +
    • appStoreDataRetrievalEmptyResults: Error retrieving App Store data as JSON results were empty. Is your app available in the US? If not, change the countryCode variable to fix this error.
    • +
    • appStoreOSVersionNumberFailure: Error retrieving iOS version number as there was no data returned.
    • +
    • appStoreOSVersionUnsupported: The version of iOS on the device is lower than that of the one required by the app verison update.
    • +
    • appStoreVersionArrayFailure: Error retrieving App Store verson number as the JSON does not contain a ‘version’ key.
    • +
    • malformedURL: The iTunes URL is malformed. Please leave an issue on https://github.com/ArtSabintsev/Siren with as many details as possible.
    • +
    • noUpdateAvailable: No new update available.
    • +
    • recentlyCheckedAlready: Not checking the version, because it was already checked recently.
    • +
    + +
    +
    +
    +
      +
    • +
      + + + + appStoreAppIDFailure + +
      +
      +
      +
      +
      +
      +

      Error retrieving trackId as the JSON does not contain a ‘trackId’ key.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case appStoreAppIDFailure
      + +
      +
      +
      +
      +
    • +
    • + +
      +
      +
      +
      +
      +

      Error retrieving App Store data as an error was returned.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case appStoreDataRetrievalFailure(underlyingError: Error?)
      + +
      +
      +
      +
      +
    • +
    • + +
      +
      +
      +
      +
      +

      Error parsing App Store JSON data.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case appStoreJSONParsingFailure(underlyingError: Error)
      + +
      +
      +
      +
      +
    • +
    • + +
      +
      +
      +
      +
      +

      Error retrieving App Store data as JSON results were empty. Is your app available in the US? If not, change the countryCode variable to fix this error.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case appStoreDataRetrievalEmptyResults
      + +
      +
      +
      +
      +
    • +
    • + +
      +
      +
      +
      +
      +

      Error retrieving iOS version number as there was no data returned.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case appStoreOSVersionNumberFailure
      + +
      +
      +
      +
      +
    • +
    • + +
      +
      +
      +
      +
      +

      The version of iOS on the device is lower than that of the one required by the app verison update.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case appStoreOSVersionUnsupported
      + +
      +
      +
      +
      +
    • +
    • + +
      +
      +
      +
      +
      +

      Error retrieving App Store verson number as the JSON does not contain a ‘version’ key.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case appStoreVersionArrayFailure
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + malformedURL + +
      +
      +
      +
      +
      +
      +

      The iTunes URL is malformed. Please leave an issue on https://github.com/ArtSabintsev/Siren with as many details as possible.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case malformedURL
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + noUpdateAvailable + +
      +
      +
      +
      +
      +
      +

      No new update available.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case noUpdateAvailable
      + +
      +
      +
      +
      +
    • +
    • + +
      +
      +
      +
      +
      +

      Not checking the version, because it was already checked recently.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case recentlyCheckedAlready
      + +
      +
      +
      +
      +
    • +
    +
    +
    +
    + +
    +
    + + + diff --git a/docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/Constants.html b/docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/Constants.html new file mode 100644 index 00000000..d85e5cfa --- /dev/null +++ b/docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/Constants.html @@ -0,0 +1,211 @@ + + + + Constants Structure Reference + + + + + + + + + + +
    +
    +

    Siren Docs (100% documented)

    +
    +
    +
    + +
    +
    + +
    +
    +
    +

    Constants

    +
    +
    +
    public struct Constants
    + +
    +
    +

    Lists all enumerated types that are used to configure the library.

    + +
    +
    +
    +
      +
    • +
      + + + + AlertType + +
      +
      +
      +
      +
      +
      +

      Determines the type of alert to present after a successful version check has been performed.

      + + See more +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public enum AlertType
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + UpdateType + +
      +
      +
      +
      +
      +
      +

      UpdateType defines what kind of update is available. +It is used as a parameter if the user wants to use +a custom alert to inform the user about an update.

      + +
        +
      • major: Major release available: A.b.c.d
      • +
      • minor: Minor release available: a.B.c.d
      • +
      • patch: Patch release available: a.b.C.d
      • +
      • revision: Revision release available: a.b.c.D
      • +
      • unknown: No information available about the update.
      • +
      + + See more +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public enum UpdateType : String
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + VersionCheckFrequency + +
      +
      +
      +
      +
      +
      +

      Determines the frequency in which the the version check is performed and the user is prompted to update the app.

      + + See more +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public enum VersionCheckFrequency : Int
      + +
      +
      +
      +
      +
    • +
    +
    +
    +
    + +
    +
    + + + diff --git a/docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/Constants/AlertType.html b/docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/Constants/AlertType.html new file mode 100644 index 00000000..a24bea17 --- /dev/null +++ b/docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/Constants/AlertType.html @@ -0,0 +1,226 @@ + + + + AlertType Enumeration Reference + + + + + + + + + + +
    +
    +

    Siren Docs (100% documented)

    +
    +
    +
    + +
    +
    + +
    +
    +
    +

    AlertType

    +
    +
    +
    public enum AlertType
    + +
    +
    +

    Determines the type of alert to present after a successful version check has been performed.

    + +
    +
    +
    +
      +
    • +
      + + + + force + +
      +
      +
      +
      +
      +
      +

      Forces user to update your app (1 button alert).

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case force
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + option + +
      +
      +
      +
      +
      +
      +

      (DEFAULT) Presents user with option to update app now or at next launch (2 button alert).

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case option
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + skip + +
      +
      +
      +
      +
      +
      +

      Presents user with option to update the app now, at next launch, or to skip this version all together (3 button alert).

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case skip
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + none + +
      +
      +
      +
      +
      +
      +

      Doesn’t show the alert, but instead returns a localized message +for use in a custom UI within the sirenDidDetectNewVersionWithoutAlert() delegate method.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case none
      + +
      +
      +
      +
      +
    • +
    +
    +
    +
    + +
    +
    + + + diff --git a/docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/Constants/UpdateType.html b/docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/Constants/UpdateType.html new file mode 100644 index 00000000..7633d0ac --- /dev/null +++ b/docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/Constants/UpdateType.html @@ -0,0 +1,262 @@ + + + + UpdateType Enumeration Reference + + + + + + + + + + +
    +
    +

    Siren Docs (100% documented)

    +
    +
    +
    + +
    +
    + +
    +
    +
    +

    UpdateType

    +
    +
    +
    public enum UpdateType : String
    + +
    +
    +

    UpdateType defines what kind of update is available. +It is used as a parameter if the user wants to use +a custom alert to inform the user about an update.

    + +
      +
    • major: Major release available: A.b.c.d
    • +
    • minor: Minor release available: a.B.c.d
    • +
    • patch: Patch release available: a.b.C.d
    • +
    • revision: Revision release available: a.b.c.D
    • +
    • unknown: No information available about the update.
    • +
    + +
    +
    +
    +
      +
    • +
      + + + + major + +
      +
      +
      +
      +
      +
      +

      Major release available: A.b.c.d

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case major
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + minor + +
      +
      +
      +
      +
      +
      +

      Minor release available: a.B.c.d

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case minor
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + patch + +
      +
      +
      +
      +
      +
      +

      Patch release available: a.b.C.d

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case patch
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + revision + +
      +
      +
      +
      +
      +
      +

      Revision release available: a.b.c.D

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case revision
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + unknown + +
      +
      +
      +
      +
      +
      +

      No information available about the update.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case unknown
      + +
      +
      +
      +
      +
    • +
    +
    +
    +
    + +
    +
    + + + diff --git a/docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/Constants/VersionCheckFrequency.html b/docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/Constants/VersionCheckFrequency.html new file mode 100644 index 00000000..c1c179fc --- /dev/null +++ b/docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/Constants/VersionCheckFrequency.html @@ -0,0 +1,198 @@ + + + + VersionCheckFrequency Enumeration Reference + + + + + + + + + + +
    +
    +

    Siren Docs (100% documented)

    +
    +
    +
    + +
    +
    + +
    +
    +
    +

    VersionCheckFrequency

    +
    +
    +
    public enum VersionCheckFrequency : Int
    + +
    +
    +

    Determines the frequency in which the the version check is performed and the user is prompted to update the app.

    + +
    +
    +
    +
      +
    • +
      + + + + immediately + +
      +
      +
      +
      +
      +
      +

      Version check performed every time the app is launched.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case immediately = 0
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + daily + +
      +
      +
      +
      +
      +
      +

      Version check performed once a day.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case daily = 1
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + weekly + +
      +
      +
      +
      +
      +
      +

      Version check performed once a week.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case weekly = 7
      + +
      +
      +
      +
      +
    • +
    +
    +
    +
    + +
    +
    + + + diff --git a/docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/DataParser.html b/docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/DataParser.html new file mode 100644 index 00000000..739b616c --- /dev/null +++ b/docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/DataParser.html @@ -0,0 +1,385 @@ + + + + DataParser Structure Reference + + + + + + + + + + +
    +
    +

    Siren Docs (100% documented)

    +
    +
    +
    + +
    +
    + +
    +
    +
    +

    DataParser

    +
    +
    +
    struct DataParser
    + +
    +
    +

    Version parsing functions for Siren.

    + +
    +
    +
    +
      +
    • + +
      +
      +
      +
      +
      +

      Checks to see if the App Store version of the app is newer than the installed version.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      static func isAppStoreVersionNewer(installedVersion: String?, appStoreVersion: String?) -> Bool
      + +
      +
      +
      +

      Parameters

      + + + + + + + + + + + +
      + + installedVersion + + +
      +

      The installed version of the app.

      +
      +
      + + appStoreVersion + + +
      +

      The App Store version of the app.

      +
      +
      +
      +
      +

      Return Value

      +

      true if the App Store version is newer. Otherwise, false.

      +
      +
      +
      +
    • +
    • + +
      +
      +
      +
      +
      +

      Validates that the latest version in the App Store is compatible with the device’s current version of iOS.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      static func isUpdateCompatibleWithDeviceOS(for model: LookupModel) -> Bool
      + +
      +
      +
      +

      Parameters

      + + + + + + + +
      + + model + + +
      +

      The iTunes Lookup Model.

      +
      +
      +
      +
      +

      Return Value

      +

      true if the latest version is compatible with the device’s current version of iOS. Otherwise, false.

      +
      +
      +
      +
    • +
    • + +
      +
      +
      +
      +
      +

      The type of update that is returned from the API in relation to the verison of the app that is installed.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      static func parseForUpdate(forInstalledVersion installedVersion: String?,
      +                           andAppStoreVersion appStoreVersion: String?) -> RulesManager.UpdateType
      + +
      +
      +
      +

      Parameters

      + + + + + + + + + + + +
      + + installedVersion + + +
      +

      The installed version of the app.

      +
      +
      + + appStoreVersion + + +
      +

      The App Store version of the app.

      +
      +
      +
      +
      +

      Return Value

      +

      The type of update in relation to the verison of the app that is installed.

      +
      +
      +
      +
    • +
    • +
      + + + + split(version:) + +
      +
      +
      +
      +
      +
      +

      Splits a version-formatted String into an[Int]`.

      + +

      Converts "a.b.c.d" into [a, b, c, d].

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      private static func split(version: String) -> [Int]
      + +
      +
      +
      +

      Parameters

      + + + + + + + +
      + + version + + +
      +

      The version formatted String.

      +
      +
      +
      +
      +

      Return Value

      +

      An array of integers representing a version of the app.

      +
      +
      +
      +
    • +
    +
    +
    +
    + +
    +
    + + + diff --git a/docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/Localization.html b/docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/Localization.html new file mode 100644 index 00000000..78a6c884 --- /dev/null +++ b/docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/Localization.html @@ -0,0 +1,458 @@ + + + + Localization Structure Reference + + + + + + + + + + +
    +
    +

    Siren Docs (100% documented)

    +
    +
    +
    + +
    +
    + +
    +
    +
    +

    Localization

    +
    +
    +
    public struct Localization
    + +
    +
    +

    Localization information and strings for Siren.

    + +
    +
    +
    +
      +
    • +
      + + + + Language + +
      +
      +
      +
      +
      +
      +

      Determines the available languages in which the update message and alert button titles should appear.

      + +

      By default, the operating system’s default lanuage setting is used. However, you can force a specific language +by setting the forceLanguageLocalization property before calling checkVersion()

      + + See more +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public enum Language : String
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + appName + +
      +
      +
      +
      +
      +
      +

      The name of the app as defined by the Info.plist.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      private var appName: String
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + forceLanguage + +
      +
      +
      +
      +
      +
      +

      Overrides the default localization of a user’s device when presenting the update message and button titles in the alert.

      + +

      See the Siren.Localization.Language enum for more details.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      private let forceLanguage: Language?
      + +
      +
      +
      +
      +
    • +
    • + +
      +
      +
      +
      +
      +

      Initializes

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      init(appName: String?, andForceLanguageLocalization forceLanguage: Language?)
      + +
      +
      +
      +

      Parameters

      + + + + + + + + + + + +
      + + appName + + +
      +

      Overrides the default name of the app. This is optional and defaults to the app that is defined in the Info.plist.

      +
      +
      + + forceLanguage + + +
      +

      The language the alert to which the alert should be set. If nil, it falls back to the device’s preferred locale.

      +
      +
      +
      +
      +
      +
    • +
    • + +
      +
      +
      +
      +
      +

      The localized string for the UIAlertController‘s message field. .

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public func alertMessage(forCurrentAppStoreVersion currentAppStoreVersion: String) -> String
      + +
      +
      +
      +

      Return Value

      +

      A localized string for the update message.

      +
      +
      +
      +
    • +
    • +
      + + + + alertTitle() + +
      +
      +
      +
      +
      +
      +

      The localized string for the UIAlertController‘s title field. .

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public func alertTitle() -> String
      + +
      +
      +
      +

      Return Value

      +

      A localized string for the phrase Update Available.

      +
      +
      +
      +
    • +
    • +
      + + + + nextTimeButtonTitle() + +
      +
      +
      +
      +
      +
      +

      The localized string for the Next time UIAlertAction.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public func nextTimeButtonTitle() -> String
      + +
      +
      +
      +

      Return Value

      +

      A localized string for the phrase Next time.

      +
      +
      +
      +
    • +
    • +
      + + + + skipButtonTitle() + +
      +
      +
      +
      +
      +
      +

      The localized string for the Skip this version UIAlertAction.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public func skipButtonTitle() -> String
      + +
      +
      +
      +

      Return Value

      +

      A localized string for the phrase Skip this version.

      +
      +
      +
      +
    • +
    • +
      + + + + updateButtonTitle() + +
      +
      +
      +
      +
      +
      +

      The localized string for the Update UIAlertAction.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public func updateButtonTitle() -> String
      + +
      +
      +
      +

      Return Value

      +

      A localized string for the phrase Update.

      +
      +
      +
      +
    • +
    +
    +
    +
    + +
    +
    + + + diff --git a/docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/Localization/Language.html b/docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/Localization/Language.html new file mode 100644 index 00000000..a1e50087 --- /dev/null +++ b/docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/Localization/Language.html @@ -0,0 +1,1295 @@ + + + + Language Enumeration Reference + + + + + + + + + + +
    +
    +

    Siren Docs (100% documented)

    +
    +
    +
    + +
    +
    + +
    +
    +
    +

    Language

    +
    +
    +
    public enum Language : String
    + +
    +
    +

    Determines the available languages in which the update message and alert button titles should appear.

    + +

    By default, the operating system’s default lanuage setting is used. However, you can force a specific language +by setting the forceLanguageLocalization property before calling checkVersion()

    + +
    +
    +
    +
      +
    • +
      + + + + arabic + +
      +
      +
      +
      +
      +
      +

      Arabic Language Localization

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case arabic = "ar"
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + armenian + +
      +
      +
      +
      +
      +
      +

      Armenian Language Localization

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case armenian = "hy"
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + basque + +
      +
      +
      +
      +
      +
      +

      Basque Language Localization

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case basque = "eu"
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + chineseSimplified + +
      +
      +
      +
      +
      +
      +

      Simplified Chinese Language Localization

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case chineseSimplified = "zh-Hans"
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + chineseTraditional + +
      +
      +
      +
      +
      +
      +

      Traditional Chinese Localization Localization

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case chineseTraditional = "zh-Hant"
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + croatian + +
      +
      +
      +
      +
      +
      +

      Croatian Language Localization

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case croatian = "hr"
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + czech + +
      +
      +
      +
      +
      +
      +

      Czech Language Localization

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case czech = "cs"
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + danish + +
      +
      +
      +
      +
      +
      +

      Danish Language Localization

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case danish = "da"
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + dutch + +
      +
      +
      +
      +
      +
      +

      Dutch Language Localization

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case dutch = "nl"
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + english + +
      +
      +
      +
      +
      +
      +

      English Language Localization

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case english = "en"
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + estonian + +
      +
      +
      +
      +
      +
      +

      Estonian Language Localization

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case estonian = "et"
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + finnish + +
      +
      +
      +
      +
      +
      +

      Finnish Language Localization

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case finnish = "fi"
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + french + +
      +
      +
      +
      +
      +
      +

      French Language Localization

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case french = "fr"
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + german + +
      +
      +
      +
      +
      +
      +

      German Language Localization

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case german = "de"
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + greek + +
      +
      +
      +
      +
      +
      +

      Greek Language Localization

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case greek = "el"
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + hebrew + +
      +
      +
      +
      +
      +
      +

      Hebrew Language Localization

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case hebrew = "he"
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + hungarian + +
      +
      +
      +
      +
      +
      +

      Hungarian Language Localization

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case hungarian = "hu"
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + indonesian + +
      +
      +
      +
      +
      +
      +

      Indonesian Language Localization

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case indonesian = "id"
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + italian + +
      +
      +
      +
      +
      +
      +

      Italian Language Localization

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case italian = "it"
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + japanese + +
      +
      +
      +
      +
      +
      +

      Japanese Language Localization

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case japanese = "ja"
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + korean + +
      +
      +
      +
      +
      +
      +

      Korean Language Localization

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case korean = "ko"
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + latvian + +
      +
      +
      +
      +
      +
      +

      Latvian Language Localization

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case latvian = "lv"
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + lithuanian + +
      +
      +
      +
      +
      +
      +

      Lithuanian Language Localization

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case lithuanian = "lt"
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + malay + +
      +
      +
      +
      +
      +
      +

      Malay Language Localization

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case malay = "ms"
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + norwegian + +
      +
      +
      +
      +
      +
      +

      Norwegian Language Localization

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case norwegian = "nb-NO"
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + persian + +
      +
      +
      +
      +
      +
      +

      Persian Language Localization

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case persian = "fa"
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + persianAfghanistan + +
      +
      +
      +
      +
      +
      +

      Persian (Afghanistan) Language Localization

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case persianAfghanistan = "fa-AF"
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + persianIran + +
      +
      +
      +
      +
      +
      +

      Persian (Iran) Language Localization

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case persianIran = "fa-IR"
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + polish + +
      +
      +
      +
      +
      +
      +

      Polish Language Localization

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case polish = "pl"
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + portugueseBrazil + +
      +
      +
      +
      +
      +
      +

      Brazilian Portuguese Language Localization

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case portugueseBrazil = "pt"
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + portuguesePortugal + +
      +
      +
      +
      +
      +
      +

      Portugal’s Portuguese Language Localization

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case portuguesePortugal = "pt-PT"
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + russian + +
      +
      +
      +
      +
      +
      +

      Russian Language Localization

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case russian = "ru"
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + serbianCyrillic + +
      +
      +
      +
      +
      +
      +

      Serbian (Cyrillic) Language Localization

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case serbianCyrillic = "sr-Cyrl"
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + serbianLatin + +
      +
      +
      +
      +
      +
      +

      Serbian (Latin) Language Localization

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case serbianLatin = "sr-Latn"
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + slovenian + +
      +
      +
      +
      +
      +
      +

      Slovenian Language Localization

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case slovenian = "sl"
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + spanish + +
      +
      +
      +
      +
      +
      +

      Spanish Language Localization

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case spanish = "es"
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + swedish + +
      +
      +
      +
      +
      +
      +

      Swedish Language Localization

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case swedish = "sv"
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + thai + +
      +
      +
      +
      +
      +
      +

      Thai Language Localization

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case thai = "th"
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + turkish + +
      +
      +
      +
      +
      +
      +

      Turkish Language Localization

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case turkish = "tr"
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + urdu + +
      +
      +
      +
      +
      +
      +

      Urdu Language Localization

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case urdu = "ur"
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + ukrainian + +
      +
      +
      +
      +
      +
      +

      Ukranian Language Localization

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case ukrainian = "uk"
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + vietnamese + +
      +
      +
      +
      +
      +
      +

      Vietnamese Language Localization

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case vietnamese = "vi"
      + +
      +
      +
      +
      +
    • +
    +
    +
    +
    + +
    +
    + + + diff --git a/docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/LookupModel.html b/docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/LookupModel.html new file mode 100644 index 00000000..ad08f39d --- /dev/null +++ b/docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/LookupModel.html @@ -0,0 +1,241 @@ + + + + LookupModel Structure Reference + + + + + + + + + + +
    +
    +

    Siren Docs (100% documented)

    +
    +
    +
    + +
    +
    + +
    +
    +
    +

    LookupModel

    +
    +
    +
    public struct LookupModel : Decodable
    + +
    +
    +

    Model representing a selection of results from the iTunes Lookup API.

    + +
    +
    +
    +
      +
    • +
      + + + + CodingKeys + +
      +
      +
      +
      +
      +
      +

      Codable Coding Keys for the Top-Level iTunes Lookup API JSON response.

      + + See more +
      +
      +

      Declaration

      +
      +

      Swift

      +
      private enum CodingKeys : String, CodingKey
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + results + +
      +
      +
      +
      +
      +
      +

      The array of results objects from the iTunes Lookup API.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public let results: [Results]
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + Results + +
      +
      +
      +
      +
      +
      +

      The Results object from the the iTunes Lookup API.

      + + See more +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public struct Results : Decodable
      + +
      +
      +
      +
      +
    • +
    +
    +
    +
    + +
    +
    + + + diff --git a/docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/LookupModel/CodingKeys.html b/docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/LookupModel/CodingKeys.html new file mode 100644 index 00000000..8bae8fe4 --- /dev/null +++ b/docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/LookupModel/CodingKeys.html @@ -0,0 +1,185 @@ + + + + CodingKeys Enumeration Reference + + + + + + + + + + +
    +
    +

    Siren Docs (100% documented)

    +
    +
    +
    + +
    +
    + +
    +
    +
    +

    CodingKeys

    +
    +
    +
    private enum CodingKeys : String, CodingKey
    + +
    +
    +

    Codable Coding Keys for the Top-Level iTunes Lookup API JSON response.

    + +
    +
    +
    +
      +
    • +
      + + + + results + +
      +
      +
      +
      +
      +
      +

      The results JSON key.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case results
      + +
      +
      +
      +
      +
    • +
    +
    +
    +
    + +
    +
    + + + diff --git a/docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/LookupModel/Results.html b/docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/LookupModel/Results.html new file mode 100644 index 00000000..6158798d --- /dev/null +++ b/docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/LookupModel/Results.html @@ -0,0 +1,321 @@ + + + + Results Structure Reference + + + + + + + + + + +
    +
    +

    Siren Docs (100% documented)

    +
    +
    +
    + +
    +
    + +
    +
    +
    +

    Results

    +
    +
    +
    public struct Results : Decodable
    + +
    +
    +

    The Results object from the the iTunes Lookup API.

    + +
    +
    +
    +
      +
    • +
      + + + + CodingKeys + +
      +
      +
      +
      +
      +
      +

      Codable Coding Keys for the Results array in the iTunes Lookup API JSON response.

      + + See more +
      +
      +

      Declaration

      +
      +

      Swift

      +
      private enum CodingKeys : String, CodingKey
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + appID + +
      +
      +
      +
      +
      +
      +

      The app’s App ID.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public let appID: Int
      + +
      +
      +
      +
      +
    • +
    • + +
      +
      +
      +
      +
      +

      The release date for the latest verison of the app.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public let currentVersionReleaseDate: String
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + minimumOSVersion + +
      +
      +
      +
      +
      +
      +

      The minimum verison of iOS that the current verison of the app requires.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public let minimumOSVersion: String
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + releaseNotes + +
      +
      +
      +
      +
      +
      +

      The releases notes from the latest version of the app.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public let releaseNotes: String?
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + version + +
      +
      +
      +
      +
      +
      +

      The latest version of the app.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public let version: String
      + +
      +
      +
      +
      +
    • +
    +
    +
    +
    + +
    +
    + + + diff --git a/docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/LookupModel/Results/CodingKeys.html b/docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/LookupModel/Results/CodingKeys.html new file mode 100644 index 00000000..0560df9b --- /dev/null +++ b/docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/LookupModel/Results/CodingKeys.html @@ -0,0 +1,293 @@ + + + + CodingKeys Enumeration Reference + + + + + + + + + + +
    +
    +

    Siren Docs (100% documented)

    +
    +
    +
    + +
    +
    + +
    +
    +
    +

    CodingKeys

    +
    +
    +
    private enum CodingKeys : String, CodingKey
    + +
    +
    +

    Codable Coding Keys for the Results array in the iTunes Lookup API JSON response.

    + +
    +
    +
    +
      +
    • +
      + + + + appID + +
      +
      +
      +
      +
      +
      +

      The appID JSON key.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case appID = "trackId"
      + +
      +
      +
      +
      +
    • +
    • + +
      +
      +
      +
      +
      +

      The current version release date JSON key.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case currentVersionReleaseDate
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + minimumOSVersion + +
      +
      +
      +
      +
      +
      +

      The minimum device iOS version compatibility JSON key.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case minimumOSVersion = "minimumOsVersion"
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + releaseNotes + +
      +
      +
      +
      +
      +
      +

      The release notes JSON key.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case releaseNotes
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + version + +
      +
      +
      +
      +
      +
      +

      The current App Store version JSON key.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case version
      + +
      +
      +
      +
      +
    • +
    +
    +
    +
    + +
    +
    + + + diff --git a/docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/PresentationManager.html b/docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/PresentationManager.html new file mode 100644 index 00000000..36ed26af --- /dev/null +++ b/docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/PresentationManager.html @@ -0,0 +1,838 @@ + + + + PresentationManager Structure Reference + + + + + + + + + + +
    +
    +

    Siren Docs (100% documented)

    +
    +
    +
    + +
    +
    + +
    +
    +
    +

    PresentationManager

    +
    +
    +
    public struct PresentationManager
    + +
    +
    +

    PresentationManager for Siren

    + +
    +
    +
    +
      +
    • +
      + + + + CompletionHandler + +
      +
      +
      +
      +
      +
      +

      Return results or errors obtained from performing a version check with Siren.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      typealias CompletionHandler = (AlertAction) -> Void
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + localization + +
      +
      +
      +
      +
      +
      +

      The localization data structure that will be used to construct localized strings for the update alert.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      let localization: Localization
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + tintColor + +
      +
      +
      +
      +
      +
      +

      The tint color of the UIAlertController buttons.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      let tintColor: UIColor?
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + alertMessage + +
      +
      +
      +
      +
      +
      +

      The descriptive update message of the UIAlertController.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      let alertMessage: String
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + alertTitle + +
      +
      +
      +
      +
      +
      +

      The main message of the UIAlertController.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      let alertTitle: String
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + nextTimeButtonTitle + +
      +
      +
      +
      +
      +
      +

      The Next time button text of the UIAlertController.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      let nextTimeButtonTitle: String
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + skipButtonTitle + +
      +
      +
      +
      +
      +
      +

      The Skip this version button text of the UIAlertController.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      let skipButtonTitle: String
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + updateButtonTitle + +
      +
      +
      +
      +
      +
      +

      The Update button text of the UIAlertController.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      let updateButtonTitle: String
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + alertController + +
      +
      +
      +
      +
      +
      +

      The instance of the UIAlertController used to present the update alert.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      private var alertController: UIAlertController?
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + updaterWindow + +
      +
      +
      +
      +
      +
      +

      The UIWindow instance that presents the SirenViewController.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      private var updaterWindow: UIWindow { get }
      + +
      +
      +
      +
      +
    • +
    • + +
      +
      +
      +
      +
      +

      PresentationManager‘s public initializer.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public init(alertTintColor tintColor: UIColor? = nil,
      +            appName: String? = nil,
      +            alertTitle: String  = AlertConstants.alertTitle,
      +            alertMessage: String  = AlertConstants.alertMessage,
      +            updateButtonTitle: String  = AlertConstants.updateButtonTitle,
      +            nextTimeButtonTitle: String  = AlertConstants.nextTimeButtonTitle,
      +            skipButtonTitle: String  = AlertConstants.skipButtonTitle,
      +            forceLanguageLocalization forceLanguage: Localization.Language? = nil)
      + +
      +
      +
      +

      Parameters

      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      + + tintColor + + +
      +

      The alert’s tintColor. Settings this to nil defaults to the system default color.

      +
      +
      + + appName + + +
      +

      The name of the app (overrides the default/bundled name).

      +
      +
      + + alertTitle + + +
      +

      The title field of the UIAlertController.

      +
      +
      + + alertMessage + + +
      +

      The message field of the UIAlertController.

      +
      +
      + + nextTimeButtonTitle + + +
      +

      The title field of the Next Time Button UIAlertAction.

      +
      +
      + + skipButtonTitle + + +
      +

      The title field of the Skip Button UIAlertAction.

      +
      +
      + + updateButtonTitle + + +
      +

      The title field of the Update Button UIAlertAction.

      +
      +
      + + forceLanguage + + +
      +

      The language the alert to which the alert should be set. If nil, it falls back to the device’s preferred locale.

      +
      +
      +
      +
      +
      +
    • +
    • +
      + + + + default + +
      +
      +
      +
      +
      +
      +

      The default PresentationManager.

      + +

      By default:

      + +
        +
      • There is no tint color (defaults to Apple’s system blue color.)
      • +
      • The name of the app is equal to the name that appears in Info.plist.
      • +
      • The strings are all set to that of the user’s device localization (if supported) or it falls back to English.
      • +
      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public static let `default`: PresentationManager
      + +
      +
      +
      +
      +
    • +
    +
    +
    +
      +
    • + +
      +
      +
      +
      +
      +

      Constructs the localized update alert UIAlertController object.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      mutating func presentAlert(withRules rules: Rules,
      +                           forCurrentAppStoreVersion currentAppStoreVersion: String,
      +                           completion handler: CompletionHandler?)
      + +
      +
      +
      +

      Parameters

      + + + + + + + + + + + + + + + +
      + + rules + + +
      +

      The rules that are used to define the type of alert that should be presented.

      +
      +
      + + currentAppStoreVersion + + +
      +

      The current version of the app in the App Store.

      +
      +
      + + handler + + +
      +

      The completion handler that returns the an AlertAction depending on the type of action the end-user took.

      +
      +
      +
      +
      +
      +
    • +
    • + +
      +
      +
      +
      +
      +

      The UIAlertAction that is executed when the Update option is selected.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      private func updateAlertAction(completion handler: CompletionHandler?) -> UIAlertAction
      + +
      +
      +
      +

      Parameters

      + + + + + + + +
      + + handler + + +
      +

      The completion handler that returns the .update option.

      +
      +
      +
      +
      +

      Return Value

      +

      The Update alert action.

      +
      +
      +
      +
    • +
    • + +
      +
      +
      +
      +
      +

      The UIAlertAction that is executed when the Next time option is selected.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      private func nextTimeAlertAction(completion handler: CompletionHandler?) -> UIAlertAction
      + +
      +
      +
      +

      Parameters

      + + + + + + + +
      + + handler + + +
      +

      The completion handler that returns the .nextTime option.

      +
      +
      +
      +
      +

      Return Value

      +

      The Next time alert action.

      +
      +
      +
      +
    • +
    • + +
      +
      +
      +
      +
      +

      The UIAlertAction that is executed when the Skip this version option is selected.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      private func skipAlertAction(forCurrentAppStoreVersion currentAppStoreVersion: String, completion handler: CompletionHandler?) -> UIAlertAction
      + +
      +
      +
      +

      Parameters

      + + + + + + + + + + + +
      + + currentAppStoreVersion + + +
      +

      The current version of the app in the App Store.

      +
      +
      + + handler + + +
      +

      The completion handler that returns the .skip option.

      +
      +
      +
      +
      +

      Return Value

      +

      The Skip this version alert action.

      +
      +
      +
      +
    • +
    +
    +
    +
    + +
    +
    + + + diff --git a/docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/Results.html b/docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/Results.html new file mode 100644 index 00000000..b2deed5f --- /dev/null +++ b/docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/Results.html @@ -0,0 +1,267 @@ + + + + Results Structure Reference + + + + + + + + + + +
    +
    +

    Siren Docs (100% documented)

    +
    +
    +
    + +
    +
    + +
    +
    +
    +

    Results

    +
    +
    +
    public struct Results
    + +
    +
    +

    The relevant metadata returned from Siren upon completing a successful version check.

    + +
    +
    +
    +
      +
    • +
      + + + + alertAction + +
      +
      +
      +
      +
      +
      +

      The UIAlertAction the user chose upon being presented with the update alert. +Defaults to unknown until an alert is actually presented.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public var alertAction: AlertAction
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + localization + +
      +
      +
      +
      +
      +
      +

      The Siren-supported locale that was used for the string in the update alert.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public var localization: Localization
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + lookupModel + +
      +
      +
      +
      +
      +
      +

      The Swift-mapped API model, if a successful version check was performed.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public var lookupModel: LookupModel
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + updateType + +
      +
      +
      +
      +
      +
      +

      The type of update that was returned for the API.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public var updateType: RulesManager.UpdateType
      + +
      +
      +
      +
      +
    • +
    +
    +
    +
    + +
    +
    + + + diff --git a/docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/Rules.html b/docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/Rules.html new file mode 100644 index 00000000..8d6d2ae2 --- /dev/null +++ b/docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/Rules.html @@ -0,0 +1,472 @@ + + + + Rules Structure Reference + + + + + + + + + + +
    +
    +

    Siren Docs (100% documented)

    +
    +
    +
    + +
    +
    + +
    +
    +
    +

    Rules

    +
    +
    +
    public struct Rules
    + +
    +
    +

    Alert Presentation Rules for Siren.

    + +
    +
    +
    +
      +
    • +
      + + + + alertType + +
      +
      +
      +
      +
      +
      +

      The type of alert that should be presented.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      let alertType: AlertType
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + frequency + +
      +
      +
      +
      +
      +
      +

      The frequency in which a the user is prompted to update the app +once a new version is available in the App Store and if they have not updated yet.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      let frequency: UpdatePromptFrequency
      + +
      +
      +
      +
      +
    • +
    • + +
      +
      +
      +
      +
      +

      Initializes the alert presentation rules.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public init(promptFrequency frequency: UpdatePromptFrequency,
      +            forAlertType alertType: AlertType)
      + +
      +
      +
      +

      Parameters

      + + + + + + + + + + + +
      + + frequency + + +
      +

      How often a user should be prompted to update the app once a new version is available in the App Store.

      +
      +
      + + alertType + + +
      +

      The type of alert that should be presented.

      +
      +
      +
      +
      +
      +
    • +
    • +
      + + + + annoying + +
      +
      +
      +
      +
      +
      +

      Performs a version check immediately, but allows the user to skip updating the app until the next time the app becomes active.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public static var annoying: Rules { get }
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + critical + +
      +
      +
      +
      +
      +
      +

      Performs a version check immediately and forces the user to update the app.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public static var critical: Rules { get }
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + default + +
      +
      +
      +
      +
      +
      +

      Performs a version check once a day, but allows the user to skip updating the app until +the next time the app becomes active or skipping the update all together until another version is released.

      + +

      This is the default setting.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public static var `default`: Rules { get }
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + persistent + +
      +
      +
      +
      +
      +
      +

      Performs a version check daily, but allows the user to skip updating the app until the next time the app becomes active.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public static var persistent: Rules { get }
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + relaxed + +
      +
      +
      +
      +
      +
      +

      Performs a version check weekly, but allows the user to skip updating the app until +the next time the app becomes active or skipping the update all together until another version is released.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public static var relaxed: Rules { get }
      + +
      +
      +
      +
      +
    • +
    +
    +
    +
      +
    • +
      + + + + AlertType + +
      +
      +
      +
      +
      +
      +

      Determines the type of alert to present after a successful version check has been performed.

      + + See more +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public enum AlertType
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + UpdatePromptFrequency + +
      +
      +
      +
      +
      +
      +

      Determines the frequency in which the user is prompted to update the app +once a new version is available in the App Store and if they have not updated yet.

      + + See more +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public enum UpdatePromptFrequency : UInt
      + +
      +
      +
      +
      +
    • +
    +
    +
    +
    + +
    +
    + + + diff --git a/docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/Rules/AlertType.html b/docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/Rules/AlertType.html new file mode 100644 index 00000000..a339ed4b --- /dev/null +++ b/docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/Rules/AlertType.html @@ -0,0 +1,267 @@ + + + + AlertType Enumeration Reference + + + + + + + + + + +
    +
    +

    Siren Docs (100% documented)

    +
    +
    +
    + +
    +
    + +
    +
    +
    +

    AlertType

    +
    +
    +
    public enum AlertType
    + +
    +
    +

    Determines the type of alert to present after a successful version check has been performed.

    + +
    +
    +
    +
      +
    • +
      + + + + force + +
      +
      +
      +
      +
      +
      +

      Forces the user to update your app (1 button alert).

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case force
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + option + +
      +
      +
      +
      +
      +
      +

      Presents the user with option to update app now or at next launch (2 button alert).

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case option
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + skip + +
      +
      +
      +
      +
      +
      +

      Presents the user with option to update the app now, at next launch, or to skip this version all together (3 button alert).

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case skip
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + none + +
      +
      +
      +
      +
      +
      +

      Doesn’t present the alert. +Use this option if you would like to present a custom alert to the end-user.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case none
      + +
      +
      +
      +
      +
    • +
    +
    +
    +
    + +
    +
    + + + diff --git a/docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/Rules/UpdatePrompFrequency.html b/docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/Rules/UpdatePrompFrequency.html new file mode 100644 index 00000000..85e98ed6 --- /dev/null +++ b/docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/Rules/UpdatePrompFrequency.html @@ -0,0 +1,237 @@ + + + + UpdatePrompFrequency Enumeration Reference + + + + + + + + + + +
    +
    +

    Siren Docs (66% documented)

    +
    +
    +
    + +
    +
    + +
    +
    +
    +

    UpdatePrompFrequency

    +
    +
    +
    public enum UpdatePrompFrequency : UInt
    + +
    +
    +

    Determines the frequency in which the user is prompted to update the app +once a new version is available in the App Store and if they have not updated yet.

    + +
    +
    +
    +
      +
    • +
      + + + + immediately + +
      +
      +
      +
      +
      +
      +

      Version check performed every time the app is launched.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case immediately = 0
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + daily + +
      +
      +
      +
      +
      +
      +

      Version check performed once a day.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case daily = 1
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + weekly + +
      +
      +
      +
      +
      +
      +

      Version check performed once a week.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case weekly = 7
      + +
      +
      +
      +
      +
    • +
    +
    +
    +
    + +
    +
    + + + diff --git a/docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/Rules/UpdatePromptFrequency.html b/docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/Rules/UpdatePromptFrequency.html new file mode 100644 index 00000000..d04dfa75 --- /dev/null +++ b/docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/Rules/UpdatePromptFrequency.html @@ -0,0 +1,240 @@ + + + + UpdatePromptFrequency Enumeration Reference + + + + + + + + + + +
    +
    +

    Siren Docs (100% documented)

    +
    +
    +
    + +
    +
    + +
    +
    +
    +

    UpdatePromptFrequency

    +
    +
    +
    public enum UpdatePromptFrequency : UInt
    + +
    +
    +

    Determines the frequency in which the user is prompted to update the app +once a new version is available in the App Store and if they have not updated yet.

    + +
    +
    +
    +
      +
    • +
      + + + + immediately + +
      +
      +
      +
      +
      +
      +

      Version check performed every time the app is launched.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case immediately = 0
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + daily + +
      +
      +
      +
      +
      +
      +

      Version check performed once a day.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case daily = 1
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + weekly + +
      +
      +
      +
      +
      +
      +

      Version check performed once a week.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case weekly = 7
      + +
      +
      +
      +
      +
    • +
    +
    +
    +
    + +
    +
    + + + diff --git a/docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/RulesManager.html b/docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/RulesManager.html new file mode 100644 index 00000000..b9b2be68 --- /dev/null +++ b/docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/RulesManager.html @@ -0,0 +1,570 @@ + + + + RulesManager Structure Reference + + + + + + + + + + +
    +
    +

    Siren Docs (100% documented)

    +
    +
    +
    + +
    +
    + +
    +
    +
    +

    RulesManager

    +
    +
    +
    public struct RulesManager
    + +
    +
    +

    RulesManager for Siren

    + +
    +
    +
    +
      +
    • +
      + + + + releasedForDays + +
      +
      +
      +
      +
      +
      +

      The alert will only show up if the current version has already been released for X days.

      + +

      This value defaults to 1 day (in RulesManager‘s initializer) to avoid an issue where +Apple updates the JSON faster than the app binary propogates to the App Store.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      let releasedForDays: Int
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + majorUpdateRules + +
      +
      +
      +
      +
      +
      +

      The Rules that should be used when the App Store version of the app signifies that it is a major version update (A.b.c.d).

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      var majorUpdateRules: Rules
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + minorUpdateRules + +
      +
      +
      +
      +
      +
      +

      The Rules that should be used when the App Store version of the app signifies that it is a minor version update (a.B.c.d).

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      var minorUpdateRules: Rules
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + patchUpdateRules + +
      +
      +
      +
      +
      +
      +

      The Rules that should be used when the App Store version of the app signifies that it is a patch version update (a.b.C.d).

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      var patchUpdateRules: Rules
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + revisionUpdateRules + +
      +
      +
      +
      +
      +
      +

      The Rules that should be used when the App Store version of the app signifies that it is a revision version update (a.b.c.D).

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      var revisionUpdateRules: Rules
      + +
      +
      +
      +
      +
    • +
    • + +
      +
      +
      +
      +
      +

      Initializer that sets update-specific Rules for all updates (e.g., major, minor, patch, revision). +This means that each of the four update types can have their own specific update rules.

      + +

      By default, the releasedForDays parameter delays the update alert from being presented for 1 day +to avoid an issue where the iTunes Lookup API response is updated faster than the time it takes for the binary +to become available on App Store CDNs across all regions. Usually it takes 6-24 hours, hence the 1 day delay.

      +
      +

      Warning

      +

      Setting releasedForDays to 0 days causes the alert to appear right away, even if the binary isn’t available. +If this value is set to 0 days, and an AlertType of type .force is set, it will cause your app to infinitely send the +end-user to the App Store to download a version that’s not there and lock them out of your application until the binary is +is available to be downloaded.

      + +
      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public init(majorUpdateRules: Rules = .default,
      +            minorUpdateRules: Rules = .default,
      +            patchUpdateRules: Rules = .default,
      +            revisionUpdateRules: Rules = .default,
      +            showAlertAfterCurrentVersionHasBeenReleasedForDays releasedForDays: Int = 1)
      + +
      +
      +
      +

      Parameters

      + + + + + + + + + + + +
      + + rules + + +
      +

      The rules that should be set for all version updates.

      +
      +
      + + releasedForDays + + +
      +

      The amount of time (in days) that the app should delay before presenting the user

      +
      +
      +
      +
      +
      +
    • +
    • + +
      +
      +
      +
      +
      +

      Initializer that sets the same update Rules for all types of updates (e.g., major, minor, patch, revision). +This means that all four update types will use the same presentation rules.

      + +

      By default, the releasedForDays parameter delays the update alert from being presented for 1 day +to avoid an issue where the iTunes Lookup API response is updated faster than the time it takes for the binary +to become available on App Store CDNs across all regions. Usually it takes 6-24 hours, hence the 1 day delay.

      +
      +

      Warning

      +

      Setting releasedForDays to 0 days causes the alert to appear right away, even if the binary isn’t available. +If this value is set to 0 days, and an AlertType of type .force is set, it will cause your app to infinitely send the +end-user to the App Store to download a version that’s not there and lock them out of your application until the binary is +is available to be downloaded.

      + +
      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public init(globalRules rules: Rules = .default,
      +            showAlertAfterCurrentVersionHasBeenReleasedForDays releasedForDays: Int = 1)
      + +
      +
      +
      +

      Parameters

      + + + + + + + + + + + +
      + + rules + + +
      +

      The rules that should be set for all version updates.

      +
      +
      + + releasedForDays + + +
      +

      The amount of time (in days) that the app should delay before presenting the user

      +
      +
      +
      +
      +
      +
    • +
    • + +
      +
      +
      +
      +
      +

      Returns the appropriate update rules based on the type of version that is returned from the API.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      func loadRulesForUpdateType(_ type: UpdateType) -> Rules
      + +
      +
      +
      +

      Parameters

      + + + + + + + +
      + + type + + +
      +

      The type of app update.

      +
      +
      +
      +
      +

      Return Value

      +

      The appropriate rule based on the type of app update that is returned by the API.

      +
      +
      +
      +
    • +
    • +
      + + + + default + +
      +
      +
      +
      +
      +
      +

      The default RulesManager.

      + +

      By default, the Rules.default rule is used for all update typs.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public static let `default`: RulesManager
      + +
      +
      +
      +
      +
    • +
    +
    +
    + +
      +
    • +
      + + + + UpdateType + +
      +
      +
      +
      +
      +
      +

      Informs Siren of the type of update that is available so that +the appropriate ruleset is used to present the update alert.

      + +
        +
      • major: Major release available: A.b.c.d
      • +
      • minor: Minor release available: a.B.c.d
      • +
      • patch: Patch release available: a.b.C.d
      • +
      • revision: Revision release available: a.b.c.D
      • +
      • unknown: No information available about the update.
      • +
      + + See more +
      +
      +

      Declaration

      +
      +

      Swift

      +
      public enum UpdateType : String
      + +
      +
      +
      +
      +
    • +
    +
    +
    +
    + +
    +
    + + + diff --git a/docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/RulesManager/UpdateType.html b/docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/RulesManager/UpdateType.html new file mode 100644 index 00000000..c061dcb7 --- /dev/null +++ b/docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/RulesManager/UpdateType.html @@ -0,0 +1,302 @@ + + + + UpdateType Enumeration Reference + + + + + + + + + + +
    +
    +

    Siren Docs (100% documented)

    +
    +
    +
    + +
    +
    + +
    +
    +
    +

    UpdateType

    +
    +
    +
    public enum UpdateType : String
    + +
    +
    +

    Informs Siren of the type of update that is available so that +the appropriate ruleset is used to present the update alert.

    + +
      +
    • major: Major release available: A.b.c.d
    • +
    • minor: Minor release available: a.B.c.d
    • +
    • patch: Patch release available: a.b.C.d
    • +
    • revision: Revision release available: a.b.c.D
    • +
    • unknown: No information available about the update.
    • +
    + +
    +
    +
    +
      +
    • +
      + + + + major + +
      +
      +
      +
      +
      +
      +

      Major release available: A.b.c.d

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case major
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + minor + +
      +
      +
      +
      +
      +
      +

      Minor release available: a.B.c.d

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case minor
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + patch + +
      +
      +
      +
      +
      +
      +

      Patch release available: a.b.C.d

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case patch
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + revision + +
      +
      +
      +
      +
      +
      +

      Revision release available: a.b.c.D

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case revision
      + +
      +
      +
      +
      +
    • +
    • +
      + + + + unknown + +
      +
      +
      +
      +
      +
      +

      No information available about the update.

      + +
      +
      +

      Declaration

      +
      +

      Swift

      +
      case unknown
      + +
      +
      +
      +
      +
    • +
    +
    +
    +
    + +
    +
    + + + diff --git a/docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/SirenAlertMessaging.html b/docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/SirenAlertMessaging.html index 8afae6bf..912a86f1 100644 --- a/docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/SirenAlertMessaging.html +++ b/docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/SirenAlertMessaging.html @@ -139,9 +139,9 @@

    Declaration

  • @@ -239,7 +239,7 @@

    Parameters

    diff --git a/docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/SirenAlertMessaging/Constants.html b/docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/SirenAlertMessaging/Constants.html index 9aa14fd1..a0c6d8aa 100644 --- a/docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/SirenAlertMessaging/Constants.html +++ b/docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/SirenAlertMessaging/Constants.html @@ -241,7 +241,7 @@

    Declaration

    diff --git a/docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/SirenError.html b/docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/SirenError.html index 22344b0a..72b4ead7 100644 --- a/docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/SirenError.html +++ b/docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/SirenError.html @@ -147,7 +147,7 @@

    Declaration

    diff --git a/docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/SirenError/Known.html b/docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/SirenError/Known.html index b318c9d3..89f03cbc 100644 --- a/docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/SirenError/Known.html +++ b/docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/SirenError/Known.html @@ -117,9 +117,9 @@

    Known

  • @@ -144,9 +144,9 @@

    Declaration

  • @@ -171,9 +171,9 @@

    Declaration

  • @@ -198,9 +198,9 @@

    Declaration

  • @@ -225,9 +225,9 @@

    Declaration

  • @@ -252,9 +252,9 @@

    Declaration

  • @@ -279,9 +279,9 @@

    Declaration

  • @@ -306,9 +306,9 @@

    Declaration

  • @@ -333,9 +333,9 @@

    Declaration

  • @@ -360,9 +360,9 @@

    Declaration

  • @@ -389,7 +389,7 @@

    Declaration

    diff --git a/docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/SirenLookupModel.html b/docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/SirenLookupModel.html index 16e627fd..4be7a899 100644 --- a/docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/SirenLookupModel.html +++ b/docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/SirenLookupModel.html @@ -161,7 +161,7 @@

    Declaration

    diff --git a/docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/SirenLookupModel/Results.html b/docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/SirenLookupModel/Results.html index fb1d7aa5..245d7b7a 100644 --- a/docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/SirenLookupModel/Results.html +++ b/docs/docsets/Siren.docset/Contents/Resources/Documents/Structs/SirenLookupModel/Results.html @@ -241,7 +241,7 @@

    Declaration

    diff --git a/docs/docsets/Siren.docset/Contents/Resources/Documents/index.html b/docs/docsets/Siren.docset/Contents/Resources/Documents/index.html index 86a7956d..8a84d714 100644 --- a/docs/docsets/Siren.docset/Contents/Resources/Documents/index.html +++ b/docs/docsets/Siren.docset/Contents/Resources/Documents/index.html @@ -33,13 +33,7 @@ Siren
  • - - @@ -47,15 +41,33 @@ Enumerations @@ -63,22 +75,52 @@ Structures @@ -101,16 +143,15 @@

    Table of Contents

  • Features
  • Screenshots
  • Installation Instructions
  • -
  • Example Code
  • -
  • Granular/Differentiated Version Management
  • -
  • Delegates (Optional)
  • +
  • Implementation Examples
  • Localization
  • Device Compatibility
  • Testing Siren
  • App Store Review & Submissions
  • -
  • Phrased Releases
  • +
  • Phased Releases
  • Words of Caution
  • Ports
  • +
  • Shout-Out and Gratitude
  • Attribution
  • @@ -125,28 +166,27 @@

    About

  • Siren is built to work with the Semantic Versioning system.
      -
    • Semantic Versioning is a three number versioning system (e.g., 1.0.0)
    • +
    • Canonical Semantic Versioning uses a three number versioning system (e.g., 1.0.0)
    • Siren also supports two-number versioning (e.g., 1.0) and four-number versioning (e.g., 1.0.0.0)
  • -
  • Siren is actively maintained by Arthur Sabintsev and Aaron Brager
  • -

    README Translations

    +

    Features

    +

    Current Features

    -

    Features

    +

    Future Features

      -
    • [x] CocoaPods Support
    • -
    • [x] Carthage Support
    • -
    • [x] Swift Package Manager Support
    • -
    • [x] Localized for 30+ languages (see Localization)
    • -
    • [x] Pre-Update Device Compatibility Check (see Device Compatibility)
    • -
    • [x] Three types of alerts (see Screenshots)
    • -
    • [x] Optional delegate methods (see Delegates (Optional))
    • -
    • [x] Unit Tests
    • -
    • [x] Documentation can be found at http://sabintsev.com/Siren.
    • +
    • [ ] Present prompt only on WiFi if app is over the OTA limit.
    • +
    • [ ] Support for Third-/Homegrown Update Servers (not including TestFlight).
    • +
    • [ ] Increase code coverage with more unit tests and UI tests.

    Screenshots

    @@ -154,7 +194,7 @@

    Screenshots

  • The left picture forces the user to update the app.
  • The center picture gives the user the option to update the app.
  • The right picture gives the user the option to skip the current update.
  • -
  • These options are controlled by the Siren.AlertType enum.
  • +
  • These options are controlled by the Rules.AlertType enum.
  • @@ -202,196 +242,56 @@

    CocoaPods

    Carthage

    github "ArtSabintsev/Siren" // Swift 4.2
    -github "ArtSabintsev/Siren", "swift4.1" // Swift 4.1
    -github "ArtSabintsev/Siren", "swift3.2" // Swift 3.2
    -github "ArtSabintsev/Siren", "swift3.1" // Swift 3.1
    -github "ArtSabintsev/Siren", "swift2.3" // Swift 2.3
    +github "ArtSabintsev/Siren" "swift4.1" // Swift 4.1
    +github "ArtSabintsev/Siren" "swift3.2" // Swift 3.2
    +github "ArtSabintsev/Siren" "swift3.1" // Swift 3.1
    +github "ArtSabintsev/Siren" "swift2.3" // Swift 2.3
     

    Swift Package Manager

    -
    .Package(url: "https://github.com/ArtSabintsev/Siren.git", majorVersion: 3)
    +
    .Package(url: "https://github.com/ArtSabintsev/Siren.git", majorVersion: 4)
     
    -

    Example Code

    - -

    Below is some commented sample code. Adapt this to meet your app’s needs.

    - -

    For a full list of optional settings/preferences, please refer to https://github.com/ArtSabintsev/Siren/blob/master/Example/Example/AppDelegate.swift in the Sample Project.

    -
    func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
    -    /* Siren code should go below window?.makeKeyAndVisible() */
    +

    Implementation Examples

    - // Siren is a singleton - let siren = Siren.shared +

    Implementing Siren is as easy as adding two line of code to your app.

    +
    import Siren // Line 1
    +import UIKit
     
    -    // Optional: Defaults to .option
    -    siren.alertType = <#Siren.AlertType_Enum_Value#>
    +@UIApplicationMain
    +final class AppDelegate: UIResponder, UIApplicationDelegate {
    +    var window: UIWindow?
     
    -    // Optional: Change the various UIAlertController and UIAlertAction messaging. One or more values can be changes. If only a subset of values are changed, the defaults with which Siren comes with will be used.
    -    siren.alertMessaging = SirenAlertMessaging(updateTitle: NSAttributedString(string: "New Fancy Title"),
    -                                                updateMessage: NSAttributedString(string: "New message goes here!"),
    -                                                updateButtonMessage: NSAttributedString(string: "Update Now, Plz!?"),
    -                                                nextTimeButtonMessage: NSAttributedString(string: "OK, next time it is!"),
    -                                                skipVersionButtonMessage: NSAttributedString(string: "Please don't push skip, please don't!"))
    +    func application(_ application: UIApplication,
    +                     didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
    +        window?.makeKeyAndVisible()
     
    +        Siren.shared.wail() // Line 2
     
    -    // Optional: Set this variable if you would only like to show an alert if your app has been available on the store for a few days.
    -    // This default value is set to 1 to avoid this issue: https://github.com/ArtSabintsev/Siren#words-of-caution
    -    // To show the update immediately after Apple has updated their JSON, set this value to 0. Not recommended due to aforementioned reason in https://github.com/ArtSabintsev/Siren#words-of-caution.
    -    siren.showAlertAfterCurrentVersionHasBeenReleasedForDays = 3
    -
    -    // Replace .immediately with .daily or .weekly to specify a maximum daily or weekly frequency for version checks.
    -    // DO NOT CALL THIS METHOD IN didFinishLaunchingWithOptions IF YOU ALSO PLAN TO CALL IT IN applicationDidBecomeActive.
    -    siren.checkVersion(checkType: .immediately)
    -
    -    return true
    -}
    -
    -func applicationDidBecomeActive(application: UIApplication) {
    -    /*
    -        Perform daily (.daily) or weekly (.weekly) checks for new version of your app.
    -        Useful if user returns to your app from the background after extended period of time.
    -            Place in applicationDidBecomeActive(_:).
    -     */
    -
    -    Siren.shared.checkVersion(checkType: .daily)
    -}
    -
    -func applicationWillEnterForeground(application: UIApplication) {
    -   /*
    -      Useful if user returns to your app from the background after being sent to the
    -      App Store, but doesn't update their app before coming back to your app.
    -
    -      ONLY USE WITH Siren.AlertType.immediately
    -   */
    -
    -    Siren.shared.checkVersion(checkType: .immediately)
    -}
    -
    - -

    And you’re all set!

    -

    Prompting for Updates without Alerts

    - -

    Some developers may want to display a less obtrusive custom interface, like a banner or small icon. To accomplish this, you can disable alert presentation by doing the following:

    -
    func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
    -    ...
    -    siren.delegate = self
    -    siren.alertType = .none
    -    ...
    -}
    -
    -extension AppDelegate: SirenDelegate {
    -    // Returns a localized message to this delegate method upon performing a successful version check
    -    func sirenDidDetectNewVersionWithoutAlert(message: String, updateType: UpdateType) {
    -        print("\(message)")
    +        return true
         }
     }
     
    -

    Siren will call the sirenDidDetectNewVersionWithoutAlert(message: String) delegate method, passing a localized, suggested update string suitable for display. Implement this method to display your own messaging, optionally using message.

    -

    Granular Version Update Management

    +

    Siren also has plenty of customization options. All examples can be found in the Example Project’s AppDelegate file. Uncomment the example you’d like to test.

    -

    If you would like to set a different type of alert for revision, patch, minor, and/or major updates, simply add one or all of the following optional lines to your setup before calling the checkVersion() method:

    -
        /* Siren defaults to Siren.AlertType.option for all updates */
    -    siren.shared.revisionUpdateAlertType = <#Siren.AlertType_Enum_Value#>
    -    siren.shared.patchUpdateAlertType = <#Siren.AlertType_Enum_Value#>
    -    siren.shared.minorUpdateAlertType = <#Siren.AlertType_Enum_Value#>
    -    siren.shared.majorUpdateAlertType = <#Siren.AlertType_Enum_Value#>
    -
    -

    Optional Delegate and Delegate Methods

    - -

    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.

    -
    public protocol SirenDelegate: NSObjectProtocol {
    -    /// Siren performed version check and did not display alert.
    -    func sirenDidDetectNewVersionWithoutAlert(message: String, updateType: UpdateType)
    -
    -    /// Siren failed to perform version check.
    -    ///
    -    /// - Note:
    -    ///     Depending on the reason for failure,
    -    ///     a system-level error may be returned.
    -    func sirenDidFailVersionCheck(error: Error)
    -
    -    /// User presented with update dialog.
    -    func sirenDidShowUpdateDialog(alertType: Siren.AlertType)
    -
    -    /// Siren performed a version check and latest version is installed.
    -    func sirenLatestVersionInstalled()
    -
    -    /// Provides the decoded JSON information from a successful version check call.
    -    ///
    -    /// - SeeAlso:
    -    ///     SirenLookupModel.swift
    -    ///
    -    /// - Parameter lookupModel: The `Decodable` model representing the JSON results from the iTunes Lookup API.
    -    func sirenNetworkCallDidReturnWithNewVersionInformation(lookupModel: SirenLookupModel)
    -
    -    /// User did click on button that cancels update dialog.
    -    func sirenUserDidCancel()
    -
    -    /// User did click on button that launched "App Store.app".
    -    func sirenUserDidLaunchAppStore()
    -
    -    /// User did click on button that skips version update.
    -    func sirenUserDidSkipVersion()
    -}
    -
    +

    WARNING: Siren should ONLY be placed in UIApplication.didFinishLaunchingWithOptions and only after the window?.makeKeyAndVisible() call. Siren initializes a listener on didBecomeActiveNotification to perform version checks.

    Localization

    -

    Siren is localized for

    +

    Siren is localized for the following languages:

    -
      -
    • Arabic
    • -
    • Armenian
    • -
    • Basque
    • -
    • Chinese (Simplified and Traditional)
    • -
    • Croatian
    • -
    • Czech
    • -
    • Danish
    • -
    • Dutch
    • -
    • English
    • -
    • Estonian
    • -
    • Finnish
    • -
    • French
    • -
    • German
    • -
    • Greek
    • -
    • Hebrew
    • -
    • Hungarian
    • -
    • Indonesian
    • -
    • Italian
    • -
    • Japanese
    • -
    • Korean
    • -
    • Latvian
    • -
    • Lithuanian
    • -
    • Malay
    • -
    • Norwegian (Bokmål)
    • -
    • Persian (Afghanistan, Iran, Persian)
    • -
    • Polish
    • -
    • Portuguese (Brazil and Portugal)
    • -
    • Russian
    • -
    • Serbian (Cyrillic and Latin)
    • -
    • Slovenian
    • -
    • Spanish
    • -
    • Swedish
    • -
    • Thai
    • -
    • Turkish
    • -
    • Ukrainian
    • -
    • Urdu
    • -
    • Vietnamese
    • -
    - -

    You may want the update dialog to always appear in a certain language, ignoring iOS’s language setting (e.g. apps released in a specific country).

    +

    Arabic, Armenian, Basque, Chinese (Simplified and Traditional), Croatian, Czech, Danish, Dutch, English, Estonian, Finnish, French, German, Greek, Hebrew, Hungarian, Indonesian, Italian, Japanese, Korean, Latvian, Lithuanian, Malay, Norwegian (Bokmål), Persian (Afghanistan, Iran, Persian), Polish, Portuguese (Brazil and Portugal), Russian, Serbian (Cyrillic and Latin), Slovenian, Spanish, Swedish, Thai, Turkish, Ukrainian, Urdu, Vietnamese

    -

    You can enable it like so:

    -
    Siren.shared.forceLanguageLocalization = Siren.LanguageType.<#Siren.LanguageType_Enum_Value#>
    +

    You may want the update dialog to always appear in a certain language, ignoring the user’s device-specific setting. You can enable it like so:

    +
    // In this example, we force the `russian` language.
    +Siren.shared.presentationManager = PresentationManager(forceLanguageLocalization: .russian)
     

    Device Compatibility

    -

    If an app update is available, Siren checks to make sure that the version of iOS on the user’s device is compatible with the one that is required by the app update. For example, if a user has iOS 10 installed on their device, but the app update requires iOS 11, an alert will not be shown. This takes care of the false positive case regarding app updating.

    +

    If an app update is available, Siren checks to make sure that the version of iOS on the user’s device is compatible with the one that is required by the app update. For example, if a user has iOS 11 installed on their device, but the app update requires iOS 12, an alert will not be shown. This takes care of the false positive case regarding app updating.

    Testing Siren

    -

    Temporarily change the version string in Xcode (within the .xcodeproj) to an older version than the one that’s currently available in the App Store. Afterwards, build and run your app, and you should see the alert.

    +

    Temporarily change the version string in Xcode (within the .xcodeproj file) to an older version than the one that’s currently available in the App Store. Afterwards, build and run your app, and you should see the alert.

    If you currently don’t have an app in the store, change your bundleID to one that is already in the store. In the sample app packaged with this library, we use the App Store Connect app’s bundleID: com.apple.AppStoreConnect.

    - -

    For your convenience, you may turn on debugging statements by setting self.debugEnabled = true before calling the checkVersion() method.

    App Store Submissions

    The App Store reviewer will not see the alert. The version in the App Store will always be older than the version being reviewed.

    @@ -401,7 +301,7 @@

    Phased Releases

    • You can leave Siren configured as normal. Phased rollout will continue to auto-update apps. Since all users can still manually update your app directly from the App Store, Siren will ignore the phased rollout and will prompt users to update.
    • -
    • You can set showAlertAfterCurrentVersionHasBeenReleasedForDays to 7, and Siren will not prompt any users until the latest version is 7 days old, after phased rollout is complete.
    • +
    • You can set showAlertAfterCurrentVersionHasBeenReleasedForDays to 7, and Siren will not prompt any users until the latest version is 7 days old, after the phased rollout is complete.
    • You can remotely disable Siren until the rollout is done using your own API / backend logic.

    Words of Caution

    @@ -415,6 +315,7 @@

    Ports

    • Harpy
    • Siren was ported from Harpy, as Siren and Harpy are maintained by the same developer.
    • +
    • As of December 2018, Harpy has been deprecated in favor of Siren.
  • Java (Android) @@ -429,14 +330,23 @@

    Ports

  • The Siren Swift library inspired the React Native library.
  • +

    Shout-Out and Gratitude

    + +

    A massive shout-out and thank you goes to the following folks:

    + +
      +
    • Aaron Brager for motivating me and assisting me in building the initial proof-of-concept of Siren (based on Harpy) back in 2015. Without him, Siren may never have been built.
    • +
    • All of Harpy’s Consitrbutors for helping building the feature set from 2012-2015 that was used as the basis for the first version of Siren.
    • +
    • All of Siren’s Contributors for helping make Siren as powerful and bug-free as it currently is today.
    • +

    Created and maintained by

    -

    Arthur Ariel Sabintsev & Aaron Brager

    +

    Arthur Ariel Sabintsev

    diff --git a/docs/docsets/Siren.docset/Contents/Resources/Documents/search.json b/docs/docsets/Siren.docset/Contents/Resources/Documents/search.json index 53596f3b..140126da 100644 --- a/docs/docsets/Siren.docset/Contents/Resources/Documents/search.json +++ b/docs/docsets/Siren.docset/Contents/Resources/Documents/search.json @@ -1 +1 @@ -{"Structs/SirenError/Known.html#/s:5Siren0A5ErrorV5KnownO20appStoreAppIDFailureyA2EmF":{"name":"appStoreAppIDFailure","abstract":"

    Error retrieving trackId as the JSON does not contain a ‘trackId’ key.

    ","parent_name":"Known"},"Structs/SirenError/Known.html#/s:5Siren0A5ErrorV5KnownO28appStoreDataRetrievalFailureyAEs0B0_pSg_tcAEmF":{"name":"appStoreDataRetrievalFailure(underlyingError:)","abstract":"

    Error retrieving App Store data as an error was returned.

    ","parent_name":"Known"},"Structs/SirenError/Known.html#/s:5Siren0A5ErrorV5KnownO26appStoreJSONParsingFailureyAEs0B0_p_tcAEmF":{"name":"appStoreJSONParsingFailure(underlyingError:)","abstract":"

    Error parsing App Store JSON data.

    ","parent_name":"Known"},"Structs/SirenError/Known.html#/s:5Siren0A5ErrorV5KnownO33appStoreDataRetrievalEmptyResultsyA2EmF":{"name":"appStoreDataRetrievalEmptyResults","abstract":"

    Error retrieving App Store data as JSON results were empty. Is your app available in the US? If not, change the countryCode variable to fix this error.

    ","parent_name":"Known"},"Structs/SirenError/Known.html#/s:5Siren0A5ErrorV5KnownO30appStoreOSVersionNumberFailureyA2EmF":{"name":"appStoreOSVersionNumberFailure","abstract":"

    Error retrieving iOS version number as there was no data returned.

    ","parent_name":"Known"},"Structs/SirenError/Known.html#/s:5Siren0A5ErrorV5KnownO28appStoreOSVersionUnsupportedyA2EmF":{"name":"appStoreOSVersionUnsupported","abstract":"

    The version of iOS on the device is lower than that of the one required by the app verison update.

    ","parent_name":"Known"},"Structs/SirenError/Known.html#/s:5Siren0A5ErrorV5KnownO27appStoreVersionArrayFailureyA2EmF":{"name":"appStoreVersionArrayFailure","abstract":"

    Error retrieving App Store verson number as the JSON does not contain a ‘version’ key.

    ","parent_name":"Known"},"Structs/SirenError/Known.html#/s:5Siren0A5ErrorV5KnownO12malformedURLyA2EmF":{"name":"malformedURL","abstract":"

    The iTunes URL is malformed. Please leave an issue on https://github.com/ArtSabintsev/Siren with as many details as possible.

    ","parent_name":"Known"},"Structs/SirenError/Known.html#/s:5Siren0A5ErrorV5KnownO17noUpdateAvailableyA2EmF":{"name":"noUpdateAvailable","abstract":"

    No new update available.

    ","parent_name":"Known"},"Structs/SirenError/Known.html#/s:5Siren0A5ErrorV5KnownO22recentlyCheckedAlreadyyA2EmF":{"name":"recentlyCheckedAlready","abstract":"

    Not checking the version, because it was already checked recently.

    ","parent_name":"Known"},"Structs/SirenError/Known.html":{"name":"Known","abstract":"

    Enumerates all potentials errors that Siren can handle.

    ","parent_name":"SirenError"},"Structs/SirenLookupModel/Results.html#/s:5Siren0A11LookupModelV7ResultsV5appIDSivp":{"name":"appID","abstract":"

    The app’s App ID.

    ","parent_name":"Results"},"Structs/SirenLookupModel/Results.html#/s:5Siren0A11LookupModelV7ResultsV25currentVersionReleaseDateSSvp":{"name":"currentVersionReleaseDate","abstract":"

    The release date for the latest verison of the app.

    ","parent_name":"Results"},"Structs/SirenLookupModel/Results.html#/s:5Siren0A11LookupModelV7ResultsV16minimumOSVersionSSvp":{"name":"minimumOSVersion","abstract":"

    The minimum verison of iOS that the current verison of the app requires.

    ","parent_name":"Results"},"Structs/SirenLookupModel/Results.html#/s:5Siren0A11LookupModelV7ResultsV12releaseNotesSSSgvp":{"name":"releaseNotes","abstract":"

    The releases notes from the latest version of the app.

    ","parent_name":"Results"},"Structs/SirenLookupModel/Results.html#/s:5Siren0A11LookupModelV7ResultsV7versionSSvp":{"name":"version","abstract":"

    The latest version of the app.

    ","parent_name":"Results"},"Structs/SirenLookupModel.html#/s:5Siren0A11LookupModelV7resultsSayAC7ResultsVGvp":{"name":"results","abstract":"

    The array of results objects from the iTunes Lookup API.

    ","parent_name":"SirenLookupModel"},"Structs/SirenLookupModel/Results.html":{"name":"Results","abstract":"

    The Results object from the the iTunes Lookup API.

    ","parent_name":"SirenLookupModel"},"Structs/SirenAlertMessaging/Constants.html#/s:5Siren0A14AlertMessagingV9ConstantsV8nextTimeSo18NSAttributedStringCvpZ":{"name":"nextTime","abstract":"

    The button text that conveys the message that the user should be prompted to update next time the app launches.

    ","parent_name":"Constants"},"Structs/SirenAlertMessaging/Constants.html#/s:5Siren0A14AlertMessagingV9ConstantsV11skipVersionSo18NSAttributedStringCvpZ":{"name":"skipVersion","abstract":"

    The text that conveys the message that the the user wants to skip this verison update.

    ","parent_name":"Constants"},"Structs/SirenAlertMessaging/Constants.html#/s:5Siren0A14AlertMessagingV9ConstantsV13updateMessageSo18NSAttributedStringCvpZ":{"name":"updateMessage","abstract":"

    The text that conveys the message that there is an app update available

    ","parent_name":"Constants"},"Structs/SirenAlertMessaging/Constants.html#/s:5Siren0A14AlertMessagingV9ConstantsV11updateTitleSo18NSAttributedStringCvpZ":{"name":"updateTitle","abstract":"

    The alert title which defaults to Update Available.

    ","parent_name":"Constants"},"Structs/SirenAlertMessaging/Constants.html#/s:5Siren0A14AlertMessagingV9ConstantsV9updateNowSo18NSAttributedStringCvpZ":{"name":"updateNow","abstract":"

    The button text that conveys the message that the user would like to update the app right away.

    ","parent_name":"Constants"},"Structs/SirenAlertMessaging/Constants.html":{"name":"Constants","abstract":"

    The default constants used for the alert messaging.

    ","parent_name":"SirenAlertMessaging"},"Structs/SirenAlertMessaging.html#/s:5Siren0A14AlertMessagingV11updateTitle0D7Message0d6ButtonF008nextTimegF0011skipVersiongF0ACSo18NSAttributedStringC_A4Jtcfc":{"name":"init(updateTitle:updateMessage:updateButtonMessage:nextTimeButtonMessage:skipVersionButtonMessage:)","abstract":"

    The public initializer

    ","parent_name":"SirenAlertMessaging"},"Structs/SirenAlertMessaging.html":{"name":"SirenAlertMessaging","abstract":"

    Allows the overriding of all the UIAlertController and UIActionSheet Strings to which Siren defaults.

    "},"Structs/SirenLookupModel.html":{"name":"SirenLookupModel","abstract":"

    MARK: Siren extension used to parse and map the iTunes JSON results into a model represented in Swift.

    "},"Structs/SirenError.html":{"name":"SirenError","abstract":"

    Data structure used to build Siren specific Errors.

    "},"Protocols/SirenDelegate.html#/s:5Siren0A8DelegateP36sirenDidDetectNewVersionWithoutAlert5title7message10updateTypeySS_SSAA06UpdateM0OtF":{"name":"sirenDidDetectNewVersionWithoutAlert(title:message:updateType:)","abstract":"

    Siren performed a version check and did not display an alert.

    ","parent_name":"SirenDelegate"},"Protocols/SirenDelegate.html#/s:5Siren0A8DelegateP24sirenDidFailVersionCheck5errorys5Error_p_tF":{"name":"sirenDidFailVersionCheck(error:)","abstract":"

    Siren failed to perform version check.

    ","parent_name":"SirenDelegate"},"Protocols/SirenDelegate.html#/s:5Siren0A8DelegateP24sirenDidShowUpdateDialog9alertTypeyA2AC05AlertI0O_tF":{"name":"sirenDidShowUpdateDialog(alertType:)","abstract":"

    User presented with an update dialog.

    ","parent_name":"SirenDelegate"},"Protocols/SirenDelegate.html#/s:5Siren0A8DelegateP27sirenLatestVersionInstalledyyF":{"name":"sirenLatestVersionInstalled()","abstract":"

    Siren performed a version check and the latest version was already installed.

    ","parent_name":"SirenDelegate"},"Protocols/SirenDelegate.html#/s:5Siren0A8DelegateP50sirenNetworkCallDidReturnWithNewVersionInformation11lookupModelyAA0a6LookupM0V_tF":{"name":"sirenNetworkCallDidReturnWithNewVersionInformation(lookupModel:)","abstract":"

    Provides the decoded JSON information from a successful version check call.

    ","parent_name":"SirenDelegate"},"Protocols/SirenDelegate.html#/s:5Siren0A8DelegateP18sirenUserDidCancelyyF":{"name":"sirenUserDidCancel()","abstract":"

    User did click on button that cancels update dialog.

    ","parent_name":"SirenDelegate"},"Protocols/SirenDelegate.html#/s:5Siren0A8DelegateP26sirenUserDidLaunchAppStoreyyF":{"name":"sirenUserDidLaunchAppStore()","abstract":"

    User did click on button that launched App Store.app.

    ","parent_name":"SirenDelegate"},"Protocols/SirenDelegate.html#/s:5Siren0A8DelegateP23sirenUserDidSkipVersionyyF":{"name":"sirenUserDidSkipVersion()","abstract":"

    User did click on button that skips version update.

    ","parent_name":"SirenDelegate"},"Protocols/SirenDelegate.html":{"name":"SirenDelegate","abstract":"

    Delegate that handles all codepaths for Siren upon version check completion.

    "},"Enums/UpdateType.html#/s:5Siren10UpdateTypeO5majoryA2CmF":{"name":"major","abstract":"

    Major release available: A.b.c.d

    ","parent_name":"UpdateType"},"Enums/UpdateType.html#/s:5Siren10UpdateTypeO5minoryA2CmF":{"name":"minor","abstract":"

    Minor release available: a.B.c.d

    ","parent_name":"UpdateType"},"Enums/UpdateType.html#/s:5Siren10UpdateTypeO5patchyA2CmF":{"name":"patch","abstract":"

    Patch release available: a.b.C.d

    ","parent_name":"UpdateType"},"Enums/UpdateType.html#/s:5Siren10UpdateTypeO8revisionyA2CmF":{"name":"revision","abstract":"

    Revision release available: a.b.c.D

    ","parent_name":"UpdateType"},"Enums/UpdateType.html#/s:5Siren10UpdateTypeO7unknownyA2CmF":{"name":"unknown","abstract":"

    No information available about the update.

    ","parent_name":"UpdateType"},"Enums/UpdateType.html":{"name":"UpdateType","abstract":"

    MARK - Siren UpdateType"},"Classes/Siren/LanguageType.html#/s:5SirenAAC12LanguageTypeO6arabicyA2DmF":{"name":"arabic","abstract":"

    Arabic

    ","parent_name":"LanguageType"},"Classes/Siren/LanguageType.html#/s:5SirenAAC12LanguageTypeO8armenianyA2DmF":{"name":"armenian","abstract":"

    Armenian

    ","parent_name":"LanguageType"},"Classes/Siren/LanguageType.html#/s:5SirenAAC12LanguageTypeO6basqueyA2DmF":{"name":"basque","abstract":"

    Basque

    ","parent_name":"LanguageType"},"Classes/Siren/LanguageType.html#/s:5SirenAAC12LanguageTypeO17chineseSimplifiedyA2DmF":{"name":"chineseSimplified","abstract":"

    Simplified Chinese

    ","parent_name":"LanguageType"},"Classes/Siren/LanguageType.html#/s:5SirenAAC12LanguageTypeO18chineseTraditionalyA2DmF":{"name":"chineseTraditional","abstract":"

    Traditional Chinese

    ","parent_name":"LanguageType"},"Classes/Siren/LanguageType.html#/s:5SirenAAC12LanguageTypeO8croatianyA2DmF":{"name":"croatian","abstract":"

    Croatian

    ","parent_name":"LanguageType"},"Classes/Siren/LanguageType.html#/s:5SirenAAC12LanguageTypeO5czechyA2DmF":{"name":"czech","abstract":"

    Czech

    ","parent_name":"LanguageType"},"Classes/Siren/LanguageType.html#/s:5SirenAAC12LanguageTypeO6danishyA2DmF":{"name":"danish","abstract":"

    Danish

    ","parent_name":"LanguageType"},"Classes/Siren/LanguageType.html#/s:5SirenAAC12LanguageTypeO5dutchyA2DmF":{"name":"dutch","abstract":"

    Dutch

    ","parent_name":"LanguageType"},"Classes/Siren/LanguageType.html#/s:5SirenAAC12LanguageTypeO7englishyA2DmF":{"name":"english","abstract":"

    English

    ","parent_name":"LanguageType"},"Classes/Siren/LanguageType.html#/s:5SirenAAC12LanguageTypeO8estonianyA2DmF":{"name":"estonian","abstract":"

    Estonian

    ","parent_name":"LanguageType"},"Classes/Siren/LanguageType.html#/s:5SirenAAC12LanguageTypeO7finnishyA2DmF":{"name":"finnish","abstract":"

    Finnish

    ","parent_name":"LanguageType"},"Classes/Siren/LanguageType.html#/s:5SirenAAC12LanguageTypeO6frenchyA2DmF":{"name":"french","abstract":"

    French

    ","parent_name":"LanguageType"},"Classes/Siren/LanguageType.html#/s:5SirenAAC12LanguageTypeO6germanyA2DmF":{"name":"german","abstract":"

    German

    ","parent_name":"LanguageType"},"Classes/Siren/LanguageType.html#/s:5SirenAAC12LanguageTypeO5greekyA2DmF":{"name":"greek","abstract":"

    Greek

    ","parent_name":"LanguageType"},"Classes/Siren/LanguageType.html#/s:5SirenAAC12LanguageTypeO6hebrewyA2DmF":{"name":"hebrew","abstract":"

    Hebrew

    ","parent_name":"LanguageType"},"Classes/Siren/LanguageType.html#/s:5SirenAAC12LanguageTypeO9hungarianyA2DmF":{"name":"hungarian","abstract":"

    Hungarian

    ","parent_name":"LanguageType"},"Classes/Siren/LanguageType.html#/s:5SirenAAC12LanguageTypeO10indonesianyA2DmF":{"name":"indonesian","abstract":"

    Indonesian

    ","parent_name":"LanguageType"},"Classes/Siren/LanguageType.html#/s:5SirenAAC12LanguageTypeO7italianyA2DmF":{"name":"italian","abstract":"

    Italian

    ","parent_name":"LanguageType"},"Classes/Siren/LanguageType.html#/s:5SirenAAC12LanguageTypeO8japaneseyA2DmF":{"name":"japanese","abstract":"

    Japanese

    ","parent_name":"LanguageType"},"Classes/Siren/LanguageType.html#/s:5SirenAAC12LanguageTypeO6koreanyA2DmF":{"name":"korean","abstract":"

    Korean

    ","parent_name":"LanguageType"},"Classes/Siren/LanguageType.html#/s:5SirenAAC12LanguageTypeO7latvianyA2DmF":{"name":"latvian","abstract":"

    Latvian

    ","parent_name":"LanguageType"},"Classes/Siren/LanguageType.html#/s:5SirenAAC12LanguageTypeO10lithuanianyA2DmF":{"name":"lithuanian","abstract":"

    Lithuanian

    ","parent_name":"LanguageType"},"Classes/Siren/LanguageType.html#/s:5SirenAAC12LanguageTypeO5malayyA2DmF":{"name":"malay","abstract":"

    Malaysian

    ","parent_name":"LanguageType"},"Classes/Siren/LanguageType.html#/s:5SirenAAC12LanguageTypeO9norwegianyA2DmF":{"name":"norwegian","abstract":"

    Norwegian

    ","parent_name":"LanguageType"},"Classes/Siren/LanguageType.html#/s:5SirenAAC12LanguageTypeO7persianyA2DmF":{"name":"persian","abstract":"

    Persian

    ","parent_name":"LanguageType"},"Classes/Siren/LanguageType.html#/s:5SirenAAC12LanguageTypeO18persianAfghanistanyA2DmF":{"name":"persianAfghanistan","abstract":"

    Persian (Afghanistan)

    ","parent_name":"LanguageType"},"Classes/Siren/LanguageType.html#/s:5SirenAAC12LanguageTypeO11persianIranyA2DmF":{"name":"persianIran","abstract":"

    Persian (Iran)

    ","parent_name":"LanguageType"},"Classes/Siren/LanguageType.html#/s:5SirenAAC12LanguageTypeO6polishyA2DmF":{"name":"polish","abstract":"

    Polish

    ","parent_name":"LanguageType"},"Classes/Siren/LanguageType.html#/s:5SirenAAC12LanguageTypeO16portugueseBrazilyA2DmF":{"name":"portugueseBrazil","abstract":"

    Portuguese (Brazil)

    ","parent_name":"LanguageType"},"Classes/Siren/LanguageType.html#/s:5SirenAAC12LanguageTypeO18portuguesePortugalyA2DmF":{"name":"portuguesePortugal","abstract":"

    Portuguese (Portugal)

    ","parent_name":"LanguageType"},"Classes/Siren/LanguageType.html#/s:5SirenAAC12LanguageTypeO7russianyA2DmF":{"name":"russian","abstract":"

    Russian

    ","parent_name":"LanguageType"},"Classes/Siren/LanguageType.html#/s:5SirenAAC12LanguageTypeO15serbianCyrillicyA2DmF":{"name":"serbianCyrillic","abstract":"

    Serbian (Cyrillic)

    ","parent_name":"LanguageType"},"Classes/Siren/LanguageType.html#/s:5SirenAAC12LanguageTypeO12serbianLatinyA2DmF":{"name":"serbianLatin","abstract":"

    Serbian (Latin)

    ","parent_name":"LanguageType"},"Classes/Siren/LanguageType.html#/s:5SirenAAC12LanguageTypeO9slovenianyA2DmF":{"name":"slovenian","abstract":"

    Slovenian

    ","parent_name":"LanguageType"},"Classes/Siren/LanguageType.html#/s:5SirenAAC12LanguageTypeO7spanishyA2DmF":{"name":"spanish","abstract":"

    Spanish

    ","parent_name":"LanguageType"},"Classes/Siren/LanguageType.html#/s:5SirenAAC12LanguageTypeO7swedishyA2DmF":{"name":"swedish","abstract":"

    Swedish

    ","parent_name":"LanguageType"},"Classes/Siren/LanguageType.html#/s:5SirenAAC12LanguageTypeO4thaiyA2DmF":{"name":"thai","abstract":"

    Thai

    ","parent_name":"LanguageType"},"Classes/Siren/LanguageType.html#/s:5SirenAAC12LanguageTypeO7turkishyA2DmF":{"name":"turkish","abstract":"

    Turkish

    ","parent_name":"LanguageType"},"Classes/Siren/LanguageType.html#/s:5SirenAAC12LanguageTypeO4urduyA2DmF":{"name":"urdu","abstract":"

    Urdu

    ","parent_name":"LanguageType"},"Classes/Siren/LanguageType.html#/s:5SirenAAC12LanguageTypeO9ukrainianyA2DmF":{"name":"ukrainian","abstract":"

    Ukranian

    ","parent_name":"LanguageType"},"Classes/Siren/LanguageType.html#/s:5SirenAAC12LanguageTypeO10vietnameseyA2DmF":{"name":"vietnamese","abstract":"

    Vietnamese

    ","parent_name":"LanguageType"},"Classes/Siren/VersionCheckType.html#/s:5SirenAAC16VersionCheckTypeO11immediatelyyA2DmF":{"name":"immediately","abstract":"

    Version check performed every time the app is launched.

    ","parent_name":"VersionCheckType"},"Classes/Siren/VersionCheckType.html#/s:5SirenAAC16VersionCheckTypeO5dailyyA2DmF":{"name":"daily","abstract":"

    Version check performed once a day.

    ","parent_name":"VersionCheckType"},"Classes/Siren/VersionCheckType.html#/s:5SirenAAC16VersionCheckTypeO6weeklyyA2DmF":{"name":"weekly","abstract":"

    Version check performed once a week.

    ","parent_name":"VersionCheckType"},"Classes/Siren/AlertType.html#/s:5SirenAAC9AlertTypeO5forceyA2DmF":{"name":"force","abstract":"

    Forces user to update your app (1 button alert).

    ","parent_name":"AlertType"},"Classes/Siren/AlertType.html#/s:5SirenAAC9AlertTypeO6optionyA2DmF":{"name":"option","abstract":"

    (DEFAULT) Presents user with option to update app now or at next launch (2 button alert).

    ","parent_name":"AlertType"},"Classes/Siren/AlertType.html#/s:5SirenAAC9AlertTypeO4skipyA2DmF":{"name":"skip","abstract":"

    Presents user with option to update the app now, at next launch, or to skip this version all together (3 button alert).

    ","parent_name":"AlertType"},"Classes/Siren/AlertType.html#/s:5SirenAAC9AlertTypeO4noneyA2DmF":{"name":"none","abstract":"

    Doesn’t show the alert, but instead returns a localized message","parent_name":"AlertType"},"Classes/Siren.html#/s:5SirenAAC0A11ErrorDomainSSvp":{"name":"SirenErrorDomain","abstract":"

    The error domain for all errors created by Siren.

    ","parent_name":"Siren"},"Classes/Siren.html#/s:5SirenAAC8delegateAA0A8Delegate_pSgXwvp":{"name":"delegate","abstract":"

    The SirenDelegate variable, which should be set if you’d like to be notified of any of specific user interactions or API success/failures.","parent_name":"Siren"},"Classes/Siren.html#/s:5SirenAAC12debugEnabledSbvp":{"name":"debugEnabled","abstract":"

    The debug flag, which is disabled by default.","parent_name":"Siren"},"Classes/Siren.html#/s:5SirenAAC9alertTypeAB05AlertC0Ovp":{"name":"alertType","abstract":"

    Determines the type of alert that should be shown.","parent_name":"Siren"},"Classes/Siren.html#/s:5SirenAAC20majorUpdateAlertTypeAB0dE0Ovp":{"name":"majorUpdateAlertType","abstract":"

    Determines the type of alert that should be shown for major version updates: A.b.c","parent_name":"Siren"},"Classes/Siren.html#/s:5SirenAAC20minorUpdateAlertTypeAB0dE0Ovp":{"name":"minorUpdateAlertType","abstract":"

    Determines the type of alert that should be shown for minor version updates: a.B.c","parent_name":"Siren"},"Classes/Siren.html#/s:5SirenAAC20patchUpdateAlertTypeAB0dE0Ovp":{"name":"patchUpdateAlertType","abstract":"

    Determines the type of alert that should be shown for minor patch updates: a.b.C","parent_name":"Siren"},"Classes/Siren.html#/s:5SirenAAC23revisionUpdateAlertTypeAB0dE0Ovp":{"name":"revisionUpdateAlertType","abstract":"

    Determines the type of alert that should be shown for revision updates: a.b.c.D","parent_name":"Siren"},"Classes/Siren.html#/s:5SirenAAC7appNameSSvp":{"name":"appName","abstract":"

    The name of your app.","parent_name":"Siren"},"Classes/Siren.html#/s:5SirenAAC14alertMessagingAA0a5AlertC0Vvp":{"name":"alertMessaging","abstract":"

    Overrides all the Strings to which Siren defaults.","parent_name":"Siren"},"Classes/Siren.html#/s:5SirenAAC11countryCodeSSSgvp":{"name":"countryCode","abstract":"

    The region or country of an App Store in which your app is available.","parent_name":"Siren"},"Classes/Siren.html#/s:5SirenAAC25forceLanguageLocalizationAB0C4TypeOSgvp":{"name":"forceLanguageLocalization","abstract":"

    Overrides the default localization of a user’s device when presenting the update message and button titles in the alert.","parent_name":"Siren"},"Classes/Siren.html#/s:5SirenAAC24alertControllerTintColorSo7UIColorCSgvp":{"name":"alertControllerTintColor","abstract":"

    Overrides the tint color for UIAlertController.

    ","parent_name":"Siren"},"Classes/Siren.html#/s:5SirenAAC50showAlertAfterCurrentVersionHasBeenReleasedForDaysSivp":{"name":"showAlertAfterCurrentVersionHasBeenReleasedForDays","abstract":"

    When this is set, the alert will only show up if the current version has already been released for X days.","parent_name":"Siren"},"Classes/Siren.html#/s:5SirenAAC22currentAppStoreVersionSSSgvp":{"name":"currentAppStoreVersion","abstract":"

    The current version of your app that is available for download on the App Store

    ","parent_name":"Siren"},"Classes/Siren.html#/s:5SirenAAC6sharedABvpZ":{"name":"shared","abstract":"

    The App’s Singleton

    ","parent_name":"Siren"},"Classes/Siren.html#/s:5SirenAAC12checkVersion0B4TypeyAB0c5CheckD0O_tF":{"name":"checkVersion(checkType:)","abstract":"

    Checks the currently installed version of your app against the App Store.","parent_name":"Siren"},"Classes/Siren.html#/s:5SirenAAC14launchAppStoreyyF":{"name":"launchAppStore()","abstract":"

    Launches the AppStore in two situations:

    ","parent_name":"Siren"},"Classes/Siren/AlertType.html":{"name":"AlertType","abstract":"

    Determines the type of alert to present after a successful version check has been performed.

    ","parent_name":"Siren"},"Classes/Siren/VersionCheckType.html":{"name":"VersionCheckType","abstract":"

    Determines the frequency in which the the version check is performed and the user is prompted to update the app.

    ","parent_name":"Siren"},"Classes/Siren/LanguageType.html":{"name":"LanguageType","abstract":"

    Determines the available languages in which the update message and alert button titles should appear.

    ","parent_name":"Siren"},"Classes/Siren.html":{"name":"Siren","abstract":"

    The Siren Class. A singleton that is initialized using the shared constant.

    "},"Classes.html":{"name":"Classes","abstract":"

    The following classes are available globally.

    "},"Enums.html":{"name":"Enumerations","abstract":"

    The following enumerations are available globally.

    "},"Protocols.html":{"name":"Protocols","abstract":"

    The following protocols are available globally.

    "},"Structs.html":{"name":"Structures","abstract":"

    The following structures are available globally.

    "}} \ No newline at end of file +{"Structs/DataParser.html#/s:5Siren10DataParserV22isAppStoreVersionNewer09installedG003appfG0SbSSSg_AGtFZ":{"name":"isAppStoreVersionNewer(installedVersion:appStoreVersion:)","abstract":"

    Checks to see if the App Store version of the app is newer than the installed version.

    ","parent_name":"DataParser"},"Structs/DataParser.html#/s:5Siren10DataParserV30isUpdateCompatibleWithDeviceOS3forSbAA11LookupModelV_tFZ":{"name":"isUpdateCompatibleWithDeviceOS(for:)","abstract":"

    Validates that the latest version in the App Store is compatible with the device’s current version of iOS.

    ","parent_name":"DataParser"},"Structs/DataParser.html#/s:5Siren10DataParserV14parseForUpdate19forInstalledVersion011andAppStoreI0AA12RulesManagerV0F4TypeOSSSg_AKtFZ":{"name":"parseForUpdate(forInstalledVersion:andAppStoreVersion:)","abstract":"

    The type of update that is returned from the API in relation to the verison of the app that is installed.

    ","parent_name":"DataParser"},"Structs/DataParser.html#/s:5Siren10DataParserV5split33_35354280B893AD05C0C27D0AD925B30FLL7versionSaySiGSS_tFZ":{"name":"split(version:)","abstract":"

    Splits a version-formatted String into an[Int]`.

    ","parent_name":"DataParser"},"Structs/Rules/UpdatePromptFrequency.html#/s:5Siren5RulesV21UpdatePromptFrequencyO11immediatelyyA2EmF":{"name":"immediately","abstract":"

    Version check performed every time the app is launched.

    ","parent_name":"UpdatePromptFrequency"},"Structs/Rules/UpdatePromptFrequency.html#/s:5Siren5RulesV21UpdatePromptFrequencyO5dailyyA2EmF":{"name":"daily","abstract":"

    Version check performed once a day.

    ","parent_name":"UpdatePromptFrequency"},"Structs/Rules/UpdatePromptFrequency.html#/s:5Siren5RulesV21UpdatePromptFrequencyO6weeklyyA2EmF":{"name":"weekly","abstract":"

    Version check performed once a week.

    ","parent_name":"UpdatePromptFrequency"},"Structs/Rules/AlertType.html#/s:5Siren5RulesV9AlertTypeO5forceyA2EmF":{"name":"force","abstract":"

    Forces the user to update your app (1 button alert).

    ","parent_name":"AlertType"},"Structs/Rules/AlertType.html#/s:5Siren5RulesV9AlertTypeO6optionyA2EmF":{"name":"option","abstract":"

    Presents the user with option to update app now or at next launch (2 button alert).

    ","parent_name":"AlertType"},"Structs/Rules/AlertType.html#/s:5Siren5RulesV9AlertTypeO4skipyA2EmF":{"name":"skip","abstract":"

    Presents the user with option to update the app now, at next launch, or to skip this version all together (3 button alert).

    ","parent_name":"AlertType"},"Structs/Rules/AlertType.html#/s:5Siren5RulesV9AlertTypeO4noneyA2EmF":{"name":"none","abstract":"

    Doesn’t present the alert.","parent_name":"AlertType"},"Structs/Rules.html#/s:5Siren5RulesV9alertTypeAC05AlertD0Ovp":{"name":"alertType","abstract":"

    The type of alert that should be presented.

    ","parent_name":"Rules"},"Structs/Rules.html#/s:5Siren5RulesV9frequencyAC21UpdatePromptFrequencyOvp":{"name":"frequency","abstract":"

    The frequency in which a the user is prompted to update the app","parent_name":"Rules"},"Structs/Rules.html#/s:5Siren5RulesV15promptFrequency12forAlertTypeA2C012UpdatePromptD0O_AC0fG0Otcfc":{"name":"init(promptFrequency:forAlertType:)","abstract":"

    Initializes the alert presentation rules.

    ","parent_name":"Rules"},"Structs/Rules.html#/s:5Siren5RulesV8annoyingACvpZ":{"name":"annoying","abstract":"

    Performs a version check immediately, but allows the user to skip updating the app until the next time the app becomes active.

    ","parent_name":"Rules"},"Structs/Rules.html#/s:5Siren5RulesV8criticalACvpZ":{"name":"critical","abstract":"

    Performs a version check immediately and forces the user to update the app.

    ","parent_name":"Rules"},"Structs/Rules.html#/s:5Siren5RulesV7defaultACvpZ":{"name":"default","abstract":"

    Performs a version check once a day, but allows the user to skip updating the app until","parent_name":"Rules"},"Structs/Rules.html#/s:5Siren5RulesV10persistentACvpZ":{"name":"persistent","abstract":"

    Performs a version check daily, but allows the user to skip updating the app until the next time the app becomes active.

    ","parent_name":"Rules"},"Structs/Rules.html#/s:5Siren5RulesV7relaxedACvpZ":{"name":"relaxed","abstract":"

    Performs a version check weekly, but allows the user to skip updating the app until","parent_name":"Rules"},"Structs/Rules/AlertType.html":{"name":"AlertType","abstract":"

    Determines the type of alert to present after a successful version check has been performed.

    ","parent_name":"Rules"},"Structs/Rules/UpdatePromptFrequency.html":{"name":"UpdatePromptFrequency","abstract":"

    Determines the frequency in which the user is prompted to update the app","parent_name":"Rules"},"Structs/Results.html#/s:5Siren7ResultsV11alertActionAA05AlertD0Ovp":{"name":"alertAction","abstract":"

    The UIAlertAction the user chose upon being presented with the update alert.","parent_name":"Results"},"Structs/Results.html#/s:5Siren7ResultsV12localizationAA12LocalizationVvp":{"name":"localization","abstract":"

    The Siren-supported locale that was used for the string in the update alert.

    ","parent_name":"Results"},"Structs/Results.html#/s:5Siren7ResultsV11lookupModelAA06LookupD0Vvp":{"name":"lookupModel","abstract":"

    The Swift-mapped API model, if a successful version check was performed.

    ","parent_name":"Results"},"Structs/Results.html#/s:5Siren7ResultsV10updateTypeAA12RulesManagerV06UpdateD0Ovp":{"name":"updateType","abstract":"

    The type of update that was returned for the API.

    ","parent_name":"Results"},"Structs/LookupModel/Results/CodingKeys.html#/s:5Siren11LookupModelV7ResultsV10CodingKeys33_F2A9942F3CA9D99FD9845937489F40B8LLO5appIDyA2HmF":{"name":"appID","abstract":"

    The appID JSON key.

    ","parent_name":"CodingKeys"},"Structs/LookupModel/Results/CodingKeys.html#/s:5Siren11LookupModelV7ResultsV10CodingKeys33_F2A9942F3CA9D99FD9845937489F40B8LLO25currentVersionReleaseDateyA2HmF":{"name":"currentVersionReleaseDate","abstract":"

    The current version release date JSON key.

    ","parent_name":"CodingKeys"},"Structs/LookupModel/Results/CodingKeys.html#/s:5Siren11LookupModelV7ResultsV10CodingKeys33_F2A9942F3CA9D99FD9845937489F40B8LLO16minimumOSVersionyA2HmF":{"name":"minimumOSVersion","abstract":"

    The minimum device iOS version compatibility JSON key.

    ","parent_name":"CodingKeys"},"Structs/LookupModel/Results/CodingKeys.html#/s:5Siren11LookupModelV7ResultsV10CodingKeys33_F2A9942F3CA9D99FD9845937489F40B8LLO12releaseNotesyA2HmF":{"name":"releaseNotes","abstract":"

    The release notes JSON key.

    ","parent_name":"CodingKeys"},"Structs/LookupModel/Results/CodingKeys.html#/s:5Siren11LookupModelV7ResultsV10CodingKeys33_F2A9942F3CA9D99FD9845937489F40B8LLO7versionyA2HmF":{"name":"version","abstract":"

    The current App Store version JSON key.

    ","parent_name":"CodingKeys"},"Structs/LookupModel/Results/CodingKeys.html":{"name":"CodingKeys","abstract":"

    Codable Coding Keys for the Results array in the iTunes Lookup API JSON response.

    ","parent_name":"Results"},"Structs/LookupModel/Results.html#/s:5Siren11LookupModelV7ResultsV5appIDSivp":{"name":"appID","abstract":"

    The app’s App ID.

    ","parent_name":"Results"},"Structs/LookupModel/Results.html#/s:5Siren11LookupModelV7ResultsV25currentVersionReleaseDateSSvp":{"name":"currentVersionReleaseDate","abstract":"

    The release date for the latest verison of the app.

    ","parent_name":"Results"},"Structs/LookupModel/Results.html#/s:5Siren11LookupModelV7ResultsV16minimumOSVersionSSvp":{"name":"minimumOSVersion","abstract":"

    The minimum verison of iOS that the current verison of the app requires.

    ","parent_name":"Results"},"Structs/LookupModel/Results.html#/s:5Siren11LookupModelV7ResultsV12releaseNotesSSSgvp":{"name":"releaseNotes","abstract":"

    The releases notes from the latest version of the app.

    ","parent_name":"Results"},"Structs/LookupModel/Results.html#/s:5Siren11LookupModelV7ResultsV7versionSSvp":{"name":"version","abstract":"

    The latest version of the app.

    ","parent_name":"Results"},"Structs/LookupModel/CodingKeys.html#/s:5Siren11LookupModelV10CodingKeys33_F2A9942F3CA9D99FD9845937489F40B8LLO7resultsyA2FmF":{"name":"results","abstract":"

    The results JSON key.

    ","parent_name":"CodingKeys"},"Structs/LookupModel/CodingKeys.html":{"name":"CodingKeys","abstract":"

    Codable Coding Keys for the Top-Level iTunes Lookup API JSON response.

    ","parent_name":"LookupModel"},"Structs/LookupModel.html#/s:5Siren11LookupModelV7resultsSayAC7ResultsVGvp":{"name":"results","abstract":"

    The array of results objects from the iTunes Lookup API.

    ","parent_name":"LookupModel"},"Structs/LookupModel/Results.html":{"name":"Results","abstract":"

    The Results object from the the iTunes Lookup API.

    ","parent_name":"LookupModel"},"Structs/Localization/Language.html#/s:5Siren12LocalizationV8LanguageO6arabicyA2EmF":{"name":"arabic","abstract":"

    Arabic Language Localization

    ","parent_name":"Language"},"Structs/Localization/Language.html#/s:5Siren12LocalizationV8LanguageO8armenianyA2EmF":{"name":"armenian","abstract":"

    Armenian Language Localization

    ","parent_name":"Language"},"Structs/Localization/Language.html#/s:5Siren12LocalizationV8LanguageO6basqueyA2EmF":{"name":"basque","abstract":"

    Basque Language Localization

    ","parent_name":"Language"},"Structs/Localization/Language.html#/s:5Siren12LocalizationV8LanguageO17chineseSimplifiedyA2EmF":{"name":"chineseSimplified","abstract":"

    Simplified Chinese Language Localization

    ","parent_name":"Language"},"Structs/Localization/Language.html#/s:5Siren12LocalizationV8LanguageO18chineseTraditionalyA2EmF":{"name":"chineseTraditional","abstract":"

    Traditional Chinese Localization Localization

    ","parent_name":"Language"},"Structs/Localization/Language.html#/s:5Siren12LocalizationV8LanguageO8croatianyA2EmF":{"name":"croatian","abstract":"

    Croatian Language Localization

    ","parent_name":"Language"},"Structs/Localization/Language.html#/s:5Siren12LocalizationV8LanguageO5czechyA2EmF":{"name":"czech","abstract":"

    Czech Language Localization

    ","parent_name":"Language"},"Structs/Localization/Language.html#/s:5Siren12LocalizationV8LanguageO6danishyA2EmF":{"name":"danish","abstract":"

    Danish Language Localization

    ","parent_name":"Language"},"Structs/Localization/Language.html#/s:5Siren12LocalizationV8LanguageO5dutchyA2EmF":{"name":"dutch","abstract":"

    Dutch Language Localization

    ","parent_name":"Language"},"Structs/Localization/Language.html#/s:5Siren12LocalizationV8LanguageO7englishyA2EmF":{"name":"english","abstract":"

    English Language Localization

    ","parent_name":"Language"},"Structs/Localization/Language.html#/s:5Siren12LocalizationV8LanguageO8estonianyA2EmF":{"name":"estonian","abstract":"

    Estonian Language Localization

    ","parent_name":"Language"},"Structs/Localization/Language.html#/s:5Siren12LocalizationV8LanguageO7finnishyA2EmF":{"name":"finnish","abstract":"

    Finnish Language Localization

    ","parent_name":"Language"},"Structs/Localization/Language.html#/s:5Siren12LocalizationV8LanguageO6frenchyA2EmF":{"name":"french","abstract":"

    French Language Localization

    ","parent_name":"Language"},"Structs/Localization/Language.html#/s:5Siren12LocalizationV8LanguageO6germanyA2EmF":{"name":"german","abstract":"

    German Language Localization

    ","parent_name":"Language"},"Structs/Localization/Language.html#/s:5Siren12LocalizationV8LanguageO5greekyA2EmF":{"name":"greek","abstract":"

    Greek Language Localization

    ","parent_name":"Language"},"Structs/Localization/Language.html#/s:5Siren12LocalizationV8LanguageO6hebrewyA2EmF":{"name":"hebrew","abstract":"

    Hebrew Language Localization

    ","parent_name":"Language"},"Structs/Localization/Language.html#/s:5Siren12LocalizationV8LanguageO9hungarianyA2EmF":{"name":"hungarian","abstract":"

    Hungarian Language Localization

    ","parent_name":"Language"},"Structs/Localization/Language.html#/s:5Siren12LocalizationV8LanguageO10indonesianyA2EmF":{"name":"indonesian","abstract":"

    Indonesian Language Localization

    ","parent_name":"Language"},"Structs/Localization/Language.html#/s:5Siren12LocalizationV8LanguageO7italianyA2EmF":{"name":"italian","abstract":"

    Italian Language Localization

    ","parent_name":"Language"},"Structs/Localization/Language.html#/s:5Siren12LocalizationV8LanguageO8japaneseyA2EmF":{"name":"japanese","abstract":"

    Japanese Language Localization

    ","parent_name":"Language"},"Structs/Localization/Language.html#/s:5Siren12LocalizationV8LanguageO6koreanyA2EmF":{"name":"korean","abstract":"

    Korean Language Localization

    ","parent_name":"Language"},"Structs/Localization/Language.html#/s:5Siren12LocalizationV8LanguageO7latvianyA2EmF":{"name":"latvian","abstract":"

    Latvian Language Localization

    ","parent_name":"Language"},"Structs/Localization/Language.html#/s:5Siren12LocalizationV8LanguageO10lithuanianyA2EmF":{"name":"lithuanian","abstract":"

    Lithuanian Language Localization

    ","parent_name":"Language"},"Structs/Localization/Language.html#/s:5Siren12LocalizationV8LanguageO5malayyA2EmF":{"name":"malay","abstract":"

    Malay Language Localization

    ","parent_name":"Language"},"Structs/Localization/Language.html#/s:5Siren12LocalizationV8LanguageO9norwegianyA2EmF":{"name":"norwegian","abstract":"

    Norwegian Language Localization

    ","parent_name":"Language"},"Structs/Localization/Language.html#/s:5Siren12LocalizationV8LanguageO7persianyA2EmF":{"name":"persian","abstract":"

    Persian Language Localization

    ","parent_name":"Language"},"Structs/Localization/Language.html#/s:5Siren12LocalizationV8LanguageO18persianAfghanistanyA2EmF":{"name":"persianAfghanistan","abstract":"

    Persian (Afghanistan) Language Localization

    ","parent_name":"Language"},"Structs/Localization/Language.html#/s:5Siren12LocalizationV8LanguageO11persianIranyA2EmF":{"name":"persianIran","abstract":"

    Persian (Iran) Language Localization

    ","parent_name":"Language"},"Structs/Localization/Language.html#/s:5Siren12LocalizationV8LanguageO6polishyA2EmF":{"name":"polish","abstract":"

    Polish Language Localization

    ","parent_name":"Language"},"Structs/Localization/Language.html#/s:5Siren12LocalizationV8LanguageO16portugueseBrazilyA2EmF":{"name":"portugueseBrazil","abstract":"

    Brazilian Portuguese Language Localization

    ","parent_name":"Language"},"Structs/Localization/Language.html#/s:5Siren12LocalizationV8LanguageO18portuguesePortugalyA2EmF":{"name":"portuguesePortugal","abstract":"

    Portugal’s Portuguese Language Localization

    ","parent_name":"Language"},"Structs/Localization/Language.html#/s:5Siren12LocalizationV8LanguageO7russianyA2EmF":{"name":"russian","abstract":"

    Russian Language Localization

    ","parent_name":"Language"},"Structs/Localization/Language.html#/s:5Siren12LocalizationV8LanguageO15serbianCyrillicyA2EmF":{"name":"serbianCyrillic","abstract":"

    Serbian (Cyrillic) Language Localization

    ","parent_name":"Language"},"Structs/Localization/Language.html#/s:5Siren12LocalizationV8LanguageO12serbianLatinyA2EmF":{"name":"serbianLatin","abstract":"

    Serbian (Latin) Language Localization

    ","parent_name":"Language"},"Structs/Localization/Language.html#/s:5Siren12LocalizationV8LanguageO9slovenianyA2EmF":{"name":"slovenian","abstract":"

    Slovenian Language Localization

    ","parent_name":"Language"},"Structs/Localization/Language.html#/s:5Siren12LocalizationV8LanguageO7spanishyA2EmF":{"name":"spanish","abstract":"

    Spanish Language Localization

    ","parent_name":"Language"},"Structs/Localization/Language.html#/s:5Siren12LocalizationV8LanguageO7swedishyA2EmF":{"name":"swedish","abstract":"

    Swedish Language Localization

    ","parent_name":"Language"},"Structs/Localization/Language.html#/s:5Siren12LocalizationV8LanguageO4thaiyA2EmF":{"name":"thai","abstract":"

    Thai Language Localization

    ","parent_name":"Language"},"Structs/Localization/Language.html#/s:5Siren12LocalizationV8LanguageO7turkishyA2EmF":{"name":"turkish","abstract":"

    Turkish Language Localization

    ","parent_name":"Language"},"Structs/Localization/Language.html#/s:5Siren12LocalizationV8LanguageO4urduyA2EmF":{"name":"urdu","abstract":"

    Urdu Language Localization

    ","parent_name":"Language"},"Structs/Localization/Language.html#/s:5Siren12LocalizationV8LanguageO9ukrainianyA2EmF":{"name":"ukrainian","abstract":"

    Ukranian Language Localization

    ","parent_name":"Language"},"Structs/Localization/Language.html#/s:5Siren12LocalizationV8LanguageO10vietnameseyA2EmF":{"name":"vietnamese","abstract":"

    Vietnamese Language Localization

    ","parent_name":"Language"},"Structs/Localization/Language.html":{"name":"Language","abstract":"

    Determines the available languages in which the update message and alert button titles should appear.

    ","parent_name":"Localization"},"Structs/Localization.html#/s:5Siren12LocalizationV7appName33_2FDBEF65899237DA36B11FA5846AD0EALLSSvp":{"name":"appName","abstract":"

    The name of the app as defined by the Info.plist.

    ","parent_name":"Localization"},"Structs/Localization.html#/s:5Siren12LocalizationV13forceLanguage33_2FDBEF65899237DA36B11FA5846AD0EALLAC0D0OSgvp":{"name":"forceLanguage","abstract":"

    Overrides the default localization of a user’s device when presenting the update message and button titles in the alert.

    ","parent_name":"Localization"},"Structs/Localization.html#/s:5Siren12LocalizationV7appName016andForceLanguageB0ACSSSg_AC0G0OSgtcfc":{"name":"init(appName:andForceLanguageLocalization:)","abstract":"

    Initializes

    ","parent_name":"Localization"},"Structs/Localization.html#/s:5Siren12LocalizationV12alertMessage25forCurrentAppStoreVersionS2S_tF":{"name":"alertMessage(forCurrentAppStoreVersion:)","abstract":"

    The localized string for the UIAlertController‘s message field. .

    ","parent_name":"Localization"},"Structs/Localization.html#/s:5Siren12LocalizationV10alertTitleSSyF":{"name":"alertTitle()","abstract":"

    The localized string for the UIAlertController‘s title field. .

    ","parent_name":"Localization"},"Structs/Localization.html#/s:5Siren12LocalizationV19nextTimeButtonTitleSSyF":{"name":"nextTimeButtonTitle()","abstract":"

    The localized string for the Next time UIAlertAction.

    ","parent_name":"Localization"},"Structs/Localization.html#/s:5Siren12LocalizationV15skipButtonTitleSSyF":{"name":"skipButtonTitle()","abstract":"

    The localized string for the Skip this version UIAlertAction.

    ","parent_name":"Localization"},"Structs/Localization.html#/s:5Siren12LocalizationV17updateButtonTitleSSyF":{"name":"updateButtonTitle()","abstract":"

    The localized string for the Update UIAlertAction.

    ","parent_name":"Localization"},"Structs/AlertConstants.html#/s:5Siren14AlertConstantsV12alertMessageSSvpZ":{"name":"alertMessage","abstract":"

    The text that conveys the message that there is an app update available

    ","parent_name":"AlertConstants"},"Structs/AlertConstants.html#/s:5Siren14AlertConstantsV10alertTitleSSvpZ":{"name":"alertTitle","abstract":"

    The alert title which defaults to Update Available.

    ","parent_name":"AlertConstants"},"Structs/AlertConstants.html#/s:5Siren14AlertConstantsV19nextTimeButtonTitleSSvpZ":{"name":"nextTimeButtonTitle","abstract":"

    The button text that conveys the message that the user should be prompted to update next time the app launches.

    ","parent_name":"AlertConstants"},"Structs/AlertConstants.html#/s:5Siren14AlertConstantsV15skipButtonTitleSSvpZ":{"name":"skipButtonTitle","abstract":"

    The text that conveys the message that the the user wants to skip this verison update.

    ","parent_name":"AlertConstants"},"Structs/AlertConstants.html#/s:5Siren14AlertConstantsV17updateButtonTitleSSvpZ":{"name":"updateButtonTitle","abstract":"

    The button text that conveys the message that the user would like to update the app right away.

    ","parent_name":"AlertConstants"},"Structs/RulesManager/UpdateType.html#/s:5Siren12RulesManagerV10UpdateTypeO5majoryA2EmF":{"name":"major","abstract":"

    Major release available: A.b.c.d

    ","parent_name":"UpdateType"},"Structs/RulesManager/UpdateType.html#/s:5Siren12RulesManagerV10UpdateTypeO5minoryA2EmF":{"name":"minor","abstract":"

    Minor release available: a.B.c.d

    ","parent_name":"UpdateType"},"Structs/RulesManager/UpdateType.html#/s:5Siren12RulesManagerV10UpdateTypeO5patchyA2EmF":{"name":"patch","abstract":"

    Patch release available: a.b.C.d

    ","parent_name":"UpdateType"},"Structs/RulesManager/UpdateType.html#/s:5Siren12RulesManagerV10UpdateTypeO8revisionyA2EmF":{"name":"revision","abstract":"

    Revision release available: a.b.c.D

    ","parent_name":"UpdateType"},"Structs/RulesManager/UpdateType.html#/s:5Siren12RulesManagerV10UpdateTypeO7unknownyA2EmF":{"name":"unknown","abstract":"

    No information available about the update.

    ","parent_name":"UpdateType"},"Structs/RulesManager.html#/s:5Siren12RulesManagerV15releasedForDaysSivp":{"name":"releasedForDays","abstract":"

    The alert will only show up if the current version has already been released for X days.

    ","parent_name":"RulesManager"},"Structs/RulesManager.html#/s:5Siren12RulesManagerV011majorUpdateB0AA0B0Vvp":{"name":"majorUpdateRules","abstract":"

    The Rules that should be used when the App Store version of the app signifies that it is a major version update (A.b.c.d).

    ","parent_name":"RulesManager"},"Structs/RulesManager.html#/s:5Siren12RulesManagerV011minorUpdateB0AA0B0Vvp":{"name":"minorUpdateRules","abstract":"

    The Rules that should be used when the App Store version of the app signifies that it is a minor version update (a.B.c.d).

    ","parent_name":"RulesManager"},"Structs/RulesManager.html#/s:5Siren12RulesManagerV011patchUpdateB0AA0B0Vvp":{"name":"patchUpdateRules","abstract":"

    The Rules that should be used when the App Store version of the app signifies that it is a patch version update (a.b.C.d).

    ","parent_name":"RulesManager"},"Structs/RulesManager.html#/s:5Siren12RulesManagerV014revisionUpdateB0AA0B0Vvp":{"name":"revisionUpdateRules","abstract":"

    The Rules that should be used when the App Store version of the app signifies that it is a revision version update (a.b.c.D).

    ","parent_name":"RulesManager"},"Structs/RulesManager.html#/s:5Siren12RulesManagerV011majorUpdateB005minoreB005patcheB008revisioneB050showAlertAfterCurrentVersionHasBeenReleasedForDaysAcA0B0V_A3JSitcfc":{"name":"init(majorUpdateRules:minorUpdateRules:patchUpdateRules:revisionUpdateRules:showAlertAfterCurrentVersionHasBeenReleasedForDays:)","abstract":"

    Initializer that sets update-specific Rules for all updates (e.g., major, minor, patch, revision).","parent_name":"RulesManager"},"Structs/RulesManager.html#/s:5Siren12RulesManagerV06globalB050showAlertAfterCurrentVersionHasBeenReleasedForDaysAcA0B0V_Sitcfc":{"name":"init(globalRules:showAlertAfterCurrentVersionHasBeenReleasedForDays:)","abstract":"

    Initializer that sets the same update Rules for all types of updates (e.g., major, minor, patch, revision).","parent_name":"RulesManager"},"Structs/RulesManager.html#/s:5Siren12RulesManagerV04loadB13ForUpdateTypeyAA0B0VAC0fG0OF":{"name":"loadRulesForUpdateType(_:)","abstract":"

    Returns the appropriate update rules based on the type of version that is returned from the API.

    ","parent_name":"RulesManager"},"Structs/RulesManager.html#/s:5Siren12RulesManagerV7defaultACvpZ":{"name":"default","abstract":"

    The default RulesManager.

    ","parent_name":"RulesManager"},"Structs/RulesManager/UpdateType.html":{"name":"UpdateType","abstract":"

    Informs Siren of the type of update that is available so that","parent_name":"RulesManager"},"Structs/PresentationManager.html#/s:5Siren19PresentationManagerV17CompletionHandlera":{"name":"CompletionHandler","abstract":"

    Return results or errors obtained from performing a version check with Siren.

    ","parent_name":"PresentationManager"},"Structs/PresentationManager.html#/s:5Siren19PresentationManagerV12localizationAA12LocalizationVvp":{"name":"localization","abstract":"

    The localization data structure that will be used to construct localized strings for the update alert.

    ","parent_name":"PresentationManager"},"Structs/PresentationManager.html#/s:5Siren19PresentationManagerV9tintColorSo7UIColorCSgvp":{"name":"tintColor","abstract":"

    The tint color of the UIAlertController buttons.

    ","parent_name":"PresentationManager"},"Structs/PresentationManager.html#/s:5Siren19PresentationManagerV12alertMessageSSvp":{"name":"alertMessage","abstract":"

    The descriptive update message of the UIAlertController.

    ","parent_name":"PresentationManager"},"Structs/PresentationManager.html#/s:5Siren19PresentationManagerV10alertTitleSSvp":{"name":"alertTitle","abstract":"

    The main message of the UIAlertController.

    ","parent_name":"PresentationManager"},"Structs/PresentationManager.html#/s:5Siren19PresentationManagerV19nextTimeButtonTitleSSvp":{"name":"nextTimeButtonTitle","abstract":"

    The Next time button text of the UIAlertController.

    ","parent_name":"PresentationManager"},"Structs/PresentationManager.html#/s:5Siren19PresentationManagerV15skipButtonTitleSSvp":{"name":"skipButtonTitle","abstract":"

    The Skip this version button text of the UIAlertController.

    ","parent_name":"PresentationManager"},"Structs/PresentationManager.html#/s:5Siren19PresentationManagerV17updateButtonTitleSSvp":{"name":"updateButtonTitle","abstract":"

    The Update button text of the UIAlertController.

    ","parent_name":"PresentationManager"},"Structs/PresentationManager.html#/s:5Siren19PresentationManagerV15alertController33_CEF2109017F934DAB33AED8753BA096CLLSo07UIAlertE0CSgvp":{"name":"alertController","abstract":"

    The instance of the UIAlertController used to present the update alert.

    ","parent_name":"PresentationManager"},"Structs/PresentationManager.html#/s:5Siren19PresentationManagerV13updaterWindow33_CEF2109017F934DAB33AED8753BA096CLLSo8UIWindowCvp":{"name":"updaterWindow","abstract":"

    The UIWindow instance that presents the SirenViewController.

    ","parent_name":"PresentationManager"},"Structs/PresentationManager.html#/s:5Siren19PresentationManagerV14alertTintColor7appName0D5Title0D7Message012updateButtonI008nextTimelI004skiplI025forceLanguageLocalizationACSo7UIColorCSg_SSSgS5SAA0R0V0Q0OSgtcfc":{"name":"init(alertTintColor:appName:alertTitle:alertMessage:updateButtonTitle:nextTimeButtonTitle:skipButtonTitle:forceLanguageLocalization:)","abstract":"

    PresentationManager‘s public initializer.

    ","parent_name":"PresentationManager"},"Structs/PresentationManager.html#/s:5Siren19PresentationManagerV7defaultACvpZ":{"name":"default","abstract":"

    The default PresentationManager.

    ","parent_name":"PresentationManager"},"Structs/PresentationManager.html#/s:5Siren19PresentationManagerV12presentAlert9withRules25forCurrentAppStoreVersion10completionyAA0G0V_SSyAA0E6ActionOcSgtF":{"name":"presentAlert(withRules:forCurrentAppStoreVersion:completion:)","abstract":"

    Constructs the localized update alert UIAlertController object.

    ","parent_name":"PresentationManager"},"Structs/PresentationManager.html#/s:5Siren19PresentationManagerV17updateAlertAction33_CEF2109017F934DAB33AED8753BA096CLL10completionSo07UIAlertF0CyAA0eF0OcSg_tF":{"name":"updateAlertAction(completion:)","abstract":"

    The UIAlertAction that is executed when the Update option is selected.

    ","parent_name":"PresentationManager"},"Structs/PresentationManager.html#/s:5Siren19PresentationManagerV19nextTimeAlertAction33_CEF2109017F934DAB33AED8753BA096CLL10completionSo07UIAlertG0CyAA0fG0OcSg_tF":{"name":"nextTimeAlertAction(completion:)","abstract":"

    The UIAlertAction that is executed when the Next time option is selected.

    ","parent_name":"PresentationManager"},"Structs/PresentationManager.html#/s:5Siren19PresentationManagerV15skipAlertAction33_CEF2109017F934DAB33AED8753BA096CLL25forCurrentAppStoreVersion10completionSo07UIAlertF0CSS_yAA0eF0OcSgtF":{"name":"skipAlertAction(forCurrentAppStoreVersion:completion:)","abstract":"

    The UIAlertAction that is executed when the Skip this version option is selected.

    ","parent_name":"PresentationManager"},"Structs/APIManager/Constants.html#/s:5Siren10APIManagerV9Constants33_8071139324B24E2065F4037045A8D960LLV8bundleIDSSvpZ":{"name":"bundleID","abstract":"

    Constant for the bundleId parameter in the iTunes Lookup API request.

    ","parent_name":"Constants"},"Structs/APIManager/Constants.html#/s:5Siren10APIManagerV9Constants33_8071139324B24E2065F4037045A8D960LLV7countrySSvpZ":{"name":"country","abstract":"

    Constant for the country parameter in the iTunes Lookup API request.

    ","parent_name":"Constants"},"Structs/APIManager/Constants.html":{"name":"Constants","abstract":"

    Constants used in the APIManager.

    ","parent_name":"APIManager"},"Structs/APIManager.html#/s:5Siren10APIManagerV17CompletionHandlera":{"name":"CompletionHandler","abstract":"

    Return results or errors obtained from performing a version check with Siren.

    ","parent_name":"APIManager"},"Structs/APIManager.html#/s:5Siren10APIManagerV11countryCodeSSSgvp":{"name":"countryCode","abstract":"

    The region or country of an App Store in which the app is available.","parent_name":"APIManager"},"Structs/APIManager.html#/s:5Siren10APIManagerV11countryCodeACSSSg_tcfc":{"name":"init(countryCode:)","abstract":"

    Initializes APIManager to the region or country of an App Store in which the app is available.","parent_name":"APIManager"},"Structs/APIManager.html#/s:5Siren10APIManagerV7defaultACvpZ":{"name":"default","abstract":"

    The default APIManager.

    ","parent_name":"APIManager"},"Structs/APIManager.html#/s:5Siren10APIManagerV26performVersionCheckRequest10completionyyAA11LookupModelVSg_AA10KnownErrorOSgtcSg_tF":{"name":"performVersionCheckRequest(completion:)","abstract":"

    Creates and performs a URLRequest against the iTunes Lookup API.

    ","parent_name":"APIManager"},"Structs/APIManager.html#/s:5Siren10APIManagerV26processVersionCheckResults33_8071139324B24E2065F4037045A8D960LL8withData8response5error10completiony10Foundation0M0VSg_So13NSURLResponseCSgs5Error_pSgyAA11LookupModelVSg_AA05KnownS0OSgtcSgtF":{"name":"processVersionCheckResults(withData:response:error:completion:)","abstract":"

    Parses and maps the the results from the iTunes Lookup API request.

    ","parent_name":"APIManager"},"Structs/APIManager.html#/s:5Siren10APIManagerV13makeITunesURL33_8071139324B24E2065F4037045A8D960LL10Foundation0E0VyKF":{"name":"makeITunesURL()","abstract":"

    Creates the URL that points to the iTunes Lookup API.

    ","parent_name":"APIManager"},"Structs/APIManager.html":{"name":"APIManager","abstract":"

    APIManager for Siren

    "},"Structs/PresentationManager.html":{"name":"PresentationManager","abstract":"

    PresentationManager for Siren

    "},"Structs/RulesManager.html":{"name":"RulesManager","abstract":"

    RulesManager for Siren

    "},"Structs/AlertConstants.html":{"name":"AlertConstants","abstract":"

    The default constants used for the update alert’s messaging.

    "},"Structs/Localization.html":{"name":"Localization","abstract":"

    Localization information and strings for Siren.

    "},"Structs/LookupModel.html":{"name":"LookupModel","abstract":"

    Model representing a selection of results from the iTunes Lookup API.

    "},"Structs/Results.html":{"name":"Results","abstract":"

    The relevant metadata returned from Siren upon completing a successful version check.

    "},"Structs/Rules.html":{"name":"Rules","abstract":"

    Alert Presentation Rules for Siren.

    "},"Structs/DataParser.html":{"name":"DataParser","abstract":"

    Version parsing functions for Siren.

    "},"Extensions/UserDefaults/SirenKeys.html#/s:So14NSUserDefaultsC5SirenE0C4Keys33_0884631E60E090AA276701A736B793BBLLO37PerformVersionCheckOnSubsequentLaunchyA2FmF":{"name":"PerformVersionCheckOnSubsequentLaunch","abstract":"

    Key that notifies Siren to perform a version check and present","parent_name":"SirenKeys"},"Extensions/UserDefaults/SirenKeys.html#/s:So14NSUserDefaultsC5SirenE0C4Keys33_0884631E60E090AA276701A736B793BBLLO22StoredVersionCheckDateyA2FmF":{"name":"StoredVersionCheckDate","abstract":"

    Key that stores the timestamp of the last version check.

    ","parent_name":"SirenKeys"},"Extensions/UserDefaults/SirenKeys.html#/s:So14NSUserDefaultsC5SirenE0C4Keys33_0884631E60E090AA276701A736B793BBLLO20StoredSkippedVersionyA2FmF":{"name":"StoredSkippedVersion","abstract":"

    Key that stores the version that a user decided to skip.

    ","parent_name":"SirenKeys"},"Extensions/UserDefaults/SirenKeys.html":{"name":"SirenKeys","abstract":"

    Siren-specific UserDefaults Keys

    ","parent_name":"UserDefaults"},"Extensions/UserDefaults.html#/s:So14NSUserDefaultsC5SirenE43shouldPerformVersionCheckOnSubsequentLaunchSbvpZ":{"name":"shouldPerformVersionCheckOnSubsequentLaunch","abstract":"

    Sets and Gets a UserDefault around performing a version check on a subsequent launch.

    ","parent_name":"UserDefaults"},"Extensions/UserDefaults.html#/s:So14NSUserDefaultsC5SirenE20storedSkippedVersionSSSgvpZ":{"name":"storedSkippedVersion","abstract":"

    Sets and Gets a UserDefault around storing a version that the user wants to skip updating.

    ","parent_name":"UserDefaults"},"Extensions/UserDefaults.html#/s:So14NSUserDefaultsC5SirenE21alertPresentationDate10Foundation0F0VSgvpZ":{"name":"alertPresentationDate","abstract":"

    Sets and Gets a UserDefault around the last time the user was presented a version update alert.

    ","parent_name":"UserDefaults"},"Extensions/UIAlertController.html#/s:So17UIAlertControllerC5SirenE4show6windowySo8UIWindowC_tF":{"name":"show(window:)","abstract":"

    Presents Siren’s UIAlertController in a new UIWindow.

    ","parent_name":"UIAlertController"},"Extensions/UIAlertController.html#/s:So17UIAlertControllerC5SirenE4hide6windowySo8UIWindowC_tF":{"name":"hide(window:)","abstract":"

    Hides Siren’s UIAlertController within a given window.

    ","parent_name":"UIAlertController"},"Extensions/Date.html#/s:10Foundation4DateV5SirenE4days5sinceSiAC_tFZ":{"name":"days(since:)","abstract":"

    The amount of days passed from a specific source date.

    ","parent_name":"Date"},"Extensions/Date.html#/s:10Foundation4DateV5SirenE4days5sinceSiSgSS_tFZ":{"name":"days(since:)","abstract":"

    The amount of days passed from a specific source date string.

    ","parent_name":"Date"},"Extensions/Bundle/Constants.html#/s:So8NSBundleC5SirenE9ConstantsV15bundleExtensionSSvpZ":{"name":"bundleExtension","abstract":"

    Constant for the .bundle file extension.

    ","parent_name":"Constants"},"Extensions/Bundle/Constants.html#/s:So8NSBundleC5SirenE9ConstantsV11displayNameSSvpZ":{"name":"displayName","abstract":"

    Constant for CFBundleDisplayName.

    ","parent_name":"Constants"},"Extensions/Bundle/Constants.html#/s:So8NSBundleC5SirenE9ConstantsV19englishLocalizationSSvpZ":{"name":"englishLocalization","abstract":"

    Constant for the default US English localization.

    ","parent_name":"Constants"},"Extensions/Bundle/Constants.html#/s:So8NSBundleC5SirenE9ConstantsV16projectExtensionSSvpZ":{"name":"projectExtension","abstract":"

    Constant for the project file extension.

    ","parent_name":"Constants"},"Extensions/Bundle/Constants.html#/s:So8NSBundleC5SirenE9ConstantsV18shortVersionStringSSvpZ":{"name":"shortVersionString","abstract":"

    Constant for CFBundleShortVersionString.

    ","parent_name":"Constants"},"Extensions/Bundle/Constants.html#/s:So8NSBundleC5SirenE9ConstantsV5tableSSvpZ":{"name":"table","abstract":"

    Constant for the localization table.

    ","parent_name":"Constants"},"Extensions/Bundle/Constants.html":{"name":"Constants","abstract":"

    Constants used in the Bundle extension.

    ","parent_name":"Bundle"},"Extensions/Bundle.html#/s:So8NSBundleC5SirenE7versionSSSgyFZ":{"name":"version()","abstract":"

    Fetches the current verison of the app.

    ","parent_name":"Bundle"},"Extensions/Bundle.html#/s:So8NSBundleC5SirenE15localizedString6forKey20andForceLocalizationS2S_AC0I0V8LanguageOSgtFZ":{"name":"localizedString(forKey:andForceLocalization:)","abstract":"

    Returns the localized string for a given default string.

    ","parent_name":"Bundle"},"Extensions/Bundle.html#/s:So8NSBundleC5SirenE19bestMatchingAppNameSSyFZ":{"name":"bestMatchingAppName()","abstract":"

    The appropriate name for the app to be displayed in the update alert.

    ","parent_name":"Bundle"},"Extensions/Bundle.html#/s:So8NSBundleC5SirenE15sirenBundlePath33_9C775CF4CEC7A5F21E625F58C71BDF22LLSSSgyFZ":{"name":"sirenBundlePath()","abstract":"

    The path to Siren’s localization Bundle.

    ","parent_name":"Bundle"},"Extensions/Bundle.html#/s:So8NSBundleC5SirenE21sirenForcedBundlePath33_9C775CF4CEC7A5F21E625F58C71BDF22LL25forceLanguageLocalizationSSSgAC0R0V0Q0O_tFZ":{"name":"sirenForcedBundlePath(forceLanguageLocalization:)","abstract":"

    The path for a particular language localizationin Siren’s localization Bundle.

    ","parent_name":"Bundle"},"Extensions/Bundle.html#/s:So8NSBundleC5SirenE14deviceLanguage33_9C775CF4CEC7A5F21E625F58C71BDF22LLAC12LocalizationV0D0OSgyFZ":{"name":"deviceLanguage()","abstract":"

    The user’s preferred language based on their device’s localization.

    ","parent_name":"Bundle"},"Extensions/Bundle.html":{"name":"Bundle"},"Extensions/Date.html":{"name":"Date"},"Extensions/UIAlertController.html":{"name":"UIAlertController"},"Extensions/UserDefaults.html":{"name":"UserDefaults"},"Enums/KnownError.html#/s:5Siren10KnownErrorO20appStoreAppIDFailureyA2CmF":{"name":"appStoreAppIDFailure","abstract":"

    Error retrieving trackId as the JSON does not contain a ‘trackId’ key.

    ","parent_name":"KnownError"},"Enums/KnownError.html#/s:5Siren10KnownErrorO33appStoreDataRetrievalEmptyResultsyA2CmF":{"name":"appStoreDataRetrievalEmptyResults","abstract":"

    Error retrieving App Store data as JSON results were empty. Is your app available in the US? If not, change the countryCode variable to fix this error.

    ","parent_name":"KnownError"},"Enums/KnownError.html#/s:5Siren10KnownErrorO28appStoreDataRetrievalFailureyACs0C0_pSg_tcACmF":{"name":"appStoreDataRetrievalFailure(underlyingError:)","abstract":"

    Error retrieving App Store data as an error was returned.

    ","parent_name":"KnownError"},"Enums/KnownError.html#/s:5Siren10KnownErrorO26appStoreJSONParsingFailureyACs0C0_p_tcACmF":{"name":"appStoreJSONParsingFailure(underlyingError:)","abstract":"

    Error parsing App Store JSON data.

    ","parent_name":"KnownError"},"Enums/KnownError.html#/s:5Siren10KnownErrorO28appStoreOSVersionUnsupportedyA2CmF":{"name":"appStoreOSVersionUnsupported","abstract":"

    The version of iOS on the device is lower than that of the one required by the app verison update.

    ","parent_name":"KnownError"},"Enums/KnownError.html#/s:5Siren10KnownErrorO27appStoreVersionArrayFailureyA2CmF":{"name":"appStoreVersionArrayFailure","abstract":"

    Error retrieving App Store verson number as the JSON does not contain a version key.

    ","parent_name":"KnownError"},"Enums/KnownError.html#/s:5Siren10KnownErrorO25currentVersionReleaseDateyA2CmF":{"name":"currentVersionReleaseDate","abstract":"

    The currentVersionReleaseDate key is missing in the JSON payload. Please leave an issue on https://github.com/ArtSabintsev/Siren with as many details as possible.

    ","parent_name":"KnownError"},"Enums/KnownError.html#/s:5Siren10KnownErrorO12malformedURLyA2CmF":{"name":"malformedURL","abstract":"

    One of the iTunes URLs used in Siren is malformed. Please leave an issue on https://github.com/ArtSabintsev/Siren with as many details as possible.

    ","parent_name":"KnownError"},"Enums/KnownError.html#/s:5Siren10KnownErrorO15missingBundleIDyA2CmF":{"name":"missingBundleID","abstract":"

    Please make sure that you have set a Bundle Identifier in your project.

    ","parent_name":"KnownError"},"Enums/KnownError.html#/s:5Siren10KnownErrorO17noUpdateAvailableyA2CmF":{"name":"noUpdateAvailable","abstract":"

    No new update available.

    ","parent_name":"KnownError"},"Enums/KnownError.html#/s:5Siren10KnownErrorO16recentlyPromptedyA2CmF":{"name":"recentlyPrompted","abstract":"

    Siren will not present an update alert if it performed one too recently. If you would like to present an alert every time Siren is called, please consider setting the UpdatePromptFrequency.immediately rule in RulesManager

    ","parent_name":"KnownError"},"Enums/KnownError.html#/s:5Siren10KnownErrorO15releasedTooSoonyACSi_SitcACmF":{"name":"releasedTooSoon(daysSinceRelease:releasedForDays:)","abstract":"

    The app has been released for X days, but Siren cannot prompt the user until Y (where Y > X) days have passed.

    ","parent_name":"KnownError"},"Enums/KnownError.html#/s:5Siren10KnownErrorO17skipVersionUpdateyACSS_SStcACmF":{"name":"skipVersionUpdate(installedVersion:appStoreVersion:)","abstract":"

    The user has opted to skip updating their current version of the app to the current App Store version.

    ","parent_name":"KnownError"},"Enums/KnownError.html#/s:5Siren10KnownErrorO20localizedDescriptionSSvp":{"name":"localizedDescription","abstract":"

    The localized description for each error handled by Siren.

    ","parent_name":"KnownError"},"Enums/KnownError.html#/s:5Siren10KnownErrorO05sirenC033_B3C911EAD28C83CC211C07566B0F499ALLSSvpZ":{"name":"sirenError","abstract":"

    An easily identifiable prefix for all errors thrown by Siren.

    ","parent_name":"KnownError"},"Enums/AlertAction.html#/s:5Siren11AlertActionO8appStoreyA2CmF":{"name":"appStore","abstract":"

    The user clicked on the Update option, which took them to the app’s App Store page.

    ","parent_name":"AlertAction"},"Enums/AlertAction.html#/s:5Siren11AlertActionO8nextTimeyA2CmF":{"name":"nextTime","abstract":"

    The user clicked on the Next Time option, which dismissed the alert.

    ","parent_name":"AlertAction"},"Enums/AlertAction.html#/s:5Siren11AlertActionO4skipyA2CmF":{"name":"skip","abstract":"

    The user clicked on the Skip this version option, which dismissed the alert.

    ","parent_name":"AlertAction"},"Enums/AlertAction.html#/s:5Siren11AlertActionO7unknownyA2CmF":{"name":"unknown","abstract":"

    (Default) The user never chose an option. This is returned when an error is thrown by Siren.

    ","parent_name":"AlertAction"},"Enums/AlertAction.html":{"name":"AlertAction","abstract":"

    The UIAlertController button that was pressed upon being presented an update alert.

    "},"Enums/KnownError.html":{"name":"KnownError","abstract":"

    Enumerates all potentials errors that Siren can handle.

    "},"Classes/SirenViewController.html#/c:@M@Siren@objc(cs)SirenViewController(py)preferredStatusBarStyle":{"name":"preferredStatusBarStyle","abstract":"

    UIStatusBarStyle override.

    ","parent_name":"SirenViewController"},"Classes/Siren.html#/s:5SirenAAC14ResultsHandlera":{"name":"ResultsHandler","abstract":"

    Return results or errors obtained from performing a version check with Siren.

    ","parent_name":"Siren"},"Classes/Siren.html#/s:5SirenAAC6sharedABvpZ":{"name":"shared","abstract":"

    The Siren singleton. The main point of entry to the Siren library.

    ","parent_name":"Siren"},"Classes/Siren.html#/s:5SirenAAC12debugEnabledSbvp":{"name":"debugEnabled","abstract":"

    The debug flag, which is disabled by default.","parent_name":"Siren"},"Classes/Siren.html#/s:5SirenAAC10apiManagerAA10APIManagerVvp":{"name":"apiManager","abstract":"

    The manager that controls the App Store API that is","parent_name":"Siren"},"Classes/Siren.html#/s:5SirenAAC19presentationManagerAA012PresentationC0Vvp":{"name":"presentationManager","abstract":"

    The manager that controls the update alert’s string localization and tint color.

    ","parent_name":"Siren"},"Classes/Siren.html#/s:5SirenAAC12rulesManagerAA05RulesC0Vvp":{"name":"rulesManager","abstract":"

    The manager that controls the type of alert that should be displayed","parent_name":"Siren"},"Classes/Siren.html#/s:5SirenAAC23currentInstalledVersionSSSgvp":{"name":"currentInstalledVersion","abstract":"

    The current installed version of your app.

    ","parent_name":"Siren"},"Classes/Siren.html#/s:5SirenAAC23didBecomeActiveObserverSo8NSObject_pSgvp":{"name":"didBecomeActiveObserver","abstract":"

    The retained NotificationCenter observer that listens for UIApplication.didBecomeActiveNotification notifications.

    ","parent_name":"Siren"},"Classes/Siren.html#/s:5SirenAAC21alertPresentationDate33_7DFB1BC200A6C64FBED860A3A8153B65LL10Foundation0D0VSgvp":{"name":"alertPresentationDate","abstract":"

    The last date that an alert was presented to the user.

    ","parent_name":"Siren"},"Classes/Siren.html#/s:5SirenAAC5appID33_7DFB1BC200A6C64FBED860A3A8153B65LLSiSgvp":{"name":"appID","abstract":"

    The App Store’s unique identifier for an app.

    ","parent_name":"Siren"},"Classes/Siren.html#/s:5SirenAAC14resultsHandler33_7DFB1BC200A6C64FBED860A3A8153B65LLyAA7ResultsVSg_AA10KnownErrorOSgtcSgvp":{"name":"resultsHandler","abstract":"

    The completion handler used to return the results or errors returned by Siren.

    ","parent_name":"Siren"},"Classes/Siren.html#/c:@M@Siren@objc(cs)Siren(im)init":{"name":"init()","abstract":"

    The initialization method.

    ","parent_name":"Siren"},"Classes/Siren.html#/s:5SirenAAC4wail10completionyyAA7ResultsVSg_AA10KnownErrorOSgtcSg_tF":{"name":"wail(completion:)","parent_name":"Siren"},"Classes/Siren.html#/s:5SirenAAC14launchAppStoreyyF":{"name":"launchAppStore()","abstract":"

    Launches the AppStore in two situations when the user clicked the Update button in the UIAlertController modal.

    ","parent_name":"Siren"},"Classes/Siren.html#/s:5SirenAAC19performVersionCheckyyF":{"name":"performVersionCheck()","abstract":"

    Initiates the uni-directional version checking flow.

    ","parent_name":"Siren"},"Classes/Siren.html#/s:5SirenAAC8validate5modelyAA11LookupModelV_tF":{"name":"validate(model:)","abstract":"

    Validates the parsed and mapped iTunes Lookup Model","parent_name":"Siren"},"Classes/Siren.html#/s:5SirenAAC45determineIfAlertPresentationRulesAreSatisfied25forCurrentAppStoreVersion14andLookupModelySS_AA0oP0VtF":{"name":"determineIfAlertPresentationRulesAreSatisfied(forCurrentAppStoreVersion:andLookupModel:)","abstract":"

    Determines if the update alert can be presented based on the","parent_name":"Siren"},"Classes/Siren.html#/s:5SirenAAC12presentAlert9withRules25forCurrentAppStoreVersion5model13andUpdateTypeyAA0E0V_SSAA11LookupModelVAA0E7ManagerV0mN0OtF":{"name":"presentAlert(withRules:forCurrentAppStoreVersion:model:andUpdateType:)","abstract":"

    Presents the update alert to the end user.","parent_name":"Siren"},"Classes/Siren.html#/s:5SirenAAC12addObserversyyF":{"name":"addObservers()","abstract":"

    Add an observer that listens for app launching/relaunching","parent_name":"Siren"},"Classes/Siren.html":{"name":"Siren","abstract":"

    The Siren Class.

    "},"Classes/SirenViewController.html":{"name":"SirenViewController","abstract":"

    UIViewController Extension for Siren

    "},"Classes.html":{"name":"Classes","abstract":"

    The following classes are available globally.

    "},"Enums.html":{"name":"Enumerations","abstract":"

    The following enumerations are available globally.

    "},"Extensions.html":{"name":"Extensions","abstract":"

    The following extensions are available globally.

    "},"Structs.html":{"name":"Structures","abstract":"

    The following structures are available globally.

    "}} \ No newline at end of file diff --git a/docs/docsets/Siren.docset/Contents/Resources/docSet.dsidx b/docs/docsets/Siren.docset/Contents/Resources/docSet.dsidx index fe48f3d3597867432d5f974775bfe663e0c45068..0faea670431cc875d7b715e4786125e2fed52d33 100644 GIT binary patch literal 73728 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 Mab%feLb<#CKfS|!q5uE@ literal 36864 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 E1Hx5}q5uE@ diff --git a/docs/docsets/Siren.tgz b/docs/docsets/Siren.tgz index 8a4a7eb06c995af0c6d5ff513a6c095dea1ad203..03d21a019725439c9d12799be2f6dfca67179c06 100644 GIT binary patch literal 115721 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

    ;X{Px^C61Umv&dMkp3~t%= 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 Ad;kCd literal 72982 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 k79(m_e{F^)*|8+`+T!EJ*71Il6mH~;_u diff --git a/docs/index.html b/docs/index.html index 86a7956d..8a84d714 100644 --- a/docs/index.html +++ b/docs/index.html @@ -33,13 +33,7 @@ Siren

    - - @@ -47,15 +41,33 @@ Enumerations @@ -63,22 +75,52 @@ Structures @@ -101,16 +143,15 @@

    Table of Contents

  • Features
  • Screenshots
  • Installation Instructions
  • -
  • Example Code
  • -
  • Granular/Differentiated Version Management
  • -
  • Delegates (Optional)
  • +
  • Implementation Examples
  • Localization
  • Device Compatibility
  • Testing Siren
  • App Store Review & Submissions
  • -
  • Phrased Releases
  • +
  • Phased Releases
  • Words of Caution
  • Ports
  • +
  • Shout-Out and Gratitude
  • Attribution
  • @@ -125,28 +166,27 @@

    About

  • Siren is built to work with the Semantic Versioning system.
      -
    • Semantic Versioning is a three number versioning system (e.g., 1.0.0)
    • +
    • Canonical Semantic Versioning uses a three number versioning system (e.g., 1.0.0)
    • Siren also supports two-number versioning (e.g., 1.0) and four-number versioning (e.g., 1.0.0.0)
  • -
  • Siren is actively maintained by Arthur Sabintsev and Aaron Brager
  • -

    README Translations

    +

    Features

    +

    Current Features

    -

    Features

    +

    Future Features

      -
    • [x] CocoaPods Support
    • -
    • [x] Carthage Support
    • -
    • [x] Swift Package Manager Support
    • -
    • [x] Localized for 30+ languages (see Localization)
    • -
    • [x] Pre-Update Device Compatibility Check (see Device Compatibility)
    • -
    • [x] Three types of alerts (see Screenshots)
    • -
    • [x] Optional delegate methods (see Delegates (Optional))
    • -
    • [x] Unit Tests
    • -
    • [x] Documentation can be found at http://sabintsev.com/Siren.
    • +
    • [ ] Present prompt only on WiFi if app is over the OTA limit.
    • +
    • [ ] Support for Third-/Homegrown Update Servers (not including TestFlight).
    • +
    • [ ] Increase code coverage with more unit tests and UI tests.

    Screenshots

    @@ -154,7 +194,7 @@

    Screenshots

  • The left picture forces the user to update the app.
  • The center picture gives the user the option to update the app.
  • The right picture gives the user the option to skip the current update.
  • -
  • These options are controlled by the Siren.AlertType enum.
  • +
  • These options are controlled by the Rules.AlertType enum.
  • @@ -202,196 +242,56 @@

    CocoaPods

    Carthage

    github "ArtSabintsev/Siren" // Swift 4.2
    -github "ArtSabintsev/Siren", "swift4.1" // Swift 4.1
    -github "ArtSabintsev/Siren", "swift3.2" // Swift 3.2
    -github "ArtSabintsev/Siren", "swift3.1" // Swift 3.1
    -github "ArtSabintsev/Siren", "swift2.3" // Swift 2.3
    +github "ArtSabintsev/Siren" "swift4.1" // Swift 4.1
    +github "ArtSabintsev/Siren" "swift3.2" // Swift 3.2
    +github "ArtSabintsev/Siren" "swift3.1" // Swift 3.1
    +github "ArtSabintsev/Siren" "swift2.3" // Swift 2.3
     

    Swift Package Manager

    -
    .Package(url: "https://github.com/ArtSabintsev/Siren.git", majorVersion: 3)
    +
    .Package(url: "https://github.com/ArtSabintsev/Siren.git", majorVersion: 4)
     
    -

    Example Code

    - -

    Below is some commented sample code. Adapt this to meet your app’s needs.

    - -

    For a full list of optional settings/preferences, please refer to https://github.com/ArtSabintsev/Siren/blob/master/Example/Example/AppDelegate.swift in the Sample Project.

    -
    func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
    -    /* Siren code should go below window?.makeKeyAndVisible() */
    +

    Implementation Examples

    - // Siren is a singleton - let siren = Siren.shared +

    Implementing Siren is as easy as adding two line of code to your app.

    +
    import Siren // Line 1
    +import UIKit
     
    -    // Optional: Defaults to .option
    -    siren.alertType = <#Siren.AlertType_Enum_Value#>
    +@UIApplicationMain
    +final class AppDelegate: UIResponder, UIApplicationDelegate {
    +    var window: UIWindow?
     
    -    // Optional: Change the various UIAlertController and UIAlertAction messaging. One or more values can be changes. If only a subset of values are changed, the defaults with which Siren comes with will be used.
    -    siren.alertMessaging = SirenAlertMessaging(updateTitle: NSAttributedString(string: "New Fancy Title"),
    -                                                updateMessage: NSAttributedString(string: "New message goes here!"),
    -                                                updateButtonMessage: NSAttributedString(string: "Update Now, Plz!?"),
    -                                                nextTimeButtonMessage: NSAttributedString(string: "OK, next time it is!"),
    -                                                skipVersionButtonMessage: NSAttributedString(string: "Please don't push skip, please don't!"))
    +    func application(_ application: UIApplication,
    +                     didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
    +        window?.makeKeyAndVisible()
     
    +        Siren.shared.wail() // Line 2
     
    -    // Optional: Set this variable if you would only like to show an alert if your app has been available on the store for a few days.
    -    // This default value is set to 1 to avoid this issue: https://github.com/ArtSabintsev/Siren#words-of-caution
    -    // To show the update immediately after Apple has updated their JSON, set this value to 0. Not recommended due to aforementioned reason in https://github.com/ArtSabintsev/Siren#words-of-caution.
    -    siren.showAlertAfterCurrentVersionHasBeenReleasedForDays = 3
    -
    -    // Replace .immediately with .daily or .weekly to specify a maximum daily or weekly frequency for version checks.
    -    // DO NOT CALL THIS METHOD IN didFinishLaunchingWithOptions IF YOU ALSO PLAN TO CALL IT IN applicationDidBecomeActive.
    -    siren.checkVersion(checkType: .immediately)
    -
    -    return true
    -}
    -
    -func applicationDidBecomeActive(application: UIApplication) {
    -    /*
    -        Perform daily (.daily) or weekly (.weekly) checks for new version of your app.
    -        Useful if user returns to your app from the background after extended period of time.
    -            Place in applicationDidBecomeActive(_:).
    -     */
    -
    -    Siren.shared.checkVersion(checkType: .daily)
    -}
    -
    -func applicationWillEnterForeground(application: UIApplication) {
    -   /*
    -      Useful if user returns to your app from the background after being sent to the
    -      App Store, but doesn't update their app before coming back to your app.
    -
    -      ONLY USE WITH Siren.AlertType.immediately
    -   */
    -
    -    Siren.shared.checkVersion(checkType: .immediately)
    -}
    -
    - -

    And you’re all set!

    -

    Prompting for Updates without Alerts

    - -

    Some developers may want to display a less obtrusive custom interface, like a banner or small icon. To accomplish this, you can disable alert presentation by doing the following:

    -
    func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
    -    ...
    -    siren.delegate = self
    -    siren.alertType = .none
    -    ...
    -}
    -
    -extension AppDelegate: SirenDelegate {
    -    // Returns a localized message to this delegate method upon performing a successful version check
    -    func sirenDidDetectNewVersionWithoutAlert(message: String, updateType: UpdateType) {
    -        print("\(message)")
    +        return true
         }
     }
     
    -

    Siren will call the sirenDidDetectNewVersionWithoutAlert(message: String) delegate method, passing a localized, suggested update string suitable for display. Implement this method to display your own messaging, optionally using message.

    -

    Granular Version Update Management

    +

    Siren also has plenty of customization options. All examples can be found in the Example Project’s AppDelegate file. Uncomment the example you’d like to test.

    -

    If you would like to set a different type of alert for revision, patch, minor, and/or major updates, simply add one or all of the following optional lines to your setup before calling the checkVersion() method:

    -
        /* Siren defaults to Siren.AlertType.option for all updates */
    -    siren.shared.revisionUpdateAlertType = <#Siren.AlertType_Enum_Value#>
    -    siren.shared.patchUpdateAlertType = <#Siren.AlertType_Enum_Value#>
    -    siren.shared.minorUpdateAlertType = <#Siren.AlertType_Enum_Value#>
    -    siren.shared.majorUpdateAlertType = <#Siren.AlertType_Enum_Value#>
    -
    -

    Optional Delegate and Delegate Methods

    - -

    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.

    -
    public protocol SirenDelegate: NSObjectProtocol {
    -    /// Siren performed version check and did not display alert.
    -    func sirenDidDetectNewVersionWithoutAlert(message: String, updateType: UpdateType)
    -
    -    /// Siren failed to perform version check.
    -    ///
    -    /// - Note:
    -    ///     Depending on the reason for failure,
    -    ///     a system-level error may be returned.
    -    func sirenDidFailVersionCheck(error: Error)
    -
    -    /// User presented with update dialog.
    -    func sirenDidShowUpdateDialog(alertType: Siren.AlertType)
    -
    -    /// Siren performed a version check and latest version is installed.
    -    func sirenLatestVersionInstalled()
    -
    -    /// Provides the decoded JSON information from a successful version check call.
    -    ///
    -    /// - SeeAlso:
    -    ///     SirenLookupModel.swift
    -    ///
    -    /// - Parameter lookupModel: The `Decodable` model representing the JSON results from the iTunes Lookup API.
    -    func sirenNetworkCallDidReturnWithNewVersionInformation(lookupModel: SirenLookupModel)
    -
    -    /// User did click on button that cancels update dialog.
    -    func sirenUserDidCancel()
    -
    -    /// User did click on button that launched "App Store.app".
    -    func sirenUserDidLaunchAppStore()
    -
    -    /// User did click on button that skips version update.
    -    func sirenUserDidSkipVersion()
    -}
    -
    +

    WARNING: Siren should ONLY be placed in UIApplication.didFinishLaunchingWithOptions and only after the window?.makeKeyAndVisible() call. Siren initializes a listener on didBecomeActiveNotification to perform version checks.

    Localization

    -

    Siren is localized for

    +

    Siren is localized for the following languages:

    -
      -
    • Arabic
    • -
    • Armenian
    • -
    • Basque
    • -
    • Chinese (Simplified and Traditional)
    • -
    • Croatian
    • -
    • Czech
    • -
    • Danish
    • -
    • Dutch
    • -
    • English
    • -
    • Estonian
    • -
    • Finnish
    • -
    • French
    • -
    • German
    • -
    • Greek
    • -
    • Hebrew
    • -
    • Hungarian
    • -
    • Indonesian
    • -
    • Italian
    • -
    • Japanese
    • -
    • Korean
    • -
    • Latvian
    • -
    • Lithuanian
    • -
    • Malay
    • -
    • Norwegian (Bokmål)
    • -
    • Persian (Afghanistan, Iran, Persian)
    • -
    • Polish
    • -
    • Portuguese (Brazil and Portugal)
    • -
    • Russian
    • -
    • Serbian (Cyrillic and Latin)
    • -
    • Slovenian
    • -
    • Spanish
    • -
    • Swedish
    • -
    • Thai
    • -
    • Turkish
    • -
    • Ukrainian
    • -
    • Urdu
    • -
    • Vietnamese
    • -
    - -

    You may want the update dialog to always appear in a certain language, ignoring iOS’s language setting (e.g. apps released in a specific country).

    +

    Arabic, Armenian, Basque, Chinese (Simplified and Traditional), Croatian, Czech, Danish, Dutch, English, Estonian, Finnish, French, German, Greek, Hebrew, Hungarian, Indonesian, Italian, Japanese, Korean, Latvian, Lithuanian, Malay, Norwegian (Bokmål), Persian (Afghanistan, Iran, Persian), Polish, Portuguese (Brazil and Portugal), Russian, Serbian (Cyrillic and Latin), Slovenian, Spanish, Swedish, Thai, Turkish, Ukrainian, Urdu, Vietnamese

    -

    You can enable it like so:

    -
    Siren.shared.forceLanguageLocalization = Siren.LanguageType.<#Siren.LanguageType_Enum_Value#>
    +

    You may want the update dialog to always appear in a certain language, ignoring the user’s device-specific setting. You can enable it like so:

    +
    // In this example, we force the `russian` language.
    +Siren.shared.presentationManager = PresentationManager(forceLanguageLocalization: .russian)
     

    Device Compatibility

    -

    If an app update is available, Siren checks to make sure that the version of iOS on the user’s device is compatible with the one that is required by the app update. For example, if a user has iOS 10 installed on their device, but the app update requires iOS 11, an alert will not be shown. This takes care of the false positive case regarding app updating.

    +

    If an app update is available, Siren checks to make sure that the version of iOS on the user’s device is compatible with the one that is required by the app update. For example, if a user has iOS 11 installed on their device, but the app update requires iOS 12, an alert will not be shown. This takes care of the false positive case regarding app updating.

    Testing Siren

    -

    Temporarily change the version string in Xcode (within the .xcodeproj) to an older version than the one that’s currently available in the App Store. Afterwards, build and run your app, and you should see the alert.

    +

    Temporarily change the version string in Xcode (within the .xcodeproj file) to an older version than the one that’s currently available in the App Store. Afterwards, build and run your app, and you should see the alert.

    If you currently don’t have an app in the store, change your bundleID to one that is already in the store. In the sample app packaged with this library, we use the App Store Connect app’s bundleID: com.apple.AppStoreConnect.

    - -

    For your convenience, you may turn on debugging statements by setting self.debugEnabled = true before calling the checkVersion() method.

    App Store Submissions

    The App Store reviewer will not see the alert. The version in the App Store will always be older than the version being reviewed.

    @@ -401,7 +301,7 @@

    Phased Releases

    • You can leave Siren configured as normal. Phased rollout will continue to auto-update apps. Since all users can still manually update your app directly from the App Store, Siren will ignore the phased rollout and will prompt users to update.
    • -
    • You can set showAlertAfterCurrentVersionHasBeenReleasedForDays to 7, and Siren will not prompt any users until the latest version is 7 days old, after phased rollout is complete.
    • +
    • You can set showAlertAfterCurrentVersionHasBeenReleasedForDays to 7, and Siren will not prompt any users until the latest version is 7 days old, after the phased rollout is complete.
    • You can remotely disable Siren until the rollout is done using your own API / backend logic.

    Words of Caution

    @@ -415,6 +315,7 @@

    Ports

    • Harpy
    • Siren was ported from Harpy, as Siren and Harpy are maintained by the same developer.
    • +
    • As of December 2018, Harpy has been deprecated in favor of Siren.
  • Java (Android) @@ -429,14 +330,23 @@

    Ports

  • The Siren Swift library inspired the React Native library.
  • +

    Shout-Out and Gratitude

    + +

    A massive shout-out and thank you goes to the following folks:

    + +
      +
    • Aaron Brager for motivating me and assisting me in building the initial proof-of-concept of Siren (based on Harpy) back in 2015. Without him, Siren may never have been built.
    • +
    • All of Harpy’s Consitrbutors for helping building the feature set from 2012-2015 that was used as the basis for the first version of Siren.
    • +
    • All of Siren’s Contributors for helping make Siren as powerful and bug-free as it currently is today.
    • +

    Created and maintained by

    -

    Arthur Ariel Sabintsev & Aaron Brager

    +

    Arthur Ariel Sabintsev

    diff --git a/docs/search.json b/docs/search.json index 53596f3b..140126da 100644 --- a/docs/search.json +++ b/docs/search.json @@ -1 +1 @@ -{"Structs/SirenError/Known.html#/s:5Siren0A5ErrorV5KnownO20appStoreAppIDFailureyA2EmF":{"name":"appStoreAppIDFailure","abstract":"

    Error retrieving trackId as the JSON does not contain a ‘trackId’ key.

    ","parent_name":"Known"},"Structs/SirenError/Known.html#/s:5Siren0A5ErrorV5KnownO28appStoreDataRetrievalFailureyAEs0B0_pSg_tcAEmF":{"name":"appStoreDataRetrievalFailure(underlyingError:)","abstract":"

    Error retrieving App Store data as an error was returned.

    ","parent_name":"Known"},"Structs/SirenError/Known.html#/s:5Siren0A5ErrorV5KnownO26appStoreJSONParsingFailureyAEs0B0_p_tcAEmF":{"name":"appStoreJSONParsingFailure(underlyingError:)","abstract":"

    Error parsing App Store JSON data.

    ","parent_name":"Known"},"Structs/SirenError/Known.html#/s:5Siren0A5ErrorV5KnownO33appStoreDataRetrievalEmptyResultsyA2EmF":{"name":"appStoreDataRetrievalEmptyResults","abstract":"

    Error retrieving App Store data as JSON results were empty. Is your app available in the US? If not, change the countryCode variable to fix this error.

    ","parent_name":"Known"},"Structs/SirenError/Known.html#/s:5Siren0A5ErrorV5KnownO30appStoreOSVersionNumberFailureyA2EmF":{"name":"appStoreOSVersionNumberFailure","abstract":"

    Error retrieving iOS version number as there was no data returned.

    ","parent_name":"Known"},"Structs/SirenError/Known.html#/s:5Siren0A5ErrorV5KnownO28appStoreOSVersionUnsupportedyA2EmF":{"name":"appStoreOSVersionUnsupported","abstract":"

    The version of iOS on the device is lower than that of the one required by the app verison update.

    ","parent_name":"Known"},"Structs/SirenError/Known.html#/s:5Siren0A5ErrorV5KnownO27appStoreVersionArrayFailureyA2EmF":{"name":"appStoreVersionArrayFailure","abstract":"

    Error retrieving App Store verson number as the JSON does not contain a ‘version’ key.

    ","parent_name":"Known"},"Structs/SirenError/Known.html#/s:5Siren0A5ErrorV5KnownO12malformedURLyA2EmF":{"name":"malformedURL","abstract":"

    The iTunes URL is malformed. Please leave an issue on https://github.com/ArtSabintsev/Siren with as many details as possible.

    ","parent_name":"Known"},"Structs/SirenError/Known.html#/s:5Siren0A5ErrorV5KnownO17noUpdateAvailableyA2EmF":{"name":"noUpdateAvailable","abstract":"

    No new update available.

    ","parent_name":"Known"},"Structs/SirenError/Known.html#/s:5Siren0A5ErrorV5KnownO22recentlyCheckedAlreadyyA2EmF":{"name":"recentlyCheckedAlready","abstract":"

    Not checking the version, because it was already checked recently.

    ","parent_name":"Known"},"Structs/SirenError/Known.html":{"name":"Known","abstract":"

    Enumerates all potentials errors that Siren can handle.

    ","parent_name":"SirenError"},"Structs/SirenLookupModel/Results.html#/s:5Siren0A11LookupModelV7ResultsV5appIDSivp":{"name":"appID","abstract":"

    The app’s App ID.

    ","parent_name":"Results"},"Structs/SirenLookupModel/Results.html#/s:5Siren0A11LookupModelV7ResultsV25currentVersionReleaseDateSSvp":{"name":"currentVersionReleaseDate","abstract":"

    The release date for the latest verison of the app.

    ","parent_name":"Results"},"Structs/SirenLookupModel/Results.html#/s:5Siren0A11LookupModelV7ResultsV16minimumOSVersionSSvp":{"name":"minimumOSVersion","abstract":"

    The minimum verison of iOS that the current verison of the app requires.

    ","parent_name":"Results"},"Structs/SirenLookupModel/Results.html#/s:5Siren0A11LookupModelV7ResultsV12releaseNotesSSSgvp":{"name":"releaseNotes","abstract":"

    The releases notes from the latest version of the app.

    ","parent_name":"Results"},"Structs/SirenLookupModel/Results.html#/s:5Siren0A11LookupModelV7ResultsV7versionSSvp":{"name":"version","abstract":"

    The latest version of the app.

    ","parent_name":"Results"},"Structs/SirenLookupModel.html#/s:5Siren0A11LookupModelV7resultsSayAC7ResultsVGvp":{"name":"results","abstract":"

    The array of results objects from the iTunes Lookup API.

    ","parent_name":"SirenLookupModel"},"Structs/SirenLookupModel/Results.html":{"name":"Results","abstract":"

    The Results object from the the iTunes Lookup API.

    ","parent_name":"SirenLookupModel"},"Structs/SirenAlertMessaging/Constants.html#/s:5Siren0A14AlertMessagingV9ConstantsV8nextTimeSo18NSAttributedStringCvpZ":{"name":"nextTime","abstract":"

    The button text that conveys the message that the user should be prompted to update next time the app launches.

    ","parent_name":"Constants"},"Structs/SirenAlertMessaging/Constants.html#/s:5Siren0A14AlertMessagingV9ConstantsV11skipVersionSo18NSAttributedStringCvpZ":{"name":"skipVersion","abstract":"

    The text that conveys the message that the the user wants to skip this verison update.

    ","parent_name":"Constants"},"Structs/SirenAlertMessaging/Constants.html#/s:5Siren0A14AlertMessagingV9ConstantsV13updateMessageSo18NSAttributedStringCvpZ":{"name":"updateMessage","abstract":"

    The text that conveys the message that there is an app update available

    ","parent_name":"Constants"},"Structs/SirenAlertMessaging/Constants.html#/s:5Siren0A14AlertMessagingV9ConstantsV11updateTitleSo18NSAttributedStringCvpZ":{"name":"updateTitle","abstract":"

    The alert title which defaults to Update Available.

    ","parent_name":"Constants"},"Structs/SirenAlertMessaging/Constants.html#/s:5Siren0A14AlertMessagingV9ConstantsV9updateNowSo18NSAttributedStringCvpZ":{"name":"updateNow","abstract":"

    The button text that conveys the message that the user would like to update the app right away.

    ","parent_name":"Constants"},"Structs/SirenAlertMessaging/Constants.html":{"name":"Constants","abstract":"

    The default constants used for the alert messaging.

    ","parent_name":"SirenAlertMessaging"},"Structs/SirenAlertMessaging.html#/s:5Siren0A14AlertMessagingV11updateTitle0D7Message0d6ButtonF008nextTimegF0011skipVersiongF0ACSo18NSAttributedStringC_A4Jtcfc":{"name":"init(updateTitle:updateMessage:updateButtonMessage:nextTimeButtonMessage:skipVersionButtonMessage:)","abstract":"

    The public initializer

    ","parent_name":"SirenAlertMessaging"},"Structs/SirenAlertMessaging.html":{"name":"SirenAlertMessaging","abstract":"

    Allows the overriding of all the UIAlertController and UIActionSheet Strings to which Siren defaults.

    "},"Structs/SirenLookupModel.html":{"name":"SirenLookupModel","abstract":"

    MARK: Siren extension used to parse and map the iTunes JSON results into a model represented in Swift.

    "},"Structs/SirenError.html":{"name":"SirenError","abstract":"

    Data structure used to build Siren specific Errors.

    "},"Protocols/SirenDelegate.html#/s:5Siren0A8DelegateP36sirenDidDetectNewVersionWithoutAlert5title7message10updateTypeySS_SSAA06UpdateM0OtF":{"name":"sirenDidDetectNewVersionWithoutAlert(title:message:updateType:)","abstract":"

    Siren performed a version check and did not display an alert.

    ","parent_name":"SirenDelegate"},"Protocols/SirenDelegate.html#/s:5Siren0A8DelegateP24sirenDidFailVersionCheck5errorys5Error_p_tF":{"name":"sirenDidFailVersionCheck(error:)","abstract":"

    Siren failed to perform version check.

    ","parent_name":"SirenDelegate"},"Protocols/SirenDelegate.html#/s:5Siren0A8DelegateP24sirenDidShowUpdateDialog9alertTypeyA2AC05AlertI0O_tF":{"name":"sirenDidShowUpdateDialog(alertType:)","abstract":"

    User presented with an update dialog.

    ","parent_name":"SirenDelegate"},"Protocols/SirenDelegate.html#/s:5Siren0A8DelegateP27sirenLatestVersionInstalledyyF":{"name":"sirenLatestVersionInstalled()","abstract":"

    Siren performed a version check and the latest version was already installed.

    ","parent_name":"SirenDelegate"},"Protocols/SirenDelegate.html#/s:5Siren0A8DelegateP50sirenNetworkCallDidReturnWithNewVersionInformation11lookupModelyAA0a6LookupM0V_tF":{"name":"sirenNetworkCallDidReturnWithNewVersionInformation(lookupModel:)","abstract":"

    Provides the decoded JSON information from a successful version check call.

    ","parent_name":"SirenDelegate"},"Protocols/SirenDelegate.html#/s:5Siren0A8DelegateP18sirenUserDidCancelyyF":{"name":"sirenUserDidCancel()","abstract":"

    User did click on button that cancels update dialog.

    ","parent_name":"SirenDelegate"},"Protocols/SirenDelegate.html#/s:5Siren0A8DelegateP26sirenUserDidLaunchAppStoreyyF":{"name":"sirenUserDidLaunchAppStore()","abstract":"

    User did click on button that launched App Store.app.

    ","parent_name":"SirenDelegate"},"Protocols/SirenDelegate.html#/s:5Siren0A8DelegateP23sirenUserDidSkipVersionyyF":{"name":"sirenUserDidSkipVersion()","abstract":"

    User did click on button that skips version update.

    ","parent_name":"SirenDelegate"},"Protocols/SirenDelegate.html":{"name":"SirenDelegate","abstract":"

    Delegate that handles all codepaths for Siren upon version check completion.

    "},"Enums/UpdateType.html#/s:5Siren10UpdateTypeO5majoryA2CmF":{"name":"major","abstract":"

    Major release available: A.b.c.d

    ","parent_name":"UpdateType"},"Enums/UpdateType.html#/s:5Siren10UpdateTypeO5minoryA2CmF":{"name":"minor","abstract":"

    Minor release available: a.B.c.d

    ","parent_name":"UpdateType"},"Enums/UpdateType.html#/s:5Siren10UpdateTypeO5patchyA2CmF":{"name":"patch","abstract":"

    Patch release available: a.b.C.d

    ","parent_name":"UpdateType"},"Enums/UpdateType.html#/s:5Siren10UpdateTypeO8revisionyA2CmF":{"name":"revision","abstract":"

    Revision release available: a.b.c.D

    ","parent_name":"UpdateType"},"Enums/UpdateType.html#/s:5Siren10UpdateTypeO7unknownyA2CmF":{"name":"unknown","abstract":"

    No information available about the update.

    ","parent_name":"UpdateType"},"Enums/UpdateType.html":{"name":"UpdateType","abstract":"

    MARK - Siren UpdateType"},"Classes/Siren/LanguageType.html#/s:5SirenAAC12LanguageTypeO6arabicyA2DmF":{"name":"arabic","abstract":"

    Arabic

    ","parent_name":"LanguageType"},"Classes/Siren/LanguageType.html#/s:5SirenAAC12LanguageTypeO8armenianyA2DmF":{"name":"armenian","abstract":"

    Armenian

    ","parent_name":"LanguageType"},"Classes/Siren/LanguageType.html#/s:5SirenAAC12LanguageTypeO6basqueyA2DmF":{"name":"basque","abstract":"

    Basque

    ","parent_name":"LanguageType"},"Classes/Siren/LanguageType.html#/s:5SirenAAC12LanguageTypeO17chineseSimplifiedyA2DmF":{"name":"chineseSimplified","abstract":"

    Simplified Chinese

    ","parent_name":"LanguageType"},"Classes/Siren/LanguageType.html#/s:5SirenAAC12LanguageTypeO18chineseTraditionalyA2DmF":{"name":"chineseTraditional","abstract":"

    Traditional Chinese

    ","parent_name":"LanguageType"},"Classes/Siren/LanguageType.html#/s:5SirenAAC12LanguageTypeO8croatianyA2DmF":{"name":"croatian","abstract":"

    Croatian

    ","parent_name":"LanguageType"},"Classes/Siren/LanguageType.html#/s:5SirenAAC12LanguageTypeO5czechyA2DmF":{"name":"czech","abstract":"

    Czech

    ","parent_name":"LanguageType"},"Classes/Siren/LanguageType.html#/s:5SirenAAC12LanguageTypeO6danishyA2DmF":{"name":"danish","abstract":"

    Danish

    ","parent_name":"LanguageType"},"Classes/Siren/LanguageType.html#/s:5SirenAAC12LanguageTypeO5dutchyA2DmF":{"name":"dutch","abstract":"

    Dutch

    ","parent_name":"LanguageType"},"Classes/Siren/LanguageType.html#/s:5SirenAAC12LanguageTypeO7englishyA2DmF":{"name":"english","abstract":"

    English

    ","parent_name":"LanguageType"},"Classes/Siren/LanguageType.html#/s:5SirenAAC12LanguageTypeO8estonianyA2DmF":{"name":"estonian","abstract":"

    Estonian

    ","parent_name":"LanguageType"},"Classes/Siren/LanguageType.html#/s:5SirenAAC12LanguageTypeO7finnishyA2DmF":{"name":"finnish","abstract":"

    Finnish

    ","parent_name":"LanguageType"},"Classes/Siren/LanguageType.html#/s:5SirenAAC12LanguageTypeO6frenchyA2DmF":{"name":"french","abstract":"

    French

    ","parent_name":"LanguageType"},"Classes/Siren/LanguageType.html#/s:5SirenAAC12LanguageTypeO6germanyA2DmF":{"name":"german","abstract":"

    German

    ","parent_name":"LanguageType"},"Classes/Siren/LanguageType.html#/s:5SirenAAC12LanguageTypeO5greekyA2DmF":{"name":"greek","abstract":"

    Greek

    ","parent_name":"LanguageType"},"Classes/Siren/LanguageType.html#/s:5SirenAAC12LanguageTypeO6hebrewyA2DmF":{"name":"hebrew","abstract":"

    Hebrew

    ","parent_name":"LanguageType"},"Classes/Siren/LanguageType.html#/s:5SirenAAC12LanguageTypeO9hungarianyA2DmF":{"name":"hungarian","abstract":"

    Hungarian

    ","parent_name":"LanguageType"},"Classes/Siren/LanguageType.html#/s:5SirenAAC12LanguageTypeO10indonesianyA2DmF":{"name":"indonesian","abstract":"

    Indonesian

    ","parent_name":"LanguageType"},"Classes/Siren/LanguageType.html#/s:5SirenAAC12LanguageTypeO7italianyA2DmF":{"name":"italian","abstract":"

    Italian

    ","parent_name":"LanguageType"},"Classes/Siren/LanguageType.html#/s:5SirenAAC12LanguageTypeO8japaneseyA2DmF":{"name":"japanese","abstract":"

    Japanese

    ","parent_name":"LanguageType"},"Classes/Siren/LanguageType.html#/s:5SirenAAC12LanguageTypeO6koreanyA2DmF":{"name":"korean","abstract":"

    Korean

    ","parent_name":"LanguageType"},"Classes/Siren/LanguageType.html#/s:5SirenAAC12LanguageTypeO7latvianyA2DmF":{"name":"latvian","abstract":"

    Latvian

    ","parent_name":"LanguageType"},"Classes/Siren/LanguageType.html#/s:5SirenAAC12LanguageTypeO10lithuanianyA2DmF":{"name":"lithuanian","abstract":"

    Lithuanian

    ","parent_name":"LanguageType"},"Classes/Siren/LanguageType.html#/s:5SirenAAC12LanguageTypeO5malayyA2DmF":{"name":"malay","abstract":"

    Malaysian

    ","parent_name":"LanguageType"},"Classes/Siren/LanguageType.html#/s:5SirenAAC12LanguageTypeO9norwegianyA2DmF":{"name":"norwegian","abstract":"

    Norwegian

    ","parent_name":"LanguageType"},"Classes/Siren/LanguageType.html#/s:5SirenAAC12LanguageTypeO7persianyA2DmF":{"name":"persian","abstract":"

    Persian

    ","parent_name":"LanguageType"},"Classes/Siren/LanguageType.html#/s:5SirenAAC12LanguageTypeO18persianAfghanistanyA2DmF":{"name":"persianAfghanistan","abstract":"

    Persian (Afghanistan)

    ","parent_name":"LanguageType"},"Classes/Siren/LanguageType.html#/s:5SirenAAC12LanguageTypeO11persianIranyA2DmF":{"name":"persianIran","abstract":"

    Persian (Iran)

    ","parent_name":"LanguageType"},"Classes/Siren/LanguageType.html#/s:5SirenAAC12LanguageTypeO6polishyA2DmF":{"name":"polish","abstract":"

    Polish

    ","parent_name":"LanguageType"},"Classes/Siren/LanguageType.html#/s:5SirenAAC12LanguageTypeO16portugueseBrazilyA2DmF":{"name":"portugueseBrazil","abstract":"

    Portuguese (Brazil)

    ","parent_name":"LanguageType"},"Classes/Siren/LanguageType.html#/s:5SirenAAC12LanguageTypeO18portuguesePortugalyA2DmF":{"name":"portuguesePortugal","abstract":"

    Portuguese (Portugal)

    ","parent_name":"LanguageType"},"Classes/Siren/LanguageType.html#/s:5SirenAAC12LanguageTypeO7russianyA2DmF":{"name":"russian","abstract":"

    Russian

    ","parent_name":"LanguageType"},"Classes/Siren/LanguageType.html#/s:5SirenAAC12LanguageTypeO15serbianCyrillicyA2DmF":{"name":"serbianCyrillic","abstract":"

    Serbian (Cyrillic)

    ","parent_name":"LanguageType"},"Classes/Siren/LanguageType.html#/s:5SirenAAC12LanguageTypeO12serbianLatinyA2DmF":{"name":"serbianLatin","abstract":"

    Serbian (Latin)

    ","parent_name":"LanguageType"},"Classes/Siren/LanguageType.html#/s:5SirenAAC12LanguageTypeO9slovenianyA2DmF":{"name":"slovenian","abstract":"

    Slovenian

    ","parent_name":"LanguageType"},"Classes/Siren/LanguageType.html#/s:5SirenAAC12LanguageTypeO7spanishyA2DmF":{"name":"spanish","abstract":"

    Spanish

    ","parent_name":"LanguageType"},"Classes/Siren/LanguageType.html#/s:5SirenAAC12LanguageTypeO7swedishyA2DmF":{"name":"swedish","abstract":"

    Swedish

    ","parent_name":"LanguageType"},"Classes/Siren/LanguageType.html#/s:5SirenAAC12LanguageTypeO4thaiyA2DmF":{"name":"thai","abstract":"

    Thai

    ","parent_name":"LanguageType"},"Classes/Siren/LanguageType.html#/s:5SirenAAC12LanguageTypeO7turkishyA2DmF":{"name":"turkish","abstract":"

    Turkish

    ","parent_name":"LanguageType"},"Classes/Siren/LanguageType.html#/s:5SirenAAC12LanguageTypeO4urduyA2DmF":{"name":"urdu","abstract":"

    Urdu

    ","parent_name":"LanguageType"},"Classes/Siren/LanguageType.html#/s:5SirenAAC12LanguageTypeO9ukrainianyA2DmF":{"name":"ukrainian","abstract":"

    Ukranian

    ","parent_name":"LanguageType"},"Classes/Siren/LanguageType.html#/s:5SirenAAC12LanguageTypeO10vietnameseyA2DmF":{"name":"vietnamese","abstract":"

    Vietnamese

    ","parent_name":"LanguageType"},"Classes/Siren/VersionCheckType.html#/s:5SirenAAC16VersionCheckTypeO11immediatelyyA2DmF":{"name":"immediately","abstract":"

    Version check performed every time the app is launched.

    ","parent_name":"VersionCheckType"},"Classes/Siren/VersionCheckType.html#/s:5SirenAAC16VersionCheckTypeO5dailyyA2DmF":{"name":"daily","abstract":"

    Version check performed once a day.

    ","parent_name":"VersionCheckType"},"Classes/Siren/VersionCheckType.html#/s:5SirenAAC16VersionCheckTypeO6weeklyyA2DmF":{"name":"weekly","abstract":"

    Version check performed once a week.

    ","parent_name":"VersionCheckType"},"Classes/Siren/AlertType.html#/s:5SirenAAC9AlertTypeO5forceyA2DmF":{"name":"force","abstract":"

    Forces user to update your app (1 button alert).

    ","parent_name":"AlertType"},"Classes/Siren/AlertType.html#/s:5SirenAAC9AlertTypeO6optionyA2DmF":{"name":"option","abstract":"

    (DEFAULT) Presents user with option to update app now or at next launch (2 button alert).

    ","parent_name":"AlertType"},"Classes/Siren/AlertType.html#/s:5SirenAAC9AlertTypeO4skipyA2DmF":{"name":"skip","abstract":"

    Presents user with option to update the app now, at next launch, or to skip this version all together (3 button alert).

    ","parent_name":"AlertType"},"Classes/Siren/AlertType.html#/s:5SirenAAC9AlertTypeO4noneyA2DmF":{"name":"none","abstract":"

    Doesn’t show the alert, but instead returns a localized message","parent_name":"AlertType"},"Classes/Siren.html#/s:5SirenAAC0A11ErrorDomainSSvp":{"name":"SirenErrorDomain","abstract":"

    The error domain for all errors created by Siren.

    ","parent_name":"Siren"},"Classes/Siren.html#/s:5SirenAAC8delegateAA0A8Delegate_pSgXwvp":{"name":"delegate","abstract":"

    The SirenDelegate variable, which should be set if you’d like to be notified of any of specific user interactions or API success/failures.","parent_name":"Siren"},"Classes/Siren.html#/s:5SirenAAC12debugEnabledSbvp":{"name":"debugEnabled","abstract":"

    The debug flag, which is disabled by default.","parent_name":"Siren"},"Classes/Siren.html#/s:5SirenAAC9alertTypeAB05AlertC0Ovp":{"name":"alertType","abstract":"

    Determines the type of alert that should be shown.","parent_name":"Siren"},"Classes/Siren.html#/s:5SirenAAC20majorUpdateAlertTypeAB0dE0Ovp":{"name":"majorUpdateAlertType","abstract":"

    Determines the type of alert that should be shown for major version updates: A.b.c","parent_name":"Siren"},"Classes/Siren.html#/s:5SirenAAC20minorUpdateAlertTypeAB0dE0Ovp":{"name":"minorUpdateAlertType","abstract":"

    Determines the type of alert that should be shown for minor version updates: a.B.c","parent_name":"Siren"},"Classes/Siren.html#/s:5SirenAAC20patchUpdateAlertTypeAB0dE0Ovp":{"name":"patchUpdateAlertType","abstract":"

    Determines the type of alert that should be shown for minor patch updates: a.b.C","parent_name":"Siren"},"Classes/Siren.html#/s:5SirenAAC23revisionUpdateAlertTypeAB0dE0Ovp":{"name":"revisionUpdateAlertType","abstract":"

    Determines the type of alert that should be shown for revision updates: a.b.c.D","parent_name":"Siren"},"Classes/Siren.html#/s:5SirenAAC7appNameSSvp":{"name":"appName","abstract":"

    The name of your app.","parent_name":"Siren"},"Classes/Siren.html#/s:5SirenAAC14alertMessagingAA0a5AlertC0Vvp":{"name":"alertMessaging","abstract":"

    Overrides all the Strings to which Siren defaults.","parent_name":"Siren"},"Classes/Siren.html#/s:5SirenAAC11countryCodeSSSgvp":{"name":"countryCode","abstract":"

    The region or country of an App Store in which your app is available.","parent_name":"Siren"},"Classes/Siren.html#/s:5SirenAAC25forceLanguageLocalizationAB0C4TypeOSgvp":{"name":"forceLanguageLocalization","abstract":"

    Overrides the default localization of a user’s device when presenting the update message and button titles in the alert.","parent_name":"Siren"},"Classes/Siren.html#/s:5SirenAAC24alertControllerTintColorSo7UIColorCSgvp":{"name":"alertControllerTintColor","abstract":"

    Overrides the tint color for UIAlertController.

    ","parent_name":"Siren"},"Classes/Siren.html#/s:5SirenAAC50showAlertAfterCurrentVersionHasBeenReleasedForDaysSivp":{"name":"showAlertAfterCurrentVersionHasBeenReleasedForDays","abstract":"

    When this is set, the alert will only show up if the current version has already been released for X days.","parent_name":"Siren"},"Classes/Siren.html#/s:5SirenAAC22currentAppStoreVersionSSSgvp":{"name":"currentAppStoreVersion","abstract":"

    The current version of your app that is available for download on the App Store

    ","parent_name":"Siren"},"Classes/Siren.html#/s:5SirenAAC6sharedABvpZ":{"name":"shared","abstract":"

    The App’s Singleton

    ","parent_name":"Siren"},"Classes/Siren.html#/s:5SirenAAC12checkVersion0B4TypeyAB0c5CheckD0O_tF":{"name":"checkVersion(checkType:)","abstract":"

    Checks the currently installed version of your app against the App Store.","parent_name":"Siren"},"Classes/Siren.html#/s:5SirenAAC14launchAppStoreyyF":{"name":"launchAppStore()","abstract":"

    Launches the AppStore in two situations:

    ","parent_name":"Siren"},"Classes/Siren/AlertType.html":{"name":"AlertType","abstract":"

    Determines the type of alert to present after a successful version check has been performed.

    ","parent_name":"Siren"},"Classes/Siren/VersionCheckType.html":{"name":"VersionCheckType","abstract":"

    Determines the frequency in which the the version check is performed and the user is prompted to update the app.

    ","parent_name":"Siren"},"Classes/Siren/LanguageType.html":{"name":"LanguageType","abstract":"

    Determines the available languages in which the update message and alert button titles should appear.

    ","parent_name":"Siren"},"Classes/Siren.html":{"name":"Siren","abstract":"

    The Siren Class. A singleton that is initialized using the shared constant.

    "},"Classes.html":{"name":"Classes","abstract":"

    The following classes are available globally.

    "},"Enums.html":{"name":"Enumerations","abstract":"

    The following enumerations are available globally.

    "},"Protocols.html":{"name":"Protocols","abstract":"

    The following protocols are available globally.

    "},"Structs.html":{"name":"Structures","abstract":"

    The following structures are available globally.

    "}} \ No newline at end of file +{"Structs/DataParser.html#/s:5Siren10DataParserV22isAppStoreVersionNewer09installedG003appfG0SbSSSg_AGtFZ":{"name":"isAppStoreVersionNewer(installedVersion:appStoreVersion:)","abstract":"

    Checks to see if the App Store version of the app is newer than the installed version.

    ","parent_name":"DataParser"},"Structs/DataParser.html#/s:5Siren10DataParserV30isUpdateCompatibleWithDeviceOS3forSbAA11LookupModelV_tFZ":{"name":"isUpdateCompatibleWithDeviceOS(for:)","abstract":"

    Validates that the latest version in the App Store is compatible with the device’s current version of iOS.

    ","parent_name":"DataParser"},"Structs/DataParser.html#/s:5Siren10DataParserV14parseForUpdate19forInstalledVersion011andAppStoreI0AA12RulesManagerV0F4TypeOSSSg_AKtFZ":{"name":"parseForUpdate(forInstalledVersion:andAppStoreVersion:)","abstract":"

    The type of update that is returned from the API in relation to the verison of the app that is installed.

    ","parent_name":"DataParser"},"Structs/DataParser.html#/s:5Siren10DataParserV5split33_35354280B893AD05C0C27D0AD925B30FLL7versionSaySiGSS_tFZ":{"name":"split(version:)","abstract":"

    Splits a version-formatted String into an[Int]`.

    ","parent_name":"DataParser"},"Structs/Rules/UpdatePromptFrequency.html#/s:5Siren5RulesV21UpdatePromptFrequencyO11immediatelyyA2EmF":{"name":"immediately","abstract":"

    Version check performed every time the app is launched.

    ","parent_name":"UpdatePromptFrequency"},"Structs/Rules/UpdatePromptFrequency.html#/s:5Siren5RulesV21UpdatePromptFrequencyO5dailyyA2EmF":{"name":"daily","abstract":"

    Version check performed once a day.

    ","parent_name":"UpdatePromptFrequency"},"Structs/Rules/UpdatePromptFrequency.html#/s:5Siren5RulesV21UpdatePromptFrequencyO6weeklyyA2EmF":{"name":"weekly","abstract":"

    Version check performed once a week.

    ","parent_name":"UpdatePromptFrequency"},"Structs/Rules/AlertType.html#/s:5Siren5RulesV9AlertTypeO5forceyA2EmF":{"name":"force","abstract":"

    Forces the user to update your app (1 button alert).

    ","parent_name":"AlertType"},"Structs/Rules/AlertType.html#/s:5Siren5RulesV9AlertTypeO6optionyA2EmF":{"name":"option","abstract":"

    Presents the user with option to update app now or at next launch (2 button alert).

    ","parent_name":"AlertType"},"Structs/Rules/AlertType.html#/s:5Siren5RulesV9AlertTypeO4skipyA2EmF":{"name":"skip","abstract":"

    Presents the user with option to update the app now, at next launch, or to skip this version all together (3 button alert).

    ","parent_name":"AlertType"},"Structs/Rules/AlertType.html#/s:5Siren5RulesV9AlertTypeO4noneyA2EmF":{"name":"none","abstract":"

    Doesn’t present the alert.","parent_name":"AlertType"},"Structs/Rules.html#/s:5Siren5RulesV9alertTypeAC05AlertD0Ovp":{"name":"alertType","abstract":"

    The type of alert that should be presented.

    ","parent_name":"Rules"},"Structs/Rules.html#/s:5Siren5RulesV9frequencyAC21UpdatePromptFrequencyOvp":{"name":"frequency","abstract":"

    The frequency in which a the user is prompted to update the app","parent_name":"Rules"},"Structs/Rules.html#/s:5Siren5RulesV15promptFrequency12forAlertTypeA2C012UpdatePromptD0O_AC0fG0Otcfc":{"name":"init(promptFrequency:forAlertType:)","abstract":"

    Initializes the alert presentation rules.

    ","parent_name":"Rules"},"Structs/Rules.html#/s:5Siren5RulesV8annoyingACvpZ":{"name":"annoying","abstract":"

    Performs a version check immediately, but allows the user to skip updating the app until the next time the app becomes active.

    ","parent_name":"Rules"},"Structs/Rules.html#/s:5Siren5RulesV8criticalACvpZ":{"name":"critical","abstract":"

    Performs a version check immediately and forces the user to update the app.

    ","parent_name":"Rules"},"Structs/Rules.html#/s:5Siren5RulesV7defaultACvpZ":{"name":"default","abstract":"

    Performs a version check once a day, but allows the user to skip updating the app until","parent_name":"Rules"},"Structs/Rules.html#/s:5Siren5RulesV10persistentACvpZ":{"name":"persistent","abstract":"

    Performs a version check daily, but allows the user to skip updating the app until the next time the app becomes active.

    ","parent_name":"Rules"},"Structs/Rules.html#/s:5Siren5RulesV7relaxedACvpZ":{"name":"relaxed","abstract":"

    Performs a version check weekly, but allows the user to skip updating the app until","parent_name":"Rules"},"Structs/Rules/AlertType.html":{"name":"AlertType","abstract":"

    Determines the type of alert to present after a successful version check has been performed.

    ","parent_name":"Rules"},"Structs/Rules/UpdatePromptFrequency.html":{"name":"UpdatePromptFrequency","abstract":"

    Determines the frequency in which the user is prompted to update the app","parent_name":"Rules"},"Structs/Results.html#/s:5Siren7ResultsV11alertActionAA05AlertD0Ovp":{"name":"alertAction","abstract":"

    The UIAlertAction the user chose upon being presented with the update alert.","parent_name":"Results"},"Structs/Results.html#/s:5Siren7ResultsV12localizationAA12LocalizationVvp":{"name":"localization","abstract":"

    The Siren-supported locale that was used for the string in the update alert.

    ","parent_name":"Results"},"Structs/Results.html#/s:5Siren7ResultsV11lookupModelAA06LookupD0Vvp":{"name":"lookupModel","abstract":"

    The Swift-mapped API model, if a successful version check was performed.

    ","parent_name":"Results"},"Structs/Results.html#/s:5Siren7ResultsV10updateTypeAA12RulesManagerV06UpdateD0Ovp":{"name":"updateType","abstract":"

    The type of update that was returned for the API.

    ","parent_name":"Results"},"Structs/LookupModel/Results/CodingKeys.html#/s:5Siren11LookupModelV7ResultsV10CodingKeys33_F2A9942F3CA9D99FD9845937489F40B8LLO5appIDyA2HmF":{"name":"appID","abstract":"

    The appID JSON key.

    ","parent_name":"CodingKeys"},"Structs/LookupModel/Results/CodingKeys.html#/s:5Siren11LookupModelV7ResultsV10CodingKeys33_F2A9942F3CA9D99FD9845937489F40B8LLO25currentVersionReleaseDateyA2HmF":{"name":"currentVersionReleaseDate","abstract":"

    The current version release date JSON key.

    ","parent_name":"CodingKeys"},"Structs/LookupModel/Results/CodingKeys.html#/s:5Siren11LookupModelV7ResultsV10CodingKeys33_F2A9942F3CA9D99FD9845937489F40B8LLO16minimumOSVersionyA2HmF":{"name":"minimumOSVersion","abstract":"

    The minimum device iOS version compatibility JSON key.

    ","parent_name":"CodingKeys"},"Structs/LookupModel/Results/CodingKeys.html#/s:5Siren11LookupModelV7ResultsV10CodingKeys33_F2A9942F3CA9D99FD9845937489F40B8LLO12releaseNotesyA2HmF":{"name":"releaseNotes","abstract":"

    The release notes JSON key.

    ","parent_name":"CodingKeys"},"Structs/LookupModel/Results/CodingKeys.html#/s:5Siren11LookupModelV7ResultsV10CodingKeys33_F2A9942F3CA9D99FD9845937489F40B8LLO7versionyA2HmF":{"name":"version","abstract":"

    The current App Store version JSON key.

    ","parent_name":"CodingKeys"},"Structs/LookupModel/Results/CodingKeys.html":{"name":"CodingKeys","abstract":"

    Codable Coding Keys for the Results array in the iTunes Lookup API JSON response.

    ","parent_name":"Results"},"Structs/LookupModel/Results.html#/s:5Siren11LookupModelV7ResultsV5appIDSivp":{"name":"appID","abstract":"

    The app’s App ID.

    ","parent_name":"Results"},"Structs/LookupModel/Results.html#/s:5Siren11LookupModelV7ResultsV25currentVersionReleaseDateSSvp":{"name":"currentVersionReleaseDate","abstract":"

    The release date for the latest verison of the app.

    ","parent_name":"Results"},"Structs/LookupModel/Results.html#/s:5Siren11LookupModelV7ResultsV16minimumOSVersionSSvp":{"name":"minimumOSVersion","abstract":"

    The minimum verison of iOS that the current verison of the app requires.

    ","parent_name":"Results"},"Structs/LookupModel/Results.html#/s:5Siren11LookupModelV7ResultsV12releaseNotesSSSgvp":{"name":"releaseNotes","abstract":"

    The releases notes from the latest version of the app.

    ","parent_name":"Results"},"Structs/LookupModel/Results.html#/s:5Siren11LookupModelV7ResultsV7versionSSvp":{"name":"version","abstract":"

    The latest version of the app.

    ","parent_name":"Results"},"Structs/LookupModel/CodingKeys.html#/s:5Siren11LookupModelV10CodingKeys33_F2A9942F3CA9D99FD9845937489F40B8LLO7resultsyA2FmF":{"name":"results","abstract":"

    The results JSON key.

    ","parent_name":"CodingKeys"},"Structs/LookupModel/CodingKeys.html":{"name":"CodingKeys","abstract":"

    Codable Coding Keys for the Top-Level iTunes Lookup API JSON response.

    ","parent_name":"LookupModel"},"Structs/LookupModel.html#/s:5Siren11LookupModelV7resultsSayAC7ResultsVGvp":{"name":"results","abstract":"

    The array of results objects from the iTunes Lookup API.

    ","parent_name":"LookupModel"},"Structs/LookupModel/Results.html":{"name":"Results","abstract":"

    The Results object from the the iTunes Lookup API.

    ","parent_name":"LookupModel"},"Structs/Localization/Language.html#/s:5Siren12LocalizationV8LanguageO6arabicyA2EmF":{"name":"arabic","abstract":"

    Arabic Language Localization

    ","parent_name":"Language"},"Structs/Localization/Language.html#/s:5Siren12LocalizationV8LanguageO8armenianyA2EmF":{"name":"armenian","abstract":"

    Armenian Language Localization

    ","parent_name":"Language"},"Structs/Localization/Language.html#/s:5Siren12LocalizationV8LanguageO6basqueyA2EmF":{"name":"basque","abstract":"

    Basque Language Localization

    ","parent_name":"Language"},"Structs/Localization/Language.html#/s:5Siren12LocalizationV8LanguageO17chineseSimplifiedyA2EmF":{"name":"chineseSimplified","abstract":"

    Simplified Chinese Language Localization

    ","parent_name":"Language"},"Structs/Localization/Language.html#/s:5Siren12LocalizationV8LanguageO18chineseTraditionalyA2EmF":{"name":"chineseTraditional","abstract":"

    Traditional Chinese Localization Localization

    ","parent_name":"Language"},"Structs/Localization/Language.html#/s:5Siren12LocalizationV8LanguageO8croatianyA2EmF":{"name":"croatian","abstract":"

    Croatian Language Localization

    ","parent_name":"Language"},"Structs/Localization/Language.html#/s:5Siren12LocalizationV8LanguageO5czechyA2EmF":{"name":"czech","abstract":"

    Czech Language Localization

    ","parent_name":"Language"},"Structs/Localization/Language.html#/s:5Siren12LocalizationV8LanguageO6danishyA2EmF":{"name":"danish","abstract":"

    Danish Language Localization

    ","parent_name":"Language"},"Structs/Localization/Language.html#/s:5Siren12LocalizationV8LanguageO5dutchyA2EmF":{"name":"dutch","abstract":"

    Dutch Language Localization

    ","parent_name":"Language"},"Structs/Localization/Language.html#/s:5Siren12LocalizationV8LanguageO7englishyA2EmF":{"name":"english","abstract":"

    English Language Localization

    ","parent_name":"Language"},"Structs/Localization/Language.html#/s:5Siren12LocalizationV8LanguageO8estonianyA2EmF":{"name":"estonian","abstract":"

    Estonian Language Localization

    ","parent_name":"Language"},"Structs/Localization/Language.html#/s:5Siren12LocalizationV8LanguageO7finnishyA2EmF":{"name":"finnish","abstract":"

    Finnish Language Localization

    ","parent_name":"Language"},"Structs/Localization/Language.html#/s:5Siren12LocalizationV8LanguageO6frenchyA2EmF":{"name":"french","abstract":"

    French Language Localization

    ","parent_name":"Language"},"Structs/Localization/Language.html#/s:5Siren12LocalizationV8LanguageO6germanyA2EmF":{"name":"german","abstract":"

    German Language Localization

    ","parent_name":"Language"},"Structs/Localization/Language.html#/s:5Siren12LocalizationV8LanguageO5greekyA2EmF":{"name":"greek","abstract":"

    Greek Language Localization

    ","parent_name":"Language"},"Structs/Localization/Language.html#/s:5Siren12LocalizationV8LanguageO6hebrewyA2EmF":{"name":"hebrew","abstract":"

    Hebrew Language Localization

    ","parent_name":"Language"},"Structs/Localization/Language.html#/s:5Siren12LocalizationV8LanguageO9hungarianyA2EmF":{"name":"hungarian","abstract":"

    Hungarian Language Localization

    ","parent_name":"Language"},"Structs/Localization/Language.html#/s:5Siren12LocalizationV8LanguageO10indonesianyA2EmF":{"name":"indonesian","abstract":"

    Indonesian Language Localization

    ","parent_name":"Language"},"Structs/Localization/Language.html#/s:5Siren12LocalizationV8LanguageO7italianyA2EmF":{"name":"italian","abstract":"

    Italian Language Localization

    ","parent_name":"Language"},"Structs/Localization/Language.html#/s:5Siren12LocalizationV8LanguageO8japaneseyA2EmF":{"name":"japanese","abstract":"

    Japanese Language Localization

    ","parent_name":"Language"},"Structs/Localization/Language.html#/s:5Siren12LocalizationV8LanguageO6koreanyA2EmF":{"name":"korean","abstract":"

    Korean Language Localization

    ","parent_name":"Language"},"Structs/Localization/Language.html#/s:5Siren12LocalizationV8LanguageO7latvianyA2EmF":{"name":"latvian","abstract":"

    Latvian Language Localization

    ","parent_name":"Language"},"Structs/Localization/Language.html#/s:5Siren12LocalizationV8LanguageO10lithuanianyA2EmF":{"name":"lithuanian","abstract":"

    Lithuanian Language Localization

    ","parent_name":"Language"},"Structs/Localization/Language.html#/s:5Siren12LocalizationV8LanguageO5malayyA2EmF":{"name":"malay","abstract":"

    Malay Language Localization

    ","parent_name":"Language"},"Structs/Localization/Language.html#/s:5Siren12LocalizationV8LanguageO9norwegianyA2EmF":{"name":"norwegian","abstract":"

    Norwegian Language Localization

    ","parent_name":"Language"},"Structs/Localization/Language.html#/s:5Siren12LocalizationV8LanguageO7persianyA2EmF":{"name":"persian","abstract":"

    Persian Language Localization

    ","parent_name":"Language"},"Structs/Localization/Language.html#/s:5Siren12LocalizationV8LanguageO18persianAfghanistanyA2EmF":{"name":"persianAfghanistan","abstract":"

    Persian (Afghanistan) Language Localization

    ","parent_name":"Language"},"Structs/Localization/Language.html#/s:5Siren12LocalizationV8LanguageO11persianIranyA2EmF":{"name":"persianIran","abstract":"

    Persian (Iran) Language Localization

    ","parent_name":"Language"},"Structs/Localization/Language.html#/s:5Siren12LocalizationV8LanguageO6polishyA2EmF":{"name":"polish","abstract":"

    Polish Language Localization

    ","parent_name":"Language"},"Structs/Localization/Language.html#/s:5Siren12LocalizationV8LanguageO16portugueseBrazilyA2EmF":{"name":"portugueseBrazil","abstract":"

    Brazilian Portuguese Language Localization

    ","parent_name":"Language"},"Structs/Localization/Language.html#/s:5Siren12LocalizationV8LanguageO18portuguesePortugalyA2EmF":{"name":"portuguesePortugal","abstract":"

    Portugal’s Portuguese Language Localization

    ","parent_name":"Language"},"Structs/Localization/Language.html#/s:5Siren12LocalizationV8LanguageO7russianyA2EmF":{"name":"russian","abstract":"

    Russian Language Localization

    ","parent_name":"Language"},"Structs/Localization/Language.html#/s:5Siren12LocalizationV8LanguageO15serbianCyrillicyA2EmF":{"name":"serbianCyrillic","abstract":"

    Serbian (Cyrillic) Language Localization

    ","parent_name":"Language"},"Structs/Localization/Language.html#/s:5Siren12LocalizationV8LanguageO12serbianLatinyA2EmF":{"name":"serbianLatin","abstract":"

    Serbian (Latin) Language Localization

    ","parent_name":"Language"},"Structs/Localization/Language.html#/s:5Siren12LocalizationV8LanguageO9slovenianyA2EmF":{"name":"slovenian","abstract":"

    Slovenian Language Localization

    ","parent_name":"Language"},"Structs/Localization/Language.html#/s:5Siren12LocalizationV8LanguageO7spanishyA2EmF":{"name":"spanish","abstract":"

    Spanish Language Localization

    ","parent_name":"Language"},"Structs/Localization/Language.html#/s:5Siren12LocalizationV8LanguageO7swedishyA2EmF":{"name":"swedish","abstract":"

    Swedish Language Localization

    ","parent_name":"Language"},"Structs/Localization/Language.html#/s:5Siren12LocalizationV8LanguageO4thaiyA2EmF":{"name":"thai","abstract":"

    Thai Language Localization

    ","parent_name":"Language"},"Structs/Localization/Language.html#/s:5Siren12LocalizationV8LanguageO7turkishyA2EmF":{"name":"turkish","abstract":"

    Turkish Language Localization

    ","parent_name":"Language"},"Structs/Localization/Language.html#/s:5Siren12LocalizationV8LanguageO4urduyA2EmF":{"name":"urdu","abstract":"

    Urdu Language Localization

    ","parent_name":"Language"},"Structs/Localization/Language.html#/s:5Siren12LocalizationV8LanguageO9ukrainianyA2EmF":{"name":"ukrainian","abstract":"

    Ukranian Language Localization

    ","parent_name":"Language"},"Structs/Localization/Language.html#/s:5Siren12LocalizationV8LanguageO10vietnameseyA2EmF":{"name":"vietnamese","abstract":"

    Vietnamese Language Localization

    ","parent_name":"Language"},"Structs/Localization/Language.html":{"name":"Language","abstract":"

    Determines the available languages in which the update message and alert button titles should appear.

    ","parent_name":"Localization"},"Structs/Localization.html#/s:5Siren12LocalizationV7appName33_2FDBEF65899237DA36B11FA5846AD0EALLSSvp":{"name":"appName","abstract":"

    The name of the app as defined by the Info.plist.

    ","parent_name":"Localization"},"Structs/Localization.html#/s:5Siren12LocalizationV13forceLanguage33_2FDBEF65899237DA36B11FA5846AD0EALLAC0D0OSgvp":{"name":"forceLanguage","abstract":"

    Overrides the default localization of a user’s device when presenting the update message and button titles in the alert.

    ","parent_name":"Localization"},"Structs/Localization.html#/s:5Siren12LocalizationV7appName016andForceLanguageB0ACSSSg_AC0G0OSgtcfc":{"name":"init(appName:andForceLanguageLocalization:)","abstract":"

    Initializes

    ","parent_name":"Localization"},"Structs/Localization.html#/s:5Siren12LocalizationV12alertMessage25forCurrentAppStoreVersionS2S_tF":{"name":"alertMessage(forCurrentAppStoreVersion:)","abstract":"

    The localized string for the UIAlertController‘s message field. .

    ","parent_name":"Localization"},"Structs/Localization.html#/s:5Siren12LocalizationV10alertTitleSSyF":{"name":"alertTitle()","abstract":"

    The localized string for the UIAlertController‘s title field. .

    ","parent_name":"Localization"},"Structs/Localization.html#/s:5Siren12LocalizationV19nextTimeButtonTitleSSyF":{"name":"nextTimeButtonTitle()","abstract":"

    The localized string for the Next time UIAlertAction.

    ","parent_name":"Localization"},"Structs/Localization.html#/s:5Siren12LocalizationV15skipButtonTitleSSyF":{"name":"skipButtonTitle()","abstract":"

    The localized string for the Skip this version UIAlertAction.

    ","parent_name":"Localization"},"Structs/Localization.html#/s:5Siren12LocalizationV17updateButtonTitleSSyF":{"name":"updateButtonTitle()","abstract":"

    The localized string for the Update UIAlertAction.

    ","parent_name":"Localization"},"Structs/AlertConstants.html#/s:5Siren14AlertConstantsV12alertMessageSSvpZ":{"name":"alertMessage","abstract":"

    The text that conveys the message that there is an app update available

    ","parent_name":"AlertConstants"},"Structs/AlertConstants.html#/s:5Siren14AlertConstantsV10alertTitleSSvpZ":{"name":"alertTitle","abstract":"

    The alert title which defaults to Update Available.

    ","parent_name":"AlertConstants"},"Structs/AlertConstants.html#/s:5Siren14AlertConstantsV19nextTimeButtonTitleSSvpZ":{"name":"nextTimeButtonTitle","abstract":"

    The button text that conveys the message that the user should be prompted to update next time the app launches.

    ","parent_name":"AlertConstants"},"Structs/AlertConstants.html#/s:5Siren14AlertConstantsV15skipButtonTitleSSvpZ":{"name":"skipButtonTitle","abstract":"

    The text that conveys the message that the the user wants to skip this verison update.

    ","parent_name":"AlertConstants"},"Structs/AlertConstants.html#/s:5Siren14AlertConstantsV17updateButtonTitleSSvpZ":{"name":"updateButtonTitle","abstract":"

    The button text that conveys the message that the user would like to update the app right away.

    ","parent_name":"AlertConstants"},"Structs/RulesManager/UpdateType.html#/s:5Siren12RulesManagerV10UpdateTypeO5majoryA2EmF":{"name":"major","abstract":"

    Major release available: A.b.c.d

    ","parent_name":"UpdateType"},"Structs/RulesManager/UpdateType.html#/s:5Siren12RulesManagerV10UpdateTypeO5minoryA2EmF":{"name":"minor","abstract":"

    Minor release available: a.B.c.d

    ","parent_name":"UpdateType"},"Structs/RulesManager/UpdateType.html#/s:5Siren12RulesManagerV10UpdateTypeO5patchyA2EmF":{"name":"patch","abstract":"

    Patch release available: a.b.C.d

    ","parent_name":"UpdateType"},"Structs/RulesManager/UpdateType.html#/s:5Siren12RulesManagerV10UpdateTypeO8revisionyA2EmF":{"name":"revision","abstract":"

    Revision release available: a.b.c.D

    ","parent_name":"UpdateType"},"Structs/RulesManager/UpdateType.html#/s:5Siren12RulesManagerV10UpdateTypeO7unknownyA2EmF":{"name":"unknown","abstract":"

    No information available about the update.

    ","parent_name":"UpdateType"},"Structs/RulesManager.html#/s:5Siren12RulesManagerV15releasedForDaysSivp":{"name":"releasedForDays","abstract":"

    The alert will only show up if the current version has already been released for X days.

    ","parent_name":"RulesManager"},"Structs/RulesManager.html#/s:5Siren12RulesManagerV011majorUpdateB0AA0B0Vvp":{"name":"majorUpdateRules","abstract":"

    The Rules that should be used when the App Store version of the app signifies that it is a major version update (A.b.c.d).

    ","parent_name":"RulesManager"},"Structs/RulesManager.html#/s:5Siren12RulesManagerV011minorUpdateB0AA0B0Vvp":{"name":"minorUpdateRules","abstract":"

    The Rules that should be used when the App Store version of the app signifies that it is a minor version update (a.B.c.d).

    ","parent_name":"RulesManager"},"Structs/RulesManager.html#/s:5Siren12RulesManagerV011patchUpdateB0AA0B0Vvp":{"name":"patchUpdateRules","abstract":"

    The Rules that should be used when the App Store version of the app signifies that it is a patch version update (a.b.C.d).

    ","parent_name":"RulesManager"},"Structs/RulesManager.html#/s:5Siren12RulesManagerV014revisionUpdateB0AA0B0Vvp":{"name":"revisionUpdateRules","abstract":"

    The Rules that should be used when the App Store version of the app signifies that it is a revision version update (a.b.c.D).

    ","parent_name":"RulesManager"},"Structs/RulesManager.html#/s:5Siren12RulesManagerV011majorUpdateB005minoreB005patcheB008revisioneB050showAlertAfterCurrentVersionHasBeenReleasedForDaysAcA0B0V_A3JSitcfc":{"name":"init(majorUpdateRules:minorUpdateRules:patchUpdateRules:revisionUpdateRules:showAlertAfterCurrentVersionHasBeenReleasedForDays:)","abstract":"

    Initializer that sets update-specific Rules for all updates (e.g., major, minor, patch, revision).","parent_name":"RulesManager"},"Structs/RulesManager.html#/s:5Siren12RulesManagerV06globalB050showAlertAfterCurrentVersionHasBeenReleasedForDaysAcA0B0V_Sitcfc":{"name":"init(globalRules:showAlertAfterCurrentVersionHasBeenReleasedForDays:)","abstract":"

    Initializer that sets the same update Rules for all types of updates (e.g., major, minor, patch, revision).","parent_name":"RulesManager"},"Structs/RulesManager.html#/s:5Siren12RulesManagerV04loadB13ForUpdateTypeyAA0B0VAC0fG0OF":{"name":"loadRulesForUpdateType(_:)","abstract":"

    Returns the appropriate update rules based on the type of version that is returned from the API.

    ","parent_name":"RulesManager"},"Structs/RulesManager.html#/s:5Siren12RulesManagerV7defaultACvpZ":{"name":"default","abstract":"

    The default RulesManager.

    ","parent_name":"RulesManager"},"Structs/RulesManager/UpdateType.html":{"name":"UpdateType","abstract":"

    Informs Siren of the type of update that is available so that","parent_name":"RulesManager"},"Structs/PresentationManager.html#/s:5Siren19PresentationManagerV17CompletionHandlera":{"name":"CompletionHandler","abstract":"

    Return results or errors obtained from performing a version check with Siren.

    ","parent_name":"PresentationManager"},"Structs/PresentationManager.html#/s:5Siren19PresentationManagerV12localizationAA12LocalizationVvp":{"name":"localization","abstract":"

    The localization data structure that will be used to construct localized strings for the update alert.

    ","parent_name":"PresentationManager"},"Structs/PresentationManager.html#/s:5Siren19PresentationManagerV9tintColorSo7UIColorCSgvp":{"name":"tintColor","abstract":"

    The tint color of the UIAlertController buttons.

    ","parent_name":"PresentationManager"},"Structs/PresentationManager.html#/s:5Siren19PresentationManagerV12alertMessageSSvp":{"name":"alertMessage","abstract":"

    The descriptive update message of the UIAlertController.

    ","parent_name":"PresentationManager"},"Structs/PresentationManager.html#/s:5Siren19PresentationManagerV10alertTitleSSvp":{"name":"alertTitle","abstract":"

    The main message of the UIAlertController.

    ","parent_name":"PresentationManager"},"Structs/PresentationManager.html#/s:5Siren19PresentationManagerV19nextTimeButtonTitleSSvp":{"name":"nextTimeButtonTitle","abstract":"

    The Next time button text of the UIAlertController.

    ","parent_name":"PresentationManager"},"Structs/PresentationManager.html#/s:5Siren19PresentationManagerV15skipButtonTitleSSvp":{"name":"skipButtonTitle","abstract":"

    The Skip this version button text of the UIAlertController.

    ","parent_name":"PresentationManager"},"Structs/PresentationManager.html#/s:5Siren19PresentationManagerV17updateButtonTitleSSvp":{"name":"updateButtonTitle","abstract":"

    The Update button text of the UIAlertController.

    ","parent_name":"PresentationManager"},"Structs/PresentationManager.html#/s:5Siren19PresentationManagerV15alertController33_CEF2109017F934DAB33AED8753BA096CLLSo07UIAlertE0CSgvp":{"name":"alertController","abstract":"

    The instance of the UIAlertController used to present the update alert.

    ","parent_name":"PresentationManager"},"Structs/PresentationManager.html#/s:5Siren19PresentationManagerV13updaterWindow33_CEF2109017F934DAB33AED8753BA096CLLSo8UIWindowCvp":{"name":"updaterWindow","abstract":"

    The UIWindow instance that presents the SirenViewController.

    ","parent_name":"PresentationManager"},"Structs/PresentationManager.html#/s:5Siren19PresentationManagerV14alertTintColor7appName0D5Title0D7Message012updateButtonI008nextTimelI004skiplI025forceLanguageLocalizationACSo7UIColorCSg_SSSgS5SAA0R0V0Q0OSgtcfc":{"name":"init(alertTintColor:appName:alertTitle:alertMessage:updateButtonTitle:nextTimeButtonTitle:skipButtonTitle:forceLanguageLocalization:)","abstract":"

    PresentationManager‘s public initializer.

    ","parent_name":"PresentationManager"},"Structs/PresentationManager.html#/s:5Siren19PresentationManagerV7defaultACvpZ":{"name":"default","abstract":"

    The default PresentationManager.

    ","parent_name":"PresentationManager"},"Structs/PresentationManager.html#/s:5Siren19PresentationManagerV12presentAlert9withRules25forCurrentAppStoreVersion10completionyAA0G0V_SSyAA0E6ActionOcSgtF":{"name":"presentAlert(withRules:forCurrentAppStoreVersion:completion:)","abstract":"

    Constructs the localized update alert UIAlertController object.

    ","parent_name":"PresentationManager"},"Structs/PresentationManager.html#/s:5Siren19PresentationManagerV17updateAlertAction33_CEF2109017F934DAB33AED8753BA096CLL10completionSo07UIAlertF0CyAA0eF0OcSg_tF":{"name":"updateAlertAction(completion:)","abstract":"

    The UIAlertAction that is executed when the Update option is selected.

    ","parent_name":"PresentationManager"},"Structs/PresentationManager.html#/s:5Siren19PresentationManagerV19nextTimeAlertAction33_CEF2109017F934DAB33AED8753BA096CLL10completionSo07UIAlertG0CyAA0fG0OcSg_tF":{"name":"nextTimeAlertAction(completion:)","abstract":"

    The UIAlertAction that is executed when the Next time option is selected.

    ","parent_name":"PresentationManager"},"Structs/PresentationManager.html#/s:5Siren19PresentationManagerV15skipAlertAction33_CEF2109017F934DAB33AED8753BA096CLL25forCurrentAppStoreVersion10completionSo07UIAlertF0CSS_yAA0eF0OcSgtF":{"name":"skipAlertAction(forCurrentAppStoreVersion:completion:)","abstract":"

    The UIAlertAction that is executed when the Skip this version option is selected.

    ","parent_name":"PresentationManager"},"Structs/APIManager/Constants.html#/s:5Siren10APIManagerV9Constants33_8071139324B24E2065F4037045A8D960LLV8bundleIDSSvpZ":{"name":"bundleID","abstract":"

    Constant for the bundleId parameter in the iTunes Lookup API request.

    ","parent_name":"Constants"},"Structs/APIManager/Constants.html#/s:5Siren10APIManagerV9Constants33_8071139324B24E2065F4037045A8D960LLV7countrySSvpZ":{"name":"country","abstract":"

    Constant for the country parameter in the iTunes Lookup API request.

    ","parent_name":"Constants"},"Structs/APIManager/Constants.html":{"name":"Constants","abstract":"

    Constants used in the APIManager.

    ","parent_name":"APIManager"},"Structs/APIManager.html#/s:5Siren10APIManagerV17CompletionHandlera":{"name":"CompletionHandler","abstract":"

    Return results or errors obtained from performing a version check with Siren.

    ","parent_name":"APIManager"},"Structs/APIManager.html#/s:5Siren10APIManagerV11countryCodeSSSgvp":{"name":"countryCode","abstract":"

    The region or country of an App Store in which the app is available.","parent_name":"APIManager"},"Structs/APIManager.html#/s:5Siren10APIManagerV11countryCodeACSSSg_tcfc":{"name":"init(countryCode:)","abstract":"

    Initializes APIManager to the region or country of an App Store in which the app is available.","parent_name":"APIManager"},"Structs/APIManager.html#/s:5Siren10APIManagerV7defaultACvpZ":{"name":"default","abstract":"

    The default APIManager.

    ","parent_name":"APIManager"},"Structs/APIManager.html#/s:5Siren10APIManagerV26performVersionCheckRequest10completionyyAA11LookupModelVSg_AA10KnownErrorOSgtcSg_tF":{"name":"performVersionCheckRequest(completion:)","abstract":"

    Creates and performs a URLRequest against the iTunes Lookup API.

    ","parent_name":"APIManager"},"Structs/APIManager.html#/s:5Siren10APIManagerV26processVersionCheckResults33_8071139324B24E2065F4037045A8D960LL8withData8response5error10completiony10Foundation0M0VSg_So13NSURLResponseCSgs5Error_pSgyAA11LookupModelVSg_AA05KnownS0OSgtcSgtF":{"name":"processVersionCheckResults(withData:response:error:completion:)","abstract":"

    Parses and maps the the results from the iTunes Lookup API request.

    ","parent_name":"APIManager"},"Structs/APIManager.html#/s:5Siren10APIManagerV13makeITunesURL33_8071139324B24E2065F4037045A8D960LL10Foundation0E0VyKF":{"name":"makeITunesURL()","abstract":"

    Creates the URL that points to the iTunes Lookup API.

    ","parent_name":"APIManager"},"Structs/APIManager.html":{"name":"APIManager","abstract":"

    APIManager for Siren

    "},"Structs/PresentationManager.html":{"name":"PresentationManager","abstract":"

    PresentationManager for Siren

    "},"Structs/RulesManager.html":{"name":"RulesManager","abstract":"

    RulesManager for Siren

    "},"Structs/AlertConstants.html":{"name":"AlertConstants","abstract":"

    The default constants used for the update alert’s messaging.

    "},"Structs/Localization.html":{"name":"Localization","abstract":"

    Localization information and strings for Siren.

    "},"Structs/LookupModel.html":{"name":"LookupModel","abstract":"

    Model representing a selection of results from the iTunes Lookup API.

    "},"Structs/Results.html":{"name":"Results","abstract":"

    The relevant metadata returned from Siren upon completing a successful version check.

    "},"Structs/Rules.html":{"name":"Rules","abstract":"

    Alert Presentation Rules for Siren.

    "},"Structs/DataParser.html":{"name":"DataParser","abstract":"

    Version parsing functions for Siren.

    "},"Extensions/UserDefaults/SirenKeys.html#/s:So14NSUserDefaultsC5SirenE0C4Keys33_0884631E60E090AA276701A736B793BBLLO37PerformVersionCheckOnSubsequentLaunchyA2FmF":{"name":"PerformVersionCheckOnSubsequentLaunch","abstract":"

    Key that notifies Siren to perform a version check and present","parent_name":"SirenKeys"},"Extensions/UserDefaults/SirenKeys.html#/s:So14NSUserDefaultsC5SirenE0C4Keys33_0884631E60E090AA276701A736B793BBLLO22StoredVersionCheckDateyA2FmF":{"name":"StoredVersionCheckDate","abstract":"

    Key that stores the timestamp of the last version check.

    ","parent_name":"SirenKeys"},"Extensions/UserDefaults/SirenKeys.html#/s:So14NSUserDefaultsC5SirenE0C4Keys33_0884631E60E090AA276701A736B793BBLLO20StoredSkippedVersionyA2FmF":{"name":"StoredSkippedVersion","abstract":"

    Key that stores the version that a user decided to skip.

    ","parent_name":"SirenKeys"},"Extensions/UserDefaults/SirenKeys.html":{"name":"SirenKeys","abstract":"

    Siren-specific UserDefaults Keys

    ","parent_name":"UserDefaults"},"Extensions/UserDefaults.html#/s:So14NSUserDefaultsC5SirenE43shouldPerformVersionCheckOnSubsequentLaunchSbvpZ":{"name":"shouldPerformVersionCheckOnSubsequentLaunch","abstract":"

    Sets and Gets a UserDefault around performing a version check on a subsequent launch.

    ","parent_name":"UserDefaults"},"Extensions/UserDefaults.html#/s:So14NSUserDefaultsC5SirenE20storedSkippedVersionSSSgvpZ":{"name":"storedSkippedVersion","abstract":"

    Sets and Gets a UserDefault around storing a version that the user wants to skip updating.

    ","parent_name":"UserDefaults"},"Extensions/UserDefaults.html#/s:So14NSUserDefaultsC5SirenE21alertPresentationDate10Foundation0F0VSgvpZ":{"name":"alertPresentationDate","abstract":"

    Sets and Gets a UserDefault around the last time the user was presented a version update alert.

    ","parent_name":"UserDefaults"},"Extensions/UIAlertController.html#/s:So17UIAlertControllerC5SirenE4show6windowySo8UIWindowC_tF":{"name":"show(window:)","abstract":"

    Presents Siren’s UIAlertController in a new UIWindow.

    ","parent_name":"UIAlertController"},"Extensions/UIAlertController.html#/s:So17UIAlertControllerC5SirenE4hide6windowySo8UIWindowC_tF":{"name":"hide(window:)","abstract":"

    Hides Siren’s UIAlertController within a given window.

    ","parent_name":"UIAlertController"},"Extensions/Date.html#/s:10Foundation4DateV5SirenE4days5sinceSiAC_tFZ":{"name":"days(since:)","abstract":"

    The amount of days passed from a specific source date.

    ","parent_name":"Date"},"Extensions/Date.html#/s:10Foundation4DateV5SirenE4days5sinceSiSgSS_tFZ":{"name":"days(since:)","abstract":"

    The amount of days passed from a specific source date string.

    ","parent_name":"Date"},"Extensions/Bundle/Constants.html#/s:So8NSBundleC5SirenE9ConstantsV15bundleExtensionSSvpZ":{"name":"bundleExtension","abstract":"

    Constant for the .bundle file extension.

    ","parent_name":"Constants"},"Extensions/Bundle/Constants.html#/s:So8NSBundleC5SirenE9ConstantsV11displayNameSSvpZ":{"name":"displayName","abstract":"

    Constant for CFBundleDisplayName.

    ","parent_name":"Constants"},"Extensions/Bundle/Constants.html#/s:So8NSBundleC5SirenE9ConstantsV19englishLocalizationSSvpZ":{"name":"englishLocalization","abstract":"

    Constant for the default US English localization.

    ","parent_name":"Constants"},"Extensions/Bundle/Constants.html#/s:So8NSBundleC5SirenE9ConstantsV16projectExtensionSSvpZ":{"name":"projectExtension","abstract":"

    Constant for the project file extension.

    ","parent_name":"Constants"},"Extensions/Bundle/Constants.html#/s:So8NSBundleC5SirenE9ConstantsV18shortVersionStringSSvpZ":{"name":"shortVersionString","abstract":"

    Constant for CFBundleShortVersionString.

    ","parent_name":"Constants"},"Extensions/Bundle/Constants.html#/s:So8NSBundleC5SirenE9ConstantsV5tableSSvpZ":{"name":"table","abstract":"

    Constant for the localization table.

    ","parent_name":"Constants"},"Extensions/Bundle/Constants.html":{"name":"Constants","abstract":"

    Constants used in the Bundle extension.

    ","parent_name":"Bundle"},"Extensions/Bundle.html#/s:So8NSBundleC5SirenE7versionSSSgyFZ":{"name":"version()","abstract":"

    Fetches the current verison of the app.

    ","parent_name":"Bundle"},"Extensions/Bundle.html#/s:So8NSBundleC5SirenE15localizedString6forKey20andForceLocalizationS2S_AC0I0V8LanguageOSgtFZ":{"name":"localizedString(forKey:andForceLocalization:)","abstract":"

    Returns the localized string for a given default string.

    ","parent_name":"Bundle"},"Extensions/Bundle.html#/s:So8NSBundleC5SirenE19bestMatchingAppNameSSyFZ":{"name":"bestMatchingAppName()","abstract":"

    The appropriate name for the app to be displayed in the update alert.

    ","parent_name":"Bundle"},"Extensions/Bundle.html#/s:So8NSBundleC5SirenE15sirenBundlePath33_9C775CF4CEC7A5F21E625F58C71BDF22LLSSSgyFZ":{"name":"sirenBundlePath()","abstract":"

    The path to Siren’s localization Bundle.

    ","parent_name":"Bundle"},"Extensions/Bundle.html#/s:So8NSBundleC5SirenE21sirenForcedBundlePath33_9C775CF4CEC7A5F21E625F58C71BDF22LL25forceLanguageLocalizationSSSgAC0R0V0Q0O_tFZ":{"name":"sirenForcedBundlePath(forceLanguageLocalization:)","abstract":"

    The path for a particular language localizationin Siren’s localization Bundle.

    ","parent_name":"Bundle"},"Extensions/Bundle.html#/s:So8NSBundleC5SirenE14deviceLanguage33_9C775CF4CEC7A5F21E625F58C71BDF22LLAC12LocalizationV0D0OSgyFZ":{"name":"deviceLanguage()","abstract":"

    The user’s preferred language based on their device’s localization.

    ","parent_name":"Bundle"},"Extensions/Bundle.html":{"name":"Bundle"},"Extensions/Date.html":{"name":"Date"},"Extensions/UIAlertController.html":{"name":"UIAlertController"},"Extensions/UserDefaults.html":{"name":"UserDefaults"},"Enums/KnownError.html#/s:5Siren10KnownErrorO20appStoreAppIDFailureyA2CmF":{"name":"appStoreAppIDFailure","abstract":"

    Error retrieving trackId as the JSON does not contain a ‘trackId’ key.

    ","parent_name":"KnownError"},"Enums/KnownError.html#/s:5Siren10KnownErrorO33appStoreDataRetrievalEmptyResultsyA2CmF":{"name":"appStoreDataRetrievalEmptyResults","abstract":"

    Error retrieving App Store data as JSON results were empty. Is your app available in the US? If not, change the countryCode variable to fix this error.

    ","parent_name":"KnownError"},"Enums/KnownError.html#/s:5Siren10KnownErrorO28appStoreDataRetrievalFailureyACs0C0_pSg_tcACmF":{"name":"appStoreDataRetrievalFailure(underlyingError:)","abstract":"

    Error retrieving App Store data as an error was returned.

    ","parent_name":"KnownError"},"Enums/KnownError.html#/s:5Siren10KnownErrorO26appStoreJSONParsingFailureyACs0C0_p_tcACmF":{"name":"appStoreJSONParsingFailure(underlyingError:)","abstract":"

    Error parsing App Store JSON data.

    ","parent_name":"KnownError"},"Enums/KnownError.html#/s:5Siren10KnownErrorO28appStoreOSVersionUnsupportedyA2CmF":{"name":"appStoreOSVersionUnsupported","abstract":"

    The version of iOS on the device is lower than that of the one required by the app verison update.

    ","parent_name":"KnownError"},"Enums/KnownError.html#/s:5Siren10KnownErrorO27appStoreVersionArrayFailureyA2CmF":{"name":"appStoreVersionArrayFailure","abstract":"

    Error retrieving App Store verson number as the JSON does not contain a version key.

    ","parent_name":"KnownError"},"Enums/KnownError.html#/s:5Siren10KnownErrorO25currentVersionReleaseDateyA2CmF":{"name":"currentVersionReleaseDate","abstract":"

    The currentVersionReleaseDate key is missing in the JSON payload. Please leave an issue on https://github.com/ArtSabintsev/Siren with as many details as possible.

    ","parent_name":"KnownError"},"Enums/KnownError.html#/s:5Siren10KnownErrorO12malformedURLyA2CmF":{"name":"malformedURL","abstract":"

    One of the iTunes URLs used in Siren is malformed. Please leave an issue on https://github.com/ArtSabintsev/Siren with as many details as possible.

    ","parent_name":"KnownError"},"Enums/KnownError.html#/s:5Siren10KnownErrorO15missingBundleIDyA2CmF":{"name":"missingBundleID","abstract":"

    Please make sure that you have set a Bundle Identifier in your project.

    ","parent_name":"KnownError"},"Enums/KnownError.html#/s:5Siren10KnownErrorO17noUpdateAvailableyA2CmF":{"name":"noUpdateAvailable","abstract":"

    No new update available.

    ","parent_name":"KnownError"},"Enums/KnownError.html#/s:5Siren10KnownErrorO16recentlyPromptedyA2CmF":{"name":"recentlyPrompted","abstract":"

    Siren will not present an update alert if it performed one too recently. If you would like to present an alert every time Siren is called, please consider setting the UpdatePromptFrequency.immediately rule in RulesManager

    ","parent_name":"KnownError"},"Enums/KnownError.html#/s:5Siren10KnownErrorO15releasedTooSoonyACSi_SitcACmF":{"name":"releasedTooSoon(daysSinceRelease:releasedForDays:)","abstract":"

    The app has been released for X days, but Siren cannot prompt the user until Y (where Y > X) days have passed.

    ","parent_name":"KnownError"},"Enums/KnownError.html#/s:5Siren10KnownErrorO17skipVersionUpdateyACSS_SStcACmF":{"name":"skipVersionUpdate(installedVersion:appStoreVersion:)","abstract":"

    The user has opted to skip updating their current version of the app to the current App Store version.

    ","parent_name":"KnownError"},"Enums/KnownError.html#/s:5Siren10KnownErrorO20localizedDescriptionSSvp":{"name":"localizedDescription","abstract":"

    The localized description for each error handled by Siren.

    ","parent_name":"KnownError"},"Enums/KnownError.html#/s:5Siren10KnownErrorO05sirenC033_B3C911EAD28C83CC211C07566B0F499ALLSSvpZ":{"name":"sirenError","abstract":"

    An easily identifiable prefix for all errors thrown by Siren.

    ","parent_name":"KnownError"},"Enums/AlertAction.html#/s:5Siren11AlertActionO8appStoreyA2CmF":{"name":"appStore","abstract":"

    The user clicked on the Update option, which took them to the app’s App Store page.

    ","parent_name":"AlertAction"},"Enums/AlertAction.html#/s:5Siren11AlertActionO8nextTimeyA2CmF":{"name":"nextTime","abstract":"

    The user clicked on the Next Time option, which dismissed the alert.

    ","parent_name":"AlertAction"},"Enums/AlertAction.html#/s:5Siren11AlertActionO4skipyA2CmF":{"name":"skip","abstract":"

    The user clicked on the Skip this version option, which dismissed the alert.

    ","parent_name":"AlertAction"},"Enums/AlertAction.html#/s:5Siren11AlertActionO7unknownyA2CmF":{"name":"unknown","abstract":"

    (Default) The user never chose an option. This is returned when an error is thrown by Siren.

    ","parent_name":"AlertAction"},"Enums/AlertAction.html":{"name":"AlertAction","abstract":"

    The UIAlertController button that was pressed upon being presented an update alert.

    "},"Enums/KnownError.html":{"name":"KnownError","abstract":"

    Enumerates all potentials errors that Siren can handle.

    "},"Classes/SirenViewController.html#/c:@M@Siren@objc(cs)SirenViewController(py)preferredStatusBarStyle":{"name":"preferredStatusBarStyle","abstract":"

    UIStatusBarStyle override.

    ","parent_name":"SirenViewController"},"Classes/Siren.html#/s:5SirenAAC14ResultsHandlera":{"name":"ResultsHandler","abstract":"

    Return results or errors obtained from performing a version check with Siren.

    ","parent_name":"Siren"},"Classes/Siren.html#/s:5SirenAAC6sharedABvpZ":{"name":"shared","abstract":"

    The Siren singleton. The main point of entry to the Siren library.

    ","parent_name":"Siren"},"Classes/Siren.html#/s:5SirenAAC12debugEnabledSbvp":{"name":"debugEnabled","abstract":"

    The debug flag, which is disabled by default.","parent_name":"Siren"},"Classes/Siren.html#/s:5SirenAAC10apiManagerAA10APIManagerVvp":{"name":"apiManager","abstract":"

    The manager that controls the App Store API that is","parent_name":"Siren"},"Classes/Siren.html#/s:5SirenAAC19presentationManagerAA012PresentationC0Vvp":{"name":"presentationManager","abstract":"

    The manager that controls the update alert’s string localization and tint color.

    ","parent_name":"Siren"},"Classes/Siren.html#/s:5SirenAAC12rulesManagerAA05RulesC0Vvp":{"name":"rulesManager","abstract":"

    The manager that controls the type of alert that should be displayed","parent_name":"Siren"},"Classes/Siren.html#/s:5SirenAAC23currentInstalledVersionSSSgvp":{"name":"currentInstalledVersion","abstract":"

    The current installed version of your app.

    ","parent_name":"Siren"},"Classes/Siren.html#/s:5SirenAAC23didBecomeActiveObserverSo8NSObject_pSgvp":{"name":"didBecomeActiveObserver","abstract":"

    The retained NotificationCenter observer that listens for UIApplication.didBecomeActiveNotification notifications.

    ","parent_name":"Siren"},"Classes/Siren.html#/s:5SirenAAC21alertPresentationDate33_7DFB1BC200A6C64FBED860A3A8153B65LL10Foundation0D0VSgvp":{"name":"alertPresentationDate","abstract":"

    The last date that an alert was presented to the user.

    ","parent_name":"Siren"},"Classes/Siren.html#/s:5SirenAAC5appID33_7DFB1BC200A6C64FBED860A3A8153B65LLSiSgvp":{"name":"appID","abstract":"

    The App Store’s unique identifier for an app.

    ","parent_name":"Siren"},"Classes/Siren.html#/s:5SirenAAC14resultsHandler33_7DFB1BC200A6C64FBED860A3A8153B65LLyAA7ResultsVSg_AA10KnownErrorOSgtcSgvp":{"name":"resultsHandler","abstract":"

    The completion handler used to return the results or errors returned by Siren.

    ","parent_name":"Siren"},"Classes/Siren.html#/c:@M@Siren@objc(cs)Siren(im)init":{"name":"init()","abstract":"

    The initialization method.

    ","parent_name":"Siren"},"Classes/Siren.html#/s:5SirenAAC4wail10completionyyAA7ResultsVSg_AA10KnownErrorOSgtcSg_tF":{"name":"wail(completion:)","parent_name":"Siren"},"Classes/Siren.html#/s:5SirenAAC14launchAppStoreyyF":{"name":"launchAppStore()","abstract":"

    Launches the AppStore in two situations when the user clicked the Update button in the UIAlertController modal.

    ","parent_name":"Siren"},"Classes/Siren.html#/s:5SirenAAC19performVersionCheckyyF":{"name":"performVersionCheck()","abstract":"

    Initiates the uni-directional version checking flow.

    ","parent_name":"Siren"},"Classes/Siren.html#/s:5SirenAAC8validate5modelyAA11LookupModelV_tF":{"name":"validate(model:)","abstract":"

    Validates the parsed and mapped iTunes Lookup Model","parent_name":"Siren"},"Classes/Siren.html#/s:5SirenAAC45determineIfAlertPresentationRulesAreSatisfied25forCurrentAppStoreVersion14andLookupModelySS_AA0oP0VtF":{"name":"determineIfAlertPresentationRulesAreSatisfied(forCurrentAppStoreVersion:andLookupModel:)","abstract":"

    Determines if the update alert can be presented based on the","parent_name":"Siren"},"Classes/Siren.html#/s:5SirenAAC12presentAlert9withRules25forCurrentAppStoreVersion5model13andUpdateTypeyAA0E0V_SSAA11LookupModelVAA0E7ManagerV0mN0OtF":{"name":"presentAlert(withRules:forCurrentAppStoreVersion:model:andUpdateType:)","abstract":"

    Presents the update alert to the end user.","parent_name":"Siren"},"Classes/Siren.html#/s:5SirenAAC12addObserversyyF":{"name":"addObservers()","abstract":"

    Add an observer that listens for app launching/relaunching","parent_name":"Siren"},"Classes/Siren.html":{"name":"Siren","abstract":"

    The Siren Class.

    "},"Classes/SirenViewController.html":{"name":"SirenViewController","abstract":"

    UIViewController Extension for Siren

    "},"Classes.html":{"name":"Classes","abstract":"

    The following classes are available globally.

    "},"Enums.html":{"name":"Enumerations","abstract":"

    The following enumerations are available globally.

    "},"Extensions.html":{"name":"Extensions","abstract":"

    The following extensions are available globally.

    "},"Structs.html":{"name":"Structures","abstract":"

    The following structures are available globally.

    "}} \ No newline at end of file