diff --git a/WordPress/Classes/Services/BlockEditorCache.swift b/WordPress/Classes/Services/BlockEditorCache.swift index b1566cc511af..9f1a1bfc6d2b 100644 --- a/WordPress/Classes/Services/BlockEditorCache.swift +++ b/WordPress/Classes/Services/BlockEditorCache.swift @@ -24,14 +24,17 @@ final actor BlockEditorCache { try settings.write(to: fileURL) } - func getBlockSettings(for blogID: String) throws -> Data? { + func getBlockSettings(for blogID: String) -> Data? { let fileURL = makeBlockSettingsURL(for: blogID) - guard FileManager.default.fileExists(atPath: fileURL.path) else { return nil } - - return try Data(contentsOf: fileURL) + do { + return try Data(contentsOf: fileURL) + } catch { + wpAssertionFailure("BlockEditorCache failed to read cached data", userInfo: ["error": "\(error)"]) + return nil + } } func deleteBlockSettings(for blogID: String) throws { diff --git a/WordPress/Classes/Services/RawBlockEditorSettingsService.swift b/WordPress/Classes/Services/RawBlockEditorSettingsService.swift index e4234b195fbd..e0bcbebe3161 100644 --- a/WordPress/Classes/Services/RawBlockEditorSettingsService.swift +++ b/WordPress/Classes/Services/RawBlockEditorSettingsService.swift @@ -31,7 +31,7 @@ final class RawBlockEditorSettingsService { /// the network. func getSettings(allowingCachedResponse: Bool = true) async throws -> Data { // Return cached settings if available - if allowingCachedResponse, let cachedSettings = try await BlockEditorCache.shared.getBlockSettings(for: blogID) { + if allowingCachedResponse, let cachedSettings = await BlockEditorCache.shared.getBlockSettings(for: blogID) { return cachedSettings } return try await fetchSettingsFromAPI() diff --git a/WordPress/Classes/ViewRelated/NewGutenberg/NewGutenbergViewController.swift b/WordPress/Classes/ViewRelated/NewGutenberg/NewGutenbergViewController.swift index fca8dff431f9..7eb8fcf7907e 100644 --- a/WordPress/Classes/ViewRelated/NewGutenberg/NewGutenbergViewController.swift +++ b/WordPress/Classes/ViewRelated/NewGutenberg/NewGutenbergViewController.swift @@ -10,46 +10,6 @@ import WebKit import CocoaLumberjackSwift class NewGutenbergViewController: UIViewController, PostEditor, PublishingEditor { - - enum EditorLoadingState { - /// We haven't done anything with the editor yet - /// - /// Valid states to transition to: - /// - .loadingDependencies - case uninitialized - - /// We're loading the editor's dependencies - /// - /// Valid states to transition to: - /// - .loadingCancelled - /// - .dependencyError - /// - .dependenciesReady - case loadingDependencies(_ task: Task) - - /// We cancelled loading the editor's dependencies - /// - /// Valid states to transition to: - /// - .loadingDependencies - case loadingCancelled - - /// An error occured while fetching dependencies - /// - /// Valid states to transition to: - /// - .loadingDependencies - case dependencyError(Error) - - /// All dependencies have been loaded, so we're ready to start the editor - /// - /// Valid states to transition to: - /// - .ready - case dependenciesReady(EditorDependencies) - - /// The editor is fully loaded and we've passed all required configuration and data to it - /// - /// There are no valid transition states from `.started` - case started - } - struct EditorDependencies { let settings: String? let didLoadCookies: Bool @@ -125,9 +85,7 @@ class NewGutenbergViewController: UIViewController, PostEditor, PublishingEditor private var suggestionViewBottomConstraint: NSLayoutConstraint? private var currentSuggestionsController: GutenbergSuggestionsViewController? - private var editorState: EditorLoadingState = .uninitialized - private var dependencyLoadingError: Error? - private var editorLoadingTask: Task? + private var editorLoadingTask: Task? // TODO: remove (none of these APIs are needed for the new editor) func prepopulateMediaItems(_ media: [Media]) {} @@ -215,8 +173,6 @@ class NewGutenbergViewController: UIViewController, PostEditor, PublishingEditor configureNavigationBar() refreshInterface() - startLoadingDependencies() - SiteSuggestionService.shared.prefetchSuggestionsIfNeeded(for: post.blog) { // Do nothing } @@ -228,56 +184,16 @@ class NewGutenbergViewController: UIViewController, PostEditor, PublishingEditor // DDLogError("Error syncing JETPACK: \(String(describing: error))") // }) + loadEditor() onViewDidLoad() } - override func viewWillAppear(_ animated: Bool) { - super.viewWillAppear(animated) - - if case .loadingDependencies = self.editorState { - self.showActivityIndicator() - } - - if case .loadingCancelled = self.editorState { - startLoadingDependencies() - } - } - - override func viewDidAppear(_ animated: Bool) { - super.viewDidAppear(animated) - - if case .loadingCancelled = self.editorState { - preconditionFailure("Dependency loading should not be cancelled") - } - - self.editorLoadingTask = Task { [weak self] in - guard let self else { return } - do { - while case .loadingDependencies = self.editorState { - try await Task.sleep(nanoseconds: 1000) - } - - switch self.editorState { - case .uninitialized: preconditionFailure("Dependencies must be initialized") - case .loadingDependencies: preconditionFailure("Dependencies should not still be loading") - case .loadingCancelled: preconditionFailure("Dependency loading should not be cancelled") - case .dependencyError(let error): self.showEditorError(error) - case .dependenciesReady(let dependencies): try await self.startEditor(settings: dependencies.settings) - case .started: preconditionFailure("The editor should not already be started") - } - } catch { - self.showEditorError(error) - } - } - } - override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) - if case .loadingDependencies(let task) = self.editorState { - task.cancel() - } - self.editorLoadingTask?.cancel() + if isBeingDismissedDirectlyOrByAncestor() { + editorLoadingTask?.cancel() + } } private func setupEditorView() { @@ -296,7 +212,7 @@ class NewGutenbergViewController: UIViewController, PostEditor, PublishingEditor setContentScrollView(editorViewController.webView.scrollView) } - // MARK: - Functions + // MARK: - Helpers private func configureNavigationBar() { navigationController?.navigationBar.accessibilityIdentifier = "Gutenberg Editor Navigation Bar" @@ -361,51 +277,6 @@ class NewGutenbergViewController: UIViewController, PostEditor, PublishingEditor } } - func startLoadingDependencies() { - switch self.editorState { - case .uninitialized: - break // This is fine – we're loading for the first time - case .loadingDependencies: - preconditionFailure("`startLoadingDependencies` should not be called while in the `.loadingDependencies` state") - case .loadingCancelled: - break // This is fine – we're loading after quickly switching posts - case .dependencyError: - break // We're retrying after an error - case .dependenciesReady: - preconditionFailure("`startLoadingDependencies` should not be called while in the `.dependenciesReady` state") - case .started: - preconditionFailure("`startLoadingDependencies` should not be called while in the `.started` state") - } - - self.editorState = .loadingDependencies(Task { - do { - let dependencies = try await fetchEditorDependencies() - self.editorState = .dependenciesReady(dependencies) - } catch { - self.editorState = .dependencyError(error) - } - }) - } - - @MainActor - func startEditor(settings: String?) async throws { - guard case .dependenciesReady = self.editorState else { - preconditionFailure("`startEditor` should only be called when the editor is in the `.dependenciesReady` state.") - } - - let updatedConfiguration = self.editorViewController.configuration.toBuilder() - .apply(settings) { $0.setEditorSettings($1) } - .setTitle(post.postTitle ?? "") - .setContent(post.content ?? "") - .build() - - self.editorViewController.updateConfiguration(updatedConfiguration) - self.editorViewController.startEditorSetup() - - // Handles refreshing controls with state context after options screen is dismissed - editorContentWasUpdated() - } - // MARK: - Keyboard Observers private func setupKeyboardObservers() { @@ -473,7 +344,36 @@ class NewGutenbergViewController: UIViewController, PostEditor, PublishingEditor } // MARK: - Editor Setup - private func fetchEditorDependencies() async throws -> EditorDependencies { + + private func loadEditor() { + editorLoadingTask = Task { @MainActor in + await actuallyLoadEditor() + } + } + + @MainActor + private func actuallyLoadEditor() async { + showActivityIndicator() + + let dependencies = await fetchEditorDependencies() + startEditor(dependencies: dependencies) + } + + private func startEditor(dependencies: EditorDependencies) { + let configuration = editorViewController.configuration.toBuilder() + .apply(dependencies.settings) { $0.setEditorSettings($1) } + .setTitle(post.postTitle ?? "") + .setContent(post.content ?? "") + .build() + + editorViewController.updateConfiguration(configuration) + editorViewController.startEditorSetup() + + // Handles refreshing controls with state context after options screen is dismissed + editorContentWasUpdated() + } + + private func fetchEditorDependencies() async -> EditorDependencies { let settings: String? do { settings = try await blockEditorSettingsService.getSettingsString(allowingCachedResponse: true) @@ -527,7 +427,7 @@ extension NewGutenbergViewController: GutenbergKit.EditorViewControllerDelegate // is still reflecting the actual startup time of the editor editorSession.start() } - self.hideActivityIndicator() + hideActivityIndicator() } func editor(_ viewContoller: GutenbergKit.EditorViewController, didDisplayInitialContent content: String) {