Skip to content

Commit 09b2d96

Browse files
committed
Add BuildSettingsEnvironment
1 parent 2eae56e commit 09b2d96

31 files changed

+113
-130
lines changed
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,45 @@
11
import Foundation
22

3-
struct BuildSettingsLiveContainer: BuildSettingsContainer {
4-
static let shared = BuildSettingsLiveContainer()
3+
extension BuildSettings {
4+
static let live = BuildSettings(bundle: .app)
55

6-
var pushNotificationAppID: String {
7-
infoPlistValue(forKey: "WPPushNotificationAppID")
8-
}
9-
10-
var appGroupName: String {
11-
infoPlistValue(forKey: "WPAppGroupName")
6+
init(bundle: Bundle) {
7+
self.pushNotificationAppID = bundle.infoPlistValue(forKey: "WPPushNotificationAppID")
8+
self.appGroupName = bundle.infoPlistValue(forKey: "WPAppGroupName")
9+
self.appKeychainAccessGroup = bundle.infoPlistValue(forKey: "WPAppKeychainAccessGroup")
1210
}
11+
}
1312

14-
var appKeychainAccessGroup: String {
15-
infoPlistValue(forKey: "WPAppKeychainAccessGroup")
13+
private extension Bundle {
14+
func infoPlistValue<T>(forKey key: String) -> T where T: LosslessStringConvertible {
15+
guard let object = object(forInfoDictionaryKey: key) else {
16+
fatalError("missing value for key: \(key)")
17+
}
18+
switch object {
19+
case let value as T:
20+
return value
21+
case let string as String:
22+
guard let value = T(string) else { fallthrough }
23+
return value
24+
default:
25+
fatalError("unexpected value: \(object) for key: \(key)")
26+
}
1627
}
1728
}
1829

19-
private func infoPlistValue<T>(forKey key: String) -> T where T: LosslessStringConvertible {
20-
guard let object = Bundle.app.object(forInfoDictionaryKey: key) else {
21-
fatalError("missing value for key: \(key)")
22-
}
23-
switch object {
24-
case let value as T:
25-
return value
26-
case let string as String:
27-
guard let value = T(string) else { fallthrough }
28-
return value
29-
default:
30-
fatalError("unexpected value: \(object) for key: \(key)")
31-
}
30+
private extension Bundle {
31+
/// Returns the `Bundle` for the host `.app`.
32+
///
33+
/// - If this is called from code already located in the main app's bundle or from a Pod/Framework,
34+
/// this will return the same as `Bundle.main`, aka the bundle of the app itself.
35+
/// - If this is called from an App Extension (Widget, ShareExtension, etc), this will return the bundle of the
36+
/// main app hosting said App Extension (while `Bundle.main` would return the App Extension itself)
37+
static let app: Bundle = {
38+
var url = Bundle.main.bundleURL
39+
while url.pathExtension != "app" && url.lastPathComponent != "/" {
40+
url.deleteLastPathComponent()
41+
}
42+
guard let appBundle = Bundle(url: url) else { fatalError("Unable to find the parent app bundle") }
43+
return appBundle
44+
}()
3245
}
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,25 @@
11
import Foundation
22

33
/// The container for Xcode previews.
4-
public struct BuildSettingsPreviewContainer: BuildSettingsContainer {
5-
public var pushNotificationAppID = "xcpreview_push_notification_id"
6-
public var appGroupName = "xcpreview_app_group_name"
7-
public var appKeychainAccessGroup = "xcpreview_app_keychain_access_group"
8-
9-
nonisolated(unsafe) static var shared = BuildSettingsPreviewContainer()
4+
extension BuildSettings {
5+
nonisolated(unsafe) static var preview = BuildSettings(
6+
pushNotificationAppID: "xcpreview_push_notification_id",
7+
appGroupName: "xcpreview_app_group_name",
8+
appKeychainAccessGroup: "xcpreview_app_keychain_access_group"
9+
)
1010
}
1111

1212
extension BuildSettings {
1313
/// Updates the preview settings for the lifetime of the given closure.
1414
/// Reverts to the original settings when done.
1515
@MainActor
16-
public static func withSettings<T>(_ configure: (inout BuildSettingsPreviewContainer) -> Void, perform closure: () -> T) -> T {
17-
var container = BuildSettingsPreviewContainer.shared
16+
public static func withSettings<T>(_ configure: (inout BuildSettings) -> Void, perform closure: () -> T) -> T {
17+
var container = BuildSettings.preview
1818
let original = container
1919
configure(&container)
20-
BuildSettingsPreviewContainer.shared = container
20+
BuildSettings.preview = container
2121
let value = closure()
22-
BuildSettingsPreviewContainer.shared = original
22+
BuildSettings.preview = original
2323
return value
2424
}
2525
}
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,5 @@
11
import Foundation
22

3-
public protocol BuildSettingsContainer: Sendable {
4-
var pushNotificationAppID: String { get }
5-
var appGroupName: String { get }
6-
var appKeychainAccessGroup: String { get }
7-
}
8-
9-
public enum AppBrand: String, Sendable {
10-
case wordpress
11-
case jetpack
12-
}
13-
143
/// Manages global build settings.
154
///
165
/// The build settings work differently depending on the environment:
@@ -22,29 +11,15 @@ public enum AppBrand: String, Sendable {
2211
/// changed at runtime.
2312
/// - **Test** – `BuildSettings` are not available when running unit tests as
2413
/// they are incompatible with parallelized tests and are generally not recommended.
25-
public enum BuildSettings {
26-
public static var current: BuildSettingsContainer {
14+
public struct BuildSettings: Sendable {
15+
public var pushNotificationAppID: String
16+
public var appGroupName: String
17+
public var appKeychainAccessGroup: String
18+
19+
public static var current: BuildSettings {
2720
switch BuildSettingsEnvironment.current {
28-
case .live: BuildSettingsLiveContainer.shared
29-
case .preview: BuildSettingsPreviewContainer.shared
21+
case .live: .live
22+
case .preview: .preview
3023
}
3124
}
3225
}
33-
34-
private enum BuildSettingsEnvironment {
35-
case live
36-
case preview
37-
38-
static let current: BuildSettingsEnvironment = {
39-
#if DEBUG
40-
let environment = ProcessInfo.processInfo.environment
41-
if environment["XCODE_RUNNING_FOR_PREVIEWS"] == "1" {
42-
return .preview
43-
}
44-
if NSClassFromString("XCTestCase") != nil {
45-
fatalError("BuildSettings are unavailable when running unit tests. Make sure to inject the values manually in system under test.")
46-
}
47-
#endif
48-
return .live
49-
}()
50-
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import Foundation
2+
3+
enum BuildSettingsEnvironment {
4+
case live
5+
case preview
6+
7+
static let current: BuildSettingsEnvironment = {
8+
#if DEBUG
9+
let environment = ProcessInfo.processInfo.environment
10+
if environment["XCODE_RUNNING_FOR_PREVIEWS"] == "1" {
11+
return .preview
12+
}
13+
if NSClassFromString("XCTestCase") != nil {
14+
fatalError("BuildSettings are unavailable when running unit tests. Make sure to inject the values manually in system under test.")
15+
}
16+
#endif
17+
return .live
18+
}()
19+
}

Modules/Sources/BuildSettingsKit/Bundle+App.swift

-24
This file was deleted.

WordPress/Classes/Services/NotificationSettingsService.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ class NotificationSettingsService {
146146

147147
notificationsServiceRemote?.registerDeviceForPushNotifications(
148148
token,
149-
pushNotificationAppId: BuildSettings.pushNotificationAppID,
149+
pushNotificationAppId: BuildSettings.current.pushNotificationAppID,
150150
success: success,
151151
failure: failure
152152
)

WordPress/Classes/Services/NotificationSupportService.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ open class NotificationSupportService: NSObject {
77
private let appKeychainAccessGroup: String
88

99
@objc convenience override init() {
10-
self.init(appKeychainAccessGroup: BuildSettings.appKeychainAccessGroup)
10+
self.init(appKeychainAccessGroup: BuildSettings.current.appKeychainAccessGroup)
1111
}
1212

1313
init(appKeychainAccessGroup: String) {

WordPress/Classes/Services/ShareExtensionService.swift

+2-2
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ open class ShareExtensionService: NSObject {
99

1010
@objc public convenience override init() {
1111
self.init(
12-
appGroupName: BuildSettings.appGroupName,
13-
appKeychainAccessGroup: BuildSettings.appKeychainAccessGroup
12+
appGroupName: BuildSettings.current.appGroupName,
13+
appKeychainAccessGroup: BuildSettings.current.appKeychainAccessGroup
1414
)
1515
}
1616

WordPress/Classes/Stores/StatsWidgetsStore.swift

+2-2
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ class StatsWidgetsStore {
1010
private let appKeychainAccessGroup: String
1111

1212
init(coreDataStack: CoreDataStack = ContextManager.shared,
13-
appGroupName: String = BuildSettings.appGroupName,
14-
appKeychainAccessGroup: String = BuildSettings.appKeychainAccessGroup) {
13+
appGroupName: String = BuildSettings.current.appGroupName,
14+
appKeychainAccessGroup: String = BuildSettings.current.appKeychainAccessGroup) {
1515
self.coreDataStack = coreDataStack
1616
self.appGroupName = appGroupName
1717
self.appKeychainAccessGroup = appKeychainAccessGroup

WordPress/Classes/System/WordPressAppDelegate.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,7 @@ class WordPressAppDelegate: UIResponder, UIApplicationDelegate {
203203
func application(_ application: UIApplication, handleEventsForBackgroundURLSession identifier: String, completionHandler: @escaping () -> Void) {
204204
// 21-Oct-2017: We are only handling background URLSessions initiated by the share extension so there
205205
// is no need to inspect the identifier beyond the simple check here.
206-
let appGroupName = BuildSettings.appGroupName
206+
let appGroupName = BuildSettings.current.appGroupName
207207
if identifier.contains(appGroupName) {
208208
let manager = ShareExtensionSessionManager(appGroup: appGroupName, backgroundSessionIdentifier: identifier)
209209
manager.backgroundSessionCompletionBlock = completionHandler

WordPress/Classes/Utility/Blogging Reminders/BloggingRemindersScheduler.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ class BloggingRemindersScheduler {
149149
}
150150

151151
private static func sharedDataFileURL() -> URL? {
152-
let sharedDirectory = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: BuildSettings.appGroupName)
152+
let sharedDirectory = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: BuildSettings.current.appGroupName)
153153
return sharedDirectory?.appendingPathComponent(defaultDataFileName)
154154
}
155155

WordPress/Classes/Utility/Migration/ContentMigrationCoordinator.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ import BuildSettingsKit
3232
dataMigrator: ContentDataMigrating = DataMigrator(),
3333
notificationCenter: NotificationCenter = .default,
3434
userPersistentRepository: UserPersistentRepository = UserDefaults.standard,
35-
sharedPersistentRepository: UserPersistentRepository? = UserDefaults(suiteName: BuildSettings.appGroupName),
35+
sharedPersistentRepository: UserPersistentRepository? = UserDefaults(suiteName: BuildSettings.current.appGroupName),
3636
eligibilityProvider: ContentMigrationEligibilityProvider = AppConfiguration(),
3737
tracker: MigrationAnalyticsTracker = .init()) {
3838
self.coreDataStack = coreDataStack

WordPress/Jetpack/Classes/Utility/DataMigrator.swift

+2-2
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,10 @@ final class DataMigrator {
2727
private let crashLogger: CrashLogging
2828

2929
init(coreDataStack: CoreDataStack = ContextManager.shared,
30-
backupLocation: URL? = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: BuildSettings.appGroupName)?.appendingPathComponent("WordPress.sqlite"),
30+
backupLocation: URL? = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: BuildSettings.current.appGroupName)?.appendingPathComponent("WordPress.sqlite"),
3131
keychainUtils: KeychainUtils = KeychainUtils(),
3232
localDefaults: UserPersistentRepository = UserDefaults.standard,
33-
sharedDefaults: UserPersistentRepository? = UserDefaults(suiteName: BuildSettings.appGroupName),
33+
sharedDefaults: UserPersistentRepository? = UserDefaults(suiteName: BuildSettings.current.appGroupName),
3434
crashLogger: CrashLogging = .main) {
3535
self.coreDataStack = coreDataStack
3636
self.backupLocation = backupLocation

WordPress/Jetpack/Classes/Utility/SharedDataIssueSolver.swift

+3-3
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ final class SharedDataIssueSolver: NSObject {
1111

1212
init(contextManager: CoreDataStack = ContextManager.shared,
1313
keychainUtils: KeychainUtils = KeychainUtils(),
14-
sharedDefaults: UserPersistentRepository? = UserDefaults(suiteName: BuildSettings.appGroupName),
14+
sharedDefaults: UserPersistentRepository? = UserDefaults(suiteName: BuildSettings.current.appGroupName),
1515
localFileStore: LocalFileStore = FileManager.default) {
1616
self.contextManager = contextManager
1717
self.keychainUtils = keychainUtils
@@ -148,8 +148,8 @@ private extension SharedDataIssueSolver {
148148
]
149149

150150
fileNames.forEach { fileName in
151-
guard let sourceURL = localFileStore.containerURL(forAppGroup: BuildSettings.appGroupName)?.appendingPathComponent(fileName.rawValue),
152-
let targetURL = localFileStore.containerURL(forAppGroup: BuildSettings.appGroupName)?.appendingPathComponent(fileName.valueForJetpack),
151+
guard let sourceURL = localFileStore.containerURL(forAppGroup: BuildSettings.current.appGroupName)?.appendingPathComponent(fileName.rawValue),
152+
let targetURL = localFileStore.containerURL(forAppGroup: BuildSettings.current.appGroupName)?.appendingPathComponent(fileName.valueForJetpack),
153153
localFileStore.fileExists(at: sourceURL) else {
154154
return
155155
}

WordPress/JetpackIntents/SitesDataProvider.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ class SitesDataProvider {
6060
// MARK: - Default Site
6161

6262
private var defaultSiteID: Int? {
63-
UserDefaults(suiteName: BuildSettings.appGroupName)?.object(forKey: WidgetStatsConfiguration.userDefaultsSiteIdKey) as? Int
63+
UserDefaults(suiteName: BuildSettings.current.appGroupName)?.object(forKey: WidgetStatsConfiguration.userDefaultsSiteIdKey) as? Int
6464
}
6565

6666
var defaultSite: Site? {

WordPress/JetpackStatsWidgets/Helpers/WidgetDataReader.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ final class WidgetDataReader<T: HomeWidgetData> {
66
let userDefaults: UserDefaults?
77
let cacheReader: WidgetDataCacheReader
88

9-
init(_ userDefaults: UserDefaults? = UserDefaults(suiteName: BuildSettings.appGroupName),
9+
init(_ userDefaults: UserDefaults? = UserDefaults(suiteName: BuildSettings.current.appGroupName),
1010
_ cacheReader: any WidgetDataCacheReader = HomeWidgetDataFileReader()
1111
) {
1212
self.userDefaults = userDefaults

WordPress/JetpackStatsWidgets/LockScreenWidgets/LockScreenSiteListProvider.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ struct LockScreenSiteListProvider<T: HomeWidgetData>: IntentTimelineProvider {
1313
let minElapsedTimeToRefresh = 1
1414

1515
private var defaultSiteID: Int? {
16-
UserDefaults(suiteName: BuildSettings.appGroupName)?.object(forKey: WidgetStatsConfiguration.userDefaultsSiteIdKey) as? Int
16+
UserDefaults(suiteName: BuildSettings.current.appGroupName)?.object(forKey: WidgetStatsConfiguration.userDefaultsSiteIdKey) as? Int
1717
}
1818

1919
private let widgetDataLoader = WidgetDataReader<T>()

WordPress/JetpackStatsWidgets/LockScreenWidgets/LockScreenStatsWidget.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import SwiftUI
44
import JetpackStatsWidgetsCore
55

66
struct LockScreenStatsWidget<T: LockScreenStatsWidgetConfig>: Widget {
7-
private let tracks = Tracks(appGroupName: BuildSettings.appGroupName)
7+
private let tracks = Tracks(appGroupName: BuildSettings.current.appGroupName)
88
private let config: T
99

1010
init(config: T) {

WordPress/JetpackStatsWidgets/Model/HomeWidgetData.swift

+4-4
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ extension HomeWidgetData {
88
static func read(from cache: HomeWidgetCache<Self>? = nil) -> [Int: Self]? {
99

1010
let cache = cache ?? HomeWidgetCache<Self>(fileName: Self.filename,
11-
appGroup: BuildSettings.appGroupName)
11+
appGroup: BuildSettings.current.appGroupName)
1212
do {
1313
return try cache.read()
1414
} catch {
@@ -20,7 +20,7 @@ extension HomeWidgetData {
2020
static func write(items: [Int: Self], to cache: HomeWidgetCache<Self>? = nil) {
2121

2222
let cache = cache ?? HomeWidgetCache<Self>(fileName: Self.filename,
23-
appGroup: BuildSettings.appGroupName)
23+
appGroup: BuildSettings.current.appGroupName)
2424

2525
do {
2626
try cache.write(items: items)
@@ -31,7 +31,7 @@ extension HomeWidgetData {
3131

3232
static func delete(cache: HomeWidgetCache<Self>? = nil) {
3333
let cache = cache ?? HomeWidgetCache<Self>(fileName: Self.filename,
34-
appGroup: BuildSettings.appGroupName)
34+
appGroup: BuildSettings.current.appGroupName)
3535

3636
do {
3737
try cache.delete()
@@ -42,7 +42,7 @@ extension HomeWidgetData {
4242

4343
static func setItem(item: Self, to cache: HomeWidgetCache<Self>? = nil) {
4444
let cache = cache ?? HomeWidgetCache<Self>(fileName: Self.filename,
45-
appGroup: BuildSettings.appGroupName)
45+
appGroup: BuildSettings.current.appGroupName)
4646

4747
do {
4848
try cache.setItem(item: item)

WordPress/JetpackStatsWidgets/Remote service/StatsWidgetsService.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,7 @@ private extension StatsWidgetsService {
222222
let token = try SFHFKeychainUtils.getPasswordForUsername(
223223
WidgetStatsConfiguration.keychainTokenKey,
224224
andServiceName: WidgetStatsConfiguration.keychainServiceName,
225-
accessGroup: BuildSettings.appKeychainAccessGroup
225+
accessGroup: BuildSettings.current.appKeychainAccessGroup
226226
)
227227
let wpApi = WordPressComRestApi(oAuthToken: token)
228228
return StatsServiceRemoteV2(wordPressComRestApi: wpApi, siteID: widgetData.siteID, siteTimezone: widgetData.timeZone)

WordPress/JetpackStatsWidgets/SiteListProvider.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ struct SiteListProvider<T: HomeWidgetData>: IntentTimelineProvider {
1515
let minElapsedTimeToRefresh = 1
1616

1717
private var defaultSiteID: Int? {
18-
UserDefaults(suiteName: BuildSettings.appGroupName)?.object(forKey: WidgetStatsConfiguration.userDefaultsSiteIdKey) as? Int
18+
UserDefaults(suiteName: BuildSettings.current.appGroupName)?.object(forKey: WidgetStatsConfiguration.userDefaultsSiteIdKey) as? Int
1919
}
2020

2121
private let widgetDataLoader = WidgetDataReader<T>()

0 commit comments

Comments
 (0)