BuildDetails.swift 2.9 KB

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