From f73241caeea81d743a96c4844af5ec7ef176a3fe Mon Sep 17 00:00:00 2001 From: CMK Date: Sat, 8 Oct 2022 15:16:10 +0800 Subject: [PATCH] chore: inject AuthContext --- .github/workflows/main.yml | 4 +- Mastodon/Coordinator/SceneCoordinator.swift | 71 +++++++++---------- .../HomeTimelineViewController.swift | 1 - .../Root/ContentSplitViewController.swift | 4 +- .../Root/MainTab/MainTabBarController.swift | 9 ++- .../Scene/Root/RootSplitViewController.swift | 8 ++- .../Settings/SettingsViewController.swift | 1 - Mastodon/Supporting Files/SceneDelegate.swift | 1 - MastodonSDK/Package.swift | 2 +- .../Sources/MastodonCore/AuthContext.swift | 64 +++++++++++++++++ .../MastodonAuthenticationBox.swift | 14 ++++ .../MastodonSDK/API/Mastodon+API+OAuth.swift | 6 +- 12 files changed, 137 insertions(+), 48 deletions(-) create mode 100644 MastodonSDK/Sources/MastodonCore/AuthContext.swift diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index e4a1876a8..827276208 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -15,12 +15,10 @@ on: jobs: build: name: CI build - runs-on: macos-11 + runs-on: macos-12 steps: - name: checkout uses: actions/checkout@v2 - - name: force Xcode 13.2.1 - run: sudo xcode-select -switch /Applications/Xcode_13.2.1.app - name: setup env: NotificationEndpointDebug: ${{ secrets.NotificationEndpointDebug }} diff --git a/Mastodon/Coordinator/SceneCoordinator.swift b/Mastodon/Coordinator/SceneCoordinator.swift index bdfa727a0..3aeafaabd 100644 --- a/Mastodon/Coordinator/SceneCoordinator.swift +++ b/Mastodon/Coordinator/SceneCoordinator.swift @@ -22,6 +22,8 @@ final public class SceneCoordinator { private weak var sceneDelegate: SceneDelegate! private weak var appContext: AppContext! + private var authContext: AuthContext? + let id = UUID().uuidString private(set) weak var tabBarController: MainTabBarController! @@ -30,7 +32,11 @@ final public class SceneCoordinator { private(set) var secondaryStackHashValues = Set() - init(scene: UIScene, sceneDelegate: SceneDelegate, appContext: AppContext) { + init( + scene: UIScene, + sceneDelegate: SceneDelegate, + appContext: AppContext + ) { self.scene = scene self.sceneDelegate = sceneDelegate self.appContext = appContext @@ -225,55 +231,48 @@ extension SceneCoordinator { func setup() { let rootViewController: UIViewController - switch UIDevice.current.userInterfaceIdiom { - case .phone: - let viewController = MainTabBarController(context: appContext, coordinator: self) - self.splitViewController = nil - self.tabBarController = viewController - rootViewController = viewController - default: - let splitViewController = RootSplitViewController(context: appContext, coordinator: self) - self.splitViewController = splitViewController - self.tabBarController = splitViewController.contentSplitViewController.mainTabBarController - rootViewController = splitViewController - } - let wizardViewController = WizardViewController() - if !wizardViewController.items.isEmpty, - let delegate = rootViewController as? WizardViewControllerDelegate - { - // do not add as child view controller. - // otherwise, the tab bar controller will add as a new tab - wizardViewController.delegate = delegate - wizardViewController.view.autoresizingMask = [.flexibleWidth, .flexibleHeight] - wizardViewController.view.frame = rootViewController.view.bounds - rootViewController.view.addSubview(wizardViewController.view) - self.wizardViewController = wizardViewController - } - - sceneDelegate.window?.rootViewController = rootViewController - } - - func setupOnboardingIfNeeds(animated: Bool) { - // Check user authentication status and show onboarding if needs do { let request = MastodonAuthentication.sortedFetchRequest - if try appContext.managedObjectContext.count(for: request) == 0 { + let _authentication = try appContext.managedObjectContext.fetch(request).first + let _authContext = _authentication.flatMap { AuthContext(authentication: $0) } + self.authContext = _authContext + + switch UIDevice.current.userInterfaceIdiom { + case .phone: + let viewController = MainTabBarController(context: appContext, coordinator: self, authContext: _authContext) + self.splitViewController = nil + self.tabBarController = viewController + rootViewController = viewController + default: + let splitViewController = RootSplitViewController(context: appContext, coordinator: self, authContext: _authContext) + self.splitViewController = splitViewController + self.tabBarController = splitViewController.contentSplitViewController.mainTabBarController + rootViewController = splitViewController + } + sceneDelegate.window?.rootViewController = rootViewController // base: main + + if _authContext == nil { // entry #1: welcome DispatchQueue.main.async { - self.present( + _ = self.present( scene: .welcome, from: self.sceneDelegate.window?.rootViewController, - transition: .modal(animated: animated, completion: nil) + transition: .modal(animated: true, completion: nil) ) } } + } catch { assertionFailure(error.localizedDescription) + Task { + try? await Task.sleep(nanoseconds: .second * 2) + setup() // entry #2: retry + } // end Task } } - - @discardableResult + @MainActor + @discardableResult func present(scene: Scene, from sender: UIViewController?, transition: Transition) -> UIViewController? { guard let viewController = get(scene: scene) else { return nil diff --git a/Mastodon/Scene/HomeTimeline/HomeTimelineViewController.swift b/Mastodon/Scene/HomeTimeline/HomeTimelineViewController.swift index 00433be44..7be12e8bf 100644 --- a/Mastodon/Scene/HomeTimeline/HomeTimelineViewController.swift +++ b/Mastodon/Scene/HomeTimeline/HomeTimelineViewController.swift @@ -410,7 +410,6 @@ extension HomeTimelineViewController { Task { @MainActor in try await context.authenticationService.signOutMastodonUser(authenticationBox: authenticationBox) self.coordinator.setup() - self.coordinator.setupOnboardingIfNeeds(animated: true) } } diff --git a/Mastodon/Scene/Root/ContentSplitViewController.swift b/Mastodon/Scene/Root/ContentSplitViewController.swift index 1222e4c58..503c56de5 100644 --- a/Mastodon/Scene/Root/ContentSplitViewController.swift +++ b/Mastodon/Scene/Root/ContentSplitViewController.swift @@ -24,6 +24,8 @@ final class ContentSplitViewController: UIViewController, NeedsDependency { weak var context: AppContext! { willSet { precondition(!isViewLoaded) } } weak var coordinator: SceneCoordinator! { willSet { precondition(!isViewLoaded) } } + var authContext: AuthContext? + weak var delegate: ContentSplitViewControllerDelegate? private(set) lazy var sidebarViewController: SidebarViewController = { @@ -37,7 +39,7 @@ final class ContentSplitViewController: UIViewController, NeedsDependency { @Published var currentSupplementaryTab: MainTabBarController.Tab = .home private(set) lazy var mainTabBarController: MainTabBarController = { - let mainTabBarController = MainTabBarController(context: context, coordinator: coordinator) + let mainTabBarController = MainTabBarController(context: context, coordinator: coordinator, authContext: authContext) if let homeTimelineViewController = mainTabBarController.viewController(of: HomeTimelineViewController.self) { homeTimelineViewController.viewModel.displaySettingBarButtonItem = false } diff --git a/Mastodon/Scene/Root/MainTab/MainTabBarController.swift b/Mastodon/Scene/Root/MainTab/MainTabBarController.swift index 39cc23b4b..5c910390c 100644 --- a/Mastodon/Scene/Root/MainTab/MainTabBarController.swift +++ b/Mastodon/Scene/Root/MainTab/MainTabBarController.swift @@ -23,6 +23,8 @@ class MainTabBarController: UITabBarController { weak var context: AppContext! weak var coordinator: SceneCoordinator! + var authContext: AuthContext? + let composeButttonShadowBackgroundContainer = ShadowBackgroundContainer() let composeButton: UIButton = { let button = UIButton() @@ -143,9 +145,14 @@ class MainTabBarController: UITabBarController { var avatarURLObserver: AnyCancellable? @Published var avatarURL: URL? - init(context: AppContext, coordinator: SceneCoordinator) { + init( + context: AppContext, + coordinator: SceneCoordinator, + authContext: AuthContext? + ) { self.context = context self.coordinator = coordinator + self.authContext = authContext super.init(nibName: nil, bundle: nil) } diff --git a/Mastodon/Scene/Root/RootSplitViewController.swift b/Mastodon/Scene/Root/RootSplitViewController.swift index 1ab2ee0f6..3a68d3342 100644 --- a/Mastodon/Scene/Root/RootSplitViewController.swift +++ b/Mastodon/Scene/Root/RootSplitViewController.swift @@ -20,12 +20,15 @@ final class RootSplitViewController: UISplitViewController, NeedsDependency { weak var context: AppContext! { willSet { precondition(!isViewLoaded) } } weak var coordinator: SceneCoordinator! { willSet { precondition(!isViewLoaded) } } + var authContext: AuthContext? + private var isPrimaryDisplay = false private(set) lazy var contentSplitViewController: ContentSplitViewController = { let contentSplitViewController = ContentSplitViewController() contentSplitViewController.context = context contentSplitViewController.coordinator = coordinator + contentSplitViewController.authContext = authContext contentSplitViewController.delegate = self return contentSplitViewController }() @@ -37,13 +40,14 @@ final class RootSplitViewController: UISplitViewController, NeedsDependency { return searchViewController }() - lazy var compactMainTabBarViewController = MainTabBarController(context: context, coordinator: coordinator) + lazy var compactMainTabBarViewController = MainTabBarController(context: context, coordinator: coordinator, authContext: authContext) let separatorLine = UIView.separatorLine - init(context: AppContext, coordinator: SceneCoordinator) { + init(context: AppContext, coordinator: SceneCoordinator, authContext: AuthContext?) { self.context = context self.coordinator = coordinator + self.authContext = authContext super.init(style: .doubleColumn) primaryEdge = .trailing diff --git a/Mastodon/Scene/Settings/SettingsViewController.swift b/Mastodon/Scene/Settings/SettingsViewController.swift index e17db3e11..edafbe1a3 100644 --- a/Mastodon/Scene/Settings/SettingsViewController.swift +++ b/Mastodon/Scene/Settings/SettingsViewController.swift @@ -299,7 +299,6 @@ extension SettingsViewController { Task { @MainActor in try await context.authenticationService.signOutMastodonUser(authenticationBox: authenticationBox) self.coordinator.setup() - self.coordinator.setupOnboardingIfNeeds(animated: true) } } diff --git a/Mastodon/Supporting Files/SceneDelegate.swift b/Mastodon/Supporting Files/SceneDelegate.swift index 04f7ea784..c1e6d7abe 100644 --- a/Mastodon/Supporting Files/SceneDelegate.swift +++ b/Mastodon/Supporting Files/SceneDelegate.swift @@ -58,7 +58,6 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { self.coordinator = sceneCoordinator sceneCoordinator.setup() - sceneCoordinator.setupOnboardingIfNeeds(animated: false) window.makeKeyAndVisible() #if SNAPSHOT diff --git a/MastodonSDK/Package.swift b/MastodonSDK/Package.swift index 1cc1d9ff0..7b0fe6af6 100644 --- a/MastodonSDK/Package.swift +++ b/MastodonSDK/Package.swift @@ -1,4 +1,4 @@ -// swift-tools-version:5.3 +// swift-tools-version:5.7 // The swift-tools-version declares the minimum version of Swift required to build this package. import PackageDescription diff --git a/MastodonSDK/Sources/MastodonCore/AuthContext.swift b/MastodonSDK/Sources/MastodonCore/AuthContext.swift new file mode 100644 index 000000000..b93a2e03a --- /dev/null +++ b/MastodonSDK/Sources/MastodonCore/AuthContext.swift @@ -0,0 +1,64 @@ +// +// AuthContext.swift +// +// +// Created by MainasuK on 22/10/8. +// + +import os.log +import Foundation +import Combine +import CoreDataStack +import MastodonSDK + +public protocol AuthContextProvider { + var authContext: AuthContext { get } +} + +public class AuthContext { + + var disposeBag = Set() + + let logger = Logger(subsystem: "AuthContext", category: "AuthContext") + + // Mastodon + public private(set) var mastodonAuthenticationBox: MastodonAuthenticationBox + + private init(mastodonAuthenticationBox: MastodonAuthenticationBox) { + self.mastodonAuthenticationBox = mastodonAuthenticationBox + } + +} + +extension AuthContext { + + public convenience init?(authentication: MastodonAuthentication) { + self.init(mastodonAuthenticationBox: MastodonAuthenticationBox(authentication: authentication)) + + ManagedObjectObserver.observe(object: authentication) + .receive(on: DispatchQueue.main) + .sink { [weak self] completion in + guard let self = self else { return } + switch completion { + case .failure(let error): + self.logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): \(error.localizedDescription)") + case .finished: + self.logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): observer finished") + } + } receiveValue: { [weak self] change in + guard let self = self else { return } + switch change.changeType { + case .update(let object): + guard let authentication = object as? MastodonAuthentication else { + assertionFailure() + return + } + self.mastodonAuthenticationBox = .init(authentication: authentication) + default: + break + } + } + .store(in: &disposeBag) + } + +} diff --git a/MastodonSDK/Sources/MastodonCore/Authentication/MastodonAuthenticationBox.swift b/MastodonSDK/Sources/MastodonCore/Authentication/MastodonAuthenticationBox.swift index 14cb03ad7..ec6cb0bfb 100644 --- a/MastodonSDK/Sources/MastodonCore/Authentication/MastodonAuthenticationBox.swift +++ b/MastodonSDK/Sources/MastodonCore/Authentication/MastodonAuthenticationBox.swift @@ -30,3 +30,17 @@ public struct MastodonAuthenticationBox: UserIdentifier { self.userAuthorization = userAuthorization } } + +extension MastodonAuthenticationBox { + + init(authentication: MastodonAuthentication) { + self = MastodonAuthenticationBox( + authenticationRecord: .init(objectID: authentication.objectID), + domain: authentication.domain, + userID: authentication.userID, + appAuthorization: Mastodon.API.OAuth.Authorization(accessToken: authentication.appAccessToken), + userAuthorization: Mastodon.API.OAuth.Authorization(accessToken: authentication.userAccessToken) + ) + } + +} diff --git a/MastodonSDK/Sources/MastodonSDK/API/Mastodon+API+OAuth.swift b/MastodonSDK/Sources/MastodonSDK/API/Mastodon+API+OAuth.swift index b5451e8fa..18f00f6b6 100644 --- a/MastodonSDK/Sources/MastodonSDK/API/Mastodon+API+OAuth.swift +++ b/MastodonSDK/Sources/MastodonSDK/API/Mastodon+API+OAuth.swift @@ -13,11 +13,15 @@ extension Mastodon.API.OAuth { public static let authorizationField = "Authorization" public struct Authorization { - public let accessToken: String + public private(set) var accessToken: String public init(accessToken: String) { self.accessToken = accessToken } + + public mutating func update(accessToken: String) { + self.accessToken = accessToken + } } }