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 }