Post

Replies

Boosts

Views

Activity

Reply to [Suggestion] SwiftUI convenience environment navigation functions
I created all the features written above. You are free to use and change. import SwiftUI public struct EVTabView<Content: View>: View { let content: () -> Content @State private var selection: Int = 0 public init(initialSelection: Int = 0, @ViewBuilder content: @escaping () -> Content) { self.selection = initialSelection self.content = content } public var body: some View { TabView(selection: $selection, content: content) .environment(\.selectedTab, $selection) .evModal() } } extension EnvironmentValues { @Entry public var selectedTab: Binding<Int> = .constant(0) } import SwiftUI public struct EVNavigationStack<Root: View>: View { let root: () -> Root @State private var path: [EVDestination] = [] @Environment(\.dismiss) private var dismiss public init(@ViewBuilder root: @escaping () -> Root) { self.root = root } public var body: some View { NavigationStack(path: $path) { root() .navigationDestination(for: EVDestination.self) { AnyView($0.view) } } .environment(\.stackCount, path.count) .environment(\.push, { path.append(EVDestination(view: $0)) }) .environment(\.pop, { if !path.isEmpty { path.removeLast() } }) .environment(\.popToLast, { if path.count >= $0 { path.removeLast($0) } }) .environment(\.popToRoot, { path.removeAll() }) .environment(\.dismissStack, { dismiss() }) .evModal() } } extension EnvironmentValues { @Entry public var stackCount: Int = 0 @Entry public var push: (any View) -> Void = { _ in } @Entry public var pop: () -> Void = { } @Entry public var popToLast: (Int) -> Void = { _ in } @Entry public var popToRoot: () -> Void = { } @Entry public var dismissStack: () -> Void = { } } import SwiftUI struct EVModalModifier: ViewModifier { @State private var sheet: EVDestination? = nil @State private var fullScreenCover: EVDestination? = nil @State private var alert: EVMessage? = nil @State private var confirmationDialog: EVMessage? = nil func body(content: Content) -> some View { content .sheet(item: $sheet) { AnyView($0.view) } .fullScreenCover(item: $fullScreenCover) { AnyView($0.view) } .environment(\.sheet, { sheet = EVDestination(view: $0) }) .environment(\.fullScreenCover, { fullScreenCover = EVDestination(view: $0) }) .environment(\.alert, { alert = $0 }) .environment(\.confirmationDialog, { confirmationDialog = $0 }) .confirmationDialog(confirmationDialog?.title ?? "", isPresented: Binding { confirmationDialog != nil } set: { if !$0 { confirmationDialog = nil } }, titleVisibility: .visible) { if let actions = confirmationDialog?.actions { AnyView(actions()) } } message: { if let message = confirmationDialog?.message { Text(message) } } .alert(alert?.title ?? "", isPresented: Binding { alert != nil } set: { if !$0 { alert = nil } }) { if let actions = alert?.actions { AnyView(actions()) } } message: { if let message = alert?.message { Text(message) } } } } extension EnvironmentValues { @Entry public var sheet: (any View) -> Void = { _ in } @Entry public var fullScreenCover: (any View) -> Void = { _ in } @Entry public var alert: (EVMessage) -> Void = { _ in } @Entry public var confirmationDialog: (EVMessage) -> Void = { _ in } } extension View { public func evModal() -> some View { self.modifier(EVModalModifier()) } } import SwiftUI public struct EVMessage { let title: LocalizedStringKey let message: LocalizedStringKey? let actions: () -> any View public init(title: LocalizedStringKey, message: LocalizedStringKey? = nil, @ViewBuilder actions: @escaping () -> any View) { self.title = title self.message = message self.actions = actions } } import SwiftUI struct EVDestination: Identifiable, Hashable { let id = UUID() let view: any View static func == (lhs: Self, rhs: Self) -> Bool { lhs.id == rhs.id } func hash(into hasher: inout Hasher) { hasher.combine(id) } } Using example EVTabView { EVNavigationStack { ContentView() } .tabItem { Label("Menu", systemImage: "list.dash") } .tag(0) EVNavigationStack { ContentView() } .tabItem { Label("Order", systemImage: "square.and.pencil") } .tag(1) } struct ContentView: View { @Environment(\.selectedTab) @Binding private var selectedTab @Environment(\.stackCount) private var stackCount @Environment(\.push) private var push @Environment(\.sheet) private var sheet @Environment(\.fullScreenCover) private var fullScreenCover @Environment(\.dismiss) private var dismiss @Environment(\.alert) private var alert @Environment(\.confirmationDialog) private var confirmationDialog var body: some View { VStack(spacing: 15) { Text("Selected tab: **\(selectedTab)**") Text("Stack count: **\(stackCount)**") Button("change tab") { if selectedTab == 1 { selectedTab = 0 } else { selectedTab = 1 } } Button("push") { push(DetailView()) } Button("sheet") { sheet(ContentView()) } Button("fullScreenCover") { fullScreenCover(EVNavigationStack { ContentView() }) } Button("dismiss") { dismiss() } Button("alert") { let message = EVMessage(title: "Lorem ipsum", message: "Lorem ipsum dolor sit amet") { Button("OK") { } } alert(message) } Button("confirmationDialog") { let message = EVMessage(title: "Lorem ipsum", message: "Lorem ipsum dolor sit amet") { Button("OK") { } } confirmationDialog(message) } } } }
Topic: UI Frameworks SubTopic: SwiftUI Tags:
1w
Reply to Stop using MVVM for SwiftUI
Hi guys, Last week my team launch one of the first big SwiftUI app, ERAKULIS almost 99.8% SwiftUI (no UIKit needed at all). Modular and following MV (no one ViewModel) architecture. Simple and uncomplicated, no VM or other layer needed at all. Just UI and non-UI objects. I will share more details later but think about "Store" that works like magic (like what @Query do for local data but for remote data) and some "manager objects". If SwiftUI is ready for production? YES Is SwiftUI productive? YES, specially if you follow the new paradigm and ignore old stuff like MVC, MVVM, VIPER, ... Is SwiftUI buggy? YES, some workaround needed for some cases, hope more SwiftUI fixes and integrations at WWDC 2024 Observables solves many problems but only iOS 17+
Topic: UI Frameworks SubTopic: SwiftUI Tags:
Apr ’24