SwiftUI "Cannot use instance member 'numberOfDevice' within property initializer; property initializers run before 'self' is available" error - swiftui

The bolded line (ie var text: String...) gives a "Cannot use instance member 'numberOfDevice' within property initializer; property initializers run before 'self' is available" error. Do I need an init? If so where? Is there a different solution?
struct PairView: View {
var theClass = BluetoothManager()
init() {theClass.viewDidLoad()}
var body: some View {
List {
ForEach(0..<BluetoothManager.peripheralArray.count) { number in //iterates thru 0 to array's count
ConnectionView(numberOfDevice: number) // create a ConnectionView for each number
}
}
}
}
//-------
struct ConnectionView: View {
var numberOfDevice: Int
**var text: String = (BluetoothManager.peripheralArray[numberOfDevice]?.name)!**
// 'name' is a String property of the B.M. class's array's 'numberOfDevice index.'
var body: some View {
ZStack{
RoundedRectangle(cornerRadius: 10.0).fill(Color.blue)
Text(text).foregroundColor(Color.black)
}
}
}

You can use read-only computed property with short-hand.
var text: String {
return (BluetoothManager.peripheralArray[numberOfDevice]?.name)!
}

The error you encountered means you can't use the numberOfDevice variable to instantiate another variable. However, you can use the number you pass to your init method.
Try the following:
struct ConnectionView: View {
var numberOfDevice: Int
var text: String
init(numberOfDevice: Int) {
self.numberOfDevice = numberOfDevice
self.text = (BluetoothManager.peripheralArray[numberOfDevice]?.name)!
}
...
}
Note: I don't recommend force-unwrapping (!). If possible try to provide a default value.
Also, BluetoothManager looks like a type and not like an instance of a class. Make sure you access the peripheralArray property on the valid object and not on the BluetoothManager type.

You can use lazy keyword for that:
lazy var text: String = (BluetoothManager.peripheralArray[numberOfDevice]?.name)!
What is lazy?
lazy means that it will postpone initialization until someone calls the variable and it will not possible if self is not initialized. So you will be sure self is ready before accessing that value.
Why?
When you call numberOfDevice, you are actually calling self.numberOfDevice, but swift is smart enough to let you not explicitly write self keyword.
The issue here is that self is not initialized yet when you are assigning a value to a variable.
So you need to make sure the variable is initialized BEFORE accessing self.

Related

How to update variable in List For Each

struct NotesView: View {
#State var notesArray = [Note]()
public var deleteid: String
var body: some View {
List{
ForEach(notesArray, id: \._id) { notesArray in
NavigationLink(destination: AddNotesView(addNotesViewIdentifier: "updateNote", id: notesArray._id, title: notesArray.title, note: notesArray.note, noteDate: notesArray.date)){
HStack {
Text(notesArray.title)
deleteid = notesArray._id //ERROR - Cannot assign to property: 'self' is immutable
}
}
}
.onDelete(perform: deleteNoteAtIndex)
}
}
func deleteNoteAtIndex(at offsets: IndexSet){ APIFunctions.functions.DeleteNote(id: _id) }
I was expecting the variable "deleteid" to update.
I assumed you can modify any variable by calling that variable and set it equal to a new value.
Like this
First declare variable:
var deleteid: String
next modify variables string valve
deleteid = notesArray._id
A couple of things:
This isn't directly related to your question, but may help you navigation your own code better... When you create a ForEach view to iterate over an array, you should use a different name for the value that represents each element in the iteration. Here, you're using the name notesArray for your array of notes, then creating a second local variable called notesArray for the loop. That variable inside the block will be an instance of Note, so I'd name it note, e.g.:
ForEach(notesArray, id: \._id) { note in
NavigationLink(destination: AddNotesView(addNotesViewIdentifier: note._id, // etc
}
If you want variables to be modifiable inside views, they should be #State variables. This is important due to the way Swift struct lifecycles work, and how the SwiftUI rendering system loads and reloads structs as it works out what has changed.
I'm not entirely sure what deleteid is supposed to represent here, and it's possible you don't need it at all. If you're using the onDelete modifier to implement SwiftUI's native swipe-to-delete system, SwiftUI will give you an IndexSet, which is a collection (usually of just one) of the positions of the item(s) to delete in the array.
From there, you can find the item(s) at each index and then either remove them, or lookup some other value (e.g., their _id attribute) and do some other operation on them.
So the method you might call in onDelete could look something like:
func deleteNoteAtIndex(offsets: IndexSet) {
// get the array objects that the offsets point to
let notes = offsets.map { noteArray[$0] }
for note in notes {
APIFunctions.functions.deleteNote(id: note._id)
}
}

Understanding SwiftUI syntax: What is actually defined inside var body?

Starting to learn SwiftUI I am a bit confused on how Viewss are implemented. View is actually not a type but a protocol which requires a var body of type View.
Problem 1: So the protocol requires it self. Is this not an infinite, recursive loop? Implementation of View requires a var body that implements View which requires a var body... How does this work?
Problem 2: var body is usually implemented as computed property. This is nothing unusual. However, the implementation does not return anything but only "creates" subviews which are not explicitly added to their parent view. Initializing the sub views is enough.
This is not only the case with the body var but with all other views which include other views like HStack, VStack, etc.
struct SomeView: View {
var body: some View {
ViewA()
OtherView()
VStack {
Sub1()
Sub2()
}
}
}
How can this work? Is this valid Swift syntax? I mean a "normal" computed property would look like this, wouldn't it?
var someValue: Int {
let value1 = getValue()
let value2 = SomeOtherValue()
return value1 + value2
}
And not like this:
var someValue: Int {
getValue()
SomeOtherValue()
}

Pass a published property as binding

I have an ObservableObject with a published dictionary of strings to arrays with arrays with Ints:
class MyObservableObject: ObservableObject {
#Published var myDict: [String: [[Int]]]
}
And I want to pass one array of Ints as a Binding from inside the same class to a function of a different struct:
{
...
func someFunc(key: String, index: Int) {
someStruct.func(myDict[key]![index])
}
...
}
I understand that #Published vars can't be passed as Bindings. I'm still hoping that there's any way to achieve this. I also tried storing a reference to the array in the other struct using an inout variable, also without success.
#Published vars can't be passed as Bindings
It is not true - it is possible, via projected value using '$', but you want to pass not a property, but part of value of a property, and this is different thing.
The context is not clear and this someFunc smells not-well :) - I'd say it is needed some refactoring here, but, anyway, technically it is possible to do what you want using dynamically in-place generated binding, like
func someFunc(key: String, index: Int) {
guard myDict[key] != nil else { return }
someStruct.func(Binding<[Int]>(
get: { self.myDict[key]![index] },
set: { self.myDict[key]![index] = $0 }
))
}

Why can't I force unwrap my $string to use as a TextField value?

This gives me the following error:
Cannot force unwrap value of non-optional type 'Binding<Person?>
I don't understand why I can't force unwrap a binding?
struct Person {
var name: String
}
struct ContentView: View {
#State private var person: Person? = Person(name: "Peter")
var body: some View {
if person != nil {
TextField("", text: $person!.name)
}
}
}
When you declare a State variable in your view, you'll get a Binding to the value by adding a leading $ to the variable's name.
So, in your code, you'll have a $person that is binding to an optional Person type. $person is Binding<Person?>
To pass value to TextField you'll need a Binding<String>. you can't force-unwrap $person because it's not an optional value. It's a Binding to an optional type. To access the name field inside the Person struct, you'll need a Binding<Person> instead.
Fortunately, there's a method to get what you want.
By using this initializer, you'll have a Binding<Person>?. Note that now instead of a Binding to an optional, you have an optional Binding.
You should be able to use this new binding like this:
// Binding($person) returns Binding<Person>?
TextField("", text: Binding($person)!.name)
Update:
As #Jessy mentioned in the comments, instead of force-unwrapping the optional binding, we can use map to transform the returned Binding to a TextField
var body: some View {
Binding($person).map {
TextField("", text: $0.name)
}
}

Global variable in swift 3

I have the variable provincias_ws which has the answer of a service, so far everything is fine but I want to know how I make the variable provincias_ws is global since I want to print it in the textFieldDidBeginEditing method
class ViewCtrl: UIViewController, ValidationDelegate, UITextFieldDelegate {
#IBOutlet weak var txt_ciudadU: SearchTextField!
override func viewDidLoad() {
super.viewDidLoad()
opciones_autocompletado()
txt_ciudadU.delegate = self
}
func opciones_autocompletado(){
txt_provinciaU.inlineMode = true
txt_ciudadU.inlineMode = true
Services.objServices.ServProv().then{
data -> Void in
let res = JSON(data)
let status = res["status"].boolValue
if(status){
let provincias_ws = res["data"]["provincias"] //How to make this variable global
}else{
let error = res["error"]["error_data"].stringValue
print(error)
}
SVProgressHUD.dismiss()
UIApplication.shared.endIgnoringInteractionEvents()
}.catch{
error -> Void in
SVProgressHUD.dismiss()
UIApplication.shared.endIgnoringInteractionEvents()
}
}
func textFieldDidBeginEditing(_ textField: UITextField) {
print(provincias_ws)
}
}
Thank you in advance
One can create a "global" variable (meaning it can be access from anywhere in the project) by defining it outside of the class block.
However, it looks like what you want is a class instance variable.
Your text field txt_ciudadU is already an example of an instance variable or property of the class ViewCtrl.
This would be the same, just without #IBOutlet weak.
So would probably be (assuming it's a String):
var provincias_ws: String?
Note the ? declares it as an optional value because it will not be set until it gets set inside your block. You could also initialize it to some default or empty value (e.g. var provincias_ws = "") or you could use ! to indicate you won't be trying to unwrap it when it's nil, although it's better not to rely on that because it is error-prone. What you choose to do depends on your needs.
And you would put that at the same hierarchical level as your text field variable (i.e. right above or below it, although it could go anywhere really, as long as it's not inside a function or another block).
And you would get rid of the current let next to provincias_ws and prepend it with self. i.e.:
self.provincias_ws = res["data"]["provincias"]
The self. is necessary because it's inside a block and the compiler needs that for context.