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


111

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


6
最新バージョンの文字列表現のみを返すWebサーバーにランダムなページを置くことができます。それをダウンロードして、アプリの起動時に比較し、ユーザーに通知します。(迅速かつ簡単な方法)
LouwHopley

1
感謝しますが、アプリ番号の検索やバージョンデータの取得など、アプリストアの機能を呼び出すことができるAPIのようなより良いソリューションを望んでいました。この目的のためだけにWebサーバーを維持する時間を節約できますが、とにかくポインタに感謝します!
user542584

私は最初のコメントと同じことをします。私は1つのエントリ(NSNumberバージョン番号)を含むplistを書きました。それを自分のウェブサイトにアップロードしました。アプリサポートとアプリのウェブページに使用しているのと同じウェブサイトでviewDidLoad、でウェブサイトのバージョン番号を確認し、アプリの現在のバージョンを確認します。次にalertView、アプリを更新するように自動的に要求する既成のメッセージがあります。必要に応じてコードを提供できます。
Andrew

おかげで、私は..私はあまりにもそれを試してみる必要がありますね
user542584

回答:


88

これは、現在のバージョンが異なるかどうかを知らせる簡単なコードスニペットです

-(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を返します。


4
私が見つけたスーパーソリューション+1
Sanjay Changani 2015

1
@MobeenAfzal、私はあなたが質問と解決策を理解していないと思います。上記のソリューションは、現在のバージョンとストア上のバージョンを比較します。一致しない場合はYESに戻り、それ以外の場合はNOを返します。アプリストアの履歴に関係なく、上記のメソッドは、現在のバージョンがアプリストアのバージョンと異なる場合にYESを返します。ユーザーが更新すると...現在のバージョンはアプリストアのバージョンと同じです。上記のメソッドは、ユーザーのバージョンが1.0でアプリストアのバージョンが1.2の場合、常にYESを返す必要があります。
datinc 2015年

1
@MobeenAfzal私はあなたが見ているものを手に入れていると思います。コードではバージョンは1.7ですが、iTunesではバージョンを1.6としてアップロードしたため、ユーザーがバージョンをスキップしたことがわかりません。それは事実ですか?もしそうなら...あなたが必要なのはあなたのアプリのバージョン番号を提供し、そのエンドポイントにアクセスするようにあなたのコードを修正するサーバーです(DropBoxがするでしょう)。これが表示されているかどうかをお知らせください。投稿に警告のメモを追加します。
datinc 2015年

1
@MobeenAfzalあなたのコメントは誤解を招くものです。ユーザーのデバイスのバージョンがappstoreのバージョンから分離されている場合、コードは期待どおりにYESを返します。バージョン1.0に続いてバージョン1.111をリリースしても、完全に機能します。
datinc 2015年

1
次のように、appstoreのバージョンが現在のバージョンより大きい場合にのみ、更新を表示する必要があります。if([appStoreVersion compare:currentVersion options:NSNumericSearch] == NSOrderedDescending){NSLog(@ "\ n \ nNeed to update。Appstore version%@ is greater than%@"、appStoreVersion、currentVersion); }
Nitesh Borad 2017年

52

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を返すのではなく、エラーをスローする方が良いと思います。この場合、VersionErrorを作成しましたが、ユーザーが定義した他のエラーまたはNSErrorの場合もあります。

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

1
この回答は、リクエストを同期的に行います。つまり、接続が悪いと、リクエストが返されるまでアプリが数分間使用できなくなる可能性があります。
uliwitness 2017

4
私は同意しDispatchQueue.global()ません。バックグラウンドキューを提供します。データはそのキューに読み込まれ、データが読み込まれたときにのみメインキューに戻ります。
juanjo

おっと。どういうわけか、その2番目のコードスニペットを見落としました。悲しいことに、あなたの回答が再び編集されるまで、私は反対票を削除できないようです:-( BTW-与えられたdataWithContentsOfURL:実際にNSURLConnectionの同期呼び出しを通過します。 。ただ、非同期NSURLSession呼び出しを使用するように設定が完了したら、彼らも、あなたはメインスレッドでコールバックしたい。
uliwitness

@juanjo 、、、、、 swift 3.0.1では機能しません。swift用に更新してアップロードできますか???
キランジャダフ

2
特定のストアにのみリストされている場合は、URLに国コードを追加する必要があることがわかりました(例:GBitunes.apple.com/(countryCode )
Ryan

13

リンクを提供してくれた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"];

1
非常に優れた正しい解決策、URLに関するわずかな更新はitunes.apple.com/en/lookup?bundleId=xxxxxxxxxx
SJ

ありがとう、あなたのコメントが適用されました
Roozbeh Zabihollahi

4
実際、私は/en/サブパスではうまくいきませんでした。それを取り除いた後、それはうまくいった
ガスパラフ16/06/30

この回答は、リクエストを同期的に行います。つまり、接続が悪いと、リクエストが返されるまでアプリが数分間使用できなくなる可能性があります。
uliwitness 2017

1
/ en / itunes.apple.com/lookup?bundleId=xxxxxxxを使用する必要がありました。ありがとう@gasparuff
Fernando Perez

13

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

Obs:このYOURBUNDLEIDについては、Xcodeプロジェクトから取得できます... " Targets> General> Identity> Bundle Identifier "

だから、これもいくつかの簡略化を加えた私のコードです:

  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
}

このコードの改善のためのすべての提案は大歓迎です!


この回答は、リクエストを同期的に行います。つまり、接続が悪いと、リクエストが返されるまでアプリが数分間使用できなくなる可能性があります。
uliwitness 2017

@Yago Zardoは、ユーザーがapp.appleテスト済み時間表示更新アラートビューまたはappleがアプリを拒否する場合は、比較機能を使用してください
Jigar Darji

@Jigarさん、アドバイスありがとうございます。現在、サーバーですべてのバージョンを管理しているため、アプリでこのメソッドを使用していません。とにかく、あなたが言ったことをもっとよく説明できますか?理解できなかったので、知っておくと本当に良さそうです。前もって感謝します。
Yago Zardo 2018

ヒントをありがとう@uliwitnessは、非同期および同期リクエストについて学ぶためにコードを全体的に改善するのに本当に役立ちました。
Yago Zardo 2018

そのリンクは宝石です!
B3none 2018

13

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;

1
これはTestflightのベータ版で機能しますか?そうでない場合、何かできるツールはありますか?
Lukasz Czerwinski、2015

いいえ、できません。現在のバージョンとAppStoreにある最新バージョンを比較するだけです。
絵文字2015

これをSwiftで使用できますか?
Zorayr

11

このスレッドに投稿された素晴らしい回答を簡略化しまし。とを使用Swift 4Alamofireます。

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

}

そしてそれを使うには:

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

2
私のアプリケーションはストアで公開されていますが、同じAPIがバージョン情報を返しません。応答:{ "resultCount":0, "results": [] }
technerd

バージョン比較にメモを追加するだけですが、serverVersion = "2.7" let localVersion = "2.6.5" let isUpdateAvailable = serverVersion.compare(localVersion、options:.numeric)== .orderedDescendingではなく、を置き換えます。空で。
Chaitu

@Chaitu提案ありがとうございます。コードの比較部分を書き直してしまいました
budidino

9

Anup GuptaのSwift 4コードを更新しました

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

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

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)

これをテストする方法に関するアイデアはありますか?正しく機能しない場合、デバッグする唯一の方法は、アプリストアにあるバージョンよりも古いバージョンをデバッグすることです。
David Rector

2
ああ、問題は気にしないでください。ローカルバージョンを「古い」バージョンに変更するだけです。
David Rector、

私はあなたのコード@Vascoに感銘を受けました。簡単な質問ですが、なぜそのURLでhttpsではなく「http」を使用したのですか?
Master AgentX

このソリューション@Vascoを共有していただき、ありがとうございます。私はそれが好きです:)なぜ使用しないのですか:バックグラウンド要求を達成するためにURLSessionにconfig = URLSessionConfiguration.background(withIdentifier: "com.example.MyExample.background")をさせますか?
mc_plectrum

appStoreAppVersion = info?.versionでtrackURLも同じかどうかを確認済みなので、強制アンラップを取り除くこともできます。
mc_plectrum

7

これが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)")
    }

1
ourVersion!= appVersionの使用に関する問題は、App Storeレビューチームがアプリの新しいバージョンを確認したときにトリガーされることです。これらのバージョン文字列を数値に変換してから、isNew = appVersion> ourVersionとします。
budidino

@budidino正解です。Alamofireを使用した一般的なアプローチを紹介しました。バージョンの解釈方法は、アプリとバージョン構造に完全に依存します。
北キャプテン

バージョン比較にメモを追加するだけでいいのですが、serverVersion = "2.7" let localVersion = "2.6.5" let isUpdateAvailable = serverVersion.compare(localVersion、options:.numeric)== .orderedDescending equal to compare equal to
Chaitu

6

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

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


3
どこかにplistファイルをホストする代わりに、App Storeでバージョン番号を直接確認できます。この回答を確認してください:stackoverflow.com/a/6569307/142358
Steve Moser

1
iVersionがApp Storeのバージョンを自動的に使用するようになりました。iTunesのリリースノートとは異なるリリースノートを指定する場合、Plistはオプションですが、使用する必要はありません。
Nick Lockwood 2012年

1
このコードはいくつかの改善を使用できますが、同期リクエストを送信する他の回答よりもはるかに優れています。それでも、それがスレッドを行う方法は悪いスタイルです。Githubに問題を報告します。
uliwitness 2017

このプロジェクトは現在、😢推奨されていません
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
}

これは、インターネットに接続していないときにクラッシュします。データを試す=試す?Data(contentsOf:url!)はnilを返し、次の行でデータを実行します!
Joris Mans、

thx @JorisMansインターネット接続がクラッシュしないように更新します
Kassem Itani 2018

これを行わないでください。を使用しURLSessionます。
JAL

4

この回答は、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
    }
}];

3
この回答は、リクエストを同期的に行います。つまり、接続が悪いと、リクエストが返されるまでアプリが数分間使用できなくなる可能性があります。
uliwitness 2017

NSURLSessionは、特に指定しない限り、バックグラウンドスレッドで自動的に機能します。
Sebastian Dwornik

4

アプリのアップデートを確認する方法はたくさんありました。だから多くの答えに基づいてそれらを混ぜて、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 And https://github.com/emotality/ATAppUpdater

ハッピーコーディング👍😊


@RobはGitHubのリンクを確認してくださいgithub.com/anupgupta-arg/iOS-Swift-ArgAppUpdater
Anup Gupta

2

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
}

storeInfoURLはappstore内のアプリのURLですか?
iamthevoid 2016

@Mario HendricksこれはSwift 3では機能しません。エラーが発生します。Swift 3にアップデートしていただけますか?
ジョージ・アズダ

この回答は、リクエストを同期的に行います。つまり、接続が悪いと、リクエストが返されるまでアプリが数分間使用できなくなる可能性があります。
uliwitness 2017

2

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

この回答は、リクエストを同期的に行います。つまり、接続が悪いと、リクエストが返されるまでアプリが数分間使用できなくなる可能性があります。
uliwitness 2017

2

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

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

Update Availableメソッドは、好きなだけ実行できます。Mineは、ユーザーがホーム画面に移動するたびに実行されます。

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

警告:与えられた回答のほとんどが使用して(同期URLを取得していない-dataWithContentsOfURL:か、-sendSynchronousRequest:それは、要求の進行中に、モバイル接続が低下した場合、アプリケーションが数分間応答しなくなることを意味する。これは、悪いです。決して上の同期インターネットアクセスを行いませんメインスレッド。

正解は、非同期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];

ネットワーク接続のデフォルトのタイムアウトは数分です。要求が通過したとしても、不良なEDGE接続を経由すると、その時間がかかる可能性があります。その場合、アプリを使用できなくする必要はありません。このようなことをテストするには、Appleのネットワークリンクコンディショナーでネットワークコードを実行すると便利です。


この質問を存続させていただきありがとうございます:-)
byJeevan

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
}

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

https://github.com/eure/AppVersionMonitor


2

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

その後、必要な関数を呼び出すことができます。

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

2

Apple App Storeバージョンを取得するのと同じくらい、@ datincと同等のC#。バンドルまたは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();
}

2

単一の関数呼び出しでこれを試してください:

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

AppStore URLを開くためのアラート機能:

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

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

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

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

    }))

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

}

上記の関数を呼び出す方法:

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

詳細については、完全なコードで以下のリンクを試してください:

AppStoreUpdate.swift

ItunesAppInfoResult.swift

ItunesAppInfoItunes.swift

これが役に立てば幸いです!


1

この質問は2011年に尋ねられました。AppStoreでアプリの新しいバージョンを確認するだけでなく、ユーザーに通知するための方法を検索しているときに2018年に見つかりました。

小さな調査の後、私は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
    }
  • 新しいバージョンに関するさまざまなタイプのアラートを表示することもできます(バージョンをスキップしたり、ユーザーに更新を強制したりできます)
  • バージョンチェックを行う頻度を指定できます(毎日/毎週/すぐ)
  • アプリストアにリリースされた新しいバージョンのアラートが表示されるまでの日数を指定できます

既存の回答へのリンクは回答ではありません。さらに、リンクが質問にどのように答えるかを明示的に追加しない限り、ライブラリへのリンクも回答にはなりません(コード例を追加するなど)。
JAL

1

スウィフト4

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

このコードをどこに配置しましたか?IIでは、LookupResultとAppInfoをデコード可能に設定していますが、どこにも保存されていないようです。ここで何が欠けていますか?
jessi

LookupResultAppInfoクラスは、プロジェクト内の別のファイルでできれば宣言します。これらは、応答をデコードするときに使用されます。JSONDecoder().decode(LookupResult.self, from: data)また、バージョン文字列が含まれています
juanjo

あなたの答えに基づいて、コードを使用して1つのファイルを作成します。iOS-Swift-ArgAppUpdater
Anup Gupta

@jessiは、GitHubで私のコードを確認してください。私がそこに投稿したソリューション
Anup Gupta

0

私のコード提案。@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())
    }
}

1
この回答は、リクエストを同期的に行います。つまり、接続が悪いと、リクエストが返されるまでアプリが数分間使用できなくなる可能性があります。
uliwitness 2017

-1

Swift 3用に更新:

アプリの現在のバージョンを確認する場合は、以下の簡単なコードを使用します。

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

  let version = object as! String
  print("version: \(version)")
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.