Error handling with UIAlertController - ios

I have a UIAlertController with a textfield that allows users to type in a title to name a file before sending the data to the server. However, the backend could reject the file for a couple reasons and an error needs to be displayed. How can I display the error I get back from the server in the same alert controller where I entered the file name?
class FileController: UIViewController
{
var alertController: UIAlertController?
func savePressed()
{
createAlert()
}
func createAlert()
{
self.alertController = UIAlertController(title: "Save", message: "Name your file.", preferredStyle: .Alert)
let saveAsPublicAction = UIAlertAction(title: "Make Public", style: .Default) { (_) in
let fileTitle = self.alertController!.textFields![0] as UITextField
if fileTitle.text != ""
{
self.initiateSave(fileTitle.text!, share: true)
}
}
let saveAsPrivateAction = UIAlertAction(title: "Make Private", style: .Default) { (_) in
let fileTitle = self.alertController!.textFields![0] as UITextField
if fileTitle.text != ""
{
self.initiateSave(fileTitle.text!, share: false)
}
}
let cancelAction = UIAlertAction(title: "Cancel", style: .Cancel) { (_) in }
self.alertController!.addTextFieldWithConfigurationHandler { (textField) in
NSNotificationCenter.defaultCenter().addObserverForName(UITextFieldTextDidChangeNotification, object: textField, queue: NSOperationQueue.mainQueue()) { (notification) in
saveAsPrivateAction.enabled = textField.text != ""
saveAsPublicAction.enabled = textField.text != ""
}
}
self.alertController!.addAction(saveAsPublicAction)
self.alertController!.addAction(saveAsPrivateAction)
self.alertController!.addAction(cancelAction)
self.presentViewController(self.alertController!, animated: true, completion: nil)
}
}
func initiateSave(title:String?, share: Bool?)
{
//package file
initiatePost()
}
func initiatePost()
{
//Send file data to server. Receive any errors and handle
}

On your server you can add more logic to send JSON data with that information.
For example:
{
"success": true,
"message": "Data received sucessfully"
}
If The request was successful, if not:
{
"success": false,
"message": "There is an error"
}
So when you parse that JSON you would check if success is false, and display the error message inside of the message key.

Related

Return text from UIAlertController into an extension

In Swift, how can I return the string that I write into the UIAlertController's textfield, added if this UIAlertController is inside an extension
Usually if you put your UIAlertController implementation locally in your class, you can easily pass the text, but when the alert is into an extension, I'm not sure which could be the way to return the text.
For example, assume you have this extension:
extension UIViewController {
func presentTextFieldAlert(title: String, message: String, textFieldPlaceholder: String ) {
let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
let saveAction = UIAlertAction(title: "OK", style: UIAlertAction.Style.default) { _ -> Void in
let urlTextField = alertController.textFields![0] as UITextField
if urlTextField.text != nil { }
}
let cancelAction = UIAlertAction(title: "Cancel", style: .default)
alertController.addTextField { (textField: UITextField!) -> Void in
textField.placeholder = textFieldPlaceholder
}
alertController.addAction(saveAction)
alertController.addAction(cancelAction)
self.present(alertController, animated: true, completion: nil)
}
}
and into your class:
class Client: UIViewController {
func showAlert() {
self.presentTextFieldAlert(title: "Hello", message: "Write sth", textFieldPlaceholder: "Write here")
}
}
how can I pass the text from the alert to the viewcontroller?
I've tried something like:
class Client: UIViewController {
func showAlert() -> String {
return self.presentTextFieldAlert(title: "Hello", message: "Write sth", textFieldPlaceholder: "Write here")
}
}
but I think it is not the correct way.
Use a completion handler.
extension UIViewController {
func presentTextFieldAlert(title: String, message: String, textFieldPlaceholder: String, completion: #escaping (String?)->()) {
let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
let saveAction = UIAlertAction(title: "OK", style: UIAlertAction.Style.default) { _ -> Void in
let urlTextField = alertController.textFields![0] as UITextField
completion(urlTextField.text)
}
let cancelAction = UIAlertAction(title: "Cancel", style: .default)
alertController.addTextField { (textField: UITextField!) -> Void in
textField.placeholder = textFieldPlaceholder
completion(nil)
}
alertController.addAction(saveAction)
alertController.addAction(cancelAction)
self.present(alertController, animated: true, completion: nil)
}
}
Then you can use it as:
class Client: UIViewController {
func showAlert() {
self.presentTextFieldAlert(title: "Hello", message: "Write sth", textFieldPlaceholder: "Write here") { (result) in
if let result = result {
// User entered some text and tapped OK.
}
}
}
}

how can I remove duplicate Contact with OK alert button?

how can I remove duplicate Contact from my table view when tap OK alert button?
Here's my findDuplicateContacts()
#objc fileprivate func findDuplicateContacts() {
let keys = [CNContactIdentifierKey as CNKeyDescriptor, CNContactFormatter.descriptorForRequiredKeys(for: .fullName)]
let request = CNContactFetchRequest(keysToFetch: keys)
var contactsByName = [String: [CNContact]]()
do {
try self.contactStore.enumerateContacts(with: request) { contact, stop in
guard let name = CNContactFormatter.string(from: contact, style: .fullName) else { return }
contactsByName[name] = (contactsByName[name] ?? []) + [contact] // or in Swift 4, `contactsByName[name, default: []].append(contact)`
}
} catch let err {
print("error:", err)
}
let duplicates = contactsByName.filter { $1.count > 1 }
let alert = UIAlertController(title: "Alert", message: "Number of duplicates: \(duplicates.count)", preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: "Ok", style: UIAlertActionStyle.default, handler: {(action:UIAlertAction!) in
//HERE I WANT TO REMOVE DUPLICATES
print("you have pressed the ok button")
}))
alert.addAction(UIAlertAction(title: "Cancel", style: UIAlertActionStyle.default, handler: nil))
self.present(alert, animated: true, completion: nil)
print(duplicates)
self.tableView.reloadData()
}
Thanks in advance for reply
You should use CNSaveRequest class func delete(_ contact: CNMutableContact) method, executing with func execute(_ saveRequest: CNSaveRequest) method of CNContactStore class
This example removes all other contacts and keeps only one (position 0) but you can add a method to determine which contact is more complete and keep that one
Full Code
alert.addAction(UIAlertAction(title: "Ok", style: UIAlertActionStyle.default, handler: {(action:UIAlertAction!) in
print("you have pressed the ok button")
var arrayOfContactsRequests : [CNSaveRequest] = []
for dict in duplicates {
for (index,contact) in dict.value.enumerated() {
if(index != 0) {
let saveRequest = CNSaveRequest()
saveRequest.delete(contact.mutableCopy() as! CNMutableContact)
arrayOfContactsRequests.append(saveRequest)
}
}
}
debugPrint(duplicates)
for request in arrayOfContactsRequests {
do{
try self.contactStore.execute(request)
}
catch let err {
print("error:", err)
}
}
}))
This answer was possible with help of this answer How to convert CNContact to CNMutableContact?

Optimizing a CloudKit sign-in on app launch with error handling. How can I better handle the optional chaining? Swift 3.0/Xcode 8.1

Is there a cleaner, swiftier solution to handle the optional chaining happening in my code below? I'm setting up the user for CloudKit access in my custom function runCKsetup():
func runCKsetup() {
container.requestApplicationPermission(.userDiscoverability) { (status, error) in
guard error == nil else {
if let error = error as? NSError {
if let errorDictionary: AnyObject = error.userInfo as? Dictionary<String, AnyObject> as AnyObject? {
let localizedDescription = errorDictionary["NSLocalizedDescription"]! as! String!
if localizedDescription! == "This operation has been rate limited" {
// Create an alert view
let alert = UIAlertController(title: "Network Error", message: localizedDescription!, preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: "Test for Connection", style: UIAlertActionStyle.default) { (action) in
self.runCKsetup()
})
self.present(alert, animated: true, completion: nil)
} else {
// Create an alert view
let alert = UIAlertController(title: "Sign in to iCloud", message: localizedDescription!, preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: "Ok", style: UIAlertActionStyle.default) { (action) in
})
self.present(alert, animated: true, completion: nil)
}
}
}
return
}
if status == CKApplicationPermissionStatus.granted {
self.container.fetchUserRecordID { (recordID, error) in
guard error == nil else {
self.presentMessageAlert((error?.localizedDescription)!, title: "Error", buttonTitle: "Ok")
return }
guard let recordID = recordID else { return }
self.container.discoverUserIdentity(withUserRecordID: recordID, completionHandler: { (info, fetchError) in
//do something with the users names: e.g. print("\(info?.nameComponents?.givenName) \(info?.nameComponents?.familyName)")
})
}
}
}
}

why is the block of code inside func call is not executed?

I am trying to implement an Xmpp client into my application.I am planning on using the following code inside my application but some part of the code doesn't seem to be executed which prevents me to connect to a server. https://github.com/processone/xmpp-messenger-ios
I am trying to connect to a server with my jabberID and password but for some this code is never executed:
if let _ = error {
let alertController = UIAlertController(title: "Sorry", message: "An error occured: \(error)", preferredStyle: UIAlertControllerStyle.Alert)
alertController.addAction(UIAlertAction(title: "Ok", style: UIAlertActionStyle.Default, handler: { (UIAlertAction) -> Void in
//do smt
}))
self.presentViewController(alertController, animated: true, completion: nil)
} else {
self.dismissViewControllerAnimated(true, completion: nil)
}
Here is the whole code that is related to the problem:
#IBAction func validate(sender: AnyObject) {
if OneChat.sharedInstance.isConnected() {
OneChat.sharedInstance.disconnect()
usernameTextField.hidden = false
passwordTextField.hidden = false
validateButton.setTitle("Validate", forState: UIControlState.Normal)
} else {
OneChat.sharedInstance.connect(username: self.usernameTextField.text!, password: self.passwordTextField.text!) {(stream, error) -> Void in
if let _ = error {
let alertController = UIAlertController(title: "Sorry", message: "An error occured: \(error)", preferredStyle: UIAlertControllerStyle.Alert)
alertController.addAction(UIAlertAction(title: "Ok", style: UIAlertActionStyle.Default, handler: { (UIAlertAction) -> Void in
//do smt
}))
self.presentViewController(alertController, animated: true, completion: nil)
} else {
self.dismissViewControllerAnimated(true, completion: nil)
}
}
}
}
public typealias OneChatConnectCompletionHandler = (stream: XMPPStream, error: DDXMLElement?) -> Void
public func connect(username username: String, password: String, completionHandler completion:OneChatConnectCompletionHandler) {
if isConnected() {
streamDidConnectCompletionBlock = completion
self.streamDidConnectCompletionBlock!(stream: self.xmppStream!, error: nil)
return
}
if (username == "kXMPPmyJID" && NSUserDefaults.standardUserDefaults().stringForKey(kXMPP.myJID) == "kXMPPmyJID") || (username == "kXMPPmyJID" && NSUserDefaults.standardUserDefaults().stringForKey(kXMPP.myJID) == nil) {
streamDidConnectCompletionBlock = completion
streamDidConnectCompletionBlock!(stream: self.xmppStream!, error: DDXMLElement(name: "Please set crendentials before trying to connect"))
return
}
if username != "kXMPPmyJID" {
setValue(username, forKey: kXMPP.myJID)
setValue(password, forKey: kXMPP.myPassword)
}
if let jid = NSUserDefaults.standardUserDefaults().stringForKey(kXMPP.myJID) {
xmppStream?.myJID = XMPPJID.jidWithString(jid)
} else {
streamDidConnectCompletionBlock = completion //was false
streamDidConnectCompletionBlock!(stream: self.xmppStream!, error: DDXMLElement(name: "Bad username"))
}
if let password = NSUserDefaults.standardUserDefaults().stringForKey(kXMPP.myPassword) {
self.password = password
} else {
streamDidConnectCompletionBlock = completion //was false
streamDidConnectCompletionBlock!(stream: self.xmppStream!, error: DDXMLElement(name: "Bad password"))
}
try! xmppStream!.connectWithTimeout(XMPPStreamTimeoutNone)
streamDidConnectCompletionBlock = completion
}
Perhaps, you are expecting a time-out that never will happened (configuration of the library)
I had the same problem and, finally, I fount that my problem was hostname and port. Did you try to use something like this:
let usuario = "name#domain.com";
OneChat.sharedInstance.connect(username: usuario, password: pass) { (stream, error) -> Void in
if let _ = error {
let alertController = UIAlertController(title: "Sorry", message: "An error occured: \(error)", preferredStyle: UIAlertControllerStyle.Alert)
alertController.addAction(UIAlertAction(title: "Ok", style: UIAlertActionStyle.Default, handler: { (UIAlertAction) -> Void in
//do something
}))
self.presentViewController(alertController, animated: true, completion: nil)
} else {
print("I did it!")
}
}

Swift iOS How to display an alert message from the handler of another alert message

I'm trying to implement a forgot password feature in my app so users can enter their email in and have it reset in the database and have the new generated password emailed to them.
I believe my code should work once I fix a few minor issues but the main hurdle I'm having is how to display an alert message from the handler of another alert message.
Any idea how to do it? Either the alert message doesn't show up at all or the first one doesn't close at all.
Here is my attempt at it:
//This function will send a password reset email
func emailPassword(alertAction: UIAlertAction!) -> Void {
let textField = reset_alert.textFields![0] as UITextField
if(!textField.text!.isEmpty){
if(textField != "mik"){
let query = PFQuery(className: "_User")
let forgotten_email = textField.text
query.whereKey("email", equalTo: forgotten_email!)
query.findObjectsInBackgroundWithBlock{
(objects: [PFObject]?, error: NSError?) -> Void in
//NO ERROR//
if(error == nil){
//email in db generate random password
let letters : NSString = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
let randomString : NSMutableString = NSMutableString(capacity: 12)
var users_name = ""
for _ in 0...10{
let length = UInt32 (letters.length)
let rand = arc4random_uniform(length)
randomString.appendFormat("%C", letters.characterAtIndex(Int(rand)))
}
//set new password for user
if let objects = objects {
for object in objects {
object["_hashed_password"] = randomString
users_name = object["name"] as! String
//send password to email
self.mailgun.sendMessageTo("\(users_name) <\(textField)>", from: "")
self.displayAlert("SUCCESS", msg: "Check your email for your new password")
self.reset_alert.dismissViewControllerAnimated(true, completion: nil)
}
}
else{
self.reset_alert.dismissViewControllerAnimated(true, completion: nil)
self.displayAlert("ERROR", msg: "Email not registered to an account")
}
}
//ERROR//
else{
self.reset_alert.dismissViewControllerAnimated(true, completion: nil)
self.displayAlert("ERROR", msg: "Email not registered to an account") }
} //end if textfield not admin email
self.presentViewController(reset_alert, animated: true, completion: nil)
}
}//end of if textfield is empty
}
Try this extension:
typealias AlertActionBlock = (UIAlertAction) -> Void
extension UIViewController {
func flash(title title:String?, message:String?, cancelTitle:String?, actions:UIAlertAction?...) {
let b = {
let alertController = UIAlertController(title: title, message: message, preferredStyle: UIAlertControllerStyle.Alert)
let cancelBlock:AlertActionBlock = {(action:UIAlertAction) -> Void in }
let cancelAction = UIAlertAction(title: cancelTitle, style: UIAlertActionStyle.Cancel, handler: cancelBlock)
alertController.addAction(cancelAction)
for action in actions {if let action = action {alertController.addAction(action)}}
self.presentViewController(alertController, animated: true, completion: nil)
}
if NSThread.isMainThread() {
b()
} else {
dispatch_async(dispatch_get_main_queue(), b)
}
}
}
That should let you call that function from background threads or the main thread just call that function and fill in the variables
EDIT (I didn't realize you need a textfield) so try this:
func flash(title title:String?, message:String?, textFieldConfigurator:(UITextField -> Void)? = nil, cancelTitle:String?, actions:UIAlertAction?...) {
let b = { () -> Void in
let alertController = UIAlertController(title: title, message: message, preferredStyle: UIAlertControllerStyle.Alert)
if let textFieldConfigurator = textFieldConfigurator {alertController.addTextFieldWithConfigurationHandler(textFieldConfigurator)}
let cancelBlock:AlertActionBlock = {(action:UIAlertAction) -> Void in }
let cancelAction = UIAlertAction(title: cancelTitle, style: UIAlertActionStyle.Cancel, handler: cancelBlock)
alertController.addAction(cancelAction)
for action in actions {if let action = action {alertController.addAction(action)}}
self.presentViewController(alertController, animated: true, completion: nil)
}
if NSThread.isMainThread() {
b()
} else {
dispatch_async(dispatch_get_main_queue(), b)
}
}
If you need multiple text fields make it an array and iterate through it. Let me know how it goes!

Resources