Polymorphism and SwiftUI
In the described use-case the polymorphism would look like the following (or in some variations):
class ProfileTab: Identifiable {
let id = UUID()
let name: String
init(name: String) {
self.name = name
}
func view() -> AnyView {
AnyView(EmptyView())
}
}
class ProfileQuoteTab: ProfileTab {
let answer: String
let prompt: String
init(name: String, answer: String, prompt: String) {
self.answer = answer
self.prompt = prompt
super.init(name: name)
}
override func view() -> AnyView {
AnyView(Text(self.answer))
}
}
class ProfileImageTab: ProfileTab {
let image: Image
init(name: String, image: Image) {
self.image = image
super.init(name: name)
}
override func view() -> AnyView {
AnyView(self.image)
}
}
struct ContentView: View {
let tabs: [ProfileTab] = [
ProfileQuoteTab(name: "This is a quote tab", answer: "This is an answer", prompt: "This is a prompt"),
ProfileImageTab(name: "This is an image tab", image: Image(systemName: "faceid"))
]
var body: some View {
List(self.tabs) { tab in
VStack {
tab.view()
}
}
}
}
SwiftUI: Binding on property of struct derived from environment object
No, you're not necessarily doing something against best practices. I think in SwiftUI, the concepts of data model storage and manipulation quickly become more complex than, for example, what Apple tends to show in its demo code. With a real app, with a single source of truth, like you seem to be using, you're going to have to come up with some ways to bind the data to your views.
One solution is to write Binding
s with your own get
and set
properties that interact with your ObservableObject
. That might look like this, for example:
struct TaskView : View {
var taskIdentifier: String // Passed from parent view
@EnvironmentObject private var taskStore: TaskStore
private var taskBinding : Binding<Task> {
Binding {
taskStore.tasks[taskIdentifier] ?? .init(identifier: "", title: "", tags: [])
} set: {
taskStore.tasks[taskIdentifier] = $0
}
}
var body: some View {
TextField("Task title", text: taskBinding.title)
}
}
If you're averse to this sort of thing, one way to avoid it is to use CoreData. Because the models are made into ObservableObject
s by the system, you can generally avoid this sort of thing and directly pass around and manipulate your models. However, that doesn't necessarily mean that it is the right (or better) choice either.
You may also want to explore TCA which is an increasingly popular state management and view binding library that provides quite a few built-in solutions for the type of thing you're looking at doing.
How to have a dynamic List of Views using SwiftUI
Looks like the answer was related to wrapping my view inside of AnyView
struct ContentView : View {
var myTypes: [Any] = [View1.self, View2.self]
var body: some View {
List {
ForEach(0..<myTypes.count) { index in
self.buildView(types: self.myTypes, index: index)
}
}
}
func buildView(types: [Any], index: Int) -> AnyView {
switch types[index].self {
case is View1.Type: return AnyView( View1() )
case is View2.Type: return AnyView( View2() )
default: return AnyView(EmptyView())
}
}
}
With this, i can now get view-data from a server and compose them. Also, they are only instanced when needed.
MVVM model in SwiftUI
as a starting point:
// Model
struct Park {
var numberOfTrees = 0
mutating func plantTree() { // `mutating`gets rid of your error
numberOfTrees += 1
}
}
// View Model
class CityVM: ObservableObject {
@Published var park = Park() // creates a Park and publishes it to the views
// ... other @Published things ...
// Intents:
func plantTree() {
park.plantTree()
}
}
// View
struct ParkView: View {
// create the ViewModel, which creates the model(s)
// usually you would do this in the App struct and make available to all views by .environmentObject
@StateObject var city = CityVM()
var body: some View {
VStack {
Text("My city has \(city.park.numberOfTrees) trees.")
Button("Plant one more") {
city.plantTree()
}
}
}
}
Related Topics
Optional Type 'Bool' Cannot Be Used as a Boolean; Test for '!=Nil' Instead
Passing Data from Simple Nsview to Swiftui View
Error: Extraneous Argument Label 'No1:' in Call
How to Fix Memory Leaks in iOS Applications
Guard Let Error: Initializer for Conditional Binding Must Have Optional Type Not 'String'
Expression Pattern of Type 'String' Cannot Match Values of Type 'Nsstoryboardsegue.Identifier
Swiftui View Property Willset & Didset Property Observers Not Working
Generate Random Number of Certain Amount of Digits
Codable/Decodable Should Decode Array with Strings
Using Foreach with a String Array - [String] Has No Member 'Identified'
Recursion Over a Swift Sliceable
How to Make Prepareforsegue with Uicollectionview
Why Use Float(Arc4Random())/0Xffffffff Instead of Drand()
Why Do Two Distinct Array Literals Equal Each Other in Swift
Swift Lazy Stored Property Versus Regular Stored Property When Using Closure
Swift - Encode and Decode a Dictionary [String:Any] into Plist
iOS Swift - Uitableviewcell Custom Subclass Not Displaying Content