Перевірте, чи в моєму додатку нова версія на AppStore


112

Я хотів би вручну перевірити, чи є нові оновлення для мого додатка, поки користувач знаходиться в ньому, і запропонувати йому завантажити нову версію. Чи можу це зробити, перевіривши версію свого додатка в магазині додатків - програмно?


6
Ви можете розмістити випадкову сторінку на веб-сервері, який повертає лише рядкове представлення останньої версії. Завантажте його та порівняйте після запуску програми та повідомте про це користувача. (Швидкий і простий спосіб)
LouwHopley

1
дякую, але я сподівався на краще рішення, як-то якийсь API, за допомогою якого я можу викликати функції магазину додатків, як-от пошук мого номера програми та отримання даних про версію. Економить час на підтримку веб-сервера саме для цієї мети, але все одно дякую за вказівник!
користувач542584

Я роблю те саме, що і перший коментар. Я написав список із одним записом: номер NSNumberверсії. Потім я завантажив його на свій веб-сайт. Той самий веб-сайт, який я використовую для своєї програми підтримки та веб-сторінок додатків, потім у viewDidLoad, я перевіряю веб-сайт на номер версії, і я перевіряю поточну версію в своєму додатку. Потім у мене є попередня пропозиція, alertViewяка автоматично запропонує оновити додаток. Я можу надати код, якщо хочете.
Андрій

дякую, я думаю, що я повинен спробувати і це ..
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, ця відповідність версії в програмі, яку ви випускаєте. Якщо ні, то вищезазначений код завжди повертає ТАК, незалежно від того, чи буде оновлений користувач.


4
супер рішення, яке я коли-небудь знайшов +1
Санджай Чангані

1
@MobeenAfzal, я думаю, ви пропустили розуміння питання та рішення. Вищевказане рішення порівнює поточну версію з версією магазину. Якщо вони не відповідають, то це відновлює ТАК, інакше він повертає НІ. Незалежно від історії в магазині додатків, описаний вище метод поверне ТАК, якщо поточна версія відрізняється від версії магазину додатків. Після оновлення користувача поточна версія дорівнює версії магазину додатків. Вищеописаний метод завжди повинен повертати ДА, якщо версія користувача 1,0, а версія магазину додатків - 1,2.
datinc

1
@MobeenAfzal Я думаю, я отримую те, що ти бачиш. У коді ваша версія 1,7, але в iTunes ви завантажили версію як 1.6, щоб ваші користувачі не знали, що ви пропустили версію. Це так? Якщо так, то ... вам потрібен сервер (DropBox, який би зробив), щоб обслуговувати номер версії ваших додатків та змінювати код для доступу до цієї кінцевої точки. Повідомте мене, чи це ви бачите, і я додам повідомлення про попередження до посади.
datinc

1
@MobeenAfzal, ваш коментар вводить в оману. Якщо версія на пристрої користувача відокремлена будь-якою від версії програми, тоді код поверне ТАК, як очікувалося. Навіть якщо ви випустите версію 1.0 з наступною версією 1.111, вона все одно буде працювати ідеально.
datinc

1
Ми повинні показувати оновлення лише тоді, коли версія додатка перевищує поточну версію, як показано нижче. якщо ([порівняння appStoreVersion: параметри currentVersion: NSNumericSearch] == NSOrderedDescending) {NSLog (@ "\ n \ nПотрібно оновити. Версія Appstore% @ перевищує% @", appStoreVersion, currentVersion); }
Нітеш Борад

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
}

Я думаю, що краще кинути помилку, а не повертати помилку, в цьому випадку я створив 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)
    }
}

Оновлення

Використання URL-сесії:

Замість використання 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 ???
Кіран джадхав

2
Зверніть увагу, якщо ви перераховані лише у певному магазині, я виявив, що вам потрібно додати код країни до URL - наприклад, GB itunes.apple.com/(countryCode)/… )
Ryan Heitner

13

Дякую Стіву Мозеру за його посилання, ось мій код:

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 Zabihollahi

4
Насправді це не спрацювало для мене з /en/підпутниками. Після вилучення він спрацював
gasparuff

Ця відповідь робить свій запит синхронно. Це означає, що при поганому з’єднанні ваш додаток може бути непридатним протягом декількох хвилин, поки запит не повернеться.
uliwitness

1
Мені довелося скористатися з / en / itunes.apple.com/lookup?bundleId=xxxxxxx , дякую @gasparuff
Фернандо Перес

13

Оскільки я зіткнувся з тією ж проблемою, я знайшов відповідь, яку надав Маріо Хендрікс . На жаль, коли я намагався застосувати його код у своєму проекті, XCode поскаржився на проблеми кастингу, кажучи: "У MDLMaterialProperty немає членів підписки". Його код намагався встановити цей MDLMaterial ... як тип постійного "lookupResult", що робить кастинг на "Int" невдалим кожен раз. Моє рішення полягало в наданні анотації типу для моєї змінної в NSDictionary, щоб було зрозуміло, яке саме значення мені потрібно. З цим я міг отримати доступ до потрібного мені значення "версія".

Висновки: для цього YOURBUNDLEID ви можете отримати свій проект Xcode .... " Цілі> Загальне> Ідентифікація> Ідентифікатор групи "

Отже, ось мій код з деякими спрощеннями:

  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 , будь ласка , використовуйте функцію порівняння в іншому випадку при випробуванні загрузках користувача app.apple час оновлення зображення на дисплеї alertview або яблуко відхилити ваше додаток
Jigar Дар'ї

Привіт @ Джигаре, дякую за пораду. Наразі я більше не використовую цей метод у своєму додатку, оскільки зараз ми оновлюємо все на нашому сервері. У будь-якому випадку, ви могли б краще пояснити, що ви сказали? Я не зрозумів, і це справді виглядає непогано знати. Заздалегідь спасибі.
Яго Зардо

Дякую @uliwitness за пораду, це дійсно допомогло мені покращити свій код взагалі, щоб дізнатися про асинхронні та синхронні запити.
Яго Зардо

Це посилання - це дорогоцінний камінь!
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? Якщо ні, чи є якийсь інструмент, який буде?
Лукаш Червінський

Ні, це не буде, він лише порівнює поточну версію з останньою версією, яка є в AppStore.
емоційність

Чи можемо ми використати це зі Свіфт?
Зорайр

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

Лише додаючи примітку до порівняння версій, я вважаю за краще, нехай serverVersion = "2.7" нехай localVersion = "2.6.5" нехай isUpdateAvailable = serverVersion.compare (localVersion, параметри: .numeric) ==. з порожнім.
Чайту

@Chaitu дякую за пропозицію. Я закінчив переписати порівняльну частину коду
budidino

9

Оновлено швидкий код 4 від Anup Gupta

Я вніс деякі зміни в цей код . Тепер функції викликаються з фонової черги, оскільки з'єднання може бути повільним і тому блокує основний потік.

Я також зробив 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)

Будь-які ідеї, як це перевірити? Якщо вона не працює належним чином, єдиний спосіб налагодити це - якось налагодити старішу версію, ніж це в магазині додатків.
Ректор

2
Ах, не забувайте про це питання. Я можу просто змінити свою локальну версію, щоб бути "старшою".
Давид Ректор

Я вражений вашим кодом @Vasco. Лише просте запитання: чому ви використовували "http" замість https у цій URL-адресі?
Master AgentX

Велике спасибі за те, що поділилися цим рішенням @Vasco! Мені це подобається :) Чому ви не використовуєте: нехай config = URLSessionConfiguration.background (withIdentifier: "com.example.MyExample.background") для URL-сесії для досягнення фонового запиту?
mc_plectrum

Ви також можете позбутися від вимкнення сили, як ви вже перевіряєте, якщо дозволити appStoreAppVersion = info? .Version та те саме для trackURL.
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

@budidino Ви маєте рацію, я просто показав загальний підхід за допомогою Alamofire. Те, як ви інтерпретуєте версію, повністю залежить від вашої програми та структури версій.
Північний капітан

Просто додати примітку до версії Comparision, я волів би, нехай serverVersion = "2,7" нехай localVersion = "2.6.5" нехай isUpdateAvailable = serverVersion.compare (localVersion, опції: .numeric) == .orderedDescending , а не по порівнянні з рівним
Чайту

6

Чи можу я запропонувати цю маленьку бібліотеку: https://github.com/nicklockwood/iVersion

Його мета - спростити поводження з віддаленими списками для запуску сповіщень.


3
Ви можете перевірити App Store безпосередньо на номер версії, а не десь розміщувати файл плісту. Ознайомтеся з цією відповіддю: stackoverflow.com/a/6569307/142358
Стів Мозер

1
Тепер iVersion автоматично використовує версію магазину додатків - Plist не є обов'язковим, якщо ви хочете вказати різні нотатки до випусків до приміток на iTunes, але не потрібно використовувати його.
Нік Локвуд

1
Цей код може використовувати деякі вдосконалення, але набагато краще, ніж інші відповіді, що надсилають синхронний запит. І все-таки те, що це робить різьблення - це поганий стиль. Я буду подавати проблеми на Github.
uliwitness

Зараз проект застарілий 😢
Зорайр

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
}

Цей збій відбувається, коли у вас немає підключення до Інтернету. нехай дані = спробуйте? Дані (contentOf: url!) Повернуть нуль, а в наступному рядку ви зробите дані!
Joris Mans

thx @JorisMans Я оновлю його, щоб не було збоїв в Інтернеті
Kassem 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 працює на фонових потоках автоматично, якщо не вказано інше.
Себастьян Дворник

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

Refrence: https://stackoverflow.com/a/48810541/5855888 І https://github.com/emotality/ATAppUpdater

Щасливе кодування 👍 😊


@Rob Перевірте посилання GitHub github.com/anupgupta-arg/iOS-Swift-ArgAppUpdater
Gupta

3

Спробуйте це за допомогою виклику однієї функції:

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

Функція сповіщення для відкриття URL-адреси AppStore:

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

Я сподіваюся, що це допоможе!


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 URL-адреса програми в appstore?
ямтевойд

@Mario Hendricks це не працює швидко. Це призводить до помилок. Чи можете ви оновити оновлення для швидкого 3?
Джордж Асда

Ця відповідь робить свій запит синхронно. Це означає, що при поганому з’єднанні ваш додаток може бути непридатним протягом декількох хвилин, поки запит не повернеться.
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, це приклад javascript, у мене в головному меню є нижній колонтитул оновлення. Якщо оновлення доступне (тобто номер моєї версії у конфігураційному файлі менший, ніж отримана версія, відобразить колонтитул). Це потім направить користувача в магазин додатків, де користувач може натиснути кнопку оновлення.

Я також отримую нові дані (тобто Примітки до випуску) і відображаю їх у модальному режимі входу, якщо це перший раз у цій версії.

Метод оновлення доступно можна використовувати так часто, як вам подобається. Шахта запускається щоразу, коли користувач переходить на головний екран.

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 Link Network Conditioner.


Дякуємо, що
зберегли

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:

По-перше, нам потрібно отримати ідентифікатор пакета з словника інформації про пакет, встановити isUpdaet як хибний.

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

2

C # еквівалентність @datinc, настільки ж, як отримання версії Apple App Store. Включений код для отримання версії як для пакета, так і для файлу AssemblyInfo.

EDIT :: Будь ласка, зверніть увагу на регіон "/ us /", включений до urlString. Цей код країни потрібно буде відповідно обробляти / змінювати.

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

1

Це запитання було задано у 2011 році. Я знайшов його у 2018 році, шукаючи якийсь спосіб не лише перевірити нову версію програми в App Store, але й повідомити про це користувача.

Після невеликих досліджень я прийшов до висновку, що відповідь juanjo (пов'язана зі Swift 3) https://stackoverflow.com/a/40939740/1218405 - це оптимальне рішення, якщо ви хочете зробити це в коді самостійно

Також я можу запропонувати два чудові проекти на GitHub (2300+ зірок кожна)

Приклад для сирени (AppDelegate.swift)

  func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {

      let siren = Siren.shared
      siren.checkVersion(checkType: .immediately)

      return true
    }
  • Ви також можете показувати різні типи сповіщень про нову версію (дозволяючи пропускати версію або змушувати користувача оновлюватись)
  • Ви можете вказати, як часто має відбуватися перевірка версій (щодня / щотижня / негайно)
  • Ви можете вказати, через скільки днів після появи нової версії в магазині додатків повинно з’явитися повідомлення

Посилання на існуючу відповідь - це не відповіді. Крім того, посилання на бібліотеки також не є відповідями, якщо ви прямо не додасте, як посилання відповідає на запитання до вашої відповіді (додайте приклади коду тощо).
JAL

1

Швидкий 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 для декодування, але я не бачу їх збережених ніде. Що я тут пропускаю?
jessi

Ви декларуєте LookupResultі AppInfoкласи десь у вашому проекті, бажано, в окремому файлі: Вони використовуються при розшифровці відповіді: JSONDecoder().decode(LookupResult.self, from: data)і містять рядок версії
juanjo

На основі вашої відповіді я створюю один файл за допомогою вашого коду. Перевірте, чи iOS-Swift-ArgAppUpdater
Gupta

@jessi, будь ласка, перевіри Мій код на GitHub Я розмістив там своє рішення
Ануп Гупта

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)")
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.