检查我的应用在 AppStore 上是不是有新版本

Posted

技术标签:

【中文标题】检查我的应用在 AppStore 上是不是有新版本【英文标题】: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 的版本,我会向用户显示警报。这样我就可以按需强制更新应用程序。 【参考方案1】:

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

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

【讨论】:

您可以直接在 App Store 中查看版本号,而不是在某处托管 plist 文件。看看这个答案:***.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://***.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://***.com/a/40939740/1218405 是最佳解决方案

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

https://github.com/ArtSabintsev/Harpy 用于 Objective-C https://github.com/ArtSabintsev/Siren 斯威夫特

警笛示例 (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 设置为可解码,但我没有看到它们保存在任何地方。我在这里错过了什么? 您在项目中的某处声明 LookupResultAppInfo 类,最好在单独的文件中:当您解码响应时使用它们: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://***.com/a/48810541/5855888 还有https://github.com/emotality/ATAppUpdater

快乐编码??

【讨论】:

@Rob 请查看 GitHub 链接 github.com/anupgupta-arg/iOS-Swift-ArgAppUpdater【参考方案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 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: ".").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】:

您需要以下物品:

    每当您向应用商店提交新版本的应用 (ipa) 时,服务器端逻辑/服务用于维护版本号。这也可以让您将版本获取到客户端。 客户端逻辑
使用 cordova 插件获取设备上安装的应用版本。 https://github.com/Rareloop/cordova-plugin-app-version。 从服务器获取版本并将其与 cordova 插件返回的版本进行比较。

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

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>

【讨论】:

以上是关于检查我的应用在 AppStore 上是不是有新版本的主要内容,如果未能解决你的问题,请参考以下文章

如果我强制用户更新到 appstore 上可用的较新版本,AppStore 会拒绝我的申请吗

如果我在 App Store 上上传我的应用程序的新版本(仅 ARMv7)会发生啥?

AppStore - 未找到构建

将新版本的应用程序上传到 AppStore 的构建选项?

当 AppStore 中有新版本时如何更新基于 Cordova 的应用程序?

CoreData:用新版本替换数据模型?