내 앱에 AppStore에 새 버전이 있는지 확인


112

사용자가 앱에있는 동안 내 앱에 대한 새 업데이트가 있는지 수동으로 확인하고 새 버전을 다운로드하라는 메시지를 표시하고 싶습니다. 앱 스토어에서 내 앱의 버전을 프로그래밍 방식으로 확인하여이를 수행 할 수 있습니까?


6
최신 버전의 문자열 표현 만 반환하는 웹 서버에 임의의 페이지를 넣을 수 있습니다. 그것을 다운로드하고 앱 시작시 비교하고 사용자에게 알립니다. (빠르고 쉬운 방법)
LouwHopley 2011-06-06

1
감사합니다.하지만 내 앱 번호 검색 및 버전 데이터 가져 오기와 같은 앱 스토어 기능을 호출 할 수있는 일종의 API와 같은 더 나은 솔루션을 원했습니다. 이 목적을 위해 웹 서버를 유지하는 시간을 절약하지만 어쨌든 포인터에 감사드립니다!
user542584

나는 첫 번째 주석과 같은 일을한다. 나는 하나의 항목, 즉 NSNumber버전 번호 로 plist를 작성했습니다 . 그런 다음 웹 사이트에 업로드했습니다. 내 앱 지원 및 앱 웹 페이지에 사용하는 것과 동일한 웹 사이트에서 viewDidLoad에서 웹 사이트에서 버전 번호를 확인하고 내 앱에서 현재 버전을 확인합니다. 그런 다음 alertView자동으로 앱을 업데이트하라는 메시지가 미리 만들어 졌습니다. 원하시면 코드를 제공해 드릴 수 있습니다.
Andrew

덕분에, 나는 .. 나도 그 시도해야 추측
user542584

답변:


88

다음은 현재 버전이 다른지 알려주는 간단한 코드 스 니펫입니다.

-(BOOL) needsUpdate{
    NSDictionary* infoDictionary = [[NSBundle mainBundle] infoDictionary];
    NSString* appID = infoDictionary[@"CFBundleIdentifier"];
    NSURL* url = [NSURL URLWithString:[NSString stringWithFormat:@"http://itunes.apple.com/lookup?bundleId=%@", appID]];
    NSData* data = [NSData dataWithContentsOfURL:url];
    NSDictionary* lookup = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];

    if ([lookup[@"resultCount"] integerValue] == 1){
        NSString* appStoreVersion = lookup[@"results"][0][@"version"];
        NSString* currentVersion = infoDictionary[@"CFBundleShortVersionString"];
        if (![appStoreVersion isEqualToString:currentVersion]){
            NSLog(@"Need to update [%@ != %@]", appStoreVersion, currentVersion);
            return YES;
        }
    }
    return NO;
}

참고 : iTunes에 새 버전을 입력 할 때이 버전이 출시중인 앱의 버전과 일치하는지 확인하십시오. 그렇지 않은 경우 위의 코드는 사용자가 업데이트하더라도 항상 YES를 반환합니다.


4
내가 찾은 슈퍼 솔루션 +1
Sanjay Changani 2015

1
@MobeenAfzal, 질문과 해결책을 이해하지 못한 것 같습니다. 위의 솔루션은 현재 버전을 스토어의 버전과 비교합니다. 일치하지 않으면 YES를 반환하고 그렇지 않으면 NO를 반환합니다. 앱 스토어의 기록에 관계없이 현재 버전이 앱 스토어 버전과 다른 경우 위의 메서드는 YES를 반환합니다. 사용자가 업데이트하면 ... 현재 버전은 앱 스토어 버전과 동일합니다. 위의 메서드는 사용자 버전이 1.0이고 앱 스토어 버전이 1.2 인 경우 항상 YES를 반환해야합니다.
datinc 2015

1
@MobeenAfzal 나는 당신이보고있는 것을 얻는 것 같아요. 코드에서는 버전이 1.7이지만 iTunes에서는 버전을 1.6으로 업로드하여 사용자가 버전을 건너 뛴 것을 알지 못합니다. 그럴까요? 그렇다면 ... 필요한 것은 앱 버전 번호를 제공하고 해당 엔드 포인트에 액세스하도록 코드를 수정하는 서버 (DropBox가 할 것임)입니다. 이것이 귀하가보고있는 것인지 알려 주시면 게시물에 경고 메모를 추가하겠습니다.
datinc

1
@MobeenAfzal 귀하의 의견은 오해의 소지가 있습니다. 사용자 기기의 버전이 앱 스토어의 버전과 구분되는 경우 코드는 예상대로 YES를 반환합니다. 버전 1.0 다음에 버전 1.111을 릴리스하더라도 여전히 완벽하게 작동합니다.
datinc

1
다음과 같이 앱 스토어 버전이 현재 버전보다 높은 경우에만 업데이트를 표시해야합니다. if ([appStoreVersion compare : currentVersion options : NSNumericSearch] == NSOrderedDescending) {NSLog (@ "\ n \ n 업데이트해야합니다. Appstore 버전 % @이 % @"보다 큽니다., appStoreVersion, currentVersion); }
Nitesh Borad

52

Swift 3 버전 :

func isUpdateAvailable() throws -> Bool {
    guard let info = Bundle.main.infoDictionary,
        let currentVersion = info["CFBundleShortVersionString"] as? String,
        let identifier = info["CFBundleIdentifier"] as? String,
        let url = URL(string: "http://itunes.apple.com/lookup?bundleId=\(identifier)") else {
        throw VersionError.invalidBundleInfo
    }
    let data = try Data(contentsOf: url)
    guard let json = try JSONSerialization.jsonObject(with: data, options: [.allowFragments]) as? [String: Any] else {
        throw VersionError.invalidResponse
    }
    if let result = (json["results"] as? [Any])?.first as? [String: Any], let version = result["version"] as? String {
        return version != currentVersion
    }
    throw VersionError.invalidResponse
}

나는 false를 반환하는 대신 오류를 던지는 것이 더 낫다고 생각합니다.

enum VersionError: Error {
    case invalidResponse, invalidBundleInfo
}

또한 연결이 느리면 현재 스레드를 차단할 수 있으므로 다른 스레드에서이 함수를 호출하는 것을 고려하십시오.

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

최신 정보

URLSession 사용 :

Data(contentsOf: url)스레드 를 사용 하고 차단하는 대신 다음을 사용할 수 있습니다 URLSession.

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

예:

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

1
이 답변은 요청을 동 기적으로 만듭니다. 즉, 연결 상태가 좋지 않으면 요청이 반환 될 때까지 몇 분 동안 앱을 사용할 수 없습니다.
uliwitness

4
동의하지 않습니다 DispatchQueue.global(). 백그라운드 큐를 제공하면 데이터가 해당 큐에로드되고 데이터가로드 될 때만 기본 큐로 돌아갑니다.
juanjo

이런. 어떻게 든 두 번째 코드 스 니펫을 간과했습니다. 슬프게도 귀하의 답변이 다시 편집 될 때까지 반대표를 제거 할 수없는 것 같습니다. :-( BTW-주어진 dataWithContentsOfURL : 실제로 NSURLConnection의 동기 호출을 통과하여 비동기 스레드와 블록을 시작하므로 오버 헤드가 적을 것입니다. . 단지 비동기 NSURLSession 호출을 사용하는 방법이 완료되면 그들은 심지어 메인 스레드에서 다시 호출 할 것입니다.
uliwitness

@ juanjo,``, swift 3.0.1에서 작동하지 않습니다. swift 업데이트를 업로드 할 수 있습니까?
Kiran jadhav

2
예를 들어, GB - 당신은 단지 특정 저장소에 등록 된 것인지 참고 난 당신이 URL에 국가 코드를 추가 할 필요가 있음을 발견했다 itunes.apple.com/(countryCode)/... )
라이언 Heitner

13

그의 링크에 대한 Steve Moser에게 감사드립니다. 여기 내 코드가 있습니다.

NSString *appInfoUrl = @"http://itunes.apple.com/en/lookup?bundleId=XXXXXXXXX";

NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
[request setURL:[NSURL URLWithString:appInfoUrl]];
[request setHTTPMethod:@"GET"];

NSURLResponse *response;
NSError *error;
NSData *data = [NSURLConnection  sendSynchronousRequest:request returningResponse: &response error: &error];
NSString *output = [NSString stringWithCString:[data bytes] length:[data length]];

NSError *e = nil;
NSData *jsonData = [output dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary *jsonDict = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers error: &e];

NSString *version = [[[jsonDict objectForKey:@"results"] objectAtIndex:0] objectForKey:@"version"];

1
아주 좋고 정확한 솔루션, URL에 관한 약간의 업데이트는 itunes.apple.com/en/lookup?bundleId=xxxxxxxxxx
SJ

감사합니다, 귀하의 의견이 적용되었습니다
Roozbeh

4
실제로 /en/하위 경로 와 함께 작동하지 않았습니다 . 를 제거 후에는 일
gasparuff

이 답변은 요청을 동 기적으로 만듭니다. 즉, 연결 상태가 좋지 않으면 요청이 반환 될 때까지 몇 분 동안 앱을 사용할 수 없습니다.
uliwitness

1
나는 / en / itunes.apple.com/lookup?bundleId=xxxxxxx 와 함께 사용해야했습니다. 감사합니다 @gasparuff
Fernando Perez

13

같은 문제에 직면했기 때문에 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
}

이 코드의 개선을위한 모든 제안을 환영합니다!


이 답변은 요청을 동 기적으로 만듭니다. 즉, 연결 상태가 좋지 않으면 요청이 반환 될 때까지 몇 분 동안 앱을 사용할 수 없습니다.
uliwitness

@Yago Zardo 시간 표시 업데이트 alertview 또는 사과 앱을 거부하는 사용자 업로드 app.apple 테스트 그렇지 비교 기능을 사용하십시오
Jigar Darji

안녕하세요 @Jigar, 조언에 감사드립니다. 지금은 서버의 모든 버전을 관리하고 있기 때문에 현재 앱에서 더 이상이 방법을 사용하지 않습니다. 어쨌든, 당신이 말한 것을 더 잘 설명해 주시겠습니까? 나는 이해하지 못했고 정말로 아는 것이 좋은 것 같습니다. 미리 감사드립니다.
Yago Zardo

팁에 대한 @uliwitness에게 감사드립니다. 비동기 및 동기 요청에 대해 배우기 위해 일반적으로 코드를 개선하는 데 도움이되었습니다.
Yago Zardo

그 링크는 보석입니다!
B3none

13

ATAppUpdater를 사용 하십시오 . 스레드로부터 안전하고 빠르며 1 줄입니다. 사용자 작업을 추적하려는 경우 위임 메서드도 있습니다.

다음은 예입니다.

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    [[ATAppUpdater sharedUpdater] showUpdateWithConfirmation]; // 1 line of code
    // or
    [[ATAppUpdater sharedUpdater] showUpdateWithForce]; // 1 line of code

   return YES;
}

선택적 대리자 메서드 :

- (void)appUpdaterDidShowUpdateDialog;
- (void)appUpdaterUserDidLaunchAppStore;
- (void)appUpdaterUserDidCancel;

1
Testflight의 베타 버전에서도 작동합니까? 그렇지 않다면 도구가 있습니까?
Lukasz Czerwinski

아니요. 현재 버전을 AppStore에있는 최신 버전과 비교합니다.
emotality dec

이것을 Swift와 함께 사용할 수 있습니까?
Zorayr

11

이 스레드에 게시 된 훌륭한 답변을 단순화 했습니다 . 사용 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)")
}

2
내 응용 프로그램은 상점에 있지만 동일한 API가 버전 정보를 반환하지 않습니다. 응답 :{ "resultCount":0, "results": [] }
technerd 19

버전 비교에 메모를 추가하기 만하면됩니다. let serverVersion = "2.7"let localVersion = "2.6.5"let isUpdateAvailable = serverVersion.compare (localVersion, options : .numeric) == .orderDescending 대신. 비어 있습니다.
Chaitu

@Chaitu 제안에 감사드립니다. 코드의 비교 부분을 다시 작성했습니다
budidino

9

에서 신속한 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)

이것을 테스트하는 방법에 대한 아이디어가 있습니까? 제대로 작동하지 않는 경우 디버그하는 유일한 방법은 앱 스토어에있는 것보다 이전 버전을 디버그하는 것입니다.
David Rector

2
아, 질문은 신경 쓰지 마세요. 로컬 버전을 "이전 버전"으로 간단히 변경할 수 있습니다.
David Rector

@Vasco 코드에 깊은 인상을 받았습니다. 간단한 질문입니다. 왜 해당 URL에서 https 대신 'http'를 사용 했습니까?
마스터는 AgentX

이 솔루션 @Vasco를 공유해 주셔서 감사합니다! 나는 그것을 좋아한다 :) 왜 백그라운드 요청을 달성하기 위해 URLSession에 대해 let config = URLSessionConfiguration.background (withIdentifier : "com.example.MyExample.background")를 사용하지 않습니까?
mc_plectrum

이미 let appStoreAppVersion = info? .version이고 trackURL에 대해 동일한 지 이미 확인 했으므로 force unwrap을 제거 할 수도 있습니다.
mc_plectrum

7

다음은 Swift 4 와 인기있는 Alamofire 라이브러리를 사용하는 내 버전입니다 (어쨌든 내 앱에서 사용합니다). 요청은 비동기 적이며 완료되면 알림을받을 콜백을 전달할 수 있습니다.

import Alamofire

class VersionCheck {

    public static let shared = VersionCheck()

    var newVersionAvailable: Bool?
    var appStoreVersion: String?

    func checkAppStore(callback: ((_ versionAvailable: Bool?, _ version: String?)->Void)? = nil) {
        let ourBundleId = Bundle.main.infoDictionary!["CFBundleIdentifier"] as! String
        Alamofire.request("https://itunes.apple.com/lookup?bundleId=\(ourBundleId)").responseJSON { response in
            var isNew: Bool?
            var versionStr: String?

            if let json = response.result.value as? NSDictionary,
               let results = json["results"] as? NSArray,
               let entry = results.firstObject as? NSDictionary,
               let appVersion = entry["version"] as? String,
               let ourVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String
            {
                isNew = ourVersion != appVersion
                versionStr = appVersion
            }

            self.appStoreVersion = versionStr
            self.newVersionAvailable = isNew
            callback?(isNew, versionStr)
        }
    }
}

사용법은 다음과 같이 간단합니다.

VersionCheck.shared.checkAppStore() { isNew, version in
        print("IS NEW VERSION AVAILABLE: \(isNew), APP STORE VERSION: \(version)")
    }

1
ourVersion! = appVersion 사용시 문제점은 App Store 검토 팀이 앱의 새 버전을 확인할 때 트리거된다는 것입니다. 해당 버전 문자열을 숫자로 변환 한 다음 isNew = appVersion> ourVersion입니다.
budidino 19-06-05

@budidino 당신이 맞습니다, 방금 Alamofire를 사용하는 일반적인 접근 방식을 보여주었습니다. 버전을 해석하는 방법은 전적으로 앱과 버전 구조에 따라 다릅니다.
노던 캡틴

그냥 내가 선호의 Serverversion을 할 것이다 버전을 비교 한에 메모를 추가 = "2.7"하자 localVersion = "2.6.5"isUpdateAvailable = serverVersion.compare (localVersion, 옵션 : .numeric)하자 == .orderedDescending보다는 동일과 비교를
Chaitu

6

이 작은 라이브러리를 제안 할 수 있습니까? https://github.com/nicklockwood/iVersion

그 목적은 알림을 트리거하기 위해 원격 plist의 처리를 단순화하는 것입니다.


3
plist 파일을 어딘가에 호스팅하는 대신 App Store에서 버전 번호를 직접 확인할 수 있습니다. 이 답변을 확인하십시오 : stackoverflow.com/a/6569307/142358
Steve Moser

1
iVersion은 이제 앱 스토어 버전을 자동으로 사용합니다. iTunes의 릴리스 노트와 다른 릴리스 정보를 지정하려는 경우 Plist는 선택 사항이지만 사용할 필요는 없습니다.
Nick Lockwood

1
이 코드는 몇 가지 개선 사항을 사용할 수 있지만 동기 요청을 보내는 다른 응답보다 훨씬 낫습니다. 그래도 스레딩 방식은 나쁜 스타일입니다. Github에 문제를 제출하겠습니다.
uliwitness

이 프로젝트는 이제 더 이상 사용되지 않습니다. 😢
Zorayr

5

스위프트 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 나는 인터넷에 연결 충돌을 위해 업데이트됩니다
카셈 (이집트) Itani

이러지마 사용 URLSession.
JAL

4

이 답변은 datinc의 답변 https://stackoverflow.com/a/25210143/2735358에 대한 수정 입니다.

datinc의 기능은 문자열 비교로 버전을 비교합니다. 따라서보다 크거나 작은 버전을 비교하지 않습니다.

하지만이 수정 된 함수 는 NSNumericSearch (숫자 비교)로 버전을 비교합니다 .

- (void)checkForUpdateWithHandler:(void(^)(BOOL isUpdateAvailable))updateHandler {

    NSDictionary *infoDictionary = [[NSBundle mainBundle] infoDictionary];
    NSString *appID = infoDictionary[@"CFBundleIdentifier"];
    NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"http://itunes.apple.com/lookup?bundleId=%@", appID]];
    NSLog(@"iTunes Lookup URL for the app: %@", url.absoluteString);

    NSURLSession *session = [NSURLSession sharedSession];
    NSURLSessionDataTask *theTask = [session dataTaskWithRequest:[NSURLRequest requestWithURL:url]
                                               completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {

                                                   NSDictionary *lookup = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
                                                   NSLog(@"iTunes Lookup Data: %@", lookup);
                                                   if (lookup && [lookup[@"resultCount"] integerValue] == 1){
                                                       NSString *appStoreVersion = lookup[@"results"][0][@"version"];
                                                       NSString *currentVersion = infoDictionary[@"CFBundleShortVersionString"];

                                                       BOOL isUpdateAvailable = [appStoreVersion compare:currentVersion options:NSNumericSearch] == NSOrderedDescending;
                                                       if (isUpdateAvailable) {
                                                           NSLog(@"\n\nNeed to update. Appstore version %@ is greater than %@",appStoreVersion, currentVersion);
                                                       }
                                                       if (updateHandler) {
                                                           updateHandler(isUpdateAvailable);
                                                       }
                                                   }
                                               }];
    [theTask resume];
}

사용하다:

[self checkForUpdateWithHandler:^(BOOL isUpdateAvailable) {
    if (isUpdateAvailable) {
        // show alert
    }
}];

3
이 답변은 요청을 동 기적으로 만듭니다. 즉, 연결 상태가 좋지 않으면 요청이 반환 될 때까지 몇 분 동안 앱을 사용할 수 없습니다.
uliwitness

NSURLSession은 달리 지정하지 않는 한 백그라운드 스레드에서 자동으로 작동합니다.
Sebastian Dwornik

4

앱 업데이트를 확인하는 방법은 여러 가지가 있습니다. 그래서 많은 답변을 기반으로 혼합하고 GitHub에서 사용할 수있는 솔루션을 만듭니다. 업데이트가 필요한 경우 알려주세요. Swift 4 용 코드

이 코드에 대한 GitHub 링크. https://github.com/anupgupta-arg/iOS-Swift-ArgAppUpdater

   import UIKit

enum VersionError: Error {
    case invalidBundleInfo, invalidResponse
}

class LookupResult: Decodable {
    var results: [AppInfo]
}

class AppInfo: Decodable {
    var version: String
    var trackViewUrl: String
    //let identifier = Bundle.main.infoDictionary?["CFBundleIdentifier"] as? String,
    // You can add many thing based on "http://itunes.apple.com/lookup?bundleId=\(identifier)"  response
    // here version and trackViewUrl are key of URL response
    // so you can add all key beased on your requirement.

}

class ArgAppUpdater: NSObject {
    private static var _instance: ArgAppUpdater?;

    private override init() {

    }

    public static func getSingleton() -> ArgAppUpdater {
        if (ArgAppUpdater._instance == nil) {
            ArgAppUpdater._instance = ArgAppUpdater.init();
        }
        return ArgAppUpdater._instance!;
    }

    private func getAppInfo(completion: @escaping (AppInfo?, Error?) -> Void) -> URLSessionDataTask? {
        guard let identifier = Bundle.main.infoDictionary?["CFBundleIdentifier"] as? String,
            let url = URL(string: "http://itunes.apple.com/lookup?bundleId=\(identifier)") else {
                DispatchQueue.main.async {
                    completion(nil, VersionError.invalidBundleInfo)
                }
                return nil
        }
        let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
            do {
                if let error = error { throw error }
                guard let data = data else { throw VersionError.invalidResponse }

                print("Data:::",data)
                print("response###",response!)

                let result = try JSONDecoder().decode(LookupResult.self, from: data)

                let dictionary = try? JSONSerialization.jsonObject(with: data, options: .mutableLeaves)

                print("dictionary",dictionary!)


                guard let info = result.results.first else { throw VersionError.invalidResponse }
                print("result:::",result)
                completion(info, nil)
            } catch {
                completion(nil, error)
            }
        }
        task.resume()

        print("task ******", task)
        return task
    }
    private  func checkVersion(force: Bool) {
        let info = Bundle.main.infoDictionary
        let currentVersion = info?["CFBundleShortVersionString"] as? String
        _ = getAppInfo { (info, error) in

            let appStoreAppVersion = info?.version

            if let error = error {
                print(error)



            }else if appStoreAppVersion!.compare(currentVersion!, options: .numeric) == .orderedDescending {
                //                print("needs update")
               // print("hiiii")
                DispatchQueue.main.async {
                    let topController: UIViewController = UIApplication.shared.keyWindow!.rootViewController!

                    topController.showAppUpdateAlert(Version: (info?.version)!, Force: force, AppURL: (info?.trackViewUrl)!)
            }

            }
        }


    }

    func showUpdateWithConfirmation() {
        checkVersion(force : false)


    }

    func showUpdateWithForce() {
        checkVersion(force : true)
    }



}

extension UIViewController {


    fileprivate func showAppUpdateAlert( Version : String, Force: Bool, AppURL: String) {
        print("AppURL:::::",AppURL)

        let bundleName = Bundle.main.infoDictionary!["CFBundleDisplayName"] as! String;
        let alertMessage = "\(bundleName) Version \(Version) is available on AppStore."
        let alertTitle = "New Version"


        let alertController = UIAlertController(title: alertTitle, message: alertMessage, preferredStyle: .alert)


        if !Force {
            let notNowButton = UIAlertAction(title: "Not Now", style: .default) { (action:UIAlertAction) in
                print("Don't Call API");


            }
            alertController.addAction(notNowButton)
        }

        let updateButton = UIAlertAction(title: "Update", style: .default) { (action:UIAlertAction) in
            print("Call API");
            print("No update")
            guard let url = URL(string: AppURL) else {
                return
            }
            if #available(iOS 10.0, *) {
                UIApplication.shared.open(url, options: [:], completionHandler: nil)
            } else {
                UIApplication.shared.openURL(url)
            }

        }

        alertController.addAction(updateButton)
        self.present(alertController, animated: true, completion: nil)
    }
}

참조 : https://stackoverflow.com/a/48810541/5855888https://github.com/emotality/ATAppUpdater

해피 코딩 👍 😊


@ 롭 GitHub의 링크 확인하시기 바랍니다 github.com/anupgupta-arg/iOS-Swift-ArgAppUpdater을
아누 프 굽타에게

2

다음은 Objective-C 답변 중 일부가 제안하는 것을 수행하는 신속한 방법입니다. 당연히, 앱 스토어 JSON에서 정보를 얻으면 원하는 경우 릴리스 노트를 추출 할 수 있습니다.

func appUpdateAvailable(storeInfoURL: String) -> Bool
{
    var upgradeAvailable = false

    // Get the main bundle of the app so that we can determine the app's version number
    let bundle = NSBundle.mainBundle()
    if let infoDictionary = bundle.infoDictionary {
        // The URL for this app on the iTunes store uses the Apple ID for the  This never changes, so it is a constant
        let urlOnAppStore = NSURL(string: storeInfoURL)
        if let dataInJSON = NSData(contentsOfURL: urlOnAppStore!) {
            // Try to deserialize the JSON that we got
            if let lookupResults = try? NSJSONSerialization.JSONObjectWithData(dataInJSON, options: NSJSONReadingOptions()) {
                // Determine how many results we got. There should be exactly one, but will be zero if the URL was wrong
                if let resultCount = lookupResults["resultCount"] as? Int {
                    if resultCount == 1 {
                        // Get the version number of the version in the App Store
                        if let appStoreVersion = lookupResults["results"]!![0]["version"] as? String {
                            // Get the version number of the current version
                            if let currentVersion = infoDictionary["CFBundleShortVersionString"] as? String {
                                // Check if they are the same. If not, an upgrade is available.
                                if appStoreVersion != currentVersion {
                                    upgradeAvailable = true                      
                                }
                            }
                        }
                    }
                }
            }
        }
    }

    return upgradeAvailable
}

storeInfoURL은 appstore에있는 앱의 URL입니까?
iamthevoid

@Mario Hendricks 이것은 신속한 3에서 작동하지 않습니다. 약간의 오류가 발생합니다. 스위프트 3을 업데이트 해 주시겠습니까?
George Asda

이 답변은 요청을 동 기적으로 만듭니다. 즉, 연결 상태가 좋지 않으면 요청이 반환 될 때까지 몇 분 동안 앱을 사용할 수 없습니다.
uliwitness

2

NSUrlRequest에서 콘텐츠 유형을 설정하지 않으면 응답을받지 못할 것이므로 아래 코드를 시도해보십시오. 도움이 되었기를 바랍니다 ....

-(BOOL) isUpdateAvailable{
    NSDictionary* infoDictionary = [[NSBundle mainBundle] infoDictionary];
    NSString* appID = infoDictionary[@"CFBundleIdentifier"];
    NSString *urlString = [NSString stringWithFormat:@"https://itunes.apple.com/lookup?bundleId=%@",appID];

    NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
    [request setURL:[NSURL URLWithString:urlString]];
    [request setHTTPMethod:@"GET"];
    [request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];

    NSURLResponse *response;
    NSError *error;
    NSData *data = [NSURLConnection  sendSynchronousRequest:request returningResponse: &response error: &error];
    NSError *e = nil;
    NSDictionary *jsonDict = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error: &e];

    self.versionInAppStore = [[[jsonDict objectForKey:@"results"] objectAtIndex:0] objectForKey:@"version"];

    self.localAppVersion = infoDictionary[@"CFBundleShortVersionString"];

    if ([self.versionInAppStore compare:self.localAppVersion options:NSNumericSearch] == NSOrderedDescending) {
        // currentVersion is lower than the version
        return YES;
    }
    return NO;
}

이 답변은 요청을 동 기적으로 만듭니다. 즉, 연결 상태가 좋지 않으면 요청이 반환 될 때까지 몇 분 동안 앱을 사용할 수 없습니다.
uliwitness

2

하이브리드 애플리케이션 POV에서 가져온 것은 자바 스크립트 예제이며, 기본 메뉴에 업데이트 사용 가능 바닥 글이 있습니다. 업데이트를 사용할 수있는 경우 (예 : 구성 파일 내의 내 버전 번호가 검색된 버전보다 낮 으면 바닥 글 표시) 그러면 사용자가 업데이트 버튼을 클릭 할 수있는 앱 스토어로 이동합니다.

또한 새로운 데이터 (예 : 릴리스 노트)를 가져 와서이 버전에서 처음 인 경우 로그인시 모달에 표시합니다.

Update Available 메서드는 원하는만큼 자주 실행할 수 있습니다. Mine은 사용자가 홈 화면으로 이동할 때마다 실행됩니다.

function isUpdateAvailable() {
        $.ajax('https://itunes.apple.com/lookup?bundleId=BUNDLEID', {
            type: "GET",
            cache: false,
            dataType: 'json'
        }).done(function (data) {
            _isUpdateAvailable(data.results[0]);
        }).fail(function (jqXHR, textStatus, errorThrown) {
            commsErrorHandler(jqXHR, textStatus, false);
        });

}

콜백 : Apple에는 API가 있으므로 쉽게 얻을 수 있습니다.

function isUpdateAvailable_iOS (data) {
    var storeVersion = data.version;
    var releaseNotes = data.releaseNotes;
    // Check store Version Against My App Version ('1.14.3' -> 1143)
    var _storeV = parseInt(storeVersion.replace(/\./g, ''));
    var _appV = parseInt(appVersion.substring(1).replace(/\./g, ''));
    $('#ft-main-menu-btn').off();
    if (_storeV > _appV) {
        // Update Available
        $('#ft-main-menu-btn').text('Update Available');
        $('#ft-main-menu-btn').click(function () {
           // Open Store      
           window.open('https://itunes.apple.com/us/app/appname/idUniqueID', '_system');
        });

    } else {
        $('#ft-main-menu-btn').html('&nbsp;');
        // Release Notes
        settings.updateReleaseNotes('v' + storeVersion, releaseNotes);
    }
}

2

경고 : 제공된 대부분의 답변은 URL을 동기식으로 검색합니다 ( -dataWithContentsOfURL:또는을 사용 -sendSynchronousRequest:합니다. 요청이 진행되는 동안 모바일 연결이 끊어지면 애플리케이션이 몇 분 동안 응답하지 않음을 의미하기 때문입니다 . 메인 스레드.

정답은 비동기 API를 사용하는 것입니다.

    NSDictionary* infoDictionary = [[NSBundle mainBundle] infoDictionary];
    NSString* appID = infoDictionary[@"CFBundleIdentifier"];
    NSURL* url = [NSURL URLWithString:[NSString stringWithFormat:@"http://itunes.apple.com/lookup?bundleId=%@", appID]];
    NSURLSession         *  session = [NSURLSession sharedSession];
    NSURLSessionDataTask *  theTask = [session dataTaskWithRequest: [NSURLRequest requestWithURL: url] completionHandler:
    ^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error)
    {
        NSDictionary<NSString*,NSArray*>* lookup = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
        if ([lookup[@"resultCount"] integerValue] == 1)
        {
            NSString* appStoreVersion = lookup[@"results"].firstObject[@"version"];
           NSString* currentVersion = infoDictionary[@"CFBundleShortVersionString"];

            if ([appStoreVersion compare:currentVersion options:NSNumericSearch] == NSOrderedDescending) {
                // *** Present alert about updating to user ***
            }
        }
    }];
    [theTask resume];

네트워크 연결에 대한 기본 제한 시간은 몇 분이며 요청이 통과하더라도 잘못된 EDGE 연결을 통해 시간이 오래 걸릴 수 있습니다. 이 경우 앱을 사용할 수 없게되는 것을 원하지 않습니다. 이와 같은 것을 테스트하려면 Apple의 Network Link Conditioner를 사용하여 네트워킹 코드를 실행하는 것이 유용합니다.


:-) 살아있는이 질문을 유지 주셔서 감사합니다
byJeevan

2
func isUpdateAvailable() -> Bool {
    guard
        let info = Bundle.main.infoDictionary,
        let identifier = info["CFBundleIdentifier"] as? String,
        let url = URL(string: "http://itunes.apple.com/lookup?bundleId=\(identifier)"),
        let data = try? Data(contentsOf: url),
        let json = try? JSONSerialization.jsonObject(with: data, options: .allowFragments) as? [String: Any],
        let results = json?["results"] as? [[String: Any]],
        results.count > 0,
        let versionString = results[0]["version"] as? String
        else {
            return false
    }

    return AppVersion(versionString) > AppVersion.marketingVersion
}

버전 문자열 비교 :

https://github.com/eure/AppVersionMonitor


2

SWIFT 4 및 3.2의 경우 :

먼저, 번들 정보 사전에서 번들 ID를 가져 와서 isUpdaet을 false로 설정해야합니다.

    var isUpdate = false
    guard let bundleInfo = Bundle.main.infoDictionary,
        let currentVersion = bundleInfo["CFBundleShortVersionString"] as? String,
        //let identifier = bundleInfo["CFBundleIdentifier"] as? String,
        let url = URL(string: "http://itunes.apple.com/lookup?bundleId=\(identifier)")
        else{
        print("something wrong")
            completion(false)
        return
       }

그런 다음 iTunes에서 버전을 가져 오기 위해 urlSession 호출을 호출해야합니다.

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

전체 코드는 다음과 같습니다.

func checkForUpdate(completion:@escaping(Bool)->()){

    guard let bundleInfo = Bundle.main.infoDictionary,
        let currentVersion = bundleInfo["CFBundleShortVersionString"] as? String,
        //let identifier = bundleInfo["CFBundleIdentifier"] as? String,
        let url = URL(string: "http://itunes.apple.com/lookup?bundleId=\(identifier)")
        else{
        print("some thing wrong")
            completion(false)
        return
       }

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

그런 다음 필요한 함수를 호출 할 수 있습니다.

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

2

Apple App Store 버전을 얻는 것만 큼 @datinc의 C # 동등성. 번들 또는 AssemblyInfo 파일의 버전을 얻기위한 코드가 포함되었습니다.

편집 :: urlString에 포함 된 "/ us /"지역을 참고하십시오. 이 국가 코드는 그에 따라 처리 / 변경해야합니다.

string GetAppStoreVersion()
{
    string version = "";

    NSDictionary infoDictionary = NSBundle
        .MainBundle
        .InfoDictionary;

    String appID = infoDictionary["CFBundleIdentifier"].ToString();

    NSString urlString = 
        new NSString(@"http://itunes.apple.com/us/lookup?bundleId=" + appID);
    NSUrl url = new NSUrl(new System.Uri(urlString).AbsoluteUri);

    NSData data = NSData.FromUrl(url);

    if (data == null)
    {
        /* <-- error obtaining data from url --> */
        return "";
    }

    NSError e = null;
    NSDictionary lookup = (NSDictionary)NSJsonSerialization
        .Deserialize(data, NSJsonReadingOptions.AllowFragments, out e);

    if (lookup == null)
    {
        /* <-- error, most probably no internet or bad connectivity --> */
        return "";
    }

    if (lookup["resultCount"].Description.Equals("1"))
    {
        NSObject nsObject = lookup["results"];
        NSString nsString = new NSString("version");
        String line = nsObject
            .ValueForKey(nsString)
            .Description;

        /* <-- format string --> */
        string[] digits = Regex.Split(line, @"\D+");
        for (int i = 0; i < digits.Length; i++)
        {
            if (int.TryParse(digits[i], out int intTest))
            {
                if (version.Length > 0)
                    version += "." + digits[i];
                else
                    version += digits[i];
            }
        }
    }

    return version;
}

string GetBundleVersion()
{
        return NSBundle
            .MainBundle
            .InfoDictionary["CFBundleShortVersionString"]
            .ToString();
}

string GetAssemblyInfoVersion()
{
        var assembly = typeof(App).GetTypeInfo().Assembly;
        var assemblyName = new AssemblyName(assembly.FullName);
        return assemblyName.Version.ToString();
}

2

단일 함수 호출로 이것을 시도하십시오.

func showAppStoreVersionUpdateAlert(isForceUpdate: Bool) {

    do {
        //Get Bundle Identifire from Info.plist
        guard let bundleIdentifire = Bundle.main.infoDictionary?["CFBundleIdentifier"] as? String else {
            print("No Bundle Info found.")
            throw CustomError.invalidIdentifires
        }

        // Build App Store URL
        guard let url = URL(string:"http://itunes.apple.com/lookup?bundleId=" + bundleIdentifire) else {
            print("Isse with generating URL.")
            throw CustomError.invalidURL
        }

        let serviceTask = URLSession.shared.dataTask(with: url) { (responseData, response, error) in

            do {
                // Check error
                if let error = error { throw error }
                //Parse response
                guard let data = responseData else { throw CustomError.jsonReading }
                let result = try? JSONSerialization.jsonObject(with: data, options: .allowFragments)
                let itunes = ItunesAppInfoItunes.init(fromDictionary: result as! [String : Any])
                print(itunes.results)
                if let itunesResult = itunes.results.first {
                    print("App Store Varsion: ",itunesResult.version)

                    //Get Bundle Version from Info.plist
                    guard let appShortVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String else {
                        print("No Short Version Info found.")
                        throw CustomError.invalidVersion
                    }

                    if appShortVersion == itunesResult.version {
                        //App Store & Local App Have same Version.
                        print("Same Version at both side")
                    } else {
                        //Show Update alert
                        var message = ""
                        //Get Bundle Version from Info.plist
                        if let appName = Bundle.main.infoDictionary?["CFBundleName"] as? String {
                            message = "\(appName) has new version(\(itunesResult.version!)) available on App Store."
                        } else {
                            message = "This app has new version(\(itunesResult.version!)) available on App Store."
                        }

                        //Show Alert on the main thread
                        DispatchQueue.main.async {
                            self.showUpdateAlert(message: message, appStoreURL: itunesResult.trackViewUrl, isForceUpdate: isForceUpdate)
                        }
                    }
                }
            } catch {
                print(error)
            }
        }
        serviceTask.resume()
    } catch {
        print(error)
    }
}

AppStore URL을 여는 경고 기능 :

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

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

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

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

    }))

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

}

위의 함수를 호출하는 방법 :

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

자세한 내용은 전체 코드로 아래 링크를 시도하십시오.

AppStoreUpdate.swift

ItunesAppInfoResult.swift

ItunesAppInfoItunes.swift

도움이 되었기를 바랍니다.


1

이 질문은 2011 년에 받았고, 2018 년에 앱 스토어에서 새로운 버전의 앱을 확인하는 것뿐만 아니라 사용자에게 알리는 방법을 찾는 동안 발견했습니다.

작은 연구 끝에 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
    }
  • 또한 새 버전에 대한 다양한 유형의 알림을 표시 할 수 있습니다 (버전을 건너 뛰거나 사용자가 업데이트하도록 할 수 있음).
  • 버전 확인 빈도를 지정할 수 있습니다 (매일 / 매주 / 즉시)
  • 새 버전이 앱 스토어에 출시 된 후 알림이 표시 될 기간을 지정할 수 있습니다.

기존 답변에 대한 링크는 답변이 아닙니다. 또한 링크가 질문에 답변하는 방법 (코드 예제 추가 등)을 명시 적으로 추가하지 않는 한 라이브러리에 대한 링크도 답변이 아닙니다.
JAL

1

스위프트 4

new JSONDecoder를 사용하여 itunes.apple.com/lookup 의 응답을 구문 분석하고 Decodable 클래스 또는 구조체로 나타낼 수 있습니다.

class LookupResult: Decodable {
    var results: [AppInfo]
}

class AppInfo: Decodable {
    var version: String
}

또는 다른 속성이 AppInfo필요한 경우 다른 속성을 추가 할 수도 있습니다 releaseNotes.

이제 다음을 사용하여 비동기 요청을 할 수 있습니다 URLSession.

func getAppInfo(completion: @escaping (AppInfo?, Error?) -> Void) -> URLSessionDataTask? {
    guard let identifier = Bundle.main.infoDictionary?["CFBundleIdentifier"] as? String,
          let url = URL(string: "http://itunes.apple.com/lookup?bundleId=\(identifier)") else {
            DispatchQueue.main.async {
                completion(nil, VersionError.invalidBundleInfo)
            }
            return nil
    }
    let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
        do {
            if let error = error { throw error }
            guard let data = data else { throw VersionError.invalidResponse }
            let result = try JSONDecoder().decode(LookupResult.self, from: data)
            guard let info = result.results.first else { throw VersionError.invalidResponse }

            completion(info, nil)
        } catch {
            completion(nil, error)
        }
    }
    task.resume()
    return task
}

enum VersionError: Error {
    case invalidBundleInfo, invalidResponse
}

이 함수는 요청이 완료 될 때 호출 될 완료 클로저를 수신하고 요청 URLSessionDataTask을 취소해야하는 경우를 반환 하며 다음과 같이 호출 할 수 있습니다.

func checkVersion() {
    let info = Bundle.main.infoDictionary
    let currentVersion = info?["CFBundleShortVersionString"] as? String
    _ = getAppInfo { (info, error) in
        if let error = error {
            print(error)
        } else if info?.version == currentVersion {
            print("updated")
        } else {
            print("needs update")
        }
    }
}

이 코드를 어디에 넣었습니까? LookupResult 및 AppInfo를 디코딩 가능으로 설정했지만 어디에도 저장되어 있지 않습니다. 내가 여기서 무엇을 놓치고 있습니까?
jessi

프로젝트의 어딘가에 LookupResultAppInfo클래스를 별도의 파일로 선언하는 것이 좋습니다. 응답을 디코딩 할 때 사용됩니다 JSONDecoder().decode(LookupResult.self, from: data). 버전 문자열이 포함되어 있습니다
juanjo

귀하의 답변을 기반으로 귀하의 코드를 사용하여 하나의 파일을 만듭니다. iOS-Swift-ArgAppUpdater
Anup Gupta

@jessi 제발 GitHub의 내 코드를 확인하십시오. 내가 거기에 귀하의 솔루션을 게시했습니다
Anup Gupta

0

내 코드 제안. @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())
    }
}

1
이 답변은 요청을 동 기적으로 만듭니다. 즉, 연결 상태가 좋지 않으면 요청이 반환 될 때까지 몇 분 동안 앱을 사용할 수 없습니다.
uliwitness

-1

신속한 3 업데이트 :

앱의 현재 버전을 확인하려면 간단한 코드 아래에 사용됩니다.

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

  let version = object as! String
  print("version: \(version)")
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.