Überprüfen Sie, ob meine App eine neue Version im AppStore hat

111

Ich möchte manuell prüfen, ob neue Updates für meine App vorhanden sind, während sich der Benutzer darin befindet, und ihn auffordern, die neue Version herunterzuladen. Kann ich dies tun, indem ich die Version meiner App im App Store programmgesteuert überprüfe?

user542584
quelle
6
Sie können eine zufällige Seite auf einen Webserver legen, die nur eine Zeichenfolgendarstellung der neuesten Version zurückgibt. Laden Sie es herunter und vergleichen Sie es beim Start der App und benachrichtigen Sie den Benutzer. (Schneller und einfacher Weg)
LouwHopley
1
Vielen Dank, aber ich hatte auf eine bessere Lösung wie eine API gehofft, mit der ich die App Store-Funktionen aufrufen kann, z. B. nach meiner App-Nummer suchen und die Versionsdaten abrufen kann. Spart Zeit, um einen Webserver nur für diesen Zweck zu warten, aber trotzdem danke für den Zeiger!
user542584
Ich mache das Gleiche wie der erste Kommentar. Ich habe eine Liste mit einem Eintrag geschrieben: einer NSNumberVersionsnummer. Dann habe ich es auf meine Website hochgeladen. Dieselbe Website, die ich für meinen App-Support und meine App-Webseiten verwende. Anschließend viewDidLoadüberprüfe ich auf der Website die Versionsnummer und die aktuelle Version in meiner App. Dann habe ich eine vorgefertigte alertView, die automatisch auffordert, die App zu aktualisieren. Ich kann Code bereitstellen, wenn Sie möchten.
Andrew
danke, ich denke ich sollte das auch versuchen ..
user542584

Antworten:

88

Hier ist ein einfaches Code-Snippet, mit dem Sie feststellen können, ob die aktuelle Version anders ist

-(BOOL) needsUpdate{
    NSDictionary* infoDictionary = [[NSBundle mainBundle] infoDictionary];
    NSString* appID = infoDictionary[@"CFBundleIdentifier"];
    NSURL* url = [NSURL URLWithString:[NSString stringWithFormat:@"http://itunes.apple.com/lookup?bundleId=%@", appID]];
    NSData* data = [NSData dataWithContentsOfURL:url];
    NSDictionary* lookup = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];

    if ([lookup[@"resultCount"] integerValue] == 1){
        NSString* appStoreVersion = lookup[@"results"][0][@"version"];
        NSString* currentVersion = infoDictionary[@"CFBundleShortVersionString"];
        if (![appStoreVersion isEqualToString:currentVersion]){
            NSLog(@"Need to update [%@ != %@]", appStoreVersion, currentVersion);
            return YES;
        }
    }
    return NO;
}

Hinweis: Stellen Sie sicher, dass diese bei der Eingabe der neuen Version in iTunes mit der Version in der App übereinstimmt, die Sie veröffentlichen. Wenn nicht, gibt der obige Code immer JA zurück, unabhängig davon, ob der Benutzer aktualisiert.

datinc
quelle
4
Super Lösung, die ich je gefunden habe +1
Sanjay Changani
1
@MobeenAfzal, ich denke du hast die Frage und Lösung nicht verstanden. Die obige Lösung vergleicht die aktuelle Version mit der Version im Store. Wenn sie nicht übereinstimmen, wird JA erneut abgestimmt, andernfalls wird NEIN zurückgegeben. Unabhängig vom Verlauf im App Store gibt die obige Methode JA zurück, wenn sich die aktuelle Version von der App Store-Version unterscheidet. Sobald der Benutzer aktualisiert hat, entspricht die aktuelle Version der App Store-Version. Die obige Methode sollte immer JA zurückgeben, wenn die Benutzerversion 1.0 und die App Store-Version 1.2 ist.
Datum
1
@MobeenAfzal Ich glaube ich verstehe was du siehst. Im Code ist Ihre Version 1.7, aber in iTunes haben Sie die Version als 1.6 hochgeladen, damit Ihre Benutzer nicht wissen, dass Sie eine Version übersprungen haben. Ist das der Fall? Wenn ja, dann ... benötigen Sie einen Server (DropBox würde dies tun), um die Versionsnummer Ihrer Apps bereitzustellen und Ihren Code für den Zugriff auf diesen Endpunkt zu ändern. Lassen Sie mich wissen, ob dies das ist, was Sie sehen, und ich werde dem Beitrag einen Warnhinweis hinzufügen.
Datum
1
@MobeenAfzal Sie Ihr Kommentar ist irreführend. Wenn die Version auf dem Gerät des Benutzers durch eine Version von der Version im Appstore getrennt ist, gibt der Code wie erwartet JA zurück. Selbst wenn Sie Version 1.0 gefolgt von Version 1.111 veröffentlichen, würde es immer noch perfekt funktionieren.
Datum
1
Das Update sollte nur angezeigt werden, wenn die Appstore-Version wie folgt größer als die aktuelle Version ist. if ([appStoreVersion vergleiche: aktuelleVersionsoptionen: NSNumericSearch] == NSOrderedDescending) {NSLog (@ "\ n \ nNach der Aktualisierung. Appstore-Version% @ ist größer als% @", appStoreVersion, currentVersion); }
Nitesh Borad
52

Swift 3 Version:

func isUpdateAvailable() throws -> Bool {
    guard let info = Bundle.main.infoDictionary,
        let currentVersion = info["CFBundleShortVersionString"] as? String,
        let identifier = info["CFBundleIdentifier"] as? String,
        let url = URL(string: "http://itunes.apple.com/lookup?bundleId=\(identifier)") else {
        throw VersionError.invalidBundleInfo
    }
    let data = try Data(contentsOf: url)
    guard let json = try JSONSerialization.jsonObject(with: data, options: [.allowFragments]) as? [String: Any] else {
        throw VersionError.invalidResponse
    }
    if let result = (json["results"] as? [Any])?.first as? [String: Any], let version = result["version"] as? String {
        return version != currentVersion
    }
    throw VersionError.invalidResponse
}

Ich denke, es ist besser, einen Fehler auszulösen, als false zurückzugeben. In diesem Fall habe ich einen VersionError erstellt, aber es kann sich um einen anderen von Ihnen definierten oder NSError handeln

enum VersionError: Error {
    case invalidResponse, invalidBundleInfo
}

Ziehen Sie auch in Betracht, diese Funktion von einem anderen Thread aus aufzurufen. Wenn die Verbindung langsam ist, kann der aktuelle Thread blockiert werden.

DispatchQueue.global().async {
    do {
        let update = try self.isUpdateAvailable()
        DispatchQueue.main.async {
            // show alert
        }
    } catch {
        print(error)
    }
}

Aktualisieren

Verwenden von URLSession:

Anstatt Data(contentsOf: url)einen Thread zu verwenden und zu blockieren, können wir Folgendes verwenden URLSession:

func isUpdateAvailable(completion: @escaping (Bool?, Error?) -> Void) throws -> URLSessionDataTask {
    guard let info = Bundle.main.infoDictionary,
        let currentVersion = info["CFBundleShortVersionString"] as? String,
        let identifier = info["CFBundleIdentifier"] as? String,
        let url = URL(string: "http://itunes.apple.com/lookup?bundleId=\(identifier)") else {
            throw VersionError.invalidBundleInfo
    }
    Log.debug(currentVersion)
    let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
        do {
            if let error = error { throw error }
            guard let data = data else { throw VersionError.invalidResponse }
            let json = try JSONSerialization.jsonObject(with: data, options: [.allowFragments]) as? [String: Any]
            guard let result = (json?["results"] as? [Any])?.first as? [String: Any], let version = result["version"] as? String else {
                throw VersionError.invalidResponse
            }
            completion(version != currentVersion, nil)
        } catch {
            completion(nil, error)
        }
    }
    task.resume()
    return task
}

Beispiel:

_ = try? isUpdateAvailable { (update, error) in
    if let error = error {
        print(error)
    } else if let update = update {
        print(update)
    }
}
Juanjo
quelle
1
Diese Antwort macht ihre Anfrage synchron. Dies bedeutet, dass bei einer schlechten Verbindung Ihre App für Minuten unbrauchbar sein kann, bis die Anforderung zurückkehrt.
uliwitness
4
Ich bin anderer Meinung, DispatchQueue.global()gibt Ihnen eine Hintergrundwarteschlange, die Daten werden in diese Warteschlange geladen und kehren erst dann in die Hauptwarteschlange zurück, wenn die Daten geladen werden.
Juanjo
Hoppla. Irgendwie habe ich diesen zweiten Code-Ausschnitt übersehen. Leider kann ich das Downvote erst entfernen, wenn Ihre Antwort erneut bearbeitet wurde :-( Übrigens - Gegeben dataWithContentsOfURL: Durchläuft tatsächlich die synchronen Aufrufe von NSURLConnection, die wiederum nur einen asynchronen Thread starten und blockieren. Dies wäre wahrscheinlich weniger Aufwand um nur die asynchronen NSURLSession-Aufrufe zu verwenden. Sie würden Sie sogar im Haupt-Thread
zurückrufen,
@juanjo ,,,, funktioniert nicht für Swift 3.0.1, bitte können Sie aktualisiert für Swift hochladen?
Kiran Jadhav
2
Hinweis, wenn Sie nur in einem bestimmten Geschäft aufgeführt sind Ich habe festgestellt, dass Sie einen Ländercode zur URL hinzufügen müssen - zB GB itunes.apple.com/(countryCode)/… )
Ryan Heitner
13

Vielen Dank an Steve Moser für seinen Link, hier ist mein Code:

NSString *appInfoUrl = @"http://itunes.apple.com/en/lookup?bundleId=XXXXXXXXX";

NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
[request setURL:[NSURL URLWithString:appInfoUrl]];
[request setHTTPMethod:@"GET"];

NSURLResponse *response;
NSError *error;
NSData *data = [NSURLConnection  sendSynchronousRequest:request returningResponse: &response error: &error];
NSString *output = [NSString stringWithCString:[data bytes] length:[data length]];

NSError *e = nil;
NSData *jsonData = [output dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary *jsonDict = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers error: &e];

NSString *version = [[[jsonDict objectForKey:@"results"] objectAtIndex:0] objectForKey:@"version"];
Roozbeh Zabihollahi
quelle
1
Sehr gute und korrekte Lösung, nur ein kleines Update bezüglich der URL ist itunes.apple.com/de/lookup?bundleId=xxxxxxxxxx
SJ
Vielen Dank, Ihr Kommentar angewendet
Roozbeh Zabihollahi
4
Eigentlich hat es bei mir mit dem /en/Unterpfad nicht geklappt. Nach dem Entfernen funktionierte es
Gasparuff
Diese Antwort macht ihre Anfrage synchron. Dies bedeutet, dass bei einer schlechten Verbindung Ihre App für Minuten unbrauchbar sein kann, bis die Anforderung zurückkehrt.
uliwitness
1
Ich musste mit dem / en / itunes.apple.com/lookup?bundleId=xxxxxxx verwenden , danke @gasparuff
Fernando Perez
13

Da ich vor dem gleichen Problem stand, fand ich die Antwort von Mario Hendricks . Unglücklicherweise beschwerte sich XCode über Casting-Probleme, als ich versuchte, seinen Code auf mein Projekt anzuwenden, und sagte: "MDLMaterialProperty hat keine tiefgestellten Mitglieder." Sein Code versuchte, dieses MDLMaterial ... als Typ der Konstante "lookupResult" festzulegen, wodurch das Casting auf "Int" jedes Mal fehlschlug. Meine Lösung bestand darin, NSDictionary eine Typanmerkung für meine Variable bereitzustellen , um klar zu machen , welche Art von Wert ich benötigte. Damit konnte ich auf den Wert "Version" zugreifen, den ich brauchte.

Obs: Für diese YOURBUNDLEID können Sie aus Ihrem Xcode-Projekt ... " Ziele> Allgemein> Identität> Bundle-ID "

Hier ist also mein Code mit einigen Vereinfachungen:

  func appUpdateAvailable() -> Bool
{
    let storeInfoURL: String = "http://itunes.apple.com/lookup?bundleId=YOURBUNDLEID"
    var upgradeAvailable = false
    // Get the main bundle of the app so that we can determine the app's version number
    let bundle = NSBundle.mainBundle()
    if let infoDictionary = bundle.infoDictionary {
        // The URL for this app on the iTunes store uses the Apple ID for the  This never changes, so it is a constant
        let urlOnAppStore = NSURL(string: storeInfoURL)
        if let dataInJSON = NSData(contentsOfURL: urlOnAppStore!) {
            // Try to deserialize the JSON that we got
            if let dict: NSDictionary = try? NSJSONSerialization.JSONObjectWithData(dataInJSON, options: NSJSONReadingOptions.AllowFragments) as! [String: AnyObject] {
                if let results:NSArray = dict["results"] as? NSArray {
                    if let version = results[0].valueForKey("version") as? String {
                        // Get the version number of the current version installed on device
                        if let currentVersion = infoDictionary["CFBundleShortVersionString"] as? String {
                            // Check if they are the same. If not, an upgrade is available.
                            print("\(version)")
                            if version != currentVersion {
                                upgradeAvailable = true
                            }
                        }
                    }
                }
            }
        }
    }
    return upgradeAvailable
}

Alle Vorschläge zur Verbesserung dieses Codes sind willkommen!

Yago Zardo
quelle
Diese Antwort macht ihre Anfrage synchron. Dies bedeutet, dass bei einer schlechten Verbindung Ihre App für Minuten unbrauchbar sein kann, bis die Anforderung zurückkehrt.
uliwitness
@ Yago Zardo bitte verwenden Sie die Vergleichsfunktion andernfalls, wenn Benutzer app.apple getestet Zeitanzeige Update Alertview oder Apple Ihre App ablehnen
Jigar Darji
Hey @Jigar, danke für den Rat. Ich verwende diese Methode derzeit nicht mehr in meiner App, da wir jetzt alles auf unserem Server versionieren. Könnten Sie besser erklären, was Sie gesagt haben? Ich habe es nicht verstanden und es sieht wirklich gut aus zu wissen. Danke im Voraus.
Yago Zardo
Vielen Dank an @uliwitness für den Tipp. Es hat mir wirklich geholfen, meinen Code im Allgemeinen zu verbessern, um mehr über asynchrone und synchrone Anforderungen zu erfahren.
Yago Zardo
Dieser Link ist ein Juwel!
B3none
13

Verwenden Sie einfach ATAppUpdater . Es ist 1 Zeile, threadsicher und schnell. Es gibt auch Delegierungsmethoden, wenn Sie Benutzeraktionen verfolgen möchten.

Hier ist ein Beispiel:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    [[ATAppUpdater sharedUpdater] showUpdateWithConfirmation]; // 1 line of code
    // or
    [[ATAppUpdater sharedUpdater] showUpdateWithForce]; // 1 line of code

   return YES;
}

Optionale Delegierungsmethoden:

- (void)appUpdaterDidShowUpdateDialog;
- (void)appUpdaterUserDidLaunchAppStore;
- (void)appUpdaterUserDidCancel;
Emotionalität
quelle
1
Funktioniert dies für Beta-Versionen in Testflight? Wenn nicht, gibt es ein Werkzeug, das das kann?
Lukasz Czerwinski
Nein, wird es nicht, es vergleicht nur die aktuelle Version mit der neuesten Version, die im AppStore ist.
Emotionalität
Könnten wir das mit Swift verwenden?
Zorayr
11

Vereinfacht eine großartige Antwort auf diesen Thread. Verwenden von Swift 4und Alamofire.

import Alamofire

class VersionCheck {

  public static let shared = VersionCheck()

  func isUpdateAvailable(callback: @escaping (Bool)->Void) {
    let bundleId = Bundle.main.infoDictionary!["CFBundleIdentifier"] as! String
    Alamofire.request("https://itunes.apple.com/lookup?bundleId=\(bundleId)").responseJSON { response in
      if let json = response.result.value as? NSDictionary, let results = json["results"] as? NSArray, let entry = results.firstObject as? NSDictionary, let versionStore = entry["version"] as? String, let versionLocal = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String {
        let arrayStore = versionStore.split(separator: ".")
        let arrayLocal = versionLocal.split(separator: ".")

        if arrayLocal.count != arrayStore.count {
          callback(true) // different versioning system
        }

        // check each segment of the version
        for (key, value) in arrayLocal.enumerated() {
          if Int(value)! < Int(arrayStore[key])! {
            callback(true)
          }
        }
      }
      callback(false) // no new version or failed to fetch app store version
    }
  }

}

Und dann, um es zu benutzen:

VersionCheck.shared.isUpdateAvailable() { hasUpdates in
  print("is update available: \(hasUpdates)")
}
Budidino
quelle
2
Meine Anwendung ist live im Store, aber dieselbe API gibt keine Versionsinformationen zurück. Antwort:{ "resultCount":0, "results": [] }
Technerd
Ich würde es vorziehen, nur einen Hinweis zum Versionsvergleich hinzuzufügen. Lassen Sie serverVersion = "2.7" let localVersion = "2.6.5" let isUpdateAvailable = serverVersion.compare (localVersion, Optionen: .numeric) == .orderedDescending, anstatt das zu ersetzen. mit leer.
Chaitu
@Chaitu danke für den Vorschlag.
Am
9

Der schnelle 4- Code von Anup Gupta wurde aktualisiert

Ich habe einige Änderungen an diesem Code vorgenommen . Jetzt werden die Funktionen aus einer Hintergrundwarteschlange aufgerufen, da die Verbindung langsam sein kann und daher den Hauptthread blockiert.

Ich habe auch den CFBundleName optional gemacht, da die vorgestellte Version "CFBundleDisplayName" hatte, was in meiner Version wahrscheinlich nicht funktionierte. Wenn es jetzt nicht vorhanden ist, stürzt es nicht ab, sondern zeigt den App-Namen nicht in der Warnung an.

import UIKit

enum VersionError: Error {
    case invalidBundleInfo, invalidResponse
}

class LookupResult: Decodable {
    var results: [AppInfo]
}

class AppInfo: Decodable {
    var version: String
    var trackViewUrl: String
}

class AppUpdater: NSObject {

    private override init() {}
    static let shared = AppUpdater()

    func showUpdate(withConfirmation: Bool) {
        DispatchQueue.global().async {
            self.checkVersion(force : !withConfirmation)
        }
    }

    private  func checkVersion(force: Bool) {
        let info = Bundle.main.infoDictionary
        if let currentVersion = info?["CFBundleShortVersionString"] as? String {
            _ = getAppInfo { (info, error) in
                if let appStoreAppVersion = info?.version{
                    if let error = error {
                        print("error getting app store version: ", error)
                    } else if appStoreAppVersion == currentVersion {
                        print("Already on the last app version: ",currentVersion)
                    } else {
                        print("Needs update: AppStore Version: \(appStoreAppVersion) > Current version: ",currentVersion)
                        DispatchQueue.main.async {
                            let topController: UIViewController = UIApplication.shared.keyWindow!.rootViewController!
                            topController.showAppUpdateAlert(Version: (info?.version)!, Force: force, AppURL: (info?.trackViewUrl)!)
                        }
                    }
                }
            }
        }
    }

    private func getAppInfo(completion: @escaping (AppInfo?, Error?) -> Void) -> URLSessionDataTask? {
        guard let identifier = Bundle.main.infoDictionary?["CFBundleIdentifier"] as? String,
            let url = URL(string: "http://itunes.apple.com/lookup?bundleId=\(identifier)") else {
                DispatchQueue.main.async {
                    completion(nil, VersionError.invalidBundleInfo)
                }
                return nil
        }
        let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
            do {
                if let error = error { throw error }
                guard let data = data else { throw VersionError.invalidResponse }
                let result = try JSONDecoder().decode(LookupResult.self, from: data)
                guard let info = result.results.first else { throw VersionError.invalidResponse }

                completion(info, nil)
            } catch {
                completion(nil, error)
            }
        }
        task.resume()
        return task
    }
}

extension UIViewController {
    @objc fileprivate func showAppUpdateAlert( Version : String, Force: Bool, AppURL: String) {
        let appName = Bundle.appName()

        let alertTitle = "New Version"
        let alertMessage = "\(appName) Version \(Version) is available on AppStore."

        let alertController = UIAlertController(title: alertTitle, message: alertMessage, preferredStyle: .alert)

        if !Force {
            let notNowButton = UIAlertAction(title: "Not Now", style: .default)
            alertController.addAction(notNowButton)
        }

        let updateButton = UIAlertAction(title: "Update", style: .default) { (action:UIAlertAction) in
            guard let url = URL(string: AppURL) else {
                return
            }
            if #available(iOS 10.0, *) {
                UIApplication.shared.open(url, options: [:], completionHandler: nil)
            } else {
                UIApplication.shared.openURL(url)
            }
        }

        alertController.addAction(updateButton)
        self.present(alertController, animated: true, completion: nil)
    }
}
extension Bundle {
    static func appName() -> String {
        guard let dictionary = Bundle.main.infoDictionary else {
            return ""
        }
        if let version : String = dictionary["CFBundleName"] as? String {
            return version
        } else {
            return ""
        }
    }
}

Ich rufe an, um auch die Bestätigungsschaltfläche hinzuzufügen:

AppUpdater.shared.showUpdate(withConfirmation: true)

Oder rufen Sie es auf, um so aufgerufen zu werden, damit die Option zum Aktualisieren erzwingen aktiviert ist:

AppUpdater.shared.showUpdate(withConfirmation: false)
Vasco
quelle
Irgendwelche Ideen, wie man das testet? Wenn es nicht richtig funktioniert, besteht die einzige Möglichkeit zum Debuggen darin, eine ältere Version als im App Store zu debuggen.
David Rector
2
Ah, egal die Frage. Ich kann einfach meine lokale Version ändern, um "älter" zu sein.
David Rector
Ich bin beeindruckt von Ihrem Code @Vasco. Nur eine einfache Frage, warum Sie in dieser URL "http" anstelle von "https" verwendet haben.
Master AgentX
Vielen Dank für das Teilen dieser Lösung @Vasco! Ich mag es :) Warum verwenden Sie nicht: let config = URLSessionConfiguration.background (withIdentifier: "com.example.MyExample.background") für die URLSession, um die Hintergrundanforderung zu erreichen?
mc_plectrum
Sie können auch das Force Unwrap entfernen, da Sie bereits prüfen, ob appStoreAppVersion = info? .Version und dasselbe für die trackURL gilt.
mc_plectrum
7

Hier ist meine Version mit Swift 4 und der beliebten Alamofire- Bibliothek (ich verwende sie sowieso in meinen Apps). Die Anforderung ist asynchron und Sie können einen Rückruf übergeben, um benachrichtigt zu werden, wenn Sie fertig sind.

import Alamofire

class VersionCheck {

    public static let shared = VersionCheck()

    var newVersionAvailable: Bool?
    var appStoreVersion: String?

    func checkAppStore(callback: ((_ versionAvailable: Bool?, _ version: String?)->Void)? = nil) {
        let ourBundleId = Bundle.main.infoDictionary!["CFBundleIdentifier"] as! String
        Alamofire.request("https://itunes.apple.com/lookup?bundleId=\(ourBundleId)").responseJSON { response in
            var isNew: Bool?
            var versionStr: String?

            if let json = response.result.value as? NSDictionary,
               let results = json["results"] as? NSArray,
               let entry = results.firstObject as? NSDictionary,
               let appVersion = entry["version"] as? String,
               let ourVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String
            {
                isNew = ourVersion != appVersion
                versionStr = appVersion
            }

            self.appStoreVersion = versionStr
            self.newVersionAvailable = isNew
            callback?(isNew, versionStr)
        }
    }
}

Die Verwendung ist so einfach:

VersionCheck.shared.checkAppStore() { isNew, version in
        print("IS NEW VERSION AVAILABLE: \(isNew), APP STORE VERSION: \(version)")
    }
Nordkapitän
quelle
1
Das Problem bei der Verwendung von ourVersion! = appVersion besteht darin, dass es ausgelöst wird, wenn das App Store Review-Team die neue Version der App überprüft. Wir konvertieren diese Versionszeichenfolgen in Zahlen und dann isNew = appVersion> ourVersion.
Budidino
@budidino Sie haben Recht, ich habe gerade den gemeinsamen Ansatz mit Alamofire gezeigt. Wie Sie die Version interpretieren, hängt vollständig von Ihrer App und der Versionsstruktur ab.
Northern Kapitän
Ich würde es vorziehen, nur einen Hinweis zum Versionsvergleich hinzuzufügen. Lassen Sie serverVersion = "2.7" let localVersion = "2.6.5" let isUpdateAvailable = serverVersion.compare (localVersion, Optionen: .numeric) == .orderedDescending, anstatt mit gleich zu vergleichen
Chaitu
6

Kann ich diese kleine Bibliothek vorschlagen: https://github.com/nicklockwood/iVersion

Ziel ist es, die Handhabung von Remote-Plists zu vereinfachen, um Benachrichtigungen auszulösen.

Andrea
quelle
3
Sie können den App Store direkt auf die Versionsnummer überprüfen, anstatt irgendwo eine Plist-Datei zu hosten. Überprüfen Sie heraus diese Antwort: stackoverflow.com/a/6569307/142358
Steve Moser
1
iVersion verwendet jetzt automatisch die App Store-Version. Die Liste ist optional, wenn Sie andere Versionshinweise als bei iTunes angeben möchten, diese jedoch nicht verwenden müssen.
Nick Lockwood
1
Dieser Code könnte einige Verbesserungen gebrauchen, ist aber viel besser als die anderen Antworten, die eine synchrone Anfrage senden. Trotzdem ist die Art und Weise, wie Threading ausgeführt wird, ein schlechter Stil. Ich werde Probleme auf Github einreichen.
uliwitness
Das Projekt ist jetzt veraltet 😢
Zorayr
5

Swift 3.1

func needsUpdate() -> Bool {
    let infoDictionary = Bundle.main.infoDictionary
    let appID = infoDictionary!["CFBundleIdentifier"] as! String
    let url = URL(string: "http://itunes.apple.com/lookup?bundleId=\(appID)")
    guard let data = try? Data(contentsOf: url) else {
      print("There is an error!")
      return false;
    }
    let lookup = (try? JSONSerialization.jsonObject(with: data! , options: [])) as? [String: Any]
    if let resultCount = lookup!["resultCount"] as? Int, resultCount == 1 {
        if let results = lookup!["results"] as? [[String:Any]] {
            if let appStoreVersion = results[0]["version"] as? String{
                let currentVersion = infoDictionary!["CFBundleShortVersionString"] as? String
                if !(appStoreVersion == currentVersion) {
                    print("Need to update [\(appStoreVersion) != \(currentVersion)]")
                    return true
                }
            }
        }
    }
    return false
}
Kassem Itani
quelle
Dies stürzt ab, wenn Sie keine Internetverbindung haben. Daten = versuchen lassen? Daten (contentOf: url!) Geben null zurück und in der nächsten Zeile geben Sie Daten ein!
Joris Mans
thx @JorisMans Ich werde es für keinen Absturz der Internetverbindung aktualisieren
Kassem Itani
Tu das nicht. Verwenden Sie URLSession.
JAL
4

Diese Antwort ist eine Änderung der Antwort von datinc https://stackoverflow.com/a/25210143/2735358 .

Die Funktion von datinc vergleicht die Version durch einen Zeichenfolgenvergleich. Daher wird die Version nicht für größer oder kleiner als verglichen.

Diese modifizierte Funktion vergleicht jedoch die Version von NSNumericSearch (numerischer Vergleich) .

- (void)checkForUpdateWithHandler:(void(^)(BOOL isUpdateAvailable))updateHandler {

    NSDictionary *infoDictionary = [[NSBundle mainBundle] infoDictionary];
    NSString *appID = infoDictionary[@"CFBundleIdentifier"];
    NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"http://itunes.apple.com/lookup?bundleId=%@", appID]];
    NSLog(@"iTunes Lookup URL for the app: %@", url.absoluteString);

    NSURLSession *session = [NSURLSession sharedSession];
    NSURLSessionDataTask *theTask = [session dataTaskWithRequest:[NSURLRequest requestWithURL:url]
                                               completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {

                                                   NSDictionary *lookup = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
                                                   NSLog(@"iTunes Lookup Data: %@", lookup);
                                                   if (lookup && [lookup[@"resultCount"] integerValue] == 1){
                                                       NSString *appStoreVersion = lookup[@"results"][0][@"version"];
                                                       NSString *currentVersion = infoDictionary[@"CFBundleShortVersionString"];

                                                       BOOL isUpdateAvailable = [appStoreVersion compare:currentVersion options:NSNumericSearch] == NSOrderedDescending;
                                                       if (isUpdateAvailable) {
                                                           NSLog(@"\n\nNeed to update. Appstore version %@ is greater than %@",appStoreVersion, currentVersion);
                                                       }
                                                       if (updateHandler) {
                                                           updateHandler(isUpdateAvailable);
                                                       }
                                                   }
                                               }];
    [theTask resume];
}

Verwenden:

[self checkForUpdateWithHandler:^(BOOL isUpdateAvailable) {
    if (isUpdateAvailable) {
        // show alert
    }
}];
Nitesh Borad
quelle
3
Diese Antwort macht ihre Anfrage synchron. Dies bedeutet, dass bei einer schlechten Verbindung Ihre App für Minuten unbrauchbar sein kann, bis die Anforderung zurückkehrt.
uliwitness
NSURLSession arbeitet automatisch mit Hintergrundthreads, sofern nicht anders angegeben.
Sebastian Dwornik
3

Ich habe viele Möglichkeiten gesehen, um das App-Update zu überprüfen. Basierend auf vielen Antworten mische ich sie und erstelle meine Lösung, die auf GitHub verfügbar ist. Wenn ein Update erforderlich ist, lassen Sie es mich bitte wissen. Dieser Code für Swift 4

GitHub-Link Zu diesem Code. https://github.com/anupgupta-arg/iOS-Swift-ArgAppUpdater

   import UIKit

enum VersionError: Error {
    case invalidBundleInfo, invalidResponse
}

class LookupResult: Decodable {
    var results: [AppInfo]
}

class AppInfo: Decodable {
    var version: String
    var trackViewUrl: String
    //let identifier = Bundle.main.infoDictionary?["CFBundleIdentifier"] as? String,
    // You can add many thing based on "http://itunes.apple.com/lookup?bundleId=\(identifier)"  response
    // here version and trackViewUrl are key of URL response
    // so you can add all key beased on your requirement.

}

class ArgAppUpdater: NSObject {
    private static var _instance: ArgAppUpdater?;

    private override init() {

    }

    public static func getSingleton() -> ArgAppUpdater {
        if (ArgAppUpdater._instance == nil) {
            ArgAppUpdater._instance = ArgAppUpdater.init();
        }
        return ArgAppUpdater._instance!;
    }

    private func getAppInfo(completion: @escaping (AppInfo?, Error?) -> Void) -> URLSessionDataTask? {
        guard let identifier = Bundle.main.infoDictionary?["CFBundleIdentifier"] as? String,
            let url = URL(string: "http://itunes.apple.com/lookup?bundleId=\(identifier)") else {
                DispatchQueue.main.async {
                    completion(nil, VersionError.invalidBundleInfo)
                }
                return nil
        }
        let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
            do {
                if let error = error { throw error }
                guard let data = data else { throw VersionError.invalidResponse }

                print("Data:::",data)
                print("response###",response!)

                let result = try JSONDecoder().decode(LookupResult.self, from: data)

                let dictionary = try? JSONSerialization.jsonObject(with: data, options: .mutableLeaves)

                print("dictionary",dictionary!)


                guard let info = result.results.first else { throw VersionError.invalidResponse }
                print("result:::",result)
                completion(info, nil)
            } catch {
                completion(nil, error)
            }
        }
        task.resume()

        print("task ******", task)
        return task
    }
    private  func checkVersion(force: Bool) {
        let info = Bundle.main.infoDictionary
        let currentVersion = info?["CFBundleShortVersionString"] as? String
        _ = getAppInfo { (info, error) in

            let appStoreAppVersion = info?.version

            if let error = error {
                print(error)



            }else if appStoreAppVersion!.compare(currentVersion!, options: .numeric) == .orderedDescending {
                //                print("needs update")
               // print("hiiii")
                DispatchQueue.main.async {
                    let topController: UIViewController = UIApplication.shared.keyWindow!.rootViewController!

                    topController.showAppUpdateAlert(Version: (info?.version)!, Force: force, AppURL: (info?.trackViewUrl)!)
            }

            }
        }


    }

    func showUpdateWithConfirmation() {
        checkVersion(force : false)


    }

    func showUpdateWithForce() {
        checkVersion(force : true)
    }



}

extension UIViewController {


    fileprivate func showAppUpdateAlert( Version : String, Force: Bool, AppURL: String) {
        print("AppURL:::::",AppURL)

        let bundleName = Bundle.main.infoDictionary!["CFBundleDisplayName"] as! String;
        let alertMessage = "\(bundleName) Version \(Version) is available on AppStore."
        let alertTitle = "New Version"


        let alertController = UIAlertController(title: alertTitle, message: alertMessage, preferredStyle: .alert)


        if !Force {
            let notNowButton = UIAlertAction(title: "Not Now", style: .default) { (action:UIAlertAction) in
                print("Don't Call API");


            }
            alertController.addAction(notNowButton)
        }

        let updateButton = UIAlertAction(title: "Update", style: .default) { (action:UIAlertAction) in
            print("Call API");
            print("No update")
            guard let url = URL(string: AppURL) else {
                return
            }
            if #available(iOS 10.0, *) {
                UIApplication.shared.open(url, options: [:], completionHandler: nil)
            } else {
                UIApplication.shared.openURL(url)
            }

        }

        alertController.addAction(updateButton)
        self.present(alertController, animated: true, completion: nil)
    }
}

Aktualisierung: https://stackoverflow.com/a/48810541/5855888 und https://github.com/emotality/ATAppUpdater

Happy Coding 👍 😊

Anup Gupta
quelle
@ Rob Bitte überprüfen Sie GitHub Link github.com/anupgupta-arg/iOS-Swift-ArgAppUpdater
Anup Gupta
2

Hier ist eine schnelle Methode, die das tut, was einige der Objective-C-Antworten vorschlagen. Sobald Sie die Informationen aus dem App Store JSON erhalten haben, können Sie natürlich die Versionshinweise extrahieren, wenn Sie dies wünschen.

func appUpdateAvailable(storeInfoURL: String) -> Bool
{
    var upgradeAvailable = false

    // Get the main bundle of the app so that we can determine the app's version number
    let bundle = NSBundle.mainBundle()
    if let infoDictionary = bundle.infoDictionary {
        // The URL for this app on the iTunes store uses the Apple ID for the  This never changes, so it is a constant
        let urlOnAppStore = NSURL(string: storeInfoURL)
        if let dataInJSON = NSData(contentsOfURL: urlOnAppStore!) {
            // Try to deserialize the JSON that we got
            if let lookupResults = try? NSJSONSerialization.JSONObjectWithData(dataInJSON, options: NSJSONReadingOptions()) {
                // Determine how many results we got. There should be exactly one, but will be zero if the URL was wrong
                if let resultCount = lookupResults["resultCount"] as? Int {
                    if resultCount == 1 {
                        // Get the version number of the version in the App Store
                        if let appStoreVersion = lookupResults["results"]!![0]["version"] as? String {
                            // Get the version number of the current version
                            if let currentVersion = infoDictionary["CFBundleShortVersionString"] as? String {
                                // Check if they are the same. If not, an upgrade is available.
                                if appStoreVersion != currentVersion {
                                    upgradeAvailable = true                      
                                }
                            }
                        }
                    }
                }
            }
        }
    }

    return upgradeAvailable
}
Mario Hendricks
quelle
storeInfoURL ist die URL der App im Appstore?
iamthevoid
@Mario Hendricks dies funktioniert nicht in Swift 3. Es wirft einige Fehler. Können Sie bitte für Swift 3 aktualisieren?
George Asda
Diese Antwort macht ihre Anfrage synchron. Dies bedeutet, dass bei einer schlechten Verbindung Ihre App für Minuten unbrauchbar sein kann, bis die Anforderung zurückkehrt.
uliwitness
2

Wenn Sie den Inhaltstyp nicht in NSUrlRequest festlegen, erhalten Sie mit Sicherheit keine Antwort. Versuchen Sie also den folgenden Code, der für mich in Ordnung ist. Ich hoffe es hilft....

-(BOOL) isUpdateAvailable{
    NSDictionary* infoDictionary = [[NSBundle mainBundle] infoDictionary];
    NSString* appID = infoDictionary[@"CFBundleIdentifier"];
    NSString *urlString = [NSString stringWithFormat:@"https://itunes.apple.com/lookup?bundleId=%@",appID];

    NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
    [request setURL:[NSURL URLWithString:urlString]];
    [request setHTTPMethod:@"GET"];
    [request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];

    NSURLResponse *response;
    NSError *error;
    NSData *data = [NSURLConnection  sendSynchronousRequest:request returningResponse: &response error: &error];
    NSError *e = nil;
    NSDictionary *jsonDict = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error: &e];

    self.versionInAppStore = [[[jsonDict objectForKey:@"results"] objectAtIndex:0] objectForKey:@"version"];

    self.localAppVersion = infoDictionary[@"CFBundleShortVersionString"];

    if ([self.versionInAppStore compare:self.localAppVersion options:NSNumericSearch] == NSOrderedDescending) {
        // currentVersion is lower than the version
        return YES;
    }
    return NO;
}
Ganka
quelle
Diese Antwort macht ihre Anfrage synchron. Dies bedeutet, dass bei einer schlechten Verbindung Ihre App für Minuten unbrauchbar sein kann, bis die Anforderung zurückkehrt.
uliwitness
2

Ausgehend von einem POV für Hybridanwendungen ist dies ein Javascript-Beispiel. In meinem Hauptmenü befindet sich die Fußzeile Update Available. Wenn ein Update verfügbar ist (dh meine Versionsnummer in der Konfigurationsdatei ist kleiner als die abgerufene Version, zeigen Sie die Fußzeile an). Dadurch wird der Benutzer zum App Store weitergeleitet, wo der Benutzer auf die Schaltfläche Update klicken kann.

Ich erhalte auch die neuen Daten (z. B. Versionshinweise) und zeige diese beim Anmelden modal an, wenn es das erste Mal in dieser Version ist.

Die Methode Update Available kann beliebig oft ausgeführt werden. Meins wird jedes Mal ausgeführt, wenn der Benutzer zum Startbildschirm navigiert.

function isUpdateAvailable() {
        $.ajax('https://itunes.apple.com/lookup?bundleId=BUNDLEID', {
            type: "GET",
            cache: false,
            dataType: 'json'
        }).done(function (data) {
            _isUpdateAvailable(data.results[0]);
        }).fail(function (jqXHR, textStatus, errorThrown) {
            commsErrorHandler(jqXHR, textStatus, false);
        });

}

Rückruf: Apple hat eine API, die sehr einfach zu bekommen ist

function isUpdateAvailable_iOS (data) {
    var storeVersion = data.version;
    var releaseNotes = data.releaseNotes;
    // Check store Version Against My App Version ('1.14.3' -> 1143)
    var _storeV = parseInt(storeVersion.replace(/\./g, ''));
    var _appV = parseInt(appVersion.substring(1).replace(/\./g, ''));
    $('#ft-main-menu-btn').off();
    if (_storeV > _appV) {
        // Update Available
        $('#ft-main-menu-btn').text('Update Available');
        $('#ft-main-menu-btn').click(function () {
           // Open Store      
           window.open('https://itunes.apple.com/us/app/appname/idUniqueID', '_system');
        });

    } else {
        $('#ft-main-menu-btn').html('&nbsp;');
        // Release Notes
        settings.updateReleaseNotes('v' + storeVersion, releaseNotes);
    }
}
tyler_mitchell
quelle
2

Warnung: Die meisten Antworten geben die URL synchron ab (mit -dataWithContentsOfURL:oder -sendSynchronousRequest:. Dies ist schlecht, da Ihre Anwendung einige Minuten lang nicht reagiert, wenn die mobile Verbindung während der Anforderung unterbrochen wird. Führen Sie niemals synchron einen Internetzugang auf der Website durch Haupt-Bedroung.

Die richtige Antwort ist die Verwendung der asynchronen API:

    NSDictionary* infoDictionary = [[NSBundle mainBundle] infoDictionary];
    NSString* appID = infoDictionary[@"CFBundleIdentifier"];
    NSURL* url = [NSURL URLWithString:[NSString stringWithFormat:@"http://itunes.apple.com/lookup?bundleId=%@", appID]];
    NSURLSession         *  session = [NSURLSession sharedSession];
    NSURLSessionDataTask *  theTask = [session dataTaskWithRequest: [NSURLRequest requestWithURL: url] completionHandler:
    ^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error)
    {
        NSDictionary<NSString*,NSArray*>* lookup = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
        if ([lookup[@"resultCount"] integerValue] == 1)
        {
            NSString* appStoreVersion = lookup[@"results"].firstObject[@"version"];
           NSString* currentVersion = infoDictionary[@"CFBundleShortVersionString"];

            if ([appStoreVersion compare:currentVersion options:NSNumericSearch] == NSOrderedDescending) {
                // *** Present alert about updating to user ***
            }
        }
    }];
    [theTask resume];

Das Standardzeitlimit für Netzwerkverbindungen beträgt mehrere Minuten. Selbst wenn die Anforderung ausgeführt wird, kann sie über eine schlechte EDGE-Verbindung langsam genug sein, um so lange zu dauern. Sie möchten in diesem Fall nicht, dass Ihre App unbrauchbar wird. Um solche Dinge zu testen, ist es hilfreich, Ihren Netzwerkcode mit Apples Network Link Conditioner auszuführen.

uliwitness
quelle
Vielen Dank, dass Sie diese Frage am Leben erhalten haben :-)
Jeevan
2
func isUpdateAvailable() -> Bool {
    guard
        let info = Bundle.main.infoDictionary,
        let identifier = info["CFBundleIdentifier"] as? String,
        let url = URL(string: "http://itunes.apple.com/lookup?bundleId=\(identifier)"),
        let data = try? Data(contentsOf: url),
        let json = try? JSONSerialization.jsonObject(with: data, options: .allowFragments) as? [String: Any],
        let results = json?["results"] as? [[String: Any]],
        results.count > 0,
        let versionString = results[0]["version"] as? String
        else {
            return false
    }

    return AppVersion(versionString) > AppVersion.marketingVersion
}

Versionszeichenfolge vergleichen:

https://github.com/eure/AppVersionMonitor

Lova
quelle
2

FÜR SWIFT 4 und 3.2:

Zuerst müssen wir die Bundle-ID aus dem Bundle-Info-Wörterbuch abrufen und isUpdaet auf false setzen.

    var isUpdate = false
    guard let bundleInfo = Bundle.main.infoDictionary,
        let currentVersion = bundleInfo["CFBundleShortVersionString"] as? String,
        //let identifier = bundleInfo["CFBundleIdentifier"] as? String,
        let url = URL(string: "http://itunes.apple.com/lookup?bundleId=\(identifier)")
        else{
        print("something wrong")
            completion(false)
        return
       }

Dann müssen wir einen urlSession-Aufruf aufrufen, um die Version von iTunes zu erhalten.

    let task = URLSession.shared.dataTask(with: url) {
        (data, resopnse, error) in
        if error != nil{
             completion(false)
            print("something went wrong")
        }else{
            do{
                guard let reponseJson = try JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? [String:Any],
                let result = (reponseJson["results"] as? [Any])?.first as? [String: Any],
                let version = result["version"] as? String
                else{
                     completion(false)
                    return
                }
                print("Current Ver:\(currentVersion)")
                print("Prev version:\(version)")
                if currentVersion != version{
                    completion(true)
                }else{
                    completion(false)
                }
            }
            catch{
                 completion(false)
                print("Something went wrong")
            }
        }
    }
    task.resume()

VOLLSTÄNDIGER CODE WIRD WIE DIESER SEIN:

func checkForUpdate(completion:@escaping(Bool)->()){

    guard let bundleInfo = Bundle.main.infoDictionary,
        let currentVersion = bundleInfo["CFBundleShortVersionString"] as? String,
        //let identifier = bundleInfo["CFBundleIdentifier"] as? String,
        let url = URL(string: "http://itunes.apple.com/lookup?bundleId=\(identifier)")
        else{
        print("some thing wrong")
            completion(false)
        return
       }

    let task = URLSession.shared.dataTask(with: url) {
        (data, resopnse, error) in
        if error != nil{
             completion(false)
            print("something went wrong")
        }else{
            do{
                guard let reponseJson = try JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? [String:Any],
                let result = (reponseJson["results"] as? [Any])?.first as? [String: Any],
                let version = result["version"] as? String
                else{
                     completion(false)
                    return
                }
                print("Current Ver:\(currentVersion)")
                print("Prev version:\(version)")
                if currentVersion != version{
                    completion(true)
                }else{
                    completion(false)
                }
            }
            catch{
                 completion(false)
                print("Something went wrong")
            }
        }
    }
    task.resume()
}

Dann können wir die Funktion beliebig aufrufen.

    checkForUpdate { (isUpdate) in
        print("Update needed:\(isUpdate)")
        if isUpdate{
            DispatchQueue.main.async {
                print("new update Available")
            }
        }
    }
Sandu
quelle
2

C # -Äquivalenz von @datinc, ebenso wie das Erhalten der Apple App Store-Version. Enthaltener Code zum Abrufen der Version sowohl für das Bundle als auch für die AssemblyInfo-Datei.

BEARBEITEN :: Bitte beachten Sie die Region "/ us /", die im urlString enthalten ist. Dieser Ländercode muss entsprechend behandelt / geändert werden.

string GetAppStoreVersion()
{
    string version = "";

    NSDictionary infoDictionary = NSBundle
        .MainBundle
        .InfoDictionary;

    String appID = infoDictionary["CFBundleIdentifier"].ToString();

    NSString urlString = 
        new NSString(@"http://itunes.apple.com/us/lookup?bundleId=" + appID);
    NSUrl url = new NSUrl(new System.Uri(urlString).AbsoluteUri);

    NSData data = NSData.FromUrl(url);

    if (data == null)
    {
        /* <-- error obtaining data from url --> */
        return "";
    }

    NSError e = null;
    NSDictionary lookup = (NSDictionary)NSJsonSerialization
        .Deserialize(data, NSJsonReadingOptions.AllowFragments, out e);

    if (lookup == null)
    {
        /* <-- error, most probably no internet or bad connectivity --> */
        return "";
    }

    if (lookup["resultCount"].Description.Equals("1"))
    {
        NSObject nsObject = lookup["results"];
        NSString nsString = new NSString("version");
        String line = nsObject
            .ValueForKey(nsString)
            .Description;

        /* <-- format string --> */
        string[] digits = Regex.Split(line, @"\D+");
        for (int i = 0; i < digits.Length; i++)
        {
            if (int.TryParse(digits[i], out int intTest))
            {
                if (version.Length > 0)
                    version += "." + digits[i];
                else
                    version += digits[i];
            }
        }
    }

    return version;
}

string GetBundleVersion()
{
        return NSBundle
            .MainBundle
            .InfoDictionary["CFBundleShortVersionString"]
            .ToString();
}

string GetAssemblyInfoVersion()
{
        var assembly = typeof(App).GetTypeInfo().Assembly;
        var assemblyName = new AssemblyName(assembly.FullName);
        return assemblyName.Version.ToString();
}
jtth
quelle
2

Versuchen Sie dies mit einem einzigen Funktionsaufruf:

func showAppStoreVersionUpdateAlert(isForceUpdate: Bool) {

    do {
        //Get Bundle Identifire from Info.plist
        guard let bundleIdentifire = Bundle.main.infoDictionary?["CFBundleIdentifier"] as? String else {
            print("No Bundle Info found.")
            throw CustomError.invalidIdentifires
        }

        // Build App Store URL
        guard let url = URL(string:"http://itunes.apple.com/lookup?bundleId=" + bundleIdentifire) else {
            print("Isse with generating URL.")
            throw CustomError.invalidURL
        }

        let serviceTask = URLSession.shared.dataTask(with: url) { (responseData, response, error) in

            do {
                // Check error
                if let error = error { throw error }
                //Parse response
                guard let data = responseData else { throw CustomError.jsonReading }
                let result = try? JSONSerialization.jsonObject(with: data, options: .allowFragments)
                let itunes = ItunesAppInfoItunes.init(fromDictionary: result as! [String : Any])
                print(itunes.results)
                if let itunesResult = itunes.results.first {
                    print("App Store Varsion: ",itunesResult.version)

                    //Get Bundle Version from Info.plist
                    guard let appShortVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String else {
                        print("No Short Version Info found.")
                        throw CustomError.invalidVersion
                    }

                    if appShortVersion == itunesResult.version {
                        //App Store & Local App Have same Version.
                        print("Same Version at both side")
                    } else {
                        //Show Update alert
                        var message = ""
                        //Get Bundle Version from Info.plist
                        if let appName = Bundle.main.infoDictionary?["CFBundleName"] as? String {
                            message = "\(appName) has new version(\(itunesResult.version!)) available on App Store."
                        } else {
                            message = "This app has new version(\(itunesResult.version!)) available on App Store."
                        }

                        //Show Alert on the main thread
                        DispatchQueue.main.async {
                            self.showUpdateAlert(message: message, appStoreURL: itunesResult.trackViewUrl, isForceUpdate: isForceUpdate)
                        }
                    }
                }
            } catch {
                print(error)
            }
        }
        serviceTask.resume()
    } catch {
        print(error)
    }
}

Warnfunktion zum Öffnen der AppStore-URL:

func showUpdateAlert(message : String, appStoreURL: String, isForceUpdate: Bool) {

    let controller = UIAlertController(title: "New Version", message: message, preferredStyle: .alert)

    //Optional Button
    if !isForceUpdate {
        controller.addAction(UIAlertAction(title: "Later", style: .cancel, handler: { (_) in }))
    }

    controller.addAction(UIAlertAction(title: "Update", style: .default, handler: { (_) in
        guard let url = URL(string: appStoreURL) else {
            return
        }
        if #available(iOS 10.0, *) {
            UIApplication.shared.open(url, options: [:], completionHandler: nil)
        } else {
            UIApplication.shared.openURL(url)
        }

    }))

    let applicationDelegate = UIApplication.shared.delegate as? AppDelegate
    applicationDelegate?.window?.rootViewController?.present(controller, animated: true)

}

So rufen Sie die obige Funktion auf:

AppStoreUpdate.shared.showAppStoreVersionUpdateAlert(isForceUpdate: false/true)

Für weitere Informationen versuchen Sie den folgenden Link mit dem vollständigen Code:

AppStoreUpdate.swift

ItunesAppInfoResult.swift

ItunesAppInfoItunes.swift

Ich hoffe das wird helfen!

CodeChanger
quelle
1

Diese Frage wurde 2011 gestellt. Ich fand sie 2018, als ich nach einer Möglichkeit suchte, nicht nur die neue Version der App im App Store zu überprüfen, sondern auch den Benutzer darüber zu benachrichtigen.

Nach kleinen Recherchen kam ich zu dem Schluss, dass die Antwort von juanjo (im Zusammenhang mit Swift 3) https://stackoverflow.com/a/40939740/1218405 die optimale Lösung ist, wenn Sie dies selbst im Code tun möchten

Außerdem kann ich zwei großartige Projekte auf GitHub vorschlagen (jeweils über 2300 Sterne).

Beispiel für Sirene (AppDelegate.swift)

  func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {

      let siren = Siren.shared
      siren.checkVersion(checkType: .immediately)

      return true
    }
  • Sie können auch verschiedene Arten von Warnungen zu neuen Versionen anzeigen (so dass Sie die Version überspringen oder den Benutzer zum Aktualisieren zwingen können).
  • Sie können festlegen, wie oft die Versionsprüfung stattfinden soll (täglich / wöchentlich / sofort).
  • Sie können festlegen, wie viele Tage nach der Veröffentlichung der neuen Version für die App Store-Warnung angezeigt werden soll
Moonvader
quelle
Links zu einer vorhandenen Antwort sind keine Antworten. Darüber hinaus sind Links zu Bibliotheken keine Antworten, es sei denn, Sie fügen Ihrer Antwort explizit hinzu, wie der Link die Frage beantwortet (Codebeispiele hinzufügen usw.).
JAL
1

Swift 4

Wir können das Neue verwenden JSONDecoder, um die Antwort von itunes.apple.com/lookup zu analysieren und sie mit decodierbaren Klassen oder Strukturen darzustellen:

class LookupResult: Decodable {
    var results: [AppInfo]
}

class AppInfo: Decodable {
    var version: String
}

Wir können auch andere Eigenschaften hinzufügen, AppInfofalls wir die releaseNotesoder eine andere Eigenschaft benötigen .

Jetzt können wir eine asynchrone Anfrage stellen mit URLSession:

func getAppInfo(completion: @escaping (AppInfo?, Error?) -> Void) -> URLSessionDataTask? {
    guard let identifier = Bundle.main.infoDictionary?["CFBundleIdentifier"] as? String,
          let url = URL(string: "http://itunes.apple.com/lookup?bundleId=\(identifier)") else {
            DispatchQueue.main.async {
                completion(nil, VersionError.invalidBundleInfo)
            }
            return nil
    }
    let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
        do {
            if let error = error { throw error }
            guard let data = data else { throw VersionError.invalidResponse }
            let result = try JSONDecoder().decode(LookupResult.self, from: data)
            guard let info = result.results.first else { throw VersionError.invalidResponse }

            completion(info, nil)
        } catch {
            completion(nil, error)
        }
    }
    task.resume()
    return task
}

enum VersionError: Error {
    case invalidBundleInfo, invalidResponse
}

Diese Funktion erhält einen Abschlussabschluss, der aufgerufen wird, wenn die Anforderung abgeschlossen ist, und gibt einen URLSessionDataTaskzurück, falls wir die Anforderung abbrechen müssen. Sie kann folgendermaßen aufgerufen werden:

func checkVersion() {
    let info = Bundle.main.infoDictionary
    let currentVersion = info?["CFBundleShortVersionString"] as? String
    _ = getAppInfo { (info, error) in
        if let error = error {
            print(error)
        } else if info?.version == currentVersion {
            print("updated")
        } else {
            print("needs update")
        }
    }
}
Juanjo
quelle
Wo hast du diesen Code abgelegt? Ich sehe, dass Sie LookupResult und AppInfo auf decodierbar setzen, aber ich sehe sie nirgendwo gespeichert. Was fehlt mir hier?
Jessi
Sie deklarieren die Klassen LookupResultund AppInfoirgendwo in Ihrem Projekt, vorzugsweise in einer separaten Datei: Sie werden verwendet, wenn Sie die Antwort dekodieren: JSONDecoder().decode(LookupResult.self, from: data)und sie enthalten die Versionszeichenfolge
juanjo
Basierend auf Ihrer Antwort erstelle ich eine Datei mit Ihrem Code. Bitte überprüfen Sie, ob iOS-Swift-ArgAppUpdater
Anup Gupta
@ Jessi bitte überprüfen Sie meinen Code auf GitHub Ich habe dort Ihre Lösung gepostet
Anup Gupta
0

Mein Code-Vorschlag. Basierend auf den Antworten von @datinc und @ Mario-Hendricks

Sie sollten natürlich durch dlog_ErrorIhren Protokollierungsfunktionsaufruf ersetzen .

Diese Art der Codestruktur sollte verhindern, dass Ihre App im Fehlerfall abstürzt. Zum Abrufen appStoreAppVersionist das nicht zwingend erforderlich und sollte nicht zu schwerwiegenden Fehlern führen. Mit dieser Art von Codestruktur wird Ihr nicht schwerwiegender Fehler dennoch protokolliert.

class func appStoreAppVersion() -> String?
{
    guard let bundleInfo = NSBundle.mainBundle().infoDictionary else {
        dlog_Error("Counldn't fetch bundleInfo.")
        return nil
    }
    let bundleId = bundleInfo[kCFBundleIdentifierKey as String] as! String
    // dbug__print("bundleId = \(bundleId)")

    let address = "http://itunes.apple.com/lookup?bundleId=\(bundleId)"
    // dbug__print("address = \(address)")

    guard let url = NSURLComponents.init(string: address)?.URL else {
        dlog_Error("Malformed internet address: \(address)")
        return nil
    }
    guard let data = NSData.init(contentsOfURL: url) else {
        if Util.isInternetAvailable() {
            dlog_MajorWarning("Web server request failed. Yet internet is reachable. Url was: \(address)")
        }// else: internet is unreachable. All ok. It is of course impossible to fetch the appStoreAppVersion like this.
        return nil
    }
    // dbug__print("data.length = \(data.length)")

    if data.length < 100 { //: We got 42 for a wrong address. And aproximately 4684 for a good response
        dlog_MajorWarning("Web server message is unexpectedly short: \(data.length) bytes")
    }

    guard let response = try? NSJSONSerialization.JSONObjectWithData(data, options: []) else {
        dlog_Error("Failed to parse server response.")
        return nil
    }
    guard let responseDic = response as? [String: AnyObject] else {
        dlog_Error("Not a dictionary keyed with strings. Response with unexpected format.")
        return nil
    }
    guard let resultCount = responseDic["resultCount"] else {
        dlog_Error("No resultCount found.")
        return nil
    }
    guard let count = resultCount as? Int else { //: Swift will handle NSNumber.integerValue
        dlog_Error("Server response resultCount is not an NSNumber.integer.")
        return nil
    }
    //:~ Determine how many results we got. There should be exactly one, but will be zero if the URL was wrong
    guard count == 1 else {
        dlog_Error("Server response resultCount=\(count), but was expected to be 1. URL (\(address)) must be wrong or something.")
        return nil
    }
    guard let rawResults = responseDic["results"] else {
        dlog_Error("Response does not contain a field called results. Results with unexpected format.")
        return nil
    }
    guard let resultsArray = rawResults as? [AnyObject] else {
        dlog_Error("Not an array of results. Results with unexpected format.")
        return nil
    }
    guard let resultsDic = resultsArray[0] as? [String: AnyObject] else {
        dlog_Error("Not a dictionary keyed with strings. Results with unexpected format.")
        return nil
    }
    guard let rawVersion = resultsDic["version"] else {
        dlog_Error("The key version is not part of the results")
        return nil
    }
    guard let versionStr = rawVersion as? String else {
        dlog_Error("Version is not a String")
        return nil
    }
    return versionStr.e_trimmed()
}

extension String {
    func e_trimmed() -> String
    {
        return stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceAndNewlineCharacterSet())
    }
}
SirEnder
quelle
1
Diese Antwort macht ihre Anfrage synchron. Dies bedeutet, dass bei einer schlechten Verbindung Ihre App für Minuten unbrauchbar sein kann, bis die Anforderung zurückkehrt.
uliwitness
-1

Aktualisiert für Swift 3:

Wenn Sie die aktuelle Version Ihrer App überprüfen möchten, verwenden Sie den folgenden einfachen Code:

 let object = Bundle.main.infoDictionary?["CFBundleShortVersionString"]

  let version = object as! String
  print("version: \(version)")
Kiran Jadhav
quelle