SwiftUI, Xcode 13, Question about .filter'ing CoreData - core-data

#FetchRequest(
entity: Client.entity(),
sortDescriptors: [])
private var clients: FetchedResults<Client>
(...)
var searchResults: FetchedResults<Client> {
if searchText.isEmpty {
return clients
} else {
return clients.filter({$0.name!.contains(searchText)}) // Error here!
}
}
(...)
ForEach(searchResults, id: \.self) { client in
(...)
Error
Cannot convert return expression of type '[FetchedResults<Client>.Element]' (aka 'Array<Client>') to return type 'FetchedResults<Client>'
Hi,
I'm not sure how my logic is wrong. Could someone please tell me how to fix searchResults?
Also, is this the more efficient way to filter results or should I filter in the ForEach()? It seems pretty slow when I put the filter in ForEach()

While I know you have a code fix, and it is the correct one, I wanted to answer the question for posterity as I have run into this myself. The logic is wrong simply because your searchResults var is of type FetchedResults<Client>, but a filter returns an Array. So, the error message is telling you exactly the problem. You are trying to return an [Client] as a FetchedResults<Client> so you have a type mismatch.
You have two solutions:
You can filter in the fetch request, which is how you solved it per #Larme suggestion. This is especially helpful if you only need the filtered results in the UI and/or you have a lot of results.
You can filter when you use your fetched results. This is useful when you want your user to be able to determine what is filtered out by their own selections, but you don't know what filtering they will want ahead of time or that regardless of the filtering you are doing, you may need the whole FetchRequest later.

Related

How to chain express-validator based on query values?

I am trying to find a solution to chain conditions based on the query values passed to the route.
Task1:
// if value for query A = noIdNeeeded, then i do not need to search for a second queryB
/endpoint?queryA=noIdNeeded
Task2:
// if value for query A = idNeeded, then i need to ensure second queryB exists
/endpoint?queryA=idNeeded&queryB=SomeId
I am having trouble with the writing parameter for Task 2.
For task 1 i have use this logic and works fine [query('page').exists().notEmpty().isIn(seoPageTypes),]
So far I have seen there is an if clause that we can use probably (link) however implementing this has been a challenge due to lack of examples and no prior experience.
If anyone can guide on how to do this correctly or any hint is much appreciated.
Make sure you have a new version of express-validator installed.
The following should do your job.
query('queryA').exists().notEmpty().isIn(seoPageTypes),
query('queryB')
.if(query('queryA').equals('idNeeded'))
.exists().notEmpty().withMessage('queryB must exist'),
Another approach is to use a custom validator
query('queryA').exists().notEmpty().isIn(seoPageTypes)
.custom((value, {req}) => {
if (value === "idNeeded" && !req.query.queryB) {
throw new Error('queryB must exist');
}
return true;
}),
Use what suits you more :)

Index Beyond Bounds / EXC_BAD_ACCESS upon Core Data Delete SwiftUI

I'm getting a Index Beyond Bounds error and a Thread 1: EXC_BAD_ACCESS (code=EXC_I386_GPFLT) when trying to delete a Core Data index in SwiftUI.
Basically, I have a Core Data entity (Dates), containing only a date attribute (Constraint - String). This has a one-to-many relationship with my Records Entity. I am trying to display a list of all date's I have. Displaying is fine, but upon trying to delete it, my app crashes.
My View currently looks as following:
import SwiftUI
struct Settings: View {
#Environment(\.managedObjectContext) var managedObjectContext
#FetchRequest(entity: Dates.entity(), sortDescriptors: []) var dates: FetchedResults<Dates>
var body: some View {
VStack {
List{
ForEach(dates, id: \.self) { day in
Text("\(day.wrappedDate)")
}.onDelete { (indexSet) in
let dateToDelete = self.dates[indexSet.first!]
self.managedObjectContext.delete(dateToDelete)
do {
try self.managedObjectContext.save()
} catch {
print(error)
}
}
}
}
}
}
I've broken my view down to the bare minimum to see if that would help, but unfortunately not.
When trying to delete. The error I get in the output is:
2020-04-29 16:08:23.980755+0300 TESTTEST[28270:2245700] [General] *** -[__NSArray0 objectAtIndex:]: index 0 beyond bounds for empty NSArray
If I have say 9 dates, it would say index 8 beyond bounds [0 .. 7], so it's not necessarily related to an empty Array.
Further output is:
=== AttributeGraph: cycle detected through attribute X ===
a bunch of times, followed by:
Thread 1: EXC_BAD_ACCESS (code=EXC_I386_GPFLT)
on my AppDelegate.
Could the issue be in generating the view - rather than the delete?
Please be aware that I'm a self-taught, absolute Noob when it comes to coding, so I might be missing something obvious here. Any help on getting to the answer myself in the form of instructions would also be greatly appreciated (so I can learn how to fix this).
EDIT:
I think I found out what's causing the issue. In another view I'm also generating a list of all dates , where I apply an index on Dates. Will amend code now to see if this fixes it....
TBC!
In a separate view I was calling a list of dates in the following way:
ForEach(0 ..< self.dates.count, id: \.self) { index in
Text("\(self.dates[index].date)")
}
Deleting one of the Date entities, would mess up the indices presented in this view. Changing this structure to the below fixed the issue:
ForEach(self.dates, id: \.self) { day in
Text("\(day.date)")
}

How do I transform SwiftUI fetch request results based on related objects?

I am building a SwiftUI list where I need a dynamic predicate. The approach is discussed here: https://www.hackingwithswift.com/books/ios-swiftui/dynamically-filtering-fetchrequest-with-swiftui
Here is my code so far:
struct SomeView: View {
var collection: Collection
var messages: FetchRequest<Message>
init(collection: Collection) {
let predicate : NSPredicate = NSPredicate(format: "collection = %#", collection)
self.collection = collection
self.messages = FetchRequest<Message>(entity: Message.entity(), sortDescriptors: [NSSortDescriptor(key: "date", ascending: true)], predicate: predicate)
}
var body: some View {
List(messages.wrappedValue, id: \.uniqueIdentifier) { message in
// construct the UI
}
}
}
So far so good.
What I can’t figure out how to do: I need to transform the messages elements based on some other messages in the results (let’s say based on previous message for simplicity). messages[0] should look a particular way. messages[1] look depends on messages[0]. messages[2] depends on messages[1] and so on. I cannot precompute this, since it may vary across time. It should be computed in the context of this specific fetch request/result.
I could express this as some transient computed property on the Message object, which the view code could then use to branch out. I could have a function where I give a particular message and the array of messages, the function looks up the message and other messages and sets the state of a given message based on that. However, SwiftUI limits what I can do in View code, I can’t execute functions this way.
I can run map or flatmap where I access the wrappedValue, but those don’t let me access other elements of the collection to make decisions (I think?).
How would I run this kind of transformation in this context?
If I correctly understood your description (and taking into account that FetchedResults is a RandomAccessCollection) I would go with the following approach
var body: some View {
List(messages.wrappedValue, id: \.uniqueIdentifier) { message in
rowView(for: message, from: messages.wrappedValue)
}
}
func rowView(for message: Message, from result: FetchedResults<Message>) -> some View {
// having .starIndex, .endIndex, .position, etc. do any dependent calculations here
// and return corresponding View
}

CouchDB Views - emit Keys as JSON & filter based on any attribute

Consider following Employee document structure
{
"_id":...,
"rev":...,
"type":"Employee",
"fName":...,
"lName":...,
"designation":...,
"department":...,
"reportingTo":...,
"isActive":..,
more attributes
more attributes
}
And following map function in a View named "Employee"
function(doc) {
if (doc.type=="Employee") {
emit({
"EID":doc._id,
"FirstName":doc.fName,
"LastName":doc.lName,
"Designation":doc.designation,
"Department":doc.department,
"ReportingTo":doc.reportingTo,
"Active":doc.isActive
},
null
);
}
};
I want to query this view based on any combination & order of emitted attributes ( a query may include few random attributes may be like duck typing ). Is it possible? If so kindly let me know some samples or links.
Thanks
I've ran into the same problem a few times; you can, but you'll have to index each by itself (not all in one hash like you've done). But you could through the whole thing in the value for emit. It can be fairly inefficient, but gets the job done. (See this link: View Snippets)
I.e.:
function(doc) {
if (doc.type=="Employee") {
emit(["EID",doc.values.EID], doc.values);
emit(["FirstName", doc.fName], doc.values);
emit(["LastName", doc.lName], doc.values);
emit(["Designation", doc.designation], doc.values);
emit(["Department", doc.department], doc.values);
emit(["ReportingTo", doc.reportingTo], doc.values);
emit(["Active", doc.isActive], doc.values);
}
}
This puts all "EID" things in the same part of the tree, etc., I'm not sure if that is good or bad for you.
If you start needing a lot of functionality with [field name:]value searches, its probably worth it to move towards a Lucene-CouchDB setup. Several exist, but are a little immature.

How to display arbitrary, schemaless data in HTML with node.js / mongodb

I'm using mongodb to store application error logs as json documents. I want to be able to format the error logs as HTML rather than returning the plain json to the browser. The logs are properly schemaless - they could change at any time, so it's no use trying to do this (in Jade):
- var items = jsonResults
- each item in items
h3 Server alias: #{item.ServerAlias}
p UUID: #{item.UUID}
p Stack trace: #{item.StackTrace}
h3 Session: #{item.Session}
p URL token: #{item.Session.UrlToken}
p Session messages: #{item.Session.SessionMessages}
as I don't know what's actually going to be in the JSON structure ahead of time. What I want is surely possible, though? Everything I'm reading says that the schema isn't enforced by the database but that your view code will outline your schema anyway - but we've got hundreds of possible fields that could be removed or added at any time so managing the views in this way is fairly unmanageable.
What am I missing? Am I making the wrong assumptions about the technology? Going at this the wrong way?
Edited with extra info following comments:
The json docs look something like this
{
"ServerAlias":"GBIZ-WEB",
"Session":{
"urltoken":"CFID=10989&CFTOKEN=f07fe950-53926E3B-F33A-093D-3FCEFB&jsessionid=84303d29a229d1",
"captcha":{
},
"sessionmessages":{
},
"sessionid":"84197a667053f63433672873j377e7d379101"
},
"UUID":"53934LBB-DB8F-79T6-C03937JD84HB864A338",
"Template":"\/home\/vagrant\/dev\/websites\/g-bis\/code\/webroot\/page\/home\/home.cfm, line 3",
"Error":{
"GeneratedContent":"",
"Mailto":"",
"RootCause":{
"Message":"Unknown tag: cfincflude.",
"tagName":"cfincflude",
"TagContext":[
{
"RAW_TRACE":"\tat cfhome2ecfm1296628853.runPage(\/home\/vagrant\/dev\/websites\/nig-bis\/code\/webroot\/page\/home\/home.cfm:3)",
"ID":"CFINCLUDE",
"TEMPLATE":"\/home\/vagrant\/dev\/websites\/nig-bis\/code\/webroot\/page\/home\/home.cfm",
"LINE":3,
"TYPE":"CFML",
"COLUMN":0
},
{
"RAW_TRACE":"\tat cfdisplay2ecfm1093821753.runPage(\/home\/vagrant\/dev\/websites\/nig-bis\/code\/webroot\/page\/display.cfm:6)",
"ID":"CFINCLUDE",
"TEMPLATE":"\/home\/vagrant\/dev\/websites\/nig-bis\/code\/webroot\/page\/display.cfm",
"LINE":6,
"TYPE":"CFML",
"COLUMN":0
}
]
}
}
... etc, but is likely to change depending on what the individual project that generates the log is configured to trigger.
What I want to end up with is a formatted HTML page with headers for each parent and the children listed below, iterating right through the data structure. The Jade sample above is effectively what we need to output, but without hard-coding that in the view.
Mike's analysis in the comments of the problem being that of creating a table-like structure from a bunch of collections that haven't really got a lot in common is bang-on. The data is relational, but only within individual documents - so hard-coding the schema into anything is virtually impossible as it requires you to know what the data structure looks like first.
The basic idea is what #Gates VP described. I use underscore.js to iterate through the arrays/objects.
function formatLog(obj){
var log = "";
_.each(obj, function(val, key){
if(typeof(val) === "object" || typeof(val) === "array"){
// if we have a new list
log += "<ul>";
log += formatLog(val);
log += "</ul>";
}
else{
// if we are at an endpoint
log += "<li>";
log += (key + ": " + val);
log += "</li>";
}
});
return log;
}
If you call formatLog()on the example data you gave it returns
ServerAlias: GBIZ-WEBurltoken: CFID=10989&CFTOKEN=f07fe950-53926E3B-F33A-093D-3FCEFB&jsessionid=84303d29a229d1sessionid: 84197a667053f63433672873j377e7d379101UUID: 53934LBB-DB8F-79T6-C03937JD84HB864A338Template: /home/vagrant/dev/websites/g-bis/code/webroot/page/home/home.cfm, line 3GeneratedContent: Mailto: Message: Unknown tag: cfincflude.tagName: cfincfludeRAW_TRACE: at cfhome2ecfm1296628853.runPage(/home/vagrant/dev/websites/nig-bis/code/webroot/page/home/home.cfm:3)ID: CFINCLUDETEMPLATE: /home/vagrant/dev/websites/nig-bis/code/webroot/page/home/home.cfmLINE: 3TYPE: CFMLCOLUMN: 0RAW_TRACE: at cfdisplay2ecfm1093821753.runPage(/home/vagrant/dev/websites/nig-bis/code/webroot/page/display.cfm:6)ID: CFINCLUDETEMPLATE: /home/vagrant/dev/websites/nig-bis/code/webroot/page/display.cfmLINE: 6TYPE: CFMLCOLUMN: 0
How to format it then is up to you.
This is basically a recursive for loop.
To do this with Jade you will need to use mixins so that you can print nested objects by calling the mixin with a deeper level of indentation.
Note that this whole thing is a little ugly as you won't get guaranteed ordering of fields and you may have to implement some logic to differentiate looping on arrays vs. looping on JSON objects.
You can try util.inspect. In your template:
pre
= util.inspect(jsonResults)

Resources