จะบอกได้อย่างไรในขณะรันไทม์ว่าแอป iOS กำลังทำงานผ่านการติดตั้ง TestFlight Beta หรือไม่


124

เป็นไปได้หรือไม่ที่จะตรวจจับที่รันไทม์ว่ามีการติดตั้งแอปพลิเคชันผ่าน TestFlight Beta (ส่งผ่าน iTunes Connect) เทียบกับ App Store คุณสามารถส่ง App Bundle ชุดเดียวและพร้อมใช้งานผ่านทั้งสองอย่าง มี API ที่ตรวจจับได้ว่าติดตั้งด้วยวิธีใด? หรือใบเสร็จรับเงินมีข้อมูลที่อนุญาตให้พิจารณาได้หรือไม่?


4
เพื่อให้ชัดเจนว่าคุณกำลังพูดถึงการทดสอบเบต้า TestFlight ใหม่ผ่าน iTunes Connect? หรือคุณกำลังพูดถึงเมื่อคุณอัปโหลดไปยัง TestFlight โดยตรง?
keji

TestFlight เบต้าใหม่จะชี้แจง
combinatorial

1
ดูเหมือนว่า - [NSString containsString:] เป็นการเพิ่ม ios8 หากการทดสอบอัตโนมัติของ App Store พยายามรันบน ios7 ไม่ต้องไป ([การรับURLString rangeOfString: @ "sandboxReceipt"]. location! = NSNotFound) ควรทำเคล็ดลับ
rgeorge

@rgeorge ขอบคุณนั่นเป็นความผิดพลาดที่โง่!
combinatorial

2
ฉันจะถามเกี่ยวกับการตรวจจับบน iOS 6 ซึ่งไม่มี appStoreReceiptURL แต่ดูเหมือนว่าแอป TestFlight จะเป็น iOS 8 เท่านั้น ดังนั้น - [NSString containsString] อาจจะดีก็ได้ ฉันได้ระงับการทดสอบเบต้าของ App Store ไว้ด้วยเหตุนี้ แต่ฉันเดาว่าบางคนอาจใช้กลยุทธ์การทดสอบแบบไฮบริดโดยมี Ad-Hoc สำหรับการทดสอบแบบเดิมและ AppStore beta สำหรับเบต้าสาธารณะดังนั้น rangeOfString จึงยังคงชนะอยู่
Gordon Dove

คำตอบ:


119

สำหรับโปรแกรมที่ติดตั้งผ่าน TestFlight เบต้าไฟล์ใบเสร็จรับเงินเป็นชื่อStoreKit\sandboxReceiptVS StoreKit\receiptปกติ การใช้[NSBundle appStoreReceiptURL]คุณสามารถค้นหา sandboxReceipt ที่ท้าย URL

NSURL *receiptURL = [[NSBundle mainBundle] appStoreReceiptURL];
NSString *receiptURLString = [receiptURL path];
BOOL isRunningTestFlightBeta =  ([receiptURLString rangeOfString:@"sandboxReceipt"].location != NSNotFound);

โปรดทราบว่าsandboxReceiptยังเป็นชื่อของไฟล์ใบเสร็จเมื่อเรียกใช้บิลด์ในเครื่องและสำหรับบิลด์ที่รันในตัวจำลอง


7
ตามที่ระบุไว้การทำงานนี้ใช้ได้กับการทดสอบภายในอุปกรณ์ แต่ไม่ใช่ในโปรแกรมจำลอง ฉันเพิ่มบางอย่างเช่น #if TARGET_IPHONE_SIMULATOR isRunningInTestMode = YES; #endif เห็นได้ชัดว่าสิ่งนี้ต้องการ #import <TargetConditionals.h>
Gordon Dove

13
รุ่นกะทัดรัด: [[[[NSBundle mainBundle] appStoreReceiptURL] lastPathComponent] isEqualToString:@"sandboxReceipt"](จริงถ้าใช้ไบนารีแบบกระจาย TestFlight) ผ่านSupertop / Haddad
Nick

2
ไม่สามารถใช้วิธีนี้ในบันเดิลส่วนขยายเนื่องจากมีใบเสร็จสำหรับโฮสต์บันเดิลเท่านั้น
jeeeyul

2
ผลการทดสอบ iOS 8 ของฉันStoreKit/sandboxReceiptเมื่อติดตั้งเป็น debug build ผ่าน Xcode บนอุปกรณ์หรือโปรแกรมจำลอง ดังนั้นสิ่งนี้อาจไม่สามารถแยกความแตกต่างของการสร้างเที่ยวบินทดสอบจากรุ่นอื่น ๆ ทั้งหมดได้อย่างถูกต้อง
pkamb

3
ดูเหมือนว่าจะส่งคืน YES เมื่อติดตั้งบิลด์ที่มีการแจกจ่าย Ad Hoc
Keller

76

จากคำตอบของ combinatorialฉันได้สร้างคลาสตัวช่วย SWIFT ต่อไปนี้ ด้วยคลาสนี้คุณสามารถระบุได้ว่าเป็น debug, testflight หรือ appstore build

enum AppConfiguration {
  case Debug
  case TestFlight
  case AppStore
}

struct Config {
  // This is private because the use of 'appConfiguration' is preferred.
  private static let isTestFlight = Bundle.main.appStoreReceiptURL?.lastPathComponent == "sandboxReceipt"
  
  // This can be used to add debug statements.
  static var isDebug: Bool {
    #if DEBUG
      return true
    #else
      return false
    #endif
  }

  static var appConfiguration: AppConfiguration {
    if isDebug {
      return .Debug
    } else if isTestFlight {
      return .TestFlight
    } else {
      return .AppStore
    }
  }
}

เราใช้วิธีการเหล่านี้ในโครงการของเราเพื่อจัดหารหัสการติดตามหรือสตริงการเชื่อมต่อที่แตกต่างกันต่อสภาพแวดล้อม:

  func getURL(path: String) -> String {    
    switch (Config.appConfiguration) {
    case .Debug:
      return host + "://" + debugBaseUrl + path
    default:
      return host + "://" + baseUrl + path
    }
  }

หรือ:

  static var trackingKey: String {
    switch (Config.appConfiguration) {
    case .Debug:
      return debugKey
    case .TestFlight:
      return testflightKey
    default:
      return appstoreKey
    }
  }

อัปเดต 05-02-2016: ข้อกำหนดเบื้องต้นในการใช้มาโครตัวประมวลผลล่วงหน้าเช่น #if DEBUG คือการตั้งค่าแฟล็กที่กำหนดเองของ Swift Compiler ข้อมูลเพิ่มเติมในคำตอบนี้: https://stackoverflow.com/a/24112024/639227


1
@Urkman ให้แน่ใจว่าคุณตั้งค่า-D DEBUGสถานะ ข้อมูลเพิ่มเติมสามารถพบได้ที่นี่
Caleb

Thnx @Caleb ฉันได้เพิ่มคำอธิบายเพิ่มเติมเกี่ยวกับข้อกำหนดเบื้องต้นในคำตอบ
LorenzoValentijn

1
ขอบคุณสำหรับคำตอบเราพบว่ามีประโยชน์มาก! สิ่งที่ควรรู้คือการใช้#if targetEnvironment(simulator)คุณเป็นตัวกำหนดว่าคุณกำลังทำงานอยู่ในเครื่องจำลองหรือไม่ ดังนั้นฉันจึงมีตัวเลือก Simulator / TestFlight / AppStore (ซึ่งในกรณีของฉันต้องการมากกว่าDebug) :-)
JeroenJK

39

เวอร์ชัน Modern Swift ซึ่งใช้สำหรับเครื่องจำลอง (ตามคำตอบที่ยอมรับ):

private func isSimulatorOrTestFlight() -> Bool {
    guard let path = Bundle.main.appStoreReceiptURL?.path else {
        return false
    }
    return path.contains("CoreSimulator") || path.contains("sandboxReceipt")
}

ยินดีที่จะรวมตัวจำลอง แต่คุณอาจต้องการเปลี่ยนชื่อฟังก์ชันเนื่องจากไม่เป็นความจริงอีกต่อไปสำหรับทุกกรณี
dbn

2
ว้าว! มันได้ผล! ! น่ากลัว ส่งคืนค่า TRUE สำหรับ TestFlight และ FALSE สำหรับ AppStore สำหรับบิลด์เดียวกัน (หนึ่งบิลด์ที่สร้างในโครงร่างเดียวพร้อมการจัดเตรียมหนึ่งรายการ) ที่สมบูรณ์แบบ! ขอบคุณ!
Argus

@dbn คุณช่วยขยายความได้ไหมว่าเหตุใดจึงไม่เป็นความจริงอีกต่อไปสำหรับทุกกรณี
Ethan

1
@ กว่าคำตอบนี้ได้รับการแก้ไขหลังจากที่ฉันแสดงความคิดเห็น; ชื่อเมธอดที่ใช้คือisTestFlight()
dbn

6

ปรับปรุง

ไม่ได้ผลอีกต่อไป ใช้วิธีอื่น

คำตอบเดิม

สิ่งนี้ยังใช้งานได้:

if NSBundle.mainBundle().pathForResource("embedded", ofType: "mobileprovision") != nil {
    // TestFlight
} else {
    // App Store (and Apple reviewers too)
}

พบในตรวจหาว่าดาวน์โหลดแอป iOS จาก Testflight ของ Apple หรือไม่


2

ฉันใช้ส่วนขยายBundle+isProductionบน Swift 5.2:

import Foundation

extension Bundle {
    var isProduction: Bool {
        #if DEBUG
            return false
        #else
            guard let path = self.appStoreReceiptURL?.path else {
                return true
            }
            return !path.contains("sandboxReceipt")
        #endif
    }
}

แล้ว:

if Bundle.main.isProduction {
    // do something
}

-3

มีวิธีหนึ่งที่ฉันใช้สำหรับโครงการของฉัน นี่คือขั้นตอน

ใน Xcode ไปที่การตั้งค่าโครงการ (โครงการไม่ใช่เป้าหมาย) และเพิ่มการกำหนดค่า "เบต้า" ในรายการ:

ใส่คำอธิบายภาพที่นี่



จากนั้นคุณต้องสร้างโครงร่างใหม่ที่จะเรียกใช้โครงการในการกำหนดค่า "เบต้า" ในการสร้างโครงการไปที่นี่:

ใส่คำอธิบายภาพที่นี่



ตั้งชื่อโครงการนี้ตามที่คุณต้องการ คุณควรแก้ไขการตั้งค่าสำหรับโครงการนี้ โดยแตะที่นี่:

ใส่คำอธิบายภาพที่นี่



เลือกแท็บเก็บถาวรที่คุณสามารถเลือกได้ Build configuration

ใส่คำอธิบายภาพที่นี่



จากนั้นคุณต้องเพิ่มคีย์ที่Configมีมูลค่า$(CONFIGURATION)รายการคุณสมบัติข้อมูลโครงการดังนี้:

ใส่คำอธิบายภาพที่นี่



จากนั้นก็เป็นเพียงสิ่งที่คุณต้องการในโค้ดเพื่อทำบางสิ่งบางอย่างเฉพาะสำหรับการสร้างเบต้า:

let config = Bundle.main.object(forInfoDictionaryKey: "Config") as! String
if config == "Debug" {
  // app running in debug configuration
}
else if config == "Release" {
  // app running in release configuration
}
else if config == "Beta" {
  // app running in beta configuration
}

6
แม้ว่านี่จะเป็นเทคนิคที่เป็นประโยชน์ แต่ก็ไม่สามารถตอบคำถามได้ ไบนารีเดียวจะถูกส่งไปยัง App Store และสามารถเรียกใช้จากการดาวน์โหลดผ่าน TestFlight หรือใหม่กว่าหลังจากที่ได้รับการอนุมัติแล้วหลังจากดาวน์โหลดจาก App Store คำถามเกี่ยวกับการตรวจจับว่าเวอร์ชันใดกำลังทำงานอยู่
combinatorial

มีตัวเลือกในการสร้างที่เก็บถาวร 2 รายการในตอนแรกหรือไม่ หนึ่งสำหรับ testflight one สำหรับ app store
Klemen

เป็นไปได้ แต่ต้องมีจำนวนการสร้างที่แตกต่างกัน และหมายถึงการจัดการสองบิลด์แทนที่จะเป็นหนึ่ง
combinatorial

โอเคในความคิดของฉันมันคุ้มค่า โดยเฉพาะอย่างยิ่งถ้าคุณใช้เครื่องมือการผสานรวมแบบต่อเนื่อง
Klemen

@KlemenZagar แนวทางของคุณเป็นที่รู้จักและดี แต่ไม่ตอบคำถาม
Stanislav Pankevich
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.