사용자가 앱에있는 동안 내 앱에 대한 새 업데이트가 있는지 수동으로 확인하고 새 버전을 다운로드하라는 메시지를 표시하고 싶습니다. 앱 스토어에서 내 앱의 버전을 프로그래밍 방식으로 확인하여이를 수행 할 수 있습니까?
답변
다음은 현재 버전이 다른지 알려주는 간단한 코드 스 니펫입니다.
-(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를 반환합니다.
답변
Swift 3 버전 :
func isUpdateAvailable() throws -> Bool {
guard let info = Bundle.main.infoDictionary,
let currentVersion = info["CFBundleShortVersionString"] as? String,
let identifier = info["CFBundleIdentifier"] as? String,
let url = URL(string: "http://itunes.apple.com/lookup?bundleId=\(identifier)") else {
throw VersionError.invalidBundleInfo
}
let data = try Data(contentsOf: url)
guard let json = try JSONSerialization.jsonObject(with: data, options: [.allowFragments]) as? [String: Any] else {
throw VersionError.invalidResponse
}
if let result = (json["results"] as? [Any])?.first as? [String: Any], let version = result["version"] as? String {
return version != currentVersion
}
throw VersionError.invalidResponse
}
나는 false를 반환하는 대신 오류를 던지는 것이 더 낫다고 생각합니다.
enum VersionError: Error {
case invalidResponse, invalidBundleInfo
}
또한 연결이 느리면 현재 스레드를 차단할 수 있으므로 다른 스레드에서이 함수를 호출하는 것을 고려하십시오.
DispatchQueue.global().async {
do {
let update = try self.isUpdateAvailable()
DispatchQueue.main.async {
// show alert
}
} catch {
print(error)
}
}
최신 정보
URLSession 사용 :
Data(contentsOf: url)
스레드 를 사용 하고 차단하는 대신 다음을 사용할 수 있습니다 URLSession
.
func isUpdateAvailable(completion: @escaping (Bool?, Error?) -> Void) throws -> URLSessionDataTask {
guard let info = Bundle.main.infoDictionary,
let currentVersion = info["CFBundleShortVersionString"] as? String,
let identifier = info["CFBundleIdentifier"] as? String,
let url = URL(string: "http://itunes.apple.com/lookup?bundleId=\(identifier)") else {
throw VersionError.invalidBundleInfo
}
Log.debug(currentVersion)
let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
do {
if let error = error { throw error }
guard let data = data else { throw VersionError.invalidResponse }
let json = try JSONSerialization.jsonObject(with: data, options: [.allowFragments]) as? [String: Any]
guard let result = (json?["results"] as? [Any])?.first as? [String: Any], let version = result["version"] as? String else {
throw VersionError.invalidResponse
}
completion(version != currentVersion, nil)
} catch {
completion(nil, error)
}
}
task.resume()
return task
}
예:
_ = try? isUpdateAvailable { (update, error) in
if let error = error {
print(error)
} else if let update = update {
print(update)
}
}
답변
그의 링크에 대한 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"];
답변
같은 문제에 직면했기 때문에 Mario Hendricks가 제공 한 답변을 찾았습니다 . 내 프로젝트에 그의 코드를 적용하려고 할 때 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
}
이 코드의 개선을위한 모든 제안을 환영합니다!
답변
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;
답변
이 스레드에 게시 된 훌륭한 답변을 단순화 했습니다 . 사용 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: ".")
let arrayLocal = versionLocal.split(separator: ".")
if arrayLocal.count != arrayStore.count {
callback(true) // different versioning system
}
// check each segment of the version
for (key, value) in arrayLocal.enumerated() {
if Int(value)! < Int(arrayStore[key])! {
callback(true)
}
}
}
callback(false) // no new version or failed to fetch app store version
}
}
}
그리고 그것을 사용하려면 :
VersionCheck.shared.isUpdateAvailable() { hasUpdates in
print("is update available: \(hasUpdates)")
}
답변
에서 신속한 4 코드를 업데이트했습니다.Anup Gupta
이 코드 를 약간 변경했습니다. . 이제 연결이 느려서 메인 스레드를 차단할 수 있으므로 백그라운드 큐에서 함수가 호출됩니다.
또한 제시된 버전에 “CFBundleDisplayName”이 있었기 때문에 CFBundleName을 선택적으로 만들었습니다.이 버전은 아마도 제 버전에서 작동하지 않았을 것입니다. 이제 존재하지 않으면 충돌하지 않지만 경고에 앱 이름이 표시되지 않습니다.
import UIKit
enum VersionError: Error {
case invalidBundleInfo, invalidResponse
}
class LookupResult: Decodable {
var results: [AppInfo]
}
class AppInfo: Decodable {
var version: String
var trackViewUrl: String
}
class AppUpdater: NSObject {
private override init() {}
static let shared = AppUpdater()
func showUpdate(withConfirmation: Bool) {
DispatchQueue.global().async {
self.checkVersion(force : !withConfirmation)
}
}
private func checkVersion(force: Bool) {
let info = Bundle.main.infoDictionary
if let currentVersion = info?["CFBundleShortVersionString"] as? String {
_ = getAppInfo { (info, error) in
if let appStoreAppVersion = info?.version{
if let error = error {
print("error getting app store version: ", error)
} else if appStoreAppVersion == currentVersion {
print("Already on the last app version: ",currentVersion)
} else {
print("Needs update: AppStore Version: \(appStoreAppVersion) > Current version: ",currentVersion)
DispatchQueue.main.async {
let topController: UIViewController = UIApplication.shared.keyWindow!.rootViewController!
topController.showAppUpdateAlert(Version: (info?.version)!, Force: force, AppURL: (info?.trackViewUrl)!)
}
}
}
}
}
}
private func getAppInfo(completion: @escaping (AppInfo?, Error?) -> Void) -> URLSessionDataTask? {
guard let identifier = Bundle.main.infoDictionary?["CFBundleIdentifier"] as? String,
let url = URL(string: "http://itunes.apple.com/lookup?bundleId=\(identifier)") else {
DispatchQueue.main.async {
completion(nil, VersionError.invalidBundleInfo)
}
return nil
}
let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
do {
if let error = error { throw error }
guard let data = data else { throw VersionError.invalidResponse }
let result = try JSONDecoder().decode(LookupResult.self, from: data)
guard let info = result.results.first else { throw VersionError.invalidResponse }
completion(info, nil)
} catch {
completion(nil, error)
}
}
task.resume()
return task
}
}
extension UIViewController {
@objc fileprivate func showAppUpdateAlert( Version : String, Force: Bool, AppURL: String) {
let appName = Bundle.appName()
let alertTitle = "New Version"
let alertMessage = "\(appName) Version \(Version) is available on AppStore."
let alertController = UIAlertController(title: alertTitle, message: alertMessage, preferredStyle: .alert)
if !Force {
let notNowButton = UIAlertAction(title: "Not Now", style: .default)
alertController.addAction(notNowButton)
}
let updateButton = UIAlertAction(title: "Update", style: .default) { (action:UIAlertAction) in
guard let url = URL(string: AppURL) else {
return
}
if #available(iOS 10.0, *) {
UIApplication.shared.open(url, options: [:], completionHandler: nil)
} else {
UIApplication.shared.openURL(url)
}
}
alertController.addAction(updateButton)
self.present(alertController, animated: true, completion: nil)
}
}
extension Bundle {
static func appName() -> String {
guard let dictionary = Bundle.main.infoDictionary else {
return ""
}
if let version : String = dictionary["CFBundleName"] as? String {
return version
} else {
return ""
}
}
}
확인 버튼도 추가해야합니다.
AppUpdater.shared.showUpdate(withConfirmation: true)
또는 다음과 같이 호출하여 강제 업데이트 옵션을 설정하십시오.
AppUpdater.shared.showUpdate(withConfirmation: false)