Refactoring

This commit is contained in:
Justin Mazzocchi 2020-09-12 17:50:22 -07:00
parent a6185c9cf4
commit 62ddaac083
No known key found for this signature in database
GPG Key ID: E223E6937AAFB01C
9 changed files with 37 additions and 35 deletions

View File

@ -13,6 +13,7 @@ public struct AppEnvironment {
let keychain: Keychain.Type
let userDefaults: UserDefaults
let userNotificationClient: UserNotificationClient
let uuid: () -> UUID
let inMemoryContent: Bool
let fixtureDatabase: IdentityDatabase?
@ -21,6 +22,7 @@ public struct AppEnvironment {
keychain: Keychain.Type,
userDefaults: UserDefaults,
userNotificationClient: UserNotificationClient,
uuid: @escaping () -> UUID,
inMemoryContent: Bool,
fixtureDatabase: IdentityDatabase?) {
self.session = session
@ -28,6 +30,7 @@ public struct AppEnvironment {
self.keychain = keychain
self.userDefaults = userDefaults
self.userNotificationClient = userNotificationClient
self.uuid = uuid
self.inMemoryContent = inMemoryContent
self.fixtureDatabase = fixtureDatabase
}
@ -41,6 +44,7 @@ public extension AppEnvironment {
keychain: LiveKeychain.self,
userDefaults: .standard,
userNotificationClient: .live(userNotificationCenter),
uuid: UUID.init,
inMemoryContent: false,
fixtureDatabase: nil)
}

View File

@ -8,14 +8,18 @@ import MastodonAPI
import Secrets
public struct AllIdentitiesService {
public let identitiesCreated: AnyPublisher<UUID, Never>
private let environment: AppEnvironment
private let database: IdentityDatabase
private let identitiesCreatedSubject = PassthroughSubject<UUID, Never>()
public init(environment: AppEnvironment) throws {
self.environment = environment
self.database = try environment.fixtureDatabase ?? IdentityDatabase(
inMemory: environment.inMemoryContent,
keychain: environment.keychain)
identitiesCreated = identitiesCreatedSubject.eraseToAnyPublisher()
}
}
@ -28,18 +32,16 @@ public extension AllIdentitiesService {
database.immediateMostRecentlyUsedIdentityIDObservation()
}
func createIdentity(id: UUID, url: URL, authenticated: Bool) -> AnyPublisher<Never, Error> {
func createIdentity(url: URL, authenticated: Bool) -> AnyPublisher<Never, Error> {
createIdentity(
id: id,
url: url,
authenticationPublisher: authenticated
? AuthenticationService(url: url, environment: environment).authenticate()
: nil)
}
func createIdentity(id: UUID, url: URL, registration: Registration) -> AnyPublisher<Never, Error> {
func createIdentity(url: URL, registration: Registration) -> AnyPublisher<Never, Error> {
createIdentity(
id: id,
url: url,
authenticationPublisher: AuthenticationService(url: url, environment: environment)
.register(registration))
@ -91,9 +93,9 @@ public extension AllIdentitiesService {
private extension AllIdentitiesService {
func createIdentity(
id: UUID,
url: URL,
authenticationPublisher: AnyPublisher<(AppAuthorization, AccessToken), Error>?) -> AnyPublisher<Never, Error> {
let id = environment.uuid()
let secrets = Secrets(identityID: id, keychain: environment.keychain)
do {
@ -107,6 +109,11 @@ private extension AllIdentitiesService {
url: url,
authenticated: authenticationPublisher != nil)
.ignoreOutput()
.handleEvents(receiveCompletion: {
if case .finished = $0 {
identitiesCreatedSubject.send(id)
}
})
.eraseToAnyPublisher()
if let authenticationPublisher = authenticationPublisher {

View File

@ -14,6 +14,7 @@ public extension AppEnvironment {
keychain: Keychain.Type = MockKeychain.self,
userDefaults: UserDefaults = MockUserDefaults(),
userNotificationClient: UserNotificationClient = .mock,
uuid: @escaping () -> UUID = UUID.init,
inMemoryContent: Bool = true,
fixtureDatabase: IdentityDatabase? = nil) -> Self {
AppEnvironment(
@ -22,6 +23,7 @@ public extension AppEnvironment {
keychain: keychain,
userDefaults: userDefaults,
userNotificationClient: userNotificationClient,
uuid: uuid,
inMemoryContent: inMemoryContent,
fixtureDatabase: fixtureDatabase)
}

View File

@ -16,17 +16,14 @@ public final class AddIdentityViewModel: ObservableObject {
@Published public private(set) var url: URL?
@Published public private(set) var instance: Instance?
@Published public private(set) var isPublicTimelineAvailable = false
public let addedIdentityID: AnyPublisher<UUID, Never>
private let allIdentitiesService: AllIdentitiesService
private let instanceURLService: InstanceURLService
private let addedIdentityIDSubject = PassthroughSubject<UUID, Never>()
private var cancellables = Set<AnyCancellable>()
init(allIdentitiesService: AllIdentitiesService, instanceURLService: InstanceURLService) {
self.allIdentitiesService = allIdentitiesService
self.instanceURLService = instanceURLService
addedIdentityID = addedIdentityIDSubject.eraseToAnyPublisher()
setupURLObservation()
}
}
@ -88,18 +85,13 @@ private extension AddIdentityViewModel {
}
func addIdentity(authenticated: Bool) {
let identityID = UUID()
guard let url = instanceURLService.url(text: urlFieldText) else {
alertItem = AlertItem(error: AddIdentityError.unableToConnectToInstance)
return
}
allIdentitiesService.createIdentity(
id: identityID,
url: url,
authenticated: authenticated)
allIdentitiesService.createIdentity(url: url, authenticated: authenticated)
.receive(on: DispatchQueue.main)
.handleEvents(receiveSubscription: { [weak self] _ in self?.loading = true })
.sink { [weak self] in
@ -107,10 +99,7 @@ private extension AddIdentityViewModel {
self.loading = false
switch $0 {
case .finished:
self.addedIdentityIDSubject.send(identityID)
case let .failure(error):
if case let .failure(error) = $0 {
if case AuthenticationError.canceled = error {
return
}

View File

@ -50,7 +50,7 @@ public extension RegistrationViewModel {
return
}
allIdentitiesService.createIdentity(id: UUID(), url: url, registration: registration)
allIdentitiesService.createIdentity(url: url, registration: registration)
.handleEvents(receiveSubscription: { [weak self] _ in self?.registering = true })
.mapError { error -> Error in
if error is URLError {

View File

@ -27,6 +27,10 @@ public final class RootViewModel: ObservableObject {
identitySelected(id: mostRecentlyUsedIdentityID, immediate: true)
allIdentitiesService.identitiesCreated
.sink { [weak self] in self?.identitySelected(id: $0) }
.store(in: &cancellables)
userNotificationService.isAuthorized()
.filter { $0 }
.zip(registerForRemoteNotifications())

View File

@ -12,11 +12,13 @@ import XCTest
class AddIdentityViewModelTests: XCTestCase {
func testAddIdentity() throws {
let environment = AppEnvironment.mock()
let uuid = UUID(uuidString: "E621E1F8-C36C-495A-93FC-0C247A3E6E5F")!
let environment = AppEnvironment.mock(uuid: { uuid })
let allIdentitiesService = try AllIdentitiesService(environment: environment)
let sut = AddIdentityViewModel(
allIdentitiesService: try AllIdentitiesService(environment: environment),
allIdentitiesService: allIdentitiesService,
instanceURLService: InstanceURLService(environment: environment))
let addedIDRecorder = sut.addedIdentityID.record()
let addedIDRecorder = allIdentitiesService.identitiesCreated.record()
sut.urlFieldText = "https://mastodon.social"
sut.logInTapped()
@ -25,11 +27,13 @@ class AddIdentityViewModelTests: XCTestCase {
}
func testAddIdentityWithoutScheme() throws {
let environment = AppEnvironment.mock()
let uuid = UUID(uuidString: "E621E1F8-C36C-495A-93FC-0C247A3E6E5F")!
let environment = AppEnvironment.mock(uuid: { uuid })
let allIdentitiesService = try AllIdentitiesService(environment: environment)
let sut = AddIdentityViewModel(
allIdentitiesService: try AllIdentitiesService(environment: environment),
allIdentitiesService: allIdentitiesService,
instanceURLService: InstanceURLService(environment: environment))
let addedIDRecorder = sut.addedIdentityID.record()
let addedIDRecorder = allIdentitiesService.identitiesCreated.record()
sut.urlFieldText = "mastodon.social"
sut.logInTapped()

View File

@ -11,8 +11,9 @@ class RootViewModelTests: XCTestCase {
var cancellables = Set<AnyCancellable>()
func testAddIdentity() throws {
let uuid = UUID(uuidString: "E621E1F8-C36C-495A-93FC-0C247A3E6E5F")!
let sut = try RootViewModel(
environment: .mock(),
environment: .mock(uuid: { uuid }),
registerForRemoteNotifications: { Empty().setFailureType(to: Error.self).eraseToAnyPublisher() })
let recorder = sut.$navigationViewModel.record()
@ -20,10 +21,6 @@ class RootViewModelTests: XCTestCase {
let addIdentityViewModel = sut.addIdentityViewModel()
addIdentityViewModel.addedIdentityID
.sink(receiveValue: sut.identitySelected(id:))
.store(in: &cancellables)
addIdentityViewModel.urlFieldText = "https://mastodon.social"
addIdentityViewModel.logInTapped()

View File

@ -74,11 +74,6 @@ struct AddIdentityView: View {
}
.animation(.default, if: !accessibilityReduceMotion)
.alertItem($viewModel.alertItem)
.onReceive(viewModel.addedIdentityID) { id in
withAnimation {
rootViewModel.identitySelected(id: id)
}
}
.onAppear(perform: viewModel.refreshFilter)
}
}