Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor AppDelegate (milestone 1) #3727

Open
wants to merge 29 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
85d82b5
Move willResignActive code into Inactive state
jaceklyp Dec 3, 2024
ed3a56c
Move app shortcuts code into extension
jaceklyp Dec 3, 2024
cdff905
Move app shortcuts code into extension
jaceklyp Dec 3, 2024
75e8004
Make states api cleaner
jaceklyp Dec 4, 2024
e4f9be0
Clean more dependency hell
jaceklyp Dec 5, 2024
85c6416
Runnable state
jaceklyp Dec 9, 2024
dfd1e96
Testing
jaceklyp Dec 10, 2024
46621eb
Refactor the code not to use functions on self
jaceklyp Dec 10, 2024
b64e651
Add code to Launch that has been added on main the meantime
jaceklyp Dec 11, 2024
8e69357
Fix subscription code
jaceklyp Dec 11, 2024
33e7bde
Fix sync cancellable
jaceklyp Dec 11, 2024
3a4f7e1
Merge branch 'main' into jacek/refactor-app-delegate-m1
jaceklyp Dec 11, 2024
ac486b4
Make it buildable after merge
jaceklyp Dec 11, 2024
017d655
Make appdelegate work in dual mode and handle shortcuts
jaceklyp Dec 12, 2024
128f049
Add autoclear code to handleShortcutItem
jaceklyp Dec 13, 2024
7fee87d
Add comment
jaceklyp Dec 13, 2024
d6edc0b
Handle new cases in state machine
jaceklyp Dec 13, 2024
d6ed3de
Fix swiftlint
jaceklyp Dec 13, 2024
0174398
Fix compilation issue
jaceklyp Dec 13, 2024
f19431b
Fix tests
jaceklyp Dec 13, 2024
5e901f8
Fix tests
jaceklyp Dec 16, 2024
29564d2
Divide code into old and new app delegates
jaceklyp Dec 16, 2024
c11ea21
Fix syncCancellable not being reassigned
jaceklyp Dec 17, 2024
e5af352
Fix running shorcuts when app is backgrounded
jaceklyp Dec 17, 2024
8cca2ce
Introduce a kill switch for the new feature
jaceklyp Dec 18, 2024
b22ce95
Fix issues
jaceklyp Dec 18, 2024
0e0412d
Add testing state
jaceklyp Dec 18, 2024
785e6da
Fix leftover comments
jaceklyp Dec 18, 2024
af9e049
Merge branch 'main' into jacek/refactor-app-delegate-m1
jaceklyp Dec 18, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Core/PixelEvent.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1885,7 +1885,7 @@ extension Pixel.Event {
case .openAIChatFromAddressBar: return "m_aichat_addressbar_icon"

// MARK: Lifecycle
case .appDidTransitionToUnexpectedState: return "m_debug_app-did-transition-to-unexpected-state"
case .appDidTransitionToUnexpectedState: return "m_debug_app-did-transition-to-unexpected-state-2"

}
}
Expand Down
76 changes: 56 additions & 20 deletions DuckDuckGo.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/DuckDuckGo/BrowserServicesKit",
"state" : {
"revision" : "b71ed70ce9b0ef3ce51d4f96da0193ab70493944",
"version" : "221.3.0"
"branch" : "jacek/refactor-app-delegate",
"revision" : "3712ccc0b6867e08b6235083f7e754d537f0b5cb"
}
},
{
Expand Down
2 changes: 1 addition & 1 deletion DuckDuckGo/AppDelegate+AppDeepLinks.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
import UIKit
import Core

extension AppDelegate {
extension OldAppDelegate {

func handleAppDeepLink(_ app: UIApplication, _ mainViewController: MainViewController?, _ url: URL) -> Bool {
guard let mainViewController else { return false }
Expand Down
1,232 changes: 50 additions & 1,182 deletions DuckDuckGo/AppDelegate.swift

Large diffs are not rendered by default.

58 changes: 58 additions & 0 deletions DuckDuckGo/AppDependencies.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
//
// AppDependencies.swift
// DuckDuckGo
//
// Copyright © 2024 DuckDuckGo. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

import Subscription
import UIKit
import Core
import DDGSync
import Combine
import BrowserServicesKit

struct AppDependencies {

let accountManager: AccountManager
let vpnWorkaround: VPNRedditSessionWorkaround
let vpnFeatureVisibility: DefaultNetworkProtectionVisibility

let appSettings: AppSettings
let privacyStore: PrivacyUserDefaults

let uiService: UIService
let mainViewController: MainViewController

let voiceSearchHelper: VoiceSearchHelper
let autoClear: AutoClear
let autofillLoginSession: AutofillLoginSession
let marketplaceAdPostbackManager: MarketplaceAdPostbackManaging
let syncService: DDGSync
let syncDataProviders: SyncDataProviders
let isSyncInProgressCancellable: AnyCancellable
let privacyProDataReporter: PrivacyProDataReporting
let remoteMessagingClient: RemoteMessagingClient

let subscriptionService: SubscriptionService

let onboardingPixelReporter: OnboardingPixelReporter
let widgetRefreshModel: NetworkProtectionWidgetRefreshModel
let autofillPixelReporter: AutofillPixelReporter
let crashReportUploaderOnboarding: CrashCollectionOnboarding

var syncDidFinishCancellable: AnyCancellable?

}
13 changes: 8 additions & 5 deletions DuckDuckGo/AppLifecycle/AppStateMachine.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,27 +21,30 @@ import UIKit

enum AppEvent {

case launching(UIApplication, launchOptions: [UIApplication.LaunchOptionsKey: Any]?)
case activating(UIApplication)
case backgrounding(UIApplication)
case suspending(UIApplication)
case launching(UIApplication, isTesting: Bool)
case activating
case backgrounding
case suspending

case openURL(URL)
case handleShortcutItem(UIApplicationShortcutItem)

}

protocol AppState {

func apply(event: AppEvent) -> any AppState
mutating func apply(event: AppEvent) -> any AppState

}

protocol AppEventHandler {

@MainActor
func handle(_ event: AppEvent)
dus7 marked this conversation as resolved.
Show resolved Hide resolved

}

@MainActor
final class AppStateMachine: AppEventHandler {

private(set) var currentState: any AppState = Init()
Expand Down
97 changes: 42 additions & 55 deletions DuckDuckGo/AppLifecycle/AppStateTransitions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,11 @@ extension Init {

func apply(event: AppEvent) -> any AppState {
switch event {
case .launching(let application, let launchOptions):
return Launched(application: application, launchOptions: launchOptions)
case .launching(let application, let isTesting):
if isTesting {
return Testing(application: application)
}
return Launched(stateContext: makeStateContext(application: application))
default:
return handleUnexpectedEvent(event)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a note: make sure to rename the pixel (if you want to avoid using app version param) before actual tests so we can easily see if things are not working as expected.

}
Expand All @@ -35,14 +38,18 @@ extension Init {

extension Launched {

func apply(event: AppEvent) -> any AppState {
mutating func apply(event: AppEvent) -> any AppState {
switch event {
case .activating(let application):
return Active(application: application)
case .openURL:
case .activating:
return Active(stateContext: makeStateContext())
case .openURL(let url):
urlToOpen = url
return self
case .handleShortcutItem(let shortcutItem):
shortcutItemToHandle = shortcutItem
return self
case .backgrounding:
return InactiveBackground()
return Background(stateContext: makeStateContext())
case .launching, .suspending:
return handleUnexpectedEvent(event)
}
Expand All @@ -54,9 +61,13 @@ extension Active {

func apply(event: AppEvent) -> any AppState {
switch event {
case .suspending(let application):
return Inactive(application: application)
case .openURL:
case .suspending:
return Inactive(stateContext: makeStateContext())
case .openURL(let url):
openURL(url)
return self
case .handleShortcutItem(let shortcutItem):
handleShortcutItem(shortcutItem)
return self
case .launching, .activating, .backgrounding:
return handleUnexpectedEvent(event)
Expand All @@ -67,15 +78,16 @@ extension Active {

extension Inactive {

func apply(event: AppEvent) -> any AppState {
mutating func apply(event: AppEvent) -> any AppState {
switch event {
case .backgrounding(let application):
return Background(application: application)
case .activating(let application):
return Active(application: application)
case .openURL:
case .backgrounding:
return Background(stateContext: makeStateContext())
case .activating:
return Active(stateContext: makeStateContext())
case .openURL(let url):
urlToOpen = url
return self
case .launching, .suspending:
case .launching, .suspending, .handleShortcutItem:
return handleUnexpectedEvent(event)
}
}
Expand All @@ -84,55 +96,29 @@ extension Inactive {

extension Background {

func apply(event: AppEvent) -> any AppState {
mutating func apply(event: AppEvent) -> any AppState {
switch event {
case .activating(let application):
return Active(application: application)
case .openURL:
case .activating:
return Active(stateContext: makeStateContext())
case .openURL(let url):
urlToOpen = url
return self
case .backgrounding:
return DoubleBackground()
run()
return self
case .handleShortcutItem(let shortcutItem):
shortcutItemToHandle = shortcutItem
return self
case .launching, .suspending:
return handleUnexpectedEvent(event)
}
}

}

extension DoubleBackground {

func apply(event: AppEvent) -> any AppState {
// report event so we know what events can be called at this moment, but do not let SM be stuck in this state just not to be flooded with these events
_ = handleUnexpectedEvent(event)

switch event {
case .activating(let application):
return Active(application: application)
case .suspending(let application):
return Inactive(application: application)
case .launching, .backgrounding, .openURL:
return self
}

}

}

extension InactiveBackground {
extension Testing {

func apply(event: AppEvent) -> any AppState {
// report event so we know what events can be called at this moment, but do not let SM be stuck in this state just not to be flooded with these events
_ = handleUnexpectedEvent(event)

switch event {
case .activating(let application):
return Active(application: application)
case .suspending(let application):
return Inactive(application: application)
case .launching, .backgrounding, .openURL:
return self
}
}
func apply(event: AppEvent) -> any AppState { self }

}

Expand All @@ -145,6 +131,7 @@ extension AppEvent {
case .backgrounding: return "backgrounding"
case .suspending: return "suspending"
case .openURL: return "openURL"
case .handleShortcutItem: return "handleShortcutItem"
}
}

Expand Down
Loading
Loading