I have the following code in my running app
func eachSecond(timer: NSTimer) {
if deviceversion.floatValue >= 8.0 {
seconds++
let secondsQuantity = HKQuantity(unit: HKUnit.secondUnit(), doubleValue: seconds)
timeLabel.text = "Tijd: " + secondsQuantity.description
let distanceQuantity = HKQuantity(unit: HKUnit.meterUnit(), doubleValue: distance)
distanceLabel.text = "Afstand: " + distanceQuantity.description
let paceUnit = HKUnit.secondUnit().unitDividedByUnit(HKUnit.meterUnit())
let paceQuantity = HKQuantity(unit: paceUnit, doubleValue: seconds / distance)
paceLabel.text = "Snelheid: " + paceQuantity.description
}else{
}
}
This is using health kit which comes with ios 8. I also want people to use the app on their 7.1 iPhone. Is there an easy way to get the same information by using swift for iOS 7?
I can't seem to find how to do this and store this for later use. They made it real easy in iOS 8.
If you need to deploy your app on iOS releases before 8.0, you should perform the calculations directly rather than relying on HealthKit to provide the conveniences.
Related
Please help it has drove me mad
I am using Xcode 14, both crashed device using IOS 16.
I have following code, it does not work on iPhone 8 Mini, iPhone 12 mini Real Device. it does work on the iPhone 12 Mini and iPhone 8 mini simulator and other real bigger sized iPhone Device.
I downloaded time string such as "19:00" if the current time less than this, button activated else disable button.
let date = Date()
let calender = Calendar.current
let hour = calender.component(.hour, from: date)
let minute = calender.component(.minute, from: date)
let slotTime = splitTime(string: time.Time)
let CurrentTimeString = "\(hour):\(minute)"
let TimeSlotTimeString = slotTime
let f = DateFormatter()
f.dateFormat = "HH:mm"
if f.date(from: CurrentTimeString)! > f.date(from: slotTime)! {
return true
} else {
return false
}
}
the crash message read found nil while unwrapping
/// I use my mouse over the error area, the Xcode does show the value.
///
Please edit your question to include your code As code not as screen-shots. If you also want to leave the screen-shots so we can see the crash info, fine, but we need the code.
Your code is using force-unwrapping.
I call the force-unwrap operator the "crash if nil" operator. That is what it does.
Don't use force-unwrapping. Rewrite your code to log a message if the date string can't be converted to a Date
You might have your different devices set up to different locales where the rules for date/time formatting are different.
Try displaying the current Date()to a string using your date formatter on each device and logging it to the console. My guess is that some of your devices are set to 12-hour time and some are set to 24 hour time.
When in 12 hour time the Date string "18:20" won't be valid.
This question already has answers here:
What does "% is unavailable: Use truncatingRemainder instead" mean?
(5 answers)
Closed 6 years ago.
I am new to coding (and this website) and I am trying to build a stopwatch using a tutorial off YouTube and I can't get it to work. When I program the function for updating the Timer, the error "% is unavailable: Use truncatingRemainder instead" shows on lines 6 and 7. I'm coding in the latest version of Swift using the latest Xcode version. Could someone please tell me how to get it to work with truncatingRemainder with milliseconds? I really appreciate it.
Here is the link to the tutorial: https://www.youtube.com/watch?v=xp0dvmvh3Cw
func updateElapsedTimeLabel (timer : Timer)
{
if watch.isRunning
{
let minutes = Int(watch.elapsd/60)
let seconds = Int(watch.elapsd % 60)
let ten0fseconds = Int(watch.elapsd * 10 % 10)
elapsedTimeLabel.text = String (format: "%2d:%2d.%2d", minutes, seconds, ten0fseconds)
}
else {
timer.invalidate()
}
}
just do the following...
let seconds = watch.elapsd.truncatingRemainder(dividingBy: 60)
let ten0fseconds = (watch.elapsd * 10).truncatingRemainder(dividingBy: 10)
I have converted an Objective-C script to Swift for forming header of an AAC frame. However the code fails to run in the app and crashes with an EXC_BAD_ACCESS. Copying the code in to playground outputs what I expect with no errors. Not sure where to go with this, because it seems like it should work. I could always use the Objective-C version but that feels like going around the issue.
func adtsDataForPacketLength(packetLength: Int) -> NSData {
let adtsLength = 7
let profile = 2
let freqIdx = 4
let chanCfg = 1
let fullLength = adtsLength + packetLength;
var packet = [UInt8]()
packet.append(UInt8(0xFF))
packet.append(UInt8(0xF9))
packet.append(UInt8(((profile-1)<<6) + (freqIdx<<2) + (chanCfg>>2)))
packet.append(UInt8(((chanCfg&3)<<6) + (fullLength>>11)))
packet.append(UInt8((fullLength&0x7FF) >> 3))
packet.append(UInt8(((fullLength&7)<<5) + 0x1F))
packet.append(0xFC)
return NSData(bytes: packet, length: packet.count)
}
So below is the code that I've got thus far, cannot figure out why I'm getting inaccurate data.
Not accounting for the pause events yet that should not affect the first two kilometre inaccuracies...
So the output would be the distance 1km and the duration that km took.
Any ideas for improvement, please help?
func getHealthKitWorkouts(){
print("HealthKit Workout:")
/* Boris here: Looks like we need some sort of Health Kit manager */
let healthStore:HKHealthStore = HKHealthStore()
let durationFormatter = NSDateComponentsFormatter()
var workouts = [HKWorkout]()
// Predicate to read only running workouts
let predicate = HKQuery.predicateForWorkoutsWithWorkoutActivityType(HKWorkoutActivityType.Running)
// Order the workouts by date
let sortDescriptor = NSSortDescriptor(key:HKSampleSortIdentifierStartDate, ascending: false)
// Create the query
let sampleQuery = HKSampleQuery(sampleType: HKWorkoutType.workoutType(), predicate: predicate, limit: 0, sortDescriptors: [sortDescriptor])
{ (sampleQuery, results, error ) -> Void in
if let queryError = error {
print( "There was an error while reading the samples: \(queryError.localizedDescription)")
}
workouts = results as! [HKWorkout]
let target:Int = 0
print(workouts[target].workoutEvents)
print("Energy ", workouts[target].totalEnergyBurned)
print(durationFormatter.stringFromTimeInterval(workouts[target].duration))
print((workouts[target].totalDistance!.doubleValueForUnit(HKUnit.meterUnit())))
self.coolMan(workouts[target])
self.coolManStat(workouts[target])
}
// Execute the query
healthStore.executeQuery(sampleQuery)
}
func coolMan(let workout: HKWorkout){
let expectedOutput = [
NSTimeInterval(293),
NSTimeInterval(359),
NSTimeInterval(359),
NSTimeInterval(411),
NSTimeInterval(810)
]
let healthStore:HKHealthStore = HKHealthStore()
let distanceType = HKObjectType.quantityTypeForIdentifier(HKQuantityTypeIdentifierDistanceWalkingRunning)
let workoutPredicate = HKQuery.predicateForObjectsFromWorkout(workout)
let startDateSort = NSSortDescriptor(key: HKSampleSortIdentifierStartDate, ascending: true)
let query = HKSampleQuery(sampleType: distanceType!, predicate: workoutPredicate,
limit: 0, sortDescriptors: [startDateSort]) {
(sampleQuery, results, error) -> Void in
// Process the detailed samples...
if let distanceSamples = results as? [HKQuantitySample] {
var count = 0.00, countPace = 0.00, countDistance = 0.0, countPacePerMeterSum = 0.0
var countSplits = 0
var firstStart = distanceSamples[0].startDate
let durationFormatter = NSDateComponentsFormatter()
print("🕒 Time Splits: ")
for (index, element) in distanceSamples.enumerate() {
count += element.quantity.doubleValueForUnit(HKUnit.meterUnit())
/* Calculate Pace */
let duration = ((element.endDate.timeIntervalSinceDate(element.startDate)))
let distance = distanceSamples[index].quantity
let pacePerMeter = distance.doubleValueForUnit(HKUnit.meterUnit()) / duration
countPace += duration
countPacePerMeterSum += pacePerMeter
if count > 1000 {
/* Account for extra bits */
let percentageUnder = (1000 / count)
//countPace = countPace * percentageUnder
// 6.83299013038 * 2.5
print("👣 Reached Kilometer \(count) ")
// MARK: Testing
let testOutput = durationFormatter.stringFromTimeInterval(NSTimeInterval.init(floatLiteral: test)),
testOutputExpected = durationFormatter.stringFromTimeInterval(expectedOutput[countSplits])
print(" Output Accuracy (", round(test - expectedOutput[countSplits]) , "): expected \(testOutputExpected) versus \(testOutput)")
print(" ", firstStart, " until ", element.endDate)
/* Print The Split Time Taken */
firstStart = distanceSamples[index].endDate;
count = (count % 1000) //0.00
countPace = (count % 1000) * pacePerMeter
countSplits++
/* Noise
\(countSplits) – \(count) – Pace \(countPace) – Pace Per Meter \(pacePerMeter) – Summed Pace Per Meter \(countPacePerMeterSum) – \(countPacePerMeterSum / Double.init(index))"
*/
}
/* Account for the last entry */
if (distanceSamples.count - 1 ) == index {
print("We started a kilometer \(countSplits+1) – \(count)")
let pacePerKM = (count / countPace) * 1000
print(durationFormatter.stringFromTimeInterval(NSTimeInterval.init(floatLiteral: (pacePerKM ))))
}
}
}else {
// Perform proper error handling here...
print("*** An error occurred while adding a sample to " + "the workout: \(error!.localizedDescription)")
abort()
}
}
healthStore.executeQuery(query)
}
func coolManStat(let workout: HKWorkout){
let healthStore:HKHealthStore = HKHealthStore()
let stepsCount = HKQuantityType.quantityTypeForIdentifier(HKQuantityTypeIdentifierDistanceWalkingRunning)
let sumOption = HKStatisticsOptions.CumulativeSum
let statisticsSumQuery = HKStatisticsQuery(quantityType: stepsCount!, quantitySamplePredicate: HKQuery.predicateForObjectsFromWorkout(workout),
options: sumOption)
{ (query, result, error) in
if let sumQuantity = result?.sumQuantity() {
let numberOfSteps = Int(sumQuantity.doubleValueForUnit(HKUnit.meterUnit()))/1000
print("👣 Right -O: ",numberOfSteps)
}
}
healthStore.executeQuery(statisticsSumQuery)
}
I'm sure you're past this problem by now, more than two years later! But I'm sure someone else will come across this thread in the future, so I thought I'd share the answer.
I started off with a version of your code (many thanks!!) and encountered the same problems. I had to make a few changes. Not all of those changes are related to the issues you were seeing, but in any case, here are all of the considerations I've thought of so far:
Drift
You don't handle the 'drift', although this isn't what's causing the big inaccuracies in your output. What I mean is that your code is saying:
if count > 1000
But you don't do anything with the remainder over 1000, so your kilometre time isn't for 1000m, it's for, let's say, 1001m. So your time both is inaccurate for the current km, and it's including some of the running from the next km, so that time will be wrong too. Over a long run, this could start to cause noticeable problems. But it's not a big deal over short runs as I don't think the difference is significant enough at small distances. But it's definitely worth fixing. In my code I'm assuming that the runner was moving at a constant pace during the current sample (which is obviously not perfect, but I don't think there's a better way), and I'm then simply finding the fraction of the current sample distance that puts the split distance over 1000m, and getting that same fraction of the current sample's duration and removing it from the current km's time, and adding it (and the distance) to the next split.
GPS drops
The real problem with your results is that you don't handle GPS drops. The way I'm currently handling this is to compare the startDate of the current sample with the endDate of the previous sample. If they're not the same then there was a GPS drop. You need to add the difference between the previous endDate and the current startDate to the current split. Edit: you also need to do this with the startDate of the activity and the startDate of the first sample. There will be a gap between these 2 dates while GPS was connecting.
Pauses
There's a slight complication to the above GPS dropping problem. If the user has paused the workout then there will also be a difference between the current sample's startDate and the previous sample's endDate. So you need to be able to detect that and not adjust the split in that case. However, if the user's GPS dropped and they also paused during that time then you'll need to subtract the pause time from the missing time before adding it to the split.
Unfortunately, my splits are still not 100% in sync with the Apple Workouts app. But they've gone from being potentially minutes off to being mostly within 1 second. The worst I've seen is 3 seconds. I've only been working on this for a couple of hours, so I plan to continue trying to get 100% accuracy. I'll update this answer if I get that. But I believe I've covered the major problems here.
is there any possibility to create a custom log function?
This is how the default println("hello world") looks like:
2015-03-04 18:33:55.788 MyApp[12345:671253923] Hello World
I would like to output something like:
18:33 MyClass > myFunc [line 1] Hello World
First, for the time, you can get the current hour and minute as String:
func printTime()->String{
let date = NSDate()
let calendar = NSCalendar.currentCalendar()
let components = calendar.components(.CalendarUnitHour | .CalendarUnitMinute, fromDate: date)
let hour = components.hour
let minutes = components.minute
return "\(hour):\(minutes)"
}
And for the function etc. you can use the Swift Literal Expressions __FILE__, __FUNCTION__ and __LINE__.
But you don't want to set it each time you want to log. So you could do something like that:
func prettyPrint(print: String, file:String = __FILE__, functionName: String = __FUNCTION__, line:Int = __LINE__) {
println("\(printTime()) \(file) > \(functionName) [line \(line)] \(print)")
}
You call prettyPrint like that:
prettyPrint("hey")
And you will get the following output:
/Path/To/Your/File/MyClass.swift > hello [line 81] hey
But as you only want the name of your class, you can remove the path with the following function:
func getFile(path:String = __FILE__)->String{
var parts = path.componentsSeparatedByString("/")
return parts[parts.count-1]
}
Or, as ChikabuZ mentioned in his answer you can directly check the class:
let className = NSStringFromClass(self.classForCoder).pathExtension
Final Function
And here the final function(s):
func getFile(path:String = __FILE__)->String{
var parts = path.componentsSeparatedByString("/")
return parts[parts.count-1]
}
func prettyPrint(print: String, functionName: String = __FUNCTION__, line:Int = __LINE__) {
println("\(printTime()) \(getFile()) > \(functionName) [line \(line)] \(print)")
}
func printTime()->String{
let date = NSDate()
let calendar = NSCalendar.currentCalendar()
let components = calendar.components(.CalendarUnitHour | .CalendarUnitMinute, fromDate: date)
let hour = components.hour
let minutes = components.minute
return "\(hour):\(minutes)"
}
And the result will be:
MyClass.swift > hello [line 81] hey
You should also note #emaloney's answer to this question. Specifically that
println()-based solutions result in output being captured by the Apple System Log (ASL).
Ideally switch to NSLog or a full blown logging system
You should make an extension to NSObject, something like this:
class MyClass: NSObject
{
func myFunc()
{
myPrint("Hello World")
}
}
extension NSObject
{
func myPrint(text: String)
{
let timeFormatter = NSDateFormatter()
timeFormatter.dateStyle = NSDateFormatterStyle.NoStyle
timeFormatter.timeStyle = NSDateFormatterStyle.ShortStyle
let time = timeFormatter.stringFromDate(NSDate())
let className = NSStringFromClass(self.classForCoder).pathExtension
let function = __FUNCTION__
let line = "\(__LINE__)"
let result = time + " " + className + " > " + function + " " + line + " " + text
println(result)
}
}
let myClass = MyClass()
myClass.myFunc()
Unfortunately, none of the println()-based solutions result in output being captured by the Apple System Log (ASL).
The ASL is the logging facility provided by the Mac OS and iOS that is used by NSLog() (and on a Mac is visible through the Console application). Because NSLog() uses ASL, log entries recorded by NSLog() will be visible through the device console. Messages logged through println() will not be captured in ASL, and as a result, provide no opportunity to go back to the console for diagnostic purposes after something has happened.
There are two big downsides to NSLog(), however: (1) you can't customize the output format, and (2) it does a lot of work on the calling thread and can therefore negatively impact performance.
CleanroomLogger is a new Swift-based open-source API for logging. If you're familiar with log4j or CocoaLumberjack, then you'll understand CleanroomLogger.
You can customize your own output format by providing your own LogFormatter implementation to ensure that your log messages are formatted exactly as you want.
For more information on CleanroomLogger, visit the GitHub project page.