【问题标题】:Check if my app has a new version on AppStore检查我的应用在 AppStore 上是否有新版本
【发布时间】:2011-09-09 13:10:23
【问题描述】:

我想在用户使用时手动检查我的应用是否有新更新,并提示他下载新版本。我可以通过在应用商店中检查我的应用版本来做到这一点吗 - 以编程方式?

【问题讨论】:

  • 您可以将一个随机页面放在一个只返回最新版本的字符串表示的网络服务器上。下载并在应用启动时进行比较并通知用户。 (快速简便的方法)
  • 谢谢,但我希望有更好的解决方案,例如某种 API,我可以使用它调用应用商店功能,例如搜索我的应用编号并获取版本数据。仅为此目的节省维护网络服务器的时间,但无论如何感谢您的指点!
  • 我做的和第一条评论一样。我写了一个包含一个条目的 plist:NSNumber 版本号。然后我把它上传到我的网站。我用于我的应用程序支持和应用程序网页的同一个网站,然后在viewDidLoad,我在那里查看网站的版本号,并在我的应用程序中查看当前版本。然后我有一个预制的alertView,它会自动提示更新应用程序。如果你愿意,我可以提供代码。
  • 谢谢,我想我也应该试试..
  • 我已经使用 Google Firebase 实施了一个解决方案。我使用 remoteConfig 来保存所需版本的值,当应用程序打开时,我将应用程序的版本与设置为 Firebase 的版本进行交叉检查。如果应用程序的版本小于 Firebase 的版本,我会向用户显示警报。这样我就可以按需强制更新应用程序。

标签: ios iphone xcode app-store version


【解决方案1】:

我可以推荐这个小图书馆吗: https://github.com/nicklockwood/iVersion

其目的是简化远程 plist 的处理以触发通知。

【讨论】:

  • 您可以直接在 App Store 中查看版本号,而不是在某处托管 plist 文件。看看这个答案:stackoverflow.com/a/6569307/142358
  • iVersion 现在自动使用应用商店版本 - 如果您想指定与 iTunes 上不同的发行说明,则 Plist 是可选的,但您不需要使用它。
  • 此代码可以使用一些改进,但比发送同步请求的其他答案要好得多。尽管如此,它做线程的方式还是很糟糕的。我会在 Github 上提交问题。
  • 该项目现已弃用 ?
【解决方案2】:

感谢 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"];

【讨论】:

  • 非常好的和正确的解决方案,只是关于网址的一点更新是itunes.apple.com/en/lookup?bundleId=xxxxxxxxxx
  • 谢谢,您的评论已应用
  • 实际上,/en/ 子路径对我不起作用。删除后,它工作了
  • 这个答案是同步发出请求的。这意味着如果连接不良,您的应用可能会在几分钟内无法使用,直到请求返回。
  • 我不得不使用 /en/ itunes.apple.com/lookup?bundleId=xxxxxxx,谢谢@gasparuff
【解决方案3】:

这是一个简单的代码sn-p,可以让你知道当前版本是否不同

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

【讨论】:

  • 我找到的超级解决方案 +1
  • @MobeenAfzal,我想你错过了理解问题和解决方案。上述解决方案将当前版本与商店上的版本进行比较。如果它们不匹配,则返回 YES,否则返回 NO。无论应用商店的历史记录如何,如果当前版本与应用商店版本不同,上述方法将返回 YES。一旦用户更新......当前版本等于应用商店版本。如果用户的版本是 1.0 并且应用商店的版本是 1.2,上述方法应该总是返回 YES。
  • @MobeenAfzal 我想我明白了你所看到的。在代码中,您的版本是 1.7,但在 iTunes 中,您将版本上传为 1.6,这样您的用户就不会知道您跳过了某个版本。是这样吗?如果是这样,那么...您需要一个服务器(DropBox 会做)来为您的应用程序版本号提供服务并修改您的代码以访问该端点。如果这是您所看到的,请告诉我,我会在帖子中添加警告说明。
  • @MobeenAfzal 您的评论具有误导性。如果用户设备上的版本与应用商店上的版本有任何分隔,则代码将按预期返回 YES。即使您发布了 1.0 版,然后发布了 1.111 版,它仍然可以完美运行。
  • 我们应该只在应用商店版本大于当前版本时显示更新,如下所示。 if ([appStoreVersion compare:currentVersion options:NSNumericSearch] == NSOrderedDescending) { NSLog(@"\n\n需要更新。应用商店版本%@大于%@",appStoreVersion, currentVersion); }
【解决方案4】:

只需使用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;

【讨论】:

  • 这是否适用于 Testflight 中的 beta 版本?如果没有,有什么工具可以吗?
  • 不会,它只会将当前版本与 AppStore 上的最新版本进行比较。
  • 我们可以在 Swift 中使用它吗?
  • 其实不是一直都是数字风格的版本,所以应该把版本比较暴露在外面。
  • @Itachi 那是 5.5 年前的事了 :) 包甚至不再被维护了..
【解决方案5】:

这是一个快速的方法,它可以执行一些 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中app的url?
  • @Mario Hendricks 这在 swift 3 中不起作用。它会引发一些错误。你能更新一下swift 3吗?
  • 这个答案是同步发出请求的。这意味着如果连接不良,您的应用可能会在几分钟内无法使用,直到请求返回。
【解决方案6】:

如果你没有在 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;
}

【讨论】:

  • 这个答案是同步发出请求的。这意味着如果连接不良,您的应用可能会在几分钟内无法使用,直到请求返回。
【解决方案7】:

由于遇到同样的问题,我找到了Mario Hendricks提供的answer。不幸的是,当我尝试在我的项目中应用他的代码时,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
}

欢迎所有改进此代码的建议!

【讨论】:

  • 这个答案是同步发出请求的。这意味着如果连接不良,您的应用可能会在几分钟内无法使用,直到请求返回。
  • @Yago Zardo 请使用比较功能,否则当用户上传 app.apple 测试时间显示更新 alertview 或苹果拒绝您的应用时
  • 嘿@Jigar,谢谢你的建议。我目前不在我的应用程序上使用此方法,因为现在我们正在对服务器中的所有内容进行版本控制。无论如何,你能更好地解释你所说的吗?我不明白,知道这真的是件好事。提前致谢。
  • 感谢@uliwitness 的提示,它确实帮助我改进了我的代码以了解异步和同步请求。
  • 喜欢那个金字塔。 (看看使用guard 而不是if。)
【解决方案8】:

我的代码建议。基于@datinc 和@Mario-Hendricks 的回答

当然,您应该将 dlog_Error 替换为您的日志功能调用。

这种代码结构应该可以防止您的应用在发生错误时崩溃。 获取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())
    }
}

【讨论】:

  • 这个答案是同步发出请求的。这意味着如果连接不良,您的应用可能会在几分钟内无法使用,直到请求返回。
【解决方案9】:

来自混合应用程序 POV,这是一个 javascript 示例,我的主菜单上有一个可用更新页脚。如果有可用更新(即我在配置文件中的版本号小于检索到的版本,则显示页脚)这会将用户引导至应用商店,然后用户可以在其中单击更新按钮。

如果这是第一次登录此版本,我还会获取最新数据(即发行说明)并在登录时以模式显示这些数据。

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

【讨论】:

    【解决方案10】:

    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:

    我们可以使用URLSession,而不是使用Data(contentsOf: url) 并阻塞线程:

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

    【讨论】:

    • 这个答案是同步发出请求的。这意味着如果连接不良,您的应用可能会在几分钟内无法使用,直到请求返回。
    • 我不同意,DispatchQueue.global() 给你一个后台队列,数据被加载到那个队列中,只有在数据加载后才返回主队列。
    • 哎呀。不知何故,我忽略了第二个代码 sn-p。可悲的是,在再次编辑您的答案之前,我似乎无法删除反对票只使用异步 NSURLSession 调用。一旦你完成,他们甚至会在主线程上回调你。
    • @juanjo,,,, 不适用于 swift 3.0.1,请您上传更新的 swift 吗???
    • 请注意,如果您仅在特定商店中列出,我发现您需要在 URL 中添加国家代码 - 例如 GB itunes.apple.com/(countryCode)/…)
    【解决方案11】:

    这个答案是对 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
        }
    }];
    

    【讨论】:

    • 这个答案是同步发出请求的。这意味着如果连接不良,您的应用可能会在几分钟内无法使用,直到请求返回。
    • NSURLSession 自动在后台线程上工作,除非我们另外指定。
    【解决方案12】:

    警告: 给出的大多数答案都是同步检索 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 的 Network Link Conditioner 运行您的网络代码很有用。

    【讨论】:

    • 感谢您一直关注这个问题 :-)
    【解决方案13】:
    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

    【讨论】:

      【解决方案14】:

      斯威夫特 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,然后在下一行执行数据!
      • 谢谢@JorisMans 我会更新它以防止互联网连接崩溃
      • 不要这样做。使用URLSession。
      【解决方案15】:

      这个问题是在 2011 年提出的,我是在 2018 年在寻找某种方式时发现的,不仅可以在 App Store 中检查新版本的应用程序,还可以通知用户。

      经过小型研究,我得出的结论是 juanjo 的回答(与 Swift 3 相关) 如果您想自己在代码中执行此操作,https://stackoverflow.com/a/40939740/1218405 是最佳解决方案

      我还可以在 GitHub 上推荐两个很棒的项目(每个都超过 2300 颗星)

      警笛示例 (AppDelegate.swift)

        func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
      
            let siren = Siren.shared
            siren.checkVersion(checkType: .immediately)
      
            return true
          }
      
      • 您还可以显示有关新版本的不同类型的警报(允许跳过版本或强制用户更新)
      • 您可以指定版本检查的频率(每天/每周/立即)
      • 您可以指定在新版本发布到应用商店后多少天出现提醒

      【讨论】:

      • 指向现有答案的链接不是答案。此外,除非您明确添加链接如何回答问题(添加代码示例等),否则指向库的链接也不是答案。
      【解决方案16】:

      斯威夫特 4

      我们可以使用新的JSONDecoder 来解析来自 itunes.apple.com/lookup 的响应,并用可解码的类或结构来表示它:

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

      【讨论】:

      • 你把这段代码放在哪里了?我看到您将 LookupResult 和 AppInfo 设置为可解码,但我没有看到它们保存在任何地方。我在这里错过了什么?
      • 您在项目中的某处声明 LookupResult 和 AppInfo 类,最好在单独的文件中:当您解码响应时使用它们:JSONDecoder().decode(LookupResult.self, from: data) 并且它们包含版本字符串跨度>
      • 根据您的回答,我使用您的代码创建了一个文件请检查iOS-Swift-ArgAppUpdater
      • @jessi 请检查我在 GitHub 上的代码我在那里发布了你的解决方案
      • 我刚刚在 swift 5 中对此进行了测试。它运行良好。我很好奇如何知道 .version 是 App Store 提供的版本(Bundle.main.InfoDictionary)?或者如何知道 CFBundleVersionString 是当前的应用 plist 版本号?我无法理解苹果文档。很高兴知道 App Store 中是否还有其他可以使用的字段,例如新版本中更改的描述是什么。这将帮助用户知道他们是否应该更新。但这不在任何 plist 中,所以可能不可用..
      【解决方案17】:

      我看到了许多检查应用更新的方法。因此,基于许多答案,我将它们混合并创建了我的解决方案,该解决方案可在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

      快乐编码??

      【讨论】:

      【解决方案18】:

      对于 SWIFT 4 和 3.2:

      首先,我们需要从 bundle info 字典中获取 bundle 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
             }
      

      然后我们需要调用 urlSession 来从 iTunes 中获取版本。

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

      完整的代码如下:

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

      【讨论】:

        【解决方案19】:

        这是我使用 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)")
            }
        

        【讨论】:

        • 使用 ourVersion 的问题 != appVersion 是在 App Store Review 团队检查应用程序的新版本时触发。我们将这些版本字符串转换为数字,然后 isNew = appVersion > ourVersion。
        • @budidino 你是对的,我只是展示了使用 Alamofire 的常用方法。您如何解释版本完全取决于您的应用和版本结构。
        • 只是在版本比较中添加注释,我更喜欢,让 serverVersion = "2.7" let localVersion = "2.6.5" let isUpdateAvailable = serverVersion.compare(localVersion, options: .numeric) == .orderedDescending 而不是与相等比较
        【解决方案20】:

        从Anup Gupta更新了swift 4代码

        我对@9​​87654322@ 做了一些改动。现在从后台队列调用函数,因为连接可能很慢,因此阻塞了主线程。

        我还将 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)
        

        【讨论】:

        • 关于如何测试的任何想法?如果它不能正常工作,调试它的唯一方法就是以某种方式调试比应用商店中的旧版本。
        • 啊,别管这个问题。我可以简单地将我的本地版本更改为“旧”。
        • 我对您的代码@Vasco 印象深刻。只是一个简单的问题,为什么你在那个 url 中使用了 'http' 而不是 https?
        • 非常感谢@Vasco 分享这个解决方案!我喜欢 :) 为什么不使用:let config = URLSessionConfiguration.background(withIdentifier: "com.example.MyExample.background") 为 URLSession 实现后台请求?
        • 您也可以摆脱强制解包,因为您已经检查 if let appStoreAppVersion = info?.version 和 trackURL 是否相同。
        【解决方案21】:

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

        【讨论】:

          【解决方案22】:

          在此线程上发布了简化的a great answer。使用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 versionStore = entry["version"] as? String, let versionLocal = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String {
                  let arrayStore = versionStore.split(separator: ".").compactMap { Int($0) }
                  let arrayLocal = versionLocal.split(separator: ".").compactMap { Int($0) }
          
                  if arrayLocal.count != arrayStore.count {
                    callback(true) // different versioning system
                    return
                  }
          
                  // check each segment of the version
                  for (localSegment, storeSegment) in zip(arrayLocal, arrayStore) {
                    if localSegment < storeSegment {
                      callback(true)
                      return
                    }
                  }
                }
                callback(false) // no new version or failed to fetch app store version
              }
            }
            
          }
          

          然后使用它:

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

          【讨论】:

          • 我的应用程序已在商店上线,但相同的 api 未返回版本信息。回复:{ "resultCount":0, "results": [] }
          • 只是在版本比较中添加注释,我更喜欢,让 serverVersion = "2.7" let localVersion = "2.6.5" let isUpdateAvailable = serverVersion.compare(localVersion, options: .numeric) == .orderedDescending 而不是替换 .有空的。
          • @Chaitu 谢谢你的建议。我最终重写了代码的比较部分
          • 回调后应该有return。
          • @LiborZapletal 谢谢。修复了问题并稍微更新了代码
          【解决方案23】:

          用一个函数调用试试这个:

          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

          我希望这会有所帮助!

          【讨论】:

            【解决方案24】:

            我想从answer here 开始添加一些在您更改中间数字版本时有用的行(例如从 1.0.10 到 1.1.0)。

            answer here 的反应就像 1.0.10 比 1.1.0 更新,所以这是我的替代解决方案:

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

            用法总是一样的:

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

            【讨论】:

              【解决方案25】:

              Swift 5(缓存问题已解决)

              enum VersionError: Error {
                  case invalidResponse, invalidBundleInfo
              }
              
              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
                  }
                      
                  let request = URLRequest(url: url, cachePolicy: URLRequest.CachePolicy.reloadIgnoringLocalCacheData)
                  
                  let task = URLSession.shared.dataTask(with: request) { (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 lastVersion = result["version"] as? String else {
                              throw VersionError.invalidResponse
                          }
                          completion(lastVersion > currentVersion, nil)
                      } catch {
                          completion(nil, error)
                      }
                  }
                  
                  task.resume()
                  return task
              }
              

              实施

                          _ = try? isUpdateAvailable {[self] (update, error) in
                              if let error = error {
                                  print(error)
                              } else if update ?? false {
                                  // show alert
                              }
                          }
              

              【讨论】:

              • 请添加更多详细信息以扩展您的答案,例如工作代码或文档引用。
              • 您应该始终为函数添加 @discardableResult 而不是 _ =。
              【解决方案26】:

              您需要以下物品:

              1. 每当您向应用商店提交新版本的应用 (ipa) 时,服务器端逻辑/服务用于维护版本号。这也可以让您将版本获取到客户端。
              2. 客户端逻辑

              如果服务器版本高于设备上安装的版本,提示用户更新应用。

              Here 是一个代码 sn-p,用于检查/比较版本号,遵循数字和点格式的格式(例如 1.2.0)

              var currVer = "1.2.0";
              var newVer = "1.2.1";
              var arr1 = currVer.split(".");
              var arr2 = newVer.split(".");
              var intArray1 = arr1.map(function(txt){return (txt.length===0?0:parseInt(txt));});
              var intArray2 = arr2.map(function(txt){return (txt.length===0?0:parseInt(txt));});
              var l1 = intArray1.length;
              var l2 = intArray2.length;
              var isOutdated=false;
              
              
              
              if(l1>0){
                if(l2>0){
                  // compare both currentversion and new version is not empty
                  if(l1==l2){
                    for(i=0;i<l1;i++){
                      if(intArray2[i]>intArray1[i]){
                        // tag as outdated if matched digit of newVersion is greater than the matching digit of current version
                        isOutdated=true;
                        break;
                      }
                      
                    }
                    
                  }
                  else{
                    if((l2-l1)>0){
                      for(i=0;i<(l2-l1);i++){
                        intArray1.push(0);
                      }
                      
                    }
                    if((l1-l2)>0){
                      for(i=0;i<(l1-l2);i++){
                        intArray2.push(0);
                      }
                      
                    }
                    l1 = intArray1.length;
                    l2 = intArray2.length;
                    
                    for(i=0;i<l1;i++){
                      if(intArray2[i]>intArray1[i]){
                        // tag as outdated if matched digit of newVersion is greater than the matching digit of current version
                        isOutdated=true;
                        break;
                      }
                      
                    }
                  }
                }
                else{
                  // if there's no new version, tag as not outdated
                  isOutdated = false;
                }
                
              }
              else{
                // if current version is empty, tag as not outdated
                isOutdated = false;
              }
              
              document.getElementById("versionTxt").innerHTML = currVer + " -> " + JSON.stringify(intArray1);
              
              
              document.getElementById("versionTxt2").innerHTML = newVer + " -> " + JSON.stringify(intArray2);
              
              document.getElementById("isOutdatedTxt").innerHTML = "Outdated? " + isOutdated.toString();
              <span id="versionTxt"></span> <br />
              <span id="txtLength"></span> <br />
              <span id="versionTxt2"></span> <br />
              <span id="txtLength2"></span> <br />
              
              <span id="lengthCompare"></span> <br />
              
              <span id="isOutdatedTxt"></span>

              【讨论】:

                猜你喜欢
                • 2021-03-13
                • 2021-09-24
                • 1970-01-01
                • 2020-02-10
                • 2012-09-01
                • 2021-11-10
                • 1970-01-01
                • 2021-09-14
                • 2022-01-19
                相关资源
                最近更新 更多