Swift function execution order - swift

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)
}

Related

How do I display a string in View in Swift from HKSampleQuery

since this is my first Swift project and I am new to programming in general. I need some help with extracting the sample data I fetched from the query and printing it in the console. I am looking for a way to extract latestHr from the local scope and display it in the View in the app itself which is in the scope below:
var body: some View {
return VStack {
}
but I couldn't find a way to do it correctly.
This is most of the code I am using and the parts for calculation and fetching.
func latestheartRate(){
guard let sampleType = HKObjectType.quantityType(forIdentifier: .heartRate) else{return}
let startDate = Calendar.current.date(byAdding: .month, value: -1, to: Date())
let predicate = HKQuery.predicateForSamples(withStart: startDate, end: Date(), options: .strictEndDate)
let sortDescriptor = NSSortDescriptor(key: HKSampleSortIdentifierEndDate, ascending: false)
let query = HKSampleQuery(sampleType: sampleType, predicate: predicate, limit: Int(HKObjectQueryNoLimit), sortDescriptors: [sortDescriptor]) {(sample, result, error) in guard error == nil else {
return
}
let data = result![0] as! HKQuantitySample
let unit = HKUnit(from:"count/min")
let latestHr = data.quantity.doubleValue(for: unit)
let date = DateFormatter()
date.dateFormat = "HH:mm E, d MMM y"
let StartDate = date.string(from: data.startDate)
let EndDate = date.string(from: data.endDate)
print("Latest Hr\(latestHr) BPM // StartDate \(StartDate): EndDate \(EndDate)")
let HR = String(latestHr)
}
healthStore.execute(query)
}
//trigger the function to authorize read and write heart rate
init()
{
authorizeHealthKit()
}
var body: some View {
return VStack {
}

in swift code, can we use a function without bracket?

I am now analyzing swift source code.
In this source code, performQuery is defined as function, but in line 2, performQuery is used without bracket"()". What does it mean?
Can we use a function without bracket?
I have read all the swift grammar but I cannot find similar code.
Is it related to "Unstructured Concurrency"?
==================================================
func calculateDailyQuantitySamplesForPastWeek() {
performQuery {
DispatchQueue.main.async { [weak self] in
self?.reloadData()
}
}
}
// MARK: - HealthQueryDataSource
func performQuery(completion: #escaping () -> Void) {
let predicate = createLastWeekPredicate()
let anchorDate = createAnchorDate()
let dailyInterval = DateComponents(day: 1)
let statisticsOptions = getStatisticsOptions(for: dataTypeIdentifier)
let query = HKStatisticsCollectionQuery(quantityType: quantityType,
quantitySamplePredicate: predicate,
options: statisticsOptions,
anchorDate: anchorDate,
intervalComponents: dailyInterval)
// The handler block for the HKStatisticsCollection object.
let updateInterfaceWithStatistics: (HKStatisticsCollection) -> Void = { statisticsCollection in
self.dataValues = []
let now = Date()
let startDate = getLastWeekStartDate()
let endDate = now
statisticsCollection.enumerateStatistics(from: startDate, to: endDate) { [weak self] (statistics, stop) in
var dataValue = HealthDataTypeValue(startDate: statistics.startDate,
endDate: statistics.endDate,
value: 0)
if let quantity = getStatisticsQuantity(for: statistics, with: statisticsOptions),
let identifier = self?.dataTypeIdentifier,
let unit = preferredUnit(for: identifier) {
dataValue.value = quantity.doubleValue(for: unit)
}
self?.dataValues.append(dataValue)
}
completion()
}
query.initialResultsHandler = { query, statisticsCollection, error in
if let statisticsCollection = statisticsCollection {
updateInterfaceWithStatistics(statisticsCollection)
}
}
query.statisticsUpdateHandler = { [weak self] query, statistics, statisticsCollection, error in
// Ensure we only update the interface if the visible data type is updated
if let statisticsCollection = statisticsCollection, query.objectType?.identifier == self?.dataTypeIdentifier {
updateInterfaceWithStatistics(statisticsCollection)
}
}
self.healthStore.execute(query)
self.query = query
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
if let query = query {
self.healthStore.stop(query)
}
}
}
you need to learn closure. please check the link

How to set a value as a condition in while loop using DispatchGroup?

I'm currently developing an application using SwiftUI.
I want to use a value created from a while loop using a DispatchGroup as a condition for while loop.
But my code doesn't work...
My goal is to get a date over a set total temperature from a start date.
How could I solve this problem?
UPDATED
For example:
If I call the method like below.
makeGetCallDateOverSetTemp(start_date:"2020-11-01", set_temp:100)
1'st loop -> totalTemp = 20 (a temperature of 2020-11-01 is 20℃)
2'nd loop -> totalTemp = 50 (a temperature of 2020-11-02 is 30℃)
3'rd loop -> totalTemp = 80 (a temperature of 2020-11-03 is 30℃)
4'th loop -> totalTemp = 105 (a temperature of 2020-11-04 is 25℃)
the while loop stops here and gets 2020-11-04 as the day over the set temperature.
AppState.swift
#Published var weatherInfos:[WeatherInfos]?
func makeGetCallDateOverSetTemp(start_date:String, set_temp:Int){
let start_date = self.dateFromString(string: start_date, format: "yyyy/MM/dd")
var addDays = 0
var totalTemp:Float = 0.0
let group = DispatchGroup()
// Set up the URL request
while Float(set_temp) < totalTemp {
let start_date = Calendar.current.date(byAdding: .day, value: addDays, to: start_date)
let url_start_date = self.stringFromDate(date: start_date!, format: "yyyy-MM-dd")
let endpoint: String = "https://sample.com/api/weather/?start_date=\(url_start_date)"
addDays += 1
guard let url = URL(string: endpoint) else {
print("Error: cannot create URL")
continue
}
var urlRequest = URLRequest(url: url)
urlRequest.addValue("token xxxxxxxxxxx", forHTTPHeaderField: "authorization")
// set up the session
let config = URLSessionConfiguration.default
let session = URLSession(configuration: config)
// make the request
group.enter()
let task = session.dataTask(with: urlRequest) {(data, response, error) in
guard error == nil else {
print("error calling GET")
return
}
// make sure we got data
guard let responseData = data else {
print("Error: did not receive data")
return
}
// check for any errors
defer { group.leave()}
// parse the result as JSON, since that's what the API provides
DispatchQueue.main.async {
do{
self.weatherInfos = try JSONDecoder().decode([WeatherInfos].self, from: responseData)
for info in self.weatherInfos!{
totalTemp += info.temp
}
}catch{
print("Error: did not decode")
return
}
}
}
task.resume()
}
group.notify(queue: .main){
print(url_start_date)
}
}
func stringFromDate(date: Date, format: String) -> String {
let formatter: DateFormatter = DateFormatter()
formatter.calendar = Calendar(identifier: .gregorian)
formatter.dateFormat = format
return formatter.string(from: date)
}
func dateFromString(string: String, format: String) -> Date {
let formatter: DateFormatter = DateFormatter()
formatter.calendar = Calendar(identifier: .gregorian)
formatter.dateFormat = format
return formatter.date(from: string) ?? Date()
}
jsonModel.swift
struct WeatherInfos:Codable,Identifiable {
var id: Int
var temp: Float
}
A while loop whose condition relies on the result of an asynchronous task is impossible.
This is a stand-alone generic example with recursion to run in a Playground.
The static data is a struct, an array, a queue and a threshold value
struct Item {
let date : String
let values : [Int]
}
let items = [Item(date: "2020-02-01", values: [1, 3, 5]),
Item(date:"2020-02-02", values:[2, 4, 6]),
Item(date: "2020-02-03", values:[3, 5, 7])]
let queue = DispatchQueue(label: "Foo")
let threshold = 25
The variables are an index and the accumulated temperature
var temp = 0
var index = 0
The function getData calls itself passing the next item if the threshold is not reached yet. The asynchronous task is simulated with asyncAfter.
Finally the function notify is called.
func notify(date : String) {
DispatchQueue.main.async{ print(date, temp) }
}
func getData(date: String, values :[Int]) {
queue.asyncAfter(deadline: .now() + 1) {
for value in values {
temp += value
if temp >= threshold {
notify(date: date)
return
}
}
index += 1
if index < items.count {
let nextItem = items[index]
getData(date: nextItem.date, values: nextItem.values)
} else {
notify(date: "\(date) – temperature below threshold")
}
}
}
let firstItem = items[index]
getData(date: firstItem.date, values: firstItem.values)

Dispatch Group not notifying? [duplicate]

This question already has answers here:
How to notify a queue in Swift (GCD)
(2 answers)
Closed 4 years ago.
I have this identical code in another app and it works without fail, now in this app the dispatch group is not notifying and my handler isn't getting called? I can't figure out the difference between the 2 apps since the code is identical?
func getHeartRateMaxFromWorkouts(workouts: [HKWorkout], handler: #escaping (careerMaxHeartRatePerWorkoutAsCustomHistoricalSample, careerAverageHeartRatePerWorkoutAsCustomHistoricalSample) -> Void) {
let workoutsReversed = workouts.reversed()
guard let heartRateType:HKQuantityType = HKQuantityType.quantityType(forIdentifier: HKQuantityTypeIdentifier.heartRate) else { return }
let heartRateUnit:HKUnit = HKUnit(from: "count/min")
var heartRateMaxArrayAsCustomHistoricalSample = [CustomHistoricalSample]()
var heartRateAvgArrayAsCustomHistoricalSample = [CustomHistoricalSample]()
//DispatchGroup needed since making async call per workout and need notified when all calls are done
let dispatchGroup = DispatchGroup()
for workout in workoutsReversed {
//predicate
let startDate = workout.startDate
let endDate = workout.endDate
let predicate = HKQuery.predicateForSamples(withStart: startDate, end: endDate, options: .strictStartDate)
//descriptor
let sortDescriptors = [
NSSortDescriptor(key: HKSampleSortIdentifierEndDate, ascending: true) //Changed this to false so that HRR and MCS would calculate, always check this if not getting these values
]
dispatchGroup.enter()
let heartRateQuery = HKSampleQuery(sampleType: heartRateType,
predicate: predicate,
limit: (HKObjectQueryNoLimit),
sortDescriptors: sortDescriptors)
{ (query:HKSampleQuery, results:[HKSample]?, error:Error?) -> Void in
guard error == nil else { print("get heart rate error"); return }
guard let unwrappedResults = results as? [HKQuantitySample] else { print("get heart rate error"); return}
let heartRatesAsDouble = unwrappedResults.map {$0.quantity.doubleValue(for: heartRateUnit)}
guard let max = heartRatesAsDouble.max() else { return }
let maxAsCustomHistoricalSample = CustomHistoricalSample(value: max, date: workout.startDate)
heartRateMaxArrayAsCustomHistoricalSample.append(maxAsCustomHistoricalSample)
let average = heartRatesAsDouble.average
let averageAsCustomHistoricalSample = CustomHistoricalSample(value: average, date: workout.startDate)
heartRateAvgArrayAsCustomHistoricalSample.append(averageAsCustomHistoricalSample)
dispatchGroup.leave()
}
healthStore.execute(heartRateQuery)
} //End of for workout loop
dispatchGroup.notify(queue: .main) {
//Need to sort by date since the dates come back jumbled
let sortedReversedHeartRateMaxArrayAsCustomHistoricalSampple = heartRateMaxArrayAsCustomHistoricalSample.sorted { $0.date > $1.date }.reversed() as [CustomHistoricalSample]
let sortedReversedHeartRateAverageArrayAsCustomHistoricalSampple = heartRateAvgArrayAsCustomHistoricalSample.sorted { $0.date > $1.date }.reversed() as [CustomHistoricalSample]
print("handler called = \(sortedReversedHeartRateMaxArrayAsCustomHistoricalSampple.count)")
handler(sortedReversedHeartRateMaxArrayAsCustomHistoricalSampple, sortedReversedHeartRateAverageArrayAsCustomHistoricalSampple)
}
} //End getHeartRateMaxFromWorkouts
It's a good practice to make leave top of callback
guard error == nil else { print("get heart rate error"); dispatchGroup.leave() return ; }
guard let unwrappedResults = results as? [HKQuantitySample] else { print("get heart rate error"); dispatchGroup.leave(); return}
let heartRatesAsDouble = unwrappedResults.map {$0.quantity.doubleValue(for: heartRateUnit)}
guard let max = heartRatesAsDouble.max() else { dispatchGroup.leave(); return }
let maxAsCustomHistoricalSample = CustomHistoricalSample(value: max, date: workout.startDate)
heartRateMaxArrayAsCustomHistoricalSample.append(maxAsCustomHistoricalSample)
let average = heartRatesAsDouble.average
let averageAsCustomHistoricalSample = CustomHistoricalSample(value: average, date: workout.startDate)
dispatchGroup.leave()
heartRateAvgArrayAsCustomHistoricalSample.append(averageAsCustomHistoricalSample)

How can I speed up performQuery in Swift?

So I am creating this app that uses CloudKit to save and fetch images and text from the Cloud. The problem is that I can only access the results after the whole fetch is done. I would like to be able to fetch each record individually as it is fetched. Here is the code.
func fetchPost() {
spinner.startAnimating()
if imageView.image != nil {
spinner.alpha = 0
}
var imageData = [UIImage]()
var text = [String]()
let predicate = NSPredicate(value: true)
let sort = NSSortDescriptor(key: "creationDate", ascending: false)
let query = CKQuery(recordType: "Post",
predicate: predicate)
query.sortDescriptors = [sort]
publicDB.performQuery(query, inZoneWithID: nil) {
results, error in
if error != nil {
dispatch_async(dispatch_get_main_queue()) {
println("Query failed")
return
}
} else {
println("test")
var number = 0
for record in results {
if let pictureRecord = record as? CKRecord {
let post = Post(record: pictureRecord, database: self.publicDB)
let postImageData = post.imageData
let postText = post.text
self.images.append(UIImage(data: postImageData)!)
self.texts.append(postText)
println("\"\(postText)\" is the text. Fetch successful.")
if number == 0 {
self.imageView.image = self.images[0]
self.nameLabel.text = self.texts[0]
}
++number
} else {
println("Records failed")
}
}
}
self.spinner.stopAnimating()
self.spinner.alpha = 1
}
}
Thanks!
If you use CKQueryOperation you can set a callback (recordFetchedBlock) that will be called for each record as it is fetched from the server.