We have the (usual?) problem that we need a method that returns a BOOL and can throw an error, inside a Framework that can be used from Objective-C and from Swift. Is there a nice and for the user understandable way to do that?
More detail:
If I just needed a method that returns a BOOL, that would look like this:
- (BOOL)isDone
Now (as you, who will hopefully answer this question, will know) if this should return an error at the same time, I can't do it like this, as in:
- (BOOL)isDone:(NSError**)error
the return value tells the user if there was an error, but doesn't return any value.
So the usual ways I have found so far to do this are these:
Send the return value as a pointer and fill it:
-(BOOL)getIsDone:(BOOL*)isDone error:(NSError**)error
or: Return an NSNumber that is nil if there is an error or contains a BOOL if not.
-(NSNumber*)isDone:(NSError**)error
In Objective-C, we so far used the first solution for this. But now the framework needs to nicely map to a swift funtion. Now in Swift, the first solution would map to:
func getIsDone(isDone: UnsafeMutablePointer<ObjCBool>) throws
and the second one to:
func isDone() throws -> NSNumber
which is really unclear for the user why this is an NSNumber and he has to use .boolValue to actually use it...
As you can see, both variants are really bad. What I would prefer is something like:
func isDone() throws -> Bool
or maybe
func getIsDone(isDone: inout Bool) throws
Is there a way to tell the clang compiler to convert it to something like this? Pleeease?
Thanks!
The deeper problem is that the Obj-C pattern for this is already ugly:
-(BOOL)getIsDone:(BOOL*)isDone error:(NSError**)error
and that pattern means you're dealing with a "BOOL*", not a "BOOL". The fact that you're dealing with a pure C pointer (not Obj-C) translates the ugliness into Swift, too.
If you want to pursue a 2-framework solution, one choice would be to distribute the Swift "wrapper" framework (which would be very tiny) in source form. In essence, that's the only alternative that any current Swift developer has, to distributing multiple ABI-versioned binary frameworks.
There might be another solution without a 2nd framework, where you return a result that's an optional Obj-C object of a custom class declared in the Obj-C framework (instead of returning an optional NSNumber). Clients would still have to use a property of the object (e.g. "result.isDone") but that's slightly better than "result.boolValue". In such a case, I wouldn't call the method "isDone ()" or "getIsDone (…)", but something like "status ()".
The other thing that occurs to me is that the right answer depends on what your "isDone" method is for. If you're retrieving a completion status/error for an action that was started earlier, it's not quite right for the method to throw. After all, the "isDone" method itself does not fail in that case. Or, if the method itself does something that really can fail, then maybe split it into two methods, one that tries the failable action, and a second (non-throwing) method that retrieves the "done" status.