Polymorphism and Swiftui

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 Bindings 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 ObservableObjects 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



Leave a reply



Submit