SwiftUI Calling functions from other class - swift

I am having a little trouble here: I have a class
class TempC {
func GetData(){
//do stuff
}
}
And in ContentView I want to call the function, but I can't manage to do it, I am getting errors...
struct ContentView: View {
var variable : TempC
variable.GetData()
var body: some View {
Text("Hello World")
}
}
Or in any other method. How can I call an external function now?
PS: The error that I get are on the line with variable.GetData() which are:
Consecutive declarations on a line must be separated by ";"
Expected "("in argument list of cantons declaration
Expected "{"in body of function declaration
Expected 'func' keyword in instance method declaration
Invalid redeclaration of 'variable()'
It's like it is expecting to create a new function not to get the one that is already existing.

Depending on what your going to do in that call there are options, ex:
Option 1
struct ContentView: View {
let variable = TempC()
init() {
variable.GetData()
}
var body: some View {
Text("Hello World")
}
}
Option 2
struct ContentView: View {
let variable = TempC()
var body: some View {
Text("Hello World")
.onAppear {
self.variable.GetData()
}
}
}
Similarly you can call it in .onTapGesture or any other, pass reference to your class instance during initialising, etc.

Related

Broken Binding and ObservedObject in SwiftUI

I have a structure where at the end of AddTaskView_Preview: ..... there is { task in } in AddTaskView, exactly like this:
struct AddTaskView_Previews: PreviewProvider {
static var previews: some View {
AddTaskView{task in }, formVM: ToDoFormViewModel())
.environmentObject(DataStore())
}
}
This type does not allow the use of #Binding or #ObservedObject - formVM: ToDoFormViewModel() in my application, because the two values Binding and ObservedObject work for me in other views only with parentheses of type () and not { }!
What about that please? I normally have this code in my views:
ContentView(viewRouter: ViewRouter(), formVM: ToDoFormViewModel()) , but it doesn't fit inside the square brackets { } with { task in } in the above example.
How should I consume my ObservedObject in formVM: ToDoFormViewModel() with those { } ?

Pass struct as generic type and access that generic types properties

I'm working on a Swift package where an option will be to pass in a generic type (Person) and then that GenericStruct can use properties on that type passed in. The issue obviously is that the generic T has no idea what's being passed in. Is there a way to define the property to access on the generic type T?
struct Person: Equatable {
var name: String
var height: Double
}
struct ContentView: View {
#State private var james = Person(name: "James", height: 175.0)
var body: some View {
GenericStruct(person: $james)
}
}
struct GenericStruct<T: Equatable>: View {
#Binding var person: T
var body: some View {
Text(person.name)) // This line.
}
}
I want to specifically pass in which property to access on Person when passing it to GenericStruct. The property won't always be name it could be anything I define within Person. For example:
GenericStruct(person: $james, use: Person.name)
Isn't this exactly a protocol?
protocol NameProviding {
var name: String { get }
}
struct GenericStruct<T: Equatable & NameProviding>: View { ... }
Or is there a more subtle part of the question?
The best way to do this is to pass a String Binding:
struct GenericStruct: View {
#Binding var text: String
var body: some View {
Text(text)) // This line.
}
}
GenericStruct(text: $james.name)
But it is possible with key paths. It's just a bit more awkward and less flexible in this particular case:
// Should be the syntax, but I haven't double-checked it.
struct GenericStruct<T: Equatable>: View {
#Binding var person: T
var use: KeyPath<T, String>
var body: some View {
Text(person[keyPath: use]))
}
}
GenericStruct(person: $james, use: \.name)

SwiftUI prevent onReceive from firing on load

Is there any way to prevent onReceive from firing when the view initially loads when the variable I'm trying to receive is a #Published property of an #EnvironmentObject?
Here's the view:
struct ContentView: View {
#EnvironmentObject var appState: AppState
var body: some View {
VStack {
Text(appState.test)
.padding()
}
.onAppear() {
appState.test = "World"
}
.onReceive(appState.$test) { test in
print("Hello from onReceive: \(test)")
}
}
}
Here's the environment object:
public class AppState: ObservableObject {
#Published public var test = "hello"
}
And output:
Hello from onReceive: hello
Hello from onReceive: World
I'm guessing the first one is fired when the environment object is injected. Is there any way to prevent this (other than some hackish bool that I set in the onAppear)? Ideally I'd like to only see "Hello from onReceive: World" This is just a simple example. On my actual app I am fetching data from a service in onAppear, in my environment object I have an error state that when cleared, I want it to fire the onReceive do some other logic then refetch.
It is publisher, so you can use any combine operators. In this case .dropFirst solves your task:
.onReceive(appState.$test.dropFirst()) { test in
print("Hello from onReceive: \(test)")
}

Swift - How to access #Published var from func outside of view?

I'm trying to remove the logic from the view, while keeping the benefits of SwiftUI. Idea 1 works but it makes use of an extra variable than I would want to. Idea 2 gives error: Property wrappers are not yet supported on local properties. The view should return "bar". What is the best way of making this work? Many thanks.
import Combine
import Foundation
import SwiftUI
// Model
enum Model: String, RawRepresentable {
case foo = "foo"
case bar = "bar"
}
// State
var data1: String = Model.foo.rawValue
class State: ObservableObject {
#Published internal var data2: String = data1
}
// Logic
func logic() {
// Idea 1: OK
//data1 = Model.bar.rawValue
//print(State().data2)
// Idea 2: Error Property wrappers are not yet supported on local properties
#EnvironmentObject private var state: State
state.data2 = Model.bar.rawValue
print(state.data2)
}
// View
struct bar: View {
#EnvironmentObject private var state: State
internal var body: some View {
logic()
return Text(verbatim: self.state.data2)
}
}
If you want a function to have access to a view's state, pass the state:
func logic(state: State) {
state.data2 = Model.bar.rawValue
print(state.data2)
}
But what you've done here is an infinite loop. Modifying a view's state causes the view to be re-rendered. So every time the view is rendered, it modifies its state and forces it to be rendered again. That will never resolve. What you may mean here is to change the state when the view first appears, in which case you'd call logic this way:
struct Bar: View {
#EnvironmentObject private var state: State
internal var body: some View {
Text(verbatim: state.data2)
.onAppear{ logic(state: self.state) }
}
}

Pass by reference primitive types in Swift

I am trying to have a model which shares a value with the objects it contains. In the below example I have a Model object, a, which contains a Struct, b. I would like them both to have access to a Bool which is referenced by variables a and aa. In order to do this, I have wrapped a Bool in a Box structure.
struct Box<Value> {
var value:Value
}
struct Model {
var a: Box<Bool>
var b:B
struct B {
var aa:Box<Bool>
}
}
struct TestView : View {
#State var model:Model
var body: some View {
VStack {
Text(model.a.value.description)
Text(model.b.aa.value.description)
Button(action: {
self.model.a.value.toggle()
}) {
Text("Button")
}
}
}
}
However, when I run the code as such:
let box = Box<Bool>(value: true)
let contentView = TestView(model: Model(a: box, b: Model.B(aa: box)))
The value in model.b.aa is not updated as I would expect. Is there a different way I should be doing this?
For your purpose, you'd need to wrap the primitive type in a class, and share an instance of that class. There is no raw reference or pointer that can be shared between the structs.