Skip to content

Commit

Permalink
Merge pull request #838 from Infomaniak/swiftui-app
Browse files Browse the repository at this point in the history
feat: SwiftUI app
  • Loading branch information
PhilippeWeidmann committed Jul 10, 2023
2 parents df3f450 + c2477b4 commit 91ea015
Show file tree
Hide file tree
Showing 43 changed files with 672 additions and 709 deletions.
87 changes: 6 additions & 81 deletions Mail/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,31 +17,19 @@
*/

import CocoaLumberjackSwift
import InfomaniakBugTracker
import InfomaniakCore
import InfomaniakCoreUI
import InfomaniakDI
import InfomaniakLogin
import InfomaniakNotifications
import MailCore
import Sentry
import SwiftUI
import UIKit

@main
class AppDelegate: UIResponder, UIApplicationDelegate {
private let notificationCenterDelegate = NotificationCenterDelegate()
private var accountManager: AccountManager!
static var orientationLock = UIInterfaceOrientationMask.all

func application(_ application: UIApplication,
willFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool {
Logging.initLogging()
setupDI()
DDLogInfo("Application starting in foreground ? \(UIApplication.shared.applicationState != .background)")
accountManager = AccountManager.instance
ApiFetcher.decoder.dateDecodingStrategy = .iso8601

func application(
_ application: UIApplication,
willFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil
) -> Bool {
UNUserNotificationCenter.current().delegate = notificationCenterDelegate
Task {
// Ask permission app launch
Expand All @@ -52,17 +40,11 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
return true
}

func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
return true
}

func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
@InjectService var notificationService: InfomaniakNotifications
for account in accountManager.accounts {
for account in AccountManager.instance.accounts {
guard account.token != nil else { continue }
let userApiFetcher = accountManager.getApiFetcher(for: account.userId, token: account.token)
let userApiFetcher = AccountManager.instance.getApiFetcher(for: account.userId, token: account.token)
Task {
await notificationService.updateRemoteNotificationsTokenIfNeeded(tokenData: deviceToken,
userApiFetcher: userApiFetcher)
Expand All @@ -74,65 +56,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
DDLogError("Failed registering for notifications: \(error)")
}

// MARK: UISceneSession Lifecycle

func application(_ application: UIApplication,
configurationForConnecting connectingSceneSession: UISceneSession,
options: UIScene.ConnectionOptions) -> UISceneConfiguration {
// Called when a new scene session is being created.
// Use this method to select a configuration to create the new scene with.
return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
}

func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {
// Called when the user discards a scene session.
// If any sessions were discarded while the application was not running, this will be called shortly after
// application:didFinishLaunchingWithOptions.
// Use this method to release any resources that were specific to the discarded scenes, as they will not return.
}

func application(_ application: UIApplication,
supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask {
return AppDelegate.orientationLock
}

func setupDI() {
let networkLoginService = Factory(type: InfomaniakNetworkLoginable.self) { _, _ in
InfomaniakNetworkLogin(clientId: MailApiFetcher.clientId)
}
let loginService = Factory(type: InfomaniakLoginable.self) { _, _ in
InfomaniakLogin(clientId: MailApiFetcher.clientId)
}
let keychainHelper = Factory(type: KeychainHelper.self) { _, _ in
KeychainHelper(accessGroup: AccountManager.accessGroup)
}
let notificationService = Factory(type: InfomaniakNotifications.self) { _, _ in
InfomaniakNotifications(appGroup: AccountManager.appGroup)
}
let appLockHelper = Factory(type: AppLockHelper.self) { _, _ in
AppLockHelper()
}
let bugTracker = Factory(type: BugTracker.self) { _, _ in
BugTracker(info: BugTrackerInfo(project: "app-mobile-mail", gitHubRepoName: "ios-mail", appReleaseType: .beta))
}
let matomoUtils = Factory(type: MatomoUtils.self) { _, _ in
MatomoUtils(siteId: Constants.matomoId, baseURL: URLConstants.matomo.url)
}
let avoider = Factory(type: SnackBarAvoider.self) { _, _ in
SnackBarAvoider()
}
let draftManager = Factory(type: DraftManager.self) { _, _ in
DraftManager()
}

SimpleResolver.sharedResolver.store(factory: networkLoginService)
SimpleResolver.sharedResolver.store(factory: loginService)
SimpleResolver.sharedResolver.store(factory: notificationService)
SimpleResolver.sharedResolver.store(factory: keychainHelper)
SimpleResolver.sharedResolver.store(factory: appLockHelper)
SimpleResolver.sharedResolver.store(factory: bugTracker)
SimpleResolver.sharedResolver.store(factory: matomoUtils)
SimpleResolver.sharedResolver.store(factory: avoider)
SimpleResolver.sharedResolver.store(factory: draftManager)
}
}
9 changes: 1 addition & 8 deletions Mail/Components/MailboxListView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,6 @@ import RealmSwift
import SwiftUI

struct MailboxListView: View {
@Environment(\.window) private var window

@AppStorage(UserDefaults.shared.key(.accentColor)) private var accentColor = DefaultPreferences.accentColor

@ObservedResults(
Expand All @@ -48,12 +46,7 @@ struct MailboxListView: View {
Spacer()

NavigationLink {
AddMailboxView { mailbox in
DispatchQueue.main.async {
guard let mailbox = mailbox else { return }
(window?.windowScene?.delegate as? SceneDelegate)?.switchMailbox(mailbox)
}
}
AddMailboxView()
} label: {
MailResourcesAsset.addCircle.swiftUIImage
.resizable()
Expand Down
2 changes: 0 additions & 2 deletions Mail/Components/UnavailableMailboxListView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,6 @@ import RealmSwift
import SwiftUI

struct UnavailableMailboxListView: View {
@Environment(\.window) private var window

@AppStorage(UserDefaults.shared.key(.accentColor)) private var accentColor = DefaultPreferences.accentColor

@ObservedResults(
Expand Down
21 changes: 0 additions & 21 deletions Mail/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -55,25 +55,6 @@
<string>To be able to lock the app</string>
<key>NSLocalNetworkUsageDescription</key>
<string>Atlantis would use Bonjour Service to discover Proxyman app from your local network.</string>
<key>UIApplicationSceneManifest</key>
<dict>
<key>UIApplicationSupportsMultipleScenes</key>
<false/>
<key>UISceneConfigurations</key>
<dict>
<key>UIWindowSceneSessionRoleApplication</key>
<array>
<dict>
<key>UISceneConfigurationName</key>
<string>Default Configuration</string>
<key>UISceneDelegateClassName</key>
<string>$(PRODUCT_MODULE_NAME).SceneDelegate</string>
<key>UISceneStoryboardFile</key>
<string>Main</string>
</dict>
</array>
</dict>
</dict>
<key>UIApplicationSupportsIndirectInputEvents</key>
<true/>
<key>UIBackgroundModes</key>
Expand All @@ -82,8 +63,6 @@
</array>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
Expand Down
8 changes: 6 additions & 2 deletions Mail/LockedAppView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ import SwiftUI

struct LockedAppView: View {
@LazyInjectService var appLockHelper: AppLockHelper
@Environment(\.window) var window

@EnvironmentObject var navigationState: NavigationState

var body: some View {
ZStack {
Expand Down Expand Up @@ -61,7 +62,10 @@ struct LockedAppView: View {
private func unlockApp() {
Task {
if (try? await appLockHelper.evaluatePolicy(reason: MailResourcesStrings.Localizable.lockAppTitle)) == true {
await (window?.windowScene?.delegate as? SceneDelegate)?.showMainView()
appLockHelper.setTime()
Task {
navigationState.transitionToRootViewDestination(.mainView)
}
}
}
}
Expand Down
162 changes: 162 additions & 0 deletions Mail/MailApp.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
/*
Infomaniak Mail - iOS App
Copyright (C) 2022 Infomaniak Network SA
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

import CocoaLumberjackSwift
import InfomaniakBugTracker
import InfomaniakCore
import InfomaniakCoreUI
import InfomaniakDI
import InfomaniakLogin
import InfomaniakNotifications
import MailCore
import Sentry
import SwiftUI
import UIKit

public struct EarlyDIHook {
public init() {
// setup DI ASAP
setupDI()
}

func setupDI() {
let networkLoginService = Factory(type: InfomaniakNetworkLoginable.self) { _, _ in
InfomaniakNetworkLogin(clientId: MailApiFetcher.clientId)
}
let loginService = Factory(type: InfomaniakLoginable.self) { _, _ in
InfomaniakLogin(clientId: MailApiFetcher.clientId)
}
let keychainHelper = Factory(type: KeychainHelper.self) { _, _ in
KeychainHelper(accessGroup: AccountManager.accessGroup)
}
let notificationService = Factory(type: InfomaniakNotifications.self) { _, _ in
InfomaniakNotifications(appGroup: AccountManager.appGroup)
}
let appLockHelper = Factory(type: AppLockHelper.self) { _, _ in
AppLockHelper()
}
let bugTracker = Factory(type: BugTracker.self) { _, _ in
BugTracker(info: BugTrackerInfo(project: "app-mobile-mail", gitHubRepoName: "ios-mail", appReleaseType: .beta))
}
let matomoUtils = Factory(type: MatomoUtils.self) { _, _ in
MatomoUtils(siteId: Constants.matomoId, baseURL: URLConstants.matomo.url)
}
let avoider = Factory(type: SnackBarAvoider.self) { _, _ in
SnackBarAvoider()
}
let draftManager = Factory(type: DraftManager.self) { _, _ in
DraftManager()
}

SimpleResolver.sharedResolver.store(factory: networkLoginService)
SimpleResolver.sharedResolver.store(factory: loginService)
SimpleResolver.sharedResolver.store(factory: notificationService)
SimpleResolver.sharedResolver.store(factory: keychainHelper)
SimpleResolver.sharedResolver.store(factory: appLockHelper)
SimpleResolver.sharedResolver.store(factory: bugTracker)
SimpleResolver.sharedResolver.store(factory: matomoUtils)
SimpleResolver.sharedResolver.store(factory: avoider)
SimpleResolver.sharedResolver.store(factory: draftManager)
}
}

@main
struct MailApp: App {
/// Making sure the DI is registered at a very early stage of the app launch.
private let dependencyInjectionHook = EarlyDIHook()
@LazyInjectService var appLockHelper: AppLockHelper

@UIApplicationDelegateAdaptor(AppDelegate.self) private var appDelegate

@Environment(\.scenePhase) private var scenePhase

@AppStorage(UserDefaults.shared.key(.accentColor)) private var accentColor = DefaultPreferences.accentColor
@AppStorage(UserDefaults.shared.key(.theme)) private var theme = DefaultPreferences.theme

@StateObject private var navigationState = NavigationState()

private let accountManager = AccountManager.instance

init() {
Logging.initLogging()
DDLogInfo("Application starting in foreground ? \(UIApplication.shared.applicationState != .background)")
ApiFetcher.decoder.dateDecodingStrategy = .iso8601
}

var body: some Scene {
WindowGroup {
RootView()
.environmentObject(navigationState)
.onAppear {
updateUI(accent: accentColor, theme: theme)
}
.onChange(of: theme) { newTheme in
updateUI(accent: accentColor, theme: newTheme)
}
.onChange(of: accentColor) { newAccentColor in
updateUI(accent: newAccentColor, theme: theme)
}
.onChange(of: scenePhase) { newScenePhase in
switch newScenePhase {
case .active:
refreshCacheData()
navigationState.transitionToLockViewIfNeeded()
case .background:
if UserDefaults.shared.isAppLockEnabled && navigationState.rootViewState != .appLocked {
appLockHelper.setTime()
}
case .inactive:
Task {
await NotificationsHelper.updateUnreadCountBadge()
}
@unknown default:
break
}
}
.onChange(of: accountManager.currentAccount) { _ in
refreshCacheData()
}
}
.defaultAppStorage(.shared)
}

func updateUI(accent: AccentColor, theme: Theme) {
let allWindows = UIApplication.shared.connectedScenes.compactMap { $0 as? UIWindowScene }.flatMap { $0.windows }
for window in allWindows {
window.tintColor = accent.primary.color
window.overrideUserInterfaceStyle = theme.interfaceStyle
}
}

func refreshCacheData() {
guard let currentAccount = accountManager.currentAccount else {
return
}

Task {
do {
try await accountManager.updateUser(for: currentAccount)
accountManager.enableBugTrackerIfAvailable()

try await accountManager.currentContactManager?.fetchContactsAndAddressBooks()
} catch {
DDLogError("Error while updating user account: \(error)")
}
}
}
}
5 changes: 3 additions & 2 deletions Mail/NotificationCenterDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,11 @@ class NotificationCenterDelegate: NSObject, UNUserNotificationCenterDelegate {
if AccountManager.instance.currentAccount.userId != mailboxManager.mailbox.userId {
if let switchedAccount = AccountManager.instance.accounts
.first(where: { $0.userId == mailboxManager.mailbox.userId }) {
(scene?.delegate as? SceneDelegate)?.switchAccount(switchedAccount, mailbox: mailbox)
AccountManager.instance.switchAccount(newAccount: switchedAccount)
AccountManager.instance.switchMailbox(newMailbox: mailbox)
}
} else {
(scene?.delegate as? SceneDelegate)?.switchMailbox(mailbox)
AccountManager.instance.switchMailbox(newMailbox: mailbox)
}
}

Expand Down
Loading

0 comments on commit 91ea015

Please sign in to comment.