Periksa apakah aplikasi saya memiliki versi baru di AppStore

112

Saya ingin memeriksa secara manual apakah ada pembaruan baru untuk aplikasi saya saat pengguna berada di dalamnya, dan memintanya untuk mengunduh versi baru. Dapatkah saya melakukan ini dengan memeriksa versi aplikasi saya di App Store - secara terprogram?

pengguna542584
sumber
6
Anda bisa meletakkan halaman acak di server web yang hanya mengembalikan representasi string dari versi terbaru. Unduh dan bandingkan saat aplikasi dimulai dan beri tahu pengguna. (Cara cepat dan mudah)
LouwHopley
1
terima kasih, tapi saya berharap untuk solusi yang lebih baik seperti semacam API yang dapat saya panggil fungsi app store, seperti mencari nomor aplikasi saya dan mendapatkan data versinya. Menghemat waktu untuk memelihara server web hanya untuk tujuan ini, tetapi terima kasih atas penunjuknya!
pengguna542584
Saya melakukan hal yang sama seperti komentar pertama. Saya menulis plist dengan satu entri: NSNumbernomor versi. Kemudian saya mengunggahnya ke situs web saya. Situs web yang sama yang saya gunakan untuk dukungan aplikasi dan halaman web aplikasi, lalu masuk viewDidLoad, saya memeriksa situs web untuk nomor versi di sana dan saya memeriksa versi saat ini di aplikasi saya. Lalu saya memiliki premade alertViewyang secara otomatis meminta untuk memperbarui aplikasi. Saya dapat memberikan kode jika Anda mau.
Andrew
terima kasih, saya rasa saya harus mencobanya juga ..
user542584

Jawaban:

88

Berikut ini potongan kode sederhana yang memberi tahu Anda jika versi saat ini berbeda

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

Catatan: Pastikan bahwa saat Anda memasukkan versi baru di iTunes, ini cocok dengan versi di aplikasi yang Anda rilis. Jika tidak maka kode di atas akan selalu mengembalikan YA terlepas dari apakah pengguna memperbarui.

datinc
sumber
4
solusi super yang pernah saya temukan +1
Sanjay Changani
1
@MobeenAfzal, saya rasa anda kurang paham soal pertanyaan dan solusinya. Solusi di atas membandingkan versi saat ini dengan versi di toko. Jika mereka tidak cocok maka itu mengembalikan YA, kalau tidak ia mengembalikan TIDAK. Tidak peduli riwayat di toko aplikasi, metode di atas akan mengembalikan YA jika versi saat ini berbeda dari versi toko aplikasi. Setelah pengguna memperbarui ... versi saat ini sama dengan versi toko aplikasi. Metode di atas harus selalu mengembalikan YA jika versi pengguna adalah 1.0 dan versi toko aplikasi 1.2.
tanggal
1
@MobeenAfzal Saya rasa saya mengerti apa yang Anda lihat. Dalam kode versi Anda adalah 1.7, Tapi di iTunes Anda mengunggah versi 1.6 sehingga pengguna Anda tidak tahu bahwa Anda melewatkan sebuah versi. Apakah itu masalahnya? Jika demikian maka ... yang Anda butuhkan adalah server (DropBox akan melakukannya) untuk melayani nomor versi aplikasi Anda dan mengubah kode Anda untuk mengakses titik akhir itu. Beri tahu saya jika ini yang Anda lihat dan saya akan menambahkan catatan peringatan ke pos.
tanggal
1
@MobeenAfzal Anda komentar Anda menyesatkan. Jika versi pada perangkat pengguna dipisahkan oleh salah satu dari versi di appstore maka kode akan mengembalikan YA seperti yang diharapkan. Bahkan jika Anda merilis versi 1.0 diikuti oleh versi 1.111 itu masih akan bekerja dengan sempurna.
tanggal
1
Kita harus menampilkan pembaruan hanya jika versi appstore lebih besar dari versi saat ini sebagai berikut. if ([perbandingan appStoreVersion: opsi currentVersion: NSNumericSearch] == NSOrderedDescending) {NSLog (@ "\ n \ nPerlu memperbarui. Versi Appstore% @ lebih besar dari% @", appStoreVersion, currentVersion); }
Nitesh Borad
52

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

Saya pikir lebih baik melempar kesalahan daripada mengembalikan false, dalam hal ini saya membuat VersionError tetapi bisa juga yang lain Anda tentukan atau NSError

enum VersionError: Error {
    case invalidResponse, invalidBundleInfo
}

Pertimbangkan juga untuk memanggil fungsi ini dari thread lain, jika koneksi lambat dapat memblokir thread saat ini.

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

Memperbarui

Menggunakan URLSession:

Alih-alih menggunakan Data(contentsOf: url)dan memblokir utas, kita dapat menggunakan 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
}

contoh:

_ = try? isUpdateAvailable { (update, error) in
    if let error = error {
        print(error)
    } else if let update = update {
        print(update)
    }
}
juanjo
sumber
1
Jawaban ini membuat permintaannya serempak. Artinya, dengan koneksi yang buruk, aplikasi Anda mungkin tidak dapat digunakan selama beberapa menit hingga permintaan kembali.
uliwitness
4
Saya tidak setuju, DispatchQueue.global()memberi Anda antrian latar belakang, data dimuat dalam antrian itu dan hanya kembali ke antrian utama saat data dimuat.
juanjo
Ups. Entah bagaimana saya mengabaikan potongan kode kedua itu. Sayangnya, sepertinya saya tidak dapat menghapus downvote sampai jawaban Anda diedit lagi :-( BTW - Given dataWithContentsOfURL: benar-benar melalui panggilan sinkron NSURLConnection, yang pada gilirannya hanya memulai utas dan blok asinkron, mungkin overhead lebih sedikit untuk hanya menggunakan panggilan NSURLSession asinkron. Mereka bahkan akan memanggil Anda kembali ke utas utama setelah Anda selesai.
uliwitness
@juanjo ,,,, tidak berfungsi untuk swift 3.0.1, bisakah Anda mengunggah pembaruan untuk cepat ???
Kiran jadhav
2
Perhatikan jika Anda hanya terdaftar di toko tertentu, saya menemukan bahwa Anda perlu menambahkan kode negara ke URL - mis. GB itunes.apple.com/(countryCode)/… )
Ryan Heitner
13

Terima kasih kepada Steve Moser untuk tautannya, ini kode saya:

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"];
Roozbeh Zabihollahi
sumber
1
solusi yang sangat baik dan benar, hanya sedikit pembaruan mengenai url adalah itunes.apple.com/en/lookup?bundleId=xxxxxxxxxx
SJ
Terima kasih, komentar Anda diterapkan
Roozbeh Zabihollahi
4
Sebenarnya itu tidak berhasil untuk saya dengan /en/subpath. Setelah menghapusnya, itu berhasil
gasparuff
Jawaban ini membuat permintaannya serempak. Artinya, dengan koneksi yang buruk, aplikasi Anda mungkin tidak dapat digunakan selama beberapa menit hingga permintaan kembali.
uliwitness
1
Saya harus menggunakan dengan / en / itunes.apple.com/lookup?bundleId=xxxxxxx , terima kasih @gasparuff
Fernando Perez
13

Karena saya menghadapi masalah yang sama, saya menemukan jawaban yang diberikan oleh Mario Hendricks . Tidak wajar ketika saya mencoba menerapkan kodenya pada proyek saya, XCode memang mengeluh tentang masalah Casting yang mengatakan "MDLMaterialProperty tidak memiliki anggota subskrip". Kodenya mencoba menyetel MDLMaterial ini ... sebagai jenis "lookupResult" konstan, membuat casting ke "Int" gagal setiap saat. Solusi saya adalah memberikan anotasi tipe untuk variabel saya ke NSDictionary agar jelas tentang jenis nilai yang saya butuhkan. Dengan itu, saya bisa mengakses nilai "versi" yang saya butuhkan.

Obs: Untuk YOURBUNDLEID ini , Anda bisa mendapatkan dari proyek Xcode Anda .... " T Target> General> Identity> Bundle Identifier "

Jadi, inilah kode saya dengan beberapa penyederhanaan juga:

  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
}

Semua saran untuk perbaikan kode ini diterima!

Yago Zardo
sumber
Jawaban ini membuat permintaannya serempak. Artinya, dengan koneksi yang buruk, aplikasi Anda mungkin tidak dapat digunakan selama beberapa menit hingga permintaan kembali.
uliwitness
@Yago Zardo silakan gunakan fungsi perbandingan jika tidak ketika pengguna mengunggah aplikasi. Apple menguji waktu tampilan pembaruan alertview atau apel menolak aplikasi Anda
Jigar Darji
Hai @ Jigar, terima kasih atas sarannya. Saat ini saya tidak menggunakan metode ini lagi di aplikasi saya karena sekarang kami sedang membuat versi semua yang ada di server kami. Bagaimanapun, bisakah Anda menjelaskan lebih baik apa yang Anda katakan? Saya tidak mengerti dan sepertinya hal yang baik untuk diketahui. Terima kasih sebelumnya.
Yago Zardo
Terima kasih @uliwitness atas tipnya, ini sangat membantu saya untuk meningkatkan kode saya secara umum untuk mempelajari tentang permintaan asinkron dan sinkron.
Yago Zardo
Tautan itu luar biasa!
B3none
13

Cukup gunakan ATAppUpdater . Ini adalah 1 baris, aman untuk benang dan cepat. Ini juga memiliki metode delegasi jika Anda ingin melacak tindakan pengguna.

Berikut ini contohnya:

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

   return YES;
}

Metode delegasi opsional:

- (void)appUpdaterDidShowUpdateDialog;
- (void)appUpdaterUserDidLaunchAppStore;
- (void)appUpdaterUserDidCancel;
emosi
sumber
1
Akankah ini berfungsi untuk versi beta di Testflight? Jika tidak, apakah ada alat yang bisa digunakan?
Lukasz Czerwinski
Tidak, ini hanya membandingkan versi saat ini dengan versi terbaru yang ada di AppStore.
emosionalitas
Bisakah kita menggunakan ini dengan Swift?
Zorayr
11

Menyederhanakan jawaban bagus yang diposting di utas ini. Menggunakan Swift 4dan 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
    }
  }

}

Dan kemudian menggunakannya:

VersionCheck.shared.isUpdateAvailable() { hasUpdates in
  print("is update available: \(hasUpdates)")
}
budidino
sumber
2
Aplikasi saya aktif di toko tetapi api yang sama tidak mengembalikan informasi versi. Tanggapan:{ "resultCount":0, "results": [] }
technerd
Hanya menambahkan catatan ke perbandingan versi, saya lebih suka, biarkan serverVersion = "2.7" let localVersion = "2.6.5" let isUpdateAvailable = serverVersion.compare (localVersion, options: .numeric) == .orderedDescending daripada mengganti. dengan kosong.
Chaitu
@Chaitu terima kasih atas sarannya. Saya akhirnya menulis ulang bagian perbandingan dari kode
budidino
9

Memperbarui kode 4 swift dari Anup Gupta

Saya telah membuat beberapa perubahan pada kode ini . Sekarang fungsi dipanggil dari antrian latar belakang, karena koneksi bisa lambat dan karena itu memblokir utas utama.

Saya juga membuat CFBundleName opsional, karena versi yang disajikan memiliki "CFBundleDisplayName" yang mungkin tidak berfungsi di versi saya. Jadi sekarang jika tidak ada itu tidak akan macet tetapi tidak akan menampilkan Nama Aplikasi dalam peringatan.

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

Saya melakukan panggilan ini untuk juga menambahkan tombol konfirmasi:

AppUpdater.shared.showUpdate(withConfirmation: true)

Atau sebut saja seperti ini untuk mengaktifkan opsi pembaruan paksa:

AppUpdater.shared.showUpdate(withConfirmation: false)
Vasco
sumber
Ada ide tentang cara menguji ini? Jika gagal berfungsi dengan benar, satu-satunya cara untuk men-debugnya adalah dengan men-debug versi yang lebih lama daripada yang ada di app store.
David Rektor
2
Ah, sudahlah pertanyaannya. Saya cukup mengubah versi lokal saya menjadi "lebih lama".
David Rektor
Saya terkesan dengan kode Anda @Vasco. Hanya pertanyaan sederhana, mengapa Anda menggunakan 'http' daripada https di url itu?
Master AgentX
Terima kasih banyak telah membagikan solusi ini @Vasco! Saya menyukainya :) Mengapa Anda tidak menggunakan: let config = URLSessionConfiguration.background (withIdentifier: "com.example.MyExample.background") untuk URLSession untuk mendapatkan permintaan latar belakang?
mc_plectrum
Anda juga dapat menghilangkan force unwrap, karena Anda sudah memeriksa apakah let appStoreAppVersion = info? .Version dan sama untuk trackURL.
mc_plectrum
7

Ini adalah versi saya menggunakan Swift 4 dan pustaka Alamofire populer (saya tetap menggunakannya di aplikasi saya). Permintaan bersifat asinkron dan Anda dapat meneruskan panggilan balik untuk diberi tahu setelah selesai.

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

Penggunaannya sederhana seperti ini:

VersionCheck.shared.checkAppStore() { isNew, version in
        print("IS NEW VERSION AVAILABLE: \(isNew), APP STORE VERSION: \(version)")
    }
Kapten Utara
sumber
1
Masalah dengan penggunaan ourVersion! = appVersion adalah masalah ini terpicu saat tim Peninjau App Store memeriksa versi baru aplikasi. Kami mengonversi string versi tersebut menjadi angka dan kemudian isNew = appVersion> ourVersion.
budidino
@budidino Anda benar, saya baru saja menunjukkan pendekatan umum menggunakan Alamofire. Cara Anda menafsirkan versi tersebut sepenuhnya bergantung pada aplikasi dan struktur versi Anda.
Kapten Utara
Hanya menambahkan catatan ke perbandingan versi, saya lebih suka, biarkan serverVersion = "2.7" let localVersion = "2.6.5" let isUpdateAvailable = serverVersion.compare (localVersion, options: .numeric) == .orderedDescending daripada membandingkan dengan equal
Chaitu
6

Dapatkah saya menyarankan perpustakaan kecil ini: https://github.com/nicklockwood/iVersion

Tujuannya adalah untuk menyederhanakan penanganan sambungan jarak jauh untuk memicu pemberitahuan.

Andrea
sumber
3
Anda dapat memeriksa App Store secara langsung untuk mengetahui nomor Versi alih-alih menyimpan file plist di suatu tempat. Lihat jawaban ini: stackoverflow.com/a/6569307/142358
Steve Moser
1
iVersion sekarang menggunakan versi app store secara otomatis - Plist bersifat opsional jika Anda ingin menentukan catatan rilis yang berbeda dengan yang ada di iTunes, tetapi Anda tidak perlu menggunakannya.
Nick Lockwood
1
Kode ini dapat menggunakan beberapa perbaikan, tetapi jauh lebih baik daripada jawaban lain yang mengirimkan permintaan sinkron. Namun, cara melakukan threading adalah gaya yang buruk. Saya akan mengajukan masalah di Github.
uliwitness
Proyek ini sekarang tidak digunakan lagi 😢
Zorayr
5

Swift 3.1.0

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
}
Kassem Itani
sumber
Ini macet ketika Anda tidak memiliki koneksi internet. biarkan data = coba? Data (contentOf: url!) Akan mengembalikan nihil, dan di baris berikutnya Anda melakukan data!
Joris Mans
thx @JorisMans Saya akan memperbaruinya untuk tidak ada gangguan konektivitas internet
Kassem Itani
Jangan lakukan ini. Gunakan URLSession.
JAL
4

Jawaban ini adalah modifikasi dari jawaban datinc https://stackoverflow.com/a/25210143/2735358 .

fungsi datinc membandingkan versi dengan perbandingan string. Jadi, itu tidak akan membandingkan versi lebih dari atau kurang dari.

Tapi, fungsi yang dimodifikasi ini membandingkan versi NSNumericSearch (perbandingan numerik) .

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

Menggunakan:

[self checkForUpdateWithHandler:^(BOOL isUpdateAvailable) {
    if (isUpdateAvailable) {
        // show alert
    }
}];
Nitesh Borad
sumber
3
Jawaban ini membuat permintaannya serempak. Artinya, dengan koneksi yang buruk, aplikasi Anda mungkin tidak dapat digunakan selama beberapa menit hingga permintaan kembali.
uliwitness
NSURLSession bekerja pada utas latar belakang secara otomatis kecuali kami menentukan sebaliknya.
Sebastian Dwornik
4

Saya melihat banyak cara untuk memeriksa pembaruan Aplikasi. jadi berdasarkan banyak jawaban saya mencampurnya dan membuat solusi saya yang tersedia di GitHub Jika Ada pembaruan yang diperlukan Tolong beri tahu saya. Kode ini untuk Swift 4

Tautan GitHub Ke kode ini. 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 Dan https://github.com/emotality/ATAppUpdater

Selamat Coding 👍 😊

Anup Gupta
sumber
@Rob Silakan Periksa Tautan GitHub github.com/anupgupta-arg/iOS-Swift-ArgAppUpdater
Anup Gupta
3

Coba ini dengan satu panggilan fungsi:

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

Fungsi Peringatan untuk membuka 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)

}

Bagaimana memanggil fungsi di atas:

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

Untuk lebih detail coba link di bawah ini dengan kode lengkap:

AppStoreUpdate.swift

ItunesAppInfoResult.swift

ItunesAppInfoItunes.swift

Saya harap ini akan membantu!

CodeChanger
sumber
2

Berikut adalah metode cepat yang melakukan apa yang disarankan oleh beberapa jawaban Objective-C. Tentunya, setelah Anda mendapatkan info dari toko aplikasi JSON, Anda dapat mengekstrak catatan rilis, jika Anda menginginkannya.

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
}
Mario Hendricks
sumber
storeInfoURL adalah url aplikasi di appstore?
iamthevoid
@ Mario Hendricks ini tidak bekerja dengan cepat 3. Ini menimbulkan beberapa kesalahan. Bisakah Anda memperbarui untuk swift 3?
George Asda
Jawaban ini membuat permintaannya serempak. Artinya, dengan koneksi yang buruk, aplikasi Anda mungkin tidak dapat digunakan selama beberapa menit hingga permintaan kembali.
uliwitness
2

Jika Anda tidak mengatur jenis konten di NSUrlRequest maka pasti Anda tidak akan mendapatkan respons, jadi coba kode di bawah ini berfungsi dengan baik untuk saya. Semoga membantu ....

-(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;
}
ganka
sumber
Jawaban ini membuat permintaannya serempak. Artinya, dengan koneksi yang buruk, aplikasi Anda mungkin tidak dapat digunakan selama beberapa menit hingga permintaan kembali.
uliwitness
2

Berasal dari POV Aplikasi Hybrid, ini adalah contoh javascript, saya memiliki footer yang Tersedia Pembaruan di menu utama saya. Jika pembaruan tersedia (mis. Nomor versi saya dalam file konfigurasi kurang dari versi yang diambil, tampilkan footer) Ini kemudian akan mengarahkan pengguna ke toko aplikasi, di mana pengguna kemudian dapat mengklik tombol perbarui.

Saya juga mendapatkan data baru apa (yaitu Catatan Rilis) dan menampilkannya sebagai modal saat login jika ini pertama kali pada versi ini.

Metode Perbarui Tersedia dapat dijalankan sesering Anda suka. Milik saya dijalankan setiap kali pengguna menavigasi ke layar beranda.

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

}

Callback: Apple memiliki API, jadi sangat mudah didapat

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);
    }
}
tyler_mitchell
sumber
2

Peringatan: Sebagian besar jawaban yang diberikan mengambil URL secara sinkron (menggunakan -dataWithContentsOfURL:atau -sendSynchronousRequest:. Ini buruk, karena itu berarti aplikasi Anda tidak akan responsif selama beberapa menit jika koneksi seluler terputus saat permintaan sedang berlangsung. Jangan pernah melakukan akses internet secara sinkron di utas utama.

Jawaban yang benar adalah dengan menggunakan API asinkron:

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

Batas waktu default untuk koneksi jaringan adalah beberapa menit., Dan bahkan jika permintaan berhasil, ini bisa menjadi cukup lambat melalui koneksi EDGE yang buruk untuk memakan waktu selama itu. Anda tidak ingin aplikasi Anda tidak dapat digunakan dalam kasus itu. Untuk menguji hal-hal seperti ini, akan berguna untuk menjalankan kode jaringan Anda dengan Kondisioner Tautan Jaringan Apple.

uliwitness
sumber
Terima kasih telah menjaga pertanyaan ini tetap hidup :-)
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
}

untuk membandingkan string versi:

https://github.com/eure/AppVersionMonitor

Lova
sumber
2

UNTUK SWIFT 4 dan 3.2:

Pertama, kita perlu mendapatkan id bundel dari kamus info bundel, setel isUpdaet sebagai 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
       }

Kemudian kita perlu memanggil panggilan urlSession untuk mendapatkan versi dari 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()

KODE LENGKAP AKAN SEPERTI INI:

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

Kemudian kita bisa memanggil fungsi anyware yang kita butuhkan.

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

Kesetaraan C # dari @datinc, sebanyak mendapatkan versi Apple App Store. Kode yang disertakan untuk mendapatkan versi bundel atau file AssemblyInfo.

EDIT :: Harap perhatikan wilayah, "/ us /", termasuk dalam urlString. Kode negara ini perlu ditangani / diubah sesuai.

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

Pertanyaan ini ditanyakan pada tahun 2011, saya menemukannya pada tahun 2018 saat mencari cara untuk tidak hanya memeriksa versi baru aplikasi di App Store tetapi juga untuk memberi tahu pengguna tentangnya.

Setelah penelitian kecil, saya sampai pada kesimpulan bahwa jawaban juanjo (terkait dengan Swift 3) https://stackoverflow.com/a/40939740/1218405 adalah solusi optimal jika Anda ingin melakukan ini dalam kode sendiri

Saya juga dapat menyarankan dua proyek hebat di GitHub (masing-masing 2300+ bintang)

Contoh Sirene (AppDelegate.swift)

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

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

      return true
    }
  • Anda juga dapat menampilkan berbagai jenis peringatan tentang versi baru (memungkinkan untuk melewati versi atau memaksa pengguna untuk memperbarui)
  • Anda dapat menentukan seberapa sering pemeriksaan versi harus dilakukan (harian / mingguan / segera)
  • Anda dapat menentukan berapa hari setelah versi baru dirilis ke app store, peringatan akan muncul
moonvader.dll
sumber
Tautan ke jawaban yang ada bukanlah jawaban. Selain itu, tautan ke perpustakaan juga bukan jawaban kecuali Anda secara eksplisit menambahkan bagaimana tautan tersebut menjawab pertanyaan ke jawaban Anda (tambahkan contoh kode, dll).
JAL
1

Cepat 4

Kita dapat menggunakan new JSONDecoderuntuk mengurai respon dari itunes.apple.com/lookup dan mewakilinya dengan kelas atau struct yang dapat didekodekan:

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

class AppInfo: Decodable {
    var version: String
}

Kita juga dapat menambahkan properti lain AppInfojika kita membutuhkan releaseNotesatau beberapa properti lain.

Sekarang kita bisa membuat permintaan async menggunakan 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
}

fungsi ini menerima penutupan penyelesaian yang akan dipanggil ketika permintaan selesai dan mengembalikan URLSessionDataTaskseandainya kita perlu membatalkan permintaan, dan bisa dipanggil seperti ini:

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")
        }
    }
}
juanjo
sumber
Di mana Anda meletakkan kode ini? Saya-saya melihat bahwa Anda menyetel LookupResult dan AppInfo ke decodable, tetapi saya tidak melihatnya disimpan di mana pun. Apa yang kulewatkan di sini?
jessi
Anda mendeklarasikan kelas LookupResultdan AppInfodi suatu tempat dalam proyek Anda, sebaiknya dalam file terpisah: Mereka digunakan saat Anda mendekode respons: JSONDecoder().decode(LookupResult.self, from: data)dan kelas tersebut berisi string versi
juanjo
Berdasarkan jawaban Anda, saya membuat Satu file menggunakan kode Anda. Harap periksa bahwa iOS-Swift-ArgAppUpdater
Anup Gupta
@jessi tolong periksa Kode saya di GitHub Saya memposting di sana solusi Anda
Anup Gupta
0

Proposal kode saya. Berdasarkan jawaban oleh @datinc dan @ Mario-Hendricks

Anda tentu saja harus mengganti dlog_Errordengan pemanggilan func call Anda.

Jenis struktur kode ini akan mencegah aplikasi Anda mogok jika terjadi kesalahan. Untuk mengambil appStoreAppVersionbukanlah keharusan, dan tidak menyebabkan kesalahan fatal. Namun, dengan struktur kode semacam ini, Anda masih akan mendapatkan log kesalahan non-fatal Anda.

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())
    }
}
SirEnder
sumber
1
Jawaban ini membuat permintaannya serempak. Artinya, dengan koneksi yang buruk, aplikasi Anda mungkin tidak dapat digunakan selama beberapa menit hingga permintaan kembali.
uliwitness
-1

Diperbarui untuk swift 3:

jika Anda ingin memeriksa versi aplikasi Anda saat ini, gunakan kode sederhana di bawah ini:

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

  let version = object as! String
  print("version: \(version)")
Kiran jadhav
sumber