alex
alex

Reputation: 4914

How do i pass a dictionary as a function parameter?

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

Upvotes: 7

Views: 27507

Answers (4)

rsmoz
rsmoz

Reputation: 547

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.

Upvotes: 10

Sweeper
Sweeper

Reputation: 271040

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> {

Upvotes: 6

Cristik
Cristik

Reputation: 32786

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

Upvotes: 0

Alex Pelletier
Alex Pelletier

Reputation: 5123

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.

Upvotes: 2

Related Questions