Skip to content

Commit

Permalink
Bring in navigation infrastructure (#6)
Browse files Browse the repository at this point in the history
  • Loading branch information
KatherineInCode authored Mar 29, 2024
1 parent bda51fc commit a2fc89d
Show file tree
Hide file tree
Showing 267 changed files with 10,107 additions and 309 deletions.
4 changes: 1 addition & 3 deletions .swiftlint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,7 @@ opt_in_rules:
- yoda_condition

excluded:
- BitwardenShared/UI/Platform/Application/Support/Generated
- BitwardenWatchApp
- BitwardenWatchShared
- AuthenticatorShared/UI/Platform/Application/Support/Generated
- build
- vendor/bundle

Expand Down
50 changes: 50 additions & 0 deletions Authenticator/Application/AppDelegate.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import AuthenticatorShared
import UIKit

/// A protocol for an `AppDelegate` that can be used by the `SceneDelegate` to look up the
/// `AppDelegate` when the app is running (`AppDelegate`) or testing (`TestingAppDelegate`).
///
protocol AppDelegateType: AnyObject {
/// The processor that manages application level logic.
var appProcessor: AppProcessor? { get }

/// Whether the app is running for unit tests.
var isTesting: Bool { get }
}

/// The app's `UIApplicationDelegate` which serves as the entry point into the app.
///
class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate, AppDelegateType {
// MARK: Properties

/// The processor that manages application level logic.
var appProcessor: AppProcessor?

/// Whether the app is running for unit tests.
var isTesting: Bool {
ProcessInfo.processInfo.arguments.contains("-testing")
}

// MARK: Methods

func application(
_: UIApplication,
didFinishLaunchingWithOptions _: [UIApplication.LaunchOptionsKey: Any]? = nil
) -> Bool {
// Exit early if testing to avoid running any app functionality.
guard !isTesting else { return true }

UNUserNotificationCenter.current().delegate = self

#if DEBUG
let errorReporter = OSLogErrorReporter()
#else
let errorReporter = CrashlyticsErrorReporter()
#endif

let services = ServiceContainer()
let appModule = DefaultAppModule(services: services)
appProcessor = AppProcessor(appModule: appModule, services: services)
return true
}
}
22 changes: 11 additions & 11 deletions Authenticator/Application/AuthenticatorApp.swift
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import SwiftUI

@main
struct AuthenticatorApp: App {
let persistenceController = PersistenceController.shared

var body: some Scene {
WindowGroup {
ContentView()
.environment(\.managedObjectContext, persistenceController.container.viewContext)
}
}
}
//@main
//struct AuthenticatorApp: App {
// let persistenceController = PersistenceController.shared
//
// var body: some Scene {
// WindowGroup {
// ContentView()
// .environment(\.managedObjectContext, persistenceController.container.viewContext)
// }
// }
//}
95 changes: 95 additions & 0 deletions Authenticator/Application/SceneDelegate.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import AuthenticatorShared
import SwiftUI
import UIKit

class SceneDelegate: UIResponder, UIWindowSceneDelegate {
// MARK: Properties

/// Whether the app is still starting up. This ensures the splash view isn't dismissed on start
/// up until the processor has shown the initial view.
var isStartingUp = true

/// Window shown as either the splash view on startup or when the app is backgrounded to
/// prevent private information from being visible in the app switcher.
var splashWindow: UIWindow?

/// The main window for this scene.
var window: UIWindow?

// MARK: Methods

func scene(
_ scene: UIScene,
willConnectTo session: UISceneSession,
options connectionOptions: UIScene.ConnectionOptions
) {
guard let windowScene = scene as? UIWindowScene else { return }
guard let appProcessor = (UIApplication.shared.delegate as? AppDelegateType)?.appProcessor else {
if (UIApplication.shared.delegate as? AppDelegateType)?.isTesting == true {
// If the app is running tests, show a testing view.
window = buildSplashWindow(windowScene: windowScene)
window?.makeKeyAndVisible()
}
return
}

let rootViewController = RootViewController()
let appWindow = UIWindow(windowScene: windowScene)
appWindow.rootViewController = rootViewController
appWindow.makeKeyAndVisible()
window = appWindow

// Splash window. This is initially visible until the app's processor has finished starting.
splashWindow = buildSplashWindow(windowScene: windowScene)

// Start the app's processor and show the splash view until the initial view is shown.
Task {
await appProcessor.start(
appContext: .mainApp,
navigator: rootViewController,
window: appWindow
)
hideSplash()
isStartingUp = false
}
}

func sceneWillResignActive(_ scene: UIScene) {
showSplash()
}

func sceneDidBecomeActive(_ scene: UIScene) {
guard !isStartingUp else { return }
hideSplash()
}

// MARK: Private

/// Builds the splash window for display in the specified window scene.
///
/// - Parameter windowScene: The window scene that the splash window will be shown in.
/// - Returns: A window containing the splash view.
///
private func buildSplashWindow(windowScene: UIWindowScene) -> UIWindow {
let window = UIWindow(windowScene: windowScene)
window.isHidden = false
window.rootViewController = UIStoryboard(
name: "LaunchScreen",
bundle: .main
).instantiateInitialViewController()
window.windowLevel = UIWindow.Level.alert + 1
return window
}

/// Hides the splash view.
private func hideSplash() {
UIView.animate(withDuration: UI.duration(0.4)) {
self.splashWindow?.alpha = 0
}
}

/// Shows the splash view.
private func showSplash() {
splashWindow?.alpha = 1
}
}
61 changes: 61 additions & 0 deletions Authenticator/Application/SceneDelegateTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import AuthenticatorShared
import XCTest

@testable import Authenticator

// MARK: - SceneDelegateTests

class SceneDelegateTests: AuthenticatorTestCase {
// MARK: Properties

var appCoordinator: MockCoordinator<AppRoute>!
var appModule: MockAppModule!
var subject: SceneDelegate!

// MARK: Setup & Teardown

override func setUp() {
super.setUp()
appCoordinator = MockCoordinator<AppRoute>()
appModule = MockAppModule()
appModule.appCoordinator = appCoordinator.asAnyCoordinator()
subject = SceneDelegate()
subject.appModule = appModule
}

override func tearDown() {
super.tearDown()
appModule = nil
subject = nil
}

// MARK: Tests

/// `scene(_:willConnectTo:options:)` with a `UIWindowScene` creates the app's UI.
func test_sceneWillConnectTo_withWindowScene() throws {
let session = TestInstanceFactory.create(UISceneSession.self)
let scene = TestInstanceFactory.create(UIWindowScene.self, properties: [
"session": session,
])
let options = TestInstanceFactory.create(UIScene.ConnectionOptions.self)
subject.scene(scene, willConnectTo: session, options: options)

XCTAssertNotNil(subject.appCoordinator)
XCTAssertNotNil(subject.window)
XCTAssertTrue(appCoordinator.isStarted)
}

/// `scene(_:willConnectTo:options:)` without a `UIWindowScene` fails to create the app's UI.
func test_sceneWillConnectTo_withNonWindowScene() throws {
let session = TestInstanceFactory.create(UISceneSession.self)
let scene = TestInstanceFactory.create(UIScene.self, properties: [
"session": session,
])
let options = TestInstanceFactory.create(UIScene.ConnectionOptions.self)
subject.scene(scene, willConnectTo: session, options: options)

XCTAssertNil(subject.appCoordinator)
XCTAssertNil(subject.window)
XCTAssertFalse(appCoordinator.isStarted)
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"images" : [
{
"filename" : "AppIcon-Beta.png",
"idiom" : "universal",
"platform" : "ios",
"size" : "1024x1024"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"images" : [
{
"filename" : "AppIcon-Dev.png",
"idiom" : "universal",
"platform" : "ios",
"size" : "1024x1024"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"images" : [
{
"filename" : "AppIcon-Dev.png",
"idiom" : "universal",
"platform" : "ios",
"size" : "1024x1024"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,4 @@
{
"colors" : [
{
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0xF7",
"green" : "0xF2",
"red" : "0xF2"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0x1E",
"green" : "0x1C",
"red" : "0x1C"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0x00",
"green" : "0x00",
"red" : "0x00"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0xFF",
"green" : "0xFF",
"red" : "0xFE"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Loading

0 comments on commit a2fc89d

Please sign in to comment.