messageComposeViewController dismissing crash swift 3 - ios

On my App, I send alert via sms. I try to open a new view controller as a modal, and in this , the alert was sent.
But, when the sms is sent or when user click on the cancel button, the messageComposeViewController do not dismiss, and crash.
Error on xcode log is :
(lldb)
This is my code used to send alerts :
import UIKit
import CoreLocation
import Social
import MessageUI
import BRYXBanner
class AlertInProgressViewController: UIViewController, MFMessageComposeViewControllerDelegate, CLLocationManagerDelegate {
[... Code here ...]
func sms()
{
//Send sms
if(sms_exist()==true)
{
if( CLLocationManager.authorizationStatus() == CLAuthorizationStatus.authorizedWhenInUse || CLLocationManager.authorizationStatus() == CLAuthorizationStatus.authorizedAlways)
{
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.requestWhenInUseAuthorization()
locationManager.startUpdatingLocation()
locationManager.startUpdatingHeading()
let mlocation = self.locationManager.location
if mlocation != nil {
let latitude: Double = mlocation!.coordinate.latitude
let longitude: Double = mlocation!.coordinate.longitude
let latitude_convert:String = String(format:"%f", latitude)
let longitude_convert:String = String(format:"%f", longitude)
let location = number_street + " " + ville + "\nLatitude " + latitude_convert + " - Longitude : " + longitude_convert
let geoCoder = CLGeocoder()
let location_details = CLLocation(latitude: mlocation!.coordinate.latitude, longitude: mlocation!.coordinate.longitude)
geoCoder.reverseGeocodeLocation(location_details)
{
(placemarks, error) -> Void in
let placeArray = placemarks as [CLPlacemark]!
// Place details
var placeMark: CLPlacemark!
placeMark = placeArray?[0]
// Address dictionary
print(placeMark.addressDictionary)
// Location name
if let locationName = placeMark.addressDictionary?["Name"] as? NSString
{
print(locationName)
self.details_location = locationName as String
}
if let city = placeMark.addressDictionary?["City"] as? NSString
{
self.city = city as String
}
self.message = NSLocalizedString("IN_DANGER_TEXT_SHARE",comment:"I'm in danger, I'm currently at ") + location + "\n - " + self.details_location + " - " + self.city
let defaults = UserDefaults.standard
let sms_perso = defaults.object(forKey: "sms") as? String
if(MFMessageComposeViewController.canSendText()) {
let controller = MFMessageComposeViewController()
controller.body = self.message
controller.recipients = [sms_perso!]
controller.messageComposeDelegate = self
self.present(controller, animated: true, completion: nil)
}
else
{
print("Can't send sms")
}
}
}
}
}
}
[...]
func messageComposeViewController(_ controller: MFMessageComposeViewController, didFinishWith result: MessageComposeResult) {
//Original code but doesn't work too
//self.dismiss(animated: true, completion: nil)
OperationQueue.main.addOperation {
self.dismiss(animated: true, completion: {});
}
print("I want to dismiss here")
}
}
In xcode log, I can see : I want to dismiss here, so messageComposeViewController is called but after it's crash.
To display AlertInProgressViewController I use a storyboard segue.

I resolve my problem by changing this :
self.dismiss(animated: true, completion: {});
by
controller.dismiss(animated: true, completion: nil)

Yes, in Swift 4.x you must use
controller.dismiss(animated: true, completion: nil)
Here's a useful delegate function for MFMailComposeViewControllerDelegate
// MAIL COMPOSER DELEGATE
func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) {
var resultMess = ""
switch result.rawValue {
case MFMailComposeResult.cancelled.rawValue:
resultMess = "Mail cancelled"
case MFMailComposeResult.saved.rawValue:
resultMess = "Mail saved"
case MFMailComposeResult.sent.rawValue:
resultMess = "Thanks for contacting us!\nWe'll get back to you asap."
case MFMailComposeResult.failed.rawValue:
resultMess = "Something went wrong with sending Mail, try again later."
default:break
}
// Show email result alert
let alert = UIAlertController(title: APP_NAME, message: resultMess, preferredStyle: .alert)
let ok = UIAlertAction(title: "OK", style: .default, handler: { (action) -> Void in
})
alert.addAction(ok)
present(alert, animated: true, completion: nil)
// Dismiss the controller
controller.dismiss(animated: true, completion: nil)
}

Related

How we can send the location messages using firebase in Swift

I want to send the location messages using firebase. like when user click on button it shows alert which message you want to send like audio,video,location.When user select the location from the given option it will call locationViewController and store the location in firebase.I've simple loaded the google map with marker pin point and defined the coordinates it's self not dynamically. InputActionSheet func that defined in chatViewController when user tap on choose button this function call and inside another function call that will call the functionality of location messages.But I'm confused how we get get coordinates dynamically when user taped on any location on google map and also it will show the marker when user tap.
SimpleViewController class for checking the functionality of google map it's working fine:
class ViewController: UIViewController {
#IBOutlet weak var mapView: GMSMapView!
let manager = CLLocationManager()
let karachi = CLLocationCoordinate2D(latitude: 24.882752, longitude: 67.149848)
let tandoAdam = CLLocationCoordinate2D(latitude: 25.76284, longitude: 68.66087)
override func viewDidLoad() {
super.viewDidLoad()
self.setupMap(title: "Karachi", subtitle: "Shah Faisal", karachi)
self.setupMap(title: "TDM", subtitle: "AK H", tandoAdam)
self.mapView.mapStyle(name:"darkTheme", type:"json")
}
func setupMap(title:String,subtitle:String,_ coordinate:CLLocationCoordinate2D){
manager.delegate = self
manager.requestWhenInUseAuthorization()
manager.startUpdatingLocation()
let camera = GMSCameraPosition.camera(withLatitude: coordinate.latitude, longitude: coordinate.longitude, zoom: 6.0)
mapView.camera = camera
mapView.camera = camera
self.addMarker(title: title, subtitle: subtitle, coordinate:coordinate)
}
func addMarker(title:String,subtitle:String,coordinate:CLLocationCoordinate2D){
let marker = GMSMarker()
marker.icon = #imageLiteral(resourceName: "DiceFive")
marker.position = coordinate
marker.title = title
marker.snippet = subtitle
marker.map = mapView
}
}
extension UIViewController : CLLocationManagerDelegate {
public func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
guard locations.first != nil else{
return
}
}
}
presentInputActionSheet func in Chat class:
private func presentInputActionSheet() {
let actionSheet = UIAlertController(title: "Attach Media",
message: "What would you like to attach?",
preferredStyle: .actionSheet)
actionSheet.addAction(UIAlertAction(title: "Photo", style: .default, handler: { [weak self] _ in
self?.presentPhotoInputActionsheet()
}))
actionSheet.addAction(UIAlertAction(title: "Video", style: .default, handler: { [weak self] _ in
self?.presentVideoInputActionsheet()
}))
actionSheet.addAction(UIAlertAction(title: "Audio", style: .default, handler: { _ in
}))
actionSheet.addAction(UIAlertAction(title: "Location", style: .default, handler: { [weak self] _ in
self?.presentLocationPicker()
}))
actionSheet.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil))
present(actionSheet, animated: true)
}
presentLocationPicker func in Chat class:
private func presentLocationPicker() {
let vc = ViewController(coordinates: nil)
vc.title = "Pick Location"
vc.navigationItem.largeTitleDisplayMode = .never
let location = Location(location: CLLocation(latitude: latitude, longitude: longitude),
size: .zero)
let message = Message(sender: selfSender,messageId: messageId,sentDate: Date(),kind: .location(location))
DatabaseManager.shared.sendMessage(to: conversationId, otherUserEmail: strongSelf.otherUserEmail, name: name, newMessage: message, completion: { success in
if success {
print("sent location message")
}
else {
print("failed to send location message")
}
})
}
navigationController?.pushViewController(vc, animated: true)
}
try this
let message = Message(sender: selfSender,messageId: messageId,sentDate: Date(),kind: .location(location))
DatabaseManager.shared.sendMessage(to: conversationId, otherUserEmail: strongSelf.otherUserEmail, name: name, newMessage: message, completion: { success in
if success {
print("sent location message")
}
else {
print("failed to send location message")
}
navigationController?.pushViewController(vc, animated: true)
})
}

How do you get a location capturer to run before an override segue?

So, I need to capture user's location (with their permission of course) before he gets to homepage. So on the login/register page I want to capture the location, but I need to do it before the override segue to homepage for logged in users. Any of you have any ideas? Below is the override segue.
I put location capturer before the segue in the override function, but it still went to homepage before the user had an opportunity to accept share location. This is not really unexpected since the entire override function is by definition an override function
override func viewDidAppear(_ animated: Bool) {
//////////////////////
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let databaseRef = Database.database().reference()
guard let uid = Auth.auth().currentUser?.uid else { return }
guard let locValue: CLLocationCoordinate2D = manager.location?.coordinate else { return }
print("locations = \(locValue.latitude) \(locValue.longitude)")
latestLocation = ["latitude" : locValue.latitude, "longitude" : locValue.longitude]
let lat = locValue.latitude
let lon = locValue.longitude
dict = CLLocation(latitude: lat, longitude: lon)
print("dict", dict)
if let locationDictionary = latestLocation {
databaseRef.child("people").child(uid).child("Coordinates").setValue(locationDictionary)
}
}
//////////////////////
if Auth.auth().currentUser != nil {
self.performSegue(withIdentifier: "tohome", sender: nil)
}
}
Update: This is how I login and register.
#IBAction func RegisterPressed(_ sender: Any) {
Auth.auth().createUser(withEmail: (emailField.text ?? ""), password: (passwordField.text ?? "")) { (user, error) in
if let _eror = error {
//something bad happning
print(_eror.localizedDescription )
let alert = UIAlertController(title: "Error", message: "Invalid Entry or Duplicate.", preferredStyle: UIAlertController.Style.alert)
let action = UIAlertAction(title: "Ok", style: .default, handler: nil)
alert.addAction(action)
self.present(alert, animated: true, completion: nil)
}else{
//user registered successfully
print(user as Any)
if let userID = user?.user.uid
{
KeychainWrapper.standard.set((userID), forKey: "uid")
let databaseRef = Database.database().reference()
databaseRef.child("A1").child(userID).child("users").setValue(self.emailField.text!)
databaseRef.child("people").child(userID).child("postID").setValue(userID)
let components = self.emailField.text!.split(separator: "#")
let child1 = components[0] //will be jake
let child2 = components[1] //will be aol.com
print(components, child1, child2, "childs")
databaseRef.child("people").child(userID).child("e2").setValue(child2)
components.forEach { print($0) }
self.performSegue(withIdentifier: "tohome", sender: nil)
}
}
}
}
#IBAction func loginInPressed(_ sender: Any) {
Auth.auth().signIn(withEmail: (emailField.text ?? ""), password: (passwordField.text ?? "")) { (user, error) in
if let _eror = error {
//something bad happning
print(_eror.localizedDescription )
let alert = UIAlertController(title: "Error", message: "Incorrect Email or Password.", preferredStyle: UIAlertController.Style.alert)
let action = UIAlertAction(title: "Ok", style: .default, handler: nil)
alert.addAction(action)
self.present(alert, animated: true, completion: nil)
}else{
//user registered successfully
print(user as Any)
if let userID = user?.user.uid
{
KeychainWrapper.standard.set((userID), forKey: "uid")
self.performSegue(withIdentifier: "tohome", sender: nil) }
}
}
}
If I understand it correctly in viewDidAppear(_:) you have one condition. If it's true you immediately trying to perform the segue.
if Auth.auth().currentUser != nil {
self.performSegue(withIdentifier: "tohome", sender: nil)
}
As location is must to move into HomeScreen, why don't you do the navigation(segue) when you receive the first location update inside CLLocationManagerDelegate?
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
// check for existing location if we received earlier
guard self.latestLoc == nil else {
// we received one location earlier, navigation to Home completed. no need to proceed
// If we no longer need to listen for loc updates, we can stop listening further
manager.stopUpdatingLocation()
return
}
// ... Your exiting data base update code
guard let latestLoc = locations.last else { return }
self.latestLoc = latestLoc // If you want to pass this location to destination view controller in `prepare(for: UIStoryboardSegue, sender: Any?)`
// Also it will good practice to call manager.stopUpdatingLocation() if you need location only for once
// Now move to HomeScreen
self.performSegue(withIdentifier: "tohome", sender: nil)
// If we no longer need to listen for loc updates, we can stop listening further
manager.stopUpdatingLocation()
}

Swift 5 - Email Class Helper / Manager

Edit:
Big thanks to Paulw11 for helping me solve this issue. I've added the full code here for easy reuse:
Class:
import UIKit
import MessageUI
struct Feedback {
let recipients: [String]
let subject: String
let body: String
let footer: String
}
class FeedbackManager: NSObject, MFMailComposeViewControllerDelegate {
private var feedback: Feedback
private var completion: ((Result<MFMailComposeResult,Error>)->Void)?
override init() {
fatalError("Use FeedbackManager(feedback:)")
}
init?(feedback: Feedback) {
guard MFMailComposeViewController.canSendMail() else {
return nil
}
self.feedback = feedback
}
func send(on viewController: UIViewController, completion:(#escaping(Result<MFMailComposeResult,Error>)->Void)) {
let mailVC = MFMailComposeViewController()
self.completion = completion
mailVC.mailComposeDelegate = self
mailVC.setToRecipients(feedback.recipients)
mailVC.setSubject(feedback.subject)
mailVC.setMessageBody("<p>\(feedback.body)<br><br><br><br><br>\(feedback.footer)</p>", isHTML: true)
viewController.present(mailVC, animated:true)
}
func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) {
if let error = error {
completion?(.failure(error))
controller.dismiss(animated: true)
} else {
completion?(.success(result))
controller.dismiss(animated: true)
}
}
}
In View Controller:
Add Variable:
var feedbackManager: FeedbackManager?
Use:
let feedback = Feedback(recipients: "String", subject: "String", body: "Body", footer: "String")
if let feedManager = FeedbackManager(feedback: feedback) {
self.feedbackManager = feedManager
self.feedbackManager?.send(on: self) { [weak self] result in
switch result {
case .failure(let error):
print("error: ", error.localizedDescription)
// Do something with the error
case .success(let mailResult):
print("Success")
// Do something with the result
}
self?.feedbackManager = nil
}
} else { // Cant Send Email: // Added UI Alert:
let failedMenu = UIAlertController(title: "String", message: nil, preferredStyle: .alert)
let okAlert = UIAlertAction(title: "String", style: .default)
failedMenu.addAction(okAlert)
present(failedMenu, animated: true)
}
I'm trying to make a class that handles initializing a MFMailComposeViewController to send an email inside of the app.
I'm having issues making it work. Well, rather making it not crash if it doesn't work.
class:
import UIKit
import MessageUI
struct Feedback {
let recipients = "String"
let subject: String
let body: String
}
class FeedbackManager: MFMailComposeViewController, MFMailComposeViewControllerDelegate {
func sendEmail(feedback: Feedback) {
if MFMailComposeViewController.canSendMail() {
self.mailComposeDelegate = self
self.setToRecipients([feedback.recipients])
self.setSubject("Feedback: \(feedback.subject)")
self.setMessageBody("<p>\(feedback.body)</p>", isHTML: true)
} else {
print("else:")
mailFailed()
}
}
func mailFailed() {
print("mailFailed():")
let failedMenu = UIAlertController(title: "Please Email Me!", message: nil, preferredStyle: .alert)
let okAlert = UIAlertAction(title: "Ok!", style: .default)
failedMenu.addAction(okAlert)
self.present(failedMenu, animated: true, completion: nil)
}
func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) {
controller.dismiss(animated: true)
}
}
And then calling it from a different view controller:
let feedbackManager = FeedbackManager()
feedbackManager.sendEmail(feedback: Feedback(subject: "String", body: "String"))
self.present(feedbackManager, animated: true, completion: nil)
tableView.deselectRow(at: indexPath, animated: true)
The above works just fine if MFMailComposeViewController.canSendMail() == true. The problem I'm facing is that if canSendMail() is not true, then the class obviously cant initialize and crashes. Which makes sense.
Error:
Unable to initialize due to + [MFMailComposeViewController canSendMail] returns NO.
I'm not sure where to go from here on how to get this working. I've tried changing FeedbackManager from MFMailComposeViewController to a UIViewController. And that seems to work but because it's adding a view on the stack, it's causing a weird graphical display.
The other thing I could do is import MessageUI, and conform to MFMailComposeViewController for every controller I want to be able to send an email from. So that I can check against canSendMail() before trying to initialize FeedbackManager(). But that also doesn't seem like the best answer.
How else can I get this working?
EDIT:
I've gotten the code to work with this however, there is an ugly transition with the addition of the view onto the stack before it presents the MFMailComposeViewController.
class FeedbackManager: UIViewController, MFMailComposeViewControllerDelegate {
func sendEmail(feedback: Feedback, presentingViewController: UIViewController) -> UIViewController {
if MFMailComposeViewController.canSendMail() {
let mail = MFMailComposeViewController()
mail.mailComposeDelegate = self
mail.setToRecipients([feedback.recipients])
mail.setSubject("Feedback: \(feedback.subject)")
mail.setMessageBody("<p>\(feedback.body)</p>", isHTML: true)
present(mail, animated: true)
return self
} else {
print("else:")
return mailFailed(presentingViewController: presentingViewController)
}
}
func mailFailed(presentingViewController: UIViewController) -> UIViewController {
print("mailFailed():")
let failedMenu = UIAlertController(title: "Please Email Me!", message: nil, preferredStyle: .alert)
let okAlert = UIAlertAction(title: "Ok!", style: .default)
failedMenu.addAction(okAlert)
return failedMenu
}
func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) {
controller.dismiss(animated: true)
self.dismiss(animated: false)
}
}
Subclassing MFMailComposeViewController is the wrong approach. This class is intended to be used "as-is". You can build a wrapper class if you like:
struct Feedback {
let recipients = "String"
let subject: String
let body: String
}
class FeedbackManager: NSObject, MFMailComposeViewControllerDelegate {
private var feedback: Feedback
private var completion: ((Result<MFMailComposeResult,Error>)->Void)?
override init() {
fatalError("Use FeedbackManager(feedback:)")
}
init?(feedback: Feedback) {
guard MFMailComposeViewController.canSendMail() else {
return nil
}
self.feedback = feedback
}
func send(on viewController: UIViewController, completion:(#escaping(Result<MFMailComposeResult,Error>)->Void)) {
let mailVC = MFMailComposeViewController()
self.completion = completion
mailVC.mailComposeDelegate = self
mailVC.setToRecipients([feedback.recipients])
mailVC.setSubject("Feedback: \(feedback.subject)")
mailVC.setMessageBody("<p>\(feedback.body)</p>", isHTML: true)
viewController.present(mailVC, animated:true)
}
func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) {
if let error = error {
completion?(.failure(error))
} else {
completion?(.success(result))
}
}
}
And then to use it from a view controller:
let feedback = Feedback(subject: "String", body: "Body")
if let feedbackMgr = FeedbackManager(feedback: feedback) {
self.feedbackManager = feedbackMgr
feedback.send(on: self) { [weak self], result in
switch result {
case .failure(let error):
// Do something with the error
case .success(let mailResult):
// Do something with the result
}
self.feedbackManager = nil
}
} else {
// Can't send email
}
You will need to hold a strong reference to the FeedbackManager in a property otherwise it will be released as soon as the containing function exits. My code above refers to a property
var feedbackManager: FeedbackManager?
While this will work, a better UX is if you check canSendMail directly and disable/hide the UI component that allows them to send feedback
Solved this by first adding a class that checks if .canSendMail is true. If it is, it then taps into the postal sending class to present the MFMailComposeViewController.
This is the only workaround I've come up with that allows MFMailComposeViewController to be it's own MFMailComposeViewControllerDelegate. While also preventing a crash if .canSendMail = false.
import UIKit
import MessageUI
struct Feedback {
let recipients = ["Strings"]
let subject: String
let body: String
}
class FeedbackManager {
func tryMail() -> Bool {
if MFMailComposeViewController.canSendMail() {
return true
} else {
return false
}
}
func mailFailed() -> UIViewController {
let failedMenu = UIAlertController(title: "Please Email Me!", message: nil, preferredStyle: .alert)
let okAlert = UIAlertAction(title: "Ok!", style: .default)
failedMenu.addAction(okAlert)
return failedMenu
}
}
class PostalManager: MFMailComposeViewController, MFMailComposeViewControllerDelegate {
func sendEmail(feedback: Feedback) -> MFMailComposeViewController {
if MFMailComposeViewController.canSendMail() {
self.mailComposeDelegate = self
self.setToRecipients(feedback.recipients)
self.setSubject("Feedback: \(feedback.subject)")
self.setMessageBody("<p>\(feedback.body)</p>", isHTML: true)
}
return self
}
func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) {
controller.dismiss(animated: true)
}
}
Called with:
let feedbackManager = FeedbackManager()
let feedback = Feedback(subject: "String", body: "Body")
switch feedbackManager.tryMail() {
case true:
let postalManager = PostalManager()
present(postalManager.sendEmail(feedback: feedback), animated: true)
case false:
present(feedbackManager.mailFailed(), animated: true)
}
You can change the code as follows.
struct Feedback {
let recipients = "String"
let subject: String
let body: String
}
class FeedbackManager: NSObject, MFMailComposeViewControllerDelegate {
func sendEmail(presentingViewController: UIViewController)) {
if MFMailComposeViewController.canSendMail() {
let mail = MFMailComposeViewController()
mail.mailComposeDelegate = self
mail.setToRecipients([feedback.recipients])
mail.setSubject("Feedback: \(feedback.subject)")
mail.setMessageBody("<p>\(feedback.body)</p>", isHTML: true)
presentingViewController.present(mail, animated: true)
} else {
print("else:")
mailFailed(presentingViewController: presentingViewController)
}
}
func mailFailed(presentingViewController: UIViewController) {
print("mailFailed():")
let failedMenu = UIAlertController(title: "Please Email Me!", message: nil, preferredStyle: .alert)
let okAlert = UIAlertAction(title: "Ok!", style: .default)
failedMenu.addAction(okAlert)
presentingViewController.present(failedMenu, animated: true, completion: nil)
}
func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) {
controller.dismiss(animated: true)
}
}
Now, mailComposer can be opened as follows from another UIViewController class.
let feedbackManager = FeedbackManager()
feedbackManager.sendEmail(presentingViewController: self)
Hope it helps

How to use a UIAlertController in MVVM?

I have a VC with code to show an alert:
func showMessage() {
let alertView = UIAlertController(title: "TEST",
message: self.loginViewModel.errorText,
preferredStyle: .alert)
alertView.addAction(UIAlertAction(title: "Ok", style: .destructive, handler: nil))
present(alertView, animated: true, completion: nil)
}
and I have this login logic in my viewModel which needs to trigger this function:
func submitLoginRequest(userLogin: String, loginPassword: String, loginSecret: String, deviceToken: String) {
let userLogin = UserServices.init()
manager.userServicesApiRequest(url: Endpoints.login, request: userLogin) { (data, error) in
if let data = data {
let status = data["status"].stringValue
if status == "success" {
guard let userObject = UserProfileModel.init(data) else { return }
let encodedUserObject: Data = NSKeyedArchiver.archivedData(withRootObject: userObject)
UserDefaults.standard.set(encodedUserObject, forKey: "userProfile")
print("Login Succeeded") self.coordinatorDelegate?.loginViewModelDidLogin(viewModel: self)
} else {
self.errorText = data["reason"].stringValue
// here is where the controller needs calling!
}
}
I wanted to know how i should have them interact correctly to trigger the VC when the VM case is hit?

alert controller not displaying in iphone using swift2

in my app i want to use alert controller so am using the following code:
import UIKit
import CoreLocation
class GPSTrackingManager: NSObject,CLLocationManagerDelegate {
var locationManager: CLLocationManager!
var seenError : Bool = false
func startTracking() {
locationManager = CLLocationManager()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.requestAlwaysAuthorization()
locationManager.startUpdatingLocation()
}
func locationManager(manager: CLLocationManager, didFailWithError error: NSError) {
locationManager.stopUpdatingLocation()
print("error occured:\(error)")
}
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
//println("locations = \(locationManager)")
let latValue = locationManager.location!.coordinate.latitude
let lonValue = locationManager.location!.coordinate.longitude
print(latValue)
print(lonValue)
GoogleLat = latValue
GoogleLong = lonValue
CLGeocoder().reverseGeocodeLocation(manager.location!, completionHandler: {(placemarks, error)->Void in
if (error != nil)
{
print("Reverse geocoder failed with error" + error!.localizedDescription)
return
}
if placemarks!.count > 0
{
let pm = placemarks![0] as CLPlacemark
self.displayLocationInfo(pm)
}
else
{
print("Problem with the data received from geocoder")
}
})
}
func displayLocationInfo(placemark: CLPlacemark?)
{
if let containsPlacemark = placemark
{
locationManager.stopUpdatingLocation()
let fourthcity = (containsPlacemark.subAdministrativeArea != nil) ? containsPlacemark.subAdministrativeArea : ""
let fourthState = (containsPlacemark.administrativeArea != nil) ? containsPlacemark.administrativeArea : ""
let fourthCountry = (containsPlacemark.country != nil) ? containsPlacemark.country : ""
print("my Real City = \(fourthcity)")
let fullName = fourthcity
let fullNameArr = fullName!.characters.split{$0 == " "}.map(String.init)
fullNameArr[0]
print(fullNameArr[0])
print("myState = \(fourthState)")
print("myCountry = \(fourthCountry)")
appDelCityname = fullNameArr[0]
appDelStateName = fourthState
appDelCountryName = fourthCountry
print("AppDelegate City Name = \(appDelCityname)")
}
}
func locationManager(manager: CLLocationManager, didChangeAuthorizationStatus status: CLAuthorizationStatus) {
switch status {
case .NotDetermined:
// If status has not yet been determied, ask for authorization
manager.requestWhenInUseAuthorization()
break
case .AuthorizedWhenInUse:
// If authorized when in use
print("AuthorizedWhenInUse")
manager.startUpdatingLocation()
break
case .AuthorizedAlways:
// If always authorized
print("AuthorizedAlways")
manager.startUpdatingLocation()
break
case .Denied:
// If user denied your app access to Location Services, but can grant access from Settings.app
print("user denied to allow")
dispatch_async(dispatch_get_main_queue(), {
NSTimer.scheduledTimerWithTimeInterval(3.0,target: self, selector: #selector(self.DisplayalertToturnonLocation), userInfo: nil, repeats: false)
})
break
default:
print("user cant allow location service")
break
}
}
func DisplayalertToturnonLocation(){
let alertController = UIAlertController(title: "GPRS is Required", message: "This app requires your location,please turn on your location service or set your address", preferredStyle: .Alert)
let saveAction = UIAlertAction(title: "Set Address", style: UIAlertActionStyle.Default, handler: {
alert -> Void in
self.alertTogetAdrressFromUser()
})
alertController.addAction(saveAction)
let cancelAction = UIAlertAction(title: "Settings", style: UIAlertActionStyle.Default, handler: {
(action : UIAlertAction!) -> Void in
UIApplication.sharedApplication().openURL(NSURL(string: UIApplicationOpenSettingsURLString)!)
})
alertController.addAction(cancelAction)
let keyWindow = UIApplication.sharedApplication().keyWindow
let mainController = keyWindow!.rootViewController!
mainController.presentViewController(alertController, animated: true, completion: nil)
}
}
I am calling this function in AppDelegate which executes well in iPad but not in iPhone also it not shows any error or warning and am feeling too difficult to find what am doing wrong here so any one help me to display alert controller in iPhone.
in AppDelegate:
var tracking = GPSTrackingManager()
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
tracking.startTracking()
}
in here you need to follow two things . 1. your code is fine you need to show ur alertcontroller using main thread, else find the root wihch one is most top presnt your alert, else check once your current window is visible or not
present your UIAlertcontroller in Main thread, for e.g
Swift3
DispatchQueue.main.async {
self.tracking.DisplayalertToturnonLocation()
}
swift2
dispatch_async(dispatch_get_main_queue()) {
self.tracking.DisplayalertToturnonLocation()
}
you get the output as
full code
class GPSTrackingManager: NSObject {
func DisplayalertToturnonLocation(){
let alertController = UIAlertController(title: "GPRS is Required", message: "This app requires your location,please turn on your location service or set your address", preferredStyle: .alert)
let saveAction = UIAlertAction(title: "Set Address", style: UIAlertActionStyle.default, handler: {
alert -> Void in
// self.alertTogetAdrressFromUser()
})
alertController.addAction(saveAction)
let cancelAction = UIAlertAction(title: "Settings", style: UIAlertActionStyle.default, handler: {
(action : UIAlertAction!) -> Void in
// UIApplication.shared.openURL(NSURL(string: UIApplicationOpenSettingsURLString)! as URL)
})
alertController.addAction(cancelAction)
UIApplication.shared.keyWindow?.rootViewController?.present(alertController, animated: true, completion: nil)
}
}

Resources