Apple Swift - Unable to retrieve data from CMPedometer's handler - ios

I am trying out Swift with some of the new iOS 8 API's and have tried the best part of the day to get the CMPedometer queryPedometerDataFromDate API to return any data within the handler. I believe its an error on my part, getting a little confused with the syntax.
Here is my code, with comments on what prints out:
var ped = CMPedometer()
var stepsTaken = NSNumber(int: 0)
println(dateNow) // 2014-06-07 21:23:55 +0000
println(dateMidnight) // 2014-06-07 00:00:00 +0000
ped.queryPedometerDataFromDate(dateMidnight, toDate: dateNow, withHandler:{
data, error in
println("Test1") // Does not print
println(error) // Does not print
stepsTaken = data.numberOfSteps
})
println("My Int Value \(stepsTaken)") // My Int Value 0

It works for me with CMPedometer queryPedometerDataFromDate, if i define the CMPedometer as a class wide constant:
let pedometer = CMPedometer()
and then in a func i'm using:
self.pedometer.queryPedometerDataFromDate(today, toDate: now, withHandler: ...

A code sample from Hipster.
import CoreMotion
let lengthFormatter = NSLengthFormatter()
let pedometer = CMPedometer()
pedometer.startPedometerUpdatesFromDate(NSDate(), withHandler: { data, error in
if !error {
println("Steps Taken: \(data.numberOfSteps)")
var distance = data.distance.doubleValue
println("Distance: \(lengthFormatter.stringFromMeters(distance))")
var time = data.endDate.timeIntervalSinceDate(data.startDate)
var speed = distance / time
println("Speed: \(lengthFormatter.stringFromMeters(speed)) / s")
}
})

After many hours playing around with the syntax, thinking I have made a rookie error, I tried alternative API's and found that the following works to grab pedometer data:
var ped2 = CMStepCounter()
ped2.queryStepCountStartingFrom(dateMidnight, to: dateNow, toQueue: NSOperationQueue(), withHandler:{data, error in
println("Test 2") // "Test 2"
println(data) // 491 (I really should go for a walk!)
println(error) // nil
})
I will file a radar with Apple as it looks like a bug in the new API. Also the CMStepCounter class is due to be deprecated for CMPedometer. From CMStepCounter.h:
NS_CLASS_DEPRECATED_IOS(7_0,8_0,"Use CMPedometer instead")

You're querying the data, which goes onto another thread. The execution continues after your call, and your NSNumber is still at 0. By the time you've come back you've printed the message a long time ago, and have no way of recovering the stepsTaken.

You need to keep a reference to your ped variable. Something like:
self.ped = CMPedometer();
Currently your ped variable is going out of scope before the handler is even called.

Related

Which swift Document or video I need to read for me to understand how this ResultsHandler works?

HI I'm a newbi coder who is code for fun.
I want to get my stepscount from my iPhone
so I start to learn Xcode and swift.
I can write bash script and a little bit python.
but I really can't understand the following.
From https://developer.apple.com/documentation/healthkit/hkstatisticscollectionquery
query.initialResultsHandler = {
query, results, error in
// My Understanding This Handler is a unknow type or thing for me how can I know what is it and how to use it from apple's web page?
I can tell from reading apple's page it have three components and
Declaration says it's a var? like
var initialResultsHandler: ((HKStatisticsCollectionQuery, HKStatisticsCollection?, Error?) -> Void)? { get set }
so it's a var with three things in it? so it's a tuple? and can be get and set!?
So I think the above code means put var query to hander's HKStatisticsCollectionQuery, get from hander's HKStatisticsCollectionQuery to var results and handle's Error to local var error
The Most important thing is what is the "in" after the error?
I only know you can for xxxx in yyyy and create a loop but without a for use in means what? what document explain this in stuff?
guard let statsCollection = results else {
// Perform proper error handling here
fatalError("*** An error occurred while calculating the statistics: \(error?.localizedDescription) ***")
}
let endDate = NSDate()
guard let startDate = calendar.dateByAddingUnit(.Month, value: -3, toDate: endDate, options: []) else {
fatalError("*** Unable to calculate the start date ***")
}
That is a closure you are looking at. The three variables are the input arguments of the closure that you name query, results and error. You could give these any other name as well, since they only exist inside the closure and only their types are used in the function type signature.
The in keyword indicates the start of the closure body.
For more information on the topic, have a look at the Closures chapter of the Swift Programming Language.

queryPedometerData returns nil steps even if there are steps logged

I want to retrieve steps from 1h ago. I don't need to do anything special, I just need to know how many steps a user has done since the last hour.
Even though my iPhone has some steps logged, the query to retrieve the number of steps returns "nil".
This is the code:
let calendar = Calendar.current //calendar now, to be used in calculating the h in the past
let beforeDate = calendar.date(byAdding: .hour, value: -1, to: Date())
let pedometer = CMPedometer() //define pedometer
if CMPedometer.isStepCountingAvailable() == true {print("steps available")}else{print("steps not available")}
pedometer.queryPedometerData(from: beforeDate!, to: Date(), withHandler: { (pedometerData, error) in
if let pedData = pedometerData{
self.dateLabel.text = "Steps:\(pedData.numberOfSteps)"
}else {
self.dateLabel.text = "error)"
print(beforeDate)
}
})
}
And this is the date format that I put in the query:
2018-03-16 12:59:17 +0000
What is wrong?
One possible problem is that your CMPedometer object is stored only in a local variable. pedometer.queryPedometerData runs asynchronously, so this object needs to persist long enough to fulfill the query. But it can't do that if it is a local variable; it vanishes before the data can even be fetched. Try making pedometer a persistent instance property instead.
Also be aware that you don't know what queue the data will be delivered on. You need to step out to the main queue in order to talk to the interface, and you are failing to do that.

NSValue(CMTime: ) in swift 3?

i have this code
var times = [NSValue]()
for time in timePoints {
times.append(NSValue(CMTime : time))
}
i get an error that their is nothing called CMTime in swift 3 i can't really find any param for cm time..
Check the reference of NSValue.
init(time: CMTime)
Use NSValue(time: time).
One more. If you want to convert [CMTime] to [NSValue], you can write something like this:
let times = timePoints.map(NSValue.init(time:))

xcode crashes when I click on the console to check the output

Facing an issue with xcode.
I'm trying to develop an app that gives me the weather info. The build succeeds, but everytime I click on the console to check the output (to search, copy etc) xcode crashes.
The following text comes from the reporting tool to Apple,
Application Specific Information:
ProductBuildVersion: 7C1002
UNCAUGHT EXCEPTION (NSRangeException): -[__NSCFString characterAtIndex:]: Range or index out of bounds
UserInfo: (null)
Hints: None
Here's the code I'm running,
override func viewDidLoad() {
super.viewDidLoad()
let url = NSURL(string:"http://www.weather-forecast.com/locations/Hyderabad/forecasts/latest")
let task = NSURLSession.sharedSession().dataTaskWithURL(url!) { (data, response, error) -> Void in
if let webContent = data {
let decodedContent = NSString(data: webContent, encoding: NSUTF8StringEncoding)
//print(decodedContent)
let weatherSiteSourceArray = decodedContent?.componentsSeparatedByString("3 Day Weather Forecast Summary:</b><span class=\"read-more-small\"><span class=\"read-more-content\"> <span class=\"phrase\">")
print(weatherSiteSourceArray)
// if weatherSiteSourceArray?.count > 0 {
//
// let weatherInfo = weatherSiteSourceArray![1]
// print(weatherInfo)
// }
}
}
task.resume()
// Do any additional setup after loading the view, typically from a nib.
}
though the report tool informs me UNCAUGHT EXCEPTION (NSRangeException): -[__NSCFString characterAtIndex:]: Range or index out of bounds.
I am unable to figure out where this is happening.
From what i have learned you can print an array in Swift using the print(items: Any...) method.
help on this would be greatly appreciated !
You are checking is there any element in your array and then you are trying to read second element. There are chances that your array is having only one item in it and hence when you are choosing weatherSiteSourceArray![1], it fails as accessing element 2 is out of bound of array.
if weatherSiteSourceArray?.count > 0 {
let weatherInfo = weatherSiteSourceArray![1]
Either check for
if weatherSiteSourceArray?.count > 1
or use first element i.e.
let weatherInfo = weatherSiteSourceArray![0]
I have no idea of swift and assume it uses zero based index for array.
A good place to start would be to figure out exactly which line is causing the crash. Try commenting out these three lines:
let decodedContent = NSString(data: webContent, encoding: NSUTF8StringEncoding)
//print(decodedContent)
let weatherSiteSourceArray = decodedContent?.componentsSeparatedByString("3 Day Weather Forecast Summary:</b><span class=\"read-more-small\"><span class=\"read-more-content\"> <span class=\"phrase\">")
print(weatherSiteSourceArray)
And if you code doesn't crash after that, then try commenting in you code one line at the time until you have the line crashing.
Another thing:
Looking at your code I'm guessing that maybe this line:
let weatherSiteSourceArray = decodedContent?.componentsSeparatedByString("3 Day Weather Forecast Summary:</b><span class=\"read-more-small\"><span class=\"read-more-content\"> <span class=\"phrase\">")
might be the one causing your problems. Its a bit risky and brittle to try separating your content based on an HTML string like that. The second that string changes (and it will :-)), you've lost.
A better way would be to receive the data in pure JSON or XML if they have an API providing that.
Just a thought :-)

XMPP last activity time shows in strange form. How to fix it?

I have added to the Bridging file XMPPLastActivity.h file and in Swift file I did the next:
let abc = XMPPLastActivity()
let a = abc.sendLastActivityQueryToJID(user.jid)
print("A is: \(a)")
but it returns me response like
1686F740-C50C-477B-BAE2-02C897826B97
How can I return it in human readable format?
UPDATE
Please, join to chat to help me: https://chat.stackoverflow.com/rooms/90972/ios-errors-fixing
You will get a response via the XMPP delegate. You need to implement:
func xmppLastActivityDidReceiveResponse(sender:XMPPLastActivity, response:XMPPIQ)
And get the time lag to now in seconds with:
let time : NSUInteger = response.lastActivitySeconds
Then it's NSDate all the way.

Resources