ตรวจสอบว่าแอปของฉันมีเวอร์ชันใหม่ใน AppStore หรือไม่


114

ฉันต้องการตรวจสอบด้วยตนเองว่ามีการอัปเดตใหม่สำหรับแอปของฉันในขณะที่ผู้ใช้ใช้งานอยู่หรือไม่และแจ้งให้เขาดาวน์โหลดเวอร์ชันใหม่ ฉันสามารถทำได้โดยตรวจสอบเวอร์ชันของแอพของฉันในแอพสโตร์ - แบบเป็นโปรแกรมหรือไม่?


7
คุณสามารถวางเพจแบบสุ่มบนเว็บเซิร์ฟเวอร์ซึ่งส่งคืนเฉพาะการแสดงสตริงของเวอร์ชันล่าสุด ดาวน์โหลดและเปรียบเทียบเมื่อเริ่มต้นแอปและแจ้งให้ผู้ใช้ทราบ (วิธีที่ง่ายและรวดเร็ว)
LouwHopley

1
ขอบคุณ แต่ฉันหวังว่าจะได้โซลูชันที่ดีกว่าเช่น API บางประเภทที่ฉันสามารถเรียกฟังก์ชันของแอพสโตร์ได้เช่นค้นหาหมายเลขแอพของฉันและรับข้อมูลเวอร์ชัน ช่วยประหยัดเวลาในการบำรุงรักษาเว็บเซิร์ฟเวอร์เพื่อจุดประสงค์นี้ แต่ต้องขอบคุณสำหรับตัวชี้อยู่ดี!
user542584

ฉันทำแบบเดียวกับความคิดเห็นแรก ฉันเขียน plist ด้วยรายการเดียว: NSNumberหมายเลขเวอร์ชัน จากนั้นฉันก็อัปโหลดไปยังเว็บไซต์ของฉัน เว็บไซต์เดียวกันกับที่ฉันใช้สำหรับการสนับสนุนแอปและหน้าเว็บแอปจากนั้นviewDidLoadฉันตรวจสอบหมายเลขเวอร์ชันในเว็บไซต์ที่นั่นและตรวจสอบเวอร์ชันปัจจุบันในแอปของฉัน จากนั้นฉันมีโปรแกรมสำเร็จรูปalertViewที่แจ้งให้อัปเดตแอปโดยอัตโนมัติ ฉันสามารถให้รหัสได้หากคุณต้องการ
Andrew

ขอบคุณครับผมว่าน่าจะลองเหมือนกัน ..
user542584

คำตอบ:


89

นี่คือข้อมูลโค้ดง่ายๆที่ช่วยให้คุณทราบว่าเวอร์ชันปัจจุบันแตกต่างกันหรือไม่

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

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

หมายเหตุ:ตรวจสอบให้แน่ใจว่าเมื่อคุณป้อนเวอร์ชันใหม่ใน iTunes จะตรงกับเวอร์ชันในแอปที่คุณกำลังปล่อย หากไม่เป็นเช่นนั้นรหัสข้างต้นจะส่งคืน YES เสมอไม่ว่าผู้ใช้จะอัปเดตหรือไม่ก็ตาม


4
ทางออกที่ยอดเยี่ยมที่ฉันเคยพบ +1
Sanjay Changani

1
@MobeenAfzal ฉันคิดว่าคุณไม่เข้าใจคำถามและวิธีแก้ปัญหา โซลูชันข้างต้นเปรียบเทียบเวอร์ชันปัจจุบันกับเวอร์ชันในสโตร์ หากไม่ตรงกันก็จะปรับใหม่เป็นใช่มิฉะนั้นจะส่งคืน NO ไม่ว่าประวัติในแอพสโตร์จะเป็นอย่างไรวิธีการข้างต้นจะคืนค่าใช่หากเวอร์ชันปัจจุบันแตกต่างจากเวอร์ชันของแอพสโตร์ เมื่อผู้ใช้อัปเดต ... เวอร์ชันปัจจุบันจะเท่ากับเวอร์ชันของแอปสโตร์ วิธีการข้างต้นควรส่งคืน YES เสมอหากเวอร์ชันของผู้ใช้คือ 1.0 และเวอร์ชันของ app store คือ 1.2
datinc

1
@MobeenAfzal ฉันคิดว่าฉันได้รับสิ่งที่คุณเห็น ในรหัสเวอร์ชันของคุณคือ 1.7 แต่ใน iTunes คุณอัปโหลดเวอร์ชันเป็น 1.6 เพื่อให้ผู้ใช้ของคุณไม่ทราบว่าคุณข้ามเวอร์ชัน เป็นอย่างนั้นหรือ? ถ้าเป็นเช่นนั้น ... สิ่งที่คุณต้องการคือเซิร์ฟเวอร์ (DropBox จะทำ) เพื่อให้บริการหมายเลขเวอร์ชันแอปของคุณและแก้ไขรหัสของคุณเพื่อเข้าถึงปลายทางนั้น โปรดแจ้งให้เราทราบหากนี่คือสิ่งที่คุณเห็นและเราจะเพิ่มหมายเหตุคำเตือนในโพสต์
datinc

1
@MobeenAfzal คุณความคิดเห็นของคุณทำให้เข้าใจผิด หากเวอร์ชันบนอุปกรณ์ของผู้ใช้ถูกคั่นด้วยเวอร์ชันใด ๆ ใน appstore โค้ดจะส่งคืนเป็น YES ตามที่คาดไว้ แม้ว่าคุณจะปล่อยเวอร์ชัน 1.0 ตามด้วยเวอร์ชัน 1.111 แต่ก็ยังทำงานได้อย่างสมบูรณ์
datinc

1
เราควรแสดงการอัปเดตเฉพาะเมื่อเวอร์ชันของ appstore มากกว่าเวอร์ชันปัจจุบันดังนี้ if ([appStoreVersion เปรียบเทียบ: currentVersion ตัวเลือก: NSNumericSearch] == NSOrderDescending) {NSLog (@ "\ n \ n ต้องอัปเดต Appstore เวอร์ชัน% @ มากกว่า% @", appStoreVersion, currentVersion); }
Nitesh Borad

52

เวอร์ชัน Swift 3:

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

ฉันคิดว่าเป็นการดีกว่าที่จะโยนข้อผิดพลาดแทนที่จะส่งคืนเท็จในกรณีนี้ฉันสร้าง VersionError แต่อาจเป็นสิ่งอื่นที่คุณกำหนดหรือ NSError

enum VersionError: Error {
    case invalidResponse, invalidBundleInfo
}

พิจารณาเรียกใช้ฟังก์ชันนี้จากเธรดอื่นด้วยหากการเชื่อมต่อช้าอาจบล็อกเธรดปัจจุบันได้

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

อัปเดต

ใช้ URLSession:

แทนที่จะใช้Data(contentsOf: url)และบล็อกเธรดเราสามารถใช้URLSession:

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

ตัวอย่าง:

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

1
คำตอบนี้ทำให้คำขอพร้อมกัน ซึ่งหมายความว่าเมื่อมีการเชื่อมต่อที่ไม่ดีแอปของคุณอาจใช้งานไม่ได้เป็นเวลาหลายนาทีจนกว่าคำขอจะกลับมา
uliwitness

4
ฉันไม่เห็นด้วยDispatchQueue.global()ให้คิวพื้นหลังข้อมูลถูกโหลดในคิวนั้นและกลับไปที่คิวหลักเมื่อโหลดข้อมูลเท่านั้น
juanjo

อ๊ะ. ฉันมองข้ามข้อมูลโค้ดที่สองนั้นไป น่าเศร้าที่ดูเหมือนว่าฉันจะไม่สามารถลบการโหวตลงได้จนกว่าคำตอบของคุณจะได้รับการแก้ไขอีกครั้ง :-( BTW - ให้ dataWithContentsOfURL: ผ่านการโทรแบบซิงโครนัสของ NSURLConnection ซึ่งในทางกลับกันเพิ่งเริ่มเธรด async และบล็อกมันอาจจะมีค่าใช้จ่ายน้อยลง เพื่อใช้การเรียก NSURLSession แบบอะซิงโครนัสพวกเขาจะโทรกลับหาคุณที่เธรดหลักเมื่อคุณทำเสร็จแล้ว
uliwitness

@juanjo ,,,, ไม่ทำงานกับ swift 3.0.1 โปรดอัปโหลดอัปเดตเพื่อความรวดเร็วได้ไหม ???
Kiran jadhav

2
โปรดทราบว่าคุณมีรายชื่ออยู่ในร้านค้าเฉพาะหรือไม่ฉันพบว่าคุณต้องเพิ่มรหัสประเทศใน URL เช่น GB itunes.apple.com/(countryCode)/… )
Ryan Heitner

13

ขอบคุณ Steve Moser สำหรับลิงค์ของเขานี่คือรหัสของฉัน:

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

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

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

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

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

1
วิธีแก้ไขที่ดีและถูกต้องมีการอัปเดตเล็กน้อยเกี่ยวกับ url คือitunes.apple.com/en/lookup?bundleId=xxxxxxxxxx
SJ

ขอบคุณความคิดเห็นของคุณถูกนำไปใช้
Roozbeh Zabihollahi

4
อันที่จริงมันไม่ได้ผลสำหรับฉันกับ/en/เส้นทางย่อย หลังจากถอดออกก็ใช้งานได้
gasparuff

คำตอบนี้ทำให้คำขอพร้อมกัน ซึ่งหมายความว่าเมื่อมีการเชื่อมต่อที่ไม่ดีแอปของคุณอาจใช้งานไม่ได้เป็นเวลาหลายนาทีจนกว่าคำขอจะกลับมา
uliwitness

1
ฉันต้องใช้กับ / en / itunes.apple.com/lookup?bundleId=xxxxxxxขอบคุณ @gasparuff
Fernando Perez

13

ตั้งแต่ผมกำลังเผชิญปัญหาเดียวกันผมพบว่าคำตอบให้โดยมาริโอเฮ็นดริก เมื่อฉันพยายามอ้างรหัสของเขาในโปรเจ็กต์ของฉันอย่างไม่เป็นธรรม XCode บ่นเกี่ยวกับปัญหาในการแคสต์ว่า "MDLMaterialProperty ไม่มีสมาชิกตัวห้อย" รหัสของเขาพยายามตั้ง MDLMaterial นี้ ... เป็นประเภทของ "lookupResult" คงที่ทำให้การแคสต์เป็น "Int" ล้มเหลวทุกครั้ง วิธีแก้ปัญหาของฉันคือให้คำอธิบายประกอบประเภทสำหรับตัวแปรของฉันกับNSDictionaryเพื่อให้ชัดเจนเกี่ยวกับประเภทของค่าที่ฉันต้องการ ด้วยเหตุนี้ฉันจึงสามารถเข้าถึงค่า "เวอร์ชัน" ที่ต้องการได้

ข้อสังเกต : สำหรับYOURBUNDLEIDนี้คุณจะได้รับจากโครงการ Xcode ของคุณ .... " Targets> General> Identity> Bundle Identifier "

ดังนั้นนี่คือรหัสของฉันที่มีความเรียบง่ายเช่นกัน:

  func appUpdateAvailable() -> Bool
{
    let storeInfoURL: String = "http://itunes.apple.com/lookup?bundleId=YOURBUNDLEID"
    var upgradeAvailable = false
    // Get the main bundle of the app so that we can determine the app's version number
    let bundle = NSBundle.mainBundle()
    if let infoDictionary = bundle.infoDictionary {
        // The URL for this app on the iTunes store uses the Apple ID for the  This never changes, so it is a constant
        let urlOnAppStore = NSURL(string: storeInfoURL)
        if let dataInJSON = NSData(contentsOfURL: urlOnAppStore!) {
            // Try to deserialize the JSON that we got
            if let dict: NSDictionary = try? NSJSONSerialization.JSONObjectWithData(dataInJSON, options: NSJSONReadingOptions.AllowFragments) as! [String: AnyObject] {
                if let results:NSArray = dict["results"] as? NSArray {
                    if let version = results[0].valueForKey("version") as? String {
                        // Get the version number of the current version installed on device
                        if let currentVersion = infoDictionary["CFBundleShortVersionString"] as? String {
                            // Check if they are the same. If not, an upgrade is available.
                            print("\(version)")
                            if version != currentVersion {
                                upgradeAvailable = true
                            }
                        }
                    }
                }
            }
        }
    }
    return upgradeAvailable
}

ข้อเสนอแนะทั้งหมดสำหรับการปรับปรุงรหัสนี้ยินดีต้อนรับ!


คำตอบนี้ทำให้คำขอพร้อมกัน ซึ่งหมายความว่าเมื่อมีการเชื่อมต่อที่ไม่ดีแอปของคุณอาจใช้งานไม่ได้เป็นเวลาหลายนาทีจนกว่าคำขอจะกลับมา
uliwitness

@Yago Zardo โปรดใช้ฟังก์ชันเปรียบเทียบมิฉะนั้นเมื่อผู้ใช้อัปโหลดแอปแอปเปิ้ลทดสอบการแสดงผลเวลาที่ทดสอบการแสดงการแจ้งเตือนการอัปเดตหรือแอปเปิ้ลปฏิเสธแอปของคุณ
Jigar Darji

เฮ้ @Jigar ขอบคุณสำหรับคำแนะนำ ขณะนี้ฉันไม่ได้ใช้วิธีนี้อีกต่อไปในแอปของฉันเพราะตอนนี้เรากำลังกำหนดเวอร์ชันทุกอย่างในเซิร์ฟเวอร์ของเรา อย่างไรก็ตามคุณช่วยอธิบายสิ่งที่คุณพูดให้ดีขึ้นได้ไหม ฉันไม่เข้าใจและมันก็ดูเป็นเรื่องดีที่จะรู้ ขอบคุณล่วงหน้า.
Yago Zardo

ขอบคุณ @uliwitness สำหรับเคล็ดลับนี้ช่วยให้ฉันปรับปรุงโค้ดโดยทั่วไปเพื่อเรียนรู้เกี่ยวกับคำขอแบบอะซิงโครนัสและซิงโครนัส
Yago Zardo

ลิงค์นั้นเป็นอัญมณี!
B3none

13

เพียงแค่ใช้ATAppUpdater เป็น 1 บรรทัดเธรดปลอดภัยและรวดเร็ว นอกจากนี้ยังมีวิธีการมอบสิทธิ์หากคุณต้องการติดตามการดำเนินการของผู้ใช้

นี่คือตัวอย่าง:

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

   return YES;
}

วิธีการมอบสิทธิ์เพิ่มเติม:

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

1
สิ่งนี้จะใช้ได้กับเวอร์ชันเบต้าใน Testflight หรือไม่ ถ้าไม่มีมีเครื่องมืออะไรที่จะ?
Lukasz Czerwinski

ไม่มันจะไม่เพียง แต่เปรียบเทียบเวอร์ชันปัจจุบันกับเวอร์ชันล่าสุดที่อยู่ใน AppStore
อารมณ์

เราสามารถใช้สิ่งนี้กับ Swift ได้หรือไม่?
Zorayr

11

ย่อคำตอบที่ดีโพสต์ในหัวข้อนี้ การใช้Swift 4และAlamofire.

import Alamofire

class VersionCheck {

  public static let shared = VersionCheck()

  func isUpdateAvailable(callback: @escaping (Bool)->Void) {
    let bundleId = Bundle.main.infoDictionary!["CFBundleIdentifier"] as! String
    Alamofire.request("https://itunes.apple.com/lookup?bundleId=\(bundleId)").responseJSON { response in
      if let json = response.result.value as? NSDictionary, let results = json["results"] as? NSArray, let entry = results.firstObject as? NSDictionary, let versionStore = entry["version"] as? String, let versionLocal = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String {
        let arrayStore = versionStore.split(separator: ".")
        let arrayLocal = versionLocal.split(separator: ".")

        if arrayLocal.count != arrayStore.count {
          callback(true) // different versioning system
        }

        // check each segment of the version
        for (key, value) in arrayLocal.enumerated() {
          if Int(value)! < Int(arrayStore[key])! {
            callback(true)
          }
        }
      }
      callback(false) // no new version or failed to fetch app store version
    }
  }

}

จากนั้นจึงจะใช้:

VersionCheck.shared.isUpdateAvailable() { hasUpdates in
  print("is update available: \(hasUpdates)")
}

2
แอปพลิเคชันของฉันอยู่ในร้านค้า แต่ api เดียวกันไม่ส่งคืนข้อมูลเวอร์ชัน ตอบกลับ:{ "resultCount":0, "results": [] }
technerd

เพียงเพิ่มหมายเหตุในการเปรียบเทียบเวอร์ชันฉันต้องการให้ serverVersion = "2.7" ให้ localVersion = "2.6.5" ให้ isUpdateAvailable = serverVersion.compare (localVersion ตัวเลือก: .numeric) == .orderedDescending แทนที่จะแทนที่ไฟล์. ด้วยความว่างเปล่า
Chaitu

@Chaitu ขอบคุณสำหรับคำแนะนำ ฉันลงเอยด้วยการเขียนส่วนเปรียบเทียบของโค้ดใหม่
budidino

9

อัปเดตรหัสswift 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)

มีแนวคิดเกี่ยวกับวิธีทดสอบสิ่งนี้หรือไม่? หากไม่สามารถทำงานได้อย่างถูกต้องวิธีเดียวในการดีบักคือการดีบักเวอร์ชันที่เก่ากว่าใน App Store
David Rector

2
อ่าไม่เป็นไรคำถาม ฉันสามารถเปลี่ยนเวอร์ชันท้องถิ่นของฉันให้ "เก่ากว่า" ได้
David Rector

ฉันประทับใจรหัสของคุณ @Vasco คำถามง่ายๆทำไมคุณถึงใช้ 'http' แทน https ใน url นั้น?
Master AgentX

ขอบคุณมากสำหรับการแบ่งปันวิธีแก้ปัญหานี้ @Vasco! ฉันชอบ :) ทำไมคุณไม่ใช้: let config = URLSessionConfiguration.background (withIdentifier: "com.example.MyExample.background") เพื่อให้ URLSession บรรลุคำขอเบื้องหลัง
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 วิธีตีความเวอร์ชันนั้นขึ้นอยู่กับแอปและโครงสร้างเวอร์ชันทั้งหมด
แม่ทัพภาคเหนือ

เพียงแค่เพิ่มหมายเหตุในการเปรียบเทียบเวอร์ชันฉันต้องการให้ serverVersion = "2.7" ให้ localVersion = "2.6.5" ให้ isUpdateAvailable = serverVersion.compare (localVersion ตัวเลือก: .numeric) == .orderedDescending แทนที่จะเปรียบเทียบกับค่าที่เท่ากัน
Chaitu

6

ฉันขอแนะนำห้องสมุดเล็ก ๆ นี้ได้ไหม: https://github.com/nicklockwood/iVersion

จุดประสงค์คือเพื่อลดความซับซ้อนในการจัดการกับ Plists ระยะไกลเพื่อเรียกการแจ้งเตือน


3
คุณสามารถตรวจสอบ App Store ได้โดยตรงสำหรับหมายเลขเวอร์ชันแทนที่จะโฮสต์ไฟล์ plist ไว้ที่ใดที่หนึ่ง ตรวจสอบคำตอบนี้: stackoverflow.com/a/6569307/142358
Steve Moser

1
ตอนนี้ iVersion ใช้เวอร์ชันร้านค้าแอปโดยอัตโนมัติ - Plist เป็นทางเลือกหากคุณต้องการระบุบันทึกประจำรุ่นที่แตกต่างกันไปยังรายการใน iTunes แต่คุณไม่จำเป็นต้องใช้
Nick Lockwood

1
รหัสนี้สามารถใช้การปรับปรุงบางอย่างได้ แต่ดีกว่าคำตอบอื่น ๆ ที่ส่งคำขอแบบซิงโครนัสมาก อย่างไรก็ตามวิธีการทำเธรดเป็นรูปแบบที่ไม่ดี ฉันจะแจ้งปัญหาเกี่ยวกับ Github
uliwitness

ตอนนี้เลิกใช้โครงการแล้ว😢
Zorayr

5

Swift 3.1

func needsUpdate() -> Bool {
    let infoDictionary = Bundle.main.infoDictionary
    let appID = infoDictionary!["CFBundleIdentifier"] as! String
    let url = URL(string: "http://itunes.apple.com/lookup?bundleId=\(appID)")
    guard let data = try? Data(contentsOf: url) else {
      print("There is an error!")
      return false;
    }
    let lookup = (try? JSONSerialization.jsonObject(with: data! , options: [])) as? [String: Any]
    if let resultCount = lookup!["resultCount"] as? Int, resultCount == 1 {
        if let results = lookup!["results"] as? [[String:Any]] {
            if let appStoreVersion = results[0]["version"] as? String{
                let currentVersion = infoDictionary!["CFBundleShortVersionString"] as? String
                if !(appStoreVersion == currentVersion) {
                    print("Need to update [\(appStoreVersion) != \(currentVersion)]")
                    return true
                }
            }
        }
    }
    return false
}

สิ่งนี้จะหยุดทำงานเมื่อคุณไม่มีการเชื่อมต่ออินเทอร์เน็ต ให้ data = ลอง? Data (contentsOf: url!) จะคืนค่าเป็นศูนย์และในบรรทัดถัดไปคุณจะทำ data!
Joris Mans

ขอบคุณ @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 ทำงานกับเธรดพื้นหลังโดยอัตโนมัติเว้นแต่เราจะระบุเป็นอย่างอื่น
Sebastian Dwornik

4

ฉันเห็นหลายวิธีในการตรวจสอบการอัปเดตแอป จากคำตอบมากมายฉันจึงผสมผสานและสร้างโซลูชันของฉันซึ่งมีอยู่ในGitHubหากจำเป็นต้องมีการอัปเดตใด ๆ โปรดแจ้งให้เราทราบ รหัสนี้สำหรับSwift 4

ลิงก์ GitHub ไปยังรหัสนี้ https://github.com/anupgupta-arg/iOS-Swift-ArgAppUpdater

   import UIKit

enum VersionError: Error {
    case invalidBundleInfo, invalidResponse
}

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

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

}

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

    private override init() {

    }

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

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

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

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

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

                print("dictionary",dictionary!)


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

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

            let appStoreAppVersion = info?.version

            if let error = error {
                print(error)



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

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

            }
        }


    }

    func showUpdateWithConfirmation() {
        checkVersion(force : false)


    }

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



}

extension UIViewController {


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

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


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


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


            }
            alertController.addAction(notNowButton)
        }

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

        }

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

อ้างอิง: https://stackoverflow.com/a/48810541/5855888 และhttps://github.com/emotality/ATAppUpdater

มีความสุขในการเข้ารหัส👍😊


@Rob โปรดตรวจสอบ GitHub Link github.com/anupgupta-arg/iOS-Swift-ArgAppUpdater
Anup 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 ของ app store แล้วคุณสามารถแยกบันทึกประจำรุ่นได้หากต้องการ

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 ของ app ใน appstore หรือไม่
iamthevoid

@Mario Hendricks นี้ไม่ทำงานใน swift 3 มันทำให้เกิดข้อผิดพลาดบางอย่าง คุณช่วยอัปเดตสำหรับ swift 3 ได้ไหม
George Asda

คำตอบนี้ทำให้คำขอพร้อมกัน ซึ่งหมายความว่าเมื่อมีการเชื่อมต่อที่ไม่ดีแอปของคุณอาจใช้งานไม่ได้เป็นเวลาหลายนาทีจนกว่าคำขอจะกลับมา
uliwitness

2

หากคุณไม่ได้ตั้งค่าประเภทเนื้อหาใน NSUrlRequest แน่นอนว่าคุณจะไม่ได้รับการตอบสนองดังนั้นลองใช้รหัสด้านล่างซึ่งใช้ได้ดีสำหรับฉัน หวังว่าจะช่วย ....

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

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

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

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

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

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

คำตอบนี้ทำให้คำขอพร้อมกัน ซึ่งหมายความว่าเมื่อมีการเชื่อมต่อที่ไม่ดีแอปของคุณอาจใช้งานไม่ได้เป็นเวลาหลายนาทีจนกว่าคำขอจะกลับมา
uliwitness

2

มาจากแอปพลิเคชันแบบไฮบริด POV นี่คือตัวอย่างจาวาสคริปต์ฉันมีส่วนท้ายที่มีการอัปเดตในเมนูหลักของฉัน หากมีการอัปเดต (เช่นหมายเลขเวอร์ชันของฉันภายในไฟล์กำหนดค่าน้อยกว่าเวอร์ชันที่ดึงมาให้แสดงส่วนท้าย) จากนั้นจะนำผู้ใช้ไปที่ร้านแอปซึ่งผู้ใช้สามารถคลิกปุ่มอัปเดตได้

ฉันยังได้รับข้อมูลใหม่คืออะไร (เช่นบันทึกประจำรุ่น) และแสดงข้อมูลเหล่านี้ในรูปแบบการเข้าสู่ระบบหากเป็นครั้งแรกในเวอร์ชันนี้

สามารถเรียกใช้วิธีการอัปเดตได้บ่อยเท่าที่คุณต้องการ ทุ่นระเบิดจะทำงานทุกครั้งที่ผู้ใช้ไปที่หน้าจอหลัก

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 อาจช้าพอที่จะใช้เวลานาน คุณไม่ต้องการให้แอปของคุณใช้งานไม่ได้ในกรณีนั้น ในการทดสอบสิ่งต่างๆเช่นนี้การเรียกใช้รหัสเครือข่ายของคุณด้วย Network Link Conditioner ของ Apple จะมีประโยชน์


ขอบคุณที่ทำให้คำถามนี้มีชีวิตอยู่ :-)
Jeevan

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

แก้ไข :: โปรดสังเกตภูมิภาค "/ 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
    }
  • คุณยังสามารถแสดงการแจ้งเตือนประเภทต่างๆเกี่ยวกับเวอร์ชันใหม่ (อนุญาตให้ข้ามเวอร์ชันหรือบังคับให้ผู้ใช้อัปเดต)
  • คุณสามารถระบุความถี่ในการตรวจสอบเวอร์ชันได้ (รายวัน / รายสัปดาห์ / ทันที)
  • คุณสามารถระบุได้ว่าจะปรากฏการแจ้งเตือนเวอร์ชันใหม่ใน App Store ภายในกี่วัน

ลิงก์ไปยังคำตอบที่มีอยู่ไม่ใช่คำตอบ นอกจากนี้ลิงก์ไปยังไลบรารียังไม่ใช่คำตอบเว้นแต่คุณจะเพิ่มวิธีที่ลิงก์ตอบคำถามให้กับคำตอบของคุณอย่างชัดเจน (เพิ่มตัวอย่างโค้ด ฯลฯ )
JAL

1

สวิฟต์ 4

เราสามารถใช้สิ่งใหม่JSONDecoderเพื่อแยกวิเคราะห์การตอบสนองจากitunes.apple.com/lookupและแสดงด้วยคลาสหรือโครงสร้างที่ถอดรหัสได้:

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

class AppInfo: Decodable {
    var version: String
}

นอกจากนี้เรายังสามารถเพิ่มคุณสมบัติอื่น ๆAppInfoในกรณีที่เราต้องการreleaseNotesหรือคุณสมบัติอื่น ๆ

ตอนนี้เราสามารถขอ async โดยใช้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
Anup Gupta

@jessi โปรดตรวจสอบรหัสของฉันใน GitHub ฉันโพสต์ที่นั่นวิธีแก้ปัญหาของคุณ
Anup Gupta

0

ข้อเสนอรหัสของฉัน อ้างอิงจากคำตอบของ @datinc และ @ Mario-Hendricks

แน่นอนคุณควรแทนที่dlog_Errorด้วยการโทร func การบันทึกของคุณ

โครงสร้างโค้ดประเภทนี้ควรป้องกันไม่ให้แอปของคุณหยุดทำงานในกรณีที่เกิดข้อผิดพลาด สำหรับการดึงข้อมูล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

อัปเดตสำหรับ swift 3:

หากคุณต้องการตรวจสอบเวอร์ชันปัจจุบันของแอปของคุณโดยใช้โค้ดง่ายๆด้านล่าง:

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

  let version = object as! String
  print("version: \(version)")
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.