BuildDetails.swift 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106
  1. // LoopFollow
  2. // BuildDetails.swift
  3. import Foundation
  4. class BuildDetails {
  5. static var `default` = BuildDetails()
  6. let dict: [String: Any]
  7. init() {
  8. guard let url = Bundle.main.url(forResource: "BuildDetails", withExtension: "plist"),
  9. let data = try? Data(contentsOf: url),
  10. let parsed = try? PropertyListSerialization.propertyList(from: data, format: nil) as? [String: Any]
  11. else {
  12. dict = [:]
  13. return
  14. }
  15. dict = parsed
  16. }
  17. var teamID: String? {
  18. dict["com-LoopFollow-development-team"] as? String
  19. }
  20. var buildDateString: String? {
  21. return dict["com-LoopFollow-build-date"] as? String
  22. }
  23. var branch: String? {
  24. return dict["com-LoopFollow-branch"] as? String
  25. }
  26. var branchAndSha: String {
  27. let branch = branch ?? "Unknown"
  28. let sha = dict["com-LoopFollow-commit-sha"] as? String ?? "Unknown"
  29. return "\(branch) \(sha)"
  30. }
  31. // Determine if the build is from TestFlight
  32. func isTestFlightBuild() -> Bool {
  33. #if targetEnvironment(simulator)
  34. return false
  35. #else
  36. if Bundle.main.url(forResource: "embedded", withExtension: "mobileprovision") != nil {
  37. return false
  38. }
  39. guard let receiptName = Bundle.main.appStoreReceiptURL?.lastPathComponent else {
  40. return false
  41. }
  42. return "sandboxReceipt".caseInsensitiveCompare(receiptName) == .orderedSame
  43. #endif
  44. }
  45. // Determine if the build is for Simulator
  46. func isSimulatorBuild() -> Bool {
  47. #if targetEnvironment(simulator)
  48. return true
  49. #else
  50. return false
  51. #endif
  52. }
  53. // Determine if the build is for Mac
  54. func isMacApp() -> Bool {
  55. #if targetEnvironment(macCatalyst)
  56. return true
  57. #else
  58. return false
  59. #endif
  60. }
  61. // Parse the build date string into a Date object
  62. func buildDate() -> Date? {
  63. guard let dateString = dict["com-LoopFollow-build-date"] as? String else {
  64. return nil
  65. }
  66. let formatter = ISO8601DateFormatter()
  67. return formatter.date(from: dateString)
  68. }
  69. // Calculate the expiration date based on the build type
  70. func calculateExpirationDate() -> Date {
  71. if isTestFlightBuild(), let buildDate = buildDate() {
  72. // For TestFlight, add 90 days to the build date
  73. return Calendar.current.date(byAdding: .day, value: 90, to: buildDate)!
  74. } else {
  75. // For Xcode builds, use the provisioning profile's expiration date
  76. if let provision = MobileProvision.read() {
  77. return provision.expirationDate
  78. } else {
  79. return .distantFuture
  80. }
  81. }
  82. }
  83. // Expiration header based on build type
  84. var expirationHeaderString: String {
  85. if isTestFlightBuild() {
  86. return "TestFlight Expires"
  87. } else {
  88. return "App Expires"
  89. }
  90. }
  91. }