How does Square's CardCase app automatically populate the user's details from the address book?
Asked Answered
R

5

21

Square's new card case iOS app has a "Create Account" feature. Tap it and it shows a form PREPOPULATED with the user's entry from the Address book.

How is this possible? Anyone know? I thought this was unpossible, to get the user's info this way. It's not an iOS 5.0 thing, afaict.

Rump answered 3/11, 2011 at 19:46 Comment(0)
M
56

the only solution I could come up with was using the device name and then searching the addressbook for a match. this assumes someone would use a particular naming convention. I for example use 'Nik's iPhone' as my device name. I am also the only Nik in my addressbook, so for my scenario is works well to use the text prior to 's as the owner name.

It makes use of the very handy wrapper for ABAddressBook by Erica Sadun, ABContactHelper. I've left the enumeration code in instead of using array index at 0 as likely a small number of matches will be returned so you could expand to give the user an option to choose 'their' details. Which whilst not exactly matching the square case solution works well. imho.

NSString *firstname;
NSString *lastname;

NSString *ownerName = [[UIDevice currentDevice] name];

NSRange t = [ownerName rangeOfString:@"'s"];
if (t.location != NSNotFound) {
    ownerName = [ownerName substringToIndex:t.location];
} 

NSArray *matches = [ABContactsHelper contactsMatchingName:ownerName];
if(matches.count == 1){ 
    for (ABContact *contact in matches){
        firstname = [contact firstname];
        lastname = [contact lastname];

    }
}
Meadors answered 8/11, 2011 at 22:51 Comment(6)
I can confirm that this appears to be how they do it. If you change your device name to a name that doesn't exist in the address book, then Card Case shows the new name derived from the device name.Institutional
Does it still work now? Won't the phone ask for address book permissions?Bewilderment
Excuse my ignorance, but - is the owner's phone number always in the address book?Rabbi
not always. Probably best to try and identify a match based on device name and either give match options to choose from or fail gracefully with fallback capture fields.Meadors
Cool idea to get owner's detailZosima
A tip: The owners name is different depending on the language the device is set to. E.g, in Sweden this is without the apostrophe. But, by removing the apostrophe your code is more vulnerable for get an incorrect substring; so add the device model to it and you should be more safe.Bivins
E
23

Since the changes that require apps to ask for permissions to access the address book this method no longer works. Nik Burns' answer worked great, but it needs access to the address book.

I downloaded the Square Wallet (which is the successor to Square Card Case references in the original question) and it no longer pre-populates the sign up form. Path also used to do the device name address book trick but it also no longer pre-populates the sign up form.

Using the above method, you could still pre-populate the sign up form after requesting Contacts access but it loses the desired "magic" experience.

Edrick answered 24/4, 2013 at 21:40 Comment(0)
J
1

Since I was not happy with having to use the device name I chose to use the first record of the address book which corresponds to "ME"

ABAddressBookRef addressBook = ABAddressBookCreate( );
CFArrayRef allPeople = ABAddressBookCopyArrayOfAllPeople( addressBook );
ABRecordRef ref = CFArrayGetValueAtIndex( allPeople, 0 );
ABContact * contact = [ABContact contactWithRecord:ref];
Jibber answered 14/1, 2016 at 23:0 Comment(0)
S
0

No you can

http://developer.apple.com/library/ios/#documentation/ContactData/Conceptual/AddressBookProgrammingGuideforiPhone/Introduction.html#//apple_ref/doc/uid/TP40007744

I know it was available in 4.X; not just a 5.0 feature... Not sure if it was available earlier.

Sternum answered 3/11, 2011 at 19:53 Comment(5)
Still not seeing it. Where in the doc can you get the user's account info?Rump
You still have to query for it, there's not a method like .me on on Mac OS X. I have seen people prompt for it, i.e. pop up the people picker, allow them to select themselves than store the ID. Or other have gotten the phone number, then queries the address book for that phone number, etc.Sternum
Yes, this isn't how the Square app does it. Still baffled.Rump
I think he's just saying this will give you programmatic access to the user's address book. As to Square's specific algorithm for determining who you are, i'm not sure. I do know that you can assign yourself to siri so she knows who you are, so i'm not sure if there is an api to directly access who you are, or if Square is cross-correlating multiple data sources to determine your contact info in your address book.Gros
Well, I threw the phone behind a proxy on my macbook and all they do is post to Flurry on startup. And if I change my picture it picks it up immediately. So they are definitely querying the ABAddressBook but as far as I can tell, this is forbidden. I want to do this in my app, but I think it's something I would get rejected for.Rump
B
0

I had the same issue, but in Swift, and used Nik Burns suggestion and wrote a blog post about it: http://paulpeelen.com/2016/01/20/prefill-an-signup-form-in-ios-using-swift-and-cncontact/

This is basically the end result in Swift:

import Contacts

...

    @IBOutlet weak var nameFirst: UILabel!
    @IBOutlet weak var nameLast: UILabel!
    @IBOutlet weak var email: UILabel!
    @IBOutlet weak var phoneNo: UILabel!
    @IBOutlet weak var address: UILabel!

    /**
     Search the addressbook for the contact
     */
    private func getMe() {
        let ownerName = getOwnerName()
        let store = CNContactStore()

        do {
            // Ask permission from the addressbook and search for the user using the owners name
            let contacts = try store.unifiedContactsMatchingPredicate(CNContact.predicateForContactsMatchingName(ownerName), keysToFetch:[CNContactGivenNameKey, CNContactFamilyNameKey, CNContactPhoneNumbersKey, CNContactPostalAddressesKey, CNContactEmailAddressesKey])

            //assuming contain at least one contact
            if let contact = contacts.first {
                // Checking if phone number is available for the given contact.
                if (contact.isKeyAvailable(CNContactPhoneNumbersKey)) {
                    // Populate the fields
                    populateUsingContact(contact)
                } else {
                    //Refetch the keys
                    let keysToFetch = [CNContactGivenNameKey, CNContactFamilyNameKey, CNContactPhoneNumbersKey, CNContactPostalAddressesKey, CNContactEmailAddressesKey]
                    let refetchedContact = try store.unifiedContactWithIdentifier(contact.identifier, keysToFetch: keysToFetch)

                    // Populate the fields
                    populateUsingContact(refetchedContact)
                }
            }
        } catch let e as NSError {
            print(e.localizedDescription)
        }

    }

    /**
     Get the device owners name

     - returns: The owners name
     */
    private func getOwnerName() -> String {
        // Get the device owners name
        var ownerName = UIDevice.currentDevice().name.trimmedString.stringByReplacingOccurrencesOfString("'", withString: "")
        // Get the model of the device
        let model = UIDevice.currentDevice().model

        // Remove the device name from the owners name
        if let t = ownerName.rangeOfString("s \(model)") {
            ownerName = ownerName.substringToIndex(t.startIndex)
        }

        return ownerName.trimmedString
    }

    /**
     Populate the fields using a contact

     - parameter contact: The contact to use
     */
    private func populateUsingContact(contact: CNContact) {
        // Set the name fields
        nameFirst.text = contact.givenName
        nameLast.text = contact.familyName

        // Check if there is an address available, it might be empty
        if (contact.isKeyAvailable(CNContactPostalAddressesKey)) {
            if let
                addrv = contact.postalAddresses.first,
                addr = addrv.value as? CNPostalAddress where addrv.value is CNPostalAddress
            {
                let address = "\(addr.street)\n\(addr.postalCode) \(addr.city)\n\(addr.country)"
                self.address.text = address
            }
        }

        // Check if there is a phonenumber available, it might be empty
        if (contact.isKeyAvailable(CNContactPhoneNumbersKey)) {
            if let
                phonenumberValue = contact.phoneNumbers.first,
                pn = phonenumberValue.value as? CNPhoneNumber where phonenumberValue.value is CNPhoneNumber
            {
                phoneNo.text = pn.stringValue
            }
        }
    }

And the extension:

extension String {

    /// Trim a string
    var trimmedString: String {
        return stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceAndNewlineCharacterSet())
    }

}
Bivins answered 20/1, 2016 at 15:12 Comment(1)
Strangely my own phone number is not in the Contact list. Is this a new change in the newer iOS versions? Do I have to add myself manually or does it happen automatically?Nevernever

© 2022 - 2024 — McMap. All rights reserved.