├── .github └── ISSUE_TEMPLATE.md ├── .gitignore ├── .travis.yml ├── Assets ├── picForcedUpdate.png ├── picOptionalUpdate.png └── picSkippedUpdate.png ├── CONTRIBUTORS.md ├── Gemfile ├── Gemfile.lock ├── Harpy.podspec ├── Harpy ├── Harpy.bundle │ ├── ar.lproj │ │ └── HarpyLocalizable.strings │ ├── cs.lproj │ │ └── HarpyLocalizable.strings │ ├── da.lproj │ │ └── HarpyLocalizable.strings │ ├── de.lproj │ │ └── HarpyLocalizable.strings │ ├── el.lproj │ │ └── HarpyLocalizable.strings │ ├── en.lproj │ │ └── HarpyLocalizable.strings │ ├── es.lproj │ │ └── HarpyLocalizable.strings │ ├── et.lproj │ │ └── HarpyLocalizable.strings │ ├── eu.lproj │ │ └── HarpyLocalizable.strings │ ├── fa-AF.lproj │ │ └── HarpyLocalizable.strings │ ├── fa-IR.lproj │ │ └── HarpyLocalizable.strings │ ├── fa.lproj │ │ └── HarpyLocalizable.strings │ ├── fi.lproj │ │ └── HarpyLocalizable.strings │ ├── fr.lproj │ │ └── HarpyLocalizable.strings │ ├── he.lproj │ │ └── HarpyLocalizable.strings │ ├── hr.lproj │ │ └── HarpyLocalizable.strings │ ├── hu.lproj │ │ └── HarpyLocalizable.strings │ ├── hy.lproj │ │ └── HarpyLocalizable.strings │ ├── id.lproj │ │ └── HarpyLocalizable.strings │ ├── it.lproj │ │ └── HarpyLocalizable.strings │ ├── ja.lproj │ │ └── HarpyLocalizable.strings │ ├── ko.lproj │ │ └── HarpyLocalizable.strings │ ├── lt.lproj │ │ └── HarpyLocalizable.strings │ ├── lv.lproj │ │ └── HarpyLocalizable.strings │ ├── ms.lproj │ │ └── HarpyLocalizable.strings │ ├── nb-NO.lproj │ │ └── HarpyLocalizable.strings │ ├── nl.lproj │ │ └── HarpyLocalizable.strings │ ├── pl.lproj │ │ └── HarpyLocalizable.strings │ ├── pt-PT.lproj │ │ └── HarpyLocalizable.strings │ ├── pt.lproj │ │ └── HarpyLocalizable.strings │ ├── ru.lproj │ │ └── HarpyLocalizable.strings │ ├── sl.lproj │ │ └── HarpyLocalizable.strings │ ├── sr-Cyrl.lproj │ │ └── HarpyLocalizable.strings │ ├── sr-Latn.lproj │ │ └── HarpyLocalizable.strings │ ├── sv.lproj │ │ └── HarpyLocalizable.strings │ ├── th.lproj │ │ └── HarpyLocalizable.strings │ ├── tr.lproj │ │ └── HarpyLocalizable.strings │ ├── uk.lproj │ │ └── HarpyLocalizable.strings │ ├── ur.lproj │ │ └── HarpyLocalizable.strings │ ├── vi.lproj │ │ └── HarpyLocalizable.strings │ ├── zh-Hans.lproj │ │ └── HarpyLocalizable.strings │ └── zh-Hant.lproj │ │ └── HarpyLocalizable.strings ├── Harpy.h └── Harpy.m ├── HarpyExample ├── Default-568h@2x.png ├── Harpy │ └── Info.plist ├── HarpyExample.xcodeproj │ ├── project.pbxproj │ └── xcshareddata │ │ └── xcschemes │ │ ├── Harpy.xcscheme │ │ └── HarpyExample.xcscheme ├── HarpyExample │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── Assets.xcassets │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ ├── Info.plist │ ├── ViewController.h │ ├── ViewController.m │ └── main.m └── HarpyExampleTests │ ├── HarpyTests.m │ └── Info.plist ├── ISSUE_TEMPLATE.md ├── LICENSE.md └── README.md /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ====== 2 | Before posting an issue, please make sure your issue has not already been resolved or answered elsewhere. 3 | 4 | Common Issue #1: 5 | > "How do I use Harpy if my app is not available in the US App Store?"" 6 | 7 | Add the corresponding country code when setting up Harpy. 8 | 9 | Common Issue #2: 10 | > "Support for macOS App Store." 11 | 12 | Harpy does not and will not support the macOS App Store. 13 | 14 | Common Issue #3: 15 | > "Support for prompting TestFlight users to update to the newest beta build." 16 | 17 | Harpy does not support this functionality. There is no publicly accessible TestFlight API akin to that of the public App Store API that harpy can utilize to provide this functionality. 18 | 19 | Please delete this text before submitting a new issue. 20 | 21 | Common Issue #4: 22 | > "Infinite Loop Updates" 23 | 24 | On rare occasion, Apple may update their App Store JSON _faster_ than the binary appears in the App Store. Due to this issue, developers who make use of the `.force` option may be prompting their users with app updates whenever launching the app, effectively creating an infinite loop until the binary appears. To fix this issue, set the `setShowAlertAfterCurrentVersionHasBeenReleasedForDays` variable to a value greater than 0. By default, it's set to 1 to avoid this issue, as the alert does not show until 24-hours/1-day has passed 25 | 26 | ====== 27 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | .DS_Store 3 | build/ 4 | *.pbxuser 5 | !default.pbxuser 6 | *.mode1v3 7 | !default.mode1v3 8 | *.mode2v3 9 | !default.mode2v3 10 | *.perspectivev3 11 | !default.perspectivev3 12 | *.xcworkspace 13 | !default.xcworkspace 14 | xcuserdata 15 | profile 16 | *.moved-aside 17 | DerivedData 18 | .idea/ 19 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: objective-c 2 | osx_image: xcode9.3 3 | 4 | notifications: 5 | email: false 6 | env: 7 | - LC_CTYPE=en_US.UTF-8 LANG=en_US.UTF-8 8 | before_install: 9 | - rvm install ruby-2.4.1 10 | - gem install cocoapods 11 | - gem install xcpretty -N 12 | - brew update 13 | script: 14 | - set -o pipefail 15 | - xcodebuild -project HarpyExample/HarpyExample.xcodeproj -scheme HarpyExample -sdk iphonesimulator ONLY_ACTIVE_ARCH=NO | xcpretty -c 16 | -------------------------------------------------------------------------------- /Assets/picForcedUpdate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArtSabintsev/Harpy/e76b5e061826d2abfdd6c2a1ae7baa3229ab4186/Assets/picForcedUpdate.png -------------------------------------------------------------------------------- /Assets/picOptionalUpdate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArtSabintsev/Harpy/e76b5e061826d2abfdd6c2a1ae7baa3229ab4186/Assets/picOptionalUpdate.png -------------------------------------------------------------------------------- /Assets/picSkippedUpdate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArtSabintsev/Harpy/e76b5e061826d2abfdd6c2a1ae7baa3229ab4186/Assets/picSkippedUpdate.png -------------------------------------------------------------------------------- /CONTRIBUTORS.md: -------------------------------------------------------------------------------- 1 | ### Created and maintained by 2 | [Arthur Ariel Sabintsev](http://www.sabintsev.com/) 3 | 4 | ### Project Contributors 5 | - **v1.0.1** 6 | - [Pius Uzamere](https://github.com/pius) 7 | - **v1.5.0** 8 | - [Aaron Brager](http://www.github.com/getaaron) 9 | - **v2.0.0** 10 | - [Claas Lange](https://github.com/claaslange) 11 | - [Josh T. Brown](https://github.com/joshuatbrown) 12 | - **v2.3.0** 13 | - [David Keegan](https://github.com/kgn) 14 | - **v2.3.1** 15 | - [Rui Perese](https://github.com/RuiAAPeres) 16 | - **v2.3.2** 17 | - [Mark Rickert](https://github.com/markrickert) 18 | - **v2.3.3** 19 | - [Erick](https://github.com/dexcell0) 20 | - **v2.3.4** 21 | - [Ercillagorka](https://github.com/ercillagorka) 22 | - **v2.3.5** 23 | - [TrentW](https://github.com/trentw) 24 | - **v2.3.6** 25 | - [Jamie Ly](http://github,com/jamiely) 26 | - **v2.3.8** 27 | - [Thomas Hempel](https://github.com/thomashempel) 28 | - **v2.4.0** 29 | - [Aaron Brager](http://www.github.com/getaaron) 30 | - [Borut Tomažin](https://github.com/borut-t) 31 | - **v2.5.0** 32 | - [Borut Tomažin](https://github.com/borut-t) 33 | - **v2.5.1** 34 | - [Bertie Liu](https://github.com/https://github.com/aceisScope) 35 | - **v2.5.2** 36 | - [Borut Tomažin](https://github.com/borut-t) 37 | - **v2.6.0** 38 | - [Rahul Jiresal](https://github.com/rahuljiresal) 39 | - **v2.7.0** 40 | - [Patrick Debois](https://github.com/jedi4ever) 41 | - **v3.1.0** 42 | - [Jon Andersen](https://github.com/jonandersen) 43 | - **v3.1.1** 44 | - [burakkilic](https://github.com/burakkilic) 45 | - **v3.1.2** 46 | - [Daniel](https://github.com/danieltskv) 47 | - **v3.3.0** 48 | - [frranck](https://github.com/frranck) 49 | - **v3.3.1** 50 | - [Nathan Hosselton](https://github.com/nathanhosselton) 51 | - **v3.3.2** 52 | - [Jędrek Kostecki](https://github.com/jedrekk) 53 | - **v3.3.3** 54 | - [ipedro](https://github.com/ipedro) 55 | - **v3.3.4** 56 | - [Jaroslav_](https://github.com/jaroslavas) 57 | - **v3.3.8** 58 | - [Parnsind Hantrakool](https://github.com/kong707) 59 | - **v3.3.9** 60 | - [Tibor Molnár](https://github.com/fatalaa) 61 | - **v3.3.10** 62 | - [Tanel Suurhans](https://github.com/tanelsuurhans) 63 | - [Jaroslav_](https://github.com/jaroslavas) 64 | - **v3.4.3** 65 | - [Zaid M. Said](https://github.com/SentulAsia) 66 | - **v3.4.4** 67 | - [Vahan Margaryan](https://github.com/VahanMargaryan) 68 | - **v3.4.5** 69 | - [Justus Kandzi](https://github.com/jkandzi) 70 | - **v3.4.8** 71 | - [Mark Berner](https://github.com/mark2b) 72 | - **v3.4.10** 73 | - [Josip Injic](https://github.com/jinjic) 74 | - **v4.0.3** 75 | - [Nikolay Petrov](https://github.com/nikolay-petrov) 76 | - **v4.0.4** 77 | - [Ilija Puaca](https://github.com/ilijapuaca) 78 | - **4.0.5** 79 | - [Thi](https://github.com/thii) 80 | - **v4.0.6** 81 | - [Kristaps Grinbergs](https://github.com/fassko) 82 | - **v4.0.7** 83 | - [Luciano Nascimento](https://github.com/@lucianocn) 84 | - **v4.0.8** 85 | - [Vladislav Jevremović](https://github.com/VladislavJevremovic) 86 | - **v4.0.9** 87 | - [Konstantinos N.](https://github.com/kwstasna) 88 | - **v4.0.10** 89 | - [Christoph Mantler](https://github.com/ChrisixFlash) 90 | - **v4.0.11** 91 | - [Christoph Mantler](https://github.com/ChrisixFlash) 92 | - [xedla](https://github.com/xedla) 93 | - **v4.1.1** 94 | - [Txai Wieser](https://github.com/txaiwieser) 95 | - [Ilija Puaca](https://github.com/ilijapuaca) 96 | - **4.1.2** 97 | - [premyslvlcek](https://github.com/premyslvlcek) 98 | - **4.1.3** 99 | - [Seyed Mojtaba Hosseini Zeidabadi](https://github.com/MojtabaHs) 100 | - **4.1.4** 101 | - [Chanchal Raj](https://github.com/RajChanchal) 102 | - **4.1.5** 103 | - [Ryoh Tsukahara](https://github.com/nixnoughtnothing) 104 | - **4.1.6** 105 | - [Ryoh Tsukahara](https://github.com/nixnoughtnothing) 106 | - **4.1.8** 107 | - [Dmytro Cheverda](https://github.com/dimacheverda) 108 | - **4.1.9** 109 | - [wbison](https://github.com/wbison) 110 | - [Attia Mo](https://github.com/AttiaMo) 111 | - **4.1.10** 112 | - [liyangdal](https://github.com/liyangdal) 113 | - **4.1.12** 114 | - [Tom Clark](https://github.com/iamtomcat) 115 | - **4.1.14** 116 | - [Oleg](https://github.com/suzhaev) 117 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | # A sample Gemfile 2 | source "https://rubygems.org" 3 | 4 | gem "cocoapods" 5 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | CFPropertyList (2.3.6) 5 | activesupport (4.2.10) 6 | i18n (~> 0.7) 7 | minitest (~> 5.1) 8 | thread_safe (~> 0.3, >= 0.3.4) 9 | tzinfo (~> 1.1) 10 | atomos (0.1.2) 11 | claide (1.0.2) 12 | cocoapods (1.4.0) 13 | activesupport (>= 4.0.2, < 5) 14 | claide (>= 1.0.2, < 2.0) 15 | cocoapods-core (= 1.4.0) 16 | cocoapods-deintegrate (>= 1.0.2, < 2.0) 17 | cocoapods-downloader (>= 1.1.3, < 2.0) 18 | cocoapods-plugins (>= 1.0.0, < 2.0) 19 | cocoapods-search (>= 1.0.0, < 2.0) 20 | cocoapods-stats (>= 1.0.0, < 2.0) 21 | cocoapods-trunk (>= 1.3.0, < 2.0) 22 | cocoapods-try (>= 1.1.0, < 2.0) 23 | colored2 (~> 3.1) 24 | escape (~> 0.0.4) 25 | fourflusher (~> 2.0.1) 26 | gh_inspector (~> 1.0) 27 | molinillo (~> 0.6.4) 28 | nap (~> 1.0) 29 | ruby-macho (~> 1.1) 30 | xcodeproj (>= 1.5.4, < 2.0) 31 | cocoapods-core (1.4.0) 32 | activesupport (>= 4.0.2, < 6) 33 | fuzzy_match (~> 2.0.4) 34 | nap (~> 1.0) 35 | cocoapods-deintegrate (1.0.2) 36 | cocoapods-downloader (1.1.3) 37 | cocoapods-plugins (1.0.0) 38 | nap 39 | cocoapods-search (1.0.0) 40 | cocoapods-stats (1.0.0) 41 | cocoapods-trunk (1.3.0) 42 | nap (>= 0.8, < 2.0) 43 | netrc (~> 0.11) 44 | cocoapods-try (1.1.0) 45 | colored2 (3.1.2) 46 | concurrent-ruby (1.0.5) 47 | escape (0.0.4) 48 | fourflusher (2.0.1) 49 | fuzzy_match (2.0.4) 50 | gh_inspector (1.1.3) 51 | i18n (0.9.5) 52 | concurrent-ruby (~> 1.0) 53 | minitest (5.11.3) 54 | molinillo (0.6.4) 55 | nanaimo (0.2.3) 56 | nap (1.1.0) 57 | netrc (0.11.0) 58 | ruby-macho (1.1.0) 59 | thread_safe (0.3.6) 60 | tzinfo (1.2.5) 61 | thread_safe (~> 0.1) 62 | xcodeproj (1.5.6) 63 | CFPropertyList (~> 2.3.3) 64 | atomos (~> 0.1.2) 65 | claide (>= 1.0.2, < 2.0) 66 | colored2 (~> 3.1) 67 | nanaimo (~> 0.2.3) 68 | 69 | PLATFORMS 70 | ruby 71 | 72 | DEPENDENCIES 73 | cocoapods 74 | 75 | BUNDLED WITH 76 | 1.16.1 77 | -------------------------------------------------------------------------------- /Harpy.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = "Harpy" 3 | s.version = "4.1.14" 4 | s.summary = "Notify users that a new version of your iOS app is available, and prompt them with the App Store link." 5 | s.homepage = "https://github.com/ArtSabintsev/Harpy" 6 | s.platform = :ios, '8.0' 7 | s.source = { :git => "https://github.com/ArtSabintsev/Harpy.git", :tag => s.version.to_s } 8 | s.source_files = 'Harpy/*.{h,m}' 9 | s.resources = 'Harpy/Harpy.bundle' 10 | s.requires_arc = true 11 | s.author = { "Arthur Ariel Sabintsev" => "arthur@sabintsev.com" } 12 | s.license = 'MIT' 13 | end 14 | -------------------------------------------------------------------------------- /Harpy/Harpy.bundle/ar.lproj/HarpyLocalizable.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArtSabintsev/Harpy/e76b5e061826d2abfdd6c2a1ae7baa3229ab4186/Harpy/Harpy.bundle/ar.lproj/HarpyLocalizable.strings -------------------------------------------------------------------------------- /Harpy/Harpy.bundle/cs.lproj/HarpyLocalizable.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArtSabintsev/Harpy/e76b5e061826d2abfdd6c2a1ae7baa3229ab4186/Harpy/Harpy.bundle/cs.lproj/HarpyLocalizable.strings -------------------------------------------------------------------------------- /Harpy/Harpy.bundle/da.lproj/HarpyLocalizable.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArtSabintsev/Harpy/e76b5e061826d2abfdd6c2a1ae7baa3229ab4186/Harpy/Harpy.bundle/da.lproj/HarpyLocalizable.strings -------------------------------------------------------------------------------- /Harpy/Harpy.bundle/de.lproj/HarpyLocalizable.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArtSabintsev/Harpy/e76b5e061826d2abfdd6c2a1ae7baa3229ab4186/Harpy/Harpy.bundle/de.lproj/HarpyLocalizable.strings -------------------------------------------------------------------------------- /Harpy/Harpy.bundle/el.lproj/HarpyLocalizable.strings: -------------------------------------------------------------------------------- 1 | /* Update alert message: A new version of {APP NAME} is available. Please update to version {NEW VERSION} now.*/ 2 | "A new version of %@ is available. Please update to version %@ now." = "Μια νέα έκδοση του %@ είναι διαθέσιμη. Παρακαλώ κάντε αναβάθμιση στο %@ τώρα."; 3 | 4 | /* Update alert title */ 5 | "Update Available" = "Διαθέσιμη Ενημέρωση"; 6 | 7 | /* Update alert dismiss button title */ 8 | "Next time" = "Άλλη φορά"; 9 | 10 | /* Update alert skip button title */ 11 | "Skip this version" = "Αγνόησε αυτήν την έκδοση"; 12 | 13 | /* Update alert skip button title */ 14 | "Update" = "Αναβάθμιση"; 15 | -------------------------------------------------------------------------------- /Harpy/Harpy.bundle/en.lproj/HarpyLocalizable.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArtSabintsev/Harpy/e76b5e061826d2abfdd6c2a1ae7baa3229ab4186/Harpy/Harpy.bundle/en.lproj/HarpyLocalizable.strings -------------------------------------------------------------------------------- /Harpy/Harpy.bundle/es.lproj/HarpyLocalizable.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArtSabintsev/Harpy/e76b5e061826d2abfdd6c2a1ae7baa3229ab4186/Harpy/Harpy.bundle/es.lproj/HarpyLocalizable.strings -------------------------------------------------------------------------------- /Harpy/Harpy.bundle/et.lproj/HarpyLocalizable.strings: -------------------------------------------------------------------------------- 1 | /* Update alert message: A new version of {APP NAME} is available. Please update to version {NEW VERSION} now.*/ 2 | "A new version of %@ is available. Please update to version %@ now." = "On ilmunud uus %@ versioon. Palun uuendage %@ versioonini."; 3 | 4 | /* Update alert title */ 5 | "Update Available" = "Uuendus saadaval"; 6 | 7 | /* Update alert dismiss button title */ 8 | "Next time" = "Järgmisel korral"; 9 | 10 | /* Update alert skip button title */ 11 | "Skip this version" = "Jäta see version vahele"; 12 | 13 | /* Update alert skip button title */ 14 | "Update" = "Uuenda"; 15 | -------------------------------------------------------------------------------- /Harpy/Harpy.bundle/eu.lproj/HarpyLocalizable.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArtSabintsev/Harpy/e76b5e061826d2abfdd6c2a1ae7baa3229ab4186/Harpy/Harpy.bundle/eu.lproj/HarpyLocalizable.strings -------------------------------------------------------------------------------- /Harpy/Harpy.bundle/fa-AF.lproj/HarpyLocalizable.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArtSabintsev/Harpy/e76b5e061826d2abfdd6c2a1ae7baa3229ab4186/Harpy/Harpy.bundle/fa-AF.lproj/HarpyLocalizable.strings -------------------------------------------------------------------------------- /Harpy/Harpy.bundle/fa-IR.lproj/HarpyLocalizable.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArtSabintsev/Harpy/e76b5e061826d2abfdd6c2a1ae7baa3229ab4186/Harpy/Harpy.bundle/fa-IR.lproj/HarpyLocalizable.strings -------------------------------------------------------------------------------- /Harpy/Harpy.bundle/fa.lproj/HarpyLocalizable.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArtSabintsev/Harpy/e76b5e061826d2abfdd6c2a1ae7baa3229ab4186/Harpy/Harpy.bundle/fa.lproj/HarpyLocalizable.strings -------------------------------------------------------------------------------- /Harpy/Harpy.bundle/fi.lproj/HarpyLocalizable.strings: -------------------------------------------------------------------------------- 1 | /* Update alert message: A new version of {APP NAME} is available. Please update to version {NEW VERSION} now.*/ 2 | "A new version of %@ is available. Please update to version %@ now." = "%@ on päivittynyt. Ole hyvä ja päivitä versioon %@ nyt."; 3 | 4 | /* Update alert title */ 5 | "Update Available" = "Päivitys saatavilla"; 6 | 7 | /* Update alert dismiss button title */ 8 | "Next time" = "Ensi kerralla"; 9 | 10 | /* Update alert skip button title */ 11 | "Skip this version" = "Jätä tämä versio väliin"; 12 | 13 | /* Update alert skip button title */ 14 | "Update" = "Päivitys"; 15 | -------------------------------------------------------------------------------- /Harpy/Harpy.bundle/fr.lproj/HarpyLocalizable.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArtSabintsev/Harpy/e76b5e061826d2abfdd6c2a1ae7baa3229ab4186/Harpy/Harpy.bundle/fr.lproj/HarpyLocalizable.strings -------------------------------------------------------------------------------- /Harpy/Harpy.bundle/he.lproj/HarpyLocalizable.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArtSabintsev/Harpy/e76b5e061826d2abfdd6c2a1ae7baa3229ab4186/Harpy/Harpy.bundle/he.lproj/HarpyLocalizable.strings -------------------------------------------------------------------------------- /Harpy/Harpy.bundle/hr.lproj/HarpyLocalizable.strings: -------------------------------------------------------------------------------- 1 | /* Update alert message: A new version of {APP NAME} is available. Please update to version {NEW VERSION} now.*/ 2 | "A new version of %@ is available. Please update to version %@ now." = "Nova verzia %@ je stigla. Ažuriraj na verziju %@ sada."; 3 | 4 | /* Update alert title */ 5 | "Update Available" = "Nova ažuriranje je stigla"; 6 | 7 | /* Update alert dismiss button title */ 8 | "Next time" = "Sljedeći put"; 9 | 10 | /* Update alert skip button title */ 11 | "Skip this version" = "Preskoči ovu verziju"; 12 | 13 | /* Update alert skip button title */ 14 | "Update" = "Ažuriraj"; 15 | -------------------------------------------------------------------------------- /Harpy/Harpy.bundle/hu.lproj/HarpyLocalizable.strings: -------------------------------------------------------------------------------- 1 | /* Update alert message: A new version of {APP NAME} is available. Please update to version {NEW VERSION} now.*/ 2 | "A new version of %@ is available. Please update to version %@ now." = "Új verziója érhető el a %@ alkalmazásnak. Kérjük frissítsen a %@ verzióra."; 3 | 4 | /* Update alert title */ 5 | "Update Available" = "Új frissítés érhető el"; 6 | 7 | /* Update alert dismiss button title */ 8 | "Next time" = "Később"; 9 | 10 | /* Update alert skip button title */ 11 | "Skip this version" = "Ennél a verziónál ne figyelmeztessen"; 12 | 13 | /* Update alert skip button title */ 14 | "Update" = "Frissítés"; 15 | -------------------------------------------------------------------------------- /Harpy/Harpy.bundle/hy.lproj/HarpyLocalizable.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArtSabintsev/Harpy/e76b5e061826d2abfdd6c2a1ae7baa3229ab4186/Harpy/Harpy.bundle/hy.lproj/HarpyLocalizable.strings -------------------------------------------------------------------------------- /Harpy/Harpy.bundle/id.lproj/HarpyLocalizable.strings: -------------------------------------------------------------------------------- 1 | /* Update alert message: A new version of {APP NAME} is available. Please update to version {NEW VERSION} now.*/ 2 | "A new version of %@ is available. Please update to version %@ now." = "Versi baru %@ tersedia. Perbarui ke versi %@ sekarang."; 3 | 4 | /* Update alert title */ 5 | "Update Available" = "Pembaruan Tersedia"; 6 | 7 | /* Update alert dismiss button title */ 8 | "Next time" = "Lain kali"; 9 | 10 | /* Update alert skip button title */ 11 | "Skip this version" = "Lewati versi ini"; 12 | 13 | /* Update alert skip button title */ 14 | "Update" = "Perbarui"; 15 | -------------------------------------------------------------------------------- /Harpy/Harpy.bundle/it.lproj/HarpyLocalizable.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArtSabintsev/Harpy/e76b5e061826d2abfdd6c2a1ae7baa3229ab4186/Harpy/Harpy.bundle/it.lproj/HarpyLocalizable.strings -------------------------------------------------------------------------------- /Harpy/Harpy.bundle/ja.lproj/HarpyLocalizable.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArtSabintsev/Harpy/e76b5e061826d2abfdd6c2a1ae7baa3229ab4186/Harpy/Harpy.bundle/ja.lproj/HarpyLocalizable.strings -------------------------------------------------------------------------------- /Harpy/Harpy.bundle/ko.lproj/HarpyLocalizable.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArtSabintsev/Harpy/e76b5e061826d2abfdd6c2a1ae7baa3229ab4186/Harpy/Harpy.bundle/ko.lproj/HarpyLocalizable.strings -------------------------------------------------------------------------------- /Harpy/Harpy.bundle/lt.lproj/HarpyLocalizable.strings: -------------------------------------------------------------------------------- 1 | /* Update alert message: A new version of {APP NAME} is available. Please update to version {NEW VERSION} now.*/ 2 | "A new version of %@ is available. Please update to version %@ now." = "Atsirado nauja %@ versija. Prašome atsinaujinti iki %@ versijos."; 3 | 4 | /* Update alert title */ 5 | "Update Available" = "Atnaujinimas"; 6 | 7 | /* Update alert dismiss button title */ 8 | "Next time" = "Kitą kartą"; 9 | 10 | /* Update alert skip button title */ 11 | "Skip this version" = "Praleisti šią versiją"; 12 | 13 | /* Update alert skip button title */ 14 | "Update" = "Atnaujinti"; 15 | -------------------------------------------------------------------------------- /Harpy/Harpy.bundle/lv.lproj/HarpyLocalizable.strings: -------------------------------------------------------------------------------- 1 | /* Update alert message: A new version of {APP NAME} is available. Please update to version {NEW VERSION} now.*/ 2 | "A new version of %@ is available. Please update to version %@ now." = "Pieejama jauna %@ versija. Lūdzam atjaunot versiju %@."; 3 | 4 | /* Update alert title */ 5 | "Update Available" = "Atjauninājums pieejams"; 6 | 7 | /* Update alert dismiss button title */ 8 | "Next time" = "Nākamreiz"; 9 | 10 | /* Update alert skip button title */ 11 | "Skip this version" = "Izlaist šo versiju"; 12 | 13 | /* Update alert skip button title */ 14 | "Update" = "Atjaunināt"; 15 | -------------------------------------------------------------------------------- /Harpy/Harpy.bundle/ms.lproj/HarpyLocalizable.strings: -------------------------------------------------------------------------------- 1 | /* Update alert message: A new version of {APP NAME} is available. Please update to version {NEW VERSION} now.*/ 2 | "A new version of %@ is available. Please update to version %@ now." = "Versi terkini untuk %@ telah ada. Sila muat turun versi %@ sekarang."; 3 | 4 | /* Update alert title */ 5 | "Update Available" = "Versi Terkini"; 6 | 7 | /* Update alert dismiss button title */ 8 | "Next time" = "Lain kali"; 9 | 10 | /* Update alert skip button title */ 11 | "Skip this version" = "Langkau versi ini"; 12 | 13 | /* Update alert skip button title */ 14 | "Update" = "Muat turun"; 15 | -------------------------------------------------------------------------------- /Harpy/Harpy.bundle/nb-NO.lproj/HarpyLocalizable.strings: -------------------------------------------------------------------------------- 1 | /* Update alert message: A new version of {APP NAME} is available. Please update to version {NEW VERSION} now.*/ 2 | "A new version of %@ is available. Please update to version %@ now." = "En ny versjon av %@ er tilgjengelig. Vennligst oppdater til versjon %@ nå."; 3 | 4 | /* Update alert title */ 5 | "Update Available" = "Oppdatering tilgjengelig"; 6 | 7 | /* Update alert dismiss button title */ 8 | "Next time" = "Neste gang"; 9 | 10 | /* Update alert skip button title */ 11 | "Skip this version" = "Hopp over denne versjonen"; 12 | 13 | /* Update alert skip button title */ 14 | "Update" = "Oppdater"; 15 | -------------------------------------------------------------------------------- /Harpy/Harpy.bundle/nl.lproj/HarpyLocalizable.strings: -------------------------------------------------------------------------------- 1 | /* Update alert message: A new version of {APP NAME} is available. Please update to version {NEW VERSION} now.*/ 2 | "A new version of %@ is available. Please update to version %@ now."="Er is een nieuwe versie van %@ beschikbaar. Je kunt nu updaten naar versie %@."; 3 | 4 | /* Update alert title */ 5 | "Update Available"="Update beschikbaar"; 6 | 7 | /* Update alert dismiss button title */ 8 | "Next time"="Volgende keer"; 9 | 10 | /* Update alert skip button title */ 11 | "Skip this version"="Sla deze versie over"; 12 | 13 | /* Update alert skip button title */ 14 | "Update"="Updaten"; 15 | 16 | -------------------------------------------------------------------------------- /Harpy/Harpy.bundle/pl.lproj/HarpyLocalizable.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArtSabintsev/Harpy/e76b5e061826d2abfdd6c2a1ae7baa3229ab4186/Harpy/Harpy.bundle/pl.lproj/HarpyLocalizable.strings -------------------------------------------------------------------------------- /Harpy/Harpy.bundle/pt-PT.lproj/HarpyLocalizable.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArtSabintsev/Harpy/e76b5e061826d2abfdd6c2a1ae7baa3229ab4186/Harpy/Harpy.bundle/pt-PT.lproj/HarpyLocalizable.strings -------------------------------------------------------------------------------- /Harpy/Harpy.bundle/pt.lproj/HarpyLocalizable.strings: -------------------------------------------------------------------------------- 1 | /* Update alert message: A new version of {APP NAME} is available. Please update to version {NEW VERSION} now.*/ 2 | "A new version of %@ is available. Please update to version %@ now."="Uma nova versão do %@ está disponível. Por favor atualize para a versão %@ agora."; 3 | 4 | /* Update alert title */ 5 | "Update Available"="Atualização disponível"; 6 | 7 | /* Update alert dismiss button title */ 8 | "Next time"="Próxima vez"; 9 | 10 | /* Update alert skip button title */ 11 | "Skip this version"="Ignorar esta versão"; 12 | 13 | /* Update alert skip button title */ 14 | "Update"="Atualizar"; 15 | 16 | -------------------------------------------------------------------------------- /Harpy/Harpy.bundle/ru.lproj/HarpyLocalizable.strings: -------------------------------------------------------------------------------- 1 | /* Update alert message: A new version of {APP NAME} is available. Please update to version {NEW VERSION} now.*/ 2 | "A new version of %@ is available. Please update to version %@ now."="Доступна новая версия %@. Пожалуйста, обновитесь до версии %@ сейчас."; 3 | 4 | /* Update alert title */ 5 | "Update Available"="Доступно обновление"; 6 | 7 | /* Update alert dismiss button title */ 8 | "Next time"="В следующий раз"; 9 | 10 | /* Update alert skip button title */ 11 | "Skip this version"="Пропустить эту версию"; 12 | 13 | /* Update alert skip button title */ 14 | "Update"="Обновить"; 15 | 16 | -------------------------------------------------------------------------------- /Harpy/Harpy.bundle/sl.lproj/HarpyLocalizable.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArtSabintsev/Harpy/e76b5e061826d2abfdd6c2a1ae7baa3229ab4186/Harpy/Harpy.bundle/sl.lproj/HarpyLocalizable.strings -------------------------------------------------------------------------------- /Harpy/Harpy.bundle/sr-Cyrl.lproj/HarpyLocalizable.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArtSabintsev/Harpy/e76b5e061826d2abfdd6c2a1ae7baa3229ab4186/Harpy/Harpy.bundle/sr-Cyrl.lproj/HarpyLocalizable.strings -------------------------------------------------------------------------------- /Harpy/Harpy.bundle/sr-Latn.lproj/HarpyLocalizable.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArtSabintsev/Harpy/e76b5e061826d2abfdd6c2a1ae7baa3229ab4186/Harpy/Harpy.bundle/sr-Latn.lproj/HarpyLocalizable.strings -------------------------------------------------------------------------------- /Harpy/Harpy.bundle/sv.lproj/HarpyLocalizable.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArtSabintsev/Harpy/e76b5e061826d2abfdd6c2a1ae7baa3229ab4186/Harpy/Harpy.bundle/sv.lproj/HarpyLocalizable.strings -------------------------------------------------------------------------------- /Harpy/Harpy.bundle/th.lproj/HarpyLocalizable.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArtSabintsev/Harpy/e76b5e061826d2abfdd6c2a1ae7baa3229ab4186/Harpy/Harpy.bundle/th.lproj/HarpyLocalizable.strings -------------------------------------------------------------------------------- /Harpy/Harpy.bundle/tr.lproj/HarpyLocalizable.strings: -------------------------------------------------------------------------------- 1 | /* Update alert message: A new version of {APP NAME} is available. Please update to version {NEW VERSION} now.*/ 2 | "A new version of %@ is available. Please update to version %@ now." = "%@' nın yeni bir sürümü mevcut. Lütfen %@ sürümüne güncelleyin."; 3 | 4 | /* Update alert title */ 5 | "Update Available" = "Güncelleme Mevcut"; 6 | 7 | /* Update alert dismiss button title */ 8 | "Next time" = "Daha sonra"; 9 | 10 | /* Update alert skip button title */ 11 | "Skip this version" = "Boşver"; 12 | 13 | /* Update alert skip button title */ 14 | "Update" = "Güncelle"; 15 | -------------------------------------------------------------------------------- /Harpy/Harpy.bundle/uk.lproj/HarpyLocalizable.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArtSabintsev/Harpy/e76b5e061826d2abfdd6c2a1ae7baa3229ab4186/Harpy/Harpy.bundle/uk.lproj/HarpyLocalizable.strings -------------------------------------------------------------------------------- /Harpy/Harpy.bundle/ur.lproj/HarpyLocalizable.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArtSabintsev/Harpy/e76b5e061826d2abfdd6c2a1ae7baa3229ab4186/Harpy/Harpy.bundle/ur.lproj/HarpyLocalizable.strings -------------------------------------------------------------------------------- /Harpy/Harpy.bundle/vi.lproj/HarpyLocalizable.strings: -------------------------------------------------------------------------------- 1 | /* Update alert message: A new version of {APP NAME} is available. Please update to version {NEW VERSION} now.*/ 2 | "A new version of %@ is available. Please update to version %@ now." = "Đã có phiên bản mới của %@. Xin vui lòng cập nhật lên phiên bản %@."; 3 | 4 | /* Update alert title */ 5 | "Update Available" = "Cập nhật mới"; 6 | 7 | /* Update alert dismiss button title */ 8 | "Next time" = "Lần tới"; 9 | 10 | /* Update alert skip button title */ 11 | "Skip this version" = "Bỏ qua phiên bản này"; 12 | 13 | /* Update alert skip button title */ 14 | "Update" = "Cập nhật"; 15 | -------------------------------------------------------------------------------- /Harpy/Harpy.bundle/zh-Hans.lproj/HarpyLocalizable.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArtSabintsev/Harpy/e76b5e061826d2abfdd6c2a1ae7baa3229ab4186/Harpy/Harpy.bundle/zh-Hans.lproj/HarpyLocalizable.strings -------------------------------------------------------------------------------- /Harpy/Harpy.bundle/zh-Hant.lproj/HarpyLocalizable.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArtSabintsev/Harpy/e76b5e061826d2abfdd6c2a1ae7baa3229ab4186/Harpy/Harpy.bundle/zh-Hant.lproj/HarpyLocalizable.strings -------------------------------------------------------------------------------- /Harpy/Harpy.h: -------------------------------------------------------------------------------- 1 | // 2 | // Harpy.h 3 | // Harpy 4 | // 5 | // Created by Arthur Ariel Sabintsev on 11/14/12. 6 | // Copyright (c) 2012 Arthur Ariel Sabintsev. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | //! Project version number for Harpy. 13 | FOUNDATION_EXPORT double HarpyVersionNumber; 14 | 15 | //! Project version string for Harpy. 16 | FOUNDATION_EXPORT const unsigned char HarpyVersionString[]; 17 | 18 | /// i18n/l10n constants 19 | FOUNDATION_EXPORT NSString * const HarpyLanguageArabic; 20 | FOUNDATION_EXPORT NSString * const HarpyLanguageArmenian; 21 | FOUNDATION_EXPORT NSString * const HarpyLanguageBasque; 22 | FOUNDATION_EXPORT NSString * const HarpyLanguageChineseSimplified; 23 | FOUNDATION_EXPORT NSString * const HarpyLanguageChineseTraditional; 24 | FOUNDATION_EXPORT NSString * const HarpyLanguageCroatian; 25 | FOUNDATION_EXPORT NSString * const HarpyLanguageCzech; 26 | FOUNDATION_EXPORT NSString * const HarpyLanguageDanish; 27 | FOUNDATION_EXPORT NSString * const HarpyLanguageDutch; 28 | FOUNDATION_EXPORT NSString * const HarpyLanguageEnglish; 29 | FOUNDATION_EXPORT NSString * const HarpyLanguageEstonian; 30 | FOUNDATION_EXPORT NSString * const HarpyLanguageFinnish; 31 | FOUNDATION_EXPORT NSString * const HarpyLanguageFrench; 32 | FOUNDATION_EXPORT NSString * const HarpyLanguageGerman; 33 | FOUNDATION_EXPORT NSString * const HarpyLanguageGreek; 34 | FOUNDATION_EXPORT NSString * const HarpyLanguageHebrew; 35 | FOUNDATION_EXPORT NSString * const HarpyLanguageHungarian; 36 | FOUNDATION_EXPORT NSString * const HarpyLanguageIndonesian; 37 | FOUNDATION_EXPORT NSString * const HarpyLanguageItalian; 38 | FOUNDATION_EXPORT NSString * const HarpyLanguageJapanese; 39 | FOUNDATION_EXPORT NSString * const HarpyLanguageKorean; 40 | FOUNDATION_EXPORT NSString * const HarpyLanguageLatvian; 41 | FOUNDATION_EXPORT NSString * const HarpyLanguageLithuanian; 42 | FOUNDATION_EXPORT NSString * const HarpyLanguageMalay; 43 | FOUNDATION_EXPORT NSString * const HarpyLanguageNorwegian; 44 | FOUNDATION_EXPORT NSString * const HarpyLanguagePersian; 45 | FOUNDATION_EXPORT NSString * const HarpyLanguagePersianIran; 46 | FOUNDATION_EXPORT NSString * const HarpyLanguagePersianAfghanistan; 47 | FOUNDATION_EXPORT NSString * const HarpyLanguagePolish; 48 | FOUNDATION_EXPORT NSString * const HarpyLanguagePortugueseBrazil; 49 | FOUNDATION_EXPORT NSString * const HarpyLanguagePortuguesePortugal; 50 | FOUNDATION_EXPORT NSString * const HarpyLanguageRussian; 51 | FOUNDATION_EXPORT NSString * const HarpyLanguageSerbianCyrillic; 52 | FOUNDATION_EXPORT NSString * const HarpyLanguageSerbianLatin; 53 | FOUNDATION_EXPORT NSString * const HarpyLanguageSlovenian; 54 | FOUNDATION_EXPORT NSString * const HarpyLanguageSwedish; 55 | FOUNDATION_EXPORT NSString * const HarpyLanguageSpanish; 56 | FOUNDATION_EXPORT NSString * const HarpyLanguageThai; 57 | FOUNDATION_EXPORT NSString * const HarpyLanguageTurkish; 58 | FOUNDATION_EXPORT NSString * const HarpyLanguageUkrainian; 59 | FOUNDATION_EXPORT NSString * const HarpyLanguageUrdu; 60 | FOUNDATION_EXPORT NSString * const HarpyLanguageVietnamese; 61 | 62 | @protocol HarpyDelegate 63 | 64 | @optional 65 | - (void)harpyDidShowUpdateDialog; // User presented with update dialog 66 | - (void)harpyUserDidLaunchAppStore; // User did click on button that launched App Store.app 67 | - (void)harpyUserDidSkipVersion; // User did click on button that skips version update 68 | - (void)harpyUserDidCancel; // User did click on button that cancels update dialog 69 | - (void)harpyDidDetectNewVersionWithoutAlert:(NSString *)message; // Harpy performed version check and did not display alert 70 | @end 71 | 72 | typedef NS_ENUM(NSUInteger, HarpyAlertType) 73 | { 74 | HarpyAlertTypeForce = 1, // Forces user to update your app 75 | HarpyAlertTypeOption, // (DEFAULT) Presents user with option to update app now or at next launch 76 | HarpyAlertTypeSkip, // Presents User with option to update the app now, at next launch, or to skip this version all together 77 | HarpyAlertTypeNone // Don't show the alert type , useful for skipping Patch, Minor, Major updates 78 | }; 79 | 80 | @interface Harpy : NSObject 81 | 82 | /** 83 | The harpy delegate can be used to know when the update dialog is shown and which action a user took. 84 | See the protocol declaration above. 85 | */ 86 | @property (nonatomic, weak) id delegate; 87 | 88 | /** 89 | The UIViewController that will present an instance of UIAlertController 90 | */ 91 | @property (nonatomic, strong) UIViewController *presentingViewController; 92 | 93 | /** 94 | The current version of your app that is available for download on the App Store 95 | */ 96 | @property (nonatomic, copy, readonly) NSString *currentAppStoreVersion; 97 | 98 | 99 | /** 100 | @b OPTIONAL: The preferred name for the app. This name will be displayed in the @c UIAlertView in place of the bundle name. 101 | */ 102 | @property (nonatomic, strong) NSString *appName; 103 | 104 | /** 105 | @b OPTIONAL: Log Debug information 106 | */ 107 | @property (nonatomic, assign, getter=isDebugEnabled) BOOL debugEnabled; 108 | 109 | /** 110 | @b OPTIONAL: The alert type to present to the user when there is an update. See the @c HarpyAlertType enum above. 111 | */ 112 | @property (nonatomic, assign) HarpyAlertType alertType; 113 | 114 | /** 115 | @b OPTIONAL: The alert type to present to the user when there is a major update (e.g. A.b.c.d). See the @c HarpyAlertType enum above. 116 | */ 117 | @property (nonatomic, assign) HarpyAlertType majorUpdateAlertType; 118 | 119 | /** 120 | @b OPTIONAL: The alert type to present to the user when there is a minor update (e.g. a.B.c.d). See the @c HarpyAlertType enum above. 121 | */ 122 | @property (nonatomic, assign) HarpyAlertType minorUpdateAlertType; 123 | 124 | /** 125 | @b OPTIONAL: The alert type to present to the user when there is a patch update (e.g. a.b.C.d). See the @c HarpyAlertType enum above. 126 | */ 127 | @property (nonatomic, assign) HarpyAlertType patchUpdateAlertType; 128 | 129 | /** 130 | @b OPTIONAL: The alert type to present to the user when there is a minor update (e.g. a.b.c.D). See the @c HarpyAlertType enum above. 131 | */ 132 | @property (nonatomic, assign) HarpyAlertType revisionUpdateAlertType; 133 | 134 | /** 135 | @b OPTIONAL: If your application is not availabe in the U.S. Store, you must specify the two-letter 136 | country code for the region in which your applicaiton is available in. 137 | */ 138 | @property (nonatomic, copy) NSString *countryCode; 139 | 140 | /** 141 | @b OPTIONAL: Overides system language to predefined language. Please use the @c HarpyLanguage constants defined in @c Harpy.h. 142 | */ 143 | @property (nonatomic, copy) NSString *forceLanguageLocalization; 144 | 145 | /** 146 | @b OPTIONAL: The tintColor for the alertController 147 | */ 148 | @property (nonatomic, strong) UIColor *alertControllerTintColor; 149 | 150 | /** 151 | @b OPTIONAL: Delays the update prompt by a specific number of days. By default, this value is set to 1 day to avoid an issue where Apple updates the JSON faster than the app binary propogates to the App Store. 152 | */ 153 | @property (nonatomic, assign) NSUInteger showAlertAfterCurrentVersionHasBeenReleasedForDays; 154 | 155 | #pragma mark - Methods 156 | 157 | /** 158 | Harpy's Singleton method 159 | */ 160 | + (Harpy *)sharedInstance; 161 | 162 | /** 163 | Checks the installed version of your application against the version currently available on the iTunes store. 164 | If a newer version exists in the AppStore, Harpy prompts your user to update their copy of your app. 165 | Place in @c application:didFinishLaunchingWithOptions: @b AFTER calling @c makeKeyAndVisible on your @c UIWindow iVar. 166 | 167 | Do not use this method if you are using checkVersionDaily or checkVersionWeekly. 168 | */ 169 | - (void)checkVersion; 170 | 171 | /** 172 | Perform daily check for new version of your app. 173 | Useful if user returns to you app from background after extended period of time. 174 | Place in @c applicationDidBecomeActive:. 175 | 176 | Do not use this method if you are using @c checkVersion or @c checkVersionWeekly. 177 | */ 178 | - (void)checkVersionDaily; 179 | 180 | /** 181 | Perform weekly check for new version of your app. 182 | Useful if user returns to you app from background after extended period of time. 183 | Place in @c applicationDidBecomeActive:. 184 | 185 | Do not use this method if you are using @c checkVersion or @c checkVersionDaily. 186 | */ 187 | - (void)checkVersionWeekly; 188 | 189 | #pragma mark - Unit Testing 190 | 191 | /** 192 | This method was created for unit testing purposes. 193 | 194 | It will specifically be used to test the string localizations. 195 | 196 | @param stringKey The string that should be looked up and localized. 197 | 198 | @return The localized string. 199 | */ 200 | 201 | - (NSString *)testLocalizedStringForKey:(NSString *)stringKey; 202 | 203 | /** 204 | This method was created for unit testing purposes. 205 | 206 | It will specifically be used to temporarily set the installed version of the app. 207 | 208 | @param version The temporary version that should be set for the app. 209 | 210 | */ 211 | 212 | - (void)testSetCurrentInstalledVersion:(NSString *)version; 213 | 214 | 215 | /** 216 | This method was created for unit testing purposes. 217 | 218 | It will specifically be used to temporarily set the app store version of the app. 219 | 220 | @param version The temporary version that should be set for the app. 221 | 222 | */ 223 | 224 | - (void)testSetCurrentAppStoreVersion:(NSString *)version; 225 | 226 | /** 227 | This method was created for unit testing purposes. 228 | 229 | It will specifically be used to compared the temporary app store version to the temporary installed version. 230 | 231 | @return True if app store version is newer, otherwise false. 232 | 233 | */ 234 | 235 | - (BOOL)testIsAppStoreVersionNewer; 236 | 237 | 238 | @end 239 | -------------------------------------------------------------------------------- /Harpy/Harpy.m: -------------------------------------------------------------------------------- 1 | // 2 | // Harpy.m 3 | // Harpy 4 | // 5 | // Created by Arthur Ariel Sabintsev on 11/14/12. 6 | // Copyright (c) 2012 Arthur Ariel Sabintsev. All rights reserved. 7 | // 8 | 9 | #import "Harpy.h" 10 | 11 | /// NSUserDefault macros to store user's preferences for HarpyAlertTypeSkip 12 | NSString * const HarpyDefaultSkippedVersion = @"Harpy User Decided To Skip Version Update Boolean"; 13 | NSString * const HarpyDefaultStoredVersionCheckDate = @"Harpy Stored Date From Last Version Check"; 14 | 15 | /// i18n/l10n constants 16 | NSString * const HarpyLanguageArabic = @"ar"; 17 | NSString * const HarpyLanguageArmenian = @"hy"; 18 | NSString * const HarpyLanguageBasque = @"eu"; 19 | NSString * const HarpyLanguageChineseSimplified = @"zh-Hans"; 20 | NSString * const HarpyLanguageChineseTraditional = @"zh-Hant"; 21 | NSString * const HarpyLanguageCroatian = @"hr"; 22 | NSString * const HarpyLanguageCzech = @"cs"; 23 | NSString * const HarpyLanguageDanish = @"da"; 24 | NSString * const HarpyLanguageDutch = @"nl"; 25 | NSString * const HarpyLanguageEnglish = @"en"; 26 | NSString * const HarpyLanguageEstonian = @"et"; 27 | NSString * const HarpyLanguageFinnish = @"fi"; 28 | NSString * const HarpyLanguageFrench = @"fr"; 29 | NSString * const HarpyLanguageGerman = @"de"; 30 | NSString * const HarpyLanguageGreek = @"el"; 31 | NSString * const HarpyLanguageHebrew = @"he"; 32 | NSString * const HarpyLanguageHungarian = @"hu"; 33 | NSString * const HarpyLanguageIndonesian = @"id"; 34 | NSString * const HarpyLanguageItalian = @"it"; 35 | NSString * const HarpyLanguageJapanese = @"ja"; 36 | NSString * const HarpyLanguageKorean = @"ko"; 37 | NSString * const HarpyLanguageLatvian = @"lv"; 38 | NSString * const HarpyLanguageLithuanian = @"lt"; 39 | NSString * const HarpyLanguageMalay = @"ms"; 40 | NSString * const HarpyLanguageNorwegian = @"nb-NO"; 41 | NSString * const HarpyLanguagePersian = @"fa"; 42 | NSString * const HarpyLanguagePersianIran = @"fa-IR"; 43 | NSString * const HarpyLanguagePersianAfghanistan = @"fa-AF"; 44 | NSString * const HarpyLanguagePolish = @"pl"; 45 | NSString * const HarpyLanguagePortugueseBrazil = @"pt"; 46 | NSString * const HarpyLanguagePortuguesePortugal = @"pt-PT"; 47 | NSString * const HarpyLanguageRussian = @"ru"; 48 | NSString * const HarpyLanguageSerbianCyrillic = @"sr-Cyrl"; 49 | NSString * const HarpyLanguageSerbianLatin = @"sr-Latn"; 50 | NSString * const HarpyLanguageSlovenian = @"sl"; 51 | NSString * const HarpyLanguageSwedish = @"sv"; 52 | NSString * const HarpyLanguageSpanish = @"es"; 53 | NSString * const HarpyLanguageThai = @"th"; 54 | NSString * const HarpyLanguageTurkish = @"tr"; 55 | NSString * const HarpyLanguageUkrainian = @"uk"; 56 | NSString * const HarpyLanguageUrdu = @"ur"; 57 | NSString * const HarpyLanguageVietnamese = @"vi"; 58 | 59 | @interface Harpy() 60 | 61 | @property (nonatomic, strong) NSDictionary *appData; 62 | @property (nonatomic, strong) NSDate *lastVersionCheckPerformedOnDate; 63 | @property (nonatomic, copy) NSString *appID; 64 | @property (nonatomic, copy) NSString *currentInstalledVersion; 65 | @property (nonatomic, copy) NSString *currentAppStoreVersion; 66 | @property (nonatomic, copy) NSString *updateAvailableMessage; 67 | @property (nonatomic, copy) NSString *theNewVersionMessage; 68 | @property (nonatomic, copy) NSString *updateButtonText; 69 | @property (nonatomic, copy) NSString *nextTimeButtonText; 70 | @property (nonatomic, copy) NSString *skipButtonText; 71 | 72 | @end 73 | 74 | @implementation Harpy 75 | 76 | #pragma mark - Initialization 77 | 78 | + (Harpy *)sharedInstance { 79 | static id sharedInstance = nil; 80 | static dispatch_once_t onceToken; 81 | 82 | dispatch_once(&onceToken, ^{ 83 | sharedInstance = [[self alloc] init]; 84 | }); 85 | 86 | return sharedInstance; 87 | } 88 | 89 | - (id)init { 90 | self = [super init]; 91 | 92 | if (self) { 93 | _alertType = HarpyAlertTypeOption; 94 | _lastVersionCheckPerformedOnDate = [[NSUserDefaults standardUserDefaults] objectForKey:HarpyDefaultStoredVersionCheckDate]; 95 | _currentInstalledVersion = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"]; 96 | _showAlertAfterCurrentVersionHasBeenReleasedForDays = 1; 97 | } 98 | 99 | return self; 100 | } 101 | 102 | #pragma mark - Public 103 | 104 | - (void)checkVersion { 105 | if (!_presentingViewController) { 106 | NSLog(@"[Harpy]: Please make sure that you have set _presentationViewController before calling checkVersion, checkVersionDaily, or checkVersionWeekly."); 107 | } else { 108 | [self performVersionCheck]; 109 | } 110 | } 111 | 112 | - (void)checkVersionDaily { 113 | /* 114 | On app's first launch, lastVersionCheckPerformedOnDate isn't set. 115 | Avoid false-positive fulfilment of second condition in this method. 116 | Also, performs version check on first launch. 117 | */ 118 | if (![self lastVersionCheckPerformedOnDate]) { 119 | 120 | // Set Initial Date 121 | self.lastVersionCheckPerformedOnDate = [NSDate date]; 122 | 123 | // Perform First Launch Check 124 | [self checkVersion]; 125 | } 126 | 127 | // If daily condition is satisfied, perform version check 128 | if ([self numberOfDaysElapsedBetweenLastVersionCheckDate] >= 1) { 129 | [self checkVersion]; 130 | } 131 | } 132 | 133 | - (void)checkVersionWeekly { 134 | /* 135 | On app's first launch, lastVersionCheckPerformedOnDate isn't set. 136 | Avoid false-positive fulfilment of second condition in this method. 137 | Also, performs version check on first launch. 138 | */ 139 | if (![self lastVersionCheckPerformedOnDate]) { 140 | 141 | // Set Initial Date 142 | self.lastVersionCheckPerformedOnDate = [NSDate date]; 143 | 144 | // Perform First Launch Check 145 | [self checkVersion]; 146 | } 147 | 148 | // If weekly condition is satisfied, perform version check 149 | if ([self numberOfDaysElapsedBetweenLastVersionCheckDate] >= 7) { 150 | [self checkVersion]; 151 | } 152 | } 153 | 154 | #pragma mark - Helpers 155 | 156 | - (void)performVersionCheck { 157 | NSURL *storeURL = [self itunesURL]; 158 | NSURLRequest *request = [NSMutableURLRequest requestWithURL:storeURL cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:30.0]; 159 | 160 | [self printDebugMessage:[NSString stringWithFormat:@"storeURL: %@", storeURL]]; 161 | 162 | NSURLSession *session = [NSURLSession sharedSession]; 163 | NSURLSessionDataTask *task = [session dataTaskWithRequest:request 164 | completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { 165 | if ([data length] > 0 && !error) { // Success 166 | [self parseResults:data]; 167 | } 168 | }]; 169 | [task resume]; 170 | } 171 | 172 | - (void)parseResults:(NSData *)data { 173 | _appData = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:nil]; 174 | 175 | [self printDebugMessage:[NSString stringWithFormat:@"JSON Results: %@", _appData]]; 176 | 177 | if ([self isUpdateCompatibleWithDeviceOS:_appData]) { 178 | 179 | __typeof__(self) __weak weakSelf = self; 180 | 181 | dispatch_async(dispatch_get_main_queue(), ^{ 182 | 183 | // Store version comparison date 184 | weakSelf.lastVersionCheckPerformedOnDate = [NSDate date]; 185 | [[NSUserDefaults standardUserDefaults] setObject:[self lastVersionCheckPerformedOnDate] forKey:HarpyDefaultStoredVersionCheckDate]; 186 | [[NSUserDefaults standardUserDefaults] synchronize]; 187 | 188 | NSDictionary *results = [self.appData valueForKey:@"results"]; 189 | 190 | /** 191 | Checks to see when the latest version of the app was released. 192 | If the release date is greater-than-or-equal-to `_showAlertAfterCurrentVersionHasBeenReleasedForDays`, 193 | the user will prompted to update their app (if the version is newer - checked later on in this method). 194 | */ 195 | 196 | NSString *releaseDateString = [[results valueForKey:@"currentVersionReleaseDate"] objectAtIndex:0]; 197 | if (releaseDateString == nil) { 198 | return; 199 | } else { 200 | NSInteger daysSinceRelease = [weakSelf daysSinceDateString:releaseDateString]; 201 | if (!(daysSinceRelease >= weakSelf.showAlertAfterCurrentVersionHasBeenReleasedForDays)) { 202 | NSString *message = [NSString stringWithFormat:@"Your app has been released for %ld days, but Harpy cannot prompt the user until %lu days have passed.", (long)daysSinceRelease, (unsigned long)weakSelf.showAlertAfterCurrentVersionHasBeenReleasedForDays]; 203 | [self printDebugMessage:message]; 204 | return; 205 | } 206 | } 207 | 208 | /** 209 | Current version that has been uploaded to the AppStore. 210 | Used to contain all versions, but now only contains the latest version. 211 | Still returns an instance of NSArray. 212 | */ 213 | 214 | NSArray *versionsInAppStore = [results valueForKey:@"version"]; 215 | if (versionsInAppStore == nil) { 216 | return; 217 | } else { 218 | if ([versionsInAppStore count]) { 219 | weakSelf.currentAppStoreVersion = [versionsInAppStore objectAtIndex:0]; 220 | if ([weakSelf isAppStoreVersionNewer:weakSelf.currentAppStoreVersion]) { 221 | [weakSelf appStoreVersionIsNewer:weakSelf.currentAppStoreVersion]; 222 | } else { 223 | [self printDebugMessage:@"Currently installed version is newer."]; 224 | } 225 | } 226 | } 227 | }); 228 | } else { 229 | [self printDebugMessage:@"Device is incompatible with installed verison of iOS."]; 230 | } 231 | } 232 | 233 | - (NSURL *)itunesURL { 234 | NSURLComponents *components = [NSURLComponents new]; 235 | components.scheme = @"https"; 236 | components.host = @"itunes.apple.com"; 237 | components.path = @"/lookup"; 238 | 239 | NSMutableArray *items = [@[[NSURLQueryItem queryItemWithName:@"bundleId" value:[self bundleID]]] mutableCopy]; 240 | 241 | if ([self countryCode]) { 242 | NSURLQueryItem *countryQueryItem = [NSURLQueryItem queryItemWithName:@"country" value:_countryCode]; 243 | [items addObject:countryQueryItem]; 244 | } 245 | 246 | components.queryItems = items; 247 | 248 | return components.URL; 249 | } 250 | 251 | - (BOOL)isUpdateCompatibleWithDeviceOS:(NSDictionary *)appData { 252 | NSArray *> *results = appData[@"results"]; 253 | 254 | if (results.count > 0) { 255 | NSString *requiresOSVersion = [results firstObject][@"minimumOsVersion"]; 256 | if (requiresOSVersion != nil) { 257 | NSString *systemVersion = [UIDevice currentDevice].systemVersion; 258 | if ( 259 | ([systemVersion compare:requiresOSVersion options:NSNumericSearch] == NSOrderedDescending) || 260 | ([systemVersion compare:requiresOSVersion options:NSNumericSearch] == NSOrderedSame) 261 | ) { 262 | return true; 263 | } else { 264 | return false; 265 | } 266 | } else { 267 | return false; 268 | } 269 | } else { 270 | return false; 271 | } 272 | } 273 | 274 | - (NSUInteger)numberOfDaysElapsedBetweenLastVersionCheckDate { 275 | NSCalendar *currentCalendar = [NSCalendar currentCalendar]; 276 | NSDateComponents *components = [currentCalendar components:NSCalendarUnitDay 277 | fromDate:[self lastVersionCheckPerformedOnDate] 278 | toDate:[NSDate date] 279 | options:0]; 280 | return [components day]; 281 | } 282 | 283 | - (BOOL)isAppStoreVersionNewer:(NSString *)currentAppStoreVersion { 284 | // Current installed version is the newest public version or newer (e.g., dev version) 285 | if ([[self currentInstalledVersion] compare:currentAppStoreVersion options:NSNumericSearch] == NSOrderedAscending) { 286 | return true; 287 | } else { 288 | return false; 289 | } 290 | } 291 | 292 | - (void)appStoreVersionIsNewer:(NSString *)currentAppStoreVersion { 293 | _appID = _appData[@"results"][0][@"trackId"]; 294 | 295 | if (_appID == nil) { 296 | [self printDebugMessage:@"appID is nil, which means to the trackId key is missing from the JSON results that Apple returned for your bundleID. If a version of your app is in the store and you are seeing this message, please open up an issue http://github.com/ArtSabintsev/Harpy and provide as much detail about your app as you can. Thanks!"]; 297 | } else { 298 | [self localizeAlertStringsForCurrentAppStoreVersion:currentAppStoreVersion]; 299 | [self alertTypeForVersion:currentAppStoreVersion]; 300 | [self showAlertIfCurrentAppStoreVersionNotSkipped:currentAppStoreVersion]; 301 | } 302 | } 303 | 304 | - (void)launchAppStore { 305 | NSString *iTunesString = [NSString stringWithFormat:@"https://itunes.apple.com/app/id%@", [self appID]]; 306 | NSURL *iTunesURL = [NSURL URLWithString:iTunesString]; 307 | 308 | dispatch_async(dispatch_get_main_queue(), ^{ 309 | if (@available(iOS 10.0, *)) { 310 | [[UIApplication sharedApplication] openURL:iTunesURL options:@{} completionHandler:nil]; 311 | } else { 312 | [[UIApplication sharedApplication] openURL:iTunesURL]; 313 | } 314 | 315 | if ([self.delegate respondsToSelector:@selector(harpyUserDidLaunchAppStore)]){ 316 | [self.delegate harpyUserDidLaunchAppStore]; 317 | } 318 | }); 319 | } 320 | 321 | #pragma mark - Alert Management 322 | 323 | - (void)showAlertIfCurrentAppStoreVersionNotSkipped:(NSString *)currentAppStoreVersion { 324 | // Check if user decided to skip this version in the past 325 | NSString *storedSkippedVersion = [[NSUserDefaults standardUserDefaults] objectForKey:HarpyDefaultSkippedVersion]; 326 | 327 | if (![storedSkippedVersion isEqualToString:currentAppStoreVersion]) { 328 | [self showAlertWithAppStoreVersion:currentAppStoreVersion]; 329 | } else { 330 | // Don't show alert. 331 | return; 332 | } 333 | } 334 | 335 | - (void)showAlertWithAppStoreVersion:(NSString *)currentAppStoreVersion { 336 | // Show Appropriate UIAlertView 337 | switch ([self alertType]) { 338 | 339 | case HarpyAlertTypeForce: { 340 | 341 | UIAlertController *alertController = [self createAlertController]; 342 | [alertController addAction:[self updateAlertAction]]; 343 | 344 | [self showAlertController:alertController]; 345 | 346 | } break; 347 | 348 | case HarpyAlertTypeOption: { 349 | 350 | UIAlertController *alertController = [self createAlertController]; 351 | [alertController addAction:[self nextTimeAlertAction]]; 352 | [alertController addAction:[self updateAlertAction]]; 353 | 354 | [self showAlertController:alertController]; 355 | 356 | } break; 357 | 358 | case HarpyAlertTypeSkip: { 359 | 360 | UIAlertController *alertController = [self createAlertController]; 361 | [alertController addAction:[self skipAlertAction]]; 362 | [alertController addAction:[self nextTimeAlertAction]]; 363 | [alertController addAction:[self updateAlertAction]]; 364 | 365 | [self showAlertController:alertController]; 366 | 367 | } break; 368 | 369 | case HarpyAlertTypeNone: { //If the delegate is set, pass a localized update message. Otherwise, do nothing. 370 | if ([self.delegate respondsToSelector:@selector(harpyDidDetectNewVersionWithoutAlert:)]) { 371 | [self.delegate harpyDidDetectNewVersionWithoutAlert:_theNewVersionMessage]; 372 | } 373 | } break; 374 | } 375 | } 376 | 377 | - (void)showAlertController:(UIAlertController *)alertController { 378 | 379 | if (_presentingViewController != nil) { 380 | [_presentingViewController presentViewController:alertController animated:YES completion:nil]; 381 | 382 | if (_alertControllerTintColor) { 383 | [alertController.view setTintColor:_alertControllerTintColor]; 384 | } 385 | } 386 | 387 | if ([self.delegate respondsToSelector:@selector(harpyDidShowUpdateDialog)]){ 388 | [self.delegate harpyDidShowUpdateDialog]; 389 | } 390 | } 391 | 392 | - (UIAlertController *)createAlertController { 393 | 394 | UIAlertController *alertController = [UIAlertController alertControllerWithTitle:_updateAvailableMessage 395 | message:_theNewVersionMessage 396 | preferredStyle:UIAlertControllerStyleAlert]; 397 | 398 | return alertController; 399 | } 400 | 401 | - (void)alertTypeForVersion:(NSString *)currentAppStoreVersion { 402 | // Check what version the update is, major, minor or a patch 403 | NSArray *oldVersionComponents = [[self currentInstalledVersion] componentsSeparatedByString:@"."]; 404 | NSArray *newVersionComponents = [currentAppStoreVersion componentsSeparatedByString: @"."]; 405 | 406 | BOOL oldVersionComponentIsProperFormat = (2 <= [oldVersionComponents count] && [oldVersionComponents count] <= 4); 407 | BOOL newVersionComponentIsProperFormat = (2 <= [newVersionComponents count] && [newVersionComponents count] <= 4); 408 | 409 | if (oldVersionComponentIsProperFormat && newVersionComponentIsProperFormat) { 410 | if ([newVersionComponents[0] integerValue] > [oldVersionComponents[0] integerValue]) { // A.b.c.d 411 | if (_majorUpdateAlertType) _alertType = _majorUpdateAlertType; 412 | } else if ([newVersionComponents[1] integerValue] > [oldVersionComponents[1] integerValue]) { // a.B.c.d 413 | if (_minorUpdateAlertType) _alertType = _minorUpdateAlertType; 414 | } else if ((newVersionComponents.count > 2) && (oldVersionComponents.count <= 2 || ([newVersionComponents[2] integerValue] > [oldVersionComponents[2] integerValue]))) { // a.b.C.d 415 | if (_patchUpdateAlertType) _alertType = _patchUpdateAlertType; 416 | } else if ((newVersionComponents.count > 3) && (oldVersionComponents.count <= 3 || ([newVersionComponents[3] integerValue] > [oldVersionComponents[3] integerValue]))) { // a.b.c.D 417 | if (_revisionUpdateAlertType) _alertType = _revisionUpdateAlertType; 418 | } 419 | } 420 | } 421 | 422 | - (void)localizeAlertStringsForCurrentAppStoreVersion:(NSString *)currentAppStoreVersion { 423 | // Reference App's name 424 | _appName = _appName ? _appName : [[[NSBundle mainBundle] infoDictionary] objectForKey:(NSString *)kCFBundleNameKey]; 425 | 426 | // Force localization if _forceLanguageLocalization is set 427 | if (_forceLanguageLocalization) { 428 | _updateAvailableMessage = [self forcedLocalizedStringForKey:@"Update Available"]; 429 | _theNewVersionMessage = [NSString stringWithFormat:[self forcedLocalizedStringForKey:@"A new version of %@ is available. Please update to version %@ now."], _appName, currentAppStoreVersion]; 430 | _updateButtonText = [self forcedLocalizedStringForKey:@"Update"]; 431 | _nextTimeButtonText = [self forcedLocalizedStringForKey:@"Next time"]; 432 | _skipButtonText = [self forcedLocalizedStringForKey:@"Skip this version"]; 433 | } else { 434 | _updateAvailableMessage = [self localizedStringForKey:@"Update Available"]; 435 | _theNewVersionMessage = [NSString stringWithFormat:[self localizedStringForKey:@"A new version of %@ is available. Please update to version %@ now."], _appName, currentAppStoreVersion]; 436 | _updateButtonText = [self localizedStringForKey:@"Update"]; 437 | _nextTimeButtonText = [self localizedStringForKey:@"Next time"]; 438 | _skipButtonText = [self localizedStringForKey:@"Skip this version"]; 439 | } 440 | } 441 | 442 | #pragma mark - NSBundle 443 | 444 | - (NSString *)bundleID { 445 | return [NSBundle mainBundle].bundleIdentifier; 446 | } 447 | 448 | - (NSString *)bundlePath { 449 | return [[NSBundle bundleForClass:[self class]] pathForResource:@"Harpy" ofType:@"bundle"]; 450 | } 451 | 452 | - (NSString *)localizedStringForKey:(NSString *)stringKey { 453 | return ([[NSBundle bundleForClass:[self class]] pathForResource:@"Harpy" ofType:@"bundle"] ? [[NSBundle bundleWithPath:[self bundlePath]] localizedStringForKey:stringKey value:stringKey table:@"HarpyLocalizable"] : stringKey); 454 | } 455 | 456 | - (NSString *)forcedLocalizedStringForKey:(NSString *)stringKey { 457 | NSString *path = [[NSBundle bundleWithPath:[self bundlePath]] pathForResource:[self forceLanguageLocalization] ofType:@"lproj"]; 458 | return [[NSBundle bundleWithPath:path] localizedStringForKey:stringKey value:stringKey table:@"HarpyLocalizable"]; 459 | } 460 | 461 | #pragma mark - NSDate 462 | 463 | - (NSInteger)daysSinceDate:(NSDate *)date { 464 | NSCalendar *calendar = NSCalendar.currentCalendar; 465 | NSDateComponents *components = [calendar components:NSCalendarUnitDay fromDate:date toDate:[NSDate new] options:0]; 466 | return components.day; 467 | } 468 | 469 | - (NSInteger)daysSinceDateString:(NSString *)dateString { 470 | NSDateFormatter *dateFormatter = [NSDateFormatter new]; 471 | dateFormatter.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"]; 472 | dateFormatter.dateFormat = @"yyyy-MM-dd'T'HH:mm:ss'Z'"; 473 | NSDate *releaseDate = [dateFormatter dateFromString:dateString]; 474 | return [self daysSinceDate:releaseDate]; 475 | } 476 | 477 | #pragma mark - UIAlertActions 478 | 479 | - (UIAlertAction *)updateAlertAction { 480 | UIAlertAction *updateAlertAction = [UIAlertAction actionWithTitle:_updateButtonText 481 | style:UIAlertActionStyleDefault 482 | handler:^(UIAlertAction *action) { 483 | [self launchAppStore]; 484 | }]; 485 | 486 | return updateAlertAction; 487 | } 488 | 489 | - (UIAlertAction *)nextTimeAlertAction { 490 | UIAlertAction *nextTimeAlertAction = [UIAlertAction actionWithTitle:_nextTimeButtonText 491 | style:UIAlertActionStyleDefault 492 | handler:^(UIAlertAction *action) { 493 | if([self.delegate respondsToSelector:@selector(harpyUserDidCancel)]){ 494 | [self.delegate harpyUserDidCancel]; 495 | } 496 | }]; 497 | 498 | return nextTimeAlertAction; 499 | } 500 | 501 | - (UIAlertAction *)skipAlertAction { 502 | __typeof__(self) __weak weakSelf = self; 503 | 504 | UIAlertAction *skipAlertAction = [UIAlertAction actionWithTitle:_skipButtonText 505 | style:UIAlertActionStyleDefault 506 | handler:^(UIAlertAction *action) { 507 | [[NSUserDefaults standardUserDefaults] setObject:weakSelf.currentAppStoreVersion forKey:HarpyDefaultSkippedVersion]; 508 | [[NSUserDefaults standardUserDefaults] synchronize]; 509 | if([self.delegate respondsToSelector:@selector(harpyUserDidSkipVersion)]){ 510 | [self.delegate harpyUserDidSkipVersion]; 511 | } 512 | }]; 513 | 514 | return skipAlertAction; 515 | } 516 | 517 | #pragma mark - Logging 518 | 519 | - (void)printDebugMessage:(NSString * _Nonnull)message { 520 | 521 | if ([self isDebugEnabled]) { 522 | NSLog(@"[Harpy]: %@", message); 523 | } 524 | 525 | } 526 | 527 | #pragma mark - Testing 528 | 529 | - (NSString *)testLocalizedStringForKey:(NSString *)stringKey { 530 | return [self forcedLocalizedStringForKey:stringKey]; 531 | } 532 | 533 | - (void)testSetCurrentInstalledVersion:(NSString *)version { 534 | _currentInstalledVersion = version; 535 | } 536 | 537 | - (void)testSetCurrentAppStoreVersion:(NSString *)version { 538 | _currentAppStoreVersion = version; 539 | } 540 | 541 | - (BOOL)testIsAppStoreVersionNewer { 542 | return [self isAppStoreVersionNewer:_currentAppStoreVersion]; 543 | } 544 | 545 | 546 | @end 547 | -------------------------------------------------------------------------------- /HarpyExample/Default-568h@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArtSabintsev/Harpy/e76b5e061826d2abfdd6c2a1ae7baa3229ab4186/HarpyExample/Default-568h@2x.png -------------------------------------------------------------------------------- /HarpyExample/Harpy/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | NSPrincipalClass 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /HarpyExample/HarpyExample.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 55B03A871E6A7A7200BE8DAC /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 55B03A841E6A7A7200BE8DAC /* AppDelegate.m */; }; 11 | 55B03A881E6A7A7200BE8DAC /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 55B03A861E6A7A7200BE8DAC /* ViewController.m */; }; 12 | 55B03A8C1E6A7A7F00BE8DAC /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 55B03A8A1E6A7A7F00BE8DAC /* main.m */; }; 13 | 55B03A911E6A7A8500BE8DAC /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 55B03A8D1E6A7A8500BE8DAC /* LaunchScreen.storyboard */; }; 14 | 55B03A921E6A7A8500BE8DAC /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 55B03A8F1E6A7A8500BE8DAC /* Main.storyboard */; }; 15 | 8E2206581F8EE724003FB62C /* Harpy.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8E2206511F8EE724003FB62C /* Harpy.framework */; }; 16 | 8E2206591F8EE724003FB62C /* Harpy.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 8E2206511F8EE724003FB62C /* Harpy.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 17 | 8E22065D1F8EE730003FB62C /* Harpy.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 8E78694A1E98B842001B4BBB /* Harpy.bundle */; }; 18 | 8E22065E1F8EE732003FB62C /* Harpy.h in Headers */ = {isa = PBXBuildFile; fileRef = 8E78694B1E98B842001B4BBB /* Harpy.h */; settings = {ATTRIBUTES = (Public, ); }; }; 19 | 8E22065F1F8EE735003FB62C /* Harpy.m in Sources */ = {isa = PBXBuildFile; fileRef = 8E78694C1E98B842001B4BBB /* Harpy.m */; }; 20 | 8EE663A81F99AF2900DACA88 /* HarpyTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8EE663A61F99AF1D00DACA88 /* HarpyTests.m */; }; 21 | 8EE663AC1F99B29400DACA88 /* Harpy.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8E2206511F8EE724003FB62C /* Harpy.framework */; }; 22 | /* End PBXBuildFile section */ 23 | 24 | /* Begin PBXContainerItemProxy section */ 25 | 55B79D971D36EC23001197C0 /* PBXContainerItemProxy */ = { 26 | isa = PBXContainerItemProxy; 27 | containerPortal = 55CF3A1B1BC212E30023B63D /* Project object */; 28 | proxyType = 1; 29 | remoteGlobalIDString = 55CF3A221BC212E30023B63D; 30 | remoteInfo = "Sample Project"; 31 | }; 32 | 8E2206561F8EE724003FB62C /* PBXContainerItemProxy */ = { 33 | isa = PBXContainerItemProxy; 34 | containerPortal = 55CF3A1B1BC212E30023B63D /* Project object */; 35 | proxyType = 1; 36 | remoteGlobalIDString = 8E2206501F8EE724003FB62C; 37 | remoteInfo = Harpy; 38 | }; 39 | /* End PBXContainerItemProxy section */ 40 | 41 | /* Begin PBXCopyFilesBuildPhase section */ 42 | 550697BB1C685420008C5BAF /* Embed Frameworks */ = { 43 | isa = PBXCopyFilesBuildPhase; 44 | buildActionMask = 2147483647; 45 | dstPath = ""; 46 | dstSubfolderSpec = 10; 47 | files = ( 48 | 8E2206591F8EE724003FB62C /* Harpy.framework in Embed Frameworks */, 49 | ); 50 | name = "Embed Frameworks"; 51 | runOnlyForDeploymentPostprocessing = 0; 52 | }; 53 | /* End PBXCopyFilesBuildPhase section */ 54 | 55 | /* Begin PBXFileReference section */ 56 | 55B03A831E6A7A7200BE8DAC /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = HarpyExample/AppDelegate.h; sourceTree = SOURCE_ROOT; }; 57 | 55B03A841E6A7A7200BE8DAC /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AppDelegate.m; path = HarpyExample/AppDelegate.m; sourceTree = SOURCE_ROOT; }; 58 | 55B03A851E6A7A7200BE8DAC /* ViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ViewController.h; path = HarpyExample/ViewController.h; sourceTree = SOURCE_ROOT; }; 59 | 55B03A861E6A7A7200BE8DAC /* ViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ViewController.m; path = HarpyExample/ViewController.m; sourceTree = SOURCE_ROOT; }; 60 | 55B03A891E6A7A7F00BE8DAC /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = HarpyExample/Info.plist; sourceTree = SOURCE_ROOT; }; 61 | 55B03A8A1E6A7A7F00BE8DAC /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = HarpyExample/main.m; sourceTree = SOURCE_ROOT; }; 62 | 55B03A8E1E6A7A8500BE8DAC /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = HarpyExample/Base.lproj/LaunchScreen.storyboard; sourceTree = SOURCE_ROOT; }; 63 | 55B03A901E6A7A8500BE8DAC /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = HarpyExample/Base.lproj/Main.storyboard; sourceTree = SOURCE_ROOT; }; 64 | 55B79D921D36EC23001197C0 /* HarpyExampleTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = HarpyExampleTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 65 | 55CF3A231BC212E30023B63D /* HarpyExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = HarpyExample.app; sourceTree = BUILT_PRODUCTS_DIR; }; 66 | 8E2206511F8EE724003FB62C /* Harpy.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Harpy.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 67 | 8E2206541F8EE724003FB62C /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 68 | 8E78694A1E98B842001B4BBB /* Harpy.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = Harpy.bundle; sourceTree = ""; }; 69 | 8E78694B1E98B842001B4BBB /* Harpy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Harpy.h; sourceTree = ""; }; 70 | 8E78694C1E98B842001B4BBB /* Harpy.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Harpy.m; sourceTree = ""; }; 71 | 8EE663A61F99AF1D00DACA88 /* HarpyTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = HarpyTests.m; sourceTree = ""; }; 72 | 8EE663A71F99AF1D00DACA88 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 73 | /* End PBXFileReference section */ 74 | 75 | /* Begin PBXFrameworksBuildPhase section */ 76 | 55B79D8F1D36EC23001197C0 /* Frameworks */ = { 77 | isa = PBXFrameworksBuildPhase; 78 | buildActionMask = 2147483647; 79 | files = ( 80 | 8EE663AC1F99B29400DACA88 /* Harpy.framework in Frameworks */, 81 | ); 82 | runOnlyForDeploymentPostprocessing = 0; 83 | }; 84 | 55CF3A201BC212E30023B63D /* Frameworks */ = { 85 | isa = PBXFrameworksBuildPhase; 86 | buildActionMask = 2147483647; 87 | files = ( 88 | 8E2206581F8EE724003FB62C /* Harpy.framework in Frameworks */, 89 | ); 90 | runOnlyForDeploymentPostprocessing = 0; 91 | }; 92 | 8E22064D1F8EE724003FB62C /* Frameworks */ = { 93 | isa = PBXFrameworksBuildPhase; 94 | buildActionMask = 2147483647; 95 | files = ( 96 | ); 97 | runOnlyForDeploymentPostprocessing = 0; 98 | }; 99 | /* End PBXFrameworksBuildPhase section */ 100 | 101 | /* Begin PBXGroup section */ 102 | 55CF3A1A1BC212E30023B63D = { 103 | isa = PBXGroup; 104 | children = ( 105 | 8EE663A51F99AF1D00DACA88 /* HarpyExampleTests */, 106 | 8E7869491E98B842001B4BBB /* Harpy */, 107 | 55CF3A251BC212E30023B63D /* HarpyExample */, 108 | 8E2206521F8EE724003FB62C /* Harpy */, 109 | 55CF3A241BC212E30023B63D /* Products */, 110 | 8EE663AB1F99B29400DACA88 /* Frameworks */, 111 | ); 112 | sourceTree = ""; 113 | }; 114 | 55CF3A241BC212E30023B63D /* Products */ = { 115 | isa = PBXGroup; 116 | children = ( 117 | 55CF3A231BC212E30023B63D /* HarpyExample.app */, 118 | 55B79D921D36EC23001197C0 /* HarpyExampleTests.xctest */, 119 | 8E2206511F8EE724003FB62C /* Harpy.framework */, 120 | ); 121 | name = Products; 122 | sourceTree = ""; 123 | }; 124 | 55CF3A251BC212E30023B63D /* HarpyExample */ = { 125 | isa = PBXGroup; 126 | children = ( 127 | 55B03A831E6A7A7200BE8DAC /* AppDelegate.h */, 128 | 55B03A841E6A7A7200BE8DAC /* AppDelegate.m */, 129 | 55B03A851E6A7A7200BE8DAC /* ViewController.h */, 130 | 55B03A861E6A7A7200BE8DAC /* ViewController.m */, 131 | 55B03A8D1E6A7A8500BE8DAC /* LaunchScreen.storyboard */, 132 | 55B03A8F1E6A7A8500BE8DAC /* Main.storyboard */, 133 | 55CF3A261BC212E30023B63D /* Supporting Files */, 134 | ); 135 | name = HarpyExample; 136 | path = "Sampe Project"; 137 | sourceTree = ""; 138 | }; 139 | 55CF3A261BC212E30023B63D /* Supporting Files */ = { 140 | isa = PBXGroup; 141 | children = ( 142 | 55B03A891E6A7A7F00BE8DAC /* Info.plist */, 143 | 55B03A8A1E6A7A7F00BE8DAC /* main.m */, 144 | ); 145 | name = "Supporting Files"; 146 | sourceTree = ""; 147 | }; 148 | 8E2206521F8EE724003FB62C /* Harpy */ = { 149 | isa = PBXGroup; 150 | children = ( 151 | 8E2206541F8EE724003FB62C /* Info.plist */, 152 | ); 153 | path = Harpy; 154 | sourceTree = ""; 155 | }; 156 | 8E7869491E98B842001B4BBB /* Harpy */ = { 157 | isa = PBXGroup; 158 | children = ( 159 | 8E78694A1E98B842001B4BBB /* Harpy.bundle */, 160 | 8E78694B1E98B842001B4BBB /* Harpy.h */, 161 | 8E78694C1E98B842001B4BBB /* Harpy.m */, 162 | ); 163 | name = Harpy; 164 | path = ../Harpy; 165 | sourceTree = ""; 166 | }; 167 | 8EE663A51F99AF1D00DACA88 /* HarpyExampleTests */ = { 168 | isa = PBXGroup; 169 | children = ( 170 | 8EE663A61F99AF1D00DACA88 /* HarpyTests.m */, 171 | 8EE663A71F99AF1D00DACA88 /* Info.plist */, 172 | ); 173 | path = HarpyExampleTests; 174 | sourceTree = ""; 175 | }; 176 | 8EE663AB1F99B29400DACA88 /* Frameworks */ = { 177 | isa = PBXGroup; 178 | children = ( 179 | ); 180 | name = Frameworks; 181 | sourceTree = ""; 182 | }; 183 | /* End PBXGroup section */ 184 | 185 | /* Begin PBXHeadersBuildPhase section */ 186 | 8E22064E1F8EE724003FB62C /* Headers */ = { 187 | isa = PBXHeadersBuildPhase; 188 | buildActionMask = 2147483647; 189 | files = ( 190 | 8E22065E1F8EE732003FB62C /* Harpy.h in Headers */, 191 | ); 192 | runOnlyForDeploymentPostprocessing = 0; 193 | }; 194 | /* End PBXHeadersBuildPhase section */ 195 | 196 | /* Begin PBXNativeTarget section */ 197 | 55B79D911D36EC23001197C0 /* HarpyExampleTests */ = { 198 | isa = PBXNativeTarget; 199 | buildConfigurationList = 55B79D991D36EC23001197C0 /* Build configuration list for PBXNativeTarget "HarpyExampleTests" */; 200 | buildPhases = ( 201 | 55B79D8E1D36EC23001197C0 /* Sources */, 202 | 55B79D8F1D36EC23001197C0 /* Frameworks */, 203 | 55B79D901D36EC23001197C0 /* Resources */, 204 | ); 205 | buildRules = ( 206 | ); 207 | dependencies = ( 208 | 55B79D981D36EC23001197C0 /* PBXTargetDependency */, 209 | ); 210 | name = HarpyExampleTests; 211 | productName = "Sample ProjectTests"; 212 | productReference = 55B79D921D36EC23001197C0 /* HarpyExampleTests.xctest */; 213 | productType = "com.apple.product-type.bundle.unit-test"; 214 | }; 215 | 55CF3A221BC212E30023B63D /* HarpyExample */ = { 216 | isa = PBXNativeTarget; 217 | buildConfigurationList = 55CF3A3A1BC212E30023B63D /* Build configuration list for PBXNativeTarget "HarpyExample" */; 218 | buildPhases = ( 219 | 55CF3A1F1BC212E30023B63D /* Sources */, 220 | 55CF3A201BC212E30023B63D /* Frameworks */, 221 | 55CF3A211BC212E30023B63D /* Resources */, 222 | 550697BB1C685420008C5BAF /* Embed Frameworks */, 223 | ); 224 | buildRules = ( 225 | ); 226 | dependencies = ( 227 | 8E2206571F8EE724003FB62C /* PBXTargetDependency */, 228 | ); 229 | name = HarpyExample; 230 | productName = "Sampe Project"; 231 | productReference = 55CF3A231BC212E30023B63D /* HarpyExample.app */; 232 | productType = "com.apple.product-type.application"; 233 | }; 234 | 8E2206501F8EE724003FB62C /* Harpy */ = { 235 | isa = PBXNativeTarget; 236 | buildConfigurationList = 8E22065C1F8EE724003FB62C /* Build configuration list for PBXNativeTarget "Harpy" */; 237 | buildPhases = ( 238 | 8E22064C1F8EE724003FB62C /* Sources */, 239 | 8E22064D1F8EE724003FB62C /* Frameworks */, 240 | 8E22064E1F8EE724003FB62C /* Headers */, 241 | 8E22064F1F8EE724003FB62C /* Resources */, 242 | ); 243 | buildRules = ( 244 | ); 245 | dependencies = ( 246 | ); 247 | name = Harpy; 248 | productName = Harpy; 249 | productReference = 8E2206511F8EE724003FB62C /* Harpy.framework */; 250 | productType = "com.apple.product-type.framework"; 251 | }; 252 | /* End PBXNativeTarget section */ 253 | 254 | /* Begin PBXProject section */ 255 | 55CF3A1B1BC212E30023B63D /* Project object */ = { 256 | isa = PBXProject; 257 | attributes = { 258 | LastSwiftUpdateCheck = 0730; 259 | LastUpgradeCheck = 1000; 260 | ORGANIZATIONNAME = "Arthur Ariel Sabintsev"; 261 | TargetAttributes = { 262 | 55B79D911D36EC23001197C0 = { 263 | CreatedOnToolsVersion = 7.3.1; 264 | DevelopmentTeam = HT94948NDD; 265 | TestTargetID = 55CF3A221BC212E30023B63D; 266 | }; 267 | 55CF3A221BC212E30023B63D = { 268 | CreatedOnToolsVersion = 7.0.1; 269 | DevelopmentTeam = HT94948NDD; 270 | }; 271 | 8E2206501F8EE724003FB62C = { 272 | CreatedOnToolsVersion = 9.0; 273 | DevelopmentTeam = HT94948NDD; 274 | ProvisioningStyle = Automatic; 275 | }; 276 | }; 277 | }; 278 | buildConfigurationList = 55CF3A1E1BC212E30023B63D /* Build configuration list for PBXProject "HarpyExample" */; 279 | compatibilityVersion = "Xcode 3.2"; 280 | developmentRegion = English; 281 | hasScannedForEncodings = 0; 282 | knownRegions = ( 283 | en, 284 | Base, 285 | ); 286 | mainGroup = 55CF3A1A1BC212E30023B63D; 287 | productRefGroup = 55CF3A241BC212E30023B63D /* Products */; 288 | projectDirPath = ""; 289 | projectRoot = ""; 290 | targets = ( 291 | 55CF3A221BC212E30023B63D /* HarpyExample */, 292 | 55B79D911D36EC23001197C0 /* HarpyExampleTests */, 293 | 8E2206501F8EE724003FB62C /* Harpy */, 294 | ); 295 | }; 296 | /* End PBXProject section */ 297 | 298 | /* Begin PBXResourcesBuildPhase section */ 299 | 55B79D901D36EC23001197C0 /* Resources */ = { 300 | isa = PBXResourcesBuildPhase; 301 | buildActionMask = 2147483647; 302 | files = ( 303 | ); 304 | runOnlyForDeploymentPostprocessing = 0; 305 | }; 306 | 55CF3A211BC212E30023B63D /* Resources */ = { 307 | isa = PBXResourcesBuildPhase; 308 | buildActionMask = 2147483647; 309 | files = ( 310 | 55B03A911E6A7A8500BE8DAC /* LaunchScreen.storyboard in Resources */, 311 | 55B03A921E6A7A8500BE8DAC /* Main.storyboard in Resources */, 312 | ); 313 | runOnlyForDeploymentPostprocessing = 0; 314 | }; 315 | 8E22064F1F8EE724003FB62C /* Resources */ = { 316 | isa = PBXResourcesBuildPhase; 317 | buildActionMask = 2147483647; 318 | files = ( 319 | 8E22065D1F8EE730003FB62C /* Harpy.bundle in Resources */, 320 | ); 321 | runOnlyForDeploymentPostprocessing = 0; 322 | }; 323 | /* End PBXResourcesBuildPhase section */ 324 | 325 | /* Begin PBXSourcesBuildPhase section */ 326 | 55B79D8E1D36EC23001197C0 /* Sources */ = { 327 | isa = PBXSourcesBuildPhase; 328 | buildActionMask = 2147483647; 329 | files = ( 330 | 8EE663A81F99AF2900DACA88 /* HarpyTests.m in Sources */, 331 | ); 332 | runOnlyForDeploymentPostprocessing = 0; 333 | }; 334 | 55CF3A1F1BC212E30023B63D /* Sources */ = { 335 | isa = PBXSourcesBuildPhase; 336 | buildActionMask = 2147483647; 337 | files = ( 338 | 55B03A871E6A7A7200BE8DAC /* AppDelegate.m in Sources */, 339 | 55B03A8C1E6A7A7F00BE8DAC /* main.m in Sources */, 340 | 55B03A881E6A7A7200BE8DAC /* ViewController.m in Sources */, 341 | ); 342 | runOnlyForDeploymentPostprocessing = 0; 343 | }; 344 | 8E22064C1F8EE724003FB62C /* Sources */ = { 345 | isa = PBXSourcesBuildPhase; 346 | buildActionMask = 2147483647; 347 | files = ( 348 | 8E22065F1F8EE735003FB62C /* Harpy.m in Sources */, 349 | ); 350 | runOnlyForDeploymentPostprocessing = 0; 351 | }; 352 | /* End PBXSourcesBuildPhase section */ 353 | 354 | /* Begin PBXTargetDependency section */ 355 | 55B79D981D36EC23001197C0 /* PBXTargetDependency */ = { 356 | isa = PBXTargetDependency; 357 | target = 55CF3A221BC212E30023B63D /* HarpyExample */; 358 | targetProxy = 55B79D971D36EC23001197C0 /* PBXContainerItemProxy */; 359 | }; 360 | 8E2206571F8EE724003FB62C /* PBXTargetDependency */ = { 361 | isa = PBXTargetDependency; 362 | target = 8E2206501F8EE724003FB62C /* Harpy */; 363 | targetProxy = 8E2206561F8EE724003FB62C /* PBXContainerItemProxy */; 364 | }; 365 | /* End PBXTargetDependency section */ 366 | 367 | /* Begin PBXVariantGroup section */ 368 | 55B03A8D1E6A7A8500BE8DAC /* LaunchScreen.storyboard */ = { 369 | isa = PBXVariantGroup; 370 | children = ( 371 | 55B03A8E1E6A7A8500BE8DAC /* Base */, 372 | ); 373 | name = LaunchScreen.storyboard; 374 | sourceTree = ""; 375 | }; 376 | 55B03A8F1E6A7A8500BE8DAC /* Main.storyboard */ = { 377 | isa = PBXVariantGroup; 378 | children = ( 379 | 55B03A901E6A7A8500BE8DAC /* Base */, 380 | ); 381 | name = Main.storyboard; 382 | sourceTree = ""; 383 | }; 384 | /* End PBXVariantGroup section */ 385 | 386 | /* Begin XCBuildConfiguration section */ 387 | 55B79D9A1D36EC23001197C0 /* Debug */ = { 388 | isa = XCBuildConfiguration; 389 | buildSettings = { 390 | BUNDLE_LOADER = "$(TEST_HOST)"; 391 | CLANG_ANALYZER_NONNULL = YES; 392 | DEVELOPMENT_TEAM = HT94948NDD; 393 | INFOPLIST_FILE = HarpyExampleTests/Info.plist; 394 | IPHONEOS_DEPLOYMENT_TARGET = 9.3; 395 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 396 | PRODUCT_BUNDLE_IDENTIFIER = "com.sabintsev.Sample-ProjectTests"; 397 | PRODUCT_NAME = "$(TARGET_NAME)"; 398 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/HarpyExample.app/HarpyExample"; 399 | }; 400 | name = Debug; 401 | }; 402 | 55B79D9B1D36EC23001197C0 /* Release */ = { 403 | isa = XCBuildConfiguration; 404 | buildSettings = { 405 | BUNDLE_LOADER = "$(TEST_HOST)"; 406 | CLANG_ANALYZER_NONNULL = YES; 407 | DEVELOPMENT_TEAM = HT94948NDD; 408 | INFOPLIST_FILE = HarpyExampleTests/Info.plist; 409 | IPHONEOS_DEPLOYMENT_TARGET = 9.3; 410 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 411 | PRODUCT_BUNDLE_IDENTIFIER = "com.sabintsev.Sample-ProjectTests"; 412 | PRODUCT_NAME = "$(TARGET_NAME)"; 413 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/HarpyExample.app/HarpyExample"; 414 | }; 415 | name = Release; 416 | }; 417 | 55CF3A381BC212E30023B63D /* Debug */ = { 418 | isa = XCBuildConfiguration; 419 | buildSettings = { 420 | ALWAYS_SEARCH_USER_PATHS = NO; 421 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 422 | CLANG_CXX_LIBRARY = "libc++"; 423 | CLANG_ENABLE_MODULES = YES; 424 | CLANG_ENABLE_OBJC_ARC = YES; 425 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 426 | CLANG_WARN_BOOL_CONVERSION = YES; 427 | CLANG_WARN_COMMA = YES; 428 | CLANG_WARN_CONSTANT_CONVERSION = YES; 429 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 430 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 431 | CLANG_WARN_EMPTY_BODY = YES; 432 | CLANG_WARN_ENUM_CONVERSION = YES; 433 | CLANG_WARN_INFINITE_RECURSION = YES; 434 | CLANG_WARN_INT_CONVERSION = YES; 435 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 436 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 437 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 438 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 439 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 440 | CLANG_WARN_STRICT_PROTOTYPES = YES; 441 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 442 | CLANG_WARN_UNREACHABLE_CODE = YES; 443 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 444 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 445 | COPY_PHASE_STRIP = NO; 446 | DEBUG_INFORMATION_FORMAT = dwarf; 447 | ENABLE_STRICT_OBJC_MSGSEND = YES; 448 | ENABLE_TESTABILITY = YES; 449 | GCC_C_LANGUAGE_STANDARD = gnu99; 450 | GCC_DYNAMIC_NO_PIC = NO; 451 | GCC_NO_COMMON_BLOCKS = YES; 452 | GCC_OPTIMIZATION_LEVEL = 0; 453 | GCC_PREPROCESSOR_DEFINITIONS = ( 454 | "DEBUG=1", 455 | "$(inherited)", 456 | ); 457 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 458 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 459 | GCC_WARN_UNDECLARED_SELECTOR = YES; 460 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 461 | GCC_WARN_UNUSED_FUNCTION = YES; 462 | GCC_WARN_UNUSED_VARIABLE = YES; 463 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 464 | MTL_ENABLE_DEBUG_INFO = YES; 465 | ONLY_ACTIVE_ARCH = YES; 466 | SDKROOT = iphoneos; 467 | }; 468 | name = Debug; 469 | }; 470 | 55CF3A391BC212E30023B63D /* Release */ = { 471 | isa = XCBuildConfiguration; 472 | buildSettings = { 473 | ALWAYS_SEARCH_USER_PATHS = NO; 474 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 475 | CLANG_CXX_LIBRARY = "libc++"; 476 | CLANG_ENABLE_MODULES = YES; 477 | CLANG_ENABLE_OBJC_ARC = YES; 478 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 479 | CLANG_WARN_BOOL_CONVERSION = YES; 480 | CLANG_WARN_COMMA = YES; 481 | CLANG_WARN_CONSTANT_CONVERSION = YES; 482 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 483 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 484 | CLANG_WARN_EMPTY_BODY = YES; 485 | CLANG_WARN_ENUM_CONVERSION = YES; 486 | CLANG_WARN_INFINITE_RECURSION = YES; 487 | CLANG_WARN_INT_CONVERSION = YES; 488 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 489 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 490 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 491 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 492 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 493 | CLANG_WARN_STRICT_PROTOTYPES = YES; 494 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 495 | CLANG_WARN_UNREACHABLE_CODE = YES; 496 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 497 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 498 | COPY_PHASE_STRIP = NO; 499 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 500 | ENABLE_NS_ASSERTIONS = NO; 501 | ENABLE_STRICT_OBJC_MSGSEND = YES; 502 | GCC_C_LANGUAGE_STANDARD = gnu99; 503 | GCC_NO_COMMON_BLOCKS = YES; 504 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 505 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 506 | GCC_WARN_UNDECLARED_SELECTOR = YES; 507 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 508 | GCC_WARN_UNUSED_FUNCTION = YES; 509 | GCC_WARN_UNUSED_VARIABLE = YES; 510 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 511 | MTL_ENABLE_DEBUG_INFO = NO; 512 | SDKROOT = iphoneos; 513 | VALIDATE_PRODUCT = YES; 514 | }; 515 | name = Release; 516 | }; 517 | 55CF3A3B1BC212E30023B63D /* Debug */ = { 518 | isa = XCBuildConfiguration; 519 | buildSettings = { 520 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 521 | CODE_SIGN_IDENTITY = "iPhone Developer"; 522 | DEVELOPMENT_TEAM = HT94948NDD; 523 | INFOPLIST_FILE = "$(SRCROOT)/HarpyExample/Info.plist"; 524 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 525 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 526 | PRODUCT_BUNDLE_IDENTIFIER = com.apple.itunesconnect.mobile; 527 | PRODUCT_NAME = HarpyExample; 528 | }; 529 | name = Debug; 530 | }; 531 | 55CF3A3C1BC212E30023B63D /* Release */ = { 532 | isa = XCBuildConfiguration; 533 | buildSettings = { 534 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 535 | CODE_SIGN_IDENTITY = "iPhone Developer"; 536 | DEVELOPMENT_TEAM = HT94948NDD; 537 | INFOPLIST_FILE = "$(SRCROOT)/HarpyExample/Info.plist"; 538 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 539 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 540 | PRODUCT_BUNDLE_IDENTIFIER = com.apple.itunesconnect.mobile; 541 | PRODUCT_NAME = HarpyExample; 542 | }; 543 | name = Release; 544 | }; 545 | 8E22065A1F8EE724003FB62C /* Debug */ = { 546 | isa = XCBuildConfiguration; 547 | buildSettings = { 548 | CLANG_ANALYZER_NONNULL = YES; 549 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 550 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 551 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 552 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 553 | CODE_SIGN_IDENTITY = "iPhone Developer"; 554 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; 555 | CODE_SIGN_STYLE = Automatic; 556 | CURRENT_PROJECT_VERSION = 1; 557 | DEFINES_MODULE = YES; 558 | DEVELOPMENT_TEAM = HT94948NDD; 559 | DYLIB_COMPATIBILITY_VERSION = 1; 560 | DYLIB_CURRENT_VERSION = 1; 561 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 562 | GCC_C_LANGUAGE_STANDARD = gnu11; 563 | INFOPLIST_FILE = Harpy/Info.plist; 564 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 565 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 566 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 567 | PRODUCT_BUNDLE_IDENTIFIER = com.sabintsev.Harpy; 568 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; 569 | SKIP_INSTALL = YES; 570 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 571 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 572 | SWIFT_VERSION = 4.0; 573 | TARGETED_DEVICE_FAMILY = "1,2"; 574 | VERSIONING_SYSTEM = "apple-generic"; 575 | VERSION_INFO_PREFIX = ""; 576 | }; 577 | name = Debug; 578 | }; 579 | 8E22065B1F8EE724003FB62C /* Release */ = { 580 | isa = XCBuildConfiguration; 581 | buildSettings = { 582 | CLANG_ANALYZER_NONNULL = YES; 583 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 584 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 585 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 586 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 587 | CODE_SIGN_IDENTITY = "iPhone Developer"; 588 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; 589 | CODE_SIGN_STYLE = Automatic; 590 | CURRENT_PROJECT_VERSION = 1; 591 | DEFINES_MODULE = YES; 592 | DEVELOPMENT_TEAM = HT94948NDD; 593 | DYLIB_COMPATIBILITY_VERSION = 1; 594 | DYLIB_CURRENT_VERSION = 1; 595 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 596 | GCC_C_LANGUAGE_STANDARD = gnu11; 597 | INFOPLIST_FILE = Harpy/Info.plist; 598 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 599 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 600 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 601 | PRODUCT_BUNDLE_IDENTIFIER = com.sabintsev.Harpy; 602 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; 603 | SKIP_INSTALL = YES; 604 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 605 | SWIFT_VERSION = 4.0; 606 | TARGETED_DEVICE_FAMILY = "1,2"; 607 | VERSIONING_SYSTEM = "apple-generic"; 608 | VERSION_INFO_PREFIX = ""; 609 | }; 610 | name = Release; 611 | }; 612 | /* End XCBuildConfiguration section */ 613 | 614 | /* Begin XCConfigurationList section */ 615 | 55B79D991D36EC23001197C0 /* Build configuration list for PBXNativeTarget "HarpyExampleTests" */ = { 616 | isa = XCConfigurationList; 617 | buildConfigurations = ( 618 | 55B79D9A1D36EC23001197C0 /* Debug */, 619 | 55B79D9B1D36EC23001197C0 /* Release */, 620 | ); 621 | defaultConfigurationIsVisible = 0; 622 | defaultConfigurationName = Release; 623 | }; 624 | 55CF3A1E1BC212E30023B63D /* Build configuration list for PBXProject "HarpyExample" */ = { 625 | isa = XCConfigurationList; 626 | buildConfigurations = ( 627 | 55CF3A381BC212E30023B63D /* Debug */, 628 | 55CF3A391BC212E30023B63D /* Release */, 629 | ); 630 | defaultConfigurationIsVisible = 0; 631 | defaultConfigurationName = Release; 632 | }; 633 | 55CF3A3A1BC212E30023B63D /* Build configuration list for PBXNativeTarget "HarpyExample" */ = { 634 | isa = XCConfigurationList; 635 | buildConfigurations = ( 636 | 55CF3A3B1BC212E30023B63D /* Debug */, 637 | 55CF3A3C1BC212E30023B63D /* Release */, 638 | ); 639 | defaultConfigurationIsVisible = 0; 640 | defaultConfigurationName = Release; 641 | }; 642 | 8E22065C1F8EE724003FB62C /* Build configuration list for PBXNativeTarget "Harpy" */ = { 643 | isa = XCConfigurationList; 644 | buildConfigurations = ( 645 | 8E22065A1F8EE724003FB62C /* Debug */, 646 | 8E22065B1F8EE724003FB62C /* Release */, 647 | ); 648 | defaultConfigurationIsVisible = 0; 649 | defaultConfigurationName = Release; 650 | }; 651 | /* End XCConfigurationList section */ 652 | }; 653 | rootObject = 55CF3A1B1BC212E30023B63D /* Project object */; 654 | } 655 | -------------------------------------------------------------------------------- /HarpyExample/HarpyExample.xcodeproj/xcshareddata/xcschemes/Harpy.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 34 | 35 | 45 | 46 | 52 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 70 | 71 | 72 | 73 | 75 | 76 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /HarpyExample/HarpyExample.xcodeproj/xcshareddata/xcschemes/HarpyExample.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 49 | 50 | 51 | 52 | 53 | 54 | 64 | 66 | 72 | 73 | 74 | 75 | 76 | 77 | 83 | 85 | 91 | 92 | 93 | 94 | 96 | 97 | 100 | 101 | 102 | -------------------------------------------------------------------------------- /HarpyExample/HarpyExample/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // Sample Project 4 | // 5 | // Created by Sabintsev, Arthur on 10/4/15. 6 | // Copyright © 2015 Arthur Ariel Sabintsev. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | 16 | @end 17 | 18 | -------------------------------------------------------------------------------- /HarpyExample/HarpyExample/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // Sample Project 4 | // 5 | // Created by Sabintsev, Arthur on 10/4/15. 6 | // Copyright © 2015 Arthur Ariel Sabintsev. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | #import "Harpy.h" 11 | 12 | @interface AppDelegate () 13 | 14 | @end 15 | 16 | @implementation AppDelegate 17 | 18 | 19 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 20 | 21 | // Present Window before calling Harpy 22 | [self.window makeKeyAndVisible]; 23 | 24 | // Set the UIViewController that will present an instance of UIAlertController 25 | [[Harpy sharedInstance] setPresentingViewController:_window.rootViewController]; 26 | 27 | // (Optional) Set the Delegate to track what a user clicked on, or to use a custom UI to present your message. 28 | [[Harpy sharedInstance] setDelegate:self]; 29 | 30 | // (Optional) When this is set, the alert will only show up if the current version has already been released for X days. 31 | // By default, this value is set to 1 (day) to avoid an issue where Apple updates the JSON faster than the app binary propogates to the App Store. 32 | // [[Harpy sharedInstance] setShowAlertAfterCurrentVersionHasBeenReleasedForDays:3]; 33 | 34 | // (Optional) The tintColor for the alertController 35 | // [[Harpy sharedInstance] setAlertControllerTintColor:[UIColor purpleColor]]; 36 | 37 | // (Optional) Set the App Name for your app 38 | // [[Harpy sharedInstance] setAppName:@"iTunes Connect Mobile"]; 39 | 40 | /* (Optional) Set the Alert Type for your app 41 | By default, Harpy is configured to use HarpyAlertTypeOption */ 42 | [[Harpy sharedInstance] setAlertType:HarpyAlertTypeOption]; 43 | 44 | /* (Optional) If your application is not available in the U.S. App Store, you must specify the two-letter 45 | country code for the region in which your applicaiton is available. */ 46 | // [[Harpy sharedInstance] setCountryCode:@"en-US"]; 47 | 48 | /* (Optional) Overrides system language to predefined language. 49 | Please use the HarpyLanguage constants defined in Harpy.h. */ 50 | // [[Harpy sharedInstance] setForceLanguageLocalization:HarpyLanguageRussian]; 51 | 52 | // Turn on Debug statements 53 | [[Harpy sharedInstance] setDebugEnabled:true]; 54 | 55 | // Perform check for new version of your app 56 | [[Harpy sharedInstance] checkVersion]; 57 | 58 | return YES; 59 | } 60 | 61 | - (void)applicationDidBecomeActive:(UIApplication *)application 62 | { 63 | 64 | /* 65 | Perform daily check for new version of your app 66 | Useful if user returns to you app from background after extended period of time 67 | Place in applicationDidBecomeActive: 68 | 69 | Also, performs version check on first launch. 70 | */ 71 | // [[Harpy sharedInstance] checkVersionDaily]; 72 | 73 | /* 74 | Perform weekly check for new version of your app 75 | Useful if you user returns to your app from background after extended period of time 76 | Place in applicationDidBecomeActive: 77 | 78 | Also, performs version check on first launch. 79 | */ 80 | // [[Harpy sharedInstance] checkVersionWeekly]; 81 | 82 | } 83 | 84 | - (void)applicationWillEnterForeground:(UIApplication *)application 85 | { 86 | /* 87 | Perform check for new version of your app 88 | Useful if user returns to you app from background after being sent tot he App Store, 89 | but doesn't update their app before coming back to your app. 90 | 91 | ONLY USE THIS IF YOU ARE USING *HarpyAlertTypeForce* 92 | 93 | Also, performs version check on first launch. 94 | */ 95 | // [[Harpy sharedInstance] checkVersion]; 96 | } 97 | 98 | #pragma mark - HarpyDelegate 99 | - (void)harpyDidShowUpdateDialog 100 | { 101 | NSLog(@"%s", __FUNCTION__); 102 | } 103 | 104 | - (void)harpyUserDidLaunchAppStore 105 | { 106 | NSLog(@"%s", __FUNCTION__); 107 | } 108 | 109 | - (void)harpyUserDidSkipVersion 110 | { 111 | NSLog(@"%s", __FUNCTION__); 112 | } 113 | 114 | - (void)harpyUserDidCancel 115 | { 116 | NSLog(@"%s", __FUNCTION__); 117 | } 118 | 119 | - (void)harpyDidDetectNewVersionWithoutAlert:(NSString *)message 120 | { 121 | NSLog(@"%@", message); 122 | } 123 | 124 | @end 125 | -------------------------------------------------------------------------------- /HarpyExample/HarpyExample/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | } 33 | ], 34 | "info" : { 35 | "version" : 1, 36 | "author" : "xcode" 37 | } 38 | } -------------------------------------------------------------------------------- /HarpyExample/HarpyExample/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /HarpyExample/HarpyExample/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /HarpyExample/HarpyExample/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /HarpyExample/HarpyExample/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // Sample Project 4 | // 5 | // Created by Sabintsev, Arthur on 10/4/15. 6 | // Copyright © 2015 Arthur Ariel Sabintsev. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UIViewController 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /HarpyExample/HarpyExample/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // Sample Project 4 | // 5 | // Created by Sabintsev, Arthur on 10/4/15. 6 | // Copyright © 2015 Arthur Ariel Sabintsev. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | 11 | @interface ViewController () 12 | 13 | @end 14 | 15 | @implementation ViewController 16 | 17 | - (void)viewDidLoad { 18 | [super viewDidLoad]; 19 | // Do any additional setup after loading the view, typically from a nib. 20 | } 21 | 22 | - (void)didReceiveMemoryWarning { 23 | [super didReceiveMemoryWarning]; 24 | // Dispose of any resources that can be recreated. 25 | } 26 | 27 | @end 28 | -------------------------------------------------------------------------------- /HarpyExample/HarpyExample/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // Sampe Project 4 | // 5 | // Created by Sabintsev, Arthur on 10/4/15. 6 | // Copyright © 2015 Arthur Ariel Sabintsev. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /HarpyExample/HarpyExampleTests/HarpyTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // Sample_ProjectTests.m 3 | // Sample ProjectTests 4 | // 5 | // Created by Sabintsev, Arthur on 7/13/16. 6 | // Copyright © 2016 Arthur Ariel Sabintsev. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "Harpy.h" 11 | 12 | @interface Sample_ProjectTests : XCTestCase 13 | 14 | @property (nonatomic, strong) Harpy *harpy; 15 | 16 | @end 17 | 18 | @implementation Sample_ProjectTests 19 | 20 | - (void)setUp { 21 | [super setUp]; 22 | 23 | _harpy = [Harpy sharedInstance]; 24 | } 25 | 26 | - (void)testSingleDigitVersionUpdate { 27 | [_harpy testSetCurrentInstalledVersion:@"1"]; 28 | 29 | [_harpy testSetCurrentAppStoreVersion:@"2"]; 30 | XCTAssertTrue([_harpy testIsAppStoreVersionNewer]); 31 | 32 | [_harpy testSetCurrentAppStoreVersion:@"2.0"]; 33 | XCTAssertTrue([_harpy testIsAppStoreVersionNewer]); 34 | 35 | [_harpy testSetCurrentAppStoreVersion:@"2.0.0"]; 36 | XCTAssertTrue([_harpy testIsAppStoreVersionNewer]); 37 | 38 | [_harpy testSetCurrentAppStoreVersion:@"2.0.0.0"]; 39 | XCTAssertTrue([_harpy testIsAppStoreVersionNewer]); 40 | 41 | [_harpy testSetCurrentAppStoreVersion:@"0"]; 42 | XCTAssertFalse([_harpy testIsAppStoreVersionNewer]); 43 | 44 | [_harpy testSetCurrentAppStoreVersion:@"0.9"]; 45 | XCTAssertFalse([_harpy testIsAppStoreVersionNewer]); 46 | 47 | [_harpy testSetCurrentAppStoreVersion:@"0.0.9"]; 48 | XCTAssertFalse([_harpy testIsAppStoreVersionNewer]); 49 | 50 | [_harpy testSetCurrentAppStoreVersion:@"0.0.0.9"]; 51 | XCTAssertFalse([_harpy testIsAppStoreVersionNewer]); 52 | } 53 | 54 | - (void)testDoubleDigitVersionUpdate { 55 | [_harpy testSetCurrentInstalledVersion:@"1.0"]; 56 | 57 | [_harpy testSetCurrentAppStoreVersion:@"2"]; 58 | XCTAssertTrue([_harpy testIsAppStoreVersionNewer]); 59 | 60 | [_harpy testSetCurrentAppStoreVersion:@"2.0"]; 61 | XCTAssertTrue([_harpy testIsAppStoreVersionNewer]); 62 | 63 | [_harpy testSetCurrentAppStoreVersion:@"2.0.0"]; 64 | XCTAssertTrue([_harpy testIsAppStoreVersionNewer]); 65 | 66 | [_harpy testSetCurrentAppStoreVersion:@"2.0.0.0"]; 67 | XCTAssertTrue([_harpy testIsAppStoreVersionNewer]); 68 | 69 | [_harpy testSetCurrentAppStoreVersion:@"0"]; 70 | XCTAssertFalse([_harpy testIsAppStoreVersionNewer]); 71 | 72 | [_harpy testSetCurrentAppStoreVersion:@"0.9"]; 73 | XCTAssertFalse([_harpy testIsAppStoreVersionNewer]); 74 | 75 | [_harpy testSetCurrentAppStoreVersion:@"0.0.9"]; 76 | XCTAssertFalse([_harpy testIsAppStoreVersionNewer]); 77 | 78 | [_harpy testSetCurrentAppStoreVersion:@"0.0.0.9"]; 79 | XCTAssertFalse([_harpy testIsAppStoreVersionNewer]); 80 | } 81 | 82 | - (void)testTripleDigitVersionUpdate { 83 | [_harpy testSetCurrentInstalledVersion:@"1.0.0"]; 84 | 85 | [_harpy testSetCurrentAppStoreVersion:@"2"]; 86 | XCTAssertTrue([_harpy testIsAppStoreVersionNewer]); 87 | 88 | [_harpy testSetCurrentAppStoreVersion:@"2.0"]; 89 | XCTAssertTrue([_harpy testIsAppStoreVersionNewer]); 90 | 91 | [_harpy testSetCurrentAppStoreVersion:@"2.0.0"]; 92 | XCTAssertTrue([_harpy testIsAppStoreVersionNewer]); 93 | 94 | [_harpy testSetCurrentAppStoreVersion:@"2.0.0.0"]; 95 | XCTAssertTrue([_harpy testIsAppStoreVersionNewer]); 96 | 97 | [_harpy testSetCurrentAppStoreVersion:@"0"]; 98 | XCTAssertFalse([_harpy testIsAppStoreVersionNewer]); 99 | 100 | [_harpy testSetCurrentAppStoreVersion:@"0.9"]; 101 | XCTAssertFalse([_harpy testIsAppStoreVersionNewer]); 102 | 103 | [_harpy testSetCurrentAppStoreVersion:@"0.0.9"]; 104 | XCTAssertFalse([_harpy testIsAppStoreVersionNewer]); 105 | 106 | [_harpy testSetCurrentAppStoreVersion:@"0.0.0.9"]; 107 | XCTAssertFalse([_harpy testIsAppStoreVersionNewer]); 108 | } 109 | 110 | - (void)testQuadrupleDigitVersionUpdate { 111 | [_harpy testSetCurrentInstalledVersion:@"1.0.0.0"]; 112 | 113 | [_harpy testSetCurrentAppStoreVersion:@"2"]; 114 | XCTAssertTrue([_harpy testIsAppStoreVersionNewer]); 115 | 116 | [_harpy testSetCurrentAppStoreVersion:@"2.0"]; 117 | XCTAssertTrue([_harpy testIsAppStoreVersionNewer]); 118 | 119 | [_harpy testSetCurrentAppStoreVersion:@"2.0.0"]; 120 | XCTAssertTrue([_harpy testIsAppStoreVersionNewer]); 121 | 122 | [_harpy testSetCurrentAppStoreVersion:@"2.0.0.0"]; 123 | XCTAssertTrue([_harpy testIsAppStoreVersionNewer]); 124 | 125 | [_harpy testSetCurrentAppStoreVersion:@"0"]; 126 | XCTAssertFalse([_harpy testIsAppStoreVersionNewer]); 127 | 128 | [_harpy testSetCurrentAppStoreVersion:@"0.9"]; 129 | XCTAssertFalse([_harpy testIsAppStoreVersionNewer]); 130 | 131 | [_harpy testSetCurrentAppStoreVersion:@"0.0.9"]; 132 | XCTAssertFalse([_harpy testIsAppStoreVersionNewer]); 133 | 134 | [_harpy testSetCurrentAppStoreVersion:@"0.0.0.9"]; 135 | XCTAssertFalse([_harpy testIsAppStoreVersionNewer]); 136 | } 137 | 138 | - (void)testArabicLocalization { 139 | [_harpy setForceLanguageLocalization:HarpyLanguageArabic]; 140 | 141 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Update Available"] isEqualToString:@"التحديث متوفر"]); 142 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Next time"] isEqualToString:@"المرة التالية"]); 143 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Skip this version"] isEqualToString:@"تخطى عن هذه النسخة"]); 144 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Update"] isEqualToString:@"تجديد"]); 145 | } 146 | 147 | - (void)testArmenianLocalization { 148 | [_harpy setForceLanguageLocalization:HarpyLanguageArmenian]; 149 | 150 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Update Available"] isEqualToString:@"Թարմացումը հասանելի Է"]); 151 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Next time"] isEqualToString:@"Հաջորդ անգամ"]); 152 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Skip this version"] isEqualToString:@"Բաց թողնել այս տարբերակը"]); 153 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Update"] isEqualToString:@"Թարմացնել"]); 154 | } 155 | 156 | - (void)testBasqueLocalization { 157 | [_harpy setForceLanguageLocalization:HarpyLanguageBasque]; 158 | 159 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Update Available"] isEqualToString:@"Eguneratzea erabilgarri"]); 160 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Next time"] isEqualToString:@"Hurrengo batean"]); 161 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Skip this version"] isEqualToString:@"Bertsio honetatik jauzi egin"]); 162 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Update"] isEqualToString:@"Eguneratu"]); 163 | } 164 | 165 | - (void)testChineseSimplifiedLocalization { 166 | [_harpy setForceLanguageLocalization:HarpyLanguageChineseSimplified]; 167 | 168 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Update Available"] isEqualToString:@"更新可用"]); 169 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Next time"] isEqualToString:@"下一次"]); 170 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Skip this version"] isEqualToString:@"跳过此版本"]); 171 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Update"] isEqualToString:@"更新"]); 172 | } 173 | 174 | - (void)testChineseTraditionalLocalization { 175 | [_harpy setForceLanguageLocalization:HarpyLanguageChineseTraditional]; 176 | 177 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Update Available"] isEqualToString:@"有更新可用"]); 178 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Next time"] isEqualToString:@"下次"]); 179 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Skip this version"] isEqualToString:@"跳過此版本"]); 180 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Update"] isEqualToString:@"更新"]); 181 | } 182 | 183 | - (void)testCroationLocalization { 184 | [_harpy setForceLanguageLocalization:HarpyLanguageCroatian]; 185 | 186 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Update Available"] isEqualToString:@"Nova ažuriranje je stigla"]); 187 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Next time"] isEqualToString:@"Sljedeći put"]); 188 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Skip this version"] isEqualToString:@"Preskoči ovu verziju"]); 189 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Update"] isEqualToString:@"Ažuriraj"]); 190 | } 191 | 192 | - (void)testCzechLocalization { 193 | [_harpy setForceLanguageLocalization:HarpyLanguageCzech]; 194 | 195 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Update Available"] isEqualToString:@"Aktualizace dostupná"]); 196 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Next time"] isEqualToString:@"Příště"]); 197 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Skip this version"] isEqualToString:@"Přeskočit tuto verzi"]); 198 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Update"] isEqualToString:@"Aktualizovat"]); 199 | } 200 | 201 | - (void)testDanishLocalization { 202 | [_harpy setForceLanguageLocalization:HarpyLanguageDanish]; 203 | 204 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Update Available"] isEqualToString:@"Tilgængelig opdatering"]); 205 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Next time"] isEqualToString:@"Næste gang"]); 206 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Skip this version"] isEqualToString:@"Spring denne version over"]); 207 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Update"] isEqualToString:@"Opdater"]); 208 | } 209 | 210 | - (void)testDutchLocalization { 211 | [_harpy setForceLanguageLocalization:HarpyLanguageDutch]; 212 | 213 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Update Available"] isEqualToString:@"Update beschikbaar"]); 214 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Next time"] isEqualToString:@"Volgende keer"]); 215 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Skip this version"] isEqualToString:@"Sla deze versie over"]); 216 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Update"] isEqualToString:@"Updaten"]); 217 | } 218 | 219 | - (void)testEnglishLocalization { 220 | [_harpy setForceLanguageLocalization:HarpyLanguageEnglish]; 221 | 222 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Update Available"] isEqualToString:@"Update Available"]); 223 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Next time"] isEqualToString:@"Next time"]); 224 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Skip this version"] isEqualToString:@"Skip this version"]); 225 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Update"] isEqualToString:@"Update"]); 226 | } 227 | 228 | - (void)testEstonianLocalization { 229 | [_harpy setForceLanguageLocalization:HarpyLanguageEstonian]; 230 | 231 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Update Available"] isEqualToString:@"Uuendus saadaval"]); 232 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Next time"] isEqualToString:@"Järgmisel korral"]); 233 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Skip this version"] isEqualToString:@"Jäta see version vahele"]); 234 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Update"] isEqualToString:@"Uuenda"]); 235 | } 236 | 237 | - (void)testFinnishLocalization { 238 | [_harpy setForceLanguageLocalization:HarpyLanguageFinnish]; 239 | 240 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Update Available"] isEqualToString:@"Päivitys saatavilla"]); 241 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Next time"] isEqualToString:@"Ensi kerralla"]); 242 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Skip this version"] isEqualToString:@"Jätä tämä versio väliin"]); 243 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Update"] isEqualToString:@"Päivitys"]); 244 | } 245 | 246 | - (void)testFrenchLocalization { 247 | [_harpy setForceLanguageLocalization:HarpyLanguageFrench]; 248 | 249 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Update Available"] isEqualToString:@"Mise à jour disponible"]); 250 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Next time"] isEqualToString:@"La prochaine fois"]); 251 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Skip this version"] isEqualToString:@"Sauter cette version"]); 252 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Update"] isEqualToString:@"Mettre à jour"]); 253 | } 254 | 255 | - (void)testGermanLocalization { 256 | [_harpy setForceLanguageLocalization:HarpyLanguageGerman]; 257 | 258 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Update Available"] isEqualToString:@"Update erhältlich"]); 259 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Next time"] isEqualToString:@"Später"]); 260 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Skip this version"] isEqualToString:@"Diese Version überspringen"]); 261 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Update"] isEqualToString:@"Update"]); 262 | } 263 | 264 | - (void)testGreekLocalization { 265 | [_harpy setForceLanguageLocalization:HarpyLanguageGreek]; 266 | 267 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Update Available"] isEqualToString:@"Διαθέσιμη Ενημέρωση"]); 268 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Next time"] isEqualToString:@"Άλλη φορά"]); 269 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Skip this version"] isEqualToString:@"Αγνόησε αυτήν την έκδοση"]); 270 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Update"] isEqualToString:@"Αναβάθμιση"]); 271 | } 272 | 273 | - (void)testHebrewLocalization { 274 | [_harpy setForceLanguageLocalization:HarpyLanguageHebrew]; 275 | 276 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Update Available"] isEqualToString:@"עדכון זמין"]); 277 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Next time"] isEqualToString:@"בפעם הבאה"]); 278 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Skip this version"] isEqualToString:@"דלג על גרסה זו"]); 279 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Update"] isEqualToString:@"עדכן"]); 280 | } 281 | 282 | - (void)testHungarianLocalization { 283 | [_harpy setForceLanguageLocalization:HarpyLanguageHungarian]; 284 | 285 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Update Available"] isEqualToString:@"Új frissítés érhető el"]); 286 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Next time"] isEqualToString:@"Később"]); 287 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Skip this version"] isEqualToString:@"Ennél a verziónál ne figyelmeztessen"]); 288 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Update"] isEqualToString:@"Frissítés"]); 289 | } 290 | 291 | - (void)testIndoneisanLocalization { 292 | [_harpy setForceLanguageLocalization:HarpyLanguageIndonesian]; 293 | 294 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Update Available"] isEqualToString:@"Pembaruan Tersedia"]); 295 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Next time"] isEqualToString:@"Lain kali"]); 296 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Skip this version"] isEqualToString:@"Lewati versi ini"]); 297 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Update"] isEqualToString:@"Perbarui"]); 298 | } 299 | 300 | - (void)testItalianLocalization { 301 | [_harpy setForceLanguageLocalization:HarpyLanguageItalian]; 302 | 303 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Update Available"] isEqualToString:@"Aggiornamento disponibile"]); 304 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Next time"] isEqualToString:@"La prossima volta"]); 305 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Skip this version"] isEqualToString:@"Salta questa versione"]); 306 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Update"] isEqualToString:@"Aggiorna"]); 307 | } 308 | 309 | - (void)testJapaneseLocalization { 310 | [_harpy setForceLanguageLocalization:HarpyLanguageJapanese]; 311 | 312 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Update Available"] isEqualToString:@"アップデートのお知らせ"]); 313 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Next time"] isEqualToString:@"次回"]); 314 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Skip this version"] isEqualToString:@"このバージョンをスキップ"]); 315 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Update"] isEqualToString:@"アップデート"]); 316 | } 317 | 318 | - (void)testKoreanLocalization { 319 | [_harpy setForceLanguageLocalization:HarpyLanguageKorean]; 320 | 321 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Update Available"] isEqualToString:@"업데이트 가능"]); 322 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Next time"] isEqualToString:@"다음에"]); 323 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Skip this version"] isEqualToString:@"이 버전 건너뜀"]); 324 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Update"] isEqualToString:@"업데이트"]); 325 | } 326 | 327 | - (void)testLatvianLocalization { 328 | [_harpy setForceLanguageLocalization:HarpyLanguageLatvian]; 329 | 330 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Update Available"] isEqualToString:@"Atjauninājums pieejams"]); 331 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Next time"] isEqualToString:@"Nākamreiz"]); 332 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Skip this version"] isEqualToString:@"Izlaist šo versiju"]); 333 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Update"] isEqualToString:@"Atjaunināt"]); 334 | } 335 | 336 | - (void)testLithuanianLocalization { 337 | [_harpy setForceLanguageLocalization:HarpyLanguageLithuanian]; 338 | 339 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Update Available"] isEqualToString:@"Atnaujinimas"]); 340 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Next time"] isEqualToString:@"Kitą kartą"]); 341 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Skip this version"] isEqualToString:@"Praleisti šią versiją"]); 342 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Update"] isEqualToString:@"Atnaujinti"]); 343 | } 344 | 345 | - (void)testMalayLocalization { 346 | [_harpy setForceLanguageLocalization:HarpyLanguageMalay]; 347 | 348 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Update Available"] isEqualToString:@"Versi Terkini"]); 349 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Next time"] isEqualToString:@"Lain kali"]); 350 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Skip this version"] isEqualToString:@"Langkau versi ini"]); 351 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Update"] isEqualToString:@"Muat turun"]); 352 | } 353 | 354 | - (void)testNorwegianLocalization { 355 | [_harpy setForceLanguageLocalization:HarpyLanguageNorwegian]; 356 | 357 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Update Available"] isEqualToString:@"Oppdatering tilgjengelig"]); 358 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Next time"] isEqualToString:@"Neste gang"]); 359 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Skip this version"] isEqualToString:@"Hopp over denne versjonen"]); 360 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Update"] isEqualToString:@"Oppdater"]); 361 | } 362 | 363 | - (void)testPersianLocalization { 364 | [_harpy setForceLanguageLocalization:HarpyLanguagePersian]; 365 | 366 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Update Available"] isEqualToString:@"بروزرسانی در دسترس"]); 367 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Next time"] isEqualToString:@"دفعه بعد"]); 368 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Skip this version"] isEqualToString:@"رد این نسخه"]); 369 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Update"] isEqualToString:@"بروزرسانی"]); 370 | } 371 | 372 | - (void)testPersianAfghanistanLocalization { 373 | [_harpy setForceLanguageLocalization:HarpyLanguagePersianAfghanistan]; 374 | 375 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Update Available"] isEqualToString:@"بروزرسانی در دسترس"]); 376 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Next time"] isEqualToString:@"دگر بار"]); 377 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Skip this version"] isEqualToString:@"رد این نسخه"]); 378 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Update"] isEqualToString:@"بروزرسانی"]); 379 | } 380 | 381 | - (void)testPersianIranLocalization { 382 | [_harpy setForceLanguageLocalization:HarpyLanguagePersianIran]; 383 | 384 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Update Available"] isEqualToString:@"بروزرسانی در دسترس"]); 385 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Next time"] isEqualToString:@"دفعه بعد"]); 386 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Skip this version"] isEqualToString:@"رد این نسخه"]); 387 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Update"] isEqualToString:@"بروزرسانی"]); 388 | } 389 | 390 | - (void)testPolishLocalization { 391 | [_harpy setForceLanguageLocalization:HarpyLanguagePolish]; 392 | 393 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Update Available"] isEqualToString:@"Aktualizacja dostępna"]); 394 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Next time"] isEqualToString:@"Następnym razem"]); 395 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Skip this version"] isEqualToString:@"Pomiń wersję"]); 396 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Update"] isEqualToString:@"Zaktualizuj"]); 397 | } 398 | 399 | - (void)testPortugueseBrazilLocalization { 400 | [_harpy setForceLanguageLocalization:HarpyLanguagePortugueseBrazil]; 401 | 402 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Update Available"] isEqualToString:@"Atualização disponível"]); 403 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Next time"] isEqualToString:@"Próxima vez"]); 404 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Skip this version"] isEqualToString:@"Ignorar esta versão"]); 405 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Update"] isEqualToString:@"Atualizar"]); 406 | } 407 | 408 | - (void)testPortuguesePortugalLocalization { 409 | [_harpy setForceLanguageLocalization:HarpyLanguagePortuguesePortugal]; 410 | 411 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Update Available"] isEqualToString:@"Nova actualização disponível"]); 412 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Next time"] isEqualToString:@"Próxima vez"]); 413 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Skip this version"] isEqualToString:@"Ignorar esta versão"]); 414 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Update"] isEqualToString:@"Actualizar"]); 415 | } 416 | 417 | - (void)testRussianLocalization { 418 | [_harpy setForceLanguageLocalization:HarpyLanguageRussian]; 419 | 420 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Update Available"] isEqualToString:@"Доступно обновление"]); 421 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Next time"] isEqualToString:@"В следующий раз"]); 422 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Skip this version"] isEqualToString:@"Пропустить эту версию"]); 423 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Update"] isEqualToString:@"Обновить"]); 424 | } 425 | 426 | - (void)testSerbianCyrillicLocalization { 427 | [_harpy setForceLanguageLocalization:HarpyLanguageSerbianCyrillic]; 428 | 429 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Update Available"] isEqualToString:@"Ажурирање доступно"]); 430 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Next time"] isEqualToString:@"Следећи пут"]); 431 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Skip this version"] isEqualToString:@"Прескочи ову верзију"]); 432 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Update"] isEqualToString:@"Ажурирај"]); 433 | } 434 | 435 | - (void)testSerbianLatinLocalization { 436 | [_harpy setForceLanguageLocalization:HarpyLanguageSerbianLatin]; 437 | 438 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Update Available"] isEqualToString:@"Ažuriranje dostupno"]); 439 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Next time"] isEqualToString:@"Sledeći put"]); 440 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Skip this version"] isEqualToString:@"Preskoči ovu verziju"]); 441 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Update"] isEqualToString:@"Ažuriraj"]); 442 | } 443 | 444 | - (void)testSlovenianLocalization { 445 | [_harpy setForceLanguageLocalization:HarpyLanguageSlovenian]; 446 | 447 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Update Available"] isEqualToString:@"Posodobitev aplikacije"]); 448 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Next time"] isEqualToString:@"Naslednjič"]); 449 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Skip this version"] isEqualToString:@"Ne želim"]); 450 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Update"] isEqualToString:@"Namesti"]); 451 | } 452 | 453 | - (void)testSpanishLocalization { 454 | [_harpy setForceLanguageLocalization:HarpyLanguageSpanish]; 455 | 456 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Update Available"] isEqualToString:@"Actualización disponible"]); 457 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Next time"] isEqualToString:@"La próxima vez"]); 458 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Skip this version"] isEqualToString:@"Saltar esta versión"]); 459 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Update"] isEqualToString:@"Actualizar"]); 460 | } 461 | 462 | - (void)testSwedishLocalization { 463 | [_harpy setForceLanguageLocalization:HarpyLanguageSwedish]; 464 | 465 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Update Available"] isEqualToString:@"Tillgänglig uppdatering"]); 466 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Next time"] isEqualToString:@"Nästa gång"]); 467 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Skip this version"] isEqualToString:@"Hoppa över den här versionen"]); 468 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Update"] isEqualToString:@"Uppdatera"]); 469 | } 470 | 471 | - (void)testThaiLocalization { 472 | [_harpy setForceLanguageLocalization:HarpyLanguageThai]; 473 | 474 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Update Available"] isEqualToString:@"มีการอัพเดท"]); 475 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Next time"] isEqualToString:@"ไว้คราวหน้า"]); 476 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Skip this version"] isEqualToString:@"ข้ามเวอร์ชั่นนี้"]); 477 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Update"] isEqualToString:@"อัพเดท"]); 478 | } 479 | 480 | - (void)testTurkishLocalization { 481 | [_harpy setForceLanguageLocalization:HarpyLanguageTurkish]; 482 | 483 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Update Available"] isEqualToString:@"Güncelleme Mevcut"]); 484 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Next time"] isEqualToString:@"Daha sonra"]); 485 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Skip this version"] isEqualToString:@"Boşver"]); 486 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Update"] isEqualToString:@"Güncelle"]); 487 | } 488 | 489 | - (void)testUkrainianLocalization { 490 | [_harpy setForceLanguageLocalization:HarpyLanguageUkrainian]; 491 | 492 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Update Available"] isEqualToString:@"Доступне Оновлення"]); 493 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Next time"] isEqualToString:@"Наступного разу"]); 494 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Skip this version"] isEqualToString:@"Пропустити версію"]); 495 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Update"] isEqualToString:@"Оновити"]); 496 | } 497 | 498 | - (void)testUrduLocalization { 499 | [_harpy setForceLanguageLocalization:HarpyLanguageUrdu]; 500 | 501 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Update Available"] isEqualToString:@"نیا اپڈیٹ"]); 502 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Next time"] isEqualToString:@"اگلی مرتبہ"]); 503 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Skip this version"] isEqualToString:@"اس ورزن کو چھوڑ دیں"]); 504 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Update"] isEqualToString:@"اپڈیٹ کریں"]); 505 | } 506 | 507 | - (void)testVietnameseLocalization { 508 | [_harpy setForceLanguageLocalization:HarpyLanguageVietnamese]; 509 | 510 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Update Available"] isEqualToString:@"Cập nhật mới"]); 511 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Next time"] isEqualToString:@"Lần tới"]); 512 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Skip this version"] isEqualToString:@"Bỏ qua phiên bản này"]); 513 | XCTAssertTrue([[_harpy testLocalizedStringForKey:@"Update"] isEqualToString:@"Cập nhật"]); 514 | } 515 | 516 | @end 517 | -------------------------------------------------------------------------------- /HarpyExample/HarpyExampleTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ====== 2 | Before posting an issue, please make sure your issue has not already been resolved or answered elsehwere. 3 | 4 | Common Issue #1: 5 | > "Error retrieving iOS version number as there was no data returned." 6 | 7 | Check if your app is available in the US App Store, otherwise add the corresponding country code when setting up Siren: https://github.com/ArtSabintsev/Siren/blob/master/Siren/Siren.swift#L198 8 | 9 | Common Issue #2: 10 | > "Support for macOS App Store." 11 | 12 | Siren does not and will not support the macOS App Store. 13 | 14 | Common Issue #3: 15 | > "Support for prompting TestFlight users to update to the newest beta build." 16 | 17 | Siren does not support this functionality. There is no publicly accessible TestFlight API akin to that of the public App Store API that Siren can utilize to provide this functionality. 18 | 19 | Please delete this text before submitting a new issue. 20 | 21 | Common Issue #4: 22 | > "Infinite Looping of App Store Prompt" 23 | 24 | If you use the `.force` update option and your app prompts the user to download the latest version from the App Store, and the latest verison happens to be unavailable, an infinite loop will occur. This is undesirable UX. To address this issue, simple set `setShowAlertAfterCurrentVersionHasBeenReleasedForDays` to a value of 1-3 days. By default, this value is set to `1` day to avoid this exact issue. 25 | 26 | ====== 27 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2012-2015 Arthur Ariel Sabintsev 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # After 6 years of Harpy and 4 years of [Siren](https://github.com/ArtSabintsev/Siren), I have decided to deprecate Harpy in favor of Siren. Why? Siren is written in Swift and has a feature set that is superior to that of Harpy. [More information can be found on this Medium post](https://link.medium.com/jE1FGj1DYS). The repository will live on, however, there will be no further updates and no transfers of the codebase at this time. 2 | 3 | --- 4 | 5 | # Harpy 6 | ### Notify users when a new version of your app is available and prompt them to upgrade. 7 | 8 | [![Build Status](https://travis-ci.org/ArtSabintsev/Harpy.svg?branch=master)](https://travis-ci.org/ArtSabintsev/Harpy) 9 | 10 | [![CocoaPods](https://img.shields.io/cocoapods/v/Harpy.svg)](https://cocoapods.org/pods/Harpy) [![Carthage Compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) [![CocoaPods](https://img.shields.io/cocoapods/dt/Harpy.svg)](https://cocoapods.org/pods/Harpy) 11 | 12 | --- 13 | 14 | ## About 15 | **Harpy** checks a user's currently installed version of your iOS app against the version that is currently available in the App Store. 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. 16 | 17 | Harpy is built to work with the [Semantic Versioning](http://www.semver.org) system. 18 | - Semantic Versioning is a three number versioning system (e.g., 1.0.0) 19 | - Harpy also supports two-number versioning (e.g., 1.0) 20 | - Harpy also supports four-number versioning (e.g., 1.0.0.0) 21 | - Harpy only works with numbers. 22 | 23 | ## Swift Support 24 | Harpy was ported to Swift by myself and [**Aaron Brager**](http://twitter.com/GetAaron). We've called the new project [**Siren**](https://github.com/ArtSabintsev/Siren). 25 | 26 | ## Features 27 | - [x] CocoaPods Support 28 | - [x] Carthage Support 29 | - [x] Localized for 40+ languages (See **Localization**) 30 | - [x] Pre-Update Device Compatibility Check (See **Device Compatibility**) 31 | - [x] Three types of alerts (see **Screenshots & Alert Types**) 32 | - [x] Optional delegate methods (see **Optional Delegate** section) 33 | - [x] Unit Tests! 34 | 35 | ## Screenshots 36 | 37 | - The **left picture** forces the user to update the app. 38 | - The **center picture** gives the user the option to update the app. 39 | - The **right picture** gives the user the option to skip the current update. 40 | - These options are controlled by the `HarpyAlertType` enum that is found in `Harpy.h`. 41 | 42 | 43 | 44 | ## Installation Instructions 45 | 46 | ### CocoaPods 47 | ``` ruby 48 | pod 'Harpy' 49 | ``` 50 | 51 | ### Carthage 52 | ``` swift 53 | github "ArtSabintsev/Harpy" 54 | ``` 55 | 56 | ### Manual 57 | 58 | Copy the 'Harpy' folder into your Xcode project. It contains the Harpy.h and Harpy.m files. 59 | 60 | ## Setup 61 | 1. Import **Harpy.h** into your AppDelegate or Pre-Compiler Header (.pch) 62 | 1. In your `AppDelegate`, optionally set the **alertType**. 63 | 1. In your `AppDelegate`, call **only one** of the `checkVersion` methods, as all three perform a check on your application's first launch. Use either: 64 | - `checkVersion` in `application:didFinishLaunchingWithOptions:` 65 | - `checkVersionDaily` in `applicationDidBecomeActive:`. 66 | - `checkVersionWeekly` in `applicationDidBecomeActive:`. 67 | 68 | 69 | ``` obj-c 70 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 71 | 72 | // Present Window before calling Harpy 73 | [self.window makeKeyAndVisible]; 74 | 75 | // Set the UIViewController that will present an instance of UIAlertController 76 | [[Harpy sharedInstance] setPresentingViewController:_window.rootViewController]; 77 | 78 | // (Optional) Set the Delegate to track what a user clicked on, or to use a custom UI to present your message. 79 | [[Harpy sharedInstance] setDelegate:self]; 80 | 81 | // (Optional) When this is set, the alert will only show up if the current version has already been released for X days. 82 | // By default, this value is set to 1 (day) to avoid an issue where Apple updates the JSON faster than the app binary propogates to the App Store. 83 | [[Harpy sharedInstance] setShowAlertAfterCurrentVersionHasBeenReleasedForDays:3]; 84 | 85 | // (Optional) The tintColor for the alertController 86 | [[Harpy sharedInstance] setAlertControllerTintColor:@"<#alert_controller_tint_color#>"]; 87 | 88 | // (Optional) Set the App Name for your app 89 | [[Harpy sharedInstance] setAppName:@"<#app_name#>"]; 90 | 91 | /* (Optional) Set the Alert Type for your app 92 | By default, Harpy is configured to use HarpyAlertTypeOption */ 93 | [[Harpy sharedInstance] setAlertType:<#alert_type#>]; 94 | 95 | /* (Optional) If your application is not available in the U.S. App Store, you must specify the two-letter 96 | country code for the region in which your applicaiton is available. */ 97 | [[Harpy sharedInstance] setCountryCode:@"<#country_code#>"]; 98 | 99 | /* (Optional) Overrides system language to predefined language. 100 | Please use the HarpyLanguage constants defined in Harpy.h. */ 101 | [[Harpy sharedInstance] setForceLanguageLocalization:<#HarpyLanguageConstant#>]; 102 | 103 | /* (Optional): Delays the update prompt by a specific number of days. By default, 104 | this value is set to 1 day to avoid an issue where Apple updates the JSON faster than the app binary propogates to the App Store.*/ 105 | [[Harpy sharedInstance] setShowAlertAfterCurrentVersionHasBeenReleasedForDays:<#Int#>]; 106 | 107 | // Perform check for new version of your app 108 | [[Harpy sharedInstance] checkVersion]; 109 | } 110 | 111 | - (void)applicationDidBecomeActive:(UIApplication *)application { 112 | 113 | /* 114 | Perform daily check for new version of your app 115 | Useful if user returns to you app from background after extended period of time 116 | Place in applicationDidBecomeActive: 117 | 118 | Also, performs version check on first launch. 119 | */ 120 | [[Harpy sharedInstance] checkVersionDaily]; 121 | 122 | /* 123 | Perform weekly check for new version of your app 124 | Useful if you user returns to your app from background after extended period of time 125 | Place in applicationDidBecomeActive: 126 | 127 | Also, performs version check on first launch. 128 | */ 129 | [[Harpy sharedInstance] checkVersionWeekly]; 130 | 131 | } 132 | 133 | - (void)applicationWillEnterForeground:(UIApplication *)application { 134 | /* 135 | Perform check for new version of your app 136 | Useful if user returns to you app from background after being sent tot he App Store, 137 | but doesn't update their app before coming back to your app. 138 | 139 | ONLY USE THIS IF YOU ARE USING *HarpyAlertTypeForce* 140 | 141 | Also, performs version check on first launch. 142 | */ 143 | [[Harpy sharedInstance] checkVersion]; 144 | } 145 | 146 | ``` 147 | 148 | And you're all set! 149 | 150 | ## Differentiated Alerts for Patch, Minor, and Major Updates 151 | 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 any of the `checkVersion` methods: 152 | 153 | ``` obj-c 154 | /* By default, Harpy is configured to use HarpyAlertTypeOption for all version updates */ 155 | [[Harpy sharedInstance] setPatchUpdateAlertType:<#alert_type#>]; 156 | [[Harpy sharedInstance] setMinorUpdateAlertType:<#alert_type#>]; 157 | [[Harpy sharedInstance] setMajorUpdateAlertType:<#alert_type#>]; 158 | [[Harpy sharedInstance] setRevisionUpdateAlertType:<#alert_type#>]; 159 | ``` 160 | 161 | ## Optional Delegate and Delegate Methods 162 | If you'd like to handle or track the end-user's behavior, four delegate methods have been made available to you: 163 | 164 | ``` obj-c 165 | // User presented with update dialog 166 | - (void)harpyDidShowUpdateDialog; 167 | 168 | // User did click on button that launched App Store.app 169 | - (void)harpyUserDidLaunchAppStore; 170 | 171 | // User did click on button that skips version update 172 | - (void)harpyUserDidSkipVersion; 173 | 174 | // User did click on button that cancels update dialog 175 | - (void)harpyUserDidCancel; 176 | ``` 177 | 178 | If you would like to use your own UI, please use the following delegate method to obtain the localized update message if a new version is available: 179 | 180 | ``` obj-c 181 | - (void)harpyDidDetectNewVersionWithoutAlert:(NSString *)message; 182 | ``` 183 | 184 | ## Localization 185 | Harpy is localized for 186 | - Arabic 187 | - Armenian 188 | - Basque 189 | - Chinese (Simplified and Traditional) 190 | - Croatian 191 | - Czech 192 | - Danish 193 | - Dutch 194 | - English 195 | - Estonian 196 | - Finnish 197 | - French 198 | - German 199 | - Greek 200 | - Hebrew 201 | - Hungarian 202 | - Indonesian 203 | - Italian 204 | - Japanese 205 | - Korean 206 | - Latvian 207 | - Lithuanian 208 | - Malay 209 | - Norwegian (Bokmål) 210 | - Persian (Iran, Afghanistan, Persian) 211 | - Polish 212 | - Portuguese (Brazil and Portugal) 213 | - Russian 214 | - Serbian (Cyrillic and Latin) 215 | - Slovenian 216 | - Spanish 217 | - Swedish 218 | - Thai 219 | - Turkish 220 | - Ukrainian 221 | - Urdu 222 | - Vietnamese 223 | 224 | 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). 225 | 226 | You can enable it like this: 227 | 228 | ``` obj-c 229 | [[Harpy sharedInstance] setForceLanguageLocalization<#HarpyLanguageConstant#>]; 230 | ``` 231 | 232 | ## Device Compatibility 233 | If an app update is available, Harpy checks to make sure that the version of iOS on the user's device is compatible the one that is required by the app update. For example, if a user has iOS 9 installed on their device, but the app update requires iOS 10, an alert will not be shown. This takes care of the *false positive* case regarding app updating. 234 | 235 | ## Testing Harpy 236 | 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. 237 | 238 | 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/us/app/itunes-connect/id376771144?mt=8) app's bundleID: `com.apple.itunesconnect.mobile`. 239 | 240 | ## Important Note on App Store Submissions 241 | The App Store reviewer will **not** see the alert. 242 | 243 | ## Phased Releases 244 | 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). Harpy 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 will need to remotely disable Harpy until the rollout is done. 245 | 246 | ## Created and maintained by 247 | [Arthur Ariel Sabintsev](http://www.sabintsev.com/) 248 | --------------------------------------------------------------------------------