There are times when an ObjC method is not bridged to Swift in a Swifty way. One example is a method accepting an NSError**
that returns an NSInteger
(not a BOOL
or reference type). Cocoa conventions require methods accepting an NSError**
to have a non-void return type to indicate whether or not an error occurred. Methods returning a BOOL
or reference type convert nicely to Swift. However, methods returning an NSInteger
, for example, need some additional help.
Legacy ObjC Database API
Let's assume we have a legacy ObjC database API that doesn't return a BOOL
or reference type. Instead the method returns an NSInteger
, where NSNotFound
is used to indicate an error.` @interface HRLDatabase : NSObject
- (BOOL)beginTransaction:(NSError Nullable Nullable)error;
- (BOOL)commitTransaction:(NSError Nullable Nullable)error;
// If NSNotFound, then check the error; otherwise the return value represents the number of updated rows. - (NSInteger)executeUpdate:(nonnull NSString *)sql error:(NSError Nullable Nullable)error;
@end
Here's the generated Swift API.
open class HRLDatabase: NSObject {
open func beginTransaction() throws
open func commitTransaction() throws
open func executeUpdate(_ sql: String, error: NSErrorPointer) -> Int }
The "begin transaction" and "commit transaction" methods look great. However, the "execute update", not so much.
## Refining ObjC Methods
We can [refine](https://developer.apple.com/library/content/documentation/Swift/Conceptual/BuildingCocoaApps/MixandMatch.html) the "execute update" method by adding the `NS_REFINED_FOR_SWIFT` macro to end of the declaration. Here's how that looks.
@interface HRLDatabase : NSObject
- (NSInteger)executeUpdate:(nonnull NSString )sql error:(NSError Nullable Nullable)error NSREFINEDFORSWIFT;
@end
The `NS_REFINED_FOR_SWIFT` macro tells the compiler to emit a method that can be used in Swift extension. The emitted method
begins with `__` (double underscore).
open class HRLDatabase: NSObject {
open func _executeUpdate( sql: String, error: NSErrorPointer) -> Int }
Here's the refined Swift interface in an extension:
extension HRLDatabase {
func executeUpdate( sql: String) throws { var error: NSError? let result = _executeUpdate(sql, error: &error) if result == NSNotFound { throw error! } } }
## Example Usage
let database = HRLDatabase()
do { try database.beginTransaction() try database.executeUpdate("select from sometable") try database.commitTransaction() } catch { // handle error/ rollback }