let mailComposeViewController = self.configuredMailComposeViewController(randomNumber, email: email!)
if MFMailComposeViewController.canSendMail()
{
self.presentViewController(mailComposeViewController, animated: true, completion { () -> Void in
self.securityCode(randomNumber, email: email!)
})
}
else
{
self.showSendMailErrorAlert()
}
You are using completion incorrectly. Use it like this
self.presentViewController(mailComposeViewController, animated:true) { () -> Void in
self.securityCode(randomNumber, email: email!)
}
Related
So, I added this extension function to the PrimitiveSequenceType to show a loader on screen when making a network call
extension PrimitiveSequenceType where Trait == SingleTrait {
func subscribeWithLoader(showLoaderOn viewController: MyUIViewController, onSuccess: ((Element) -> Void)? = nil, onFailure: ((Swift.Error) -> Void)? = nil)-> Disposable {
let loader = viewController.showLoading()
return subscribe { (element) in
DispatchQueue.main.async {
loader.dismiss(animated: true, completion: {
onSuccess?(element)
})
}
} onFailure: { (error) in
DispatchQueue.main.async {
loader.dismiss(animated: true, completion: {
onFailure?(error)
})
}
}
}
}
Here is my showLoading function
func showLoading()-> UIAlertController {
let alert = UIAlertController(title: nil, message: "Please wait...", preferredStyle: .alert)
let loadingIndicator = UIActivityIndicatorView(frame: CGRect(x: 10, y: 5, width: 50, height: 50))
loadingIndicator.hidesWhenStopped = true
loadingIndicator.style = UIActivityIndicatorView.Style.medium
loadingIndicator.startAnimating();
alert.view.addSubview(loadingIndicator)
present(alert, animated: true, completion: nil)
return alert
}
But the loader never stops. Can anybody let me know what I'm doing wrong. Any help would be appreciated.
It's not very Rx like, but it works... except for one edge case. You dismiss the alert on success and on failure, but what if the Single is disposed without emitting either? Then the alert won't dismiss.
Try something like this instead:
extension PrimitiveSequenceType where Trait == SingleTrait {
func withLoader(showLoaderOn viewController: UIViewController) -> Single<Element> {
func loadingController() -> UIAlertController {
let alert = UIAlertController(title: nil, message: "Please wait...", preferredStyle: .alert)
let loadingIndicator = UIActivityIndicatorView(frame: CGRect(x: 10, y: 5, width: 50, height: 50))
loadingIndicator.hidesWhenStopped = true
loadingIndicator.style = UIActivityIndicatorView.Style.medium
loadingIndicator.startAnimating();
alert.view.addSubview(loadingIndicator)
return alert
}
return Single.create { fullfil in
let loader = loadingController()
viewController.present(loader, animated: true)
let disposable = self.subscribe(fullfil)
return Disposables.create {
disposable.dispose()
loader.dismiss(animated: true)
}
}
}
}
If you like wrapping view controllers up like this. Check out this library... Cause-Logic-Effect
So, I end up adding a delay like this
extension PrimitiveSequenceType where Trait == SingleTrait {
func subscribeWithLoader(showLoaderOn viewController: MyUIViewController, onSuccess: ((Element) -> Void)? = nil, onFailure: ((Swift.Error) -> Void)? = nil)-> Disposable {
let loader = viewController.showLoading()
return subscribe { (element) in
onSuccess?(element)
DispatchQueue.global().asyncAfter(deadline: .now() + 0.5, execute: {
DispatchQueue.main.async {
loader.dismiss(animated: true, completion: nil)
}
})
} onFailure: { (error) in
onFailure?(error)
DispatchQueue.global().asyncAfter(deadline: .now() + 0.5, execute: {
DispatchQueue.main.async {
loader.dismiss(animated: true, completion: nil)
}
})
} onDisposed: {
DispatchQueue.global().asyncAfter(deadline: .now() + 0.5, execute: {
DispatchQueue.main.async {
loader.dismiss(animated: true, completion: nil)
}
})
}
}
}
I think the problem was onSuccess (that means calling loader.dismiss) was getting called even before UIAlertController could show itself. So, by adding a delay of 500ms solves the issue the UIAlertController is going to have enough time to show itself, and then we are dismissing it.
Open to new ideas and improvements.
I want to ask I have a sign in view controller who don't want to dismiss after correctly add email and password. but when I try the simulator for the first time the sign in is working and directing to me to my home controller, but after I sign out. and try to sign in again, then the sign in not dismissing my sign in view controller. how is that possible? at first is working later on is not working, here I show you my code.
// this is my sign out button
#objc private func handleSignOut() {
let alert = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
alert.addAction(UIAlertAction(title: "Log Out".localized(), style: .destructive, handler: { (_) in
self.progressHUD.show(in: self.view)
ProfileServices.shared.signOutUser { success in
if success {
self.progressHUD.dismiss(animated: true)
let signInVC = SigninViewController()
self.present(signInVC, animated: true, completion: nil)
} else {
self.progressHUD.textLabel.text = "Error"
self.progressHUD.dismiss(afterDelay: 0.4)
}
}
}))
alert.addAction(UIAlertAction(title: "Cancel".localized(), style: .cancel, handler: nil))
present(alert, animated: true, completion: nil)
}
// this is my sign out function in ProfileServices.shared
func signOutUser(completion: #escaping (Bool) -> Void) {
AF.request(API_URL.AUTHENTICATION.LOGOUT, method: .delete, parameters: nil, encoding: URLEncoding.default, headers: HEADERS, interceptor: nil).responseData { (dataResponse) in
if dataResponse.error == nil {
let domain = Bundle.main.bundleIdentifier!
UserDefaults.standard.removePersistentDomain(forName: domain)
UserDefaults.standard.synchronize()
UserDefaults.removeToken()
completion(true)
} else {
completion(false)
}
}
}
// this is my sign in route in my sign in view controller
func routeToMainView(_ data: SigninModel.Response) {
let school = UserDefaults.getSelectedSchool()
guard let schools = data.schools?.schools else { return }
if let selectedSchool = school, let selected = schools.first(where: { $0.id == selectedSchool.id}) {
UserDefaults.saveSelectedSchool(data: selected)
let vc = MainViewController()
self.viewController?.navigationController?.setViewControllers([vc], animated: true)
} else {
if schools.count > 1 {
let vc = SwitchSchoolViewController()
self.viewController?.navigationController?.setViewControllers([vc], animated: true)
} else {
guard let selected = schools.first else { return }
UserDefaults.saveSelectedSchool(data: selected)
DispatchQueue.main.async {
let vc = MainViewController()
self.viewController?.navigationController?.setViewControllers([vc], animated: true)
}
}
}
}
// this is in my appDelegate
var root: UIViewController?
root = SigninViewController()
if UserDefaults.getToken() != nil {
root = MainViewController()
}
In logout you need to dissmiss the presented viewController.
inplace :
let signInVC = SigninViewController()
self.present(signInVC, animated: true, completion: nil)
you need to use:
self.dismiss(animated: true, completion: nil)
or pop if you will use Push.
I have a problem with understanding how work with view/delegate and completion.
I use library which have callback - something like:
func youShouldChoose()->String.
I desided to give a choice to user and open popover. But I don't understand how to return the selected value.
I read about completion. So i've tried this:
func youShouldChoose() -> String {
askUser()
return self.valueForResult //This line is executed earlier than askUser is finished
}
func askUser(){
showAlert(completion: {(result)->Void in
self.valueForResult = result
})
}
func showAlert(completion:#escaping (_ result:String)->Void)
{
let alert = UIAlertController(...)
alert.addAction(UIAlertAction(title: "Click", style: UIAlertAction.Style.default, handler: { action in
completion(textField.text)
}))
alert.addTextField(configurationHandler: {(textField: UITextField!) in
textField.placeholder = "Enter text:"
})
self.present(alert, animated: true, completion: nil )
}
How can I wait until askUser() will end completely? Is there a way to return value from completion to my library?
I found two ways to solve out this problem:
1. Use loop. Showing view until flag is false
askUser() //we should set flag to true here
while( flag == false ) {
CFRunLoopRunInMode(CFRunLoopMode.defaultMode, 1, true);
}
return self.valueForResult
Use semaphore
let semaphore = DispatchSemaphore(value: 0)
askUser()
semaphore.lock()
return self.valueForResult
Here is an example solution (Swift 4.2 / 5.0):
func youShouldChoose(_ completion: #escaping ((String) -> Void)) {
askUser(completion) // handing over the completion block to `askUser.
// Alternative completion block execution:
// askUser { (enteredText) in
// // This block is called when the "Click" action button on the alert was tapped.
// completion(enteredText)
// }
}
func askUser(_ completion: #escaping ((String) -> Void)) {
showAlert(completion) // handing over the completion block to `showAlert`.
}
func showAlert(_ completion: #escaping (String) -> Void) {
let alert = UIAlertController(title: nil, message: nil, preferredStyle: .alert)
alert.addAction(UIAlertAction.init(title: "Click", style: .default, handler: { (_) in
if let textField = alert.textFields?.first, let text = textField.text {
completion(text) // -> handing over the text of the textField!
} else {
// No text field or text available. Something went wrong!
}
}))
alert.addTextField { (textField) in
textField.placeholder = "Enter text:"
}
self.present(alert, animated: true, completion: nil)
}
// How to use `youShouldChoose `:
func foo() {
youShouldChoose { (enteredText) in
// This block is called when `youShouldChoose` is finished.
print(enteredText) // -> prints the user's entered text.
print("Hello")
}
}
I am integrating payU money in my iOS app. Everything is working fine except one thing. Whenever I am returning from the payU money page all my values that are stored in UserDefaults are removed. I am using this code.
func startPaymentFlow() -> Void {
UserDefaults.standard.set( "test" , forKey: "test")
Toast(text:"Please wait.... Generating hash from server").show()
let paymentVC : PUMMainVController = PUMMainVController()
var paymentNavController : UINavigationController;
paymentNavController = UINavigationController(rootViewController: paymentVC)
self.present(paymentNavController, animated: true, completion: nil)
}
func transactionCompleted(withResponse response : NSDictionary,errorDescription error:NSError) -> Void {
self.dismiss(animated: true){
self.showAlertViewWithTitle(title: "Message", message: "congrats! Payment is Successful")
self.Status = "1"
self.MobilePaymentUpdateApi()
}
}
func transactinFailed(withResponse response : NSDictionary,errorDescription error:NSError) -> Void {
self.dismiss(animated: true){
self.showAlertViewWithTitle(title: "Message", message: "Oops!!! Payment Failed")
self.Status = "-1"
self.MobilePaymentUpdateApi()
}
}
func transactinCanceledByUser() -> Void {
print(UserDefaults.standard.value(forKey: "test") as! String)
self.dismiss(animated: true){
self.showAlertViewWithTitle(title: "Message", message: "Payment Cancelled ")
self.Status = "-1"
print(self.Status)
self.MobilePaymentUpdateApi()
}
}
getting this just after execution of
self.present(paymentNavController, animated: true, completion: nil)
*** -[NSKeyedUnarchiver initForReadingWithData:]: data is NULL
I am trying add a nullable completion block to a custom function
func disPlayAlertMessage(titleMessage:String, alertMsg:String, completion: (() -> Void)? = nil){
AlertMessage.alertMessageController = UIAlertController(title: titleMessage, message:
alertMsg, preferredStyle: UIAlertControllerStyle.Alert)
AlertMessage.alertMessageController.addAction(UIAlertAction(title: "Ok", style: UIAlertActionStyle.Default,handler: nil))
if completion == nil {
controller.presentViewController(AlertMessage.alertMessageController, animated: true, completion: nil)
} else {
controller.presentViewController(AlertMessage.alertMessageController, animated: true, completion: {
completion!()
})
}
return
}
When I am trying to call the above function like below
AlertMessage(controller: self).disPlayAlertMessage(CustomAlertMessages.AlertTitle, alertMsg: CustomAlertMessages.DOANoUpdate, completion: { () -> Void in
{
self.navigationController?.popViewControllerAnimated(true)
}
})
The completion block is always nil.
Here is how you define a nil'able completion
func function(completion: (Void -> Void)? = nil) {
completion?()
}
There are a few different ways in which you can call it
function() //without any argument
function({ //with parens and braces
print("I will get called")
})
function() { //with parens and braces
print("I will get called")
}
function { //without parens
print("I will get called")
}
Edit: Tested with Swift 2.0 only..
You should change your completion parameter.
Example:
func Test( completion: () -> () = {_ in }) {
completion()
}
This function can be called in two different ways:
Test() // Nothing happens
Test({ print("Completed") }) // Prints Completed
Hope this helps :)
This works for me
typealias CompletionHandler = (_ success:Bool) -> Void
func yourCompletionBlockName(completionHandler: CompletionHandler) {
//code
let flag = true
completionHandler(flag)
}
call completion block whenever you need
yourCompletionBlockName(completionHandler: { (success) -> Void in
if success {
} else {
}
})