Few cents about my commits

bro-gen: binding NSError error codes right

|

In RoboVM’s cocoatouch there are bunch of NSError subclasses that don’t exist in objc iOS itself. For example WKError This is hand written class required to allow RoboVM’s NSError return object error code which can be used to compare against enum members, such as WKErrorCode.
NSError provides information by following properties:

@interface NSError : NSObject <NSCopying, NSSecureCoding>
@property (readonly, copy) NSErrorDomain domain;
@property (readonly) NSInteger code;
@property (readonly, copy) NSDictionary<NSErrorUserInfoKey, id> * userInfo;

in RoboVM world NSError exposes same methods, but there is another hand written method getErrorCode and its purpose is to return Object representation for error which can be used to compare agains error codes enums, usually present in binding as well.

So all this is just to allow using following code:

NSError error;
if (error.getErrorCode() == WKErrorCode.MediaPlayer) {
    ....
}

instead of:

NSError error;
if (error.getCode() == WKErrorCode.MediaPlayer.value()) {
    ....
}

how it works
getErrorCode is confusing as it knows only about values of NSCocoaErrorCode enum. The magic is in following places:

  1. static initializer
  2. NSError.Marshaller

What is happening:

  1. static initializer looks over all VM known classes for NSError subclasses and builds the map allNSErrorClasses domain to NSError subclass. To get to the party subclass shall contain static getClassDomain method which is being called to get domain this NSError subclass is responsible for.
  2. marshaller is responsible to create java side object corresponding to native one. But it first goes native side to get its domain staring and then if allNSErrorClasses contains class for domain it creates this virtual NSError subclass instance, e.g. WKError
  3. now WKError will be able to return proper object in getErrorCode

how it was bound

  1. WKErrorCode was generated by bro-gen to enum, then it was manually updated to implement NSErrorCode interface
  2. WKError was manually written with implementation of getErrorCode to return proper value from WKErrorCode;
  3. WKErrorCode had to be annotated with @ForceLinkClass(WKError.class) this forces WKError not to be excluded from scope as long as WKErrorCode is used.

how it was reworked in bro-gen to simplify bindings

  1. new enum template nserror_enum_template added. it is used for enum as long as in enum specification there is nserror: true
  2. this template contains NSErrorWrap subclass of NSError subclass. So no need to manually write NSError subclass
  3. getErrorCode required to be added enum (which connects NSError with it), usually there is global variable for this, so it has to be bound with enum by getErrorCode name.

Check this example:

enums:
    GADErrorCode: {nserror: true, prefix: kGADError}
....    
values:
    kGADErrorDomain:
        class: GADErrorCode
        name: getClassDomain

Happy bro-gening!
Related: Tutorial: [ADMOB] using bro-gen to quick generate bindings

Comments