Ios – UIAlertController ‘UIAlertAction’ tag/userdata or anything in Swift

iosswiftuialertcontroller

in my iOS actionsheet, I am showing Names from the JSON dictionary:

[
  { "Name": "Doctor for Disease AAA",
    "Doctor_id": "21"
  },
  { "Name": "Doctor for Disease BBB",
    "Doctor_id": "22"
  },
  { "Name": "Doctor for Disease AAA",
    "Doctor_id": "25"
  }
]

So, on button click delegate, I can get the button index and can fetch the corresponding 'Name' and 'Doctor_id'. This is working fine.

But now it seems like 'UIActionSheet' is deprecated, and I have to use 'UIAlertController'. As I have a large data, I am iterating through my array values and calling the alertcontroller handler (so a single function for all button click). But how can I get the button index from UIAlertController, so that I can fetch the 'Name' and 'Doctor_id' simultaneously.

Please help me.

Best Solution

You have multiple possibilities here.

You can use find to get the UIAlertAction index

find let you find the index of an object in an array. You can use it to find the index of the action (that is passed as the parameter of the UIAlertAction's handler, which is the UIAlertAction itself) in the alert.actions array of all actions.

let alert = UIAlertController(title: "Doctors", message: "Choose a doctor", preferredStyle: .ActionSheet)
let closure = { (action: UIAlertAction!) -> Void in
    let index = find(alert.actions as! [UIAlertAction], action)
    println("Index: \(index)")
}
alert.addAction(UIAlertAction(title: "Doc1", style: .Default, handler: closure))
alert.addAction(UIAlertAction(title: "Doc2", style: .Default, handler: closure))
alert.addAction(UIAlertAction(title: "Doc3", style: .Default, handler: closure))
alert.addAction(UIAlertAction(title: "Doc4", style: .Default, handler: closure))
alert.addAction(UIAlertAction(title: "Cancel", style: .Cancel) { _ in
    println("User cancelled.")
})
self.presentViewController(alert, animated: true) {}

You can create a closure… that returns a closure

Create a closure that takes a parameter of your choice (here an Int) and return a closure that captures that parameter so you can use it

 let alert = UIAlertController(title: "Doctors", message: "Choose a doctor", preferredStyle: .ActionSheet)
 let closure = { (index: Int) in
     { (action: UIAlertAction!) -> Void in
         println("Index: \(index)")
     }
 }
 alert.addAction(UIAlertAction(title: "Doc1", style: .Default, handler: closure(0)))
 alert.addAction(UIAlertAction(title: "Doc2", style: .Default, handler: closure(1)))
 alert.addAction(UIAlertAction(title: "Doc3", style: .Default, handler: closure(2)))
 alert.addAction(UIAlertAction(title: "Doc4", style: .Default, handler: closure(3)))
 alert.addAction(UIAlertAction(title: "Cancel", style: .Cancel) { _ in
     println("User cancelled.")
 })
 self.presentViewController(alert, animated: true) {}

This way you have a function (closure) that generate closures for your UIAlertAction handlers, all with the same body except that they capture a different object (a different Int here).

What is really great with this solution is that you can capture anything. You can even capture an hypothetic Doctor object that represent your doctor, or directly the doctor ID, etc.!

Use a loop

But generally you will add your actions using a for loop, so why not take advantage of that, plus take advantage of closure and the fact that they capture variables, to make a nice function that will directly tell your the selected doctor's ID?

func testMyAlert() {
    let doctors = [
        ["Name": "Doctor for Disease AAA", "Doctor_id": "21"],
        ["Name": "Doctor for Disease BBB", "Doctor_id": "22"],
        ["Name": "Doctor for Disease AAA", "Doctor_id": "25"]
    ]

    chooseDoctor(doctors) { selectedDocID in
        if let docID = selectedDocID {
            println("User selected doctor with ID \(docID)")
        } else {
            println("User cancelled, no doctor selected")
        }
    }
}

func chooseDoctor(doctors: Array<[String:String]>, completion: Int?->Void) {
    let alert = UIAlertController(title: "Doctors", message: "Choose a doctor", preferredStyle: .ActionSheet)
    for doc in doctors {
        let action = UIAlertAction(title: doc["Name"]!, style: UIAlertActionStyle.Default) { _ in
            // On selecting this action, get the doctor's ID, convert it to an Int, and return that.
            completion(doc["Doctor_id"]?.toInt())
        }
        alert.addAction(action)
    }
    alert.addAction(UIAlertAction(title: "Cancel", style: UIAlertActionStyle.Cancel) { _ in completion(nil) } )
    self.presentViewController(alert, animated: true) {}

}