Core Data #FetchRequest SwiftUI - core-data

I am trying to use a fetch request in a SwiftUI Charts package/CocoaPod. My issue is that when I use a ForEach to iterate over the Attribute (ACFTScores.totalScore) it populates one value per chart... I know this is because the BarChart is in the body of the ForEach but I don't know how to grab the range of the fetch and make it work with the SwiftUI Charts.
struct DashboardACFT: View {
#FetchRequest(entity: ACFTScores.entity(), sortDescriptors: [NSSortDescriptor(keyPath: \ACFTScores.totalScore, ascending: true)]) var acftScores: FetchedResults<ACFTScores>
var body: some View {
VStack {
ForEach(acftScores, id: \.id) { score in
BarChartView(data: [Int(score.totalScore)], title: "Title", legend: "Legendary")
}
}
}
}

I haven't worked with charts but you need to map your fetch request into an array containing the values you want to use in your chart. Something like this
var body: some View {
VStack {
BarChartView(data: acftScores.map {$0.totalScore}, title: "Title", legend: "Legendary")
}
}
}
Since you for some reason seems to need to convert the property to Int using compactMap might be better.
acftScores.compactMap {Int($0.totalScore)}

Related

Coredata Fetchrequest Foreach loop with subsections of data and sort methods

I have a data model entity called Object laid out like so:
name: String
createdDate: Date()
updatedDate: Date()
My fetch request:
#FetchRequest(sortDescriptors: [SortDescriptor(\.updatedDate, order: .reverse)]) var objects: FetchedResults<Object>
I want to have my list of objects sorted with the most recently updated (keyed off updatedDate) at the top. I chose this sort method, because when combined with withAnimation I get a real nice animation when items lower in the list are updated and move to the top.
When the user creates a new object however, it goes to the bottom of the list because this new object doesn't have a value for updatedDate (intentional because my users tell the app when an object is updated). This behaviour makes sense but is undesired.
My current ObjectListView looks like this (simplified to focus on my question):
struct intervalsExist: View {
#Environment(\.managedObjectContext) private var viewContext
#FetchRequest(sortDescriptors: [SortDescriptor(\.updatedDate, order: .reverse)]) var objects: FetchedResults<Object>
var body: some View {
List {
ForEach(objects) { object in
NavigationLink {
ObjectDetailView(object: object)
} label: {
Text(object.name ?? "Unknown name")
}
.swipeActions(edge: .leading) {
withAnimation {
// Update `updatedDate` with current Date()
}
}
}
}
}
}
What I would like is for my list of objects to be presented as such:
---
Object with no updatedDate, most recent
Object with no updatedDate
Object with no updatedDate
Object with updatedDate, most recent
Object with updatedDate
Object with updatedDate
---
Where objects with updatedDate will reorder themselves to the the newest at the top when they are updated.
I tried using 2 sort methods in my fetch request, like so:
#FetchRequest(sortDescriptors: [
SortDescriptor(\.createdDate, order: .reverse),
SortDescriptor(\.updateDate, order: .reverse),
]) var objects: FetchedResults<Object>
While this gets me the ordering I am looking for above, items no longer re-order themselves within the list which is a key part of what I'm trying to build.

SwiftUI reading a Core Data entity into a []

I have a Core Data entity with the attributes "latitude" and "longitude," and I want to read its data into a [] for MapKit's MapAnnotation. I understand how to use ForEach to iterate through an entity to create a View, as seen in the sample below, but I don't understand how I read data into a [].
ForEach(stores.reversed()) { store in
HStack {
Text("\(store.name ?? "")")
Spacer()
Text("\(store.latitude, specifier: "%.3f"),")
Text("\(store.longitude, specifier: "%.3f")")
}
}
"locations" is a variable containing coordinates for MapAnnotation.
"stores" is a variable containing the fetched entity data.
"Location" is an identifiable for the CLLocationCoordinate2D format.
Below is what I've attempted, but clearly it is wrong. How do I iterate through "stores" correctly?
#State var locations: [Location] = [
for store in stores {
Location(coordinate: .init(latitude: store.latitude, longitude: store.longitude))
}
]
A for loop inside of array brackets does nothing because it doesn’t return anything. I think what you're looking for is .map
#State var locations: [Location] = stores.map { store in
Location(
coordinate: .init(
latitude: store.latitude,
longitude: store.longitude
)
)
}

ForEach Identifiable Struct Object Properties Into List Ordered By categoryName

I feel like I'm missing something obvious here, but I've been stuck on this for a couple of days and just can't seem to find the answer.
1.) I'm using a separate swift file with an Identifiable Struct that has an object with 2 of the Struct properties, name & categoryName. (side note, I'm using var instead of let in the object because the rows can't be rearranged with the .onMove modifier as a constant)
//STRUCT
struct Item: Identifiable {
var id = UUID()
var name: String
var categoryName: String
}
//OBJECT
var items : [Item] = [
//CLOTHING
Item(name: "Hats", categoryName: "Clothing"),
Item(name: "Shirts", categoryName: "Clothing"),
Item(name: "Pants", categoryName: "Clothing"),
//Electronics
Item(name: "Macbook", categoryName: "Electronics"),
Item(name: "Macbook Adapter", categoryName: "Electronics"),
Item(name: "iPhone", categoryName: "Electronics"),
]
2.) In a swiftui file I have this code to build the list, using a nested ForEach loop to pull the categoryName, add it to the Section header, then another to loop out the items.
//List code
NavigationView {
List {
ForEach(items) { currentItem in
Section(header: Text(currentItem.categoryName)){
ForEach(items) { currentItem in
NavigationLink(destination: ItemDetail(itemData: currentItem)){ ItemRow(item: currentItem)
}
}
Unfortunately what I get is a laughable result.
I get my categoryName in the section header and I get my items listed below it. Actually, I get ALL of the items listed below it, regardless of category. Then in a very confusing fashion the sections will print out exactly as many times as the rows in my object array.
In this instance I have 6, so I get 6 rows. Yet, of the 2 categoryName strings "Clothing" and "Electronics", they'll print out 3 times each.
It feels like there's a simple way to do "for each categoryName in items.categoryName add a title to the section and list the corresponding name" - but I'm not cracking this one.
Hoping someone here can point out what I'm doing wrong.
You have flat array and just iterate though it several times, so result is also flat multiplied several times.
Ok, for model you selected, ie items, the result you tried to accomplish can be reached with the following...
List {
ForEach(Array(Set(items.compactMap{ $0[keyPath: \.categoryName] })), id: \.self) { category in
Section(header: Text(category)) {
ForEach(items.filter { $0.categoryName == category }) { currentItem in
NavigationLink(destination: Text("ItemDetail(itemData: currentItem)")){ Text("\(currentItem.name)") }
}
}
}
}
.listStyle(GroupedListStyle())
However I would select different model for items, ie. dictionary as [categoryName: Item]
You can group your array by category:
let sections = Dictionary(grouping: items) { $0.categoryName }
Then you can use it like:
var keys: [String] = { sections.map {$0.key} }
var body: some View {
List {
ForEach(keys, id: \.self) { section in
Section(header: Text(section)){
ForEach(self.sections[section] ?? []) { currentItem in
Text(currentItem.name)
}
}
}
}
}
Note that I have simplified your code to run on my machine but changes are not effecting the answer

Define advanced criteria for join tables in smartclient grid component for Openbravo ERP

I'm newbie in a OpenBravo ERP system, so I'm sorry if I posted an obvious question.
I have created a custom MDI view in Openbravo ERP, added some input field for filtering product's name in Smarclient's listgrid which contains Orders. I have created a Order datasource and bind it to the Smartclient grid. What I want to achieve is to filter a Order list in the grid by product name which is added in the input text field. I tried with several approaches but without success.
So I came to question is it possible to add join table in AdvancedCriteria in the grid's fetchData function?
Here is a snippet of my code:
isc.ELB_MainView.addProperties({
form: null,
grid: null,
initWidget: function () {
this.grid = isc.OBGrid.create({
setDataSource: function (ds, fields) {
var selectedFields = [],
fs = OB.Constants.FIELDSEPARATOR,
ident = OB.Constants.IDENTIFIER,
fieldNames = ['documentNo', 'documentStatus', 'orderDate', 'accountingDate', 'businessPartner'+fs+ident, 'currency'+fs+ident],
i, j;
for(i = 0; i < fieldNames.length; i++) {
for(j in ds.fields) {
if(ds.fields.hasOwnProperty(j) && j === fieldNames[i]) {
selectedFields.push(ds.fields[j]);
}
}
}
this.Super('setDataSource', [ds, selectedFields]);
this.fetchData();
}
});
this.form = isc.DynamicForm.create({
fields: [{name : "productName",
title: "Product Name",
type : "text",
width: '100%',
change: function(form, item, value, oldValue){
form.main.grid.invalidateCache();
form.main.grid.fetchData({
_constructor: 'AdvancedCriteria',
operator: 'and',
criteria: {
'fieldName': 'orderLineList.product.name',
'operator': 'equals',
'value': value
}
});
this.Super('change', arguments);
}}],
main: this
});
OB.Datasource.get('Order', this.grid, null, true);
this.addMember(this.form);
this.addMember(this.grid);
this.Super("initWidget", arguments);
}
});
Tnx in advance.
Similar question is posted on SmartClient Forum about Joining tables for listgrid datasource.
Here is my solution that will definitely solve your problem:
Create a view that is created by joining tables required for grid and use it as data source

YUI-grid: re load data

Hi guys
I am try to research Yahoo lib- grid. I created a button to display grid. But when i click button N times -> it is displayed N grids. I only want display one grid and after click button it will get data from server again. My code like below:
Please help me., Thank you.
YUI({ filter: 'raw' }).use("jsonp", "node",function (Y) {
function handleJSONP(response) {
// Y.one("#out").setContent(Y.Lang.sub(template, response.user));
YUI().use("datatable-base", function(Y) {
var cols = [
{ key: "id", label: "Transaction No", abbr: "id"},
{ key: "userName", label: "User Name", abbr: "userName"},
{ key: "storeName", label: "StoreName", abbr: "storeName"}
],
data = response.Root,
dt = new Y.DataTable.Base({
columnset: cols,
recordset: data,
summary: "Price sheet for inventory parts",
caption: "These columns have labels and abbrs"
}).render("#example");
});
}
Y.one("#demo_btn").on("click", function (e) {
var url = "server.jsp"+ "?callback={callback}";
Y.jsonp(url, handleJSONP);
});
});
You should be using the DataSource utility to retrieve the data via JSONP. This will allow you to then reload the data via something like
dt.datasource.load();
See the docs for details: DataTable + DataSource.Get + JSON Data
Your handler needs to check if you've already created the table.
var dt;
if (dt === null) {
dt = new Y.DataTable.Base // etc.
}

Resources