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

BIT-174: Adds app-wide navigation layer #16

Merged
merged 4 commits into from
Sep 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 4 additions & 0 deletions Bitwarden/Application/AppDelegate.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import BitwardenShared
import UIKit

class AppDelegate: UIResponder, UIApplicationDelegate {}
37 changes: 37 additions & 0 deletions Bitwarden/Application/SceneDelegate.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import BitwardenShared
import UIKit

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

/// The root module to use to create sub-coordinators.
var appModule: AppModule = DefaultAppModule()

/// The root coordinator of this scene.
var appCoordinator: AnyCoordinator<AppRoute>?

/// 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
}

let appWindow = UIWindow(windowScene: windowScene)
let rootViewController = RootViewController()
let coordinator = appModule.makeAppCoordinator(navigator: rootViewController)
coordinator.start()

appWindow.rootViewController = rootViewController
appWindow.makeKeyAndVisible()
appCoordinator = coordinator
window = appWindow
}
}
61 changes: 61 additions & 0 deletions Bitwarden/Application/SceneDelegateTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import BitwardenShared
import XCTest

@testable import Bitwarden

// MARK: - SceneDelegateTests

class SceneDelegateTests: BitwardenTestCase {
// 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)
}
}
151 changes: 84 additions & 67 deletions Bitwarden/Application/Support/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -2,46 +2,18 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>MinimumOSVersion</key>
<string>15.0</string>
<key>CADisableMinimumFrameDurationOnPhone</key>
<true/>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleDisplayName</key>
<string>Bitwarden</string>
<key>CFBundleName</key>
<string>Bitwarden</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleShortVersionString</key>
<string>$(MARKETING_VERSION)</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>CFBundleIconName</key>
<string>AppIcon</string>
<key>CFBundlePackageType</key>
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLSchemes</key>
<array>
<string>bitwarden</string>
<string>org-appextension-feature-password-management</string>
</array>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLName</key>
<string>com.8bit.bitwarden.url</string>
</dict>
<dict>
<key>CFBundleURLName</key>
<string>com.8bit.bitwarden</string>
<key>CFBundleURLSchemes</key>
<array>
<string>otpauth</string>
</array>
</dict>
</array>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleLocalizations</key>
<array>
<string>en</string>
Expand Down Expand Up @@ -80,56 +52,101 @@
<string>el</string>
<string>th</string>
</array>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>UISupportedInterfaceOrientations</key>
<key>CFBundleName</key>
<string>Bitwarden</string>
<key>CFBundlePackageType</key>
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
<key>CFBundleShortVersionString</key>
<string>$(MARKETING_VERSION)</string>
<key>CFBundleURLTypes</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLName</key>
<string>com.8bit.bitwarden.url</string>
<key>CFBundleURLSchemes</key>
<array>
<string>bitwarden</string>
<string>org-appextension-feature-password-management</string>
</array>
</dict>
<dict>
<key>CFBundleURLName</key>
<string>com.8bit.bitwarden</string>
<key>CFBundleURLSchemes</key>
<array>
<string>otpauth</string>
</array>
</dict>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>ITSAppUsesNonExemptEncryption</key>
<true/>
<key>ITSEncryptionExportComplianceCode</key>
<string>ecf076d3-4824-4d7b-b716-2a9a47d7d296</string>
<key>MinimumOSVersion</key>
<string>15.0</string>
<key>NFCReaderUsageDescription</key>
<string>Use Yubikeys for two-factor authentication.</string>
<key>NSCameraUsageDescription</key>
<string>Scan QR codes</string>
<key>NSFaceIDUsageDescription</key>
<string>Use Face ID to unlock your vault.</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>This app requires access to the photo library in order to share files.</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>
</dict>
</array>
</dict>
</dict>
<key>UIBackgroundModes</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
<string>remote-notification</string>
</array>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile~ipad</key>
<string>LaunchScreen</string>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
<key>UIBackgroundModes</key>
<array>
<string>remote-notification</string>
</array>
<key>UIStatusBarHidden</key>
<true/>
<key>UIRequiredDeviceCapabilities</key>
<dict>
<key>arm64</key>
<true/>
</dict>
<key>CADisableMinimumFrameDurationOnPhone</key>
<key>UIStatusBarHidden</key>
<true/>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
<key>XSAppIconAssets</key>
<string>Resources/Assets.xcassets/AppIcons.appiconset</string>
<key>ITSAppUsesNonExemptEncryption</key>
<true/>
<key>ITSEncryptionExportComplianceCode</key>
<string>ecf076d3-4824-4d7b-b716-2a9a47d7d296</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>This app requires access to the photo library in order to share files.</string>
<key>NSCameraUsageDescription</key>
<string>Scan QR codes</string>
<key>NSFaceIDUsageDescription</key>
<string>Use Face ID to unlock your vault.</string>
<key>NFCReaderUsageDescription</key>
<string>Use Yubikeys for two-factor authentication.</string>
</dict>
</plist>
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import Foundation

enum TestInstanceFactory {
static func create<T: NSObject>(_ type: T.Type, properties: [String: Any] = [:]) -> T {
let instance = type.init()
for property in properties {
instance.setValue(property.value, forKey: property.key)
}
return instance
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,7 @@ import UIKit

@testable import Bitwarden

class TestingAppDelegate: NSObject, UIApplicationDelegate {
var window: UIWindow?

func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
let window = UIWindow(frame: UIScreen.main.bounds)
window.rootViewController = UIViewController()
window.makeKeyAndVisible()
self.window = window
return true
}
}
/// A replacement for `AppDelegate` that allows for checking that certain app delegate methods get called at the
/// appropriate times during unit tests.
///
class TestingAppDelegate: NSObject, UIApplicationDelegate {}
8 changes: 8 additions & 0 deletions Bitwarden/Application/main.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import UIKit

/// Determine the app delegate class that should be used during this launch of the app. If the `TestingAppDelegate`
/// can be found, the app was launched in a test environment, and we should use the `TestingAppDelegate` for
/// handling all app lifecycle events.
private let appDelegateClass: AnyClass = NSClassFromString("BitwardenTests.TestingAppDelegate") ?? AppDelegate.self

UIApplicationMain(CommandLine.argc, CommandLine.unsafeArgv, nil, NSStringFromClass(appDelegateClass))
10 changes: 0 additions & 10 deletions Bitwarden/BitwardenApp.swift

This file was deleted.

19 changes: 0 additions & 19 deletions Bitwarden/ContentView.swift

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class APIServiceTests: BitwardenTestCase {
}

/// `init(client:)` sets the default base URLs for the HTTP services.
func testInitDefaultURLs() {
func test_init_defaultURLs() {
let apiServiceBaseURL = subject.apiService.baseURL
XCTAssertEqual(apiServiceBaseURL, URL(string: "https://vault.bitwarden.com/api")!)

Expand Down
Loading