Constant Availability Check in Swift

When I was writing a compatibility wrapper for AddressBook and Contacts frameworks, I found this code does not work in iOS 7:

import Foundation
import AddressBook
import AddressBookUI
import Contacts
import ContactsUI
private var k2Key: [ABPropertyID: String] = [
    ://...
]
private func setupConstants() {
    //...
    if #available(iOS 8.0, *) {
        // Alternate birthday
        //@availability(iOS, introduced=8.0)
        k2Key[kABPersonAlternateBirthdayProperty] = MyContactNonGregorianBirthdayKey
    }
    //...
}

As shown in the comment, the constant kABPersonAlternateBirthdayProperty is introduced in iOS 8.0, so I enclosed the code with #available check.

But it causes a runtime error, when run on iOS 7 device:

dyld: Symbol not found: _kABPersonAlternateBirthdayProperty
  Referenced from: /var/mobile/Applications/3F5FB593-AC7B-4F5A-9563-66A5949769D4/QuickContacts.app/QuickContacts
  Expected in: /System/Library/Frameworks/AddressBook.framework/AddressBook
 in /var/mobile/Applications/3F5FB593-AC7B-4F5A-9563-66A5949769D4/QuickContacts.app/QuickContacts


How do you check availability of a constant and use it safely?

Replies

It looks like the new

AB_DEPRECATED
macro, which decorates all the Address Book framework, does not take into account when the symbol was * introduced. I’d call that a bug and I recommend that you file it as such. Please post your bug number, just for the record.

As to a workaround, you may be able to cobble something together with a bridging header hack but if I were in your shoes I’d do one of two things:

  • access the symbolic dynamically

  • bounce over to Objective-C and, in your Objective-C code, redeclare the symbol with the correct availability settings

Share and Enjoy

Quinn "The Eskimo!"
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Thanks, eskimo.


access the symbolic dynamically

This is what I use now:

    let RTLD_DEFAULT = UnsafeMutablePointer<Void>(bitPattern: -2)
    if dlsym(RTLD_DEFAULT, "kABPersonAlternateBirthdayProperty") != nil {
        let kAddress = dlsym(RTLD_DEFAULT, "kABPersonAlternateBirthdayProperty")
        let kABPersonAlternateBirthdayProperty = UnsafePointer<ABPropertyID>(kAddress).memory
        //...code using kABPersonAlternateBirthdayProperty
    }


I'll report bug number later.