No NSSortDescriptor on distanceToLocation for CKQuery? - cloudkit

With a CloudKit CKQuery you can filter on distance (see sample). But how can you sort on distance? Like location ASC. A sort descriptor on location returns an error: query.sortDescriptors = [NSSortDescriptor(key: "distanceToLocation", ascending: true)] :
static func getNearbySpots(location : CLLocation, completionHandler: (spots : CKRecord[]) -> Void) {
let predicate = NSPredicate(format: "distanceToLocation:fromLocation:(location, %#) < 10000", location)
let queryOperation = CKQueryOperation(query: query)
var results : CKRecord[] = []
queryOperation.recordFetchedBlock = {
results += $0
}
queryOperation.queryCompletionBlock = {
if $1 {
NSLog($1.description,[])
abort()
} else {
completionHandler(spots: results)
}
}
self.publicDB().addOperation(queryOperation)
}

Ok, found it. There is a CKLocationSortDescriptor where you can specify the location.
query.sortDescriptors = [CKLocationSortDescriptor(key: "location", relativeLocation: location)]

Related

Swift 5 NSFetchRequest predicate when trying to lookup a String UUID

I have a string UUID coming into this method, to lookup an entity in CoreData that has UUID's saved as UUID type (Not String).
I keep getting "Fatal error: Unexpectedly found nil while unwrapping an Optional value" on line for the predicate.
func loadUser(uuid: String) -> [ExistingUsers2] {
let request : NSFetchRequest<ExistingUsers2> = ExistingUsers2.fetchRequest()
let uuidQuery = NSUUID(uuidString: uuid)
request.predicate = NSPredicate(format: "%K == %#", #keyPath(ExistingUsers2.uuid), uuidQuery! as CVarArg)
request.sortDescriptors = [NSSortDescriptor(key: "name", ascending: true)]
do {
existingUsersArray = try context.fetch(request)
print("Returned \(existingUsersArray.count)")
} catch {
print("Error fetching data from context \(error)")
}
return existingUsersArray
}
Any help? I haven't found anything here or Dr Google. TKS
You can replace your predicate with this:
guard let uuidQuery = UUID(uuidString: uuid) else { return [] } // no valid UUID with this code
request.predicate = NSPredicate(format: "%K == %#", #keyPath(ExistingUsers2.uuid), uuidQuery as CVarArg)
Everything else should work.
UPDATE
This is the code that finally worked, thanks for your help #André Henrique da Silva
func loadUser(uuid: String) -> [ExistingUsers2] {
let request : NSFetchRequest<ExistingUsers2> = ExistingUsers2.fetchRequest()
let uuidQuery = NSUUID(uuidString: uuid)
request.predicate = NSPredicate(format: "uuid == %#", uuidQuery! as CVarArg)
request.sortDescriptors = [NSSortDescriptor(key: "name", ascending: true)]
do {
existingUsersArray = try context.fetch(request)
} catch {
print("Error fetching data from context \(error)")
}
return existingUsersArray
}
Try this as your predicate: NSPredicate(format: "cid = %#", "\(id)")
where cid is the UUID in CoreData and id is the UUID you got from the string. Also do not use NSUUID.
Replace the uuidAttributeName with your attribute name and yourStringuuid with the your string that you want to convert into UUID type.
var uuid = UUID(uuidString: yourStringuuid)
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "ExistingUsers2")
fetchRequest.predicate = NSPredicate(format: "uuidAttributeName == %#",uuid)
let results = try context.fetch(fetchRequest)

Swift function execution order

I'm trying to load sleep data from healthkit.
after retrieving some data, I converted it into Double type to use chart, and reflected them on setChart function.
But here's some problem.
To successfully show on chart, my function should executed in order.
Ideal order is : retrieve data, convert data -> setchart
but the order is not kept.
I read some articles about dispatchqueue, but still I can't solve the problem.
What should I do to solve this problem? \
override func viewDidLoad() {
super.viewDidLoad()
//선행작업
DispatchQueue.global(qos: .userInteractive).sync {
print("1")
self.retrieveSleepAnalysis()
}
DispatchQueue.global(qos: .default).sync {
print("2")
self.recentSleepStartHourDouble = self.stringTimeToDouble(stringTime: self.recentSleepStartHour)
self.wakeUp = self.wakeupTimeCalcuation(start: self.recentSleepStartHourDouble, end: self.resultToSleepAmount)
}
DispatchQueue.main.async {
// self.recentSleepStartHourDouble = self.stringTimeToDouble(stringTime: self.recentSleepStartHour)
// self.wakeUp = self.wakeupTimeCalcuation(start: self.recentSleepStartHourDouble, end: self.resultToSleepAmount)
self.setChart(days: self.recentSleepDate.reversed(), sleepTimes: self.recentSleepStartHourDouble.reversed(), wakeupTimes:
public func retrieveSleepAnalysis() {
// first, we define the object type we want
if let sleepType = HKObjectType.categoryType(forIdentifier: HKCategoryTypeIdentifier.sleepAnalysis) {
// print(sleepType)
// Use a sortDescriptor to get the recent data first
let sortDescriptor = NSSortDescriptor(key: HKSampleSortIdentifierEndDate, ascending: false)
// we create our query with a block completion to execute
let query = HKSampleQuery(sampleType: sleepType, predicate: nil, limit: 7, sortDescriptors: [sortDescriptor]) { (query, tmpResult, error) -> Void in
if error != nil {
// something happened
return
}
if let result = tmpResult {
for item in result {
if let sample = item as? HKCategorySample {
let value = (sample.value == HKCategoryValueSleepAnalysis.inBed.rawValue) ? "InBed" : "Asleep"
print("Healthkit sleep: \(sample.startDate) \(sample.endDate) - value: \(value)")
let sleepHour = Calendar.current.component(.hour, from: sample.startDate)
print("현지시각으로 잠든시간 \(sleepHour)")
if sleepHour < 19 && sleepHour > 12{
print("낮잠")
}
else{
self.resultToSleepAmount.append(CFDateGetTimeIntervalSinceDate(sample.endDate as CFDate, sample.startDate as CFDate)/3600)
let myDateFormatter = DateFormatter()
myDateFormatter.dateFormat = "MM / dd"
myDateFormatter.locale = Locale(identifier: "ko_KR")
let sleepDate = myDateFormatter.string(from: sample.startDate)
self.recentSleepDate.append(sleepDate)
let myHourFormatter = DateFormatter()
myHourFormatter.dateFormat = "HHmm"
myHourFormatter.locale = Locale(identifier: "ko_KR")
let sleepStartHour = myHourFormatter.string(from: sample.startDate)
self.recentSleepStartHour.append(sleepStartHour)
print("잠든 시간은 몇시몇분? \(sleepStartHour)")
let sleepEndHour = myHourFormatter.string(from: sample.endDate)
self.recentSleepEndHour.append(sleepEndHour)
}
}
print("total amount of sleep time : \(self.resultToSleepAmount), 현재 크기는 : \(self.resultToSleepAmount.count)")
}
}
}
// finally, we execute our query
healthStore.execute(query)
}

Swift Core Data fetch data 1 column [duplicate]

This question already has an answer here:
Fetching selected attribute in entities
(1 answer)
Closed 4 years ago.
I cannot fetch data by "name" column. When respone, system print all attribute in People. Help me, thanks.
private func getPeople(product: String) {
let temp = product
let entityDescription = NSEntityDescription.entity(forEntityName: "People", in: context)
let fetchRequest = NSFetchRequest<NSFetchRequestResult>()
fetchRequest.entity = entityDescription
fetchRequest.includesPropertyValues = true
fetchRequest.returnsObjectsAsFaults = false
fetchRequest.predicate = NSPredicate(format: "product == %#", temp)
fetchRequest.propertiesToFetch = ["name"]
fetchRequest.sortDescriptors = [NSSortDescriptor(key: "id", ascending: true)]
do {
let personList = try context.fetch(fetchRequest) as! [People]
print(personList)
} catch let error as NSError {
print(error)
}
}
To fetch only specific properties the resultType of the request must be dictionaryResultType
private func getPeople(product: String) {
let fetchRequest = NSFetchRequest<NSDictionary>(entityName: "People")
fetchRequest.predicate = NSPredicate(format: "product == %#", product)
fetchRequest.resultType = .dictionaryResultType
fetchRequest.propertiesToFetch = ["name"]
fetchRequest.sortDescriptors = [NSSortDescriptor(key: "id", ascending: true)]
do {
let personList = try context.fetch(fetchRequest) as! [[String:String]]
print(personList)
} catch {
print(error)
}
}
}

FetchedResultsController Using Predicate to Exclude Results Contained in a Set

I am attempting to filter the items in my FetchedResultsController to filter out a certain set of items. These are engines which have a certain letter designation in them followed by numbers. The possible rage of letters in the designation are from "A" to "O". I would like to be able to filter out engines that have the designations from "D" to "O" based on another condition.
I set a constant "highPowerEngines" that is a set containing those letters. Since the designation only contains one letter, I want to exclude any designation that contains any of the letters in highPowerEngines. So far, this is my code, the predicate I am working on is contained after the FIXME:
func configureFetchedResultsController() {
let context = databaseController.getContext()
let enginesFetchRequest = NSFetchRequest<Engine>(entityName: CoreData.engine)
let highPowerEngines: Set = ["D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O"]
var predicate = NSPredicate()
if currentStage == 1 && stages! == 1 {
predicate = NSPredicate(format: "engineType == %#", EngineType.singleStage.rawValue)
} else if currentStage < stages! {
predicate = NSPredicate(format: "engineType == %#", EngineType.boosterStage.rawValue)
} else {
predicate = NSPredicate(format: "engineType == %#", EngineType.upperStage.rawValue)
}
var predicateArray:[NSPredicate] = [
predicate
]
// FIXME: Sort out highPowerEngines
if !dPlusEngineIAP {
if currentStage == 1 && stages! == 1 {
predicate = NSPredicate(format: "NOT engineDesignation CONTAINS %#", highPowerEngines)
predicateArray.append(predicate)
}
}
let compoundPredicate = NSCompoundPredicate(andPredicateWithSubpredicates: predicateArray)
enginesFetchRequest.predicate = compoundPredicate
let primarySortDescriptor = NSSortDescriptor(key: CoreData.isMadeByName, ascending: true)
let secondarySortDescriptor = NSSortDescriptor(key: CoreData.engineDesignation, ascending: true)
enginesFetchRequest.sortDescriptors = [primarySortDescriptor, secondarySortDescriptor]
self.fetchedResultsController = NSFetchedResultsController<Engine>(
fetchRequest: enginesFetchRequest,
managedObjectContext: context,
sectionNameKeyPath: CoreData.isMadeByName,
cacheName: nil)
self.fetchedResultsController.delegate = self
}
Use IN instead of CONTAINS
NOT (engineDesignation IN %#)
update
Since you want to exclude all but tree letters why not turn the predicate around to include rather than exclude, something like
engineDesignation BEGINSWITH ‘A’ OR
engineDesignation BEGINSWITH ‘B’ OR
engineDesignation BEGINSWITH ‘C’
Thanks to the post here: CoreData Predicate get every sentence that contains any word in array
, I was finally able to solve my problem. My current working code is here:
func configureFetchedResultsController() {
let context = databaseController.getContext()
let enginesFetchRequest = NSFetchRequest<Engine>(entityName: CoreData.engine)
let lowPowerEngines = ["A", "B", "C"]
var predicate = NSPredicate()
if currentStage == 1 && stages! == 1 {
predicate = NSPredicate(format: "engineType == %#", EngineType.singleStage.rawValue)
} else if currentStage < stages! {
predicate = NSPredicate(format: "engineType == %#", EngineType.boosterStage.rawValue)
} else {
predicate = NSPredicate(format: "engineType == %#", EngineType.upperStage.rawValue)
}
var predicateArray:[NSPredicate] = [
predicate
]
if !dPlusEngineIAP {
let predicates = lowPowerEngines.map {
NSPredicate(format: "engineDesignation CONTAINS %#", $0)
}
let predicate = NSCompoundPredicate(orPredicateWithSubpredicates: predicates)
predicateArray.append(predicate)
}
let compoundPredicate = NSCompoundPredicate(andPredicateWithSubpredicates: predicateArray)
enginesFetchRequest.predicate = compoundPredicate
let primarySortDescriptor = NSSortDescriptor(key: CoreData.isMadeByName, ascending: true)
let secondarySortDescriptor = NSSortDescriptor(key: CoreData.engineDesignation, ascending: true)
enginesFetchRequest.sortDescriptors = [primarySortDescriptor, secondarySortDescriptor]
self.fetchedResultsController = NSFetchedResultsController<Engine>(
fetchRequest: enginesFetchRequest,
managedObjectContext: context,
sectionNameKeyPath: CoreData.isMadeByName,
cacheName: nil)
self.fetchedResultsController.delegate = self
}
The solution to dealing with each item in the array was to make a compound predicate using the map function. Then adding the compound predicate to the fetchedResultsController. That allows me to compare the engine to see if it is in lowPowerEngines. Map makes a separate predicate for each item in lowPowerEngines. I could even change it programmatically on the fly. I really hope this helps someone as there is no central place to come up with these sort of tricks. When I collect enough of them, I will do a master post.

CloudKit CKQueryOperation doesn't get all records

In CloudKit RecordType is more than 100 records. Following code gets from these only 11 and they are not first 11 records, they are picked randomly from beginning, center and at the end of records. I can't get whats wrong in code.
EDIT : I got it working by changing .reseltsLimit to 5000!
let cloudContainer = CKContainer.default()
let publicDatabase = cloudContainer.publicCloudDatabase
let predicate = NSPredicate(value: true)
let query = CKQuery(recordType: "Sijainti", predicate: predicate)
var queryOperation = CKQueryOperation(query: query)
queryOperation.queuePriority = .veryHigh
queryOperation.resultsLimit = 50
queryOperation.desiredKeys = ["Koordinaatit"]
queryOperation.recordFetchedBlock = { (record) -> Void in
self.sijainnitArray.append(record)
}
queryOperation.queryCompletionBlock = { (cursor, error) -> Void in
if error != nil {
print("Failed to get data")
return
}
if cursor != nil {
let newQueryOperation = CKQueryOperation(cursor: cursor!)
newQueryOperation.cursor = cursor
newQueryOperation.resultsLimit = queryOperation.resultsLimit
newQueryOperation.queryCompletionBlock = queryOperation.queryCompletionBlock
queryOperation = newQueryOperation
publicDatabase.add(queryOperation)
return
}
}
publicDatabase.add(queryOperation)