Swift filter array using NSPredicate
Asked Answered
C

3

6

I have an application written in Swift that is pulling in the users contacts from their address book.

I want to filter out the contact that only contain a company name (so that you get your "assumed" real person contact and not businesses)

Here is how this is being accomplish in the Objective-C version of my app:

NSArray *allContacts = (__bridge_transfer NSArray *)ABAddressBookCopyArrayOfAllPeople(addressBook);

NSPredicate *predicate = [NSPredicate predicateWithBlock:^BOOL(id person, NSDictionary *bindings) {
    NSString *firstName = CFBridgingRelease(ABRecordCopyValue((__bridge ABRecordRef)person, kABPersonFirstNameProperty));
    NSString *lastName  = CFBridgingRelease(ABRecordCopyValue((__bridge ABRecordRef)person, kABPersonLastNameProperty));

    return (firstName || lastName);
}];

NSArray *peopleNotCompanies = [allContacts filteredArrayUsingPredicate:predicate];

This works perfectly, so here is my attempt to do this in Swift:

var contactList: NSArray = ABAddressBookCopyArrayOfAllPeople(addressBook).takeRetainedValue()

var predicate: NSPredicate = NSPredicate { (AnyObject person, NSDictionary bindings) -> Bool in
    var firstName: String = ABRecordCopyValue(person as ABRecordRef, kABPersonFirstNameProperty).takeRetainedValue() as String
    var lastName: String = ABRecordCopyValue(person as ABRecordRef, kABPersonLastNameProperty).takeRetainedValue() as String

    return firstName || lastName
})

Now this has a couple problems. I am getting these errors on the return statement and the end of the predicate call:

Error Messages

How can I provide similar functionality found in my ObjC code in Swift? Or is there a better way in swift to check if a contact has ONLY a company name and then omit it from the final array?

Thanks!

Cogitate answered 7/9, 2014 at 23:42 Comment(0)
R
5

If you have firstName and lastName be optional strings, you can compare them against nil and use them in a boolean expression.

Your second error is due to the extra paren after your closure. This code should work.

var predicate: NSPredicate = NSPredicate { (AnyObject person, NSDictionary bindings) -> Bool in
    var firstName: String? = ABRecordCopyValue(person as ABRecordRef, kABPersonFirstNameProperty).takeRetainedValue() as? String
    var lastName: String? = ABRecordCopyValue(person as ABRecordRef, kABPersonLastNameProperty).takeRetainedValue() as? String

    return firstName != nil || lastName != nil
}
Run answered 8/9, 2014 at 1:28 Comment(2)
If you use NSArray instead of Array, you can then perform this line of code: let filteredContacts: NSArray = contactList.filteredArrayUsingPredicate(predicate)Cogitate
Thank you for the clarification, the extra ) was really throwing me off because I didn't notice it!Cogitate
I
5

If you convert the NSArray into a Swift Array, you can use Swift's Array.filter method. Here's an example with simpler objects for clarity:

let arrObjc: NSArray = ["aaa", "bab", "bbb", "baa", "cbc"]
let arr: [AnyObject] = arrObjc //create a swift array with same data

// filter takes a block that returns a boolean. True: keep the item, False: drop it.
arr.filter{
  if let s = $0 as? String {  // first try to cast AnyObject to our expected type.
    return contains(s, "a")   // return true if we want this item, false otherwise.
  } else {
    return false              // this object is of the wrong type, return false.
  }
}

// returns: ["aaa", "bab", "baa"]
Irreligious answered 8/9, 2014 at 1:42 Comment(0)
S
0

Your test at the last line in Objective-C will return if firstName is nil or if lastName is nil. In your swift code, you are just trying to compare two strings as if they were Bools. Instead, you want to return if firstName exists, so you should instead do this in both your objective-c and Swift code:

return firstName != nil
Simon answered 7/9, 2014 at 23:55 Comment(2)
Doesn't seem to work either, throws another type of error. The more I am looking into this though, it seems that I am wasting my time because Swift does not have a filteredArrayUsingPredicate: method. So I really need to check each ABRecordRef and see if first name and last name are nil while the company value is not, and then ignore that ABRecordRefCogitate
@KyleBegeman Swift has access to all of the same methods as you have access to in Objective-C you just need to look at NSArray rather than Swift's native array class. What's the new error?Pocket

© 2022 - 2024 — McMap. All rights reserved.