SwiftUI: Init not initialising arrays - initialization

I'm having a problem initialising my arrays in my view:
struct ProjectList: View
{
#ObservedObject var store: ProjectStore
#Binding var searchText: String
#State private var query: [Project] = []
#State private var indexes: [String] = ["E","F"]
init(store: ProjectStore, searchText: Binding<String>)
{
self.store = store
self._searchText = searchText
self.query = []
self.indexes = ["C","D"]
indexes = ["A","B"] //store.getIndexes(search: searchText.wrappedValue)
print (indexes)
}
}
indexes is being set to ["E","F"] not ["A","B"] in my init routine as I would have expected. What is happening?

Just don't init state when declare (because it is initialised only once and then works during view life-time, ie in body)
#State private var indexes: [String] // << only declare
init(store: ProjectStore, searchText: Binding<String>)
{
...
self._indexes = State(initialValue: ["C","D"]) // initialise !!

#State is property wrapper that means it takes the input of wrapped value and then modifies it, you should never set #State var in init, you have to set #State var directly if you really wanna initialize it in init you have to use init of #State since #State is a struct with syntactic sugar with #. Change
init(store: ProjectStore, searchText: Binding<String>)
{
self.store = store
self._searchText = searchText
self.query = []
self.indexes = ["C","D"]
indexes = ["A","B"] //store.getIndexes(search: searchText.wrappedValue)
print (indexes)
}
to :
init(store: ProjectStore, searchText: Binding<String>)
{
self.store = store
self._searchText = searchText
self.query = []
self.indexes = ["C","D"]
indexes = State(initialValue: ["C","D"]) //changed value
print (indexes)
}

Related

initializing detail view in swiftUI

i'm writing a todo list app, and i'm having trouble initializing the detail view.
in HomeView i use a foreach to set up each line.
struct HomeListView: View {
ForEach(items) { item in
NavigationLink(destination: ItemDetailView(item: item, editMode: true)) {
ItemView(title: item.title, text: item.text, type: item.type, dueDate: item.dueDate)
}
}
in the ItemDetailView i defined a init() to set up title, text and other properties.
struct ItemDetailView: View {
#State var title: String = ""
#State var text: String = ""
#State var type: Int = 0
#State var dueDate: Date = Date()
init(item: ItemEntity, editMode: Bool) {
self.title = item.title
self.text = item.text
self.type = item.type
self.dueDate = item.dueDate
self.item = item
print(item.title)
if self.title.isEmpty {
print(self.title)
}
self.editMode = editMode
print(self.editMode)
}
}
when i run the app, in the console it prints
2
true
4
true
i can see that the init() receice the item and set editMode properly, but i can't figure out why the title, text, type and dueDate properties are set to their default values instead of the values in the item that the init receive.
does it have anything to do with the #State wrapper? how can i set their values to what i want?
Thanks for helping!
does it have anything to do with the #State wrapper?
Yes, it does. The state should be initialised via init in the next way (example for one)
struct ItemDetailView: View {
#State var title: String // << no, default value
...
init(item: ItemEntity, editMode: Bool) {
self._title = State<String>(initialValue: item.title) // << explicitly !!
...

I'm trying to write some test with firstore and swiftui, I can create and read, but now i don't know how to list unique data,

You can see I have two fields in this document, "ing01", and "nameReceta" every document in this collection has the same name fields, in the field "ing01" I have "Pimienta" in description, my app it allows more documents whit "Pimienta" in "ing01" and this is fine, but when I want to list it I need list only one "Pimienta", how can I remove the others?
I have this data model
struct ModeloRecetasIng : Identifiable, Hashable, Equatable {
var id: String
var nameReceta: String
var ing01: String
}
and here I get the data, and it works fine.
class ingredientesAdd : ObservableObject {
// #Published var datosNoDupl = [DataNoDuplicates]()
#Published var datas = [ModeloRecetasIng]()
init() {
// Borra el cache
let settings = FirestoreSettings()
settings.isPersistenceEnabled = false
let db = Firestore.firestore()
db.settings = settings
// Borra el cache
db.collection("DespensaIng01").getDocuments { (snap, err) in
if err != nil {
print((err?.localizedDescription)!)
return
}
for i in snap!.documents
{
let id = i.documentID
let nameReceta = i.get("nameReceta") as! String
let ing01 = i.get("ing01") as! String
//
self.datas.append(ModeloRecetasIng(id: id, nameReceta: nameReceta, ing01: ing01))
}
// I'm trying to use set but it doesn't work
let uniqueUnordered = Array(Set(self.datas))
self.datas = uniqueUnordered
print(self.datas)
}
}
}
When I get my Print I see this
Pimienta
Pimienta
Zanahoria
Zanahoria
Zanahoria
and I only want this
Pimienta
Zanahoria
Solved.. just using a custom ID in firestore, and no more duplicates and now i need to sum de quantity only

Manipulate list of Flow generic type with optional id

I'd like to have a function that takes an array with objects that could have id property on them. The function will remove the first object that has id and it matches function's second argument.
Here is my stub:
// #flow
function removeFromArrayByObjectId<T: {id?: string}>(array: Array<T>, id: any): Array<T> {
for (let i = 0; i < array.length; i++) {
if (array[i].id && array[i].id === id) {
array.splice(i, 1);
break;
}
}
return array;
}
export {
removeFromArrayByObjectId,
};
When I'm passing arr defined as (below) I'm getting
type Obj = { id: string, value: string };
const arr: Array<Obj> = [obj1, obj2];
Cannot call removeFromArrayByObjectId with array literal bound to array because string [1] is incompatible with undefined [2] in property id of array element.Flow(InferError)
However, when I remove ? from id? it works fine. I'm expecting that some arrays will have elements without the id so would like it to be optional. Any suggestions?
The error is very similar to the one mentioned in Why can't `{ a: string }` flow into `{ a?: string }`. Essentially, Flow does not know that removeFromArrayByObjectId does not manipulate the elements of array (e.g., delete a property). You'll want to mark T as "read-only" so Flow knows that the function will not do so.
function removeFromArrayByObjectId<T: $ReadOnly<{id?: string}>>(array: Array<T>, id: any): Array<T> {
for (let i = 0; i < array.length; i++) {
if (array[i].id && array[i].id === id) {
array.splice(i, 1);
break;
}
}
return array;
}
type Obj = { id: string, value: string };
declare var obj1: Obj;
declare var obj2: Obj;
const arr: Array<Obj> = [obj1, obj2];
const modified_arr: Array<Obj> = removeFromArrayByObjectId(arr);
Try Flow

type check dynamic properties flowtype

I am trying to add dynamic properties to an object and have flow type check them:
my function would be like this:
function defineStuff(obj:MyType, keys:string[]):??? {
keys.forEach(function(key) {
Object.defineProperty(obj, key, {get:function(){....}});
obj["Add"+key] = function(value) {....};
obj["Remove"+key] = function(value) {....};
}
return obj;
}
I would like to be able to do stuff like this;
var obj : MyType = fetchMyObj();
defineStuff(obj, ["Thing", "OtherThing"]);
var thing = obj.Thing;
obj.AddOtherThing(10);
all dynamic properties type would be number
is there a syntax for doing this in flow? (i.e. how to fill the ???)
This should work for dictionaries.
type MyType = {[key: string]: number};
// ...
function defineStuff(obj: MyType, keys:string[]): MyType {
keys.forEach(function(key) {
Object.defineProperty(obj, key, {get:function(){....}});
// you can't have functions here, because you say that all values should be numbers
// obj["Add"+key] = function(value) {....};
// obj["Remove"+key] = function(value) {....};
}
return obj;
}
// ...
See docs

I cannot undetstand how works "isSubtypeOf" in Dart mirrors

According to this test, I can not invoke the method "method" with an argument "list" because argument type is not compatible with the type of the method parameter.
Where I am wrong in my test?
import "dart:mirrors";
void main() {
var list = new List<String>();
var listMirror = reflectClass(list.runtimeType);
// Is "List<String>" subtype of "List<String>"?
print(listMirror.isSubtypeOf(listMirror));
// Method with parameter "List<String>"
var method = (List<String> list) {};
var closure = reflect(method) as ClosureMirror;
var function = closure.function;
var parameter = function.parameters.first;
// Is "List<String>" subtype of "List<String>"?
print(parameter.type.isSubtypeOf(listMirror));
print(listMirror.isSubtypeOf(parameter.type));
// Invoke method with arg: "List<String>" on param "List<String>"
method(list);
}
Output:
true
false
false
P.S.
Maybe I not understand something but it still not works.
import "dart:mirrors";
void main() {
var stringMirror = reflectClass(String);
// Query "List<int> get codeUnits"
MethodMirror method = stringMirror.declarations.values
.where((e) => e.simpleName == #codeUnits).first;
// List<int>
var returnType = method.returnType;
print(returnType);
// List
var baseType = reflectClass(List);
print(baseType);
// List<int> is List
print(returnType.isSubtypeOf(baseType));
}
Output:
ClassMirror on 'List'
ClassMirror on 'List'
false
This line is the culprit:
var listMirror = reflectClass(list.runtimeType);
it returns
ClassMirror on '_GrowableList'
if you use
var listMirror = reflectType(List);
it should work.
What you can when you need to get the type from a value at runtime
var listMirror = reflect(list).type.superinterfaces;
...
listMirror.forEach((e) => print(parameter.type.isSubtypeOf(e)));
depending on the situation you may need to make additional checks with
reflect(list).type.mixin;
If one of these checks is true it is a subtype.

Resources