I'm trying to check the error code value in Swift, and find myself a bit confused by the new struct types and conversions.
What I want to do is simply take an NSError object passed in a handler closure and check its code type by comparing it to a value stored in a CMError struct. In Objective-C I would simply write
[pedometer queryPedometerDataFromDate:now toDate:now withHandler:^(CMPedometerData *pedometerData, NSError *error) {
BOOL isAuthorized = (error.code != CMErrorMotionActivityNotAuthorized);
}];
In Swift, when I write what I expect to be the equivalent
pedometer.queryPedometerDataFromDate(now, toDate: now) {(data:CMPedometerData!, error:NSError!) in
let isAuthorised:Bool = (error.code != CMErrorMotionActivityNotAuthorized)
}
I get the error Could not find an overload for '!=' that accepts the supplied arguments. This points to a type cast error. And indeed CMErrorMotionActivityNotAuthorized
is of type CMError
, which is a Swift struct. And I can't seem to convert between this CMError
type and the Int
type that is that of error.code
.
So how can I check my error code?
If I try to decompose and explicitly cast:
let errorCode:Int = (CMErrorMotionActivityNotAuthorized as Int)
let isAuthorized:Bool = (error.code != errorCode)
I get the absurd error message Cannot convert the expression's type 'Int' to type 'Int'.
Documentations says CMError
is defined as
struct CMError {
init(_ value: CUnsignedInt)
var value: CUnsignedInt
}
in Swift. In Objective-C it is defined as
typedef enum {
CMErrorNULL = 100,
CMErrorDeviceRequiresMovement,
CMErrorTrueNorthNotAvailable,
CMErrorUnknown,
CMErrorMotionActivityNotAvailable,
CMErrorMotionActivityNotAuthorized,
CMErrorMotionActivityNotEntitled,
CMErrorInvalidParameter
} CMError;
Jack Wu's comments are right — this looks like a non-modernized enum, and filing a bug about that would be a great idea. In the meantime...
Command-click a CMError
declaration in a Swift file and you'll get the Swift definition of both the type and the related constants. CMErrorMotionActivityNotAuthorized
and friends aren't a subtype of Int
, they're instances of the CMError
struct, which contains an integer value
. That value is a CUnsignedInt
, and Swift doesn't automatically convert that to a signed Int
for you — part of Swift being a "safe" language is avoiding situations where implicit type conversions can lead to over/underflows that cause bugs.
So, your query should look something like this:
pedometer.queryPedometerDataFromDate(now, toDate: now) { data, error in
let isAuthorized = (error.code != Int(CMErrorMotionActivityNotAuthorized.value))
}
(Also slimmed the code a bit to use type inference.)
in Swift 4.0 you have option to of CMPedometer.authorizationStatus()
which makes your life a bit easier :)
Here is what I would do, first check version if it is iOS 11+, we can use this method, otherwise we use the error parsing method:
CMPedometer().queryPedometerData(from: Date(), to: Date()) { (_, error) in
let isAuthorised: Bool = {
if #available(iOS 11.0, *) {
return CMPedometer.authorizationStatus() == .authorized
} else {
if let error = error as NSError? {
return error.code != Int(CMErrorMotionActivityNotAuthorized.rawValue)
} else { return true }
}
}()
if isAuthorised {
// do your stuff
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With