diff --git a/apple/OmnivoreKit/Sources/App/Views/Home/Components/FeedCardNavigationLink.swift b/apple/OmnivoreKit/Sources/App/Views/Home/Components/FeedCardNavigationLink.swift index dbf0084ca..93fd2670b 100644 --- a/apple/OmnivoreKit/Sources/App/Views/Home/Components/FeedCardNavigationLink.swift +++ b/apple/OmnivoreKit/Sources/App/Views/Home/Components/FeedCardNavigationLink.swift @@ -22,7 +22,7 @@ struct FeedCardNavigationLink: View { .opacity(0) .buttonStyle(PlainButtonStyle()) .onAppear { - viewModel.itemAppeared(item: item, dataService: dataService) + Task { await viewModel.itemAppeared(item: item, dataService: dataService) } } FeedCard(item: item) } @@ -60,7 +60,7 @@ struct GridCardNavigationLink: View { } }) .onAppear { - viewModel.itemAppeared(item: item, dataService: dataService) + Task { await viewModel.itemAppeared(item: item, dataService: dataService) } } } .aspectRatio(1.8, contentMode: .fill) diff --git a/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift b/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift index 46720c206..f5462772b 100644 --- a/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift +++ b/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewIOS.swift @@ -19,7 +19,7 @@ import Views viewModel: viewModel ) .refreshable { - viewModel.loadItems(dataService: dataService, isRefresh: true) + Task { await viewModel.loadItems(dataService: dataService, isRefresh: true) } } .searchable( text: $viewModel.searchTerm, @@ -35,13 +35,13 @@ import Views .onChange(of: viewModel.searchTerm) { _ in // Maybe we should debounce this, but // it feels like it works ok without - viewModel.loadItems(dataService: dataService, isRefresh: true) + Task { await viewModel.loadItems(dataService: dataService, isRefresh: true) } } .onChange(of: viewModel.selectedLabels) { _ in - viewModel.loadItems(dataService: dataService, isRefresh: true) + Task { await viewModel.loadItems(dataService: dataService, isRefresh: true) } } .onSubmit(of: .search) { - viewModel.loadItems(dataService: dataService, isRefresh: true) + Task { await viewModel.loadItems(dataService: dataService, isRefresh: true) } } .sheet(item: $viewModel.itemUnderLabelEdit) { item in ApplyLabelsView(mode: .item(item)) { labels in @@ -54,7 +54,7 @@ import Views .onReceive(NotificationCenter.default.publisher(for: UIApplication.willEnterForegroundNotification)) { _ in // Don't refresh the list if the user is currently reading an article if viewModel.selectedLinkItem == nil { - viewModel.loadItems(dataService: dataService, isRefresh: true) + Task { await viewModel.loadItems(dataService: dataService, isRefresh: true) } } } .onReceive(NotificationCenter.default.publisher(for: Notification.Name("PushFeedItem"))) { notification in @@ -75,7 +75,7 @@ import Views } .onAppear { if viewModel.items.isEmpty { - viewModel.loadItems(dataService: dataService, isRefresh: true) + Task { await viewModel.loadItems(dataService: dataService, isRefresh: true) } } } .onChange(of: viewModel.selectedLinkItem) { _ in @@ -322,7 +322,7 @@ import Views .onPreferenceChange(ScrollViewOffsetPreferenceKey.self) { offset in DispatchQueue.main.async { if !viewModel.isLoading, offset > 240 { - viewModel.loadItems(dataService: dataService, isRefresh: true) + Task { await viewModel.loadItems(dataService: dataService, isRefresh: true) } } } } diff --git a/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewModel.swift b/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewModel.swift index 4ee67a777..e3bfffbc8 100644 --- a/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewModel.swift +++ b/apple/OmnivoreKit/Sources/App/Views/Home/HomeFeedViewModel.swift @@ -5,7 +5,7 @@ import SwiftUI import Utils import Views -final class HomeFeedViewModel: ObservableObject { +@MainActor final class HomeFeedViewModel: ObservableObject { var currentDetailViewModel: LinkItemDetailViewModel? /// Track progress updates to be committed when user navigates back to grid view @@ -36,14 +36,14 @@ final class HomeFeedViewModel: ObservableObject { init() {} - func itemAppeared(item: FeedItem, dataService: DataService) { + func itemAppeared(item: FeedItem, dataService: DataService) async { if isLoading { return } let itemIndex = items.firstIndex(where: { $0.id == item.id }) let thresholdIndex = items.index(items.endIndex, offsetBy: -5) // Check if user has scrolled to the last five items in the list if let itemIndex = itemIndex, itemIndex > thresholdIndex, items.count < thresholdIndex + 10 { - loadItems(dataService: dataService, isRefresh: false) + await loadItems(dataService: dataService, isRefresh: false) } } @@ -51,7 +51,7 @@ final class HomeFeedViewModel: ObservableObject { items.insert(item, at: 0) } - func loadItems(dataService: DataService, isRefresh: Bool) { + func loadItems(dataService: DataService, isRefresh: Bool) async { // Clear offline highlights since we'll be populating new FeedItems with the correct highlights set dataService.clearHighlights() @@ -62,11 +62,7 @@ final class HomeFeedViewModel: ObservableObject { // Cache the viewer if dataService.currentViewer == nil { - dataService.viewerPublisher().sink( - receiveCompletion: { _ in }, - receiveValue: { _ in } - ) - .store(in: &subscriptions) + _ = try? await dataService.fetchViewer() } dataService.libraryItemsPublisher( diff --git a/apple/OmnivoreKit/Sources/App/Views/LinkItemDetailView.swift b/apple/OmnivoreKit/Sources/App/Views/LinkItemDetailView.swift index d267389e3..6ae0f7ad9 100644 --- a/apple/OmnivoreKit/Sources/App/Views/LinkItemDetailView.swift +++ b/apple/OmnivoreKit/Sources/App/Views/LinkItemDetailView.swift @@ -9,7 +9,7 @@ enum PDFProvider { static var pdfViewerProvider: ((URL, FeedItem) -> AnyView)? } -final class LinkItemDetailViewModel: ObservableObject { +@MainActor final class LinkItemDetailViewModel: ObservableObject { let homeFeedViewModel: HomeFeedViewModel @Published var item: FeedItem @Published var webAppWrapperViewModel: WebAppWrapperViewModel? @@ -45,31 +45,22 @@ final class LinkItemDetailViewModel: ObservableObject { .store(in: &subscriptions) } - func loadWebAppWrapper(dataService: DataService, rawAuthCookie: String?) { - // Attempt to get `Viewer` from DataService - if let currentViewer = dataService.currentViewer { + func loadWebAppWrapper(dataService: DataService, rawAuthCookie: String?) async { + let viewer: Viewer? = await { + if let currentViewer = dataService.currentViewer { + return currentViewer + } + + return try? await dataService.fetchViewer() + }() + + if let viewer = viewer { createWebAppWrapperViewModel( - username: currentViewer.username, + username: viewer.username, dataService: dataService, rawAuthCookie: rawAuthCookie ) - return } - - dataService.viewerPublisher().sink( - receiveCompletion: { completion in - guard case let .failure(error) = completion else { return } - print(error) - }, - receiveValue: { [weak self] viewer in - self?.createWebAppWrapperViewModel( - username: viewer.username, - dataService: dataService, - rawAuthCookie: rawAuthCookie - ) - } - ) - .store(in: &subscriptions) } private func createWebAppWrapperViewModel(username: String, dataService: DataService, rawAuthCookie: String?) { @@ -265,8 +256,8 @@ struct LinkItemDetailView: View { navBar Spacer() } - .onAppear { - viewModel.loadWebAppWrapper( + .task { + await viewModel.loadWebAppWrapper( dataService: dataService, rawAuthCookie: authenticator.omnivoreAuthCookieString ) @@ -311,8 +302,8 @@ struct LinkItemDetailView: View { Text("Loading...") Spacer() } - .onAppear { - viewModel.loadWebAppWrapper( + .task { + await viewModel.loadWebAppWrapper( dataService: dataService, rawAuthCookie: authenticator.omnivoreAuthCookieString )