web-dev-qa-db-ja.com

アプリにAppStoreの新しいバージョンがあるかどうかを確認します

ユーザーがアプリを使用しているときに、アプリの新しい更新があるかどうかを手動で確認し、新しいバージョンをダウンロードするように促します。プログラムでアプリストアのアプリのバージョンを確認することでこれを実行できますか?

92
user542584

現在のバージョンが異なるかどうかを確認できる簡単なコードスニペットを次に示します。

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

注: iTunesで新しいバージョンを入力するとき、これがリリースするアプリのバージョンと一致することを確認してください。そうでない場合、上記のコードは、ユーザーが更新するかどうかに関係なく、常にYESを返します。

83
datinc

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

Falseを返すのではなくエラーをスローする方が良いと思う

_enum VersionError: Error {
    case invalidResponse, invalidBundleInfo
}
_

また、この関数を別のスレッドから呼び出すことを検討してください。接続が遅い場合、現在のスレッドをブロックする可能性があります。

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

更新

URLSessionの使用:

Data(contentsOf: url)を使用してスレッドをブロックする代わりに、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
}
_

例:

__ = try? isUpdateAvailable { (update, error) in
    if let error = error {
        print(error)
    } else if let update = update {
        print(update)
    }
}
_
39
juanjo

私は同じ問題に直面していたので、 answer によって提供された Mario Hendricks を見つけました。私のプロジェクトに彼のコードを適用しようとすると、XCodeは「MDLMaterialPropertyには添え字メンバーがありません」というキャストの問題について不満を述べました。彼のコードは、このMDLMaterial ...を定数「lookupResult」のタイプとして設定しようとしていたため、「Int」へのキャストが毎回失敗していました。私の解決策は、必要な値の種類を明確にするために、変数の型注釈を NSDictionary に提供することでした。これにより、必要な「バージョン」という値にアクセスできました。

Obs:このため[〜#〜] yourbundleid [〜#〜]、Xcodeプロジェクトから取得できます。.. "ターゲット>一般>アイデンティティ>バンドル識別子 "

だからここにいくつかの簡略化を加えた私のコードがあります:

  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
}

このコードを改善するためのすべての提案を歓迎します!

13
Yago Zardo

彼のリンクを提供してくれたSteve Moserに感謝します。これが私のコードです。

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

ATAppUpdater を使用するだけです。 1行で、スレッドセーフで高速です。ユーザーアクションを追跡する場合は、デリゲートメソッドもあります。

以下に例を示します。

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

   return YES;
}

オプションのデリゲートメソッド:

- (void)appUpdaterDidShowUpdateDialog;
- (void)appUpdaterUserDidLaunchAppStore;
- (void)appUpdaterUserDidCancel;
11
emotality

Swift 4コードを Anup Gupta から更新

このコードにいくつかの変更を加えました 。接続が遅くなり、メインスレッドがブロックされる可能性があるため、関数はバックグラウンドキューから呼び出されます。

また、提示されたバージョンには「CFBundleDisplayName」が含まれていたため、CFBundleNameをオプションにしました。これはおそらく私のバージョンでは機能しませんでした。そのため、存在しない場合はクラッシュせず、アラートにアプリ名が表示されません。

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

確認ボタンも追加するためにこの呼び出しを行います:

AppUpdater.shared.showUpdate(withConfirmation: true)

または、次のように呼び出すように呼び出して、強制更新オプションをオンにします。

AppUpdater.shared.showUpdate(withConfirmation: false)
8
Vasco

Swift 4および人気のあるAlamofireライブラリを使用しているバージョンです(とにかくアプリで使用しています)。要求は非同期であり、完了時に通知されるコールバックを渡すことができます。

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

使い方は次のように簡単です。

VersionCheck.shared.checkAppStore() { isNew, version in
        print("IS NEW VERSION AVAILABLE: \(isNew), APP STORE VERSION: \(version)")
    }
7

この小さなライブラリを提案できますか? https://github.com/nicklockwood/iVersion

その目的は、通知をトリガーするためのリモートプリストの処理を簡素化することです。

7
Andrea

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
}
5
Kassem Itani

この回答は、datincの回答を修正したものです https://stackoverflow.com/a/25210143/2735358

datincの機能は、文字列比較によってバージョンを比較します。したがって、バージョンの大小は比較されません。

ただし、この変更された関数は、NSNumericSearch(数値比較)によるバージョンと比較します。

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

使用:

[self checkForUpdateWithHandler:^(BOOL isUpdateAvailable) {
    if (isUpdateAvailable) {
        // show alert
    }
}];
4
Nitesh Borad

簡略化 すばらしい回答 このスレッドに投稿されました。 Swift 4および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 appStoreVersion = entry["version"] as? String, let installedVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String {
        if let installed = Int(installedVersion.replacingOccurrences(of: ".", with: "")), let appStore = Int(appStoreVersion.replacingOccurrences(of: ".", with: "")), appStore > installed {
          callback(true)
        }
      }
    }
  }

}

そして、それを使用するには:

VersionCheck.shared.isUpdateAvailable() { (hasUpdates) in
  print("is update available: \(hasUpdates)")
}
3
budidino

警告:指定された回答のほとんどは、同期的にURLを取得します(-dataWithContentsOfURL: または -sendSynchronousRequest:。リクエストの進行中にモバイル接続が切断されると、アプリケーションが数分間応答しなくなるため、これは悪いことです。 neverメインスレッドでインターネットアクセスを同期的に行います。

正しい答えは、非同期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];

ネットワーク接続のデフォルトのタイムアウトは数分です。リクエストが通過したとしても、エッジ接続が悪いと、接続に時間がかかることがあります。その場合、アプリを使用不可にしたくないでしょう。このようなことをテストするには、AppleのNetwork Link Conditionerでネットワークコードを実行すると便利です。

2
uliwitness

FOR Swift 4および3.2:

まず、バンドル情報ディクショナリからバンドルIDを取得し、isUpdaetを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
       }

次に、iTunesからバージョンを取得するためにurlSession呼び出しを呼び出す必要があります。

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

完全なコードは次のようになります。

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

次に、必要なanyware関数を呼び出すことができます。

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

ここにSwiftメソッドがあります。これはObjective-Cの回答のいくつかが示唆することを行います。明らかに、アプリストアJSONから情報を取得したら、必要に応じてリリースノートを抽出できます。

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
}
2
Mario Hendricks
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
}

バージョン文字列を比較するには:

https://github.com/eure/AppVersionMonitor

2
Lova

ハイブリッドアプリケーションPOVから来る、これはjavascriptの例です。メインメニューにUpdate Availableフッターがあります。更新が利用可能な場合(つまり、構成ファイル内の私のバージョン番号が取得したバージョンよりも小さい場合、フッターを表示します)、これによりユーザーはアプリストアに移動し、ユーザーはそこで更新ボタンをクリックできます。

また、whatsの新しいデータ(リリースノートなど)を取得し、このバージョンで初めての場合はログイン時にモーダルで表示します。

Update Availableメソッドは、何度でも実行できます。ユーザーがホーム画面に移動するたびに、鉱山が実行されます。

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

}

コールバック:Apple APIがあるため、非常に簡単に取得できます

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

NSUrlRequestでコンテンツタイプを設定していない場合は、確実に応答を得られないので、以下のコードを試してください。それが役に立てば幸い....

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

Swift 4

新しいJSONDecoderを使用して、iTunes.Apple.com/lookupからの応答を解析し、Decodableクラスまたは構造体で表すことができます。

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

class AppInfo: Decodable {
    var version: String
}

AppInfoまたはその他のプロパティが必要な場合は、releaseNotesに他のプロパティを追加することもできます。

これで、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
}

この関数は、リクエストが完了したときに呼び出される完了クロージャを受け取り、リクエストをキャンセルする必要がある場合にURLSessionDataTaskを返します。次のように呼び出すことができます。

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")
        }
    }
}
1
juanjo

アプリの更新を確認する多くの方法を見ました。多くの回答に基づいて、それらを混合し、 GitHub で利用可能なソリューションを作成します。更新が必要な場合はお知らせください。 Swift 4のコード

GitHubリンクこのコードへ。 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)
    }
}

参照: https://stackoverflow.com/a/48810541/5855888 そして https://github.com/emotality/ATAppUpdater

ハッピーコーディング???? ????

1
Anup Gupta

@datincのC#相当、Apple App Storeバージョン。バンドルまたはAssemblyInfoファイルの両方のバージョンを取得するコードが含まれています。

編集:: urlStringに含まれる地域「/ us /」に注意してください。この国コードは、それに応じて処理/変更する必要があります。

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();
}
1
jtth

この質問は2011年に尋ねられましたが、2018年にApp Storeでアプリの新しいバージョンをチェックするだけでなく、ユーザーに通知する方法を探しているときに見つけました。

小さな調査の後、私はjuanjoの答え(Swift 3)に関連する) https://stackoverflow.com/a/40939740/1218405 が最適なソリューションであると結論付けました自分でコードでこれを行いたい場合

また、GitHubで2つの素晴らしいプロジェクトを提案できます(それぞれ2300以上の星)

サイレンの例(AppDelegate.Swift)

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

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

      return true
    }
  • また、新しいバージョンに関するさまざまな種類のアラートを表示することもできます(バージョンのスキップを許可するか、ユーザーに更新を強制する)
  • バージョンチェックを実行する頻度を指定できます(毎日/毎週/即時)
  • アプリストアにリリースされた新しいバージョンのアラートが表示されるまでの日数を指定できます
1
moonvader

私のコード提案。 @datincと@ Mario-Hendricksの回答に基づく

もちろん、dlog_Errorロギングfunc呼び出し。

この種のコード構造により、エラーが発生した場合にアプリがクラッシュするのを防ぐことができます。 appStoreAppVersionを取得することは必須ではなく、致命的なエラーにつながるべきではありません。それでも、この種のコード構造では、致命的ではないエラーがログに記録されます。

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

Swift 3:

以下の簡単なコードで使用されているアプリの現在のバージョンを確認する場合:

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

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