From 1073c634768043197a43069bdc3e836b64e84d7d Mon Sep 17 00:00:00 2001 From: Satindar Dhillon Date: Sat, 26 Feb 2022 10:54:05 -0800 Subject: [PATCH] use @StateObject when the view owns the observable object --- .../Share/ShareExtensionScene.swift | 4 +- .../Sources/App/RootViewModel.swift | 31 ++++++------- .../Views/CreateProfileContainerView.swift | 6 ++- .../Sources/App/Views/HomeFeedView.swift | 2 +- .../App/Views/NewAppleSignupView.swift | 4 +- .../Views/Profile/NewsletterEmailsView.swift | 2 +- .../App/Views/Profile/ProfileView.swift | 2 +- .../Sources/App/Views/RegistrationView.swift | 2 +- .../Sources/Views/AsyncImage.swift | 46 ++++++++++++------- 9 files changed, 55 insertions(+), 44 deletions(-) diff --git a/apple/OmnivoreKit/Sources/App/AppExtensions/Share/ShareExtensionScene.swift b/apple/OmnivoreKit/Sources/App/AppExtensions/Share/ShareExtensionScene.swift index dc91774f5..7dcbab7b0 100644 --- a/apple/OmnivoreKit/Sources/App/AppExtensions/Share/ShareExtensionScene.swift +++ b/apple/OmnivoreKit/Sources/App/AppExtensions/Share/ShareExtensionScene.swift @@ -95,10 +95,10 @@ final class ShareExtensionViewModel: ObservableObject { } struct ShareExtensionView: View { - @ObservedObject private var viewModel: ShareExtensionViewModel + @StateObject private var viewModel: ShareExtensionViewModel init(extensionContext: NSExtensionContext?) { - self.viewModel = ShareExtensionViewModel(extensionContext: extensionContext) + self._viewModel = StateObject(wrappedValue: ShareExtensionViewModel(extensionContext: extensionContext)) } var body: some View { diff --git a/apple/OmnivoreKit/Sources/App/RootViewModel.swift b/apple/OmnivoreKit/Sources/App/RootViewModel.swift index 6b50a6add..6684d1a45 100644 --- a/apple/OmnivoreKit/Sources/App/RootViewModel.swift +++ b/apple/OmnivoreKit/Sources/App/RootViewModel.swift @@ -22,8 +22,18 @@ public final class RootViewModel: ObservableObject { public var subscriptions = Set() - public init() { + public init(pdfViewerProvider: ((URL, PDFViewerViewModel) -> AnyView)?) { registerFonts() + + if let pdfViewerProvider = pdfViewerProvider { + configurePDFProvider(pdfViewerProvider: pdfViewerProvider) + } + + #if DEBUG + if CommandLine.arguments.contains("--uitesting") { + services.authenticator.logout() + } + #endif } func configurePDFProvider(pdfViewerProvider: @escaping (URL, PDFViewerViewModel) -> AnyView) { @@ -112,26 +122,13 @@ private struct SafariWebLinkPath: Identifiable { } public struct RootView: View { - @ObservedObject private var viewModel: RootViewModel - @ObservedObject private var authenticator: Authenticator + @StateObject private var viewModel: RootViewModel public init( pdfViewerProvider: ((URL, PDFViewerViewModel) -> AnyView)?, intercomProvider: IntercomProvider? ) { - let rootViewModel = RootViewModel() - self.viewModel = rootViewModel - self.authenticator = rootViewModel.services.authenticator - - #if DEBUG - if CommandLine.arguments.contains("--uitesting") { - authenticator.logout() - } - #endif - - if let pdfViewerProvider = pdfViewerProvider { - viewModel.configurePDFProvider(pdfViewerProvider: pdfViewerProvider) - } + self._viewModel = StateObject(wrappedValue: RootViewModel(pdfViewerProvider: pdfViewerProvider)) if let intercomProvider = intercomProvider { DataService.showIntercomMessenger = intercomProvider.showIntercomMessenger @@ -141,7 +138,7 @@ public struct RootView: View { } @ViewBuilder private var innerBody: some View { - if authenticator.isLoggedIn { + if viewModel.services.authenticator.isLoggedIn { PrimaryContentView() .onAppear { viewModel.triggerPushNotificationRequestIfNeeded() diff --git a/apple/OmnivoreKit/Sources/App/Views/CreateProfileContainerView.swift b/apple/OmnivoreKit/Sources/App/Views/CreateProfileContainerView.swift index c9c21452c..dc9254c64 100644 --- a/apple/OmnivoreKit/Sources/App/Views/CreateProfileContainerView.swift +++ b/apple/OmnivoreKit/Sources/App/Views/CreateProfileContainerView.swift @@ -101,13 +101,15 @@ final class CreateProfileViewModel: ObservableObject { struct CreateProfileContainerView: View { @EnvironmentObject var authenticator: Authenticator @EnvironmentObject var dataService: DataService - @ObservedObject private var viewModel: CreateProfileViewModel + @StateObject private var viewModel: CreateProfileViewModel @State private var name: String @State private var bio = "" init(userProfile: UserProfile, dataService: DataService) { - self.viewModel = CreateProfileViewModel(initialUserProfile: userProfile, dataService: dataService) + self._viewModel = StateObject( + wrappedValue: CreateProfileViewModel(initialUserProfile: userProfile, dataService: dataService) + ) self._name = State(initialValue: userProfile.name) } diff --git a/apple/OmnivoreKit/Sources/App/Views/HomeFeedView.swift b/apple/OmnivoreKit/Sources/App/Views/HomeFeedView.swift index 13cb2ca1d..759dbbd63 100644 --- a/apple/OmnivoreKit/Sources/App/Views/HomeFeedView.swift +++ b/apple/OmnivoreKit/Sources/App/Views/HomeFeedView.swift @@ -179,7 +179,7 @@ final class HomeFeedViewModel: ObservableObject { struct HomeFeedView: View { @EnvironmentObject var dataService: DataService - @ObservedObject private var viewModel = HomeFeedViewModel() + @StateObject private var viewModel = HomeFeedViewModel() @State private var selectedLinkItem: FeedItem? @State private var searchQuery = "" @State private var itemToRemove: FeedItem? diff --git a/apple/OmnivoreKit/Sources/App/Views/NewAppleSignupView.swift b/apple/OmnivoreKit/Sources/App/Views/NewAppleSignupView.swift index d433ede10..9529f06f7 100644 --- a/apple/OmnivoreKit/Sources/App/Views/NewAppleSignupView.swift +++ b/apple/OmnivoreKit/Sources/App/Views/NewAppleSignupView.swift @@ -30,12 +30,12 @@ final class NewAppleSignupViewModel: ObservableObject { struct NewAppleSignupView: View { @EnvironmentObject var authenticator: Authenticator - @ObservedObject private var viewModel: NewAppleSignupViewModel + @StateObject private var viewModel: NewAppleSignupViewModel let showProfileEditView: () -> Void init(userProfile: UserProfile, showProfileEditView: @escaping () -> Void) { self.showProfileEditView = showProfileEditView - self.viewModel = NewAppleSignupViewModel(userProfile: userProfile) + self._viewModel = StateObject(wrappedValue: NewAppleSignupViewModel(userProfile: userProfile)) } var body: some View { diff --git a/apple/OmnivoreKit/Sources/App/Views/Profile/NewsletterEmailsView.swift b/apple/OmnivoreKit/Sources/App/Views/Profile/NewsletterEmailsView.swift index 0e7063d36..5f171b349 100644 --- a/apple/OmnivoreKit/Sources/App/Views/Profile/NewsletterEmailsView.swift +++ b/apple/OmnivoreKit/Sources/App/Views/Profile/NewsletterEmailsView.swift @@ -43,7 +43,7 @@ final class NewsletterEmailsViewModel: ObservableObject { struct NewsletterEmailsView: View { @EnvironmentObject var dataService: DataService - @ObservedObject var viewModel = NewsletterEmailsViewModel() + @StateObject var viewModel = NewsletterEmailsViewModel() let footerText = "Add PDFs to your library, or subscribe to emails using an Omnivore email address." var body: some View { diff --git a/apple/OmnivoreKit/Sources/App/Views/Profile/ProfileView.swift b/apple/OmnivoreKit/Sources/App/Views/Profile/ProfileView.swift index 332e732b4..a50e5643a 100644 --- a/apple/OmnivoreKit/Sources/App/Views/Profile/ProfileView.swift +++ b/apple/OmnivoreKit/Sources/App/Views/Profile/ProfileView.swift @@ -30,7 +30,7 @@ struct ProfileView: View { @EnvironmentObject var authenticator: Authenticator @EnvironmentObject var dataService: DataService - @ObservedObject private var viewModel = ProfileContainerViewModel() + @StateObject private var viewModel = ProfileContainerViewModel() @State private var showLogoutConfirmation = false diff --git a/apple/OmnivoreKit/Sources/App/Views/RegistrationView.swift b/apple/OmnivoreKit/Sources/App/Views/RegistrationView.swift index d358b28d0..b36090bec 100644 --- a/apple/OmnivoreKit/Sources/App/Views/RegistrationView.swift +++ b/apple/OmnivoreKit/Sources/App/Views/RegistrationView.swift @@ -88,7 +88,7 @@ struct RegistrationView: View { @EnvironmentObject var authenticator: Authenticator @EnvironmentObject var dataService: DataService @Environment(\.horizontalSizeClass) var horizontalSizeClass - @ObservedObject private var viewModel = RegistrationViewModel() + @StateObject private var viewModel = RegistrationViewModel() var authenticationView: some View { VStack(spacing: 0) { diff --git a/apple/OmnivoreKit/Sources/Views/AsyncImage.swift b/apple/OmnivoreKit/Sources/Views/AsyncImage.swift index c29d497ce..0ebbd9bd5 100644 --- a/apple/OmnivoreKit/Sources/Views/AsyncImage.swift +++ b/apple/OmnivoreKit/Sources/Views/AsyncImage.swift @@ -5,31 +5,43 @@ import Utils struct AsyncImage: View { let isResizable: Bool - @ObservedObject private var imageLoader = ImageLoader() + let url: URL? + @State private var isLoaded = false + @StateObject private var imageLoader = ImageLoader() init(url: URL?, isResizable: Bool = true) { self.isResizable = isResizable - if let url = url { + self.url = url + } + + func load() { + if let url = url, !isLoaded { imageLoader.load(fromUrl: url) + isLoaded = true } } var body: some View { - #if os(iOS) - if isResizable { - Image(uiImage: imageLoader.image ?? imageLoader.placeholder) - .resizable() - } else { - Image(uiImage: imageLoader.image ?? imageLoader.placeholder) - } - #elseif os(macOS) - if isResizable { - Image(nsImage: imageLoader.image ?? imageLoader.placeholder) - .resizable() - } else { - Image(nsImage: imageLoader.image ?? imageLoader.placeholder) - } - #endif + Group { + #if os(iOS) + if isResizable { + Image(uiImage: imageLoader.image ?? imageLoader.placeholder) + .resizable() + } else { + Image(uiImage: imageLoader.image ?? imageLoader.placeholder) + } + #elseif os(macOS) + if isResizable { + Image(nsImage: imageLoader.image ?? imageLoader.placeholder) + .resizable() + } else { + Image(nsImage: imageLoader.image ?? imageLoader.placeholder) + } + #endif + } + .onAppear { + load() + } } }