7

I am trying to pass a dictionary as a function parameter. I have the following function

func makeAndAddVisitorRecord2(visitorDict: Dictionary) -> ABRecordRef <AnyObject, AnyObject> {
    let visitorRecord: ABRecordRef = ABPersonCreate().takeRetainedValue()
    ABRecordSetValue(visitorRecord, kABPersonFirstNameProperty, visitorDict[1], nil)
    ABRecordSetValue(visitorRecord, kABPersonLastNameProperty, visitorDict[2], nil)
    //ABRecordSetValue(visitorRecord, kABPersonEmailProperty, visitorDict[5], nil)

    let phoneNumbers: ABMutableMultiValue =
    ABMultiValueCreateMutable(ABPropertyType(kABMultiStringPropertyType)).takeRetainedValue()
    ABMultiValueAddValueAndLabel(phoneNumbers, visitorDict["visitorPhone"], kABPersonPhoneMainLabel, nil)
    ABRecordSetValue(visitorRecord, kABPersonPhoneProperty, phoneNumbers, nil)

    ABAddressBookAddRecord(addressBookRef, visitorRecord, nil)
    saveAddressBookChanges()

    return visitorRecord
}

Which i like to trigger by

func addVisitorToContacts(sender: AnyObject) {
    //let visitor = ListVisitors[visitorButton.tag]
    var visitorDict:[Int:String] = [1:"\(visitorName)", 2:"\(visitorCompany)", 3:"\(visitorCity)",
        4:"\(visitorPhone)", 5:"\(visitorEmail)"]

    let visitorRecord: ABRecordRef = makeAndAddVisitorRecord2(visitorDict)
    let contactAddedAlert = UIAlertController(title: "\(visitorName) was successfully added.",
        message: nil, preferredStyle: .Alert)
    contactAddedAlert.addAction(UIAlertAction(title: "OK", style: .Cancel, handler: nil))
    presentViewController(contactAddedAlert, animated: true, completion: nil)
}

But makeAndAddVisitorRecord2 compiles an error

 Cannot specialize non-generic type 'ABRecordRef' (aka 'AnyObject')

[EDIT 1] workable solution but not optimal as i am not using my Visitor struct

func makeAndAddVisitorRecord2(visitorDict: Dictionary <Int, String>) -> ABRecordRef  {

[EDIT 2] as @rsmoz pointed out i should use my Visitor struct

class Visitor {

var visitorName : String
var visitorCompany : String
var visitorPlace : String
var visitorPhone : String
var visitorEmail : String

init(visitorName: String, visitorCompany: String, visitorPlace: String, visitorPhone: String, visitorEmail: String) {
    self.visitorName = visitorName
    self.visitorCompany = visitorCompany
    self.visitorPlace = visitorPlace
    self.visitorPhone = visitorPhone
    self.visitorEmail = visitorEmail
}

}

So i have a ListVisitors class which generates some Visitors and looks like

class ListVisitors{
    static var sharedInstance = [Visitor]()

static func load()
    {
        // @todo: stored and loaded data
        var visitor = Visitor(visitorName: "From Class Matt", visitorCompany: "Google", visitorPlace: "San Diego", visitorPhone: "94888484", visitorEmail: "[email protected]")
        sharedInstance = [visitor]

        visitor = Visitor(visitorName: "From Class John", visitorCompany: "nike", visitorPlace: "New York", visitorPhone: "94888484", visitorEmail: "[email protected]")
        //        ListVisitors.sharedInstance += [visitor]
        sharedInstance += [visitor]
...
}
}

And in my main controller i have a table view and a selected row sends the visitor details to detailcontroller (HOW can i have the selected visitor struct in detail view controller?? Should i pass let selectedVisitor to the detail view controller?)

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?){
if (segue.identifier == "visitorDetails") {

            if let indexPath = tableView.indexPathForCell(sender as! UITableViewCell) {

                let selectedVisitor = lVisitors[indexPath.row] as Visitor

                let detailVC = segue.destinationViewController as! DetailViewController

                detailVC.visitorName = selectedVisitor.visitorName
                detailVC.visitorCompany = selectedVisitor.visitorCompany
                detailVC.visitorPlace = selectedVisitor.visitorPlace
                detailVC.visitorPhone = selectedVisitor.visitorPhone
                detailVC.visitorEmail = selectedVisitor.visitorEmail

            } // .end accessory select

        } // .end segue
3
  • Possible duplicate of How to pass dictionary as a parameter in the methind in Swift?
    – stek29
    Commented Jan 3, 2016 at 11:18
  • Why do you need Int:String dictionary? Just use array.
    – stek29
    Commented Jan 3, 2016 at 11:20
  • @stek29 hmm i tried it let visitorArr: Array = [String]() and visitorArr["visitorName"] = visitorName and tried to pass the array as parameter. Could you provide a simple code example??? Any why would using an array > dictionary??
    – alex
    Commented Jan 3, 2016 at 11:24

4 Answers 4

10

I'm not sure what you're trying to do with ABRecordRef <AnyObject, AnyObject>, but the <> syntax is for specifying a generic type. Like, an array that holds strings is Array<String>. ABRecordRef is not a generic type.

The Dictionary needs to have the types it holds specified in the parameter: Dictionary<String, Int>

Also, you're treating a dictionary like an array. Better to use a dictionary as it's meant to be used. Instead of [1:"\(visitorName)"], why not ["visitorName":visitorName]? That way you can access it like dict["visitorName"] You also don't need to do "\(visitorName)" if visitorName is a String to begin with. Just use the variable directly.

It would be even better, though, to represent a Visitor as a struct, not an array or dictionary:

struct Visitor {
    let name: String
    let company: String
    let city: String
    let phone: String //Yes, this should be a String and not an Int
    let email: String
}

And you could set it like this:

let v = Visitor(name: "Joe", company: "A Corp", city: "New York", phone: "+44 392 39275 22", email: "[email protected]")

And access it like this:

v.name

And that's just so much cleaner and safer. Now your code won't have any errors from accidentally accessing the wrong key on a dictionary.

Oh, and you should be using the Contacts framework these days, not ABAddressBook.

8
  • 2
    Actually, Dictionary<Int:String> won't work. It's Dictionary<Int, String>. Use a comma, not a colon.
    – Sweeper
    Commented Jan 3, 2016 at 12:06
  • @rsmoz, i ralready have a Visitor struct, which i use. But in detailviewcontroller i don't know how to use Visitor struct again, i just "spit out" the values like ` override func viewDidLoad() { super.viewDidLoad() lblName.text = visitorName lblCompany.text = visitorCompany ....` So what i did i created an "temp" dictonary which holds the values from Visitor struct
    – alex
    Commented Jan 3, 2016 at 12:07
  • Maybe if you showed more of your code I could help you better. You should still be able to use the struct within your file.
    – rsmoz
    Commented Jan 3, 2016 at 12:09
  • @rsmoz, i have edited my question a bit. Hope you give me some more tips. I am stuck with using the right "selected"/passing the right visitor struct to detail view controller BTW i am developing for > iOS 8 and Contacts framework is > iOS 9 isn't it?
    – alex
    Commented Jan 3, 2016 at 12:28
  • @rsmoz would you mind taking another look, how i can pass the right struct/class between viewcontrollers?
    – alex
    Commented Jan 4, 2016 at 5:38
6

The dictionary type in Swift is called Dictionary. However, it is generic, meaning that you need to add <> after the type name to specify what type of dictionary you want it to be. In this case, it is Dictionary<Int, String>. This is because the variable that you are passing to the method (visitorDict) is of type Dictionary<Int, String>.

Write your function header like this:

func makeAndAddVisitorRecord2(visitorDict: Dictionary<Int, String>) -> ABRecordRef <AnyObject, AnyObject> {

If you want to go one step further, you can use the shorthand type name for dictionary:

func makeAndAddVisitorRecord2(visitorDict: [Int: String]) -> ABRecordRef <AnyObject, AnyObject> {

Also, if your keys for the dictionary are sequential, like 1, 2, 3, 4 ,5 etc, you can use an array instead:

func makeAndAddVisitorRecord2(visitorDict: Array<String>) -> ABRecordRef <AnyObject, AnyObject> {

And the shorthand for arrays is:

func makeAndAddVisitorRecord2(visitorDict: [String]) -> ABRecordRef <AnyObject, AnyObject> {
2

You have to specify what type of dictionary it is in swift, like so:

func makeAndAddVisitorRecord2(visitorDict: [Int: String]) -> ABRecordRef <AnyObject, AnyObject> {
    let visitorRecord: ABRecordRef = ABPersonCreate().takeRetainedValue()
    ABRecordSetValue(visitorRecord, kABPersonFirstNameProperty, visitorDict[1], nil)
    ABRecordSetValue(visitorRecord, kABPersonLastNameProperty, visitorDict[2], nil)
    //ABRecordSetValue(visitorRecord, kABPersonEmailProperty, visitorDict[5], nil)

    let phoneNumbers: ABMutableMultiValue =
    ABMultiValueCreateMutable(ABPropertyType(kABMultiStringPropertyType)).takeRetainedValue()
    ABMultiValueAddValueAndLabel(phoneNumbers, visitorDict["visitorPhone"], kABPersonPhoneMainLabel, nil)
    ABRecordSetValue(visitorRecord, kABPersonPhoneProperty, phoneNumbers, nil)

    ABAddressBookAddRecord(addressBookRef, visitorRecord, nil)
    saveAddressBookChanges()

    return visitorRecord
}

Then you can use makeAndAddVisitorRecord2(visitorDict) and everything should work.

You collection types apple doc has more in-depth examples.

0

ABRecordRef is not generic, thus you cannot use like you did. Most likely you wanted to add the generic declaration to the Dictionary parameter.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.