Làm thế nào để biết trong thời gian chạy ứng dụng iOS có đang chạy qua bản cài đặt TestFlight Beta hay không


123

Có thể phát hiện trong thời gian chạy rằng một ứng dụng đã được cài đặt thông qua TestFlight Beta (được gửi qua iTunes Connect) so với App Store không? Bạn có thể gửi một gói ứng dụng duy nhất và có sẵn nó thông qua cả hai. Có một API nào có thể phát hiện ra nó đã được cài đặt theo cách nào không? Hay biên nhận có thông tin cho phép xác định điều này?


4
Xin nói rõ rằng bạn đang nói về thử nghiệm beta TestFlight mới thông qua iTunes Connect? Hay bạn đang nói về thời điểm bạn đã tải trực tiếp lên TestFlight?
keji

Bản beta TestFlight mới, sẽ làm rõ
tổng hợp

1
Có vẻ như - [NSString chứaString:] là một phần bổ sung của ios8. Nếu quá trình kiểm tra tự động của App Store cố gắng chạy nó trên ios7, bạn không cần thực hiện. ([biên nhậnURLString rangeOfString: @ "sandboxReceipt"]. location! = NSNotFound) nên thực hiện thủ thuật.
rgeorge

@rgeorge cảm ơn, đó là một sai lầm ngớ ngẩn!
tổng hợp

2
Tôi đã định hỏi về việc phát hiện trên iOS 6 không có appStoreReceiptURL, nhưng có vẻ như ứng dụng TestFlight chỉ dành cho iOS 8; vì vậy - [NSString chứaString] có thể ổn. Tôi đã tạm dừng thử nghiệm beta của cửa hàng ứng dụng vì lý do này, nhưng tôi đoán một số người có thể đang sử dụng chiến lược thử nghiệm kết hợp, với Ad-Hoc cho thử nghiệm cũ và bản beta AppStore cho bản beta công khai, vì vậy rangeOfString vẫn thắng.
Gordon Dove

Câu trả lời:


117

Đối với một ứng dụng được cài đặt thông qua TestFlight Beta, tệp biên nhận được đặt tên StoreKit\sandboxReceiptso với thông thường StoreKit\receipt. Khi sử dụng, [NSBundle appStoreReceiptURL]bạn có thể tìm sandboxReceipt ở cuối URL.

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

Lưu ý rằng đó sandboxReceiptcũng là tên của tệp biên nhận khi chạy các bản dựng cục bộ và cho các bản dựng chạy trong trình mô phỏng.


7
Như đã lưu ý, điều này hoạt động để thử nghiệm cục bộ trên thiết bị, nhưng không hoạt động trên trình mô phỏng. Tôi đã thêm một cái gì đó như #if TARGET_IPHONE_SIMULATOR isRunningInTestMode = YES; #endif Rõ ràng, điều này cần #import <TargetConditionals.h>
Gordon Dove

13
Phiên bản nhỏ gọn: [[[[NSBundle mainBundle] appStoreReceiptURL] lastPathComponent] isEqualToString:@"sandboxReceipt"](Đúng nếu chạy tệp nhị phân phân tán TestFlight) qua Supertop / Haddad
Nick

2
Không thể sử dụng phương pháp này trong các gói tiện ích mở rộng vì biên nhận chỉ tồn tại cho gói máy chủ.
jeeeyul

2
Kết quả thử nghiệm iOS 8 của tôi StoreKit/sandboxReceiptkhi được cài đặt dưới dạng bản dựng gỡ lỗi thông qua Xcode trên thiết bị hoặc trình mô phỏng. Vì vậy, điều này có thể không phân biệt chính xác các bản dựng testflight với tất cả các bản dựng khác.
pkamb

3
Cũng có vẻ như trả về CÓ khi cài đặt bản dựng với bản phân phối Ad Hoc.
Keller

75

Dựa trên câu trả lời của tổ hợp, tôi đã tạo lớp trợ giúp SWIFT sau. Với lớp này, bạn có thể xác định xem đó là bản dựng debug, testflight hay appstore.

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

Chúng tôi sử dụng các phương pháp này trong dự án của mình để cung cấp các id theo dõi hoặc chuỗi kết nối khác nhau cho mỗi môi trường:

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

HOẶC LÀ:

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

CẬP NHẬT 05-02-2016: Điều kiện tiên quyết để sử dụng macro tiền xử lý như #if DEBUG là đặt một số Cờ tùy chỉnh của trình biên dịch Swift. Thông tin thêm trong câu trả lời này: https://stackoverflow.com/a/24112024/639227


1
@Urkman Đảm bảo rằng bạn đang đặt -D DEBUGcờ. Thông tin thêm có thể được tìm thấy ở đây .
Caleb

Thnx @Caleb, tôi đã thêm giải thích thêm về các điều kiện tiên quyết cho câu trả lời.
LorenzoValentijn

1
Cảm ơn câu trả lời của bạn, tôi thấy nó rất hữu ích! Cũng cần biết rằng, việc sử dụng #if targetEnvironment(simulator)bạn xác định xem bạn có đang chạy trong một trình mô phỏng hay không. Vì vậy, tôi có các tùy chọn Simulator / TestFlight / AppStore (trong trường hợp của tôi được ưu tiên hơn Debug) :-)
JeroenJK

39

Phiên bản Swift hiện đại, tài khoản cho Trình mô phỏng (dựa trên câu trả lời được chấp nhận):

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

Rất vui khi bao gồm trình mô phỏng, nhưng bạn có thể muốn thay đổi tên hàm vì nó không còn đúng cho mọi trường hợp.
dbn

2
WOW! Nó hoạt động! Tuyệt vời! Trả về TRUE cho TestFlight và FALSE cho AppStore cho cùng một bản dựng (một bản dựng được xây dựng trong một lược đồ với một lần cung cấp). Hoàn hảo! Cảm ơn bạn!
Argus

@dbn bạn có thể mở rộng thêm tại sao điều này không còn đúng với mọi trường hợp không?
Ethan

1
@Ethan câu trả lời này đã được chỉnh sửa sau khi tôi đưa ra nhận xét của mình; tên phương pháp sử dụng để đượcisTestFlight()
DBN


2

Tôi sử dụng tiện ích mở rộng Bundle+isProductiontrên 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
    }
}

Sau đó:

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

-3

Có một cách mà tôi sử dụng nó cho các dự án của mình. Đây là các bước.

Trong Xcode, đi tới cài đặt dự án (dự án, không phải mục tiêu) và thêm cấu hình "beta" vào danh sách:

nhập mô tả hình ảnh ở đây



Sau đó, bạn cần tạo lược đồ mới sẽ chạy dự án trong cấu hình "beta". Để tạo lược đồ, hãy vào đây:

nhập mô tả hình ảnh ở đây



Đặt tên cho chương trình này bất cứ điều gì bạn muốn. Bạn nên chỉnh sửa cài đặt cho chương trình này. Để làm điều này, hãy nhấn vào đây:

nhập mô tả hình ảnh ở đây



Chọn tab Lưu trữ nơi bạn có thể chọn Build configuration

nhập mô tả hình ảnh ở đây



Sau đó, bạn cần thêm một khóa Configcó giá trị $(CONFIGURATION)vào danh sách tài sản thông tin dự án như sau:

nhập mô tả hình ảnh ở đây



Sau đó, vấn đề chỉ là những gì bạn cần trong mã để thực hiện điều gì đó cụ thể cho bản dựng beta:

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
Mặc dù đây là một kỹ thuật hữu ích nhưng nó không trả lời câu hỏi. Một tệp nhị phân duy nhất được gửi đến App Store và có thể được chạy từ quá trình tải xuống thông qua TestFlight hoặc sau khi chạy được chấp thuận sau khi tải xuống từ App Store. Câu hỏi là về việc phát hiện phiên bản nào đang chạy.
tổ hợp

Có một tùy chọn để tạo 2 kho lưu trữ ngay từ đầu. một cho testflight một cho cửa hàng ứng dụng.
Klemen

Có thể, nhưng chúng phải có số lượng xây dựng khác nhau. Và nó có nghĩa là quản lý hai bản dựng thay vì một.
tổ hợp

ok, theo ý kiến ​​của tôi thì nó đáng giá. Đặc biệt nếu bạn sử dụng các công cụ tích hợp liên tục.
Klemen

@KlemenZagar, cách tiếp cận của bạn là một phương pháp nổi tiếng và tốt nhưng nó không trả lời được câu hỏi.
Stanislav Pankevich
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.