Replace the ServiceLocator with direct dependency injection (#5548)

Removes the ServiceLocator singleton and instead directly injects the dependencies it was holding for more control over instantiation of the various component subtrees and better visibility over how they're being used.
This commit is contained in:
Michael
2026-05-14 01:45:49 -05:00
committed by GitHub
parent f70182d09a
commit ff5dcb040d
108 changed files with 971 additions and 729 deletions
+20 -19
View File
@@ -12,18 +12,19 @@ import PostHog
import Testing
final class AnalyticsTests {
private var appSettings: AppSettings
private var analyticsClient: AnalyticsClientMock
private let appSettings: AppSettings
private let analytics: AnalyticsService
private let analyticsClient: AnalyticsClientMock
private var posthogMock: PHGPostHogMock
@MainActor
init() {
AppSettings.resetAllSettings()
appSettings = AppSettings()
analyticsClient = AnalyticsClientMock()
analyticsClient.isRunning = false
ServiceLocator.shared.register(analytics: AnalyticsService(client: analyticsClient,
appSettings: appSettings))
analytics = .mock(analyticsClient, settings: appSettings)
posthogMock = PHGPostHogMock()
posthogMock.configureMockBehavior()
@@ -37,7 +38,7 @@ final class AnalyticsTests {
func analyticsPromptNewUser() {
// Given a fresh install of the app (without PostHog analytics having been set).
// When the user is prompted for analytics.
let showPrompt = ServiceLocator.shared.analytics.shouldShowAnalyticsPrompt
let showPrompt = analytics.shouldShowAnalyticsPrompt
// Then the prompt should be shown.
#expect(showPrompt, "A prompt should be shown for a new user.")
@@ -49,7 +50,7 @@ final class AnalyticsTests {
appSettings.analyticsConsentState = .optedOut
// When the user is prompted for analytics
let showPrompt = ServiceLocator.shared.analytics.shouldShowAnalyticsPrompt
let showPrompt = analytics.shouldShowAnalyticsPrompt
// Then no prompt should be shown.
#expect(!showPrompt, "A prompt should not be shown any more.")
@@ -61,7 +62,7 @@ final class AnalyticsTests {
appSettings.analyticsConsentState = .optedIn
// When the user is prompted for analytics
let showPrompt = ServiceLocator.shared.analytics.shouldShowAnalyticsPrompt
let showPrompt = analytics.shouldShowAnalyticsPrompt
// Then no prompt should be shown.
#expect(!showPrompt, "A prompt should not be shown any more.")
@@ -71,7 +72,7 @@ final class AnalyticsTests {
func analyticsPromptNotDisplayed() {
// Given a fresh install of the app Analytics should be disabled
#expect(appSettings.analyticsConsentState == .unknown)
#expect(!ServiceLocator.shared.analytics.isEnabled)
#expect(!analytics.isEnabled)
#expect(!analyticsClient.startAnalyticsConfigurationCalled)
}
@@ -79,10 +80,10 @@ final class AnalyticsTests {
func analyticsOptOut() {
// Given a fresh install of the app (without PostHog analytics having been set).
// When analytics is opt-out
ServiceLocator.shared.analytics.optOut()
analytics.optOut()
// Then analytics should be disabled
#expect(appSettings.analyticsConsentState == .optedOut)
#expect(!ServiceLocator.shared.analytics.isEnabled)
#expect(!analytics.isEnabled)
#expect(!analyticsClient.isRunning)
// Analytics client should have been stopped
#expect(analyticsClient.stopCalled)
@@ -92,10 +93,10 @@ final class AnalyticsTests {
func analyticsOptIn() {
// Given a fresh install of the app (without PostHog analytics having been set).
// When analytics is opt-in
ServiceLocator.shared.analytics.optIn()
analytics.optIn()
// The analytics should be enabled
#expect(appSettings.analyticsConsentState == .optedIn)
#expect(ServiceLocator.shared.analytics.isEnabled)
#expect(analytics.isEnabled)
// Analytics client should have been started
#expect(analyticsClient.startAnalyticsConfigurationCalled)
}
@@ -105,8 +106,8 @@ final class AnalyticsTests {
// Given an existing install of the app where the user previously declined the tracking
appSettings.analyticsConsentState = .optedOut
// Analytics should not start
#expect(!ServiceLocator.shared.analytics.isEnabled)
ServiceLocator.shared.analytics.startIfEnabled()
#expect(!analytics.isEnabled)
analytics.startIfEnabled()
#expect(!analyticsClient.startAnalyticsConfigurationCalled)
}
@@ -115,8 +116,8 @@ final class AnalyticsTests {
// Given an existing install of the app where the user previously accepted the tracking
appSettings.analyticsConsentState = .optedIn
// Analytics should start
#expect(ServiceLocator.shared.analytics.isEnabled)
ServiceLocator.shared.analytics.startIfEnabled()
#expect(analytics.isEnabled)
analytics.startIfEnabled()
#expect(analyticsClient.startAnalyticsConfigurationCalled)
}
@@ -214,14 +215,14 @@ final class AnalyticsTests {
func resetConsentState() {
// Given an existing install of the app where the user previously accpeted the tracking
appSettings.analyticsConsentState = .optedIn
#expect(!ServiceLocator.shared.analytics.shouldShowAnalyticsPrompt)
#expect(!analytics.shouldShowAnalyticsPrompt)
// When forgetting analytics consents
ServiceLocator.shared.analytics.resetConsentState()
analytics.resetConsentState()
// Then the analytics prompt should be presented again
#expect(appSettings.analyticsConsentState == .unknown)
#expect(ServiceLocator.shared.analytics.shouldShowAnalyticsPrompt)
#expect(analytics.shouldShowAnalyticsPrompt)
}
@Test