-
Notifications
You must be signed in to change notification settings - Fork 80
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
PIA-1058: App navigation setup (#51)
* PIA-1058: App navigation setup * PIA-1058: Move external library entities compliance out of presentation layer
- Loading branch information
1 parent
b3d7dea
commit 4618b55
Showing
16 changed files
with
464 additions
and
7 deletions.
There are no files selected for viewing
9 changes: 9 additions & 0 deletions
9
PIA VPN-tvOS/Dashboard/CompositionRoot/DashboardFactory.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
17 changes: 17 additions & 0 deletions
17
PIA VPN-tvOS/Dashboard/Presentation/DashboardViewModel.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
|
||
|
||
import Foundation | ||
|
||
class DashboardViewModel: ObservableObject { | ||
|
||
let accountProvider: AccountProviderType | ||
|
||
init(accountProvider: AccountProviderType) { | ||
self.accountProvider = accountProvider | ||
} | ||
|
||
func logOut() { | ||
accountProvider.logout(nil) | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
|
||
import Foundation | ||
|
||
import SwiftUI | ||
|
||
typealias Destinations = Hashable | ||
|
||
// AppRouter enables any component to navigate the user to any screen defined within Destinations | ||
class AppRouter: ObservableObject { | ||
|
||
static let shared: AppRouter = AppRouter(with: NavigationPath()) | ||
|
||
@Published public var path: NavigationPath | ||
|
||
/// Returns the amount of stacked views. Useful during unit test validation | ||
var stackCount: Int { | ||
path.count | ||
} | ||
|
||
init(with path: NavigationPath) { | ||
self.path = path | ||
} | ||
|
||
func navigate(to destination: any Destinations) { | ||
path.append(destination) | ||
} | ||
|
||
func pop() { | ||
path.removeLast() | ||
} | ||
|
||
func goBackToRoot() { | ||
path.removeLast(path.count) | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
16 changes: 16 additions & 0 deletions
16
PIA VPN-tvOS/RootContainer/CompositionRoot/RootContainerFactory.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
|
||
import Foundation | ||
import PIALibrary | ||
|
||
class RootContainerFactory { | ||
static func makeRootContainerView() -> RootContainerView { | ||
RootContainerView(viewModel: makeRootContainerViewModel()) | ||
} | ||
|
||
private static func makeRootContainerViewModel() -> RootContainerViewModel { | ||
guard let defaultAccountProvider = Client.providers.accountProvider as? DefaultAccountProvider else { | ||
fatalError("Incorrect account provider type") | ||
} | ||
return RootContainerViewModel(accountProvider: defaultAccountProvider) | ||
} | ||
} |
62 changes: 62 additions & 0 deletions
62
PIA VPN-tvOS/RootContainer/Presentation/RootContainerViewModel.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
|
||
import Foundation | ||
import SwiftUI | ||
|
||
class RootContainerViewModel: ObservableObject { | ||
enum State { | ||
case splash | ||
case notActivated | ||
case activatedNotOnboarded | ||
case activated | ||
} | ||
|
||
@Published var state: State = .splash | ||
|
||
// TODO: Update this value from the Vpn OnBoarding installation profile screen | ||
@AppStorage(.kOnboardingVpnProfileInstalled) var onBoardingVpnProfileInstalled = true | ||
|
||
let accountProvider: AccountProviderType | ||
let notificationCenter: NotificationCenterType | ||
|
||
init(accountProvider: AccountProviderType, notificationCenter: NotificationCenterType = NotificationCenter.default) { | ||
|
||
self.accountProvider = accountProvider | ||
self.notificationCenter = notificationCenter | ||
updateState() | ||
subscribeToAccountUpdates() | ||
} | ||
|
||
deinit { | ||
notificationCenter.removeObserver(self) | ||
} | ||
|
||
private func updateState() { | ||
switch (accountProvider.isLoggedIn, onBoardingVpnProfileInstalled) { | ||
// logged in, vpn profile installed | ||
case (true, true): | ||
state = .activated | ||
// logged in, vpn profile not installed | ||
case (true, false): | ||
state = .activatedNotOnboarded | ||
// not logged in, any | ||
case (false, _): | ||
state = .notActivated | ||
} | ||
} | ||
|
||
private func subscribeToAccountUpdates() { | ||
notificationCenter.addObserver(self, selector: #selector(handleAccountDidLogin), name: .PIAAccountDidLogin, object: nil) | ||
|
||
notificationCenter.addObserver(self, selector: #selector(handleAccountDidLogout), name: .PIAAccountDidLogout, object: nil) | ||
} | ||
|
||
@objc func handleAccountDidLogin() { | ||
updateState() | ||
} | ||
|
||
@objc func handleAccountDidLogout() { | ||
updateState() | ||
} | ||
|
||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
|
||
import Foundation | ||
import SwiftUI | ||
|
||
struct RootContainerView: View { | ||
@ObservedObject var viewModel: RootContainerViewModel | ||
|
||
var body: some View { | ||
switch viewModel.state { | ||
case .splash: | ||
VStack { | ||
// TODO: Add Splash screen here | ||
} | ||
case .notActivated: | ||
LoginFactory.makeLoginView() | ||
case .activatedNotOnboarded: | ||
// TODO: Replace this view with the Onboarding Vpn Profile installation view | ||
VStack { | ||
Text("Show Onboarding vpn installation view") | ||
} | ||
case .activated: | ||
DashboardFactory.makeDashboardView() | ||
} | ||
} | ||
|
||
|
||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
|
||
import Foundation | ||
|
||
/// Expose Core Foundation APIs via protocols to the PIA app | ||
|
||
|
||
protocol NotificationCenterType { | ||
func addObserver( | ||
_ observer: Any, | ||
selector aSelector: Selector, | ||
name aName: NSNotification.Name?, | ||
object anObject: Any? | ||
) | ||
|
||
func removeObserver(_ observer: Any) | ||
|
||
// Add methods here from NSNotificationCenter as needed | ||
} | ||
|
||
extension NotificationCenter: NotificationCenterType {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
|
||
import Foundation | ||
|
||
extension String { | ||
static let kOnboardingVpnProfileInstalled = "kOnboardingVpnProfileInstalled" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
|
||
import Foundation | ||
import PIALibrary | ||
|
||
|
||
protocol AccountProviderType { | ||
var isLoggedIn: Bool { get } | ||
func logout(_ callback: ((Error?) -> Void)?) | ||
} | ||
|
||
extension DefaultAccountProvider: AccountProviderType { } |
23 changes: 23 additions & 0 deletions
23
PIA VPN-tvOSTests/Common/Mocks/AccountProviderTypeMock.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
// | ||
// AccountProviderTypeMock.swift | ||
// PIA VPN-tvOSTests | ||
// | ||
// Created by Laura S on 12/20/23. | ||
// Copyright © 2023 Private Internet Access Inc. All rights reserved. | ||
// | ||
|
||
import Foundation | ||
@testable import PIA_VPN_tvOS | ||
|
||
class AccountProviderTypeMock: AccountProviderType { | ||
var isLoggedIn: Bool = false | ||
|
||
private(set) var logoutCalled = false | ||
private(set) var logoutCalledAttempt = 0 | ||
func logout(_ callback: ((Error?) -> Void)?) { | ||
logoutCalled = true | ||
logoutCalledAttempt += 1 | ||
} | ||
|
||
|
||
} |
23 changes: 23 additions & 0 deletions
23
PIA VPN-tvOSTests/Dashboard/Mocks/NotificationCenterMock.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
|
||
import Foundation | ||
@testable import PIA_VPN_tvOS | ||
|
||
class NotificationCenterMock: NotificationCenterType { | ||
private(set) var addObserverCalled = false | ||
private(set) var addObserverCalledAttempt = 0 | ||
private(set) var addObserverCalledWithNotificationName: NSNotification.Name? = nil | ||
|
||
func addObserver(_ observer: Any, selector aSelector: Selector, name aName: NSNotification.Name?, object anObject: Any?) { | ||
addObserverCalled = true | ||
addObserverCalledAttempt += 1 | ||
addObserverCalledWithNotificationName = aName | ||
} | ||
|
||
private(set) var removeObserverCalled = false | ||
private(set) var remoververCalledAttempt = 0 | ||
func removeObserver(_ observer: Any) { | ||
removeObserverCalled = true | ||
remoververCalledAttempt += 1 | ||
} | ||
|
||
} |
File renamed without changes.
79 changes: 79 additions & 0 deletions
79
PIA VPN-tvOSTests/RootContainer/RootContainerViewModelTests.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
// | ||
// RootContainerViewModelTests.swift | ||
// PIA VPN-tvOSTests | ||
// | ||
// Created by Laura S on 12/19/23. | ||
// Copyright © 2023 Private Internet Access Inc. All rights reserved. | ||
// | ||
|
||
import XCTest | ||
@testable import PIA_VPN_tvOS | ||
|
||
final class RootContainerViewModelTests: XCTestCase { | ||
|
||
final class Fixture { | ||
let accountProvierMock = AccountProviderTypeMock() | ||
let notificationCenterMock = NotificationCenterMock() | ||
} | ||
|
||
var fixture: Fixture! | ||
var sut: RootContainerViewModel! | ||
|
||
override func setUp() { | ||
fixture = Fixture() | ||
} | ||
|
||
override func tearDown() { | ||
fixture = nil | ||
sut = nil | ||
UserDefaults.standard.removeObject(forKey: .kOnboardingVpnProfileInstalled) | ||
} | ||
|
||
private func initializeSut() { | ||
sut = RootContainerViewModel(accountProvider: fixture.accountProvierMock, notificationCenter: fixture.notificationCenterMock) | ||
} | ||
|
||
func testState_WhenUserIsNotAuthenticated() { | ||
// GIVEN that the user is not logged in | ||
fixture.accountProvierMock.isLoggedIn = false | ||
|
||
// WHEN the RootContainer is created | ||
initializeSut() | ||
|
||
// THEN the state becomes 'notActivated' | ||
XCTAssertEqual(sut.state, .notActivated) | ||
} | ||
|
||
func testState_WhenUserIsAuthenticatedAndVpnProfileNotInstalled() { | ||
// GIVEN that the user is logged in | ||
fixture.accountProvierMock.isLoggedIn = true | ||
// AND GIVEN that the Onboarding Vpn Profile is NOT installed | ||
stubOnboardingVpnInstallation(finished: false) | ||
|
||
// WHEN the RootContainer is created | ||
initializeSut() | ||
|
||
// THEN the state becomes 'activatedNotOnboarded' | ||
XCTAssertEqual(sut.state, .activatedNotOnboarded) | ||
} | ||
|
||
func testState_WhenUserIsAuthenticatedAndVpnProfileInstalled() { | ||
// GIVEN that the user is logged in | ||
fixture.accountProvierMock.isLoggedIn = true | ||
// AND GIVEN that the Onboarding Vpn Profile is installed | ||
stubOnboardingVpnInstallation(finished: true) | ||
|
||
// WHEN the RootContainer is created | ||
initializeSut() | ||
|
||
// THEN the state becomes 'activated' | ||
XCTAssertEqual(sut.state, .activated) | ||
} | ||
} | ||
|
||
|
||
extension RootContainerViewModelTests { | ||
private func stubOnboardingVpnInstallation(finished: Bool) { | ||
UserDefaults.standard.setValue(finished, forKey: .kOnboardingVpnProfileInstalled) | ||
} | ||
} |
Oops, something went wrong.