Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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;
like image 428
KPM Avatar asked Jun 20 '14 14:06

KPM


2 Answers

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

like image 188
rickster Avatar answered Oct 08 '22 17:10

rickster


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
    }

}
like image 45
Masih Avatar answered Oct 08 '22 18:10

Masih