diff --git a/Mastodon/Coordinator/SceneCoordinator.swift b/Mastodon/Coordinator/SceneCoordinator.swift index 6c92f1955..ee2ca3de5 100644 --- a/Mastodon/Coordinator/SceneCoordinator.swift +++ b/Mastodon/Coordinator/SceneCoordinator.swift @@ -46,7 +46,7 @@ final public class SceneCoordinator { scene.session.sceneCoordinator = self - appContext.notificationService.requestRevealNotificationPublisher + NotificationService.shared.requestRevealNotificationPublisher .receive(on: DispatchQueue.main) .sink(receiveValue: { [weak self] pushNotification in @@ -98,12 +98,12 @@ final public class SceneCoordinator { switch type { case .follow: - let account = try await appContext.apiService.notification( + let account = try await APIService.shared.notification( notificationID: notificationID, authenticationBox: authenticationBox ).value.account - let relationship = try await appContext.apiService.relationship(forAccounts: [account], authenticationBox: authenticationBox).value.first + let relationship = try await APIService.shared.relationship(forAccounts: [account], authenticationBox: authenticationBox).value.first let profileViewModel = ProfileViewModel( context: appContext, @@ -645,7 +645,7 @@ extension SceneCoordinator: SettingsCoordinatorDelegate { let signOutAction = UIAlertAction(title: L10n.Common.Alerts.SignOut.confirm, style: .destructive) { [weak self] _ in guard let self, let authenticationBox = self.authenticationBox else { return } - self.appContext.notificationService.clearNotificationCountForActiveUser() + NotificationService.shared.clearNotificationCountForActiveUser() Task { @MainActor in try await AuthenticationServiceProvider.shared.signOutMastodonUser( diff --git a/Mastodon/Protocol/Provider/DataSourceFacade+Block.swift b/Mastodon/Protocol/Provider/DataSourceFacade+Block.swift index deb57fd25..8abe707c9 100644 --- a/Mastodon/Protocol/Provider/DataSourceFacade+Block.swift +++ b/Mastodon/Protocol/Provider/DataSourceFacade+Block.swift @@ -17,7 +17,7 @@ extension DataSourceFacade { ) async throws -> Mastodon.Entity.Relationship { FeedbackGenerator.shared.generate(.selectionChanged) - let apiService = dependency.context.apiService + let apiService = APIService.shared let authBox = dependency.authenticationBox let response = try await apiService.toggleBlock( @@ -40,7 +40,7 @@ extension DataSourceFacade { ) async throws -> Mastodon.Entity.Empty { FeedbackGenerator.shared.generate(.selectionChanged) - let apiService = dependency.context.apiService + let apiService = APIService.shared let authBox = dependency.authenticationBox let response = try await apiService.toggleDomainBlock(account: account, authenticationBox: authBox) diff --git a/Mastodon/Protocol/Provider/DataSourceFacade+Bookmark.swift b/Mastodon/Protocol/Provider/DataSourceFacade+Bookmark.swift index 6d2ad4459..76d12a249 100644 --- a/Mastodon/Protocol/Provider/DataSourceFacade+Bookmark.swift +++ b/Mastodon/Protocol/Provider/DataSourceFacade+Bookmark.swift @@ -19,7 +19,7 @@ extension DataSourceFacade { ) async throws { FeedbackGenerator.shared.generate(.selectionChanged) - let updatedStatus = try await provider.context.apiService.bookmark( + let updatedStatus = try await APIService.shared.bookmark( record: status, authenticationBox: provider.authenticationBox ).value diff --git a/Mastodon/Protocol/Provider/DataSourceFacade+Favorite.swift b/Mastodon/Protocol/Provider/DataSourceFacade+Favorite.swift index e176056df..d02dc0130 100644 --- a/Mastodon/Protocol/Provider/DataSourceFacade+Favorite.swift +++ b/Mastodon/Protocol/Provider/DataSourceFacade+Favorite.swift @@ -18,7 +18,7 @@ extension DataSourceFacade { ) async throws { FeedbackGenerator.shared.generate(.selectionChanged) - let updatedStatus = try await provider.context.apiService.favorite( + let updatedStatus = try await APIService.shared.favorite( status: status, authenticationBox: provider.authenticationBox ).value diff --git a/Mastodon/Protocol/Provider/DataSourceFacade+Follow.swift b/Mastodon/Protocol/Provider/DataSourceFacade+Follow.swift index 75d51cb5a..e4e64df77 100644 --- a/Mastodon/Protocol/Provider/DataSourceFacade+Follow.swift +++ b/Mastodon/Protocol/Provider/DataSourceFacade+Follow.swift @@ -18,7 +18,7 @@ extension DataSourceFacade { account: Mastodon.Entity.Account ) async throws -> Mastodon.Entity.Relationship { let authBox = dependency.authenticationBox - let relationship = try await dependency.context.apiService.relationship( + let relationship = try await APIService.shared.relationship( forAccounts: [account], authenticationBox: authBox ).value.first @@ -27,7 +27,7 @@ extension DataSourceFacade { let performAction = { FeedbackGenerator.shared.generate(.selectionChanged) - let response = try await dependency.context.apiService.toggleFollow( + let response = try await APIService.shared.toggleFollow( account: account, authenticationBox: dependency.authenticationBox ).value @@ -100,7 +100,7 @@ extension DataSourceFacade { await notificationView.configure(notification: notification, authenticationBox: dependency.authenticationBox) do { - let newRelationship = try await dependency.context.apiService.followRequest( + let newRelationship = try await APIService.shared.followRequest( userID: userID, query: query, authenticationBox: dependency.authenticationBox @@ -149,7 +149,7 @@ extension DataSourceFacade { dependency: NeedsDependency & AuthContextProvider, account: Mastodon.Entity.Account ) async throws { - let newRelationship = try await dependency.context.apiService.toggleShowReblogs( + let newRelationship = try await APIService.shared.toggleShowReblogs( for: account, authenticationBox: dependency.authenticationBox ) diff --git a/Mastodon/Protocol/Provider/DataSourceFacade+Mute.swift b/Mastodon/Protocol/Provider/DataSourceFacade+Mute.swift index a7efab8aa..d82006556 100644 --- a/Mastodon/Protocol/Provider/DataSourceFacade+Mute.swift +++ b/Mastodon/Protocol/Provider/DataSourceFacade+Mute.swift @@ -16,7 +16,7 @@ extension DataSourceFacade { ) async throws -> Mastodon.Entity.Relationship { FeedbackGenerator.shared.generate(.selectionChanged) - let response = try await dependency.context.apiService.toggleMute( + let response = try await APIService.shared.toggleMute( authenticationBox: dependency.authenticationBox, account: account ) diff --git a/Mastodon/Protocol/Provider/DataSourceFacade+Notifications.swift b/Mastodon/Protocol/Provider/DataSourceFacade+Notifications.swift index a4e23b50b..12cbd763c 100644 --- a/Mastodon/Protocol/Provider/DataSourceFacade+Notifications.swift +++ b/Mastodon/Protocol/Provider/DataSourceFacade+Notifications.swift @@ -12,7 +12,7 @@ extension DataSourceFacade { provider.coordinator.showLoading() do { - let notificationRequests = try await provider.context.apiService.notificationRequests(authenticationBox: provider.authenticationBox).value + let notificationRequests = try await APIService.shared.notificationRequests(authenticationBox: provider.authenticationBox).value let viewModel = NotificationRequestsViewModel(appContext: provider.context, authenticationBox: provider.authenticationBox, coordinator: provider.coordinator, requests: notificationRequests) provider.coordinator.hideLoading() diff --git a/Mastodon/Protocol/Provider/DataSourceFacade+Profile.swift b/Mastodon/Protocol/Provider/DataSourceFacade+Profile.swift index 40800e0f6..b7ff7f3c2 100644 --- a/Mastodon/Protocol/Provider/DataSourceFacade+Profile.swift +++ b/Mastodon/Protocol/Provider/DataSourceFacade+Profile.swift @@ -57,7 +57,7 @@ extension DataSourceFacade { provider.coordinator.showLoading() do { - guard let account = try await provider.context.apiService.fetchUser( + guard let account = try await APIService.shared.fetchUser( username: username, domain: domain, authenticationBox: provider.authenticationBox @@ -82,7 +82,7 @@ extension DataSourceFacade { provider.coordinator.showLoading() do { - let account = try await provider.context.apiService.accountInfo( + let account = try await APIService.shared.accountInfo( domain: domain, userID: accountID, authorization: provider.authenticationBox.userAuthorization @@ -104,7 +104,7 @@ extension DataSourceFacade { provider.coordinator.showLoading() guard let me = provider.authenticationBox.authentication.account(), - let relationship = try? await provider.context.apiService.relationship(forAccounts: [account], authenticationBox: provider.authenticationBox).value.first else { + let relationship = try? await APIService.shared.relationship(forAccounts: [account], authenticationBox: provider.authenticationBox).value.first else { return provider.coordinator.hideLoading() } diff --git a/Mastodon/Protocol/Provider/DataSourceFacade+Reblog.swift b/Mastodon/Protocol/Provider/DataSourceFacade+Reblog.swift index 0a35cf3b7..ce8db2207 100644 --- a/Mastodon/Protocol/Provider/DataSourceFacade+Reblog.swift +++ b/Mastodon/Protocol/Provider/DataSourceFacade+Reblog.swift @@ -49,7 +49,7 @@ private extension DataSourceFacade { ) async throws { FeedbackGenerator.shared.generate(.selectionChanged) - let updatedStatus = try await provider.context.apiService.reblog( + let updatedStatus = try await APIService.shared.reblog( status: status, authenticationBox: provider.authenticationBox ).value diff --git a/Mastodon/Protocol/Provider/DataSourceFacade+Status+History.swift b/Mastodon/Protocol/Provider/DataSourceFacade+Status+History.swift index b9dbbfe8d..f7f1aaaf7 100644 --- a/Mastodon/Protocol/Provider/DataSourceFacade+Status+History.swift +++ b/Mastodon/Protocol/Provider/DataSourceFacade+Status+History.swift @@ -10,7 +10,7 @@ extension DataSourceFacade { forStatus status: Status, provider: NeedsDependency & AuthContextProvider ) async throws -> [Mastodon.Entity.StatusEdit] { - let reponse = try await provider.context.apiService.getHistory(forStatusID: status.id, authenticationBox: provider.authenticationBox) + let reponse = try await APIService.shared.getHistory(forStatusID: status.id, authenticationBox: provider.authenticationBox) return reponse.value } diff --git a/Mastodon/Protocol/Provider/DataSourceFacade+Status.swift b/Mastodon/Protocol/Provider/DataSourceFacade+Status.swift index 02ad1bf09..d60a3e60e 100644 --- a/Mastodon/Protocol/Provider/DataSourceFacade+Status.swift +++ b/Mastodon/Protocol/Provider/DataSourceFacade+Status.swift @@ -23,7 +23,7 @@ extension DataSourceFacade { dependency: NeedsDependency & AuthContextProvider & DataSourceProvider, status: MastodonStatus ) async throws { - let deletedStatus = try await dependency.context.apiService.deleteStatus( + let deletedStatus = try await APIService.shared.deleteStatus( status: status, authenticationBox: dependency.authenticationBox ).value.asMastodonStatus @@ -234,7 +234,7 @@ extension DataSourceFacade { alertController.addAction(cancelAction) dependency.present(alertController, animated: true) case .reportUser: - guard let relationship = try? await dependency.context.apiService.relationship(forAccounts: [menuContext.author], authenticationBox: dependency.authenticationBox).value.first else { return } + guard let relationship = try? await APIService.shared.relationship(forAccounts: [menuContext.author], authenticationBox: dependency.authenticationBox).value.first else { return } let reportViewModel = ReportViewModel( context: dependency.context, @@ -333,7 +333,7 @@ extension DataSourceFacade { guard let status = menuContext.statusViewModel?.originalStatus else { return } - let statusSource = try await dependency.context.apiService.getStatusSource( + let statusSource = try await APIService.shared.getStatusSource( forStatusID: status.id, authenticationBox: dependency.authenticationBox ).value diff --git a/Mastodon/Protocol/Provider/DataSourceFacade+Translate.swift b/Mastodon/Protocol/Provider/DataSourceFacade+Translate.swift index 49b15fb3f..ff5246cd7 100644 --- a/Mastodon/Protocol/Provider/DataSourceFacade+Translate.swift +++ b/Mastodon/Protocol/Provider/DataSourceFacade+Translate.swift @@ -25,8 +25,7 @@ extension DataSourceFacade { FeedbackGenerator.shared.generate(.selectionChanged) do { - let value = try await provider.context - .apiService + let value = try await APIService.shared .translateStatus( statusID: status.id, authenticationBox: provider.authenticationBox diff --git a/Mastodon/Protocol/Provider/DataSourceProvider+NotificationTableViewCellDelegate.swift b/Mastodon/Protocol/Provider/DataSourceProvider+NotificationTableViewCellDelegate.swift index aa15e75f0..8466bfb7b 100644 --- a/Mastodon/Protocol/Provider/DataSourceProvider+NotificationTableViewCellDelegate.swift +++ b/Mastodon/Protocol/Provider/DataSourceProvider+NotificationTableViewCellDelegate.swift @@ -568,7 +568,7 @@ extension NotificationTableViewCellDelegate where Self: DataSourceProvider & Aut .compactMap { poll.options.firstIndex(of: $0) } do { - let newPoll = try await context.apiService.vote( + let newPoll = try await APIService.shared.vote( poll: poll.entity, choices: choices, authenticationBox: authenticationBox diff --git a/Mastodon/Protocol/Provider/DataSourceProvider+StatusTableViewCellDelegate.swift b/Mastodon/Protocol/Provider/DataSourceProvider+StatusTableViewCellDelegate.swift index 589b4e12b..f85e5e2ad 100644 --- a/Mastodon/Protocol/Provider/DataSourceProvider+StatusTableViewCellDelegate.swift +++ b/Mastodon/Protocol/Provider/DataSourceProvider+StatusTableViewCellDelegate.swift @@ -310,7 +310,7 @@ extension StatusTableViewCellDelegate where Self: DataSourceProvider & AuthConte .compactMap { poll.options.firstIndex(of: $0) } do { - let newPoll = try await context.apiService.vote( + let newPoll = try await APIService.shared.vote( poll: poll.entity, choices: choices, authenticationBox: authenticationBox @@ -584,7 +584,7 @@ extension StatusTableViewCellDelegate where Self: DataSourceProvider & AuthConte } do { - let edits = try await context.apiService.getHistory(forStatusID: status.id, authenticationBox: authenticationBox).value + let edits = try await APIService.shared.getHistory(forStatusID: status.id, authenticationBox: authenticationBox).value await coordinator.hideLoading() diff --git a/Mastodon/Protocol/Provider/DataSourceProvider+UITableViewDelegate.swift b/Mastodon/Protocol/Provider/DataSourceProvider+UITableViewDelegate.swift index 571401ab8..6b1e928c5 100644 --- a/Mastodon/Protocol/Provider/DataSourceProvider+UITableViewDelegate.swift +++ b/Mastodon/Protocol/Provider/DataSourceProvider+UITableViewDelegate.swift @@ -124,7 +124,7 @@ extension UITableViewDelegate where Self: DataSourceProvider & MediaPreviewableV guard let self = self else { return } Task { @MainActor in do { - try await self.context.photoLibraryService.save( + try await PhotoLibraryService.shared.save( imageSource: .url(assetURL) ).singleOutput() } catch { @@ -153,7 +153,7 @@ extension UITableViewDelegate where Self: DataSourceProvider & MediaPreviewableV ) { [weak self] _ in guard let self = self else { return } Task { - try await self.context.photoLibraryService.copy( + try await PhotoLibraryService.shared.copy( imageSource: .url(assetURL) ).singleOutput() } diff --git a/Mastodon/Scene/Compose/ComposeViewController.swift b/Mastodon/Scene/Compose/ComposeViewController.swift index b915e6c11..51d62b6ef 100644 --- a/Mastodon/Scene/Compose/ComposeViewController.swift +++ b/Mastodon/Scene/Compose/ComposeViewController.swift @@ -245,7 +245,7 @@ extension ComposeViewController { private func enqueuePublishStatus() { do { let statusPublisher = try composeContentViewModel.statusPublisher() - viewModel.context.publisherService.enqueue( + PublisherService.shared.enqueue( statusPublisher: statusPublisher, authenticationBox: viewModel.authenticationBox ) @@ -294,7 +294,7 @@ extension ComposeViewController { private func enqueuePublishStatusEdit() { do { guard let editStatusPublisher = try composeContentViewModel.statusEditPublisher() else { return } - viewModel.context.publisherService.enqueue( + PublisherService.shared.enqueue( statusPublisher: editStatusPublisher, authenticationBox: viewModel.authenticationBox ) @@ -325,7 +325,6 @@ extension ComposeViewController { if UIPasteboard.general.hasImages, let images = UIPasteboard.general.images { let attachmentViewModels = images.map { image in return AttachmentViewModel( - api: viewModel.context.apiService, authenticationBox: viewModel.authenticationBox, input: .image(image), sizeLimit: composeContentViewModel.sizeLimit, diff --git a/Mastodon/Scene/Discovery/ForYou/DiscoveryForYouViewController.swift b/Mastodon/Scene/Discovery/ForYou/DiscoveryForYouViewController.swift index a912abb32..38052a6cf 100644 --- a/Mastodon/Scene/Discovery/ForYou/DiscoveryForYouViewController.swift +++ b/Mastodon/Scene/Discovery/ForYou/DiscoveryForYouViewController.swift @@ -145,7 +145,7 @@ extension DiscoveryForYouViewController: ProfileCardTableViewCellDelegate { do { let userID = account.id let familiarFollowers = viewModel.familiarFollowers.first(where: { $0.id == userID })?.accounts ?? [] - let relationships = try await context.apiService.relationship(forAccounts: familiarFollowers, authenticationBox: authenticationBox).value + let relationships = try await APIService.shared.relationship(forAccounts: familiarFollowers, authenticationBox: authenticationBox).value coordinator.hideLoading() diff --git a/Mastodon/Scene/Discovery/ForYou/DiscoveryForYouViewModel.swift b/Mastodon/Scene/Discovery/ForYou/DiscoveryForYouViewModel.swift index 2535d0141..d46f60a38 100644 --- a/Mastodon/Scene/Discovery/ForYou/DiscoveryForYouViewModel.swift +++ b/Mastodon/Scene/Discovery/ForYou/DiscoveryForYouViewModel.swift @@ -48,12 +48,12 @@ extension DiscoveryForYouViewModel { do { let suggestedAccounts = try await fetchSuggestionAccounts() - let familiarFollowersResponse = try? await context.apiService.familiarFollowers( + let familiarFollowersResponse = try? await APIService.shared.familiarFollowers( query: .init(ids: suggestedAccounts.compactMap { $0.id }), authenticationBox: authenticationBox ).value - let relationships = try? await context.apiService.relationship( + let relationships = try? await APIService.shared.relationship( forAccounts: suggestedAccounts, authenticationBox: authenticationBox ).value @@ -85,14 +85,14 @@ extension DiscoveryForYouViewModel { private func fetchSuggestionAccounts() async throws -> [Mastodon.Entity.Account] { do { - let response = try await context.apiService.suggestionAccountV2( + let response = try await APIService.shared.suggestionAccountV2( query: nil, authenticationBox: authenticationBox ).value return response.compactMap { $0.account } } catch { // fallback V1 - let response = try await context.apiService.suggestionAccount( + let response = try await APIService.shared.suggestionAccount( query: nil, authenticationBox: authenticationBox ).value diff --git a/Mastodon/Scene/Discovery/Hashtags/DiscoveryHashtagsViewModel.swift b/Mastodon/Scene/Discovery/Hashtags/DiscoveryHashtagsViewModel.swift index fcf81e55e..23f425133 100644 --- a/Mastodon/Scene/Discovery/Hashtags/DiscoveryHashtagsViewModel.swift +++ b/Mastodon/Scene/Discovery/Hashtags/DiscoveryHashtagsViewModel.swift @@ -35,7 +35,7 @@ final class DiscoveryHashtagsViewModel { .throttle(for: 3, scheduler: DispatchQueue.main, latest: true) .asyncMap { _ in let authenticationBox = authenticationBox - return try await context.apiService.trendHashtags(domain: authenticationBox.domain, + return try await APIService.shared.trendHashtags(domain: authenticationBox.domain, query: nil, authenticationBox: authenticationBox ) @@ -63,7 +63,7 @@ extension DiscoveryHashtagsViewModel { func fetch() async throws { let authenticationBox = authenticationBox - let response = try await context.apiService.trendHashtags(domain: authenticationBox.domain, + let response = try await APIService.shared.trendHashtags(domain: authenticationBox.domain, query: nil, authenticationBox: authenticationBox ) diff --git a/Mastodon/Scene/Discovery/News/DiscoveryNewsViewModel+State.swift b/Mastodon/Scene/Discovery/News/DiscoveryNewsViewModel+State.swift index 95dea3073..306edd834 100644 --- a/Mastodon/Scene/Discovery/News/DiscoveryNewsViewModel+State.swift +++ b/Mastodon/Scene/Discovery/News/DiscoveryNewsViewModel+State.swift @@ -8,6 +8,7 @@ import Foundation import GameplayKit import MastodonSDK +import MastodonCore extension DiscoveryNewsViewModel { class State: GKState { @@ -123,7 +124,7 @@ extension DiscoveryNewsViewModel.State { Task { do { - let response = try await viewModel.context.apiService.trendLinks( + let response = try await APIService.shared.trendLinks( domain: viewModel.authenticationBox.domain, query: Mastodon.API.Trends.StatusQuery( offset: offset, diff --git a/Mastodon/Scene/Discovery/News/DiscoveryNewsViewModel.swift b/Mastodon/Scene/Discovery/News/DiscoveryNewsViewModel.swift index 7c66ceefb..411c0bb77 100644 --- a/Mastodon/Scene/Discovery/News/DiscoveryNewsViewModel.swift +++ b/Mastodon/Scene/Discovery/News/DiscoveryNewsViewModel.swift @@ -55,7 +55,7 @@ final class DiscoveryNewsViewModel { extension DiscoveryNewsViewModel { func checkServerEndpoint() async { do { - _ = try await context.apiService.trendLinks( + _ = try await APIService.shared.trendLinks( domain: authenticationBox.domain, query: .init(offset: nil, limit: nil), authenticationBox: authenticationBox diff --git a/Mastodon/Scene/Discovery/Posts/DiscoveryPostsViewModel+State.swift b/Mastodon/Scene/Discovery/Posts/DiscoveryPostsViewModel+State.swift index e7a227c41..fc58c4994 100644 --- a/Mastodon/Scene/Discovery/Posts/DiscoveryPostsViewModel+State.swift +++ b/Mastodon/Scene/Discovery/Posts/DiscoveryPostsViewModel+State.swift @@ -122,7 +122,7 @@ extension DiscoveryPostsViewModel.State { Task { do { - let response = try await viewModel.context.apiService.trendStatuses( + let response = try await APIService.shared.trendStatuses( domain: viewModel.authenticationBox.domain, query: Mastodon.API.Trends.StatusQuery( offset: offset, diff --git a/Mastodon/Scene/Discovery/Posts/DiscoveryPostsViewModel.swift b/Mastodon/Scene/Discovery/Posts/DiscoveryPostsViewModel.swift index 6243eb618..396331fbb 100644 --- a/Mastodon/Scene/Discovery/Posts/DiscoveryPostsViewModel.swift +++ b/Mastodon/Scene/Discovery/Posts/DiscoveryPostsViewModel.swift @@ -55,7 +55,7 @@ final class DiscoveryPostsViewModel { extension DiscoveryPostsViewModel { func checkServerEndpoint() async { do { - _ = try await context.apiService.trendStatuses( + _ = try await APIService.shared.trendStatuses( domain: authenticationBox.domain, query: .init(offset: nil, limit: nil), authenticationBox: authenticationBox diff --git a/Mastodon/Scene/HashtagTimeline/HashtagTimelineViewModel+State.swift b/Mastodon/Scene/HashtagTimeline/HashtagTimelineViewModel+State.swift index d20e176c6..33ac3b94a 100644 --- a/Mastodon/Scene/HashtagTimeline/HashtagTimelineViewModel+State.swift +++ b/Mastodon/Scene/HashtagTimeline/HashtagTimelineViewModel+State.swift @@ -9,6 +9,7 @@ import Foundation import GameplayKit import CoreDataStack import MastodonSDK +import MastodonCore extension HashtagTimelineViewModel { class State: GKState { @@ -126,7 +127,7 @@ extension HashtagTimelineViewModel.State { Task { do { - let response = try await viewModel.context.apiService.hashtagTimeline( + let response = try await APIService.shared.hashtagTimeline( maxID: maxID, hashtag: viewModel.hashtag, authenticationBox: viewModel.authenticationBox diff --git a/Mastodon/Scene/HashtagTimeline/HashtagTimelineViewModel.swift b/Mastodon/Scene/HashtagTimeline/HashtagTimelineViewModel.swift index 8439456e6..5083b1268 100644 --- a/Mastodon/Scene/HashtagTimeline/HashtagTimelineViewModel.swift +++ b/Mastodon/Scene/HashtagTimeline/HashtagTimelineViewModel.swift @@ -68,7 +68,7 @@ extension HashtagTimelineViewModel { func followTag() { self.hashtagDetails.send(hashtagDetails.value?.copy(following: true)) Task { @MainActor in - let tag = try? await context.apiService.followTag( + let tag = try? await APIService.shared.followTag( for: hashtag, authenticationBox: authenticationBox ).value @@ -79,7 +79,7 @@ extension HashtagTimelineViewModel { func unfollowTag() { self.hashtagDetails.send(hashtagDetails.value?.copy(following: false)) Task { @MainActor in - let tag = try? await context.apiService.unfollowTag( + let tag = try? await APIService.shared.unfollowTag( for: hashtag, authenticationBox: authenticationBox ).value @@ -91,7 +91,7 @@ extension HashtagTimelineViewModel { private extension HashtagTimelineViewModel { func updateTagInformation() { Task { @MainActor in - let tag = try? await context.apiService.getTagInformation( + let tag = try? await APIService.shared.getTagInformation( for: hashtag, authenticationBox: authenticationBox ).value diff --git a/Mastodon/Scene/HomeTimeline/HomeTimelineViewController.swift b/Mastodon/Scene/HomeTimeline/HomeTimelineViewController.swift index 3816f6ffb..fc04a4a31 100644 --- a/Mastodon/Scene/HomeTimeline/HomeTimelineViewController.swift +++ b/Mastodon/Scene/HomeTimeline/HomeTimelineViewController.swift @@ -308,14 +308,14 @@ extension HomeTimelineViewController { } .store(in: &disposeBag) - context.publisherService.statusPublishResult.receive(on: DispatchQueue.main).sink { result in + PublisherService.shared.statusPublishResult.receive(on: DispatchQueue.main).sink { result in if case .success(.edit(let status)) = result { self.viewModel?.hasPendingStatusEditReload = true self.viewModel?.dataController.update(status: .fromEntity(status.value), intent: .edit) } }.store(in: &disposeBag) - context.publisherService.$currentPublishProgress + PublisherService.shared.$currentPublishProgress .receive(on: DispatchQueue.main) .sink { [weak self] progress in guard let self = self else { return } @@ -434,7 +434,7 @@ extension HomeTimelineViewController { }) .store(in: &disposeBag) - context.publisherService.statusPublishResult.prepend(.failure(AppError.badRequest)) + PublisherService.shared.statusPublishResult.prepend(.failure(AppError.badRequest)) .receive(on: DispatchQueue.main) .sink { [weak self] publishResult in guard let self else { return } @@ -625,7 +625,7 @@ extension HomeTimelineViewController { } @objc private func settingBarButtonItemPressed(_ sender: UIBarButtonItem) { - guard let setting = context.settingService.currentSetting.value else { return } + guard let setting = SettingService.shared.currentSetting.value else { return } _ = coordinator.present(scene: .settings(setting: setting), from: self, transition: .none) } @@ -638,7 +638,6 @@ extension HomeTimelineViewController { } @objc func signOutAction(_ sender: UIAction) { - guard let authContext = viewModel?.authenticationBox else { return } Task { @MainActor in try await AuthenticationServiceProvider.shared.signOutMastodonUser(authentication: authenticationBox.authentication) diff --git a/Mastodon/Scene/HomeTimeline/HomeTimelineViewModel+Diffable.swift b/Mastodon/Scene/HomeTimeline/HomeTimelineViewModel+Diffable.swift index baa92a431..a05635dbd 100644 --- a/Mastodon/Scene/HomeTimeline/HomeTimelineViewModel+Diffable.swift +++ b/Mastodon/Scene/HomeTimeline/HomeTimelineViewModel+Diffable.swift @@ -8,6 +8,7 @@ import UIKit import MastodonUI import MastodonSDK +import MastodonCore extension HomeTimelineViewModel { @@ -25,7 +26,7 @@ extension HomeTimelineViewModel { statusTableViewCellDelegate: statusTableViewCellDelegate, timelineMiddleLoaderTableViewCellDelegate: timelineMiddleLoaderTableViewCellDelegate, filterContext: .home, - activeFilters: context.statusFilterService.$activeFilters + activeFilters: StatusFilterService.shared.$activeFilters ) ) diff --git a/Mastodon/Scene/HomeTimeline/HomeTimelineViewModel+Donation.swift b/Mastodon/Scene/HomeTimeline/HomeTimelineViewModel+Donation.swift index 19fef4dc3..b5f6cb98a 100644 --- a/Mastodon/Scene/HomeTimeline/HomeTimelineViewModel+Donation.swift +++ b/Mastodon/Scene/HomeTimeline/HomeTimelineViewModel+Donation.swift @@ -11,7 +11,7 @@ extension HomeTimelineViewModel { let userAuthentication = authenticationBox .authentication guard let accountCreatedAt = userAuthentication.accountCreatedAt else { - let updated = try? await context.apiService.accountVerifyCredentials(domain: userAuthentication.domain, authorization: authenticationBox.userAuthorization) + let updated = try? await APIService.shared.accountVerifyCredentials(domain: userAuthentication.domain, authorization: authenticationBox.userAuthorization) guard let accountCreatedAt = updated?.createdAt else { return } AuthenticationServiceProvider.shared.updateAccountCreatedAt(accountCreatedAt, forAuthentication: userAuthentication) return @@ -31,7 +31,7 @@ extension HomeTimelineViewModel { guard let self else { return } do { - let campaign = try await self.context.apiService + let campaign = try await APIService.shared .getDonationCampaign(seed: seed, source: nil).value guard !Mastodon.Entity.DonationCampaign.hasPreviouslyDismissed(campaign.id) && !Mastodon.Entity.DonationCampaign.hasPreviouslyContributed(campaign.id) else { return } onPresentDonationCampaign.send(campaign) diff --git a/Mastodon/Scene/HomeTimeline/HomeTimelineViewModel+LoadLatestState.swift b/Mastodon/Scene/HomeTimeline/HomeTimelineViewModel+LoadLatestState.swift index 13f8de841..e7ea62ef3 100644 --- a/Mastodon/Scene/HomeTimeline/HomeTimelineViewModel+LoadLatestState.swift +++ b/Mastodon/Scene/HomeTimeline/HomeTimelineViewModel+LoadLatestState.swift @@ -115,7 +115,7 @@ extension HomeTimelineViewModel.LoadLatestState { } do { - await AuthenticationServiceProvider.shared.fetchAccounts(apiService: viewModel.context.apiService) + await AuthenticationServiceProvider.shared.fetchAccounts() let response: Mastodon.Response.Content<[Mastodon.Entity.Status]> /// To find out wether or not we need to show the "Load More" button @@ -124,23 +124,23 @@ extension HomeTimelineViewModel.LoadLatestState { switch viewModel.timelineContext { case .home: - response = try await viewModel.context.apiService.homeTimeline( + response = try await APIService.shared.homeTimeline( sinceID: sinceID, authenticationBox: viewModel.authenticationBox ) case .public: - response = try await viewModel.context.apiService.publicTimeline( + response = try await APIService.shared.publicTimeline( query: .init(local: true, sinceID: sinceID), authenticationBox: viewModel.authenticationBox ) case let .list(id): - response = try await viewModel.context.apiService.listTimeline( + response = try await APIService.shared.listTimeline( id: id, query: .init(sinceID: sinceID), authenticationBox: viewModel.authenticationBox ) case let .hashtag(tag): - response = try await viewModel.context.apiService.hashtagTimeline( + response = try await APIService.shared.hashtagTimeline( hashtag: tag, authenticationBox: viewModel.authenticationBox ) diff --git a/Mastodon/Scene/HomeTimeline/HomeTimelineViewModel+LoadOldestState.swift b/Mastodon/Scene/HomeTimeline/HomeTimelineViewModel+LoadOldestState.swift index 0934cdfe8..45427c618 100644 --- a/Mastodon/Scene/HomeTimeline/HomeTimelineViewModel+LoadOldestState.swift +++ b/Mastodon/Scene/HomeTimeline/HomeTimelineViewModel+LoadOldestState.swift @@ -59,29 +59,29 @@ extension HomeTimelineViewModel.LoadOldestState { } do { - await AuthenticationServiceProvider.shared.fetchAccounts(apiService: viewModel.context.apiService) + await AuthenticationServiceProvider.shared.fetchAccounts() let response: Mastodon.Response.Content<[Mastodon.Entity.Status]> switch viewModel.timelineContext { case .home: - response = try await viewModel.context.apiService.homeTimeline( + response = try await APIService.shared.homeTimeline( maxID: maxID, authenticationBox: viewModel.authenticationBox ) case .public: - response = try await viewModel.context.apiService.publicTimeline( + response = try await APIService.shared.publicTimeline( query: .init(local: true, maxID: maxID), authenticationBox: viewModel.authenticationBox ) case let .list(id): - response = try await viewModel.context.apiService.listTimeline( - id: id, + response = try await APIService.shared.listTimeline( + id: id, query: .init(local: true, maxID: maxID), authenticationBox: viewModel.authenticationBox ) case let .hashtag(tag): - response = try await viewModel.context.apiService.hashtagTimeline( + response = try await APIService.shared.hashtagTimeline( hashtag: tag, authenticationBox: viewModel.authenticationBox ) diff --git a/Mastodon/Scene/HomeTimeline/HomeTimelineViewModel.swift b/Mastodon/Scene/HomeTimeline/HomeTimelineViewModel.swift index 35f81a715..a08bfab08 100644 --- a/Mastodon/Scene/HomeTimeline/HomeTimelineViewModel.swift +++ b/Mastodon/Scene/HomeTimeline/HomeTimelineViewModel.swift @@ -163,30 +163,30 @@ extension HomeTimelineViewModel { guard let status = record.status else { return } record.isLoadingMore = true - await AuthenticationServiceProvider.shared.fetchAccounts(apiService: context.apiService) + await AuthenticationServiceProvider.shared.fetchAccounts() // fetch data let response: Mastodon.Response.Content<[Mastodon.Entity.Status]>? switch timelineContext { case .home: - response = try? await context.apiService.homeTimeline( + response = try? await APIService.shared.homeTimeline( maxID: status.id, authenticationBox: authenticationBox ) case .public: - response = try? await context.apiService.publicTimeline( + response = try? await APIService.shared.publicTimeline( query: .init(local: true, maxID: status.id), authenticationBox: authenticationBox ) case let .list(id): - response = try? await context.apiService.listTimeline( - id: id, + response = try? await APIService.shared.listTimeline( + id: id, query: .init(local: true, maxID: status.id), authenticationBox: authenticationBox ) case let .hashtag(tag): - response = try? await context.apiService.hashtagTimeline( + response = try? await APIService.shared.hashtagTimeline( hashtag: tag, authenticationBox: authenticationBox ) diff --git a/Mastodon/Scene/MediaPreview/MediaPreviewViewController.swift b/Mastodon/Scene/MediaPreview/MediaPreviewViewController.swift index 6eaf104e1..551a97c27 100644 --- a/Mastodon/Scene/MediaPreview/MediaPreviewViewController.swift +++ b/Mastodon/Scene/MediaPreview/MediaPreviewViewController.swift @@ -274,7 +274,7 @@ extension MediaPreviewViewController: MediaPreviewImageViewControllerDelegate { switch action { case .savePhoto: guard let assetURL = viewController.viewModel.item.assetURL else { return } - context.photoLibraryService.save(imageSource: .url(assetURL)) + PhotoLibraryService.shared.save(imageSource: .url(assetURL)) .sink { [weak self] completion in guard let self = self else { return } switch completion { @@ -300,7 +300,7 @@ extension MediaPreviewViewController: MediaPreviewImageViewControllerDelegate { case .copyPhoto: guard let assetURL = viewController.viewModel.item.assetURL else { return } - context.photoLibraryService.copy(imageSource: .url(assetURL)) + PhotoLibraryService.shared.copy(imageSource: .url(assetURL)) .sink { completion in switch completion { case .failure(_): diff --git a/Mastodon/Scene/Notification/Notification Filtering/Policy/NotificationPolicyViewController.swift b/Mastodon/Scene/Notification/Notification Filtering/Policy/NotificationPolicyViewController.swift index 8bcfcf7c4..6039be8d2 100644 --- a/Mastodon/Scene/Notification/Notification Filtering/Policy/NotificationPolicyViewController.swift +++ b/Mastodon/Scene/Notification/Notification Filtering/Policy/NotificationPolicyViewController.swift @@ -149,7 +149,7 @@ class NotificationPolicyViewController: UIViewController { guard let self else { return } do { - let updatedPolicy = try await viewModel.appContext.apiService.updateNotificationPolicy( + let updatedPolicy = try await APIService.shared.updateNotificationPolicy( authenticationBox: authenticationBox, filterNotFollowing: viewModel.notFollowing, filterNotFollowers: viewModel.noFollower, diff --git a/Mastodon/Scene/Notification/Notification Filtering/Requests/NotificationRequestsTableViewController.swift b/Mastodon/Scene/Notification/Notification Filtering/Requests/NotificationRequestsTableViewController.swift index d544b721d..726133c44 100644 --- a/Mastodon/Scene/Notification/Notification Filtering/Requests/NotificationRequestsTableViewController.swift +++ b/Mastodon/Scene/Notification/Notification Filtering/Requests/NotificationRequestsTableViewController.swift @@ -136,10 +136,10 @@ extension NotificationRequestsTableViewController: NotificationRequestTableViewC } private func acceptNotificationRequest(_ notificationRequest: MastodonSDK.Mastodon.Entity.NotificationRequest) async throws { - _ = try await context.apiService.acceptNotificationRequests(authenticationBox: authenticationBox, + _ = try await APIService.shared.acceptNotificationRequests(authenticationBox: authenticationBox, id: notificationRequest.id) - let requests = try await context.apiService.notificationRequests(authenticationBox: authenticationBox).value + let requests = try await APIService.shared.notificationRequests(authenticationBox: authenticationBox).value NotificationCenter.default.post(name: .notificationFilteringChanged, object: nil) @@ -183,10 +183,10 @@ extension NotificationRequestsTableViewController: NotificationRequestTableViewC } private func rejectNotificationRequest(_ notificationRequest: MastodonSDK.Mastodon.Entity.NotificationRequest) async throws { - _ = try await context.apiService.rejectNotificationRequests(authenticationBox: authenticationBox, + _ = try await APIService.shared.rejectNotificationRequests(authenticationBox: authenticationBox, id: notificationRequest.id) - let requests = try await context.apiService.notificationRequests(authenticationBox: authenticationBox).value + let requests = try await APIService.shared.notificationRequests(authenticationBox: authenticationBox).value NotificationCenter.default.post(name: .notificationFilteringChanged, object: nil) diff --git a/Mastodon/Scene/Notification/NotificationTimeline/NotificationTimelineViewController.swift b/Mastodon/Scene/Notification/NotificationTimeline/NotificationTimelineViewController.swift index 2458b79e0..88941a8fb 100644 --- a/Mastodon/Scene/Notification/NotificationTimeline/NotificationTimelineViewController.swift +++ b/Mastodon/Scene/Notification/NotificationTimeline/NotificationTimelineViewController.swift @@ -126,7 +126,7 @@ extension NotificationTimelineViewController { @objc private func refreshControlValueChanged(_ sender: RefreshControl) { Task { - let policy = try? await context.apiService.notificationPolicy(authenticationBox: authenticationBox) + let policy = try? await APIService.shared.notificationPolicy(authenticationBox: authenticationBox) viewModel.notificationPolicy = policy?.value await viewModel.loadLatest() diff --git a/Mastodon/Scene/Notification/NotificationTimeline/NotificationTimelineViewModel+Diffable.swift b/Mastodon/Scene/Notification/NotificationTimeline/NotificationTimelineViewModel+Diffable.swift index 4b3479c0e..d516f0bb6 100644 --- a/Mastodon/Scene/Notification/NotificationTimeline/NotificationTimelineViewModel+Diffable.swift +++ b/Mastodon/Scene/Notification/NotificationTimeline/NotificationTimelineViewModel+Diffable.swift @@ -8,6 +8,7 @@ import UIKit import CoreData import MastodonSDK +import MastodonCore extension NotificationTimelineViewModel { @@ -22,7 +23,7 @@ extension NotificationTimelineViewModel { authenticationBox: authenticationBox, notificationTableViewCellDelegate: notificationTableViewCellDelegate, filterContext: .notifications, - activeFilters: context.statusFilterService.$activeFilters + activeFilters: StatusFilterService.shared.$activeFilters ) ) diff --git a/Mastodon/Scene/Notification/NotificationTimeline/NotificationTimelineViewModel+LoadOldestState.swift b/Mastodon/Scene/Notification/NotificationTimeline/NotificationTimelineViewModel+LoadOldestState.swift index 4168b1bd3..ea3c2e5cb 100644 --- a/Mastodon/Scene/Notification/NotificationTimeline/NotificationTimelineViewModel+LoadOldestState.swift +++ b/Mastodon/Scene/Notification/NotificationTimeline/NotificationTimelineViewModel+LoadOldestState.swift @@ -77,7 +77,7 @@ extension NotificationTimelineViewModel.LoadOldestState { } do { - let response = try await viewModel.context.apiService.notifications( + let response = try await APIService.shared.notifications( maxID: maxID, accountID: accountID, scope: scope, diff --git a/Mastodon/Scene/Notification/NotificationTimeline/NotificationTimelineViewModel.swift b/Mastodon/Scene/Notification/NotificationTimeline/NotificationTimelineViewModel.swift index 25399e4ed..f01caa5a4 100644 --- a/Mastodon/Scene/Notification/NotificationTimeline/NotificationTimelineViewModel.swift +++ b/Mastodon/Scene/Notification/NotificationTimeline/NotificationTimelineViewModel.swift @@ -99,7 +99,7 @@ final class NotificationTimelineViewModel { Task { [weak self] in guard let self else { return } - let policy = try await self.context.apiService.notificationPolicy(authenticationBox: self.authenticationBox) + let policy = try await APIService.shared.notificationPolicy(authenticationBox: self.authenticationBox) self.notificationPolicy = policy.value await self.loadLatest() diff --git a/Mastodon/Scene/Notification/NotificationViewController.swift b/Mastodon/Scene/Notification/NotificationViewController.swift index 7ce680148..d9a15a85e 100644 --- a/Mastodon/Scene/Notification/NotificationViewController.swift +++ b/Mastodon/Scene/Notification/NotificationViewController.swift @@ -100,14 +100,14 @@ extension NotificationViewController { super.viewDidAppear(animated) // reset notification count - context.notificationService.clearNotificationCountForActiveUser() + NotificationService.shared.clearNotificationCountForActiveUser() } override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) // reset notification count - context.notificationService.clearNotificationCountForActiveUser() + NotificationService.shared.clearNotificationCountForActiveUser() } override func viewDidDisappear(_ animated: Bool) { diff --git a/Mastodon/Scene/Notification/NotificationViewModel.swift b/Mastodon/Scene/Notification/NotificationViewModel.swift index 3fd48e413..0e93c27b1 100644 --- a/Mastodon/Scene/Notification/NotificationViewModel.swift +++ b/Mastodon/Scene/Notification/NotificationViewModel.swift @@ -56,7 +56,7 @@ final class NotificationViewModel { // end init Task { do { - let policy = try await context.apiService.notificationPolicy(authenticationBox: authenticationBox) + let policy = try await APIService.shared.notificationPolicy(authenticationBox: authenticationBox) self.notificationPolicy = policy.value } catch { // we won't show the filtering-options. diff --git a/Mastodon/Scene/Onboarding/ConfirmEmail/MastodonConfirmEmailViewController.swift b/Mastodon/Scene/Onboarding/ConfirmEmail/MastodonConfirmEmailViewController.swift index 62a861f8c..1c36c6cf6 100644 --- a/Mastodon/Scene/Onboarding/ConfirmEmail/MastodonConfirmEmailViewController.swift +++ b/Mastodon/Scene/Onboarding/ConfirmEmail/MastodonConfirmEmailViewController.swift @@ -109,7 +109,7 @@ extension MastodonConfirmEmailViewController { // upload avatar and set display name in the background Just(self.viewModel.userToken.accessToken) .asyncMap { token in - try await self.context.apiService.accountUpdateCredentials( + try await APIService.shared.accountUpdateCredentials( domain: self.viewModel.authenticateInfo.domain, query: self.viewModel.updateCredentialQuery, authorization: Mastodon.API.OAuth.Authorization(accessToken: token) diff --git a/Mastodon/Scene/Onboarding/Login/MastodonLoginViewController.swift b/Mastodon/Scene/Onboarding/Login/MastodonLoginViewController.swift index ef69aa8f6..fdaafa579 100644 --- a/Mastodon/Scene/Onboarding/Login/MastodonLoginViewController.swift +++ b/Mastodon/Scene/Onboarding/Login/MastodonLoginViewController.swift @@ -137,7 +137,7 @@ class MastodonLoginViewController: UIViewController, NeedsDependency { .store(in: &disposeBag) authenticationViewModel.isAuthenticating.send(true) - context.apiService.createApplication(domain: server.domain) + APIService.shared.createApplication(domain: server.domain) .tryMap { response -> AuthenticationViewModel.AuthenticateInfo in let application = response.value guard let info = AuthenticationViewModel.AuthenticateInfo( diff --git a/Mastodon/Scene/Onboarding/Login/MastodonLoginViewModel.swift b/Mastodon/Scene/Onboarding/Login/MastodonLoginViewModel.swift index 64237d71f..ea00016b5 100644 --- a/Mastodon/Scene/Onboarding/Login/MastodonLoginViewModel.swift +++ b/Mastodon/Scene/Onboarding/Login/MastodonLoginViewModel.swift @@ -28,7 +28,7 @@ class MastodonLoginViewModel { } func updateServers() { - appContext?.apiService.servers(registrations: "all").sink(receiveCompletion: { [weak self] completion in + APIService.shared.servers(registrations: "all").sink(receiveCompletion: { [weak self] completion in switch completion { case .finished: guard let self = self else { return } diff --git a/Mastodon/Scene/Onboarding/PickServer/MastodonPickServerViewController.swift b/Mastodon/Scene/Onboarding/PickServer/MastodonPickServerViewController.swift index b2d7822b7..eb31f5a7a 100644 --- a/Mastodon/Scene/Onboarding/PickServer/MastodonPickServerViewController.swift +++ b/Mastodon/Scene/Onboarding/PickServer/MastodonPickServerViewController.swift @@ -271,13 +271,13 @@ extension MastodonPickServerViewController { authenticationViewModel.isAuthenticating.send(true) - context.apiService.instance(domain: server.domain, authenticationBox: nil) + APIService.shared.instance(domain: server.domain, authenticationBox: nil) .compactMap { [weak self] response -> AnyPublisher? in guard let self = self else { return nil } guard response.value.registrations != false else { return Fail(error: AuthenticationViewModel.AuthenticationError.registrationClosed).eraseToAnyPublisher() } - return self.context.apiService.createApplication(domain: server.domain) + return APIService.shared.createApplication(domain: server.domain) .map { MastodonPickServerViewModel.SignUpResponseFirst(instance: response, application: $0) } .eraseToAnyPublisher() } @@ -299,7 +299,7 @@ extension MastodonPickServerViewController { guard let self = self else { return nil } let instance = response.instance let authenticateInfo = response.authenticateInfo - return self.context.apiService.applicationAccessToken( + return APIService.shared.applicationAccessToken( domain: server.domain, clientID: authenticateInfo.clientID, clientSecret: authenticateInfo.clientSecret, diff --git a/Mastodon/Scene/Onboarding/PickServer/MastodonPickServerViewModel+LoadIndexedServerState.swift b/Mastodon/Scene/Onboarding/PickServer/MastodonPickServerViewModel+LoadIndexedServerState.swift index f7ccebd84..a3a8b2c79 100644 --- a/Mastodon/Scene/Onboarding/PickServer/MastodonPickServerViewModel+LoadIndexedServerState.swift +++ b/Mastodon/Scene/Onboarding/PickServer/MastodonPickServerViewModel+LoadIndexedServerState.swift @@ -8,6 +8,7 @@ import Foundation import GameplayKit import MastodonSDK +import MastodonCore extension MastodonPickServerViewModel { class LoadIndexedServerState: GKState { @@ -37,7 +38,7 @@ extension MastodonPickServerViewModel.LoadIndexedServerState { guard let viewModel = self.viewModel, let stateMachine = self.stateMachine else { return } viewModel.isLoadingIndexedServers.value = true - viewModel.context.apiService.servers(language: nil, category: nil) + APIService.shared.servers(language: nil, category: nil) .sink { completion in switch completion { case .failure(let error): diff --git a/Mastodon/Scene/Onboarding/PickServer/MastodonPickServerViewModel.swift b/Mastodon/Scene/Onboarding/PickServer/MastodonPickServerViewModel.swift index f0278bb3e..916ee6bd4 100644 --- a/Mastodon/Scene/Onboarding/PickServer/MastodonPickServerViewModel.swift +++ b/Mastodon/Scene/Onboarding/PickServer/MastodonPickServerViewModel.swift @@ -85,7 +85,7 @@ extension MastodonPickServerViewModel { private func configure() { - context.apiService.languages().sink { completion in + APIService.shared.languages().sink { completion in } receiveValue: { response in self.allLanguages.value = response.value @@ -165,9 +165,9 @@ extension MastodonPickServerViewModel { return Just(Result.failure(APIService.APIError.implicit(.badRequest))).eraseToAnyPublisher() } self.unindexedServers.value = nil - return self.context.apiService.webFinger(domain: domain) + return APIService.shared.webFinger(domain: domain) .flatMap { domain -> AnyPublisher, Error>, Never> in - return self.context.apiService.instance(domain: domain, authenticationBox: nil) + return APIService.shared.instance(domain: domain, authenticationBox: nil) .map { response -> Result, Error>in let newResponse = response.map { [Mastodon.Entity.Server(domain: domain, instance: $0)] } return Result.success(newResponse) diff --git a/Mastodon/Scene/Onboarding/Register/MastodonRegisterViewController.swift b/Mastodon/Scene/Onboarding/Register/MastodonRegisterViewController.swift index 864b0355e..fe7572bb6 100644 --- a/Mastodon/Scene/Onboarding/Register/MastodonRegisterViewController.swift +++ b/Mastodon/Scene/Onboarding/Register/MastodonRegisterViewController.swift @@ -197,7 +197,7 @@ extension MastodonRegisterViewController { var retryCount = 0 // register without show server rules - context.apiService.accountRegister( + APIService.shared.accountRegister( domain: viewModel.domain, query: query, authorization: viewModel.applicationAuthorization @@ -222,7 +222,7 @@ extension MastodonRegisterViewController { locale: self.viewModel.instance.languages?.first ?? "en" ) retryCount += 1 - return self.context.apiService.accountRegister( + return APIService.shared.accountRegister( domain: self.viewModel.domain, query: retryQuery, authorization: self.viewModel.applicationAuthorization diff --git a/Mastodon/Scene/Onboarding/Register/MastodonRegisterViewModel.swift b/Mastodon/Scene/Onboarding/Register/MastodonRegisterViewModel.swift index 4aa8b2011..c68f6ce42 100644 --- a/Mastodon/Scene/Onboarding/Register/MastodonRegisterViewModel.swift +++ b/Mastodon/Scene/Onboarding/Register/MastodonRegisterViewModel.swift @@ -110,7 +110,7 @@ final class MastodonRegisterViewModel: ObservableObject { .compactMap { [weak self] text -> AnyPublisher, Error>, Never>? in guard let self = self else { return nil } let query = Mastodon.API.Account.AccountLookupQuery(acct: text) - return context.apiService.accountLookup(domain: domain, query: query, authorization: self.applicationAuthorization) + return APIService.shared.accountLookup(domain: domain, query: query, authorization: self.applicationAuthorization) .map { response -> Result, Error> in Result.success(response) diff --git a/Mastodon/Scene/Onboarding/ServerRules/MastodonServerRulesViewController+Debug.swift b/Mastodon/Scene/Onboarding/ServerRules/MastodonServerRulesViewController+Debug.swift index 0cfde37dd..ebcc5655b 100644 --- a/Mastodon/Scene/Onboarding/ServerRules/MastodonServerRulesViewController+Debug.swift +++ b/Mastodon/Scene/Onboarding/ServerRules/MastodonServerRulesViewController+Debug.swift @@ -22,9 +22,9 @@ extension MastodonRegisterViewController { viewController.context = context viewController.coordinator = coordinator - let instanceResponse = try await context.apiService.instance(domain: domain, authenticationBox: nil).singleOutput() - let applicationResponse = try await context.apiService.createApplication(domain: domain).singleOutput() - let accessTokenResponse = try await context.apiService.applicationAccessToken( + let instanceResponse = try await APIService.shared.instance(domain: domain, authenticationBox: nil).singleOutput() + let applicationResponse = try await APIService.shared.createApplication(domain: domain).singleOutput() + let accessTokenResponse = try await APIService.shared.applicationAccessToken( domain: domain, clientID: applicationResponse.value.clientID!, clientSecret: applicationResponse.value.clientSecret!, diff --git a/Mastodon/Scene/Onboarding/Share/AuthenticationViewModel.swift b/Mastodon/Scene/Onboarding/Share/AuthenticationViewModel.swift index 596cf3088..0aae1322d 100644 --- a/Mastodon/Scene/Onboarding/Share/AuthenticationViewModel.swift +++ b/Mastodon/Scene/Onboarding/Share/AuthenticationViewModel.swift @@ -143,7 +143,7 @@ extension AuthenticationViewModel { }) .compactMap { [weak self] code -> AnyPublisher, Error>? in guard let self = self else { return nil } - return self.context.apiService + return APIService.shared .userAccessToken( domain: info.domain, clientID: info.clientID, @@ -188,7 +188,7 @@ extension AuthenticationViewModel { ) -> AnyPublisher, Error> { let authorization = Mastodon.API.OAuth.Authorization(accessToken: userToken.accessToken) - return context.apiService.accountVerifyCredentials( + return APIService.shared.accountVerifyCredentials( domain: info.domain, authorization: authorization ) @@ -224,7 +224,7 @@ extension AuthenticationViewModel { ) async throws -> Mastodon.Entity.Account { let authorization = Mastodon.API.OAuth.Authorization(accessToken: userToken) - let account = try await context.apiService.accountVerifyCredentials( + let account = try await APIService.shared.accountVerifyCredentials( domain: domain, authorization: authorization ) diff --git a/Mastodon/Scene/Onboarding/Welcome/WelcomeViewController.swift b/Mastodon/Scene/Onboarding/Welcome/WelcomeViewController.swift index 498e99d53..8b3516ad4 100644 --- a/Mastodon/Scene/Onboarding/Welcome/WelcomeViewController.swift +++ b/Mastodon/Scene/Onboarding/Welcome/WelcomeViewController.swift @@ -283,13 +283,13 @@ extension WelcomeViewController { authenticationViewModel.isAuthenticating.send(true) - context.apiService.instance(domain: server.domain, authenticationBox: nil) + APIService.shared.instance(domain: server.domain, authenticationBox: nil) .compactMap { [weak self] response -> AnyPublisher? in guard let self = self else { return nil } guard response.value.registrations != false else { return Fail(error: AuthenticationViewModel.AuthenticationError.registrationClosed).eraseToAnyPublisher() } - return self.context.apiService.createApplication(domain: server.domain) + return APIService.shared.createApplication(domain: server.domain) .map { MastodonPickServerViewModel.SignUpResponseFirst(instance: response, application: $0) } .eraseToAnyPublisher() } @@ -311,7 +311,7 @@ extension WelcomeViewController { guard let self = self else { return nil } let instance = response.instance let authenticateInfo = response.authenticateInfo - return self.context.apiService.applicationAccessToken( + return APIService.shared.applicationAccessToken( domain: server.domain, clientID: authenticateInfo.clientID, clientSecret: authenticateInfo.clientSecret, diff --git a/Mastodon/Scene/Onboarding/Welcome/WelcomeViewModel.swift b/Mastodon/Scene/Onboarding/Welcome/WelcomeViewModel.swift index 2067cf7cc..842f71ca1 100644 --- a/Mastodon/Scene/Onboarding/Welcome/WelcomeViewModel.swift +++ b/Mastodon/Scene/Onboarding/Welcome/WelcomeViewModel.swift @@ -31,7 +31,7 @@ final class WelcomeViewModel { } func downloadDefaultServer(completion: (() -> Void)? = nil) { - context.apiService.defaultServers() + APIService.shared.defaultServers() .timeout(.milliseconds(500) , scheduler: DispatchQueue.main) .sink { [weak self] result in diff --git a/Mastodon/Scene/Profile/Bookmark/BookmarkViewModel+State.swift b/Mastodon/Scene/Profile/Bookmark/BookmarkViewModel+State.swift index eb4699dfd..1821dbf50 100644 --- a/Mastodon/Scene/Profile/Bookmark/BookmarkViewModel+State.swift +++ b/Mastodon/Scene/Profile/Bookmark/BookmarkViewModel+State.swift @@ -124,7 +124,7 @@ extension BookmarkViewModel.State { Task { do { - let response = try await viewModel.context.apiService.bookmarkedStatuses( + let response = try await APIService.shared.bookmarkedStatuses( maxID: maxID, authenticationBox: viewModel.authenticationBox ) diff --git a/Mastodon/Scene/Profile/Favorite/FavoriteViewModel+State.swift b/Mastodon/Scene/Profile/Favorite/FavoriteViewModel+State.swift index ff42a6c72..e238ac940 100644 --- a/Mastodon/Scene/Profile/Favorite/FavoriteViewModel+State.swift +++ b/Mastodon/Scene/Profile/Favorite/FavoriteViewModel+State.swift @@ -123,7 +123,7 @@ extension FavoriteViewModel.State { Task { do { - let response = try await viewModel.context.apiService.favoritedStatuses( + let response = try await APIService.shared.favoritedStatuses( maxID: maxID, authenticationBox: viewModel.authenticationBox ) diff --git a/Mastodon/Scene/Profile/FollowedTags/FollowedTagsViewModel.swift b/Mastodon/Scene/Profile/FollowedTags/FollowedTagsViewModel.swift index 36c97d595..3ac57d8a0 100644 --- a/Mastodon/Scene/Profile/FollowedTags/FollowedTagsViewModel.swift +++ b/Mastodon/Scene/Profile/FollowedTags/FollowedTagsViewModel.swift @@ -38,7 +38,7 @@ extension FollowedTagsViewModel { func fetchFollowedTags(completion: (() -> Void)? = nil ) { Task { @MainActor in do { - followedTags = try await context.apiService.getFollowedTags( + followedTags = try await APIService.shared.getFollowedTags( domain: authenticationBox.domain, query: Mastodon.API.Account.FollowedTagsQuery(limit: nil), authenticationBox: authenticationBox @@ -59,12 +59,12 @@ extension FollowedTagsViewModel { func followOrUnfollow(_ tag: Mastodon.Entity.Tag) { Task { @MainActor in if tag.following ?? false { - _ = try? await context.apiService.unfollowTag( + _ = try? await APIService.shared.unfollowTag( for: tag.name, authenticationBox: authenticationBox ) } else { - _ = try? await context.apiService.followTag( + _ = try? await APIService.shared.followTag( for: tag.name, authenticationBox: authenticationBox ) diff --git a/Mastodon/Scene/Profile/Follower/FollowerListViewModel+State.swift b/Mastodon/Scene/Profile/Follower/FollowerListViewModel+State.swift index 6026c13d8..4c13e4011 100644 --- a/Mastodon/Scene/Profile/Follower/FollowerListViewModel+State.swift +++ b/Mastodon/Scene/Profile/Follower/FollowerListViewModel+State.swift @@ -138,7 +138,7 @@ extension FollowerListViewModel.State { Task { do { - let accountResponse = try await viewModel.context.apiService.followers( + let accountResponse = try await APIService.shared.followers( userID: userID, maxID: maxID, authenticationBox: viewModel.authenticationBox @@ -154,7 +154,7 @@ extension FollowerListViewModel.State { var hasNewAppend = false - let newRelationships = try await viewModel.context.apiService.relationship(forAccounts: accountResponse.value, authenticationBox: viewModel.authenticationBox) + let newRelationships = try await APIService.shared.relationship(forAccounts: accountResponse.value, authenticationBox: viewModel.authenticationBox) var accounts = viewModel.accounts diff --git a/Mastodon/Scene/Profile/Following/FollowingListViewModel+State.swift b/Mastodon/Scene/Profile/Following/FollowingListViewModel+State.swift index 989c07325..064fc4de8 100644 --- a/Mastodon/Scene/Profile/Following/FollowingListViewModel+State.swift +++ b/Mastodon/Scene/Profile/Following/FollowingListViewModel+State.swift @@ -8,6 +8,7 @@ import Foundation import GameplayKit import MastodonSDK +import MastodonCore extension FollowingListViewModel { class State: GKState { @@ -133,7 +134,7 @@ extension FollowingListViewModel.State { Task { do { - let accountResponse = try await viewModel.context.apiService.following( + let accountResponse = try await APIService.shared.following( userID: userID, maxID: maxID, authenticationBox: viewModel.authenticationBox @@ -149,7 +150,7 @@ extension FollowingListViewModel.State { var hasNewAppend = false - let newRelationships = try await viewModel.context.apiService.relationship(forAccounts: accountResponse.value, authenticationBox: viewModel.authenticationBox) + let newRelationships = try await APIService.shared.relationship(forAccounts: accountResponse.value, authenticationBox: viewModel.authenticationBox) var accounts = viewModel.accounts diff --git a/Mastodon/Scene/Profile/ProfileViewController.swift b/Mastodon/Scene/Profile/ProfileViewController.swift index f20c1b29f..08cf3fe1a 100644 --- a/Mastodon/Scene/Profile/ProfileViewController.swift +++ b/Mastodon/Scene/Profile/ProfileViewController.swift @@ -405,7 +405,7 @@ extension ProfileViewController { } .store(in: &disposeBag) - context.publisherService.statusPublishResult.sink { [weak self] result in + PublisherService.shared.statusPublishResult.sink { [weak self] result in if case .success(.edit(let status)) = result { self?.updateViewModelsWithDataControllers(status: .fromEntity(status.value), intent: .edit) } @@ -576,7 +576,7 @@ extension ProfileViewController { } @objc private func settingBarButtonItemPressed(_ sender: UIBarButtonItem) { - guard let setting = context.settingService.currentSetting.value else { return } + guard let setting = SettingService.shared.currentSetting.value else { return } _ = coordinator.present(scene: .settings(setting: setting), from: self, transition: .none) } @@ -645,15 +645,15 @@ extension ProfileViewController { let account = viewModel.account if let domain = account.domain, - let updatedAccount = try? await context.apiService.fetchUser(username: account.acct, domain: domain, authenticationBox: viewModel.authenticationBox), - let updatedRelationship = try? await context.apiService.relationship(forAccounts: [updatedAccount], authenticationBox: viewModel.authenticationBox).value.first + let updatedAccount = try? await APIService.shared.fetchUser(username: account.acct, domain: domain, authenticationBox: viewModel.authenticationBox), + let updatedRelationship = try? await APIService.shared.relationship(forAccounts: [updatedAccount], authenticationBox: viewModel.authenticationBox).value.first { viewModel.account = updatedAccount viewModel.relationship = updatedRelationship viewModel.profileAboutViewModel.fields = updatedAccount.mastodonFields } - if let updatedMe = try? await context.apiService.authenticatedUserInfo(authenticationBox: viewModel.authenticationBox).value { + if let updatedMe = try? await APIService.shared.authenticatedUserInfo(authenticationBox: viewModel.authenticationBox).value { viewModel.me = updatedMe FileManager.default.store(account: updatedMe, forUserID: viewModel.authenticationBox.authentication.userIdentifier()) } @@ -916,7 +916,7 @@ extension ProfileViewController: ProfileHeaderViewControllerDelegate { Task { _ = try await DataSourceFacade.responseToDomainBlockAction(dependency: self, account: account) - guard let newRelationship = try await self.context.apiService.relationship(forAccounts: [account], authenticationBox: viewModel.authenticationBox).value.first else { return } + guard let newRelationship = try await APIService.shared.relationship(forAccounts: [account], authenticationBox: viewModel.authenticationBox).value.first else { return } viewModel.isUpdating = false @@ -1084,7 +1084,7 @@ extension ProfileViewController { Task { let account = viewModel.account if let domain = account.domain, - let updatedAccount = try? await context.apiService.fetchUser(username: account.acct, domain: domain, authenticationBox: viewModel.authenticationBox) { + let updatedAccount = try? await APIService.shared.fetchUser(username: account.acct, domain: domain, authenticationBox: viewModel.authenticationBox) { viewModel.account = updatedAccount viewModel.relationship = relationship @@ -1097,7 +1097,7 @@ extension ProfileViewController { } else if viewModel.account == viewModel.me { // update my profile Task { - if let updatedMe = try? await context.apiService.authenticatedUserInfo(authenticationBox: viewModel.authenticationBox).value { + if let updatedMe = try? await APIService.shared.authenticatedUserInfo(authenticationBox: viewModel.authenticationBox).value { viewModel.me = updatedMe viewModel.account = updatedMe FileManager.default.store(account: updatedMe, forUserID: viewModel.authenticationBox.authentication.userIdentifier()) diff --git a/Mastodon/Scene/Profile/ProfileViewModel.swift b/Mastodon/Scene/Profile/ProfileViewModel.swift index 15b313a5f..a50436563 100644 --- a/Mastodon/Scene/Profile/ProfileViewModel.swift +++ b/Mastodon/Scene/Profile/ProfileViewModel.swift @@ -134,7 +134,7 @@ class ProfileViewModel: NSObject { Task { do { - let response = try await self.context.apiService.relationship( + let response = try await APIService.shared.relationship( forAccounts: [account], authenticationBox: self.authenticationBox ) @@ -178,7 +178,7 @@ class ProfileViewModel: NSObject { let mastodonAuthentication = authenticationBox.authentication let authorization = Mastodon.API.OAuth.Authorization(accessToken: mastodonAuthentication.userAccessToken) - return context.apiService.accountVerifyCredentials(domain: domain, authorization: authorization) + return APIService.shared.accountVerifyCredentials(domain: domain, authorization: authorization) .tryMap { response in FileManager.default.store(account: response.value, forUserID: mastodonAuthentication.userIdentifier()) return response @@ -227,7 +227,7 @@ extension ProfileViewModel { source: nil, fieldsAttributes: fieldsAttributes ) - let response = try await context.apiService.accountUpdateCredentials( + let response = try await APIService.shared.accountUpdateCredentials( domain: domain, query: query, authorization: authorization diff --git a/Mastodon/Scene/Profile/Timeline/UserTimelineViewModel+State.swift b/Mastodon/Scene/Profile/Timeline/UserTimelineViewModel+State.swift index a46fd563e..2b7676845 100644 --- a/Mastodon/Scene/Profile/Timeline/UserTimelineViewModel+State.swift +++ b/Mastodon/Scene/Profile/Timeline/UserTimelineViewModel+State.swift @@ -126,7 +126,7 @@ extension UserTimelineViewModel.State { let queryFilter = viewModel.queryFilter do { - let response = try await viewModel.context.apiService.userTimeline( + let response = try await APIService.shared.userTimeline( accountID: userID, maxID: maxID, sinceID: nil, diff --git a/Mastodon/Scene/Profile/UserList/UserListViewModel+State.swift b/Mastodon/Scene/Profile/UserList/UserListViewModel+State.swift index 0eb41d7eb..d8308b45e 100644 --- a/Mastodon/Scene/Profile/UserList/UserListViewModel+State.swift +++ b/Mastodon/Scene/Profile/UserList/UserListViewModel+State.swift @@ -8,6 +8,7 @@ import Foundation import GameplayKit import MastodonSDK +import MastodonCore extension UserListViewModel { class State: GKState { @@ -128,13 +129,13 @@ extension UserListViewModel.State { let accountResponse: Mastodon.Response.Content<[Mastodon.Entity.Account]> switch viewModel.kind { case .favoritedBy(let status): - accountResponse = try await viewModel.context.apiService.favoritedBy( + accountResponse = try await APIService.shared.favoritedBy( status: status, query: .init(maxID: maxID, limit: nil), authenticationBox: authenticationBox ) case .rebloggedBy(let status): - accountResponse = try await viewModel.context.apiService.rebloggedBy( + accountResponse = try await APIService.shared.rebloggedBy( status: status, query: .init(maxID: maxID, limit: nil), authenticationBox: authenticationBox @@ -151,7 +152,7 @@ extension UserListViewModel.State { var hasNewAppend = false - let newRelationships = try await viewModel.context.apiService.relationship(forAccounts: accountResponse.value, authenticationBox: viewModel.authenticationBox) + let newRelationships = try await APIService.shared.relationship(forAccounts: accountResponse.value, authenticationBox: viewModel.authenticationBox) var accounts = viewModel.accounts diff --git a/Mastodon/Scene/Report/Report/ReportViewModel.swift b/Mastodon/Scene/Report/Report/ReportViewModel.swift index 4c537ac8b..ab60499c5 100644 --- a/Mastodon/Scene/Report/Report/ReportViewModel.swift +++ b/Mastodon/Scene/Report/Report/ReportViewModel.swift @@ -67,7 +67,7 @@ class ReportViewModel { // bind server rules Task { @MainActor in do { - let response = try await context.apiService.instance(domain: authenticationBox.domain, authenticationBox: authenticationBox) + let response = try await APIService.shared.instance(domain: authenticationBox.domain, authenticationBox: authenticationBox) .timeout(3, scheduler: DispatchQueue.main) .singleOutput() let rules = response.value.rules ?? [] @@ -143,7 +143,7 @@ extension ReportViewModel { do { isReporting = true - let _ = try await context.apiService.report( + let _ = try await APIService.shared.report( query: query, authenticationBox: authenticationBox ) diff --git a/Mastodon/Scene/Report/ReportStatus/ReportStatusViewModel+State.swift b/Mastodon/Scene/Report/ReportStatus/ReportStatusViewModel+State.swift index c1bcdd319..c431d164e 100644 --- a/Mastodon/Scene/Report/ReportStatus/ReportStatusViewModel+State.swift +++ b/Mastodon/Scene/Report/ReportStatus/ReportStatusViewModel+State.swift @@ -10,6 +10,7 @@ import Foundation import CoreData import CoreDataStack import GameplayKit +import MastodonCore extension ReportStatusViewModel { class State: GKState { @@ -69,7 +70,7 @@ extension ReportStatusViewModel.State { let maxID = await viewModel.dataController.records.last?.id do { - let response = try await viewModel.context.apiService.userTimeline( + let response = try await APIService.shared.userTimeline( accountID: viewModel.account.id, maxID: maxID, sinceID: nil, diff --git a/Mastodon/Scene/Root/MainTab/MainTabBarController.swift b/Mastodon/Scene/Root/MainTab/MainTabBarController.swift index de5e8fea7..24f089b36 100644 --- a/Mastodon/Scene/Root/MainTab/MainTabBarController.swift +++ b/Mastodon/Scene/Root/MainTab/MainTabBarController.swift @@ -136,7 +136,7 @@ extension MainTabBarController { } } - context.apiService.error + APIService.shared.error .receive(on: DispatchQueue.main) .sink { [weak self] error in guard let self, let coordinator = self.coordinator else { return } @@ -161,7 +161,7 @@ extension MainTabBarController { // handle push notification. // toggle entry when finish fetch latest notification Publishers.CombineLatest( - context.notificationService.unreadNotificationCountDidUpdate, + NotificationService.shared.unreadNotificationCountDidUpdate, $currentTab ) .receive(on: DispatchQueue.main) @@ -380,7 +380,7 @@ extension MainTabBarController { guard let authenticationBox else { return } Task { @MainActor in - let profileResponse = try await context.apiService.authenticatedUserInfo(authenticationBox: authenticationBox) + let profileResponse = try await APIService.shared.authenticatedUserInfo(authenticationBox: authenticationBox) FileManager.default.store(account: profileResponse.value, forUserID: authenticationBox.authentication.userIdentifier()) } } @@ -519,7 +519,7 @@ extension MainTabBarController { } // open settings - if context.settingService.currentSetting.value != nil { + if SettingService.shared.currentSetting.value != nil { commands.append(openSettingsKeyCommand) } } @@ -562,7 +562,7 @@ extension MainTabBarController { } @objc private func openSettingsKeyCommandHandler(_ sender: UIKeyCommand) { - guard let setting = context.settingService.currentSetting.value else { return } + guard let setting = SettingService.shared.currentSetting.value else { return } _ = coordinator.present(scene: .settings(setting: setting), from: self, transition: .none) } diff --git a/Mastodon/Scene/Root/Sidebar/SidebarViewController.swift b/Mastodon/Scene/Root/Sidebar/SidebarViewController.swift index 90cfd3a53..24f33fd64 100644 --- a/Mastodon/Scene/Root/Sidebar/SidebarViewController.swift +++ b/Mastodon/Scene/Root/Sidebar/SidebarViewController.swift @@ -198,7 +198,7 @@ extension SidebarViewController: UICollectionViewDelegate { case .tab(let tab): delegate?.sidebarViewController(self, didSelectTab: tab) case .setting: - guard let setting = context.settingService.currentSetting.value else { return } + guard let setting = SettingService.shared.currentSetting.value else { return } _ = coordinator.present(scene: .settings(setting: setting), from: self, transition: .none) case .compose: diff --git a/Mastodon/Scene/Root/Sidebar/SidebarViewModel.swift b/Mastodon/Scene/Root/Sidebar/SidebarViewModel.swift index 721a60874..97d97008f 100644 --- a/Mastodon/Scene/Root/Sidebar/SidebarViewModel.swift +++ b/Mastodon/Scene/Root/Sidebar/SidebarViewModel.swift @@ -107,7 +107,7 @@ extension SidebarViewModel { switch item { case .notifications: Publishers.CombineLatest( - self.context.notificationService.unreadNotificationCountDidUpdate, + NotificationService.shared.unreadNotificationCountDidUpdate, self.$currentTab ) .receive(on: DispatchQueue.main) diff --git a/Mastodon/Scene/Search/SearchDetail/Search Results Overview/SearchResultOverviewCoordinator.swift b/Mastodon/Scene/Search/SearchDetail/Search Results Overview/SearchResultOverviewCoordinator.swift index a6095d6e7..7781445f6 100644 --- a/Mastodon/Scene/Search/SearchDetail/Search Results Overview/SearchResultOverviewCoordinator.swift +++ b/Mastodon/Scene/Search/SearchDetail/Search Results Overview/SearchResultOverviewCoordinator.swift @@ -71,7 +71,7 @@ extension SearchResultOverviewCoordinator: SearchResultsOverviewTableViewControl let authenticationBox = self.authenticationBox Task { - let searchResult = try await context.apiService.search( + let searchResult = try await APIService.shared.search( query: query, authenticationBox: authenticationBox ).value @@ -124,7 +124,7 @@ extension SearchResultOverviewCoordinator: SearchResultsOverviewTableViewControl ) Task { - let searchResult = try await context.apiService.search( + let searchResult = try await APIService.shared.search( query: query, authenticationBox: authenticationBox ).value diff --git a/Mastodon/Scene/Search/SearchDetail/Search Results Overview/SearchResultsOverviewTableViewController.swift b/Mastodon/Scene/Search/SearchDetail/Search Results Overview/SearchResultsOverviewTableViewController.swift index 1dd0c94a0..0037b4967 100644 --- a/Mastodon/Scene/Search/SearchDetail/Search Results Overview/SearchResultsOverviewTableViewController.swift +++ b/Mastodon/Scene/Search/SearchDetail/Search Results Overview/SearchResultsOverviewTableViewController.swift @@ -156,7 +156,7 @@ class SearchResultsOverviewTableViewController: UIViewController, NeedsDependenc let searchTask = Task { do { - let searchResult = try await context.apiService.search( + let searchResult = try await APIService.shared.search( query: query, authenticationBox: authenticationBox ).value diff --git a/Mastodon/Scene/Search/SearchDetail/SearchResult/SearchResultViewModel+State.swift b/Mastodon/Scene/Search/SearchDetail/SearchResult/SearchResultViewModel+State.swift index 3230af553..4995345db 100644 --- a/Mastodon/Scene/Search/SearchDetail/SearchResult/SearchResultViewModel+State.swift +++ b/Mastodon/Scene/Search/SearchDetail/SearchResult/SearchResultViewModel+State.swift @@ -113,7 +113,7 @@ extension SearchResultViewModel.State { Task { do { - let searchResults = try await viewModel.context.apiService.search( + let searchResults = try await APIService.shared.search( query: query, authenticationBox: viewModel.authenticationBox ).value @@ -129,7 +129,7 @@ extension SearchResultViewModel.State { let relationships: [Mastodon.Entity.Relationship] if accounts.isNotEmpty { - relationships = try await viewModel.context.apiService.relationship( + relationships = try await APIService.shared.relationship( forAccounts: accounts, authenticationBox: viewModel.authenticationBox ).value diff --git a/Mastodon/Scene/Settings/Privacy and Safety/PrivacySafetyViewModel.swift b/Mastodon/Scene/Settings/Privacy and Safety/PrivacySafetyViewModel.swift index c0f3131f8..321a228af 100644 --- a/Mastodon/Scene/Settings/Privacy and Safety/PrivacySafetyViewModel.swift +++ b/Mastodon/Scene/Settings/Privacy and Safety/PrivacySafetyViewModel.swift @@ -145,7 +145,7 @@ extension PrivacySafetyViewModel { let domain = authenticationBox.domain let userAuthorization = authenticationBox.userAuthorization - let account = try await appContext.apiService.accountVerifyCredentials( + let account = try await APIService.shared.accountVerifyCredentials( domain: domain, authorization: userAuthorization ).singleOutput().value @@ -172,7 +172,7 @@ extension PrivacySafetyViewModel { let domain = authenticationBox.domain let userAuthorization = authenticationBox.userAuthorization - let _ = try await appContext.apiService.accountUpdateCredentials( + let _ = try await APIService.shared.accountUpdateCredentials( domain: domain, query: .init( discoverable: suggestMyAccountToOthers, diff --git a/Mastodon/Scene/Settings/SettingsCoordinator.swift b/Mastodon/Scene/Settings/SettingsCoordinator.swift index 37be9608f..e5143ae67 100644 --- a/Mastodon/Scene/Settings/SettingsCoordinator.swift +++ b/Mastodon/Scene/Settings/SettingsCoordinator.swift @@ -48,7 +48,7 @@ class SettingsCoordinator: NSObject, Coordinator { let userAuthentication = s.authenticationBox.authentication let seed = Mastodon.Entity.DonationCampaign.donationSeed(username: userAuthentication.username, domain: userAuthentication.domain) do { - let campaign = try await s.appContext.apiService.getDonationCampaign(seed: seed, source: nil).value + let campaign = try await APIService.shared.getDonationCampaign(seed: seed, source: nil).value await MainActor.run { s.settingsViewController.donationCampaign = campaign @@ -84,8 +84,8 @@ extension SettingsCoordinator: SettingsViewControllerDelegate { navigationController.pushViewController(generalSettingsViewController, animated: true) case .notifications: - let currentSetting = appContext.settingService.currentSetting.value - let notificationsEnabled = appContext.notificationService.isNotificationPermissionGranted.value + let currentSetting = SettingService.shared.currentSetting.value + let notificationsEnabled = NotificationService.shared.isNotificationPermissionGranted.value let notificationViewController = NotificationSettingsViewController(currentSetting: currentSetting, notificationsEnabled: notificationsEnabled) notificationViewController.delegate = self @@ -101,7 +101,7 @@ extension SettingsCoordinator: SettingsViewControllerDelegate { let serverDetailsViewController = ServerDetailsViewController(domain: domain, appContext: appContext, authenticationBox: authenticationBox, sceneCoordinator: sceneCoordinator) serverDetailsViewController.delegate = self - appContext.apiService.instanceV2(domain: domain, authenticationBox: authenticationBox) + APIService.shared.instanceV2(domain: domain, authenticationBox: authenticationBox) .sink { _ in } receiveValue: { content in @@ -109,7 +109,7 @@ extension SettingsCoordinator: SettingsViewControllerDelegate { } .store(in: &disposeBag) - appContext.apiService.extendedDescription(domain: domain, authenticationBox: authenticationBox) + APIService.shared.extendedDescription(domain: domain, authenticationBox: authenticationBox) .sink { _ in } receiveValue: { content in @@ -219,7 +219,7 @@ extension SettingsCoordinator: NotificationSettingsViewControllerDelegate { guard let subscription = setting.activeSubscription, setting.domain == authenticationBox.domain, setting.userID == authenticationBox.userID, - let legacyViewModel = appContext.notificationService.dequeueNotificationViewModel(mastodonAuthenticationBox: authenticationBox), let deviceToken = appContext.notificationService.deviceToken.value else { return } + let legacyViewModel = NotificationService.shared.dequeueNotificationViewModel(mastodonAuthenticationBox: authenticationBox), let deviceToken = NotificationService.shared.deviceToken.value else { return } let queryData = Mastodon.API.Subscriptions.QueryData( policy: viewModel.selectedPolicy.subscriptionPolicy, @@ -237,7 +237,7 @@ extension SettingsCoordinator: NotificationSettingsViewControllerDelegate { mastodonAuthenticationBox: authenticationBox ) - appContext.apiService.createSubscription( + APIService.shared.createSubscription( subscriptionObjectID: subscription.objectID, query: query, mastodonAuthenticationBox: authenticationBox @@ -260,7 +260,7 @@ extension SettingsCoordinator: NotificationSettingsViewControllerDelegate { extension SettingsCoordinator: PolicySelectionViewControllerDelegate { func newPolicySelected(_ viewController: PolicySelectionViewController, newPolicy: NotificationPolicy) { self.setting.activeSubscription?.policyRaw = newPolicy.subscriptionPolicy.rawValue - try? self.appContext.managedObjectContext.save() + try? PersistenceManager.shared.managedObjectContext.save() } } diff --git a/Mastodon/Scene/Share/View/TableviewCell/UserTableViewCell+ViewModel.swift b/Mastodon/Scene/Share/View/TableviewCell/UserTableViewCell+ViewModel.swift index 6487aba8b..fb88fc615 100644 --- a/Mastodon/Scene/Share/View/TableviewCell/UserTableViewCell+ViewModel.swift +++ b/Mastodon/Scene/Share/View/TableviewCell/UserTableViewCell+ViewModel.swift @@ -62,7 +62,7 @@ extension UserTableViewCellDelegate where Self: ViewControllerWithDependencies & // Otherwise the relationship might still be `pending` try await Task.sleep(for: .seconds(1)) - let relationship = try await self.context.apiService.relationship(forAccounts: [account], authenticationBox: authenticationBox).value.first + let relationship = try await APIService.shared.relationship(forAccounts: [account], authenticationBox: authenticationBox).value.first let isMe: Bool if let me { diff --git a/Mastodon/Scene/SuggestionAccount/SuggestionAccountViewModel.swift b/Mastodon/Scene/SuggestionAccount/SuggestionAccountViewModel.swift index f9df84d62..c7bbb2514 100644 --- a/Mastodon/Scene/SuggestionAccount/SuggestionAccountViewModel.swift +++ b/Mastodon/Scene/SuggestionAccount/SuggestionAccountViewModel.swift @@ -50,7 +50,7 @@ final class SuggestionAccountViewModel: NSObject { Task { var suggestedAccounts: [Mastodon.Entity.V2.SuggestionAccount] = [] do { - let response = try await context.apiService.suggestionAccountV2( + let response = try await APIService.shared.suggestionAccountV2( query: .init(limit: 5), authenticationBox: authenticationBox ) @@ -60,7 +60,7 @@ final class SuggestionAccountViewModel: NSObject { let accounts = suggestedAccounts.compactMap { $0.account } - let relationships = try await context.apiService.relationship( + let relationships = try await APIService.shared.relationship( forAccounts: accounts, authenticationBox: authenticationBox ).value diff --git a/Mastodon/Scene/Thread/RemoteThreadViewModel.swift b/Mastodon/Scene/Thread/RemoteThreadViewModel.swift index 207a20408..a9106f78e 100644 --- a/Mastodon/Scene/Thread/RemoteThreadViewModel.swift +++ b/Mastodon/Scene/Thread/RemoteThreadViewModel.swift @@ -24,7 +24,7 @@ final class RemoteThreadViewModel: ThreadViewModel { ) Task { @MainActor in - let response = try await context.apiService.status( + let response = try await APIService.shared.status( statusID: statusID, authenticationBox: authenticationBox ) @@ -47,7 +47,7 @@ final class RemoteThreadViewModel: ThreadViewModel { ) Task { @MainActor in - let response = try await context.apiService.notification( + let response = try await APIService.shared.notification( notificationID: notificationID, authenticationBox: authenticationBox ) diff --git a/Mastodon/Scene/Thread/ThreadViewModel+Diffable.swift b/Mastodon/Scene/Thread/ThreadViewModel+Diffable.swift index ea0713df0..94b6e7230 100644 --- a/Mastodon/Scene/Thread/ThreadViewModel+Diffable.swift +++ b/Mastodon/Scene/Thread/ThreadViewModel+Diffable.swift @@ -29,7 +29,7 @@ extension ThreadViewModel { statusTableViewCellDelegate: statusTableViewCellDelegate, timelineMiddleLoaderTableViewCellDelegate: nil, filterContext: .thread, - activeFilters: context.statusFilterService.$activeFilters + activeFilters: StatusFilterService.shared.$activeFilters ) ) @@ -78,7 +78,7 @@ extension ThreadViewModel { newSnapshot.appendSections([.main]) // top loader - let _hasReplyTo: Bool? = try? await self.context.managedObjectContext.perform { + let _hasReplyTo: Bool? = try? await PersistenceManager.shared.managedObjectContext.perform { guard case let .root(threadContext) = root else { return nil } return threadContext.status.entity.inReplyToID != nil } diff --git a/Mastodon/Scene/Thread/ThreadViewModel+LoadThreadState.swift b/Mastodon/Scene/Thread/ThreadViewModel+LoadThreadState.swift index e32315cbf..905bd9c17 100644 --- a/Mastodon/Scene/Thread/ThreadViewModel+LoadThreadState.swift +++ b/Mastodon/Scene/Thread/ThreadViewModel+LoadThreadState.swift @@ -10,6 +10,7 @@ import Combine import GameplayKit import CoreDataStack import MastodonSDK +import MastodonCore extension ThreadViewModel { class LoadThreadState: GKState { @@ -60,7 +61,7 @@ extension ThreadViewModel.LoadThreadState { Task { @MainActor in do { - let response = try await viewModel.context.apiService.statusContext( + let response = try await APIService.shared.statusContext( statusID: threadContext.statusID, authenticationBox: viewModel.authenticationBox ) @@ -70,7 +71,7 @@ extension ThreadViewModel.LoadThreadState { // assert(!Thread.isMainThread) // await Task.sleep(1_000_000_000) // 1s delay to prevent UI render issue - _ = try await viewModel.context.apiService.getHistory(forStatusID: threadContext.statusID, + _ = try await APIService.shared.getHistory(forStatusID: threadContext.statusID, authenticationBox: viewModel.authenticationBox) viewModel.mastodonStatusThreadViewModel.appendAncestor( diff --git a/Mastodon/Scene/Thread/ThreadViewModel.swift b/Mastodon/Scene/Thread/ThreadViewModel.swift index 2e966e91b..ef7fff1da 100644 --- a/Mastodon/Scene/Thread/ThreadViewModel.swift +++ b/Mastodon/Scene/Thread/ThreadViewModel.swift @@ -81,7 +81,7 @@ class ThreadViewModel { } .store(in: &disposeBag) - context.publisherService + PublisherService.shared .statusPublishResult .sink { [weak self] value in guard let self else { return } diff --git a/Mastodon/Supporting Files/AppDelegate.swift b/Mastodon/Supporting Files/AppDelegate.swift index 0a76d6b2e..b712c18df 100644 --- a/Mastodon/Supporting Files/AppDelegate.swift +++ b/Mastodon/Supporting Files/AppDelegate.swift @@ -65,7 +65,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { extension AppDelegate { func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) { - appContext.notificationService.deviceToken.value = deviceToken + NotificationService.shared.deviceToken.value = deviceToken } } @@ -85,16 +85,16 @@ extension AppDelegate: UNUserNotificationCenterDelegate { let accessToken = pushNotification.accessToken UserDefaults.shared.increaseNotificationCount(accessToken: accessToken) - appContext.notificationService.applicationIconBadgeNeedsUpdate.send() + NotificationService.shared.applicationIconBadgeNeedsUpdate.send() - appContext.notificationService.handle(pushNotification: pushNotification) + NotificationService.shared.handle(pushNotification: pushNotification) completionHandler([.sound]) } // notification present in the background (or resume from background) func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any]) async -> UIBackgroundFetchResult { - let shortcutItems = try? await appContext.notificationService.unreadApplicationShortcutItems() + let shortcutItems = try? await NotificationService.shared.unreadApplicationShortcutItems() UIApplication.shared.shortcutItems = shortcutItems return .noData } @@ -111,8 +111,8 @@ extension AppDelegate: UNUserNotificationCenterDelegate { return } - appContext.notificationService.handle(pushNotification: pushNotification) - appContext.notificationService.requestRevealNotificationPublisher.send(pushNotification) + NotificationService.shared.handle(pushNotification: pushNotification) + NotificationService.shared.requestRevealNotificationPublisher.send(pushNotification) completionHandler() } @@ -126,10 +126,3 @@ extension AppDelegate: UNUserNotificationCenterDelegate { } } - -extension AppContext { - static var shared: AppContext { - let appDelegate = UIApplication.shared.delegate as! AppDelegate - return appDelegate.appContext - } -} diff --git a/Mastodon/Supporting Files/SceneDelegate.swift b/Mastodon/Supporting Files/SceneDelegate.swift index c56064ba8..b84bcecc7 100644 --- a/Mastodon/Supporting Files/SceneDelegate.swift +++ b/Mastodon/Supporting Files/SceneDelegate.swift @@ -89,10 +89,10 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive. // update application badge - AppContext.shared.notificationService.applicationIconBadgeNeedsUpdate.send() + NotificationService.shared.applicationIconBadgeNeedsUpdate.send() // trigger status filter update - AppContext.shared.statusFilterService.filterUpdatePublisher.send() + StatusFilterService.shared.filterUpdatePublisher.send() // trigger authenticated user account update AuthenticationServiceProvider.shared.updateActiveUserAccountPublisher.send() @@ -141,12 +141,12 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { Task { guard let me = authenticationBox.authentication.account() else { return } - guard let account = try await AppContext.shared.apiService.search( + guard let account = try await APIService.shared.search( query: .init(q: incomingURL.absoluteString, type: .accounts, resolve: true), authenticationBox: authenticationBox ).value.accounts.first else { return } - guard let relationship = try await AppContext.shared.apiService.relationship( + guard let relationship = try await APIService.shared.relationship( forAccounts: [account], authenticationBox: authenticationBox ).value.first else { return } @@ -167,7 +167,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { case (profile, statusID): Task { - guard let statusOnMyInstance = try await AppContext.shared.apiService.search(query: .init(q: incomingURL.absoluteString, resolve: true), authenticationBox: authenticationBox).value.statuses.first else { return } + guard let statusOnMyInstance = try await APIService.shared.search(query: .init(q: incomingURL.absoluteString, resolve: true), authenticationBox: authenticationBox).value.statuses.first else { return } let threadViewModel = RemoteThreadViewModel( context: AppContext.shared, @@ -285,12 +285,12 @@ extension SceneDelegate { do { guard let me = authenticationBox.authentication.account() else { return } - guard let account = try await AppContext.shared.apiService.search( + guard let account = try await APIService.shared.search( query: .init(q: components[1], type: .accounts, resolve: true), authenticationBox: authenticationBox ).value.accounts.first else { return } - guard let relationship = try await AppContext.shared.apiService.relationship( + guard let relationship = try await APIService.shared.relationship( forAccounts: [account], authenticationBox: authenticationBox ).value.first else { return } diff --git a/MastodonIntent/Handler/FollowersCountIntentHandler.swift b/MastodonIntent/Handler/FollowersCountIntentHandler.swift index 287c2fab1..50291b38d 100644 --- a/MastodonIntent/Handler/FollowersCountIntentHandler.swift +++ b/MastodonIntent/Handler/FollowersCountIntentHandler.swift @@ -23,8 +23,7 @@ class FollowersCountIntentHandler: INExtension, FollowersCountIntentHandling { return INObjectCollection(items: []) } - let results = try await AppContext.shared - .apiService + let results = try await APIService.shared .search(query: .init(q: searchTerm), authenticationBox: authenticationBox) return INObjectCollection(items: results.value.accounts.map { $0.acctWithDomainIfMissing(authenticationBox.domain) as NSString }) diff --git a/MastodonIntent/Handler/HashtagIntentHandler.swift b/MastodonIntent/Handler/HashtagIntentHandler.swift index 60b721a5b..4c955b39b 100644 --- a/MastodonIntent/Handler/HashtagIntentHandler.swift +++ b/MastodonIntent/Handler/HashtagIntentHandler.swift @@ -16,7 +16,7 @@ class HashtagIntentHandler: INExtension, HashtagIntentHandling { var results: [NSString] = [] if let searchTerm, searchTerm.isEmpty == false { - let searchResults = try await AppContext.shared.apiService + let searchResults = try await APIService.shared .search(query: .init(q: searchTerm, type: .hashtags), authenticationBox: authenticationBox) .value .hashtags @@ -25,7 +25,7 @@ class HashtagIntentHandler: INExtension, HashtagIntentHandling { results = searchResults } else { - let followedTags = try await AppContext.shared.apiService.getFollowedTags( + let followedTags = try await APIService.shared.getFollowedTags( domain: authenticationBox.domain, query: Mastodon.API.Account.FollowedTagsQuery(limit: nil), authenticationBox: authenticationBox) diff --git a/MastodonIntent/Handler/MultiFollowersCountIntentHandler.swift b/MastodonIntent/Handler/MultiFollowersCountIntentHandler.swift index b341a8e2a..06319186c 100644 --- a/MastodonIntent/Handler/MultiFollowersCountIntentHandler.swift +++ b/MastodonIntent/Handler/MultiFollowersCountIntentHandler.swift @@ -15,8 +15,7 @@ class MultiFollowersCountIntentHandler: INExtension, MultiFollowersCountIntentHa return INObjectCollection(items: []) } - let results = try await AppContext.shared - .apiService + let results = try await APIService.shared .search(query: .init(q: searchTerm), authenticationBox: authenticationBox) return INObjectCollection(items: results.value.accounts.map { $0.acctWithDomainIfMissing(authenticationBox.domain) as NSString }) diff --git a/MastodonIntent/Handler/SendPostIntentHandler.swift b/MastodonIntent/Handler/SendPostIntentHandler.swift index acde944c1..0e11561c0 100644 --- a/MastodonIntent/Handler/SendPostIntentHandler.swift +++ b/MastodonIntent/Handler/SendPostIntentHandler.swift @@ -20,10 +20,7 @@ final class SendPostIntentHandler: NSObject { let coreDataStack = CoreDataStack() lazy var managedObjectContext = coreDataStack.persistentContainer.viewContext lazy var api: APIService = { - let backgroundManagedObjectContext = coreDataStack.newTaskContext() - return APIService( - backgroundManagedObjectContext: backgroundManagedObjectContext - ) + return APIService.isolatedService() }() } diff --git a/MastodonSDK/Sources/MastodonCore/AppContext.swift b/MastodonSDK/Sources/MastodonCore/AppContext.swift index c22bd4761..c5770980c 100644 --- a/MastodonSDK/Sources/MastodonCore/AppContext.swift +++ b/MastodonSDK/Sources/MastodonCore/AppContext.swift @@ -12,26 +12,39 @@ import CoreData import CoreDataStack import AlamofireImage -public class AppContext: ObservableObject { - public static let shared = AppContext() - - public var disposeBag = Set() - +public class PersistenceManager { + public static let shared = { PersistenceManager() }() public let coreDataStack: CoreDataStack public let managedObjectContext: NSManagedObjectContext public let backgroundManagedObjectContext: NSManagedObjectContext - public let apiService: APIService - public let emojiService: EmojiService + private var disposeBag = Set() + + private init() { + let _coreDataStack = CoreDataStack() + let _managedObjectContext = _coreDataStack.persistentContainer.viewContext + let _backgroundManagedObjectContext = _coreDataStack.persistentContainer.newBackgroundContext() + + coreDataStack = _coreDataStack + managedObjectContext = _managedObjectContext + backgroundManagedObjectContext = _backgroundManagedObjectContext + + backgroundManagedObjectContext.mergePolicy = NSMergePolicy.mergeByPropertyObjectTrump + NotificationCenter.default.publisher(for: .NSManagedObjectContextDidSave, object: backgroundManagedObjectContext) + .sink { [weak self] notification in + guard let self = self else { return } + self.managedObjectContext.perform { + self.managedObjectContext.mergeChanges(fromContextDidSave: notification) + } + } + .store(in: &disposeBag) + } +} - public let publisherService: PublisherService - public let notificationService: NotificationService - public let settingService: SettingService - public let instanceService: InstanceService - - public let blockDomainService: BlockDomainService - public let statusFilterService: StatusFilterService - public let photoLibraryService = PhotoLibraryService() +public class AppContext: ObservableObject { + public static let shared = { AppContext() }() + + public var disposeBag = Set() public let placeholderImageCacheService = PlaceholderImageCacheService() public let blurhashImageCacheService = BlurhashImageCacheService.shared @@ -48,57 +61,6 @@ public class AppContext: ObservableObject { private init() { let authProvider = AuthenticationServiceProvider.shared - let _coreDataStack = CoreDataStack() - if authProvider.authenticationMigrationRequired { - authProvider.migrateLegacyAuthentications( - in: _coreDataStack.persistentContainer.viewContext - ) - } - - let _managedObjectContext = _coreDataStack.persistentContainer.viewContext - let _backgroundManagedObjectContext = _coreDataStack.persistentContainer.newBackgroundContext() - - coreDataStack = _coreDataStack - managedObjectContext = _managedObjectContext - backgroundManagedObjectContext = _backgroundManagedObjectContext - - let _apiService = APIService(backgroundManagedObjectContext: _backgroundManagedObjectContext) - apiService = _apiService - -// let _authenticationService = AuthenticationService( -// managedObjectContext: _managedObjectContext, -// backgroundManagedObjectContext: _backgroundManagedObjectContext, -// apiService: _apiService -// ) -// authenticationService = _authenticationService - - emojiService = EmojiService( - apiService: apiService - ) - - publisherService = .init(apiService: _apiService) - - let _notificationService = NotificationService( - apiService: _apiService - ) - notificationService = _notificationService - - settingService = SettingService( - apiService: _apiService, - notificationService: _notificationService - ) - - instanceService = InstanceService( - apiService: _apiService - ) - - blockDomainService = BlockDomainService( - backgroundManagedObjectContext: _backgroundManagedObjectContext - ) - - statusFilterService = StatusFilterService( - apiService: _apiService - ) documentStore = DocumentStore() documentStoreSubscription = documentStore.objectWillChange @@ -106,16 +68,6 @@ public class AppContext: ObservableObject { .sink { [unowned self] in self.objectWillChange.send() } - - backgroundManagedObjectContext.mergePolicy = NSMergePolicy.mergeByPropertyObjectTrump - NotificationCenter.default.publisher(for: .NSManagedObjectContextDidSave, object: backgroundManagedObjectContext) - .sink { [weak self] notification in - guard let self = self else { return } - self.managedObjectContext.perform { - self.managedObjectContext.mergeChanges(fromContextDidSave: notification) - } - } - .store(in: &disposeBag) } } diff --git a/MastodonSDK/Sources/MastodonCore/AuthenticationServiceProvider.swift b/MastodonSDK/Sources/MastodonCore/AuthenticationServiceProvider.swift index 7f5823fec..aaedf5532 100644 --- a/MastodonSDK/Sources/MastodonCore/AuthenticationServiceProvider.swift +++ b/MastodonSDK/Sources/MastodonCore/AuthenticationServiceProvider.swift @@ -47,6 +47,11 @@ public class AuthenticationServiceProvider: ObservableObject { .assign(to: &$mastodonAuthenticationBoxes) Task { + if authenticationMigrationRequired { + migrateLegacyAuthentications( + in: PersistenceManager.shared.managedObjectContext + ) + } await prepareForUse() authentications = authenticationSortedByActivation() } @@ -146,7 +151,7 @@ public extension AuthenticationServiceProvider { func signOutMastodonUser(authentication: MastodonAuthentication) async throws { try await AuthenticationServiceProvider.shared.delete(authentication: authentication) - _ = try await AppContext.shared.apiService.cancelSubscription(domain: authentication.domain, authorization: authentication.authorization) + _ = try await APIService.shared.cancelSubscription(domain: authentication.domain, authorization: authentication.authorization) } @MainActor @@ -217,13 +222,13 @@ public extension AuthenticationServiceProvider { userDefaults.didMigrateAuthentications == false } - func fetchAccounts(apiService: APIService) async { + func fetchAccounts() async { // FIXME: This is a dirty hack to make the performance-stuff work. // Problem is, that we don't persist the user on disk anymore. So we have to fetch // it when we need it to display on the home timeline. // We need this (also) for the Account-list, but it might be the wrong place. App Startup might be more appropriate for authentication in authentications { - guard let account = try? await apiService.accountInfo(domain: authentication.domain, + guard let account = try? await APIService.shared.accountInfo(domain: authentication.domain, userID: authentication.userID, authorization: Mastodon.API.OAuth.Authorization(accessToken: authentication.userAccessToken)).value else { continue } @@ -250,7 +255,7 @@ private extension AuthenticationServiceProvider { _ previousFollowingIDs: [String]? = nil, _ maxID: String? = nil ) async throws { - let apiService = AppContext.shared.apiService + let apiService = APIService.shared let followingResponse = try await fetchFollowing(maxID, apiService, authBox) let followingIds = (previousFollowingIDs ?? []) + followingResponse.ids diff --git a/MastodonSDK/Sources/MastodonCore/DataController/FeedDataController.swift b/MastodonSDK/Sources/MastodonCore/DataController/FeedDataController.swift index 3ee2a9387..9c7d4b588 100644 --- a/MastodonSDK/Sources/MastodonCore/DataController/FeedDataController.swift +++ b/MastodonSDK/Sources/MastodonCore/DataController/FeedDataController.swift @@ -169,29 +169,29 @@ private extension FeedDataController { func load(kind: MastodonFeed.Kind, maxID: MastodonStatus.ID?) async throws -> [MastodonFeed] { switch kind { case .home(let timeline): - await AuthenticationServiceProvider.shared.fetchAccounts(apiService: context.apiService) + await AuthenticationServiceProvider.shared.fetchAccounts() let response: Mastodon.Response.Content<[Mastodon.Entity.Status]> switch timeline { case .home: - response = try await context.apiService.homeTimeline( + response = try await APIService.shared.homeTimeline( maxID: maxID, authenticationBox: authenticationBox ) case .public: - response = try await context.apiService.publicTimeline( + response = try await APIService.shared.publicTimeline( query: .init(local: true, maxID: maxID), authenticationBox: authenticationBox ) case let .list(id): - response = try await context.apiService.listTimeline( + response = try await APIService.shared.listTimeline( id: id, query: .init(maxID: maxID), authenticationBox: authenticationBox ) case let .hashtag(tag): - response = try await context.apiService.hashtagTimeline( + response = try await APIService.shared.hashtagTimeline( hashtag: tag, authenticationBox: authenticationBox ) @@ -209,10 +209,10 @@ private extension FeedDataController { private func getFeeds(with scope: APIService.MastodonNotificationScope?, accountID: String? = nil) async throws -> [MastodonFeed] { - let notifications = try await context.apiService.notifications(maxID: nil, accountID: accountID, scope: scope, authenticationBox: authenticationBox).value + let notifications = try await APIService.shared.notifications(maxID: nil, accountID: accountID, scope: scope, authenticationBox: authenticationBox).value let accounts = notifications.map { $0.account } - let relationships = try await context.apiService.relationship(forAccounts: accounts, authenticationBox: authenticationBox).value + let relationships = try await APIService.shared.relationship(forAccounts: accounts, authenticationBox: authenticationBox).value let notificationsWithRelationship: [(notification: Mastodon.Entity.Notification, relationship: Mastodon.Entity.Relationship?)] = notifications.compactMap { notification in guard let relationship = relationships.first(where: {$0.id == notification.account.id }) else { return (notification: notification, relationship: nil)} diff --git a/MastodonSDK/Sources/MastodonCore/FetchedResultsController/SettingFetchedResultController.swift b/MastodonSDK/Sources/MastodonCore/FetchedResultsController/SettingFetchedResultController.swift index a51d9501b..de6a3f468 100644 --- a/MastodonSDK/Sources/MastodonCore/FetchedResultsController/SettingFetchedResultController.swift +++ b/MastodonSDK/Sources/MastodonCore/FetchedResultsController/SettingFetchedResultController.swift @@ -22,7 +22,7 @@ public final class SettingFetchedResultController: NSObject { // output public let settings = CurrentValueSubject<[Setting], Never>([]) - public init(managedObjectContext: NSManagedObjectContext, additionalPredicate: NSPredicate?) { + public init(additionalPredicate: NSPredicate?) { self.fetchedResultsController = { let fetchRequest = Setting.sortedFetchRequest fetchRequest.returnsObjectsAsFaults = false @@ -32,7 +32,7 @@ public final class SettingFetchedResultController: NSObject { fetchRequest.fetchBatchSize = 20 let controller = NSFetchedResultsController( fetchRequest: fetchRequest, - managedObjectContext: managedObjectContext, + managedObjectContext: PersistenceManager.shared.managedObjectContext, sectionNameKeyPath: nil, cacheName: nil ) diff --git a/MastodonSDK/Sources/MastodonCore/Service/API/APIService.swift b/MastodonSDK/Sources/MastodonCore/Service/API/APIService.swift index 70ea4ce5f..c65ed31f1 100644 --- a/MastodonSDK/Sources/MastodonCore/Service/API/APIService.swift +++ b/MastodonSDK/Sources/MastodonCore/Service/API/APIService.swift @@ -15,6 +15,9 @@ import AlamofireImage public final class APIService { + @MainActor + public static let shared = { APIService(backgroundContext: PersistenceManager.shared.backgroundManagedObjectContext) }() + public static let callbackURLScheme = "mastodon" public static let oauthCallbackURL = "mastodon://joinmastodon.org/oauth" @@ -23,15 +26,13 @@ public final class APIService { // internal let session: URLSession - // input public let backgroundManagedObjectContext: NSManagedObjectContext // output public let error = PassthroughSubject() - public init(backgroundManagedObjectContext: NSManagedObjectContext) { - self.backgroundManagedObjectContext = backgroundManagedObjectContext - + private init(backgroundContext: NSManagedObjectContext) { + backgroundManagedObjectContext = backgroundContext let appVersion = Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as? String ?? "Unknown" let configuration = URLSessionConfiguration.default configuration.httpAdditionalHeaders = ["User-Agent" : "mastodon-ios/" + appVersion] @@ -48,6 +49,10 @@ public final class APIService { UIImageView.af.sharedImageDownloader = ImageDownloader(downloadPrioritization: .lifo) } + public static func isolatedService() -> APIService { + let taskContext = PersistenceManager.shared.coreDataStack.newTaskContext() + return APIService(backgroundContext: taskContext) + } } extension APIService { diff --git a/MastodonSDK/Sources/MastodonCore/Service/Emoji/EmojiService+CustomEmojiViewModel+LoadState.swift b/MastodonSDK/Sources/MastodonCore/Service/Emoji/EmojiService+CustomEmojiViewModel+LoadState.swift index d222a1a8f..209a3923e 100644 --- a/MastodonSDK/Sources/MastodonCore/Service/Emoji/EmojiService+CustomEmojiViewModel+LoadState.swift +++ b/MastodonSDK/Sources/MastodonCore/Service/Emoji/EmojiService+CustomEmojiViewModel+LoadState.swift @@ -37,7 +37,7 @@ extension EmojiService.CustomEmojiViewModel.LoadState { let authenticationBox = AuthenticationServiceProvider.shared.activeAuthentication, let stateMachine else { return } - let apiService = viewModel.service.apiService + let apiService = APIService.shared apiService.customEmoji(domain: viewModel.domain, authenticationBox: authenticationBox) // .receive(on: DispatchQueue.main) diff --git a/MastodonSDK/Sources/MastodonCore/Service/Emoji/EmojiService.swift b/MastodonSDK/Sources/MastodonCore/Service/Emoji/EmojiService.swift index 2a25b6100..a2d0ac821 100644 --- a/MastodonSDK/Sources/MastodonCore/Service/Emoji/EmojiService.swift +++ b/MastodonSDK/Sources/MastodonCore/Service/Emoji/EmojiService.swift @@ -10,15 +10,9 @@ import Combine import MastodonSDK public final class EmojiService { - let apiService: APIService - + public static let shared = { EmojiService() }() let workingQueue = DispatchQueue(label: "org.joinmastodon.app.EmojiService.working-queue") private(set) var customEmojiViewModelDict: [String: CustomEmojiViewModel] = [:] - - init(apiService: APIService) { - self.apiService = apiService - } - } extension EmojiService { diff --git a/MastodonSDK/Sources/MastodonCore/Service/MastodonAttachment/MastodonAttachmentService+UploadState.swift b/MastodonSDK/Sources/MastodonCore/Service/MastodonAttachment/MastodonAttachmentService+UploadState.swift index cb12a3b36..a71c15300 100644 --- a/MastodonSDK/Sources/MastodonCore/Service/MastodonAttachment/MastodonAttachmentService+UploadState.swift +++ b/MastodonSDK/Sources/MastodonCore/Service/MastodonAttachment/MastodonAttachmentService+UploadState.swift @@ -67,7 +67,7 @@ extension MastodonAttachmentService.UploadState { ) // and needs clone the `query` if needs retry - service.context.apiService.uploadMedia( + APIService.shared.uploadMedia( domain: authenticationBox.domain, query: query, mastodonAuthenticationBox: authenticationBox, @@ -126,7 +126,7 @@ extension MastodonAttachmentService.UploadState { return } - service.context.apiService.getMedia( + APIService.shared.getMedia( attachmentID: attachment.id, mastodonAuthenticationBox: authenticationBox ) diff --git a/MastodonSDK/Sources/MastodonCore/Service/Notification/NotificationService.swift b/MastodonSDK/Sources/MastodonCore/Service/Notification/NotificationService.swift index 7558169c8..f3fb1842c 100644 --- a/MastodonSDK/Sources/MastodonCore/Service/Notification/NotificationService.swift +++ b/MastodonSDK/Sources/MastodonCore/Service/Notification/NotificationService.swift @@ -15,6 +15,8 @@ import MastodonLocalization public final class NotificationService { + public static let shared = { NotificationService() }() + public static let unreadShortcutItemIdentifier = "org.joinmastodon.app.NotificationService.unread-shortcut" var disposeBag = Set() @@ -22,7 +24,6 @@ public final class NotificationService { let workingQueue = DispatchQueue(label: "org.joinmastodon.app.NotificationService.working-queue") // input - weak var apiService: APIService? public let isNotificationPermissionGranted = CurrentValueSubject(false) public let deviceToken = CurrentValueSubject(nil) public let applicationIconBadgeNeedsUpdate = CurrentValueSubject(Void()) @@ -33,11 +34,7 @@ public final class NotificationService { public let unreadNotificationCountDidUpdate = CurrentValueSubject(Void()) public let requestRevealNotificationPublisher = PassthroughSubject() - init( - apiService: APIService - ) { - self.apiService = apiService - + private init() { AuthenticationServiceProvider.shared.$authentications .sink(receiveValue: { [weak self] mastodonAuthentications in guard let self = self else { return } @@ -171,10 +168,9 @@ extension NotificationService { private func fetchLatestNotifications( pushNotification: MastodonPushNotification ) async throws { - guard let apiService = apiService else { return } guard let authenticationBox = try await authenticationBox(for: pushNotification) else { return } - _ = try await apiService.notifications( + _ = try await APIService.shared.notifications( maxID: nil, scope: .everything, authenticationBox: authenticationBox @@ -186,8 +182,7 @@ extension NotificationService { ) async throws { // Subscription maybe failed to cancel when sign-out // Try cancel again if receive that kind push notification - let managedObjectContext = AppContext.shared.managedObjectContext - guard let apiService = apiService else { return } + let managedObjectContext = PersistenceManager.shared.managedObjectContext let userAccessToken = pushNotification.accessToken @@ -204,7 +199,7 @@ extension NotificationService { guard let domain = try await domain(for: pushNotification) else { return } do { - _ = try await apiService.cancelSubscription( + _ = try await APIService.shared.cancelSubscription( domain: domain, authorization: .init(accessToken: userAccessToken) ) @@ -213,7 +208,7 @@ extension NotificationService { } private func domain(for pushNotification: MastodonPushNotification) async throws -> String? { - let managedObjectContext = AppContext.shared.managedObjectContext + let managedObjectContext = PersistenceManager.shared.managedObjectContext return try await managedObjectContext.perform { let subscriptionRequest = NotificationSubscription.sortedFetchRequest subscriptionRequest.predicate = NotificationSubscription.predicate(userToken: pushNotification.accessToken) diff --git a/MastodonSDK/Sources/MastodonCore/Service/PhotoLibraryService.swift b/MastodonSDK/Sources/MastodonCore/Service/PhotoLibraryService.swift index 0432547e3..813e14daf 100644 --- a/MastodonSDK/Sources/MastodonCore/Service/PhotoLibraryService.swift +++ b/MastodonSDK/Sources/MastodonCore/Service/PhotoLibraryService.swift @@ -12,6 +12,7 @@ import Alamofire import AlamofireImage public final class PhotoLibraryService: NSObject { + public static let shared = { PhotoLibraryService() }() } diff --git a/MastodonSDK/Sources/MastodonCore/Service/PublisherService/PublisherService.swift b/MastodonSDK/Sources/MastodonCore/Service/PublisherService/PublisherService.swift index 1997b2041..3f09db60b 100644 --- a/MastodonSDK/Sources/MastodonCore/Service/PublisherService/PublisherService.swift +++ b/MastodonSDK/Sources/MastodonCore/Service/PublisherService/PublisherService.swift @@ -10,11 +10,10 @@ import Combine public final class PublisherService { + public static let shared = { PublisherService() }() + var disposeBag = Set() - // input - let apiService: APIService - @Published public private(set) var statusPublishers: [StatusPublisher] = [] // output @@ -23,11 +22,7 @@ public final class PublisherService { var currentPublishProgressObservation: NSKeyValueObservation? @Published public var currentPublishProgress: Double = 0 - public init( - apiService: APIService - ) { - self.apiService = apiService - + private init() { $statusPublishers .receive(on: DispatchQueue.main) .sink { [weak self] publishers in @@ -84,7 +79,7 @@ extension PublisherService { Task { do { - let result = try await publisher.publish(api: apiService, authenticationBox: authenticationBox) + let result = try await publisher.publish(api: APIService.shared, authenticationBox: authenticationBox) self.statusPublishResult.send(.success(result)) self.statusPublishers.removeAll(where: { $0 === publisher }) diff --git a/MastodonSDK/Sources/MastodonCore/Service/SettingService.swift b/MastodonSDK/Sources/MastodonCore/Service/SettingService.swift index dbcbbb291..505376b44 100644 --- a/MastodonSDK/Sources/MastodonCore/Service/SettingService.swift +++ b/MastodonSDK/Sources/MastodonCore/Service/SettingService.swift @@ -15,24 +15,20 @@ import MastodonCommon public final class SettingService { + public static let shared = { SettingService() }() + var disposeBag = Set() // input - weak var apiService: APIService? - weak var notificationService: NotificationService? - + var apiService: APIService { APIService.shared } + var notificationService: NotificationService { NotificationService.shared + } // output let settingFetchedResultController: SettingFetchedResultController public let currentSetting = CurrentValueSubject(nil) - init( - apiService: APIService, - notificationService: NotificationService - ) { - self.apiService = apiService - self.notificationService = notificationService + private init() { self.settingFetchedResultController = SettingFetchedResultController( - managedObjectContext: AppContext.shared.managedObjectContext, additionalPredicate: nil ) @@ -41,7 +37,7 @@ public final class SettingService { .compactMap { [weak self] mastodonAuthenticationBoxes -> AnyPublisher<[MastodonAuthenticationBox], Never>? in guard let self = self else { return nil } - let managedObjectContext = AppContext.shared.backgroundManagedObjectContext + let managedObjectContext = PersistenceManager.shared.backgroundManagedObjectContext return managedObjectContext.performChanges { for authenticationBox in mastodonAuthenticationBoxes { let domain = authenticationBox.domain @@ -95,7 +91,7 @@ public final class SettingService { guard setting.domain == authenticationBox.domain, setting.userID == authenticationBox.userID else { return nil } - let _viewModel = self.notificationService?.dequeueNotificationViewModel( + let _viewModel = notificationService.dequeueNotificationViewModel( mastodonAuthenticationBox: authenticationBox ) guard let viewModel = _viewModel else { return nil } diff --git a/MastodonSDK/Sources/MastodonCore/Service/StatusFilterService.swift b/MastodonSDK/Sources/MastodonCore/Service/StatusFilterService.swift index e0ebd62ca..db5e7c427 100644 --- a/MastodonSDK/Sources/MastodonCore/Service/StatusFilterService.swift +++ b/MastodonSDK/Sources/MastodonCore/Service/StatusFilterService.swift @@ -13,21 +13,17 @@ import MastodonSDK import MastodonMeta public final class StatusFilterService { + public static let shared = { StatusFilterService() }() var disposeBag = Set() // input - weak var apiService: APIService? public let filterUpdatePublisher = PassthroughSubject() // output @Published public var activeFilters: [Mastodon.Entity.Filter] = [] - init( - apiService: APIService - ) { - self.apiService = apiService - + private init() { // fetch account filters every 300s // also trigger fetch when app resume from background let filterUpdateTimerPublisher = Timer.publish(every: 300.0, on: .main, in: .common) @@ -48,7 +44,7 @@ public final class StatusFilterService { guard let box = mastodonAuthenticationBoxes.first else { return Just(Result { throw APIService.APIError.implicit(.authenticationMissing) }).eraseToAnyPublisher() } - return apiService.filters(mastodonAuthenticationBox: box) + return APIService.shared.filters(mastodonAuthenticationBox: box) .map { response in let now = Date() let newResponse = response.map { filters in diff --git a/MastodonSDK/Sources/MastodonUI/Scene/ComposeContent/Attachment/AttachmentViewModel+Upload.swift b/MastodonSDK/Sources/MastodonUI/Scene/ComposeContent/Attachment/AttachmentViewModel+Upload.swift index ea560b5d4..6eb41e984 100644 --- a/MastodonSDK/Sources/MastodonUI/Scene/ComposeContent/Attachment/AttachmentViewModel+Upload.swift +++ b/MastodonSDK/Sources/MastodonUI/Scene/ComposeContent/Attachment/AttachmentViewModel+Upload.swift @@ -77,7 +77,7 @@ extension AttachmentViewModel { do { let result = try await upload( context: .init( - apiService: self.api, + apiService: APIService.shared, authenticationBox: self.authenticationBox ), isRetry: isRetry diff --git a/MastodonSDK/Sources/MastodonUI/Scene/ComposeContent/Attachment/AttachmentViewModel.swift b/MastodonSDK/Sources/MastodonUI/Scene/ComposeContent/Attachment/AttachmentViewModel.swift index 135c1423c..5db5731a2 100644 --- a/MastodonSDK/Sources/MastodonUI/Scene/ComposeContent/Attachment/AttachmentViewModel.swift +++ b/MastodonSDK/Sources/MastodonUI/Scene/ComposeContent/Attachment/AttachmentViewModel.swift @@ -41,7 +41,6 @@ final public class AttachmentViewModel: NSObject, ObservableObject, Identifiable }() // input - public let api: APIService public let authenticationBox: MastodonAuthenticationBox public let input: Input public let sizeLimit: SizeLimit @@ -72,7 +71,6 @@ final public class AttachmentViewModel: NSObject, ObservableObject, Identifiable @Published var remainTimeLocalizedString: String? public init( - api: APIService, authenticationBox: MastodonAuthenticationBox, input: Input, sizeLimit: SizeLimit, @@ -80,7 +78,6 @@ final public class AttachmentViewModel: NSObject, ObservableObject, Identifiable isEditing: Bool = false, caption: String? = nil ) { - self.api = api self.authenticationBox = authenticationBox self.input = input self.sizeLimit = sizeLimit diff --git a/MastodonSDK/Sources/MastodonUI/Scene/ComposeContent/AutoComplete/AutoCompleteViewModel+State.swift b/MastodonSDK/Sources/MastodonUI/Scene/ComposeContent/AutoComplete/AutoCompleteViewModel+State.swift index 8b95956f6..11bd1f99d 100644 --- a/MastodonSDK/Sources/MastodonUI/Scene/ComposeContent/AutoComplete/AutoCompleteViewModel+State.swift +++ b/MastodonSDK/Sources/MastodonUI/Scene/ComposeContent/AutoComplete/AutoCompleteViewModel+State.swift @@ -130,7 +130,7 @@ extension AutoCompleteViewModel.State { ) do { - let response = try await viewModel.context.apiService.search( + let response = try await APIService.shared.search( query: query, authenticationBox: viewModel.authenticationBox ) diff --git a/MastodonSDK/Sources/MastodonUI/Scene/ComposeContent/AutoComplete/AutoCompleteViewModel.swift b/MastodonSDK/Sources/MastodonUI/Scene/ComposeContent/AutoComplete/AutoCompleteViewModel.swift index 1046654bf..ffb00b975 100644 --- a/MastodonSDK/Sources/MastodonUI/Scene/ComposeContent/AutoComplete/AutoCompleteViewModel.swift +++ b/MastodonSDK/Sources/MastodonUI/Scene/ComposeContent/AutoComplete/AutoCompleteViewModel.swift @@ -40,7 +40,7 @@ final class AutoCompleteViewModel { init(context: AppContext, authenticationBox: MastodonAuthenticationBox) { self.context = context self.authenticationBox = authenticationBox - self.customEmojiViewModel = context.emojiService.dequeueCustomEmojiViewModel(for: authenticationBox.domain) + self.customEmojiViewModel = EmojiService.shared.dequeueCustomEmojiViewModel(for: authenticationBox.domain) // end init autoCompleteItems diff --git a/MastodonSDK/Sources/MastodonUI/Scene/ComposeContent/ComposeContentViewController.swift b/MastodonSDK/Sources/MastodonUI/Scene/ComposeContent/ComposeContentViewController.swift index 20686bcaa..e36baeff5 100644 --- a/MastodonSDK/Sources/MastodonUI/Scene/ComposeContent/ComposeContentViewController.swift +++ b/MastodonSDK/Sources/MastodonUI/Scene/ComposeContent/ComposeContentViewController.swift @@ -469,7 +469,6 @@ extension ComposeContentViewController: PHPickerViewControllerDelegate { let attachmentViewModels: [AttachmentViewModel] = results.map { result in AttachmentViewModel( - api: viewModel.context.apiService, authenticationBox: viewModel.authenticationBox, input: .pickerResult(result), sizeLimit: viewModel.sizeLimit, @@ -488,7 +487,6 @@ extension ComposeContentViewController: UIImagePickerControllerDelegate & UINavi guard let image = info[.originalImage] as? UIImage else { return } let attachmentViewModel = AttachmentViewModel( - api: viewModel.context.apiService, authenticationBox: viewModel.authenticationBox, input: .image(image), sizeLimit: viewModel.sizeLimit, @@ -508,7 +506,6 @@ extension ComposeContentViewController: UIDocumentPickerDelegate { guard let url = urls.first else { return } let attachmentViewModel = AttachmentViewModel( - api: viewModel.context.apiService, authenticationBox: viewModel.authenticationBox, input: .url(url), sizeLimit: viewModel.sizeLimit, diff --git a/MastodonSDK/Sources/MastodonUI/Scene/ComposeContent/ComposeContentViewModel.swift b/MastodonSDK/Sources/MastodonUI/Scene/ComposeContent/ComposeContentViewModel.swift index 6ee002389..b47270982 100644 --- a/MastodonSDK/Sources/MastodonUI/Scene/ComposeContent/ComposeContentViewModel.swift +++ b/MastodonSDK/Sources/MastodonUI/Scene/ComposeContent/ComposeContentViewModel.swift @@ -179,11 +179,11 @@ public final class ComposeContentViewModel: NSObject, ObservableObject { return visibility }() - self.customEmojiViewModel = context.emojiService.dequeueCustomEmojiViewModel( + self.customEmojiViewModel = EmojiService.shared.dequeueCustomEmojiViewModel( for: authenticationBox.domain ) - let recentLanguages = context.settingService.currentSetting.value?.recentLanguages ?? [] + let recentLanguages = SettingService.shared.currentSetting.value?.recentLanguages ?? [] self.recentLanguages = recentLanguages self.language = UserDefaults.shared.defaultPostLanguage super.init() @@ -259,7 +259,6 @@ public final class ComposeContentViewModel: NSObject, ObservableObject { guard let assetURL = $0.assetURL, let url = URL(string: assetURL) else { return nil } let attachmentViewModel = AttachmentViewModel( - api: context.apiService, authenticationBox: authenticationBox, input: .mastodonAssetUrl(url: url, attachmentId: $0.id), sizeLimit: sizeLimit, @@ -502,7 +501,7 @@ extension ComposeContentViewModel { .assign(to: &$shouldDismiss) // languages - context.settingService.currentSetting + SettingService.shared.currentSetting .flatMap { settings in if let settings { return settings.publisher(for: \.recentLanguages, options: .initial).eraseToAnyPublisher() @@ -591,7 +590,7 @@ extension ComposeContentViewModel { }() // save language to recent languages - if let settings = context.settingService.currentSetting.value { + if let settings = SettingService.shared.currentSetting.value { settings.managedObjectContext?.performAndWait { settings.recentLanguages = [language] + settings.recentLanguages.filter { $0 != language } } @@ -640,7 +639,7 @@ extension ComposeContentViewModel { }() // save language to recent languages - if let settings = context.settingService.currentSetting.value { + if let settings = SettingService.shared.currentSetting.value { settings.managedObjectContext?.performAndWait { settings.recentLanguages = [language] + settings.recentLanguages.filter { $0 != language } } diff --git a/MastodonSDK/Sources/MastodonUI/Scene/ComposeContent/Publisher/MastodonStatusPublisher.swift b/MastodonSDK/Sources/MastodonUI/Scene/ComposeContent/Publisher/MastodonStatusPublisher.swift index a4c1cfe0f..dc12c56c8 100644 --- a/MastodonSDK/Sources/MastodonUI/Scene/ComposeContent/Publisher/MastodonStatusPublisher.swift +++ b/MastodonSDK/Sources/MastodonUI/Scene/ComposeContent/Publisher/MastodonStatusPublisher.swift @@ -151,7 +151,7 @@ extension MastodonStatusPublisher: StatusPublisher { guard pollOptions != nil else { return nil } return self.pollExpireConfigurationOption.seconds }() - let inReplyToID: Mastodon.Entity.Status.ID? = try await api.backgroundManagedObjectContext.perform { + let inReplyToID: Mastodon.Entity.Status.ID? = try await PersistenceManager.shared.backgroundManagedObjectContext.perform { guard let replyTo = self.replyTo else { return nil } return replyTo.id } diff --git a/ShareActionExtension/Scene/ShareViewController.swift b/ShareActionExtension/Scene/ShareViewController.swift index aac63919d..6b567edc0 100644 --- a/ShareActionExtension/Scene/ShareViewController.swift +++ b/ShareActionExtension/Scene/ShareViewController.swift @@ -140,7 +140,7 @@ extension ShareViewController { throw AppError.badRequest } - _ = try await statusPublisher.publish(api: context.apiService, authenticationBox: authenticationBox) + _ = try await statusPublisher.publish(api: APIService.shared, authenticationBox: authenticationBox) self.publishButton.setTitle(L10n.Common.Controls.Actions.done, for: .normal) try await Task.sleep(nanoseconds: 1 * .second) @@ -255,7 +255,6 @@ extension ShareViewController { if let movieProvider = _movieProvider { let attachmentViewModel = AttachmentViewModel( - api: context.apiService, authenticationBox: authenticationBox, input: .itemProvider(movieProvider), sizeLimit: .init(image: nil, video: nil), @@ -265,7 +264,6 @@ extension ShareViewController { } else if !imageProviders.isEmpty { let attachmentViewModels = imageProviders.map { provider in AttachmentViewModel( - api: context.apiService, authenticationBox: authenticationBox, input: .itemProvider(provider), sizeLimit: .init(image: nil, video: nil), diff --git a/WidgetExtension/Variants/FollowersCount/FollowersCountWidget.swift b/WidgetExtension/Variants/FollowersCount/FollowersCountWidget.swift index c207d8837..a0d775012 100644 --- a/WidgetExtension/Variants/FollowersCount/FollowersCountWidget.swift +++ b/WidgetExtension/Variants/FollowersCount/FollowersCountWidget.swift @@ -91,8 +91,7 @@ private extension FollowersCountWidgetProvider { } guard - let resultingAccount = try await AppContext.shared - .apiService + let resultingAccount = try await APIService.shared .search(query: .init(q: desiredAccount, type: .accounts), authenticationBox: authBox) .value .accounts diff --git a/WidgetExtension/Variants/Hashtag/HashtagWidget.swift b/WidgetExtension/Variants/Hashtag/HashtagWidget.swift index 53244b969..deaa4c8c2 100644 --- a/WidgetExtension/Variants/Hashtag/HashtagWidget.swift +++ b/WidgetExtension/Variants/Hashtag/HashtagWidget.swift @@ -51,8 +51,7 @@ extension HashtagWidgetProvider { Task { do { - let mostRecentStatuses = try await AppContext.shared - .apiService + let mostRecentStatuses = try await APIService.shared .hashtagTimeline(limit: 40, hashtag: desiredHashtag, authenticationBox: authBox) .value diff --git a/WidgetExtension/Variants/LatestFollowers/LatestFollowersWidget.swift b/WidgetExtension/Variants/LatestFollowers/LatestFollowersWidget.swift index 49be33556..fe93e4ffc 100644 --- a/WidgetExtension/Variants/LatestFollowers/LatestFollowersWidget.swift +++ b/WidgetExtension/Variants/LatestFollowers/LatestFollowersWidget.swift @@ -93,8 +93,7 @@ private extension LatestFollowersWidgetProvider { var accounts = [LatestFollowersEntryAccountable]() - let followers = try await AppContext.shared - .apiService + let followers = try await APIService.shared .followers(userID: authBox.userID, maxID: nil, authenticationBox: authBox) .value .prefix(2) // X most recent followers diff --git a/WidgetExtension/Variants/MultiFollowersCount/MultiFollowersCountWidget.swift b/WidgetExtension/Variants/MultiFollowersCount/MultiFollowersCountWidget.swift index feb10cc55..964f4bd21 100644 --- a/WidgetExtension/Variants/MultiFollowersCount/MultiFollowersCountWidget.swift +++ b/WidgetExtension/Variants/MultiFollowersCount/MultiFollowersCountWidget.swift @@ -98,8 +98,7 @@ private extension MultiFollowersCountWidgetProvider { for desiredAccount in desiredAccounts { guard - let resultingAccount = try await AppContext.shared - .apiService + let resultingAccount = try await APIService.shared .search(query: .init(q: desiredAccount, type: .accounts), authenticationBox: authBox) .value .accounts