Ferrite: Fix iOS 14 onAppear bug
onAppear does not fire properly on iOS 14 due to a longstanding bug in SwiftUI. Add a UIKit onAppear hook for listening to these events and implement inside the backport namespace. Signed-off-by: kingbri <bdashore3@proton.me>
This commit is contained in:
parent
4512318e8f
commit
9ff7f5a7d5
23 changed files with 126 additions and 32 deletions
|
|
@ -46,8 +46,8 @@
|
|||
0C50055B2992BA6A0064606A /* PluginTag+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C5005592992BA6A0064606A /* PluginTag+CoreDataProperties.swift */; };
|
||||
0C54D36328C5086E00BFEEE2 /* History+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C54D36128C5086E00BFEEE2 /* History+CoreDataClass.swift */; };
|
||||
0C54D36428C5086E00BFEEE2 /* History+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C54D36228C5086E00BFEEE2 /* History+CoreDataProperties.swift */; };
|
||||
0C572D4C2993FC2A003EEC05 /* OnAppearHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C572D4B2993FC2A003EEC05 /* OnAppearHandler.swift */; };
|
||||
0C572D4E299403B7003EEC05 /* DidAppearModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C572D4D299403B7003EEC05 /* DidAppearModifier.swift */; };
|
||||
0C572D4C2993FC2A003EEC05 /* ViewDidAppearHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C572D4B2993FC2A003EEC05 /* ViewDidAppearHandler.swift */; };
|
||||
0C572D4E299403B7003EEC05 /* ViewDidAppearModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C572D4D299403B7003EEC05 /* ViewDidAppearModifier.swift */; };
|
||||
0C57D4CC289032ED008534E8 /* SearchResultInfoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C57D4CB289032ED008534E8 /* SearchResultInfoView.swift */; };
|
||||
0C5FCB05296744F300849E87 /* AllDebridCloudView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C5FCB04296744F300849E87 /* AllDebridCloudView.swift */; };
|
||||
0C64A4B4288903680079976D /* Base32 in Frameworks */ = {isa = PBXBuildFile; productRef = 0C64A4B3288903680079976D /* Base32 */; };
|
||||
|
|
@ -169,8 +169,8 @@
|
|||
0C5005592992BA6A0064606A /* PluginTag+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PluginTag+CoreDataProperties.swift"; sourceTree = "<group>"; };
|
||||
0C54D36128C5086E00BFEEE2 /* History+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "History+CoreDataClass.swift"; sourceTree = "<group>"; };
|
||||
0C54D36228C5086E00BFEEE2 /* History+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "History+CoreDataProperties.swift"; sourceTree = "<group>"; };
|
||||
0C572D4B2993FC2A003EEC05 /* OnAppearHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnAppearHandler.swift; sourceTree = "<group>"; };
|
||||
0C572D4D299403B7003EEC05 /* DidAppearModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DidAppearModifier.swift; sourceTree = "<group>"; };
|
||||
0C572D4B2993FC2A003EEC05 /* ViewDidAppearHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewDidAppearHandler.swift; sourceTree = "<group>"; };
|
||||
0C572D4D299403B7003EEC05 /* ViewDidAppearModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewDidAppearModifier.swift; sourceTree = "<group>"; };
|
||||
0C57D4CB289032ED008534E8 /* SearchResultInfoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchResultInfoView.swift; sourceTree = "<group>"; };
|
||||
0C5FCB04296744F300849E87 /* AllDebridCloudView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AllDebridCloudView.swift; sourceTree = "<group>"; };
|
||||
0C68134F28BC1A2D00FAD890 /* GithubWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GithubWrapper.swift; sourceTree = "<group>"; };
|
||||
|
|
@ -380,7 +380,7 @@
|
|||
0CD5E78828CD932B001BF684 /* DisabledAppearance.swift */,
|
||||
0CBAB83528D12ED500AC903E /* DisableInteraction.swift */,
|
||||
0CB6516428C5A5D700DCA721 /* InlinedList.swift */,
|
||||
0C572D4D299403B7003EEC05 /* DidAppearModifier.swift */,
|
||||
0C572D4D299403B7003EEC05 /* ViewDidAppearModifier.swift */,
|
||||
);
|
||||
path = Modifiers;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -531,7 +531,7 @@
|
|||
isa = PBXGroup;
|
||||
children = (
|
||||
0CA148CE288903F000DE2211 /* WebView.swift */,
|
||||
0C572D4B2993FC2A003EEC05 /* OnAppearHandler.swift */,
|
||||
0C572D4B2993FC2A003EEC05 /* ViewDidAppearHandler.swift */,
|
||||
);
|
||||
path = RepresentableViews;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -745,7 +745,7 @@
|
|||
0CA148EC288903F000DE2211 /* ContentView.swift in Sources */,
|
||||
0CC389532970AD900066D06F /* Action+CoreDataClass.swift in Sources */,
|
||||
0C03EB72296F619900162E9A /* PluginList+CoreDataProperties.swift in Sources */,
|
||||
0C572D4C2993FC2A003EEC05 /* OnAppearHandler.swift in Sources */,
|
||||
0C572D4C2993FC2A003EEC05 /* ViewDidAppearHandler.swift in Sources */,
|
||||
0C95D8D828A55B03005E22B3 /* DefaultActionsPickerViews.swift in Sources */,
|
||||
0C44E2AF28D52E8A007711AE /* BackupsView.swift in Sources */,
|
||||
0CA148E1288903F000DE2211 /* Collection.swift in Sources */,
|
||||
|
|
@ -795,7 +795,7 @@
|
|||
0C4CFC4D28970C8B00AD9FAD /* SourceComplexQuery+CoreDataClass.swift in Sources */,
|
||||
0CA148E8288903F000DE2211 /* RealDebridWrapper.swift in Sources */,
|
||||
0CA148D6288903F000DE2211 /* SettingsView.swift in Sources */,
|
||||
0C572D4E299403B7003EEC05 /* DidAppearModifier.swift in Sources */,
|
||||
0C572D4E299403B7003EEC05 /* ViewDidAppearModifier.swift in Sources */,
|
||||
0CA148E5288903F000DE2211 /* DebridManager.swift in Sources */,
|
||||
0C54D36328C5086E00BFEEE2 /* History+CoreDataClass.swift in Sources */,
|
||||
0CBAB83628D12ED500AC903E /* DisableInteraction.swift in Sources */,
|
||||
|
|
|
|||
|
|
@ -56,4 +56,8 @@ extension View {
|
|||
func inlinedList() -> some View {
|
||||
modifier(InlinedList())
|
||||
}
|
||||
|
||||
func viewDidAppear(_ callback: @escaping () -> Void) -> some View {
|
||||
modifier(ViewDidAppearModifier(callback: callback))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ struct FerriteApp: App {
|
|||
var body: some Scene {
|
||||
WindowGroup {
|
||||
MainView()
|
||||
.onAppear {
|
||||
.backport.onAppear {
|
||||
scrapingModel.toastModel = toastModel
|
||||
debridManager.toastModel = toastModel
|
||||
pluginManager.toastModel = toastModel
|
||||
|
|
|
|||
|
|
@ -7,8 +7,9 @@
|
|||
|
||||
import Foundation
|
||||
|
||||
// Version is optional until v1 is phased out
|
||||
public struct Backup: Codable {
|
||||
let version: Int
|
||||
let version: Int?
|
||||
var bookmarks: [BookmarkJson]?
|
||||
var history: [HistoryJson]?
|
||||
var sourceNames: [String]?
|
||||
|
|
|
|||
|
|
@ -161,8 +161,10 @@ public class BackupManager: ObservableObject {
|
|||
}
|
||||
}
|
||||
|
||||
if let storedLists = backup.sourceLists, (backup.version == 1) {
|
||||
// Only present in v1 backups
|
||||
let version = backup.version ?? -1
|
||||
|
||||
if let storedLists = backup.sourceLists, version < 2 {
|
||||
// Only present in v1 or no version backups
|
||||
for list in storedLists {
|
||||
try await pluginManager.addPluginList(list.urlString, existingPluginList: nil)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -81,7 +81,11 @@ public class PluginManager: ObservableObject {
|
|||
}
|
||||
}
|
||||
} catch {
|
||||
toastModel?.updateToastDescription("Plugin fetch error: \(error)")
|
||||
let error = error as NSError
|
||||
if error.code != -999 {
|
||||
toastModel?.updateToastDescription("Plugin fetch error: \(error)")
|
||||
}
|
||||
|
||||
print("Plugin fetch error: \(error)")
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,7 +20,12 @@ extension View {
|
|||
}
|
||||
|
||||
extension Backport where Content: View {
|
||||
@ViewBuilder func alert(isPresented: Binding<Bool>, title: String, message: String?, buttons: [AlertButton] = []) -> some View {
|
||||
@ViewBuilder func alert(
|
||||
isPresented: Binding<Bool>,
|
||||
title: String,
|
||||
message: String?,
|
||||
buttons: [AlertButton] = []
|
||||
) -> some View {
|
||||
if #available(iOS 15, *) {
|
||||
content
|
||||
.alert(
|
||||
|
|
@ -63,7 +68,11 @@ extension Backport where Content: View {
|
|||
}
|
||||
}
|
||||
|
||||
@ViewBuilder func confirmationDialog(isPresented: Binding<Bool>, title: String, message: String?, buttons: [AlertButton]) -> some View {
|
||||
@ViewBuilder func confirmationDialog(
|
||||
isPresented: Binding<Bool>,
|
||||
title: String, message: String?,
|
||||
buttons: [AlertButton]
|
||||
) -> some View {
|
||||
if #available(iOS 15, *) {
|
||||
content
|
||||
.confirmationDialog(
|
||||
|
|
@ -100,4 +109,18 @@ extension Backport where Content: View {
|
|||
.accentColor(color)
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder func onAppear(callback: @escaping () -> Void) -> some View {
|
||||
if #available(iOS 15, *) {
|
||||
content
|
||||
.onAppear {
|
||||
callback()
|
||||
}
|
||||
} else {
|
||||
content
|
||||
.viewDidAppear {
|
||||
callback()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ struct IndeterminateProgressView: View {
|
|||
.offset(x: -reader.size.width * 0.6, y: 0)
|
||||
.offset(x: reader.size.width * 1.2 * self.offset, y: 0)
|
||||
.animation(.default.repeatForever().speed(0.5), value: self.offset)
|
||||
.onAppear {
|
||||
.backport.onAppear {
|
||||
withAnimation {
|
||||
self.offset = 1
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,17 @@
|
|||
//
|
||||
// ViewDidAppearModifier.swift
|
||||
// Ferrite
|
||||
//
|
||||
// Created by Brian Dashore on 2/8/23.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct ViewDidAppearModifier: ViewModifier {
|
||||
let callback: () -> Void
|
||||
|
||||
func body(content: Content) -> some View {
|
||||
content
|
||||
.background(ViewDidAppearHandler(callback: callback))
|
||||
}
|
||||
}
|
||||
|
|
@ -54,7 +54,7 @@ struct BookmarksView: View {
|
|||
}
|
||||
.inlinedList()
|
||||
.listStyle(.insetGrouped)
|
||||
.onAppear {
|
||||
.backport.onAppear {
|
||||
if debridManager.enabledDebrids.count > 0 {
|
||||
viewTask = Task {
|
||||
let magnets = bookmarks.compactMap {
|
||||
|
|
@ -72,7 +72,7 @@ struct BookmarksView: View {
|
|||
viewTask?.cancel()
|
||||
}
|
||||
}
|
||||
.onAppear {
|
||||
.backport.onAppear {
|
||||
applyPredicate()
|
||||
}
|
||||
.onChange(of: searchText) { _ in
|
||||
|
|
|
|||
|
|
@ -80,7 +80,7 @@ struct AllDebridCloudView: View {
|
|||
}
|
||||
}
|
||||
}
|
||||
.onAppear {
|
||||
.backport.onAppear {
|
||||
viewTask = Task {
|
||||
await debridManager.fetchAdCloud()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ struct PremiumizeCloudView: View {
|
|||
}
|
||||
}
|
||||
}
|
||||
.onAppear {
|
||||
.backport.onAppear {
|
||||
viewTask = Task {
|
||||
await debridManager.fetchPmCloud()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -117,7 +117,7 @@ struct RealDebridCloudView: View {
|
|||
}
|
||||
}
|
||||
}
|
||||
.onAppear {
|
||||
.backport.onAppear {
|
||||
viewTask = Task {
|
||||
await debridManager.fetchRdCloud()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ struct HistoryView: View {
|
|||
}
|
||||
.listStyle(.insetGrouped)
|
||||
}
|
||||
.onAppear {
|
||||
.backport.onAppear {
|
||||
applyPredicate()
|
||||
}
|
||||
.onChange(of: searchText) { _ in
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ struct PluginListView<P: Plugin, PJ: PluginJson>: View {
|
|||
.environmentObject(navModel)
|
||||
}
|
||||
}
|
||||
.onAppear {
|
||||
.backport.onAppear {
|
||||
filteredAvailablePlugins = pluginManager.fetchFilteredPlugins(installedPlugins: installedPlugins, searchText: searchText)
|
||||
filteredUpdatedPlugins = pluginManager.fetchUpdatedPlugins(installedPlugins: installedPlugins, searchText: searchText)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -97,7 +97,7 @@ struct SourceSettingsBaseUrlView: View {
|
|||
}
|
||||
})
|
||||
.keyboardType(.URL)
|
||||
.onAppear {
|
||||
.backport.onAppear {
|
||||
tempBaseUrl = selectedSource.baseUrl ?? ""
|
||||
}
|
||||
}
|
||||
|
|
@ -127,7 +127,7 @@ struct SourceSettingsApiView: View {
|
|||
}
|
||||
})
|
||||
.autocapitalization(.none)
|
||||
.onAppear {
|
||||
.backport.onAppear {
|
||||
tempClientId = clientId.value ?? ""
|
||||
}
|
||||
}
|
||||
|
|
@ -140,7 +140,7 @@ struct SourceSettingsApiView: View {
|
|||
}
|
||||
})
|
||||
.autocapitalization(.none)
|
||||
.onAppear {
|
||||
.backport.onAppear {
|
||||
tempClientSecret = clientSecret.value ?? ""
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -138,7 +138,7 @@ struct SearchResultButtonView: View {
|
|||
existingBookmark = nil
|
||||
}
|
||||
}
|
||||
.onAppear {
|
||||
.backport.onAppear {
|
||||
// Only run a exists request if a bookmark isn't passed to the view
|
||||
if existingBookmark == nil, !runOnce {
|
||||
let bookmarkRequest = Bookmark.fetchRequest()
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ struct BackupsView: View {
|
|||
.listStyle(.insetGrouped)
|
||||
}
|
||||
}
|
||||
.onAppear {
|
||||
.backport.onAppear {
|
||||
backupManager.backupUrls = FileManager.default.appDirectory
|
||||
.appendingPathComponent("Backups", isDirectory: true).contentsByDateAdded
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ struct PluginListEditorView: View {
|
|||
.autocapitalization(.none)
|
||||
.conditionalId(sourceUrlSet)
|
||||
}
|
||||
.onAppear {
|
||||
.backport.onAppear {
|
||||
pluginListUrl = selectedPluginList?.urlString ?? ""
|
||||
sourceUrlSet = true
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ struct SettingsAppVersionView: View {
|
|||
.listStyle(.insetGrouped)
|
||||
}
|
||||
}
|
||||
.onAppear {
|
||||
.backport.onAppear {
|
||||
viewTask = Task {
|
||||
do {
|
||||
if let fetchedReleases = try await Github().fetchReleases() {
|
||||
|
|
|
|||
|
|
@ -72,7 +72,7 @@ struct MainView: View {
|
|||
}
|
||||
}
|
||||
}
|
||||
.onAppear {
|
||||
.backport.onAppear {
|
||||
if autoUpdateNotifs {
|
||||
viewTask = Task {
|
||||
do {
|
||||
|
|
|
|||
|
|
@ -75,7 +75,7 @@ struct PluginsView: View {
|
|||
ProgressView()
|
||||
}
|
||||
}
|
||||
.onAppear {
|
||||
.backport.onAppear {
|
||||
viewTask = Task {
|
||||
await pluginManager.fetchPluginsFromUrl()
|
||||
checkedForPlugins = true
|
||||
|
|
|
|||
43
Ferrite/Views/RepresentableViews/ViewDidAppearHandler.swift
Normal file
43
Ferrite/Views/RepresentableViews/ViewDidAppearHandler.swift
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
//
|
||||
// ViewDidAppearHandler.swift
|
||||
// Ferrite
|
||||
//
|
||||
// Created by Brian Dashore on 2/8/23.
|
||||
//
|
||||
// UIKit onAppear hook to fix onAppear behavior in iOS 14
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct ViewDidAppearHandler: UIViewControllerRepresentable {
|
||||
let callback: () -> Void
|
||||
|
||||
class Coordinator: UIViewController {
|
||||
let callback: () -> Void
|
||||
|
||||
init(callback: @escaping () -> Void) {
|
||||
self.callback = callback
|
||||
super.init(nibName: nil, bundle: nil)
|
||||
}
|
||||
|
||||
@available(*, unavailable)
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
override func viewDidAppear(_ animated: Bool) {
|
||||
super.viewDidAppear(animated)
|
||||
callback()
|
||||
}
|
||||
}
|
||||
|
||||
func makeCoordinator() -> Coordinator {
|
||||
Coordinator(callback: callback)
|
||||
}
|
||||
|
||||
func makeUIViewController(context: Context) -> UIViewController {
|
||||
context.coordinator
|
||||
}
|
||||
|
||||
func updateUIViewController(_ uiViewController: UIViewController, context: Context) {}
|
||||
}
|
||||
Loading…
Reference in a new issue