Vérifier si mon application a une nouvelle version sur l'AppStore

113

Je voudrais vérifier manuellement s'il y a de nouvelles mises à jour pour mon application pendant que l'utilisateur y est, et l'inviter à télécharger la nouvelle version. Puis-je le faire en vérifiant la version de mon application dans l'App Store - par programme?

user542584
la source
6
Vous pouvez placer une page aléatoire sur un serveur Web qui ne renvoie qu'une représentation sous forme de chaîne de la dernière version. Téléchargez-le et comparez-le au démarrage de l'application et informez l'utilisateur. (
Méthode
1
merci, mais j'espérais une meilleure solution comme une sorte d'API avec laquelle je peux appeler les fonctionnalités de l'App Store, comme rechercher mon numéro d'application et obtenir les données de version. Gain de temps pour maintenir un serveur Web uniquement à cette fin, mais merci pour le pointeur quand même!
user542584
Je fais la même chose que le premier commentaire. J'ai écrit un plist avec une entrée: un NSNumbernuméro de version. Ensuite, je l'ai téléchargé sur mon site Web. Le même site Web que j'utilise pour le support de mon application et les pages Web de l'application, puis dans viewDidLoad, je consulte le site Web pour le numéro de version et je vérifie la version actuelle dans mon application. Ensuite, j'ai un premade alertViewqui invite automatiquement à mettre à jour l'application. Je peux fournir du code si vous le souhaitez.
Andrew le
merci, je suppose que je devrais essayer ça aussi ..
user542584

Réponses:

89

Voici un simple extrait de code qui vous permet de savoir si la version actuelle est différente

-(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;
}

Remarque: assurez-vous que lorsque vous entrez la nouvelle version dans iTunes, cela correspond à la version de l'application que vous publiez. Sinon, le code ci-dessus retournera toujours OUI, que l'utilisateur soit mis à jour.

datinc
la source
4
super solution I ever Found +1
Sanjay Changani
1
@MobeenAfzal, je pense que vous avez mal compris la question et la solution. La solution ci-dessus compare la version actuelle avec la version sur le magasin. S'ils ne correspondent pas, il réaccorde OUI, sinon il renvoie NON. Peu importe l'historique sur l'App Store, la méthode ci-dessus retournera OUI si la version actuelle est différente de la version de l'App Store. Une fois que l'utilisateur a mis à jour ... la version actuelle est égale à la version de l'App Store. La méthode ci-dessus doit toujours renvoyer YES si la version de l'utilisateur est 1.0 et la version de l'App Store est 1.2.
datinc
1
@MobeenAfzal Je pense avoir compris ce que vous voyez. Dans le code, votre version est 1.7, mais dans iTunes, vous avez téléchargé la version 1.6 afin que vos utilisateurs ne sachent pas que vous avez sauté une version. Est-ce le cas? Si tel est le cas, vous avez besoin d'un serveur (DropBox ferait) pour servir le numéro de version de vos applications et modifier votre code pour accéder à ce point de terminaison. Faites-moi savoir si c'est ce que vous voyez et j'ajouterai une note d'avertissement au message.
daté du
1
@MobeenAfzal vous votre commentaire est trompeur. Si la version sur l'appareil de l'utilisateur est séparée par any de la version sur l'Appstore, le code renverra OUI comme prévu. Même si vous publiez la version 1.0 suivie de la version 1.111, cela fonctionnerait toujours parfaitement.
daté du
1
Nous devons afficher la mise à jour uniquement lorsque la version de l'Appstore est supérieure à la version actuelle, comme suit. if ([appStoreVersion compare: currentVersion options: NSNumericSearch] == NSOrderedDescending) {NSLog (@ "\ n \ nNécessaire de mettre à jour. La version de l'Appstore% @ est supérieure à% @", appStoreVersion, currentVersion); }
Nitesh Borad
52

Version Swift 3:

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
}

Je pense qu'il est préférable de lancer une erreur au lieu de renvoyer false, dans ce cas, j'ai créé une VersionError mais cela peut être une autre que vous définissez ou NSError

enum VersionError: Error {
    case invalidResponse, invalidBundleInfo
}

Pensez également à appeler cette fonction à partir d'un autre thread, si la connexion est lente, cela peut bloquer le thread actuel.

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

Mettre à jour

Utilisation d'URLSession:

Au lieu d'utiliser Data(contentsOf: url)et de bloquer un thread, nous pouvons utiliser 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
}

exemple:

_ = try? isUpdateAvailable { (update, error) in
    if let error = error {
        print(error)
    } else if let update = update {
        print(update)
    }
}
Juanjo
la source
1
Cette réponse fait sa demande de manière synchrone. Cela signifie qu'avec une mauvaise connexion, votre application pourrait être inutilisable pendant quelques minutes jusqu'à ce que la demande revienne.
uliwitness
4
Je ne suis pas d'accord, DispatchQueue.global()vous donne une file d'attente en arrière-plan, les données sont chargées dans cette file d'attente et ne retournent à la file d'attente principale que lorsque les données sont chargées.
juanjo
Oups. D'une manière ou d'une autre, j'ai oublié ce deuxième extrait de code. Malheureusement, il semble que je ne puisse pas supprimer le vote défavorable jusqu'à ce que votre réponse soit à nouveau modifiée :-( BTW - Étant donné dataWithContentsOfURL: passe en fait par les appels synchrones de NSURLConnection, qui à son tour ne font que démarrer un thread asynchrone et bloquer, ce serait probablement moins de frais généraux pour simplement utiliser les appels asynchrones NSURLSession. Ils vous rappelleraient même sur le fil de discussion principal une fois que vous avez terminé.
uliwitness
@juanjo ,,,, ne fonctionne pas pour swift 3.0.1, pouvez-vous télécharger une mise à jour pour swift ???
Kiran jadhav
2
Notez que si vous n'êtes répertorié que dans un magasin spécifique, j'ai trouvé que vous devez ajouter un code de pays à l'URL - par exemple, GB itunes.apple.com/(countryCode )
Ryan Heitner
13

Merci à Steve Moser pour son lien, voici mon 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
la source
1
solution très bonne et correcte, juste une petite mise à jour concernant l'url est itunes.apple.com/en/lookup?bundleId=xxxxxxxxxx
SJ
Merci, votre commentaire appliqué
Roozbeh Zabihollahi
4
En fait, cela n'a pas fonctionné pour moi avec le sous- /en/chemin. Après l'avoir retiré, cela a fonctionné
gasparuff
Cette réponse fait sa demande de manière synchrone. Cela signifie qu'avec une mauvaise connexion, votre application pourrait être inutilisable pendant quelques minutes jusqu'à ce que la demande revienne.
uliwitness
1
J'ai dû utiliser avec le / fr / itunes.apple.com/lookup?bundleId=xxxxxxx , merci @gasparuff
Fernando Perez le
13

Comme j'étais confronté au même problème, j'ai trouvé la réponse apportée par Mario Hendricks . Malheureusement, lorsque j'ai essayé d'appliquer son code sur mon projet, XCode s'est plaint de problèmes de diffusion en disant "MDLMaterialProperty n'a pas de membres en indice". Son code essayait de définir ce MDLMaterial ... comme le type de la constante "lookupResult", faisant échouer la conversion en "Int" à chaque fois. Ma solution était de fournir une annotation de type pour ma variable à NSDictionary pour être claire sur le type de valeur dont j'avais besoin. Avec cela, j'ai pu accéder à la valeur "version" dont j'avais besoin.

Obs: Pour ce YOURBUNDLEID , vous pouvez obtenir à partir de votre projet Xcode .... " Cibles> Général> Identité> Bundle Identifier "

Voici donc mon code avec quelques simplifications:

  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
}

Toutes les suggestions d'amélioration de ce code sont les bienvenues!

Yago Zardo
la source
Cette réponse fait sa demande de manière synchrone. Cela signifie qu'avec une mauvaise connexion, votre application pourrait être inutilisable pendant quelques minutes jusqu'à ce que la demande revienne.
uliwitness
@Yago Zardo s'il vous plaît utiliser la fonction de comparaison sinon lorsque l'utilisateur télécharge app.apple temps testé affichage d'alerte de mise à jour ou Apple rejette votre application
Jigar Darji
Salut @Jigar, merci pour les conseils. Je n'utilise actuellement plus cette méthode sur mon application car nous effectuons maintenant un versionnage de tout sur notre serveur. Quoi qu'il en soit, pourriez-vous mieux expliquer ce que vous avez dit? Je n'ai pas compris et cela semble vraiment une bonne chose à savoir. Merci d'avance.
Yago Zardo
Merci @uliwitness pour le conseil, cela m'a vraiment aidé à améliorer mon code en général pour en savoir plus sur les requêtes asynchrones et synchrones.
Yago Zardo
Ce lien est un bijou!
B3none le
13

Utilisez simplement ATAppUpdater . Il s'agit d'une ligne, sûre et rapide. Il a également des méthodes de délégation si vous souhaitez suivre l'action de l'utilisateur.

Voici un exemple:

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

   return YES;
}

Méthodes de délégué facultatives:

- (void)appUpdaterDidShowUpdateDialog;
- (void)appUpdaterUserDidLaunchAppStore;
- (void)appUpdaterUserDidCancel;
émotalité
la source
1
Cela fonctionnera-t-il pour les versions bêta de Testflight? Sinon, y a-t-il un outil qui le fera?
Lukasz Czerwinski
Non, ce ne sera pas le cas, il compare uniquement la version actuelle avec la dernière version qui se trouve sur l'AppStore.
emotality
Pouvons-nous l'utiliser avec Swift?
Zorayr le
11

Simplifié une excellente réponse publiée sur ce fil. Utilisation Swift 4et 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
    }
  }

}

Et puis pour l'utiliser:

VersionCheck.shared.isUpdateAvailable() { hasUpdates in
  print("is update available: \(hasUpdates)")
}
budidino
la source
2
Mon application est en ligne sur le magasin mais la même API ne renvoie pas les informations de version. Réponse:{ "resultCount":0, "results": [] }
technerd
En ajoutant simplement une note à la comparaison de versions, je préférerais, laissez serverVersion = "2.7" laissez localVersion = "2.6.5" laissez isUpdateAvailable = serverVersion.compare (localVersion, options: .numeric) == .orderedDescending plutôt que de remplacer le. avec vide.
Chaitu
@Chaitu merci pour la suggestion. J'ai fini par réécrire la partie comparaison du code
budidino
9

Mise à jour du code Swift 4 d' Anup Gupta

J'ai apporté quelques modifications à ce code . Désormais, les fonctions sont appelées à partir d'une file d'attente en arrière-plan, car la connexion peut être lente et donc bloquer le thread principal.

J'ai également rendu le CFBundleName facultatif, car la version présentée avait "CFBundleDisplayName" qui ne fonctionnait probablement pas dans ma version. Alors maintenant, s'il n'est pas présent, il ne plantera pas mais n'affichera tout simplement pas le nom de l'application dans l'alerte.

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 ""
        }
    }
}

Je fais cet appel pour ajouter également le bouton de confirmation:

AppUpdater.shared.showUpdate(withConfirmation: true)

Ou appelez-le comme ceci pour avoir l'option de mise à jour forcée sur:

AppUpdater.shared.showUpdate(withConfirmation: false)
Vasco
la source
Des idées sur la façon de tester cela? Si cela ne fonctionne pas correctement, la seule façon de le déboguer est de déboguer une version plus ancienne que celle de l'App Store.
David Rector le
2
Ah, peu importe la question. Je peux simplement changer ma version locale pour qu'elle soit "plus ancienne".
David Rector
Je suis impressionné par votre code @Vasco. Juste une question simple, pourquoi avez-vous utilisé «http» au lieu de https dans cette URL?
Master AgentX
Merci beaucoup d'avoir partagé cette solution @Vasco! Je l'aime :) Pourquoi n'utilisez-vous pas: let config = URLSessionConfiguration.background (withIdentifier: "com.example.MyExample.background") pour que l'URLSession réalise la requête d'arrière-plan?
mc_plectrum
Vous pouvez également vous débarrasser du déballage forcé, car vous avez déjà vérifié si si let appStoreAppVersion = info? .Version et même pour la trackURL.
mc_plectrum
7

Voici ma version utilisant Swift 4 et la populaire bibliothèque Alamofire (je l'utilise quand même dans mes applications). La demande est asynchrone et vous pouvez transmettre un rappel pour être averti une fois terminé.

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)
        }
    }
}

L'utilisation est simple comme ceci:

VersionCheck.shared.checkAppStore() { isNew, version in
        print("IS NEW VERSION AVAILABLE: \(isNew), APP STORE VERSION: \(version)")
    }
Capitaine du Nord
la source
1
Le problème avec l'utilisation de ourVersion! = appVersion est qu'il se déclenche lorsque l'équipe de révision de l'App Store vérifie la nouvelle version de l'application. Nous convertissons ces chaînes de version en nombres, puis isNew = appVersion> ourVersion.
budidino
@budidino vous avez raison, je viens de montrer l'approche courante en utilisant Alamofire. La façon dont vous interprétez la version dépend totalement de votre application et de la structure de la version.
Northern Captain
En ajoutant simplement une note à la comparaison de versions, je préférerais, laissez serverVersion = "2.7" laissez localVersion = "2.6.5" laissez isUpdateAvailable = serverVersion.compare (localVersion, options: .numeric) == .orderedDescending plutôt que de comparer avec égal
Chaitu
6

Puis-je suggérer cette petite bibliothèque: https://github.com/nicklockwood/iVersion

Son but est de simplifier la gestion des plists distants pour déclencher des notifications.

Andrea
la source
3
Vous pouvez vérifier directement dans l'App Store le numéro de version au lieu d'héberger un fichier plist quelque part. Consultez cette réponse: stackoverflow.com/a/6569307/142358
Steve Moser
1
iVersion utilise désormais automatiquement la version de l'App Store - le Plist est facultatif si vous souhaitez spécifier des notes de publication différentes de celles d'iTunes, mais vous n'avez pas besoin de l'utiliser.
Nick Lockwood
1
Ce code pourrait utiliser certaines améliorations, mais il est bien meilleur que les autres réponses qui envoient une requête synchrone. Pourtant, la façon dont il filme est un mauvais style. Je vais signaler les problèmes sur Github.
uliwitness
Le projet est désormais obsolète 😢
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
la source
Cela se bloque lorsque vous n'avez pas de connexion Internet. laissez les données = essayer? Data (contentsOf: url!) Renverra nil, et dans la ligne suivante vous faites des données!
Joris Mans
thx @JorisMans je vais le mettre à jour pour aucun crash de connectivité Internet
Kassem Itani
Ne fais pas ça. Utilisez URLSession.
JAL
4

Cette réponse est une modification de la réponse de datinc https://stackoverflow.com/a/25210143/2735358 .

La fonction de datinc compare la version par comparaison de chaînes. Ainsi, il ne comparera pas la version supérieure ou inférieure à.

Mais, cette fonction modifiée compare la version par NSNumericSearch (comparaison numérique) .

- (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];
}

Utilisation:

[self checkForUpdateWithHandler:^(BOOL isUpdateAvailable) {
    if (isUpdateAvailable) {
        // show alert
    }
}];
Nitesh Borad
la source
3
Cette réponse fait sa demande de manière synchrone. Cela signifie qu'avec une mauvaise connexion, votre application pourrait être inutilisable pendant quelques minutes jusqu'à ce que la demande revienne.
uliwitness
NSURLSession fonctionne automatiquement sur les threads d'arrière-plan, sauf indication contraire.
Sebastian Dwornik
4

J'ai vu de nombreuses façons de vérifier la mise à jour de l'application. donc basé sur de nombreuses réponses, je les mélange et crée ma solution qui est disponible sur GitHub. Si une mise à jour est nécessaire, veuillez me le faire savoir. Ce code pour Swift 4

Lien GitHub vers ce 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)
    }
}

Réfrence: https://stackoverflow.com/a/48810541/5855888 et https://github.com/emotality/ATAppUpdater

Codage heureux 👍 😊

Anup Gupta
la source
@Rob Veuillez vérifier le lien GitHub github.com/anupgupta-arg/iOS-Swift-ArgAppUpdater
Anup Gupta
3

Essayez ceci avec un seul appel de fonction:

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)
    }
}

Fonction d'alerte pour ouvrir l'URL de l'AppStore:

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)

}

Comment appeler la fonction ci-dessus:

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

Pour plus de détails, essayez le lien ci-dessous avec le code complet:

AppStoreUpdate.swift

ItunesAppInfoResult.swift

ItunesAppInfoItunes.swift

J'espère que cela aidera!

CodeChanger
la source
2

Voici une méthode rapide qui fait ce que suggèrent certaines des réponses Objective-C. Évidemment, une fois que vous avez obtenu les informations de l'App Store JSON, vous pouvez extraire les notes de publication, si vous le souhaitez.

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
la source
storeInfoURL est l'url de l'application dans l'Appstore?
iamthevoid
@Mario Hendricks cela ne fonctionne pas dans Swift 3. Cela génère des erreurs. Pouvez-vous s'il vous plaît mettre à jour pour swift 3?
George Asda
Cette réponse fait sa demande de manière synchrone. Cela signifie qu'avec une mauvaise connexion, votre application pourrait être inutilisable pendant quelques minutes jusqu'à ce que la demande revienne.
uliwitness
2

Si vous ne définissez pas le type de contenu dans NSUrlRequest, vous n'obtiendrez certainement pas de réponse, alors essayez le code ci-dessous, cela fonctionne bien pour moi. J'espère que ça aide....

-(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
la source
Cette réponse fait sa demande de manière synchrone. Cela signifie qu'avec une mauvaise connexion, votre application pourrait être inutilisable pendant quelques minutes jusqu'à ce que la demande revienne.
uliwitness
2

Venant d'un POV d'application hybride, ceci est un exemple javascript, j'ai un pied de page Mise à jour disponible dans mon menu principal. Si une mise à jour est disponible (c'est-à-dire que mon numéro de version dans le fichier de configuration est inférieur à la version récupérée, affichez le pied de page) Cela dirigera alors l'utilisateur vers l'App Store, où l'utilisateur peut alors cliquer sur le bouton de mise à jour.

Je reçois également les nouvelles données (c'est-à-dire les notes de publication) et les affiche dans un modal lors de la connexion si c'est la première fois sur cette version.

La méthode Update Available peut être exécutée aussi souvent que vous le souhaitez. Le mien est exécuté chaque fois que l'utilisateur accède à l'écran d'accueil.

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);
        });

}

Rappel: Apple a une API, donc très facile à obtenir

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
la source
2

Avertissement: la plupart des réponses données récupèrent l'URL de manière synchrone (en utilisant -dataWithContentsOfURL:ou -sendSynchronousRequest:. Ceci est incorrect, car cela signifie que votre application ne répondra pas pendant plusieurs minutes si la connexion mobile est interrompue pendant que la requête est en cours. Ne jamais accéder à Internet de manière synchrone sur le fil principal.

La bonne réponse est d'utiliser l'API asynchrone:

    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];

Le délai d'expiration par défaut pour les connexions réseau est de plusieurs minutes., Et même si la demande aboutit, elle peut être suffisamment lente sur une mauvaise connexion EDGE pour prendre autant de temps. Vous ne voulez pas que votre application soit inutilisable dans ce cas. Pour tester des choses comme celle-ci, il est utile d'exécuter votre code réseau avec le conditionneur de lien réseau d'Apple.

uliwitness
la source
Merci d'avoir gardé cette question vivante :-)
parJeevan
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
}

pour comparer la chaîne de version:

https://github.com/eure/AppVersionMonitor

Lova
la source
2

POUR SWIFT 4 et 3.2:

Tout d'abord, nous devons obtenir l'identifiant du bundle à partir du dictionnaire d'informations du bundle, définissez isUpdaet sur false.

    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
       }

Ensuite, nous devons appeler un appel urlSession pour obtenir la version d'itunes.

    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()

LE CODE COMPLET SERA COMME CECI:

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()
}

Ensuite, nous pouvons appeler la fonction tout logiciel dont nous avons besoin.

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

Équivalence C # de @datinc, dans la mesure où l'obtention de la version Apple App Store. Code inclus pour obtenir la version du bundle ou du fichier AssemblyInfo.

EDIT :: Veuillez noter la région, "/ us /", incluse dans l'urlString. Ce code de pays devra être traité / modifié en conséquence.

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
la source
1

Cette question a été posée en 2011, je l'ai trouvée en 2018 en cherchant un moyen non seulement de vérifier la nouvelle version de l'application dans l'App Store, mais également d'en informer l'utilisateur.

Après de petites recherches, je suis arrivé à la conclusion que la réponse de juanjo (liée à Swift 3) https://stackoverflow.com/a/40939740/1218405 est la solution optimale si vous voulez faire cela dans le code par vous-même

Je peux aussi suggérer deux grands projets sur GitHub (2300+ étoiles chacun)

Exemple pour Siren (AppDelegate.swift)

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

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

      return true
    }
  • Vous pouvez également afficher différents types d'alertes sur la nouvelle version (permettant de sauter la version ou forçant l'utilisateur à mettre à jour)
  • Vous pouvez spécifier la fréquence à laquelle la vérification de version doit avoir lieu (quotidienne / hebdomadaire / immédiate)
  • Vous pouvez spécifier le nombre de jours après la publication de la nouvelle version sur l'App Store.
Moonvader
la source
Les liens vers une réponse existante ne sont pas des réponses. De plus, les liens vers les bibliothèques ne sont pas non plus des réponses, sauf si vous ajoutez explicitement comment le lien répond à la question à votre réponse (ajoutez des exemples de code, etc.)
JAL
1

Swift 4

Nous pouvons utiliser le nouveau JSONDecoderpour analyser la réponse de itunes.apple.com/lookup et la représenter avec des classes ou des structures décodables:

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

class AppInfo: Decodable {
    var version: String
}

Nous pouvons également ajouter d'autres propriétés au AppInfocas où nous aurions besoin de la releaseNotespropriété ou d'une autre propriété.

Nous pouvons maintenant faire une requête asynchrone en utilisant 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
}

cette fonction reçoit une fermeture d'achèvement qui sera appelée lorsque la demande est terminée et retourne un URLSessionDataTaskau cas où nous aurions besoin d'annuler la demande, et peut être appelée comme ceci:

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
la source
Où avez-vous mis ce code? Je vois que vous définissez LookupResult et AppInfo sur décodable, mais je ne les vois pas enregistrés nulle part. Qu'est-ce que j'oublie ici?
jessi
Vous déclarez les classes LookupResultet AppInfoquelque part dans votre projet, dans un fichier séparé de préférence: Elles sont utilisées lorsque vous décodez la réponse: JSONDecoder().decode(LookupResult.self, from: data)et elles contiennent la chaîne de version
juanjo
En fonction de votre réponse, je crée un fichier en utilisant votre code Veuillez vérifier que iOS-Swift-ArgAppUpdater
Anup Gupta
@jessi s'il vous plaît vérifier mon code sur GitHub J'y ai posté votre solution
Anup Gupta
0

Ma proposition de code. Basé sur les réponses de @datinc et @ Mario-Hendricks

Vous devriez bien sûr remplacer dlog_Errorpar votre appel de fonction de journalisation.

Ce type de structure de code devrait empêcher votre application de planter en cas d'erreur. Pour récupérer le appStoreAppVersionn'est pas impératif, et ne doit pas conduire à des erreurs fatales. Et pourtant, avec ce type de structure de code, votre erreur non fatale sera toujours enregistrée.

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
la source
1
Cette réponse fait sa demande de manière synchrone. Cela signifie qu'avec une mauvaise connexion, votre application pourrait être inutilisable pendant quelques minutes jusqu'à ce que la demande revienne.
uliwitness
-1

Mis à jour pour Swift 3:

si vous souhaitez vérifier la version actuelle de votre application, utilisée ci-dessous un code simple:

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

  let version = object as! String
  print("version: \(version)")
Kiran jadhav
la source