I'm trying to create a loop on my app to check Connectivity. So, when the app loads for the first time, the user must be connected to the internet, otherwise he cannot proceed on using the app.
However, I'm trying to create a function that while the user has no internet, the UIAlerView persists on the screen until find an internet connection, then it should be dismissed and another method be lunched. What would be the best way for me to do that?
let alert = UIAlertController(title: "Primeiro Uso", message: "Voce precisa estar conectado na internet", preferredStyle: UIAlertControllerStyle.alert)
// add the actions (buttons)
alert.addAction(UIAlertAction(title: "Estou Conectado", style: .default, handler: { action in
if Connectivity.isConnectedToInternet != true {
//How do I create a loop here until find a connection?
//I'd like to persist the UIAlerView until find a connection
}
//Once the user is connected, this code below should be
//lunched
getApiData { (cerveja) in
arrCerveja = cerveja
//Backup
do{
let data = try JSONEncoder().encode(arrCerveja)
UserDefaults.standard.set(data, forKey: "backupSaved")
//
self.tableView.reloadData()
}catch{print(error)
}
}}))
alert.addAction(UIAlertAction(title: "Cancelar", style: UIAlertActionStyle.cancel, handler: nil))
// show the alert
self.present(alert, animated: true, completion: nil)
}
you can use this answer
https://stackoverflow.com/a/3597085/2621857
in the appDelegate level of you application.
i think creating loop for checking internet, somehow will pressure the processor to memory leak so i suggest not using a kind of loop to check the internet.
i hope this will work for you .
Related
I want to open the default Mail application chosen by the user on iOS 14 - but without showing a compose view.
After signing up for an account, the user should confirm their email address, so I want to direct the user there.
There seem to be two known approaches based on the other questions I found:
UIApplication.shared.open(URL(string: "mailto://")!)
and
UIApplication.shared.open(URL(string: "message://")!)
The problem with the first option is that it will open an empty compose mail view in the app that comes up asking the user to type in a new email. That's not what I want. It would confuse users and they make think they have to send us an email. Putting in some text through parameters of the mailto URL syntax where I basically prepopulate the mail compose view with some text instructing to discard that new email draft and asking to check their email instead would work as a workaround but is not very nice.
The problem with the second option is that always opens the Mail.app, even if that is not the default mail app, and presumably it will ask the user to install the Mail.app if they deleted it from their phones because they have chosen e.g. Protonmail as their default mail app instead. Also not a very nice option for anyone who does not use Mail.app mainly.
So neither of the two approaches that have been proposed by other people solve my issue very nicely.
What is the best way to approach this?
Is there maybe some app to query iOS for the default mail app so at least I can try and launch that app if I know that app's custom URL scheme (e.g. googlegmail://)?
I ended up half-solving it by asking the user with an alert view about their preference, because I did not find a way to query iOS about it directly.
So first am showing an alert view like this:
func askUserForTheirPreference(in presentingViewController: UIViewController) {
let alertController = UIAlertController(title: nil, message: "pleaseConfirmWithApp", preferredStyle: .actionSheet)
alertController.addAction(UIAlertAction(title: "Apple Mail", style: .default) { action in
self.open(presentingViewController, .applemail)
})
alertController.addAction(UIAlertAction(title: "Google Mail", style: .default) { action in
self.open(presentingViewController, .googlemail)
})
alertController.addAction(UIAlertAction(title: "Microsoft Outlook", style: .default) { action in
self.open(presentingViewController, .outlook)
})
alertController.addAction(UIAlertAction(title: "Protonmail", style: .default) { action in
self.open(presentingViewController, .protonmail)
})
alertController.addAction(UIAlertAction(title: "Cancel", style: .cancel) { action in
os_log("Cancelling", log: Self.log, type: .debug)
})
presentingViewController.present(alertController, animated: true)
}
Then, I am responding to the user's choice like this:
func open(_ presentingViewController: UIViewController, _ appType: AppType) {
switch appType {
case .applemail: UIApplication.shared.open(URL(string: "message:")!, completionHandler: { handleAppOpenCompletion(presentingViewController, $0) })
case .googlemail: UIApplication.shared.open(URL(string: "googlegmail:")!, completionHandler: { handleAppOpenCompletion(presentingViewController, $0) })
case .outlook: UIApplication.shared.open(URL(string: "ms-outlook:")!, completionHandler: { handleAppOpenCompletion(presentingViewController, $0) })
case .protonmail: UIApplication.shared.open(URL(string: "protonmail:")!, completionHandler: { handleAppOpenCompletion(presentingViewController, $0) })
}
}
private func handleAppOpenCompletion(_ presentingViewController: UIViewController, _ isSuccess: Bool) {
guard isSuccess else {
let alertController = UIAlertController(title: nil, message: "thisAppIsNotInstalled", preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: "Dismiss", style: .cancel))
presentingViewController.present(alertController, animated: true)
return
}
}
enum AppType {
case applemail, googlemail, outlook, protonmail
}
A clear limitation of this approach is of course that I am limiting the user to very specific apps (in this case Google Mail, iOS "default" Mail, Microsoft Outlook and ProtonMail).
So this approach does not really scale well.
But at least, you can cover a few favorite ones and go from there based on your users' feedback.
The main reason for jumping through these hoops of asking the first is that, at least at the moment, it seems impossible to get that information from iOS directly.
I also could not find a URL scheme that would always open the chosen default Mail app without showing the compose new email view.
I believe it can be done with a button with link: href=“message://“
Visual example from Revolut app:
Before saving data into CloudKit, how do I check that user has an account and is signed in?
You need to use accountStatusWithCompletionHandler from the CKContainer class and check the accountStatus returned.
This is described in the CloudKit Quick Start documentation.
Here is the Swift version of the example:
CKContainer.defaultContainer().accountStatusWithCompletionHandler { accountStatus, error in
if accountStatus == .NoAccount {
let alert = UIAlertController(title: "Sign in to iCloud", message: "Sign in to your iCloud account to write records. On the Home screen, launch Settings, tap iCloud, and enter your Apple ID. Turn iCloud Drive on. If you don't have an iCloud account, tap Create a new Apple ID.", preferredStyle: .Alert)
alert.addAction(UIAlertAction(title: "OK", style: .Default, handler: nil))
self.presentViewController(alert, animated: true, completion: nil)
} else {
// Code if user has account here...
}
}
If a user opens up the application without an internet connection, a window pops up that says a connection is required, and there is an ok button. I want to the ok button to exit the application. Here is what I have:
if !isConnectedToNetwork(){
let alert = UIAlertController(title: "No Internet", message: "You need an internet connection to use this app", preferredStyle: UIAlertControllerStyle.Alert)
alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.Default, handler: nil))
self.presentViewController(alert, animated: true, completion: nil)
}
I am going to use this to exit the app:
UIControl().sendAction(Selector("suspend"), to: UIApplication.sharedApplication(), forEvent: nil)
I just don't know how to connect it to the OK button above.
Don't. Apple will reject this (if they see it).
Simply inform the user and add a 'retry' button. The retry button should obviously check the connection again.
To actually answer the question, you have currently set the handler: nil on the button action, instead you can actually set a handler and use it to call whatever logic you like.
You can handle when user press OK by the following code
alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.Default,
handler: { (action:UIAlertAction!) -> Void in
//after user press ok, the following code will be execute
NSLog("User pressed OK!!")
}))
I would like to display a pop-up where I'll ask the user about their preferences related to the push notifications and for that one, I want to display a list of options to the user. User can select more than one options.
I think that I'll have to display a tableview inside the UIAlertView, but it is deprecated now. So, how can I display a pop (with some small message + multiple select list ) before the APN system permissions dialog in Swift.
Any help will be appreciated.
You can use this code:
let alert = UIAlertController(title: title, message:message, preferredStyle: .Alert)
let action = UIAlertAction(title: "OK", style: .Default) { _ in
acceptNotification = true //code to execute when the user taps that OK
}
alert.addAction(action)
//you can add more actions
self.presentViewController(alert, animated: true){ // this part if provided, will be invoked after the dismissed controller's viewDidDisappear: callback is invoked.
}
I've got an app that needs access to the Photos on your device. I check to see the device status, and if they deny access I trigger a modal which will warn them that they did not provide the necessary access, and then gives them the option to go to their settings and correct the choice.
When this happens, my app crashes with the following error:
This application is modifying the autolayout engine from a background thread, which can lead to engine corruption and weird crashes. This will cause an exception in a future release.
Here is my code for triggering the redirect. Any ideas what could be causing this or suggestions on how I should do this better?
let title = "You didn't allow us to view your photos!"
let message = "Without access, we cannot help you add photos from your device."
let alertController = UIAlertController(title: title, message: message, preferredStyle: UIAlertControllerStyle.Alert)
alertController.addAction(UIAlertAction(title: "I know!", style: UIAlertActionStyle.Cancel, handler: nil))
alertController.addAction(UIAlertAction(title: "Settings (Required)", style: UIAlertActionStyle.Default, handler: { (action: UIAlertAction) -> Void in
let settingsURL = NSURL(string: UIApplicationOpenSettingsURLString)
UIApplication.sharedApplication().openURL(settingsURL!)
}))
self.presentViewController(alertController, animated: true, completion: nil)
This code is called within a method that I call from the following spot:
PHPhotoLibrary.requestAuthorization({ (status: PHAuthorizationStatus) -> Void in
if(status == .Authorized){
self.getPhonePhotos()
}else{
self.showDeniedPhotosPopup()
}
})
Thanks in advance.
UPDATE
I just realized something I didn't earlier. The code only crashes if I activate the "Photos" switch in the settings. The navigation itself doesn't cause the app to crash, its changing the photo settings configuration while the app is still running. To test my theory, I never triggered the popup, and simply went to the settings, and activated the photos switch and the app crashed. So the crash is definitely sourcing from the change in photo settings.
From the error it appears you are presenting your view controller on a thread which is not main thread leading to implementation of UI stuff (auto layout etc.) on your new view controller on a background thread.
Try encapsulating your view controller presentation code on main queue. Something like this:
weak var aBlockSelf = self
dispatch_async(dispatch_get_main_queue(), {
aBlockSelf.presentViewController(alertController, animated: true, completion: nil)
})
If it still crashes, profile your application to find out the exact culprit.