Skip to content
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
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ class AppCoordinator: Coordinator, HasRootNavigator {
coordinator.navigate(to: route)
} else {
guard let rootNavigator else { return }
let tabNavigator = UITabBarController()
let tabNavigator = BitwardenTabBarController()
let coordinator = module.makeTabCoordinator(
errorReporter: services.errorReporter,
rootNavigator: rootNavigator,
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import BitwardenKit
import UIKit

// MARK: - BitwardenTabBarController

/// A `UITabBarController` subclass conforming to `TabNavigator`. This class manages
/// a set of tabs and handles dynamic appearance changes between light/dark mode.
///
class BitwardenTabBarController: UITabBarController, TabNavigator {
// MARK: Properties

/// The tabs used in the UITabBarController, mapping each `TabRoute` to its respective `Navigator`.
private var tabsAndNavigators: [TabRoute: any Navigator] = [:]

// MARK: AlertPresentable

var rootViewController: UIViewController? {
self
}

// MARK: TabNavigator

func navigator<Tab: TabRepresentable>(for tab: Tab) -> Navigator? {
viewControllers?[tab.index] as? Navigator
}

func setNavigators<Tab: Hashable & TabRepresentable>(_ tabs: [Tab: Navigator]) {
tabsAndNavigators = tabs as? [TabRoute: Navigator] ?? [:]

viewControllers = tabs
.sorted { $0.key.index < $1.key.index }
.compactMap { tab in
guard let viewController = tab.value.rootViewController else { return nil }
viewController.tabBarItem.title = tab.key.title
viewController.tabBarItem.image = tab.key.image
viewController.tabBarItem.selectedImage = tab.key.selectedImage
return viewController
}
}

// MARK: Lifecycle

/// Called when the trait collection (such as light/dark mode) changes.
///
/// UIKit does not seem to refresh the tab bar icon images dynamically when switching between
/// light/dark mode in mid-session. This override ensures the icons update correctly by re-applying
/// the navigators with the current tabs.
///
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
super.traitCollectionDidChange(previousTraitCollection)

if traitCollection.hasDifferentColorAppearance(comparedTo: previousTraitCollection) {
setNavigators(tabsAndNavigators)
}
}
}
1 change: 1 addition & 0 deletions AuthenticatorShared/UI/Platform/Tabs/TabRoute.swift
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import BitwardenKit
import BitwardenResources
import UIKit

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import BitwardenKit
import UIKit

public final class MockTabNavigator: TabNavigator {
public var navigators: [Navigator] = []
public var navigatorForTabValue: Int?
public var navigatorForTabReturns: Navigator?
public var rootViewController: UIViewController?
public var selectedIndex: Int = 0

public init() {}

public func setChildren(_ navigators: [Navigator]) {
self.navigators = navigators
}

public func navigator<Tab: TabRepresentable>(for tab: Tab) -> Navigator? {
navigatorForTabValue = tab.index
return navigatorForTabReturns
}

public func present(
_ viewController: UIViewController,
animated: Bool,
overFullscreen: Bool,
onCompletion: (() -> Void)?,
) {}

public func setNavigators<Tab: Hashable & TabRepresentable>(_ tabs: [Tab: Navigator]) {
navigators = tabs
.sorted { $0.key.index < $1.key.index }
.map(\.value)
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import BitwardenKit
import UIKit

// MARK: - TabNavigator

/// A navigator that displays a child navigators in a tab interface.
/// A navigator that displays a child navigator in a tab interface.
///
@MainActor
public protocol TabNavigator: Navigator {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import UIKit

// MARK: - BitwardenTabBarController

/// A `UITabBarController` subclass confirming to `TabBavigator`. This class manages
/// A `UITabBarController` subclass conforming to `TabNavigator`. This class manages
/// a set of tabs and handles dynamic appearance changes between light/dark mode.
///
class BitwardenTabBarController: UITabBarController, TabNavigator {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import BitwardenKit
import BitwardenKitMocks
import SwiftUI
import XCTest

@testable import BitwardenShared

class TabNavigatorTests: BitwardenTestCase {
class BitwardenTabBarControllerTests: BitwardenTestCase {
// MARK: Types

enum TestRoute: Int, Equatable, Hashable, TabRepresentable {
Expand Down
26 changes: 0 additions & 26 deletions BitwardenShared/UI/Platform/Tabs/TabRepresentable.swift

This file was deleted.

1 change: 1 addition & 0 deletions BitwardenShared/UI/Platform/Tabs/TabRoute.swift
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import BitwardenKit
import BitwardenResources
import UIKit

Expand Down
18 changes: 9 additions & 9 deletions GlobalTestHelpers/MockAppModule.swift
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ class MockAppModule:
authCoordinator.asAnyCoordinator()
}

func makeAuthRouter() -> BitwardenShared.AnyRouter<BitwardenShared.AuthEvent, BitwardenShared.AuthRoute> {
func makeAuthRouter() -> AnyRouter<AuthEvent, AuthRoute> {
authRouter.asAnyRouter()
}

Expand Down Expand Up @@ -183,19 +183,19 @@ class MockAppModule:

func makeTabCoordinator( // swiftlint:disable:this function_parameter_count
errorReporter _: ErrorReporter,
rootNavigator _: BitwardenKit.RootNavigator,
settingsDelegate _: BitwardenShared.SettingsCoordinatorDelegate,
tabNavigator _: BitwardenShared.TabNavigator,
vaultDelegate _: BitwardenShared.VaultCoordinatorDelegate,
vaultRepository _: BitwardenShared.VaultRepository,
) -> BitwardenShared.AnyCoordinator<BitwardenShared.TabRoute, Void> {
rootNavigator _: RootNavigator,
settingsDelegate _: SettingsCoordinatorDelegate,
tabNavigator _: TabNavigator,
vaultDelegate _: VaultCoordinatorDelegate,
vaultRepository _: VaultRepository,
) -> AnyCoordinator<TabRoute, Void> {
tabCoordinator.asAnyCoordinator()
}

func makeVaultCoordinator(
delegate _: BitwardenShared.VaultCoordinatorDelegate,
delegate _: VaultCoordinatorDelegate,
stackNavigator _: StackNavigator,
) -> BitwardenShared.AnyCoordinator<BitwardenShared.VaultRoute, AuthAction> {
) -> AnyCoordinator<VaultRoute, AuthAction> {
vaultCoordinator.asAnyCoordinator()
}

Expand Down
33 changes: 0 additions & 33 deletions GlobalTestHelpers/MockTabNavigator.swift

This file was deleted.