Skip to content

Instantly share code, notes, and snippets.

@VAndrJ
Created January 6, 2024 22:01
Show Gist options
  • Save VAndrJ/b25ac4d201dbe539f76e0f58b9e605e2 to your computer and use it in GitHub Desktop.
Save VAndrJ/b25ac4d201dbe539f76e0f58b9e605e2 to your computer and use it in GitHub Desktop.
SwiftUI onAppear bug
//
// ContentView.swift
// OnAppearCheck
//
// Created by VAndrJ on 21.12.2023.
//
import SwiftUI
struct ContentView: View {
@State var isLoggedIn: Bool = false
var body: some View {
if isLoggedIn {
TabView {
TabViewView(title: "Store", tabIcon: "trash", isLoggedIn: $isLoggedIn)
TabViewView(title: "New", tabIcon: "newspaper", isLoggedIn: $isLoggedIn)
TabViewView(title: "Gallery", tabIcon: "photo", isLoggedIn: $isLoggedIn)
TabViewView(title: "Discover", tabIcon: "magnifyingglass", isLoggedIn: $isLoggedIn)
TabViewView(title: "Profile", tabIcon: "person", isLoggedIn: $isLoggedIn)
}
} else {
LoginView {
isLoggedIn = true
}
}
}
}
struct TabViewView: View {
let title: String
let tabIcon: String
@Binding var isLoggedIn: Bool
var body: some View {
NavigationStack {
Text(title)
.navigationBarTitle(title)
.task {
print("πŸ‹οΈ \(title) some task")
}
.onAppear {
print("πŸ‘‹ \(title) onAppear")
}
.onDisappear {
print("πŸ˜Άβ€πŸŒ«οΈ \(title) onDisappear")
}
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Button("Logout") {
isLoggedIn = false
}
}
}
}
.task {
print("πŸ‹οΈ \(title) NavigationStack some task")
}
.onAppear {
print("πŸ‘‹ \(title) NavigationStack onAppear")
}
.onDisappear {
print("πŸ˜Άβ€πŸŒ«οΈ \(title) NavigationStack onDisappear")
}
.tabItem {
Image(systemName: tabIcon)
Text(title)
}
}
}
struct LoginView: View {
let onLogin: () -> Void
var body: some View {
Button("Login", action: onLogin)
}
}
//
// ContentView.swift
// OnAppearCheck
//
// Created by VAndrJ on 21.12.2023.
//
import SwiftUI
struct ContentView: View {
@State var isLoggedIn: Bool = false
var body: some View {
if isLoggedIn {
TabView {
TabViewView(title: "Store", tabIcon: "trash")
TabViewView(title: "New", tabIcon: "newspaper")
TabViewView(title: "Gallery", tabIcon: "photo")
TabViewView(title: "Discover", tabIcon: "magnifyingglass")
TabViewView(title: "Profile", tabIcon: "person")
}
.environment(\.logout, LogoutAction(action: logout))
} else {
LoginView {
isLoggedIn = true
}
}
}
func logout() {
isLoggedIn = false
}
}
struct LogoutAction {
let action: () -> Void
func callAsFunction() {
action()
}
}
struct LogoutActionKey: EnvironmentKey {
static var defaultValue: LogoutAction? = nil
}
extension EnvironmentValues {
var logout: LogoutAction? {
get { self[LogoutActionKey.self] }
set { self[LogoutActionKey.self] = newValue }
}
}
struct TabViewView: View {
let title: String
let tabIcon: String
@Environment(\.logout) var logout
var body: some View {
NavigationStack {
Text(title)
.navigationBarTitle(title)
.task {
print("πŸ‹οΈ \(title) some task")
}
.onAppear {
print("πŸ‘‹ \(title) onAppear")
}
.onDisappear {
print("πŸ˜Άβ€πŸŒ«οΈ \(title) onDisappear")
}
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Button("Logout") {
logout?()
}
}
}
}
.task {
print("πŸ‹οΈ \(title) NavigationStack some task")
}
.onAppear {
print("πŸ‘‹ \(title) NavigationStack onAppear")
}
.onDisappear {
print("πŸ˜Άβ€πŸŒ«οΈ \(title) NavigationStack onDisappear")
}
.tabItem {
Image(systemName: tabIcon)
Text(title)
}
}
}
struct LoginView: View {
let onLogin: () -> Void
var body: some View {
Button("Login", action: onLogin)
}
}
@VAndrJ
Copy link
Author

VAndrJ commented Jan 9, 2024

@hmlongco , only this workaround helps:

public extension View {

    func onDidAppear(perform action: @escaping () -> Void) -> some View {
        background(DidAppearView(action: action))
    }
}

struct DidAppearView: UIViewControllerRepresentable {
    class Coordinator: ActionControllerRepresentableDelegate {
        var action: () -> Void

        init(action: @escaping () -> Void) {
            self.action = action
        }

        func performAction() {
            action()
        }
    }

    let action: () -> Void

    func makeUIViewController(context: Context) -> DidAppearViewController {
        let controller = DidAppearViewController()
        controller.delegate = context.coordinator

        return controller
    }

    func updateUIViewController(_ controller: DidAppearViewController, context: Context) {
    }

    func makeCoordinator() -> Coordinator {
        Coordinator(action: action)
    }
}

protocol ActionControllerRepresentableDelegate: AnyObject {
    func performAction()
}

class DidAppearViewController: UIViewController {
    weak var delegate: ActionControllerRepresentableDelegate?

    override func viewDidAppear(_ animated: Bool) {
        delegate?.performAction()
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment