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?
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.
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)
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With