BuildDetails.swift 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  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 commitSha: String? {
  27. return dict["com-LoopFollow-commit-sha"] as? String
  28. }
  29. var branchAndSha: String {
  30. let branch = branch ?? "Unknown"
  31. let sha = commitSha ?? "Unknown"
  32. return "\(branch) \(sha)"
  33. }
  34. // Determine if the build is from TestFlight
  35. func isTestFlightBuild() -> Bool {
  36. #if targetEnvironment(simulator)
  37. return false
  38. #else
  39. if Bundle.main.url(forResource: "embedded", withExtension: "mobileprovision") != nil {
  40. return false
  41. }
  42. guard let receiptName = Bundle.main.appStoreReceiptURL?.lastPathComponent else {
  43. return false
  44. }
  45. return "sandboxReceipt".caseInsensitiveCompare(receiptName) == .orderedSame
  46. #endif
  47. }
  48. // Determine if the build is for Simulator
  49. func isSimulatorBuild() -> Bool {
  50. #if targetEnvironment(simulator)
  51. return true
  52. #else
  53. return false
  54. #endif
  55. }
  56. // Determine if the build is for Mac
  57. func isMacApp() -> Bool {
  58. #if targetEnvironment(macCatalyst)
  59. return true
  60. #else
  61. return false
  62. #endif
  63. }
  64. // Parse the build date string into a Date object
  65. func buildDate() -> Date? {
  66. guard let dateString = dict["com-LoopFollow-build-date"] as? String else {
  67. return nil
  68. }
  69. let formatter = ISO8601DateFormatter()
  70. return formatter.date(from: dateString)
  71. }
  72. // Calculate the expiration date based on the build type
  73. func calculateExpirationDate() -> Date {
  74. if isTestFlightBuild(), let buildDate = buildDate() {
  75. // For TestFlight, add 90 days to the build date
  76. return Calendar.current.date(byAdding: .day, value: 90, to: buildDate)!
  77. } else {
  78. // For Xcode builds, use the provisioning profile's expiration date
  79. if let provision = MobileProvision.read() {
  80. return provision.expirationDate
  81. } else {
  82. return .distantFuture
  83. }
  84. }
  85. }
  86. // Expiration header based on build type
  87. var expirationHeaderString: String {
  88. if isTestFlightBuild() {
  89. return String(localized: "TestFlight Expires")
  90. } else {
  91. return String(localized: "App Expires")
  92. }
  93. }
  94. }