Dismiss alert after task on MainViewController swift - swift

I need to download some content from API when users login for the first time at my app and show them I'm doing that. I do this at my MainViewController:
override func viewDidAppear(_ animated: Bool) {
let alert = UIAlertController(title: nil, message: "Wait please...", preferredStyle: .alert)
let loadingIndicator = UIActivityIndicatorView(frame: CGRect(x: 10, y: 5, width: 50, height: 50))
loadingIndicator.hidesWhenStopped = true
loadingIndicator.style = UIActivityIndicatorView.Style.gray
loadingIndicator.startAnimating();
alert.view.addSubview(loadingIndicator)
present(alert, animated: true, completion: nil)
let parceiroId = self.defaults.getParceiroId()
if !self.defaults.getDownloadInicialTipoEntrega() {
TipoEntregaAPI().loadTiposEntrega(parceiroId){ (dados) in
if dados != nil {
for tipoEntrega in dados!{
// Do some stuff, no errors
}
}
}
}
if !self.defaults.getDownloadInicialPedido() {
PedidoAPI().loadOrders(parceiroId){ (dados) in
if dados != nil {
for pedidos in dados!{
// Do some stuff, no errors
}
}
}
}
self.dismiss(animated: false, completion: { () in print("Done") })
}
The problem is that my alert with the loading never gets dismissed. It never prints "Done". Can anyone help me please?
I dont know if it is useful, but I always get this warning:
Warning: Attempt to dismiss from view controller <MyApp.MainViewController: 0x0000000> while a presentation or dismiss is in progress!

The problem is exactly what the error says. Your presentation call isn't completed at the moment you're calling self.dismiss(...). For further explanation, you're calling present(alert, animated: true, completion: nil) with animated: true parameter so it's not going to finish the presentation instantly. On the other hand, you are calling self.dismiss(animated: false, completion: { () in print("Done") }) on the same thread in the relatively short instruction block, so it gets executed before the iOS finishes the animated presentation of the dialog and that's why you get the error.
But furthermore, before actually fixing the issue you should ask yourself do you really want to dismiss the dialog as soon as it's presented. Judging from the code you posted, I'd assume you want it to be dismissed after either or both of the API calls are finished. If that's the case you need to move the dismiss method call within the completion block (closures) of your API calls.

Solution for me (Working fine and printing "Done"):
override func viewDidAppear(_ animated: Bool) {
if !self.defaults.getDownloadInicialTipoEntrega() || !self.defaults.getDownloadInicialPedido() || !self.defaults.getDownloadInicialVitrine() {
Functions.showAlertWaiting("Wait please...", self)
}
loadDeliveryTypesNOrders { (completed) in
if completed {
self.dismiss(animated: false, completion: { () in print("Done") })
}
}
}
func loadDeliveryTypesNOrders (completion: #escaping (Bool) -> ()) {
let parceiroId = self.defaults.getParceiroId()
if !self.defaults.getDownloadInicialTipoEntrega() {
TipoEntregaAPI().loadTiposEntrega(parceiroId){ (dados) in
if dados != nil {
for tipoEntrega in dados!{
// Do some stuff, no errors
}
}
}
}
if !self.defaults.getDownloadInicialPedido() {
PedidoAPI().loadOrders(parceiroId){ (dados) in
if dados != nil {
for pedidos in dados!{
// Do some stuff, no errors
}
}
}
}
completion(true)
}

Related

<_SFAppPasswordSavingViewController:> that is already being presented by <UIKeyboardHiddenViewController_Save>

I am working on an iPad application that has both Portrait and Landscape orientation. The issue that I am facing is regarding the the popup presentation that iPad shows you for saving password while you log-in in the application.
Now, when the pop-up is presented, at that moment if I try to rotate the device, the application crashes with the message
Fatal Exception: NSInvalidArgumentException
Application tried to present modally a view controller <_SFAppPasswordSavingViewController: 0x107be7230> that is already being presented by <UIKeyboardHiddenViewController_Save: 0x107a49190>.
Now, the problem is that SFAppPasswordSavingViewController presentation is not controlled by me. Also UIKeyboardHiddenViewController_Save,is something I am not controlling.
Please help me out on how to solve this issue or let me know if I am missing anything.
This is how I am presenting Login screen in my application.
guard let loginVC = LoginVC.loadFromXIB() else {
Log.w("LoginVC could not be loaded")
return
}
let navVC = PopNavigationController(rootViewController: loginVC)
UIWindow.key?.replaceRootViewControllerWith(navVC, animated: true, completion: nil)
Here replaceRootViewControllerWith is a custom function that is an extension on UIWindow.
extension UIWindow {
func replaceRootViewControllerWith(_ replacementController: UIViewController,
animated: Bool,
completion: (() -> Void)?) {
let snapshotImageView = UIImageView(image: self.snapshot())
self.addSubview(snapshotImageView)
let dismissCompletion = { [weak self] in // dismiss all modal view controllers
self?.rootViewController = replacementController
self?.bringSubviewToFront(snapshotImageView)
if animated {
UIView.animate(withDuration: 0.4, animations: { () -> Void in
snapshotImageView.alpha = 0
}, completion: { (_) -> Void in
snapshotImageView.removeFromSuperview()
completion?()
})
} else {
snapshotImageView.removeFromSuperview()
completion?()
}
}
DispatchQueue.main.async { [weak self] in
if self?.rootViewController?.presentedViewController != nil {
self?.rootViewController?.dismiss(animated: false, completion: dismissCompletion)
} else {
dismissCompletion()
}
}
}
}

Can't Dismiss View controller

I am trying to dismiss a View Controller that has been presented as a modal .overCurrentContext.
The controller is presented like so
let vc= UIViewController()
vc.modalPresentationStyle = .overCurrentContext
present(vc, animated: true, completion: nil)
When I call dismiss inside the vc controller that has appeared it does nothing.
To give more detail about the parent ViewController, it is a ViewController inside a NavigationController and it is third on the NavigationController's VC stack.
So the actual solution was very obscure.
I was using a Pod called FloatingPanelController that was causing some issues with dismissing my entire view stack intermittently.
In order to resolve it I modified an extension in the Pod
public extension UIViewController {
#objc func fp_original_dismiss(animated flag: Bool, completion: (() -> Void)? = nil) {
// Implementation will be replaced by IMP of self.dismiss(animated:completion:)
}
#objc func fp_dismiss(animated flag: Bool, completion: (() -> Void)? = nil) {
// Call dismiss(animated:completion:) to a content view controller
if let fpc = parent as? FloatingPanelController {
if fpc.presentingViewController != nil {
self.fp_original_dismiss(animated: flag, completion: completion)
} else {
fpc.removePanelFromParent(animated: flag, completion: completion)
}
return
}
// Call dismiss(animated:completion:) to FloatingPanelController directly
if let fpc = self as? FloatingPanelController {
if fpc.presentingViewController != nil {
self.fp_original_dismiss(animated: flag, completion: completion)
} else {
fpc.removePanelFromParent(animated: flag, completion: completion)
}
return
}
// For other view controllers
self.fp_original_dismiss(animated: flag, completion: completion)
}
}
In particular I removed the last line:
self.fp_original_dismiss(animated: flag, completion: completion)
This is what caused the issue. I had no idea that this method was actually overriding the entire dismiss method. Once I re-inserted that line the issue was resolved.
This is likely very little use to anyone else!

Setting DarkTheme has no effect Braintree iOS v4 SDK

The documentation clearly says it can be done, easy breezy:
https://developers.braintreepayments.com/guides/drop-in/customization/ios/v4#themes
I can indeed customize the primaryTextColor to red.
Here is a screenshot, that demonstrates that red works but not darkTheme:
And is here is my code in my UIViewController:
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
showDropIn(clientTokenOrTokenizationKey: clientToken)
}
func showDropIn(clientTokenOrTokenizationKey: String) {
BTUIKAppearance.darkTheme()
BTUIKAppearance.sharedInstance().primaryTextColor = UIColor.red
let request = BTDropInRequest()
request.vaultManager = true
let dropIn = BTDropInController(authorization: clientTokenOrTokenizationKey, request: request)
{ (controller, result, error) in
if (error != nil) {
print("ERROR")
} else if (result?.isCancelled == true) {
print("CANCELLED")
} else if let result = result {
// Use the BTDropInResult properties to update your UI
// result.paymentOptionType
// result.paymentMethod
// result.paymentIcon
// result.paymentDescription
}
controller.dismiss(animated: true, completion: nil)
}
self.present(dropIn!, animated: true, completion: nil)
}
So Braintree's documentation on Theme's is a bit poorly choose wording IMO.
The instruction is what is misleading to me: "To use the Dark theme instead, call this method before initializing Drop-in". Yet you have to initialize or instantiate the drop-in before setting darkTheme.
The instruction might better read: "To use the Dark theme instead, call this method before presenting the Drop-in"
Here is my working code:
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
showDropIn(clientTokenOrTokenizationKey: clientToken)
}
func showDropIn(clientTokenOrTokenizationKey: String) {
let request = BTDropInRequest()
request.vaultManager = true
dropIn = BTDropInController(authorization: clientTokenOrTokenizationKey, request: request)
{ (controller, result, error) in
if (error != nil) {
print("ERROR")
} else if (result?.isCancelled == true) {
print("CANCELLED")
} else if let result = result {
// Use the BTDropInResult properties to update your UI
// result.paymentOptionType
// result.paymentMethod
// result.paymentIcon
// result.paymentDescription
}
controller.dismiss(animated: true, completion: nil)
}
BTUIKAppearance.darkTheme()
BTUIKAppearance.sharedInstance()?.primaryTextColor = UIColor.lightGray
self.present(dropIn!, animated: true, completion: nil)
}

Send SMS in iOS

I am brand new in iOS developing and xCode.
Previously i developed android and i know i should work with Intent to make call and send SMS in android App.
Now i want to develop a simple App that when i press a Button it send a SMS to a specific number.
So i installed a Mac OS 10.11 VM on my Ubuntu.
And could connect connect my iPhone 5 to that and run my simple Hello World App in real iPhone Device and not simulator.
Now i created a Button in StoryBoard and make a funcion by following this article :
Send SMS Message Toturial
Also look at other links and was similar.
Here is my simple ViewController.swift.
import UIKit
import MessageUI
class ViewController: UIViewController, MFMessageComposeViewControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func sendMessage(sender: AnyObject) {
print("Send button pressed") //display a text to make sure is calling
if MFMessageComposeViewController.canSendText() {
let controller = MFMessageComposeViewController()
controller.body = "TestMessage "
controller.recipients = ["xxxxxxxxx", "xxxxxxxxx"] // sending for two numbers
controller.messageComposeDelegate = self
self.presentViewController(controller, animated: true, completion: nil)
}
}
func messageComposeViewController(controller: MFMessageComposeViewController, didFinishWithResult result: MessageComposeResult) {
switch (result.rawValue) {
case MessageComposeResultCancelled.rawValue:
print("Message was cancelled")
self.dismissViewControllerAnimated(true, completion: nil)
case MessageComposeResultFailed.rawValue:
print("Message failed")
self.dismissViewControllerAnimated(true, completion: nil)
case MessageComposeResultSent.rawValue:
print("Message was sent")
self.dismissViewControllerAnimated(true, completion: nil)
default:
break;
}
}
}
And also created a Simple Button and link that to sendMessage above.
When i run the app, displays the button and when i press it, it seems it is doing something, but the SMS is not sent.
Any Idea??
I really appreciate it.
Update : As our friend said i added print("Send button pressed") in sendMessage Button, so i find out the button call sendMessage when i press it.
I finally did it, I mixed up two solutions,
First of all,i changed the function of sendMessage and renamed it to sendText, in this way :
#IBAction func sendText(sender: AnyObject) {
if (MFMessageComposeViewController.canSendText()) {
let controller = MFMessageComposeViewController()
controller.body = "Text Body"
controller.recipients = ["xxxxxxxxxxx"]
controller.messageComposeDelegate = self
self.presentViewController(controller, animated: true, completion: nil)
}
}
And the protocol of MessageComposeViewController is :
func messageComposeViewController(controller: MFMessageComposeViewController, didFinishWithResult result: MessageComposeResult) {
switch (result.rawValue) {
case MessageComposeResultCancelled.rawValue:
print("Message was cancelled")
self.dismissViewControllerAnimated(true, completion: nil)
case MessageComposeResultFailed.rawValue:
print("Message failed")
self.dismissViewControllerAnimated(true, completion: nil)
case MessageComposeResultSent.rawValue:
print("Message was sent")
self.dismissViewControllerAnimated(true, completion: nil)
default:
break;
}
}
And then click on the button in storyboards, and press control key and drag the button to ViewController and select the sendText function, now it works correctly.
Thx to my Friend #Kabiroberai for help and give some time.
Heres the bottom function converted to swift 4:
func messageComposeViewController(_ controller: MFMessageComposeViewController, didFinishWith result: MessageComposeResult) {
switch (result.rawValue) {
case MessageComposeResult.cancelled.rawValue:
print("Message was cancelled")
self.dismiss(animated: true, completion: nil)
case MessageComposeResult.failed.rawValue:
print("Message failed")
self.dismiss(animated: true, completion: nil)
case MessageComposeResult.sent.rawValue:
print("Message was sent")
self.dismiss(animated: true, completion: nil)
default:
break;
}
}

Calling achievement screen in Swift

I am having some extreme difficulties calling the achievements screen in game center. I have already set up the achievements in iTunes connect and it pops up fine if I access the achievements screen through the leaderboard first. However; I would like to be able to press a specific achievement button and be directed directly to the achievements screen. Can any one help? I have searched high and low on the internet ( and read through all of the documentation). I have found many resources for implementing leaderboards, but not many resources for implementing achievements in swift. My code is below. Any suggestions for my last two functions?
override func viewDidLoad() {
super.viewDidLoad()
login()
}
func login() {
println("Game Center Login Called")
let localPlayer = GKLocalPlayer.localPlayer()
// Handle the authentication
localPlayer.authenticateHandler = {(Home: UIViewController!, error: NSError!) -> Void in
if Home != nil {
println("Authentication is being processed.")
self.presentViewController(Home, animated: true, completion: nil)
} else {
println("Player has been successfully authenticated.")
}
}
}
func showLeaderboard() {
let gkScore = GKScore(leaderboardIdentifier: "high_Score_Leader_Board")
gkScore.value = Int64(highscore)
GKScore.reportScores([gkScore], withCompletionHandler: ( { (error: NSError!) -> Void in
if (error != nil) {
// handle error
println("Error: " + error.localizedDescription);
} else {
println("Score reported: \(gkScore.value)")
}
}))
var gcViewController: GKGameCenterViewController = GKGameCenterViewController()
gcViewController.gameCenterDelegate = self
gcViewController.viewState = GKGameCenterViewControllerState.Leaderboards
gcViewController.leaderboardIdentifier = "high_Score_Leader_Board"
self.showViewController(gcViewController, sender: self)
self.presentViewController(gcViewController, animated: true, completion: nil)
}
#IBAction func gameCenterButtoPressed(sender: AnyObject) {
showLeaderboard()
}
func gameCenterViewControllerDidFinish(gcViewController: GKGameCenterViewController!)
{
self.dismissViewControllerAnimated(true, completion: nil)
}
func showAchievements() {
// show Achievements screen
}
#IBAction func achievementButtonPressed(sender: AnyObject) {
// Call show achievements function when button pressed
}
Instead of:
gcViewController.viewState = GKGameCenterViewControllerState.Leaderboards
I think what you'll want is:
gcViewController.viewState = GKGameCenterViewControllerState.Achievements
And I found this information in this related tutorial.