检查我的应用程序是否在AppStore上有新版本

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

这是一个简单的代码片段,可以让你知道当前版本是不同的

 -(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中input新版本时,这与您要发布的应用程序中的版本相匹配。 如果没有,那么无论用户更新,上面的代码将总是返回YES。

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,在这种情况下,我创build了一个VersionError,但它可以是一些其他你定义或NSError

 enum VersionError: Error { case invalidResponse, invalidBundleInfo } 

另外考虑从另一个线程调用这个函数,如果连接速度慢,可以阻塞当前线程。

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

更新

使用URLSession:

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

 func isUpdateAvailable(completion: @escaping (Bool?, Error?) -> Void) throws -> URLSessionDataTask { guard let info = Bundle.main.infoDictionary, let currentVersion = info["CFBundleShortVersionString"] as? String, let identifier = info["CFBundleIdentifier"] as? String, let url = URL(string: "http://itunes.apple.com/lookup?bundleId=\(identifier)") else { throw VersionError.invalidBundleInfo } Log.debug(currentVersion) let task = URLSession.shared.dataTask(with: url) { (data, response, error) in do { if let error = error { throw error } guard let data = data else { throw VersionError.invalidResponse } let json = try JSONSerialization.jsonObject(with: data, options: [.allowFragments]) as? [String: Any] guard let result = (json?["results"] as? [Any])?.first as? [String: Any], let version = result["version"] as? String else { throw VersionError.invalidResponse } completion(version != currentVersion, nil) } catch { completion(nil, error) } } task.resume() return task } 

例:

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

由于我面临同样的问题,我find了马里奥亨德里克斯提供的答案 。 当我尝试在我的项目上试用他的代码时,Xcode确实抱怨Casting问题:“MDLMaterialProperty没有下标成员”。 他的代码试图设置这个MDLMaterial …作为常量“lookupResult”的types,从而使每个时间都陷入“Int”失败。 我的解决scheme是提供一个types注释我的variablesNSDictionary清楚我需要的那种价值。 有了这个,我可以访问我需要的值“版本”。

Obs:对于这个YOURBUNDLEID ,你可以从你的Xcode项目中获得….“ 目标>常规>标识> Bundle标识符

所以这里是我的代码以及一些简化:

  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 } 

所有的build议,以改善这个代码是受欢迎的!

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

我可以build议这个小图书馆: https : //github.com/nicklockwood/iVersion

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

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

这个答案是修改datinc的答案https://stackoverflow.com/a/25210143/2735358

datinc的function比较版本的string比较。 所以,它不会比较大于或小于的版本。

但是,这个修改过的函数比较了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 } }]; 

来自混合应用程序POV,这是一个JavaScript的例子,我有一个更新可用页脚在我的主菜单上。 如果有更新可用(即,我的configuration文件中的版本号小于检索的版本,则显示页脚)。然后,这将引导用户访问app store,然后用户可以单击更新button。

我也得到什么新的数据(即发行说明),并显示这些login模式,如果它是第一次在这个版本。

更新可用的方法可以随意运行。 每次用户导航到主屏幕时都会运行矿井。

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

callback:苹果有一个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(' '); // Release Notes settings.updateReleaseNotes('v' + storeVersion, releaseNotes); } } 

这是一个快速的方法,做一些Objective-C的答案build议。 显然,一旦你从app storeJSON获得信息,你可以提取发行说明,如果你想要的话。

 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 } 

如果你没有在NSUrlRequest中设置内容types,那么肯定你不会得到响应,所以尝试下面的代码,它为我工作正常。 希望能帮助到你….

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

警告:大多数答案同时检索URL(使用-dataWithContentsOfURL:-sendSynchronousRequest:这是不好的,因为这意味着如果移动连接在请求正在进行时掉下来,你的应用程序将会在几分钟内不响应。在主线程上同步上网。

正确的答案是使用asynchronousAPI:

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

networking连接的默认超时时间为几分钟,即使请求经过,它也可以在一个恶劣的EDGE连接上足够缓慢地花费很长时间。 在这种情况下,您不希望您的应用程序无法使用。 要testing这样的事情,用Apple的Network Link Conditioner运行networking代码是有用的。

 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 } 

比较版本string:

https://github.com/eure/AppVersionMonitor

我的代码提案。 基于@datinc和@ Mario-Hendricks的回答

你当然应该用你的日志函数调用来replacedlog_Error

这种代码结构应该可以防止你的应用在发生错误时崩溃。 获取appStoreAppVersion并不是必须的,不应该导致致命的错误。 然而,用这种代码结构,你仍然会得到你的非致命错误logging。

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

更新为swift 3:

如果你想检查你的应用程序的当前版本,使用下面的简单代码:

  let object = Bundle.main.infoDictionary?["CFBundleShortVersionString"] let version = object as! String print("version: \(version)") 

Swift 3.1

 func needsUpdate() -> Bool { let infoDictionary = Bundle.main.infoDictionary let appID = infoDictionary!["CFBundleIdentifier"] as! String let url = URL(string: "http://itunes.apple.com/lookup?bundleId=\(appID)") let data = try? Data(contentsOf: url!) 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 } 

AppStore上的新版本

迅速3.0

 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 = Bundle.main 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(contentsOf: urlOnAppStore! as URL) { // Try to deserialize the JSON that we got if let dict: NSDictionary = try! JSONSerialization.jsonObject(with: dataInJSON as Data, options: JSONSerialization.ReadingOptions.allowFragments) as! [String: AnyObject] as NSDictionary? { if let results:NSArray = dict["results"] as? NSArray { if let version = ((results[0] as! NSDictionary).value(forKey: "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 }