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

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)
}

in swift 4.0
class ABAddressBookManager: NSObject {
static let shared = ABAddressBookManager()
private var addressBook: ABAddressBook?
override init() {
super.init()
let addressBookForObserving = ABAddressBookCreate().takeRetainedValue()
let context = Unmanaged.passUnretained(self).toOpaque()
DispatchQueue.main.async {
ABAddressBookRegisterExternalChangeCallback(addressBookForObserving, { (addressBook, info, context) in
guard let context = context else {
return
}
let manager = Unmanaged<ABAddressBookManager>.fromOpaque(context).takeUnretainedValue()
//call manager's method
}
}
self.addressBook = addressBookForObserving
}
}

I got the same issue. But I have cleared the cache by calling ABAddressBookRevert() in addressbook object.

Related

JSContext onload trigger

I need to rewrite that bunch of code from Objective-c to swift:
JSContext *jsContext = [webView valueForKeyPath:#"documentView.webView.mainFrame.javaScriptContext"];
jsContext[#"loaded"] = ^{
NSLog(#"Content Loaded!");
};
The question is about getting context when webView is loaded.
Found a solution for swift:
if let jsContext = svtwebView?.value(forKeyPath: "documentView.webView.mainFrame.javaScriptContext") as? JSContext {
let loaded: #convention(block) () -> Void = {
print("Loaded:")
}
jsContext.setObject(loaded, forKeyedSubscript: "loaded" as NSCopying & NSObjectProtocol)
jsContext.evaluateScript("loaded")
}

Weak method argument semantics

Is there any way to specify that a particular method argument has weak semantics?
To elaborate, this is an Objective-C sample code that works as expected:
- (void)runTest {
__block NSObject *object = [NSObject new];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self myMethod:object];
});
// to make sure it happens after `myMethod:` call
dispatch_async(dispatch_get_main_queue(), ^{
object = nil;
});
}
- (void)myMethod:(__weak id)arg0 {
NSLog(#"%#", arg0); // <NSObject: 0x7fb0bdb1eaa0>
sleep(1);
NSLog(#"%#", arg0); // nil
}
This is the Swift version, that doesn't
public func runTest() {
var object: NSObject? = NSObject()
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
self.myMethod(object)
}
dispatch_async(dispatch_get_main_queue()) {
object = nil
}
}
private func myMethod(arg0: AnyObject?) {
println("\(arg0)") //Optional(<NSObject: 0x7fc778f26cf0>)
sleep(1)
println("\(arg0)") //Optional(<NSObject: 0x7fc778f26cf0>)
}
Am I correct in ym assumption that there is no way for the arg0 to become nil between the method calls in Swift version?
Thank you!
Update a user from Apple Dev.Forums pointed out that sleep is not a good function to use and consecutive dispatches might cause race conditions. While those might be reasonable concerns, this is just a sample code, the focus of the question is on passing weak arguments.
Swift doesn't have “weak args”… but that's probably only because Swift (3.0) args are immutable (equivalent to lets) and weak things in Swift need to be both a var and an Optional.
That said, there is indeed a pretty-easy way to accomplish an equivalent to weak args— use a weak var local (which frees up the arg-var to be released).  This works because Swift doesn't hang onto vars until the end of the current scope (like C++ so strictly does); but rather it releases vars from the scope after the last usage of them (which makes lldb-ing a PitA sometimes, but whatever).
The following example works consistently in Swift 3.0.2 on Xcode 8.2.1 on macOS 10.11.6:
class Test
{
func runTest() {
var object:NSObject? = NSObject()
myMethod(arg0: object)
DispatchQueue.main.asyncAfter(
deadline: DispatchTime.now() + 1.0,
qos: .userInteractive,
flags: DispatchWorkItemFlags.enforceQoS
){
object = nil
}
}
func myMethod(arg0:AnyObject?) {
weak var arg0Weak = arg0
// `arg0` get “released” at this point.  Note: It's essential that you
// don't use `arg0` in the rest of this method; only use `arg0Weak`.
NSLog("\(arg0Weak)"); // Optional(<NSObject: 0x600000000810>)
DispatchQueue.main.asyncAfter(
deadline: DispatchTime.now() + 2.0,
qos: .userInteractive,
flags: DispatchWorkItemFlags.enforceQoS
){
NSLog("\(arg0Weak)"); // nil
}
}
}
Test().runTest()
Note that if you try this in a Playground, the playground will finish execution before the DispatchQueues fire.  The simplest way to get the executable to run indefinitely (what I did) is create a new Cocoa application and paste all the code above into the func applicationDidFinishLaunching(_:Notification) { … } (yes, verbatim— Swift allows class definitions nested inside of methods).
In response to the thread-safety-lecturing you've gotten over using dispatch_async & sleep in your example, to prove that weak args are indeed the real deal here's a complete-main.m-source variant of your test that's single-threaded and queue-free:
#import <Foundation/Foundation.h>
#interface Test : NSObject
- (void)runTest;
- (void)myMethod:(__weak id)arg0 callback:(void (^)())callback;
#end
int main(int argc, const char * argv[]) {
#autoreleasepool {
[[Test new] runTest];
}
return 0;
}
#implementation Test
- (void)runTest {
__block NSObject *object = [NSObject new];
[self myMethod:object callback:^{
object = nil;
}];
}
- (void)myMethod:(__weak id)arg0 callback:(void (^)())callback {
NSLog(#"%#", arg0); // <NSObject: 0x100400bc0>
callback();
NSLog(#"%#", arg0); // (null)
}
#end
No way with language syntax for now.
I think this workaround is closest one for now.
public struct Weak<T> where T: AnyObject {
public weak var object: T?
public init(_ object: T?) {
self.object = object
}
}
func run(_ a: Weak<A>) {
guard let a = a.object else { return }
}
Is there any way to specify that a particular method argument has weak semantics?
That isn't what your Objective-C code example is doing. You're getting accidentally almost-weak semantics and you have undefined behavior (race condition) that real weak references don't have.
myMethod can send a message into la-la-land at any sequence point (the first NSLog statement or second, or even in the middle of NSLog somewhere... even if ARC doesn't elide the retain of arg0 you're still racing the main queue release or worse - retaining a zombie object).
Declaring something as __block just means allocate a slot in the heap environment for the block (because dispatch_async is guaranteed to let the block escape it will promote from a stack-allocated block to a heap block, and one of the storage slots in that heap block environment will be for your __block variable. Under ARC the block will automatically have Block_copy called, perhaps more aptly named Block_copy_to_heap).
This means both executing instances of the block will point to this same memory location.
If it helps, imagine this really silly code which has an obvious race condition. There are 1000 blocks queued concurrently all trying to modify unsafe. We're almost guaranteed to execute the nasty statements inside the if block because our assignment and comparison are not atomic and we're fighting over the same memory location.
static volatile size_t unsafe = 0;
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_apply(1000, queue, ^(size_t instance) {
unsafe = instance;
if (unsafe != instance) {
FORMAT_ALL_STORAGE();
SEND_RESIGNATION_EMAIL();
WATCH_THE_WORLD_BURN();
}
});
Your Swift example doesn't have the same problem because the block that doesn't modify the value probably captures (and retains) the object so doesn't see the modification from the other block.
It is up to the creator of a closure to deal with the memory management consequences so you can't create an API contract that enforces no retain cycles in a closure, other than marking something as #noescape in which case Swift won't do any retain/release or other memory management because the block doesn't outlive the current stack frame. That precludes async dispatch for obvious reasons.
If you want to present an API contract that solves this you can have a type adopt a protocol protocol Consumer: class { func consume(thing: Type) } then inside your API keep a weak reference to the Consumer instances.
Another technique is to accept a curried version of an instance function and weakly capture self:
protocol Protocol: class { }
typealias FuncType = () -> Void
var _responders = [FuncType]()
func registerResponder<P: Protocol>(responder: P, usingHandler handler: (P) -> () -> Void) {
_responders.append({ [weak responder] in
guard let responder = responder else { return }
handler(responder)()
})
}
class Inst: Protocol {
func myFunc() {
}
}
let inst = Inst()
registerResponder(inst, usingHandler: Inst.myFunc)

Calling delegate method from Swift's Extension class

All
I am implementing a UIAlertView extension for callback implementation like UIAlertView+Block in Objective C but the delegate method is not calling from this class any help.
Thanks in advance.
The callback from the alert view runs in a background thread and the code receiving it is probably running in the main thread, as per apple documentation:
From the Apple iOS Documentation on Notification Centers:
In a multithreaded application, notifications are always delivered in
the thread in which the notification was posted, which may not be the
same thread in which an observer registered itself.
You need to call the notification back in the main thread otherwise strange things can happen
dispatch_async(dispatch_get_main_queue(), {
//Call your notification from here
NSNotificationCenter.defaultCenter().postNotificationName(mySpecialNotificationKey, object: self)
})
This is the updated answer after solving the issue.
typealias SelectBlock = (buttonIndex : Int) -> Void
typealias CancelBlock = () -> Void
private var blockSelect : SelectBlock?
private var blockCancel : CancelBlock?
extension UIAlertView {
//Initilization
func initWithTitle(
title : String ,
message : String,
onSelect : SelectBlock?,
onCancel : CancelBlock?,
otherButtonTitles : [String]){
//Initialize
self.title = title
self.delegate = self
self.message = message
//Block
if let onSelectObj = onSelect? {
blockSelect = onSelectObj
}
if let onCancelObj = onCancel? {
blockCancel = onCancelObj
}
//Other buttons
for buttons in otherButtonTitles {
self.addButtonWithTitle(buttons)
}
//Cancel index
// println("buttons \(self.numberOfButtons)")
self.cancelButtonIndex = 0
self.show()
}
func alertView(alertView: UIAlertView, didDismissWithButtonIndex buttonIndex: Int) {
println("Index \(buttonIndex)")
if buttonIndex == alertView.cancelButtonIndex {
blockCancel!()
// println("Cancel")
}else{
blockSelect!(buttonIndex: buttonIndex)
// println("Retry")
}
}
}

App Crashing Swift Xcode 6.1 With Certain Method

My app is crashing on this method:
func loadData() {
timelineData.removeAllObjects()
var findTimelineData:PFQuery = PFQuery(className:"AllTweets")
findTimelineData.findObjectsInBackgroundWithBlock {
(objects:[AnyObject]!, error:NSError!)-> Void in
if (error == nil) {
for object:AnyObject in objects {
self.timelineData.addObject(object as PFObject)
}
let array:NSArray = self.timelineData.reverseObjectEnumerator().allObjects
self.timelineData = array as NSMutableArray
self.tableView.reloadData()
}
}
}
The error that it throws is:
ECX_BREAKPOINT(code=EXC_I386_BPT,subcode=0x0)
I am calling this method in my viewDidAppear method.
Does anybody have any ideas why this is happening?
The reverseObjectEnumerator().allObjects will return a NSArray, no matter the type of the original array is Mutable or not. (https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSEnumerator_Class/index.html#//apple_ref/occ/instp/NSEnumerator/allObjects)
In swift, if you need a mutable array, then use
var mutableArray = array.reverseObjectEnumerator().allObjects
var is for mutable and let is the opposite.

Calling Core-Foundation methods from Swift

I'm familiar with Obj-c and getting familiar with Swift, but I'm having a horrendous time struggling with Swift's very strict type-checking. Case in point, all I wanted to do was convert the routine in Obj-c below:
- (void)receiveIncomingConnectionNotification:(NSNotification *)notification {
NSDictionary *userInfo = [notification userInfo];
NSFileHandle *incomingFileHandle = [userInfo objectForKey:NSFileHandleNotificationFileHandleItem];
if (incomingFileHandle) {
CFDictionaryAddValue(
incomingRequests,
(__bridge const void *)(incomingFileHandle),
(__bridge const void *)((__bridge id)CFHTTPMessageCreateEmpty(kCFAllocatorDefault, TRUE)));
}
}
To this in Swift:
func receiveIncomingConnectionNotification(notification: NSNotification) {
let userInfo: NSDictionary = notification.userInfo!
let incomingFileHandle: NSFileHandle? = userInfo.objectForKey(NSFileHandleNotificationFileHandleItem) as? NSFileHandle
if (incomingFileHandle != nil) {
CFDictionaryAddValue(self.incomingRequests as CFMutableDictionaryRef, incomingFileHandle, CFHTTPMessageCreateEmpty(kCFAllocatorDefault, true))
}
}
But the CFDictionaryAddValue call keeps generating errors pointing to the 'true' parameter that says "Cannot convert the expression's type '(CFMutableDictionaryRef, NSFileHandle?, Unmanaged<CFHTTPMessage>!' to type BooleanLiteralConvertible!.
Anyone know the correct way to do this, or what a BooleanLiteralConvertible is?
It will be clearer if you consider the arguments one at a time:
let m = CFHTTPMessageCreateEmpty(kCFAllocatorDefault, 1).takeRetainedValue()
let f = incomingFileHandle!
let d = self.incomingRequests as CFMutableDictionaryRef
CFDictionaryAddValue(d, unsafeAddressOf(f), unsafeAddressOf(m))
The C Boolean must be expressed as a number.
CFHTTPMessageCreateEmpty needs memory management, as it comes back as an Unmanaged.
Your incomingFileHandle is an Optional; it needs unwrapping.
CFDictionaryAddValue expects unsafe addresses (UnsafePointer).