Search code examples
swiftcore-motionnserror

Check NSError code in swift


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?


Note 1

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'.


Note 2

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;

Solution

  • 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.)