Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Address Book External Change Callback in Swift (with C Function Pointers?)

Original Question (see solution below):

I am trying to use the AddressBook.framework in my Swift App, but can't figure out how to implement the ABAddressBookRegisterExternalChangeCallback function.

In Objective-C, I just implement the callback as a C function and pass its pointer:

// somewhere in the initializer of the MyAddressBook class:
ABAddressBookRef addressBook = ABAddressBookCreateWithOptions(nil, nil);
ABAddressBookRegisterExternalChangeCallback(addressBook, externalChangeCallback, (__bridge void *)(self));

// somewhere else in the MyAddressBook class:
void externalChangeCallback(ABAddressBookRef reference, CFDictionaryRef info, void *context)
{
    [(__bridge MyAddressBook *)context addressBookDidChangeExternally];
}

- (void)addressBookDidChangeExternally
{
    // good old Obj-C from here on!
}

In Swift, it is proving very difficult for me to handle C functions. I found that Apple added the ability to pass C function pointers around in beta 3, but how do I declare such a function? It would be good to use Swift's closure syntax, but is that even possible here?

This is where I create the ABAddressBookRef:

var addressBookRef: ABAddressBookRef = {
    let addressBookRef: ABAddressBookRef = ABAddressBookCreateWithOptions(nil, nil).takeRetainedValue()

    // TODO: how do I make this work?
    let externalChangeCallback: ABExternalChangeCallback = {
        println("Address book changed externally!")
    }
    ABAddressBookRegisterExternalChangeCallback(addressBookRef, externalChangeCallback, nil)

    return addressBookRef
}()

So how can I implement this in Swift?


Solution (with flaws):

As suggested by pNre, this is how I implemented it now:

In Objective-C:

AddressBookExternalChangeCallback.h:

#import <AddressBook/AddressBook.h>
void registerExternalChangeCallbackForAddressBook(ABAddressBookRef addressBookRef);

AddressBookExternalChangeCallback.m:

#import "AddressBookExternalChangeCallback.h"

void addressBookExternalChangeCallback(ABAddressBookRef addressBookRef, CFDictionaryRef info, void *context)
{
    dispatch_async(dispatch_get_main_queue(), ^{
        [[NSNotificationCenter defaultCenter] postNotificationName:@"AddressBookDidChangeExternallyNotification" object:nil];
    });
}

void registerExternalChangeCallbackForAddressBook(ABAddressBookRef addressBookRef)
{
    ABAddressBookRegisterExternalChangeCallback(addressBookRef, addressBookExternalChangeCallback, nil);
}

In Swift:

after importing bridging header:

registerExternalChangeCallbackForAddressBook(addressBookRef)

A notification is posted whenever the address book changes. Only @objc classes can register for notifications, though, so is there a way to call a Swift function or method instead?

like image 230
knl Avatar asked Aug 17 '14 05:08

knl


2 Answers

ABExternalChangeCallback is defined as

typealias ABExternalChangeCallback = CFunctionPointer<((ABAddressBookRef!, CFDictionary!, UnsafeMutablePointer<()>) -> Void)>

From the Xcode release notes:

However, you cannot call a C function pointer (CFunctionPointer) or convert a closure to C function pointer type.

This means you can't assign a block the way you're doing. However you can bypass this limitation calling ABAddressBookRegisterExternalChangeCallback in an objc function and calling it from your swift code.

like image 171
pNre Avatar answered Sep 18 '22 13:09

pNre


Swift 2.0

if let addressBook = ABAddressBookCreateWithOptions(nil, nil) {
    let ref = addressBook.takeRetainedValue()
    let callback: @convention(c) (addressBookRef: ABAddressBookRef!, info: CFDictionaryRef!, context: UnsafeMutablePointer<Void>) -> Void = {
        (addressBookRef, info, context) in

        // do the things you want

    }

    let addressBookChangeCallback = unsafeBitCast(callback, ABExternalChangeCallback.self)

    ABAddressBookRegisterExternalChangeCallback(ref, addressBookChangeCallback, nil)

}
like image 35
ethanchli Avatar answered Sep 18 '22 13:09

ethanchli