AppGroupID.swift 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778
  1. // LoopFollow
  2. // AppGroupID.swift
  3. import Foundation
  4. /// Resolves the App Group identifier in a PR-safe way.
  5. ///
  6. /// Preferred contract:
  7. /// - App Group = "group.<baseBundleIdentifier>"
  8. /// - No team-specific hardcoding
  9. ///
  10. /// Important nuance:
  11. /// - Extensions often have a *different* bundle identifier than the main app.
  12. /// - To keep app + extensions aligned, we:
  13. /// 1) Prefer an explicit base bundle id if provided via Info.plist key.
  14. /// 2) Otherwise, apply a conservative suffix-stripping heuristic.
  15. /// 3) Fall back to the current bundle identifier.
  16. enum AppGroupID {
  17. /// Optional Info.plist key you can set in *both* app + extension targets
  18. /// to force a shared base bundle id (recommended for reliability).
  19. private static let baseBundleIDPlistKey = "LFAppGroupBaseBundleID"
  20. /// The base bundle identifier for the main app, with extension suffixes stripped.
  21. /// Usable from both the main app and extensions.
  22. static var baseBundleID: String {
  23. if let base = Bundle.main.object(forInfoDictionaryKey: baseBundleIDPlistKey) as? String,
  24. !base.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty
  25. {
  26. return base
  27. }
  28. let bundleID = Bundle.main.bundleIdentifier ?? "unknown"
  29. return stripLikelyExtensionSuffixes(from: bundleID)
  30. }
  31. /// URL scheme derived from the bundle identifier. Works across app and extensions.
  32. /// Default build: "loopfollow", second: "loopfollow2", third: "loopfollow3", etc.
  33. static var urlScheme: String {
  34. let base = baseBundleID
  35. // Extract the suffix after "LoopFollow" in the bundle ID
  36. // e.g. "com.TEAM.LoopFollow2" → "2", "com.TEAM.LoopFollow" → ""
  37. if let range = base.range(of: "LoopFollow", options: .backwards) {
  38. let suffix = base[range.upperBound...]
  39. return "loopfollow\(suffix)"
  40. }
  41. return "loopfollow"
  42. }
  43. static func current() -> String {
  44. "group.\(baseBundleID)"
  45. }
  46. private static func stripLikelyExtensionSuffixes(from bundleID: String) -> String {
  47. let knownSuffixes = [
  48. ".LiveActivity",
  49. ".LiveActivityExtension",
  50. ".LoopFollowLAExtension",
  51. ".Widget",
  52. ".WidgetExtension",
  53. ".Widgets",
  54. ".WidgetsExtension",
  55. ".Watch",
  56. ".WatchExtension",
  57. ".CarPlay",
  58. ".CarPlayExtension",
  59. ".Intents",
  60. ".IntentsExtension",
  61. ]
  62. for suffix in knownSuffixes {
  63. if bundleID.hasSuffix(suffix) {
  64. return String(bundleID.dropLast(suffix.count))
  65. }
  66. }
  67. return bundleID
  68. }
  69. }