Class with an Observable Object has been used by is not initializing - swift

I've added an Observable Object to my class DetailViewModel, but I am getting an error "Class 'DetailViewModel' has no initializers". Can anyone explain why?
import Foundation
import UIKit
#MainActor
class DetailViewModel: ObservableObject{
#Published var strMeal: String = ""
#Published var strInstructions: String
#Published var strIngredient: String
#Published var strMeasure: String
#Published var strMealThumb:URL?
private func loadMealDetails(idMeal: String) async {
do {
let mealDetailResponse = try await WebServiceRequest().loadData(url: Constants.Urls.getMealByIdUrl(strMeal)) { data in
return try? JSONDecoder().decode(MealDetailModel.self, from:data )
}
} catch {
print(error)
}
}

You have defined some properties (strInstructions, strIngredient, and strMeasure) that don't have initial values specified. Unlike structs, which get synthesized initializers (eg the compiler makes a initializer for us), with a class, we have to create an initializer ourselves (or give all of the properties default values).
With default values, it may look like:
#MainActor
class DetailViewModel: ObservableObject{
#Published var strMeal: String = ""
#Published var strInstructions: String = ""
#Published var strIngredient: String = ""
#Published var strMeasure: String = ""
#Published var strMealThumb:URL?
}
Or, with an initializer, it could be something like:
#MainActor
class DetailViewModel: ObservableObject{
#Published var strMeal: String = ""
#Published var strInstructions: String
#Published var strIngredient: String
#Published var strMeasure: String
#Published var strMealThumb:URL?
init(strInstructions: String, strIngredient: String, strMeasure: String) {
self.strInstructions = strInstructions
self.strIngredient = strIngredient
self.strMeasure = strMeasure
}
}
You may also want to accept values for strMeal and strMealThumb in your initializer -- it's up to you.

Related

Pass Variable from Struct to Class in SwiftUI

i Have a Structure and have some variable in it
struct Home: View {
var token: String
var loginapiurl: String
var companyname: String
#StateObject var dataservice = StatAPI()
i have to pass this token and loginapiurl in the class
class StatAPI: ObservableObject {
#Published var statsdetails = [Result]()
init()
{
print("in init")
let currenDate = getCurrentDate()
let past7DaysBeforeDate = past7dayDate(date1: currenDate)
getStats(dateFrom: past7DaysBeforeDate, dateTo: currenDate)
}
i want to use that two variable in init and passed in to getStats function
I am new to Swiftui. Can anyone help me about this

How can I decode these models with Realm?

I want to use these models both for realm and as codable with Json. How can I do this? As I understand it, Realm doesn't accept it when I use Dictionary.
class QuestionContainer: Codable {
var questionCategories: [Question]
}
class Question: Object, Codable, Identifiable {
#objc dynamic var title: String
#objc dynamic var id: String
#objc dynamic var questions: [QuestionList]
}
class QuestionList: Object, Codable, Identifiable {
#objc dynamic var id: String
#objc dynamic var question: String
#objc dynamic var isQuestionImage, isSectionImage: Bool
#objc dynamic var imageURL: String
#objc dynamic var imageData: Data?
#objc dynamic var sections: [QuestionSections.RawValue : String]
#objc dynamic var selected: String
#objc dynamic var correct: String
}
enum QuestionSections: String, Codable {
case A = "A"
case B = "B"
case C = "C"
case D = "D"
}
Your model should look like:
class QuestionContainer: Object, Codable {
#Persisted var questionCategories: List<Question>
}
class Question: Object, Codable, Identifiable {
#Persisted var title: String
#Persisted var id: String
#Persisted var questions: List<QuestionList
}
class QuestionList: Object, Codable, Identifiable {
#Persisted var id: String
#Persisted var question: String
#Persisted var isQuestionImage: Bool
#Persisted var isSectionImage: Bool
#Persisted var imageURL: String
#Persisted var imageData: Data?
#Persisted var sections: Map<String, String>
#Persisted var selected: String
#Persisted var correct: String
}

How to pass Binding variable to an ObservableObject?

I have a binding variable that I need to pass to an ObservableObject in swiftUI.
Let's say I have this code:
struct MYView: View {
#ObservedObject var fetch = Fetch()
#Binding var IDTxt: Int
var body: some View{
//some code here
}
}
Now I want to pass the IDTxt value to this:
public class Fetch: ObservableObject {
//I need to pass the Binding variable here. something like this?
#Binding var IDTxt: Int
init(){
load()
}
func load() {
//So I can use it here like this:
let url = URL(string: "http://someurl.com/\(IDTxt)")
}
Could someone please advice on the above?
You do not need to pass the Binding value. But you can pass direct value.
public class Fetch: ObservableObject {
var IDTxt: Int
init(id: Int){
self.IDTxt = id
load()
}
func load() {
//So I can use it here like this:
let url = URL(string: "http://someurl.com/\(IDTxt)")
}
}
struct MYView: View {
#ObservedObject var fetch: Fetch
#Binding var IDTxt: Int
init(IDTxt: Binding<Int>) {
self._IDTxt = IDTxt
self.fetch = Fetch(id: IDTxt.wrappedValue)
}
var body: some View{
//some code here
Color.red
}
}
If you want to observe IDTxt text then use #Published in class.
public class Fetch: ObservableObject {
#Published var IDTxt: Int
There's no need for Bindings if the property you are trying to inject into Fetch is coming from a parent view. You can simply inject the value in the init.
Also, if you are creating an ObservableObject inside a View, you need to declare it as #StateObject. #ObservedObject should only be used when injecting the object into the view.
public class Fetch: ObservableObject {
init(id: Int) {
load(id: id)
}
func load(id: Int) {
let url = URL(string: "http://someurl.com/\(id)")
}
struct MYView: View {
#StateObject private var fetch: Fetch
init(id: Int) {
self._fetch = StateObject(wrappedValue: Fetch(id: id))
}
var body: some View{
EmptyView()
}
}

Recommended way to refactor/combine multiple near-identical classes using Swift?

In Swift, how would I combine the following into one class?
class FirstClass: Codable, ObservableObject {
#Published var int: Int
#Published var bool: Bool
#Published var firstObject: FirstType
// plus inits and custom decoding/encoding stuff
}
class SecondClass: Codable, ObservableObject {
#Published var int: Int
#Published var bool: Bool
#Published var secondObject: SecondType
// plus inits and custom decoding/encoding stuff
}
class ThirdClass: Codable, ObservableObject {
#Published var int: Int
#Published var bool: Bool
#Published var thirdObject: ThirdType
// plus inits and custom decoding/encoding stuff
}
(with FirstType, SecondType and ThirdType also being class models that conform to Codable and ObservableObject)
in order to end up with something like this:
class CommonClass: Codable, ObservableObject {
#Published var int: Int
#Published var bool: Bool
#Published var object: CommonType // which could accept FirstType, SecondType or ThirdType
// plus inits and custom decoding/encoding stuff
}
How should I go about doing this? And is there a better way that still works the way I intend?
I'm basically trying to achieve 2 things: 1-avoid repeating code (as in my real-life scenario there are a lot more variables in common than just an int and a bool) and 2-Make downstream code more straightforward by hopefully also minimising the need for separate views.
It is not clear about other code, but for that provided I would use generics, like
class CommonClass<T>: Codable, ObservableObject {
//class CommonClass<T: CommonType>: Codable, ObservableObject { // << as variant
#Published var int: Int
#Published var bool: Bool
#Published var object: T
// plus inits and custom decoding/encoding stuff
}

Can I use a generic type in a state variable in SwiftUI?

Is there a way to use a generic type in a state variable in Swift?
I have a view which presents 3 buttons. Each button loads an array of items of either MyGenericObject<TypeA>, MyGenericObject<TypeB> or MyGenericObject<TypeC> type and the aim is to then present a list of objects from the chosen array.
But XCode throws an error when I write #State var objects: [MyGenericObject<T>]?
The error is there even when working with an individual object instead of an array, as in the following example:
struct ObjectView: View {
// I would like the following to be #State var object: MyGenericObject<T> instead...
#State var object: MyGenericObject<TypeA>?
var body: some View {
Text("\(object?.data.name)")
// because I would then like to do something like this, if possible:
if type(of: object) == MyGenericObject<TypeA> {
...show list of item of this type
} else
if type(of: object) == MyGenericObject<TypeB> {
...show list of item of this type
}
else {
...show list of item of type MyGenericObject<TypeC>
}
// or better yet, use a switch
}
How can I use a generic type in a #State variable in SwiftUI? And is there a better way to achieve what I want?
I stumbled across this question and took a look at the gist. It's a little hard to tell if this is exactly what you want, but it seems like it might get you on the right path:
protocol InstrumentType {
var name : String { get set }
var int : Int { get set }
var bool : Bool { get set }
}
class Instrument<T : InstrumentType>: ObservableObject {
#Published var object: T
init(object: T){
self.object = object
}
}
class Trumpet: NSObject, ObservableObject, InstrumentType {
#Published var name: String
#Published var int: Int
#Published var bool: Bool
init(name: String = "newTrumpet", int: Int = 1, bool: Bool = true){
self.name = name
self.int = int
self.bool = bool
}
}
class Guitar: NSObject, ObservableObject, InstrumentType {
#Published var name: String
#Published var int: Int
#Published var bool: Bool
init(name: String = "newGuitar", int: Int = 2, bool: Bool = true){
self.name = name
self.int = int
self.bool = bool
}
}
class Clarinet: NSObject, ObservableObject, InstrumentType {
#Published var name: String
#Published var int: Int
#Published var bool: Bool
init(name: String = "newClarinet", int: Int = 3, bool: Bool = true){
self.name = name
self.int = int
self.bool = bool
}
}
struct ContentView : View {
var body: some View {
_ContentView(instrument: Instrument(object: Trumpet(name: "testTrumpet")))
}
}
//VIEWS
struct _ContentView<T : InstrumentType>: View {
#StateObject var instrument: Instrument<T>
var body: some View {
InstrumentView(instrument: instrument)
.environmentObject(instrument)
.frame(width: 300, height: 100)
}
}
struct InstrumentView<T : InstrumentType>: View {
#ObservedObject var instrument: Instrument<T>
var body: some View {
VStack {
if type(of: instrument) == Instrument<Trumpet>.self {
Text("Instrument is a trumpet")
Text("\(instrument.object.name)")
Text("\(instrument.object.int)")
Text("\(instrument.object.bool ? "true" : "false")")
} else
if type(of: instrument) == Instrument<Guitar>.self {
Text("Instrument is a guitar")
Text("\(instrument.object.name)")
Text("\(instrument.object.int)")
}
else {
Text("Instrument is a clarinet")
}
Button("Update int") {
instrument.object.int += 1
}
}
}
}
Added a protocol InstrumentType that defines the available properties on each instrument -- that allowed me to get rid of the Metadata, since it was all stored on each instrument anyway
Constrained each generic to InstrumentType
I was a little confused by the #StateObject for each type of instrument -- I assumed that maybe what I did was what you were looking for (one generic #StateObject, but perhaps this is where the answer differs from the intent)
I was able to use environmentObject and object.name in the way you were hoping
Added a Button to show that the #Published properties propagate correctly.