I tried to get the index of an element from Core Data, but it shows an error even I force unwrapped the value of the index - swift

I try to use a function to sorted the data in the Core Data, and get the firstIndex of the first element base on the predicate, but when I used .firstIndex, it shows:
Value of type '((Filedetails) -> Array.Index?)?' (aka 'Optional<(Filedetails) -> Optional>') has no member 'compactMap'"
func countingLastOccurrence(filterkey1: String) -> Int {
let context = PersistenceController.shared.container.viewContext
let fetchquest1 = NSFetchRequest<Filedetails>(entityName: "Filedetails")
fetchquest1.sortDescriptors = [NSSortDescriptor(keyPath: \Filedetails.year, ascending: false), NSSortDescriptor(keyPath: \Filedetails.sequence, ascending: false)]
fetchquest1.predicate = NSPredicate(format: "%K == 1", filterkey1)
let item1 = try? context.fetch(fetchquest1).firstIndex
let item1a = item1.compactMap { Int($0) }
return item1a
I had tried to force unwrapped the value, but it is error
"Cannot assign value of type '(Filedetails) -> Array.Index?' (aka '(Filedetails) -> Optional') to type 'Int'"
var item1a: Int = 0
if let item1 = try? context.fetch(fetchquest1).firstIndex {
item1a = item1
}
return item1a
I tried "if let" and "guard let"
guard let item1 = try? context.fetch(fetchquest1).firstIndex else {
return 0
}
return item1
It still shows an error:
Cannot convert return expression of type '(Filedetails) -> Array.Index?' (aka '(Filedetails) -> Optional') to return type 'Int'
If I used .first for the predicate, I can get the details of the data, but when I used .firstIndex, I can't get the result.
How can I get the index as a Int ?
Thank you so much.

The problem is that your code generates two optionals, one by try? and one by first(Index).
The first index of the first element base on the predicate is always zero so it doesn't make much sense to return the index especially to return 0 in case of an error.
Calling compactMap is also pointless because both input and output is an array. It just skips the nil values.
To avoid the double optional you could return an optional Filedetails item
func countingLastOccurrence(filterKey: String) -> Filedetails? {
let context = PersistenceController.shared.container.viewContext
let request = NSFetchRequest<Filedetails>(entityName: "Filedetails")
request.sortDescriptors = [NSSortDescriptor(keyPath: \Filedetails.year, ascending: false), NSSortDescriptor(keyPath: \Filedetails.sequence, ascending: false)]
request.predicate = NSPredicate(format: "%K == 1", filterKey)
guard let result = try? context.fetch(request) else { return nil}
return result.first
}
Or hand over the error to the caller
func countingLastOccurrence(filterKey: String) throws -> Filedetails? {
let context = PersistenceController.shared.container.viewContext
let request = NSFetchRequest<Filedetails>(entityName: "Filedetails")
request.sortDescriptors = [NSSortDescriptor(keyPath: \Filedetails.year, ascending: false), NSSortDescriptor(keyPath: \Filedetails.sequence, ascending: false)]
request.predicate = NSPredicate(format: "%K == 1", filterKey)
return try context.fetch(request).first
}
Side note: Consider a better naming than the 1 and 1a suffixes which are a bit confusing

Related

sum only values with specified bool with core data

I have a tableview with values. The database is made with core data. You can set the values to true or false. I only want to sum the values with true. To sum the values i have this code.
func printData() {
//Shorthand Argument Names
//let request: NSFetchRequest<Gegenstand> = Gegenstand.fetchRequest()
//let records = try! context.fetch(request) as [NSManagedObject]
let sum = ViewController.liste.reduce(0) { $0 + ($1.value(forKey: "gewicht") as? Double ?? 0) }
print("Gesamtgewicht: \(sum) kg")
gewicht = sum
if gewicht > 3500 {
gewichtLabel.textColor = .red
gewichtLabel.text = "\(gewicht) kg"
}
}
I tried it with an if-function but i don't know to use it with core data.
Create a coreData fetchRequest with isValue=true,
Calculate the sum of return fetchRequest
func fetchAllWithTrue() -> [Gegenstand] {
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: Gegenstand)
fetchRequest.predicate = NSPredicate(format: “isValue== YES")
do {
let fetchedObjects = try coreDataManager.context.fetch(fetchRequest) as? [Gegenstand]
return fetchedObjects
} catch {
print(error.localizedDescription)
return [Gegenstand]()
}
}
You can do all of it in Core Data if you want. Filter for true filtering with an NSPredicate, and have Core Data calculate the sum using NSExpression.
First set up the fetch request to get only entries where the property is true and make sure it returns dictionary-type results (I don't know what your boolean is called, so here I'm calling it flag. Put your property name there):
let fetchRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest(entityName: "Gegenstand")
fetchRequest.resultType = .dictionaryResultType
fetchRequest.predicate = NSPredicate(format: "flag = true")
Then set up an NSExpressionDescription that can get the sum of gewicht values:
let sumExpression = NSExpression(format: "sum:(gewicht)")
let sumExpressionDescription = NSExpressionDescription()
sumExpressionDescription.expression = sumExpression
sumExpressionDescription.resultType = .double
sumExpressionDescription.name = "gewichtSum"
What this does is create a new kind of property that Core Data understands where the value is the sum the values of gewicht and is named gewichtSum. Fetch requests know how to use that kind of property. You do it like this:
fetchRequest.propertiesToFetch = [ sumExpressionDescription ]
Since the fetch uses dictionaryResultType, running the fetch returns an array of dictionaries. It's an array because fetch always returns an array, but here there's only one entry. The dictionary in that array entry has a key called gewichtSum for a Double property that was calculated from the expression above. You get the value like this:
do {
let result = try context.fetch(fetchRequest)
if result.count > 0,
let sumInfo = result[0] as? [String:Double],
let gewichtSum: Double = sumInfo["gewichtSum"] {
print("Sum: \(gewichtSum)")
}
} catch {
...
}
The print statement above prints the sum of all gewicht values where flag is true. It only includes true values for flag because of the NSPredicate, and it contains the sum because of the expression description.

Swift - FetchRequest and filter results to display in list from CoreData

I am trying to create a fetch request that pulls data from CoreData, groups it by a id and then puts it into a list. I also want this to be dynamic, new list items can be added at any time while in this view. I have grinder my gears on this for hours and cannot figure out how to make this work dynamically. With my most recent attempt I am just getting initializing errors which I commented into the code. I am so stuck on this and would really appreciate some help. Also I am a total noob at swift so I am sorry if this is illiterate, Thanks
import CoreData
import SwiftUI
struct FilteredList: View {
#Environment(\.managedObjectContext) var moc
#FetchRequest var fetchRequest: FetchedResults<Workout>
var filter: String
var collectedWorkout: [Int64 : [Workout]] = [:]
var uniqueWorkout: [Int64] = []
init(filterIn: String) {
filter = filterIn
//_fetchRequest = FetchRequest<Workout>(sortDescriptors: [SortDescriptor(\.order, order: .reverse)], predicate: NSPredicate(format: "type = %#", filterIn))
// _outerRequest = FetchRequest<Workout>(sortDescriptors: [SortDescriptor(\.order, order: .reverse)], predicate: NSPredicate(format: "new"))
let FR: NSFetchRequest<Workout> = Workout.fetchRequest()
let predicate = NSPredicate(format: "type = %#", filterIn)
var result: [Workout] = []
FR.sortDescriptors = [NSSortDescriptor(keyPath: \Workout.order, ascending: false)]
FR.predicate = predicate
do {
let wOuts: [Workout]
wOuts = try moc.fetch(FR) // <- Varialbe 'self.fetchRequest' used before being initialized
for wOut in wOuts {
print(wOut.last)
result = wOuts
}
} catch{
print("Unable to fetch")
result = []
}
// Then you can use your properties.
let unsortDict = Dictionary(grouping: result, by: { $0.workoutId })
uniqueWorkout = unsortDict.map({ $0.key }).sorted()
} // <- Return from initializer without initializing all stored properties

How does case insensitive sort work in Swift or other languages?

So, I am fetching some data from CoreData and I am using NSSortDescriptor to sort it in ascending order (I've tried other methods too). What happens is that the lowercase string with same text comes first. As per my understanding, in ASCII, uppercase strings come first ('A' starting at 65) and then lowercase strings ('a' starting at 97) and if we consider this, uppercase strings should come first when sorting in ascending order.
let sortDescriptor = NSSortDescriptor(key: "name", ascending: true, selector: #selector(NSString.caseInsensitiveCompare(_:)))
let entities = CoreDataManager.getData(entityName: "Entity", predicate: nil, sortDescriptor: sortDescriptor) as? [Entity]
In my CoreDataManager class, I have the following methods.
class CoreDataManager: NSObject {
static func getManagedObject() -> NSManagedObjectContext {
let appDelegate = UIApplication.shared.delegate as! AppDelegate
return appDelegate.managedObjectContext
}
static func getData(entityName: String, predicate: NSPredicate? = nil, sortDescriptor: NSSortDescriptor? = nil) -> [NSManagedObject] {
var resultsManagedObject: [NSManagedObject] = []
do {
let managedObject = getManagedObject()
let request = NSFetchRequest<NSFetchRequestResult>(entityName: entityName)
if let descriptor = sortDescriptor {
request.sortDescriptors = [descriptor]
}
if predicate != nil {
request.predicate = predicate
}
let results = try managedObject.fetch(request)
resultsManagedObject = results as! [NSManagedObject]
} catch {
print("There was an error retrieving data")
}
return resultsManagedObject
}
}
I've also tried sorting using sorted method of arrays but that was also giving the same output as using NSSortDescriptor.
let arr = ["bamboo", "document", "BAMBOO", "DOCUMENT"]
print(arr.sorted { $0.lowercased() < $1.lowercased() }) // Output ~> ["bamboo", "BAMBOO", "document", "DOCUMENT"]
Why is sorting working like this? Is it the same in other languages?
It's not sorting lowercase first. It's comparing uppercase and lowercase as equal. But obviously, in an array, something has to come first. If a sorting algorithm is "stable", then if two elements compare equal, the one that appears first in the input will also appear first in the output. If the algorithm is "unstable" they might come out in any random order. It looks like you're getting a stable sort (or just getting lucky); since your example input has lowercase values before their corresponding uppercase values, the output does too.

how to save mixed sort?

From the list in which the words are located, I need to display them in a mixed state.
To do this, I added a shuffle sort field to the entity
func setShuffleWords(id: UUID?) -> [CoreWord]? {
guard let id = id else { return nil }
let requestWords = NSFetchRequest<NSFetchRequestResult>(entityName: "CoreWord")
requestWords.predicate = NSPredicate(format: "ANY dictionary.id == %#", id as CVarArg)
var coreWords: [CoreWord]?
if let result = try? managedContext.fetch(requestWords) as? [CoreWord] {
let words = result.shuffled()
for (index, value) in words.enumerated() {
if value.value(forKey: "shuffle") != nil {
value.setValue(index, forKey: "shuffle")
}
}
coreWords = words
}
save()
return coreWords
}
func getShuffleWords(id: UUID?) -> [CoreWord]? {
guard let id = id else { return nil }
let requestWords = NSFetchRequest<NSFetchRequestResult>(entityName: "CoreWord")
let sort = NSSortDescriptor(key: "shuffle", ascending: true)
requestWords.predicate = NSPredicate(format: "ANY dictionary.id == %#", id as CVarArg)
requestWords.sortDescriptors = [sort]
do {
let result = try managedContext.fetch(requestWords)
return result as? [CoreWord]
} catch let error {
fatalError(error.localizedDescription)
}
}
But, the problem is that if I mix up another list where this word is, then the value is rewritten and it will change its position everywhere.
How to keep mixed order in a specific list?
In this situation you need to replace your many-many relationship with an intermediate entity. Let’s call that entity a Listing. It will represent the listing of a particular word in a particular group, so each Listing should have a to-one relationship to CoreWord and a to-one relationship to CoreGroup. In each case the inverse (which you might call “listings”) should be to-many. The Listing entity should have (at least) one attribute, shuffle, which represents the sequence of a particular word on a particular group.
Life generally gets more complicated, because you have to create and manage these intermediate objects rather than just working the Word and Group objects and their relationships.

How to read HKWorkoutActivityType.Running in healthkit?

I tried below code to read data from healthapp, But I'm getting results as nil value and also error Invalid HKObjectType HKQuantityTypeIdentifierDistanceWalkingRunning for keyPath workoutType.
mac version : 10.10.5
xcode version : 7.1
let distanceType =
HKObjectType.quantityTypeForIdentifier(
HKQuantityTypeIdentifierDistanceWalkingRunning)
let workoutPredicate = HKQuery.predicateForWorkoutsWithWorkoutActivityType(HKWorkoutActivityType.Running)
let startDateSort =
NSSortDescriptor(key: HKSampleSortIdentifierStartDate, ascending: true)
let query = HKSampleQuery(sampleType: distanceType!, predicate: workoutPredicate,
limit: 0, sortDescriptors: [startDateSort]) {
(sampleQuery, results, error) -> Void in
if let distanceSamples = results as? [HKQuantitySample] {
// process the detailed samples...
}
else {
// Perform proper error handling here...
print("*** An error occurred while adding a sample to " +
"the workout: \(error!.localizedDescription)")
abort()
}
}
// Execute the query
healthManager.healthKitStore.executeQuery(query)
below code to access healthkit data
// 1. Set the types you want to read from HK Store
let typeOfRead = [HKObjectType.quantityTypeForIdentifier(HKQuantityTypeIdentifierDietaryEnergyConsumed)!,
HKObjectType.quantityTypeForIdentifier(HKQuantityTypeIdentifierActiveEnergyBurned)!,
HKObjectType.quantityTypeForIdentifier(HKQuantityTypeIdentifierHeight)!,
HKObjectType.quantityTypeForIdentifier(HKQuantityTypeIdentifierBodyMass)!,
HKObjectType.characteristicTypeForIdentifier(HKCharacteristicTypeIdentifierDateOfBirth)!,
HKObjectType.characteristicTypeForIdentifier(HKCharacteristicTypeIdentifierBiologicalSex)!,
HKObjectType.characteristicTypeForIdentifier(HKCharacteristicTypeIdentifierBloodType)!,
HKObjectType.quantityTypeForIdentifier(HKQuantityTypeIdentifierStepCount)!,
HKObjectType.quantityTypeForIdentifier(HKQuantityTypeIdentifierDistanceWalkingRunning)!,
HKObjectType.quantityTypeForIdentifier(HKQuantityTypeIdentifierHeartRate)!,
HKObjectType.quantityTypeForIdentifier(HKQuantityTypeIdentifierOxygenSaturation)!,
HKObjectType.quantityTypeForIdentifier(HKQuantityTypeIdentifierBloodPressureDiastolic)!,
HKObjectType.quantityTypeForIdentifier(HKQuantityTypeIdentifierBloodPressureSystolic)!]
let typeOfReads = NSSet(array: typeOfRead)
// 2. Set the types you want to write to HK Store
let typeOfWrite = [
HKObjectType.quantityTypeForIdentifier(HKQuantityTypeIdentifierDietaryEnergyConsumed)!,
HKObjectType.quantityTypeForIdentifier(HKQuantityTypeIdentifierActiveEnergyBurned)!,
HKObjectType.quantityTypeForIdentifier(HKQuantityTypeIdentifierHeight)!,
HKObjectType.quantityTypeForIdentifier(HKQuantityTypeIdentifierBodyMass)!,
HKObjectType.quantityTypeForIdentifier(HKQuantityTypeIdentifierStepCount)!,
HKObjectType.quantityTypeForIdentifier(HKQuantityTypeIdentifierDistanceWalkingRunning)!
]
let typeOfWrites = NSSet(array: typeOfWrite)
use this:
let query = HKSampleQuery(sampleType: HKWorkoutType.workoutType(), predicate: workoutPredicate,
limit: 0, sortDescriptors: [startDateSort]) {
// ...
}
with the sampleType you say that you want to select workouts.
the predicate determines what workout properties are used to select.
You told the healthSore to select running samples with workout properties. That doesn't fit together.