Refactoring

This commit is contained in:
Justin Mazzocchi 2020-10-06 17:31:29 -07:00
parent dc7d0df55b
commit feef7d794d
No known key found for this signature in database
GPG Key ID: E223E6937AAFB01C
8 changed files with 68 additions and 83 deletions

View File

@ -8,10 +8,10 @@ final class ProfileViewController: TableViewController {
private let viewModel: ProfileViewModel
private var cancellables = Set<AnyCancellable>()
required init(viewModel: ProfileViewModel) {
required init(viewModel: ProfileViewModel, identification: Identification) {
self.viewModel = viewModel
super.init(viewModel: viewModel)
super.init(viewModel: viewModel, identification: identification)
}
override func viewDidLoad() {

View File

@ -7,6 +7,7 @@ import ViewModels
class TableViewController: UITableViewController {
private let viewModel: CollectionViewModel
private let identification: Identification
private let loadingTableFooterView = LoadingTableFooterView()
private let webfingerIndicatorView = WebfingerIndicatorView()
private var cancellables = Set<AnyCancellable>()
@ -37,8 +38,9 @@ class TableViewController: UITableViewController {
}
}()
init(viewModel: CollectionViewModel) {
init(viewModel: CollectionViewModel, identification: Identification) {
self.viewModel = viewModel
self.identification = identification
super.init(style: .plain)
}
@ -183,25 +185,9 @@ private extension TableViewController {
viewModel.sections.sink { [weak self] in self?.update(items: $0) }.store(in: &cancellables)
viewModel.navigationEvents.receive(on: DispatchQueue.main).sink { [weak self] in
guard let self = self else { return }
switch $0 {
case let .share(url):
self.share(url: url)
case let .collectionNavigation(viewModel):
self.show(TableViewController(viewModel: viewModel), sender: self)
case let .profileNavigation(viewModel):
self.show(ProfileViewController(viewModel: viewModel), sender: self)
case let .urlNavigation(url):
self.present(SFSafariViewController(url: url), animated: true)
case .webfingerStart:
self.webfingerIndicatorView.startAnimating()
case .webfingerEnd:
self.webfingerIndicatorView.stopAnimating()
}
}
.store(in: &cancellables)
viewModel.events.receive(on: DispatchQueue.main)
.sink { [weak self] in self?.handle(event: $0) }
.store(in: &cancellables)
viewModel.loading.receive(on: RunLoop.main).sink { [weak self] in
guard let self = self else { return }
@ -246,6 +232,38 @@ private extension TableViewController {
}
}
func handle(event: CollectionItemEvent) {
switch event {
case .ignorableOutput:
break
case let .share(url):
share(url: url)
case let.navigation(navigation):
switch navigation {
case let .collection(collectionService):
show(TableViewController(
viewModel: CollectionItemsViewModel(
collectionService: collectionService,
identification: identification),
identification: identification),
sender: self)
case let .profile(profileService):
show(ProfileViewController(
viewModel: ProfileViewModel(
profileService: profileService,
identification: identification),
identification: identification),
sender: self)
case let .url(url):
present(SFSafariViewController(url: url), animated: true)
case .webfingerStart:
webfingerIndicatorView.startAnimating()
case .webfingerEnd:
webfingerIndicatorView.stopAnimating()
}
}
}
func share(url: URL) {
let activityViewController = UIActivityViewController(activityItems: [url], applicationActivities: nil)

View File

@ -12,15 +12,17 @@ final public class CollectionItemsViewModel: ObservableObject {
private let items = CurrentValueSubject<[[CollectionItem]], Never>([])
private let collectionService: CollectionService
private let identification: Identification
private var viewModelCache = [CollectionItem: (viewModel: CollectionItemViewModel, events: AnyCancellable)]()
private let navigationEventsSubject = PassthroughSubject<NavigationEvent, Never>()
private let eventsSubject = PassthroughSubject<CollectionItemEvent, Never>()
private let loadingSubject = PassthroughSubject<Bool, Never>()
private var topVisibleIndexPath = IndexPath(item: 0, section: 0)
private var lastSelectedLoadMore: LoadMore?
private var cancellables = Set<AnyCancellable>()
init(collectionService: CollectionService) {
public init(collectionService: CollectionService, identification: Identification) {
self.collectionService = collectionService
self.identification = identification
collectionService.sections
.handleEvents(receiveOutput: { [weak self] in self?.process(items: $0) })
@ -46,7 +48,7 @@ extension CollectionItemsViewModel: CollectionViewModel {
public var loading: AnyPublisher<Bool, Never> { loadingSubject.eraseToAnyPublisher() }
public var navigationEvents: AnyPublisher<NavigationEvent, Never> { navigationEventsSubject.eraseToAnyPublisher() }
public var events: AnyPublisher<CollectionItemEvent, Never> { eventsSubject.eraseToAnyPublisher() }
public func request(maxId: String? = nil, minId: String? = nil) {
collectionService.request(maxId: maxId, minId: minId)
@ -64,20 +66,18 @@ extension CollectionItemsViewModel: CollectionViewModel {
switch item {
case let .status(status, _):
navigationEventsSubject.send(
.collectionNavigation(
CollectionItemsViewModel(
collectionService: collectionService
.navigationService
.contextService(id: status.displayStatus.id))))
eventsSubject.send(
.navigation(.collection(collectionService
.navigationService
.contextService(id: status.displayStatus.id))))
case let .loadMore(loadMore):
lastSelectedLoadMore = loadMore
(viewModel(indexPath: indexPath) as? LoadMoreViewModel)?.loadMore()
case let .account(account):
navigationEventsSubject.send(
.profileNavigation(
ProfileViewModel(
profileService: collectionService.navigationService.profileService(account: account))))
eventsSubject.send(
.navigation(.profile(collectionService
.navigationService
.profileService(account: account))))
}
}
@ -142,9 +142,9 @@ extension CollectionItemsViewModel: CollectionViewModel {
private extension CollectionItemsViewModel {
func cache(viewModel: CollectionItemViewModel, forItem item: CollectionItem) {
viewModelCache[item] = (viewModel, viewModel.events.flatMap { $0.compactMap(NavigationEvent.init) }
viewModelCache[item] = (viewModel, viewModel.events.flatMap { $0 }
.assignErrorsToAlertItem(to: \.alertItem, on: self)
.sink { [weak self] in self?.navigationEventsSubject.send($0) })
.sink { [weak self] in self?.eventsSubject.send($0) })
}
func process(items: [[CollectionItem]]) {

View File

@ -8,7 +8,7 @@ public protocol CollectionViewModel {
var title: AnyPublisher<String, Never> { get }
var alertItems: AnyPublisher<AlertItem, Never> { get }
var loading: AnyPublisher<Bool, Never> { get }
var navigationEvents: AnyPublisher<NavigationEvent, Never> { get }
var events: AnyPublisher<CollectionItemEvent, Never> { get }
var nextPageMaxId: String? { get }
var maintainScrollPositionOfItem: CollectionItemIdentifier? { get }
func request(maxId: String?, minId: String?)

View File

@ -1,36 +0,0 @@
// Copyright © 2020 Metabolist. All rights reserved.
import Foundation
public enum NavigationEvent {
case collectionNavigation(CollectionViewModel)
case profileNavigation(ProfileViewModel)
case urlNavigation(URL)
case share(URL)
case webfingerStart
case webfingerEnd
}
extension NavigationEvent {
public init?(_ event: CollectionItemEvent) {
switch event {
case .ignorableOutput:
return nil
case let .navigation(item):
switch item {
case let .url(url):
self = .urlNavigation(url)
case let .collection(statusListService):
self = .collectionNavigation(CollectionItemsViewModel(collectionService: statusListService))
case let .profile(profileService):
self = .profileNavigation(ProfileViewModel(profileService: profileService))
case .webfingerStart:
self = .webfingerStart
case .webfingerEnd:
self = .webfingerEnd
}
case let .share(url):
self = .share(url)
}
}
}

View File

@ -91,7 +91,9 @@ public extension NavigationViewModel {
}
func viewModel(timeline: Timeline) -> CollectionItemsViewModel {
CollectionItemsViewModel(collectionService: identification.service.service(timeline: timeline))
CollectionItemsViewModel(
collectionService: identification.service.service(timeline: timeline),
identification: identification)
}
}

View File

@ -14,11 +14,13 @@ final public class ProfileViewModel {
private let collectionViewModel: CurrentValueSubject<CollectionItemsViewModel, Never>
private var cancellables = Set<AnyCancellable>()
init(profileService: ProfileService) {
public init(profileService: ProfileService, identification: Identification) {
self.profileService = profileService
collectionViewModel = CurrentValueSubject(
CollectionItemsViewModel(collectionService: profileService.timelineService(profileCollection: .statuses)))
CollectionItemsViewModel(
collectionService: profileService.timelineService(profileCollection: .statuses),
identification: identification))
profileService.accountServicePublisher
.map(AccountViewModel.init(accountService:))
@ -27,7 +29,7 @@ final public class ProfileViewModel {
$collection.dropFirst()
.map(profileService.timelineService(profileCollection:))
.map(CollectionItemsViewModel.init(collectionService:))
.map { CollectionItemsViewModel(collectionService: $0, identification: identification) }
.sink { [weak self] in
guard let self = self else { return }
@ -55,14 +57,12 @@ extension ProfileViewModel: CollectionViewModel {
collectionViewModel.flatMap(\.loading).eraseToAnyPublisher()
}
public var navigationEvents: AnyPublisher<NavigationEvent, Never> {
public var events: AnyPublisher<CollectionItemEvent, Never> {
$accountViewModel.compactMap { $0 }
.flatMap(\.events)
.flatMap { $0 }
.map(NavigationEvent.init)
.compactMap { $0 }
.assignErrorsToAlertItem(to: \.alertItem, on: self)
.merge(with: collectionViewModel.flatMap(\.navigationEvents))
.merge(with: collectionViewModel.flatMap(\.events))
.eraseToAnyPublisher()
}

View File

@ -4,10 +4,11 @@ import SwiftUI
import ViewModels
struct TableView: UIViewControllerRepresentable {
@EnvironmentObject var identification: Identification
let viewModel: CollectionViewModel
func makeUIViewController(context: Context) -> TableViewController {
TableViewController(viewModel: viewModel)
TableViewController(viewModel: viewModel, identification: identification)
}
func updateUIViewController(_ uiViewController: TableViewController, context: Context) {