I'm aware that there are some questions out there that are similar to this question, however, a lot of them are in Objective C and I haven't been able to find a really applicable solution yet that works.
The main problem I am having is that when I log out of one account in my app, then log into another, the tab bar is not reset, and it displays the previously signed in users data. In other words, I need a way to "reset" the app back to the state it was in before any user had signed in.
I have tried to achieve this by writing a function inside App Delegate (setupTabBarController) and calling it when the user logs out, but no such luck yet.
This is what I have so far:
Logout code:
#objc func handleSignOutButtonTapped() {
let signOutAction = UIAlertAction(title: "Sign Out", style: .destructive) { (action) in
do {
try Auth.auth().signOut()
let welcomeControl = WelcomeController()
let welcomeNavCon = UINavigationController(rootViewController: welcomeControl)
self.present(welcomeNavCon, animated: true, completion: nil)
} catch let err {
print("Failed to sign out with error", err)
Service.showAlert(on: self, style: .alert, title: "Sign Out Error", message: err.localizedDescription)
}
}
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
Service.showAlert(on: self, style: .actionSheet, title: nil, message: nil, actions: [signOutAction, cancelAction]) {
}
let delegate = AppDelegate()
delegate.setupTabBarController()
}
Part of my App Delegate:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
FirebaseApp.configure()
FBSDKApplicationDelegate.sharedInstance().application(application, didFinishLaunchingWithOptions: launchOptions)
setupTabBarController()
return true
}
func setupTabBarController() {
window = UIWindow()
window?.makeKeyAndVisible()
let vc = MainTabBarController()
let controller = UINavigationController(rootViewController: vc)
window?.rootViewController = controller
}
Sign in code:
#objc func handleNormalLogin() {
hud.textLabel.text = "Signing In..."
hud.show(in: view, animated: true)
//TODO
guard let email = emailTextField.text else { return }
guard let password = passwordTextField.text else { return }
Auth.auth().signIn(withEmail: email, password: password) { (user, error) in
if let error = error {
print("Error signing in: \(error)")
return
}
//sucessfully signed in
self.hud.dismiss(animated: true)
self.dismiss(animated: true, completion: nil)
}
}
Any help is really appreciated, I have been stuck on this for a few hours now and I really want to understand what I'm doing wrong.
Cheers
Problem
When you write let delegate = AppDelegate(). It means that you create a new AppDelegate. Instead of using current AppDelegate, you use another AppDelegate. That's why setupTabBarController method doesn't affects anything.
Calling setupTabBarController at the end of handleSignOutButtonTapped isn't a good idea. Because it will replace current rootViewController with UINavigation of MainTabBarController.
Answer
Use self.tabBarController? instead of self to present welcomeNavCon.
Don't call setupTabBarController at the end of handleSignOutButtonTapped method.
Recreate and set new viewControllers for MainTabBarController.
Code
#objc func handleSignOutButtonTapped() {
let signOutAction = UIAlertAction(title: "Sign Out", style: .destructive) { (action) in
do {
try Auth.auth().signOut()
let welcomeControl = WelcomeController()
let welcomeNavCon = UINavigationController(rootViewController: welcomeControl)
self.tabBarController?.present(welcomeNavCon, animated: true) {
let appDelegate = UIApplication.shared.delegate as! AppDelegate;
appDelegate.resetTabBarController();
};
} catch let err {
print("Failed to sign out with error", err)
Service.showAlert(on: self, style: .alert, title: "Sign Out Error", message: err.localizedDescription)
}
}
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
Service.showAlert(on: self, style: .actionSheet, title: nil, message: nil, actions: [signOutAction, cancelAction]) {
}
}
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
var tabBarController : UITabBarController?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
FirebaseApp.configure()
FBSDKApplicationDelegate.sharedInstance().application(application, didFinishLaunchingWithOptions: launchOptions)
setupTabBarController()
return true
}
func setupTabBarController() {
window = UIWindow()
window?.makeKeyAndVisible()
let vc = MainTabBarController()
let controller = UINavigationController(rootViewController: vc)
window?.rootViewController = controller
}
func resetTabBarController() -> Void {
let viewControllerAtIndex1 = ...
let viewControllerAtIndex2 = ...
let viewControllerAtIndex3 = ...
tabBarController?.viewControllers = [viewControllerAtIndex1, viewControllerAtIndex2, viewControllerAtIndex3];
}
}
Related
I try to show an alert when the app launches (a tabbed app)
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
let welcomeAlert = UIAlertController(title: "Turn on Bluetooth?", message: "Turn on Bluetooth?", preferredStyle: UIAlertControllerStyle.alert)
welcomeAlert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: {
action in self.alertServerClient()
}))
welcomeAlert.addAction(UIAlertAction(title: "Cancel", style: UIAlertActionStyle.cancel, handler: nil))
let alertWindow = UIWindow(frame: UIScreen.main.bounds)
alertWindow.rootViewController = UIViewController()
alertWindow.windowLevel = UIWindowLevelAlert + 1;
alertWindow.makeKeyAndVisible()
alertWindow.rootViewController?.present(welcomeAlert, animated: true, completion: nil)
return true
}
In alertServerClient() I want to change UIImage like
func alertServerClient() {
let vc = FirstViewController()
let imgBTOn = UIImage(named: "BT_Button.png")
vc.btnBT.setImage(imgBTOn, for: .normal)
}
Unfortunately, it doesn't work and throw error
Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value
Is it possible to change UI content from AppDelegate?
I have a problem with showing alert after application start. I am using Notification Center observe notification error and find current visible view controller to show alert on it. This work when I handle error after start an app, but if I want to show this error just after start an app, it does not happen.
My code for app delegate:
func listenForRealmErrorNotification() {
NotificationCenter.default.addObserver(forName: MyDataModelDidFailNotification, object: nil, queue: OperationQueue.main, using: { notification in
let alert = UIAlertController(title: NSLocalizedString("Internal alert", comment: "Internal error header"),
message: NSLocalizedString("There was an error while working whith your data", comment: "Internal error description") + "\n\n" + NSLocalizedString("Press OK to terminate the app. Sorry for the inconvenience", comment: "Internal error excuses"),
preferredStyle: .alert)
let action = UIAlertAction(title: NSLocalizedString("OK", comment: "Agree button on internal error"), style: .default, handler: {_ in
let exeption = NSException(name: NSExceptionName.internalInconsistencyException, reason: "Realm error", userInfo: nil)
exeption.raise()
})
alert.addAction(action)
print("***Observe error")
self.viewControllerForShowingAlert().present(alert, animated: true, completion: nil)
})
}
func viewControllerForShowingAlert() -> UIViewController {
let rootViewController = self.window!.rootViewController!
return topViewController(from: rootViewController)
}
func topViewController(from controller: UIViewController) -> UIViewController {
if controller is UINavigationController {
return topViewController(from: (controller as! UINavigationController).visibleViewController!)
}
if controller is UITabBarController {
return topViewController(from:(controller as! UITabBarController).selectedViewController!)
}
if let presentedViewController = controller.presentedViewController {
return topViewController(from:presentedViewController)
}
return controller
}
And code for post notification:
func fatalRealmError(_ error: Error) {
print("***Fatal error with dataBase: \(error)")
Crashlytics.sharedInstance().recordError(error)
NotificationCenter.default.post(name: MyDataModelDidFailNotification, object: nil)
}
UPDATE:
Initiating my data source in delegate:
func initialDataSource() {
do {
dataSource = try UserDataSource()
}
catch let error as NSError {
fatalRealmError(error)
}
}
And here I have set am observer:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
customizeAppearance()
listenForRealmErrorNotification()
initialDataSource()
let rootViewController = window?.rootViewController as! UINavigationController
let rootContentController = rootViewController.viewControllers[0] as! YourFoodViewController
rootContentController.dataSource = dataSource
Fabric.with([Crashlytics.self])
return true
}
I have a terms agreement that needs to pop up only once, however it is popping up each time the app is launched, how can I make it only pop up one time and when pressed agreed to never pop up again unless app is deleted and redownloaded. I am trying to follow How can I show a view on the first launch only?
class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
if !UserDefaults.standard.bool(forKey: "Walkthrough") {
UserDefaults.standard.set(false, forKey: "Walkthrough")
}
}
}
class FirstViewController: UIViewController, UIAlertViewDelegate {
override func viewDidLoad() {
super.viewDidLoad()
if UserDefaults.standard.bool(forKey: "Walkthrough") {
print("already shown")
// Terms have been accepted, proceed as normal
} else {
agree()
}
}
}
my agree function is an alert controller
i forgot to add after agree()
UserDefaults.standard.set(true, forKey: "Walkthrough")
Just save a bool in UserDefaults with the answer if agreed or not and check if the key "WalkThrough" exists or not. If not you will show him the alert if found do nothing.
Add this to the initial viewController in viewDidAppear method:
let alert = UIAlertController(title: "Message", message: "You need to agree to terms and conditions", preferredStyle: .alert)
let action = UIAlertAction(title: "Not Agreed", style: .default) { (action) in
UserDefaults.standard.set(false, forKey: "WalkThrough")
alert.dismiss(animated: true, completion: nil)
}
let action2 = UIAlertAction(title: "Agreed", style: .default) { (action) in
UserDefaults.standard.set(true, forKey: "WalkThrough")
alert.dismiss(animated: true, completion: nil)
}
alert.addAction(action)
alert.addAction(action2)
if (UserDefaults.standard.object(forKey: "WalkThrough") == nil) {
//show alert and save answer
self.present(alert, animated: true, completion: nil)
}
I want to get user location use CCLocationManager. I could get location clearly but "Allow location permission" alert showing repeatedly and non-stop when start first time on any device.
What should I do?
My AppDelegate.swift code;
class AppDelegate: UIResponder, UIApplicationDelegate, CLLocationManagerDelegate {
var window: UIWindow?
func applicationDidBecomeActive(_ application: UIApplication) {
let locationManager = CLLocationManager()
locationManager.requestAlwaysAuthorization()
locationManager.requestWhenInUseAuthorization()
if CLLocationManager.locationServicesEnabled() {
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
locationManager.startUpdatingLocation()
if ((locationManager.location?.coordinate) != nil) {
let locValue:CLLocationCoordinate2D = (locationManager.location?.coordinate)!
let geoCoder = CLGeocoder()
let location = CLLocation(latitude: locValue.latitude, longitude: locValue.longitude)
geoCoder.reverseGeocodeLocation(location) {
(placemarks, error) -> Void in
let placeArray = placemarks as [CLPlacemark]!
// Place details
var placeMark: CLPlacemark!
placeMark = placeArray?[0]
if let state = placeMark.addressDictionary?["State"] as? NSString {
let parameters: Parameters = ["Location": state]
Alamofire.request(GlobalValues.APIUrl, method: .post, parameters: parameters).validate().responseJSON{
response in
if let result = response.result.value {
let JSON = result as! NSDictionary
let items = JSON.object(forKey: "Items") as! NSDictionary
userData.set(items.object(forKey: "fajr") as! String, forKey: "sabahNamazi")
userData.set(items.object(forKey: "dhuhr") as! String, forKey: "ogleNamazi")
userData.set(items.object(forKey: "asr") as! String, forKey: "ikindiNamazi")
userData.set(items.object(forKey: "maghrib") as! String, forKey: "aksamNamazi")
userData.set(items.object(forKey: "isha") as! String, forKey: "yatsiNamazi")
}
}
}
}
} else {
// create the alert
let alert = UIAlertController(title: "Konum Alınamıyor", message: "Telefonunuzun konum ayarlarını kontrol edip tekrar deneyin!", preferredStyle: UIAlertControllerStyle.alert)
// add the actions (buttons)
alert.addAction(UIAlertAction(title: "Tamam", style: UIAlertActionStyle.default, handler: nil))
// show the alert
self.window?.rootViewController?.present(alert, animated: true, completion: nil)
}
} else {
// create the alert
let alert = UIAlertController(title: "Konum Ayarını Açın", message: "Bu uygulama bulunduğunuz şehrin 'EZAN VAKİTLERİNİ' tespit edebilmek için konum bilginize ihtiyaç duyuyor.", preferredStyle: UIAlertControllerStyle.alert)
// add the actions (buttons)
alert.addAction(UIAlertAction(title: "Ayarlar", style: UIAlertActionStyle.default, handler: {(
action:UIAlertAction!) -> Void in
UIApplication.shared.openURL(URL(string: "prefs:root=LOCATION_SERVICES")!)
}))
alert.addAction(UIAlertAction(title: "İptal", style: UIAlertActionStyle.cancel, handler: nil))
// show the alert
self.window?.rootViewController?.present(alert, animated: true, completion: nil)
}
}
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
return true
}
}
Alert Image:
put your location permission code in didFinishLaunchingWithOptions instead, so it will be asked once, then set any boolean value such as alreadyAskedPermission variable in NSDefault
I am trying to make a UIAlertController pop up one time when the user first downloads the app. However, I get the following error when I put the code in didFinishLaunchingWithOptions,
2015-01-15 23:54:45.306 WeddingApp Warning: Attempt to present UIAlertController: on UITabBarController: whose view is not in the window hierarchy!
My code is below:
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Override point for customization after application launch.
if window == nil {
println("window is nil");
return true;
}
if window!.rootViewController == nil {
println("window!.rootViewController is nil");
return true;
}
//TabBarController//
let tabBarController: UITabBarController = window!.rootViewController! as UITabBarController;
UITabBar.appearance().tintColor = UIColor.magentaColor();
UITabBar.appearance().translucent = false;
/* tableView cells are now completely visable. They do not hie behind the tabBar */
tabBarController.viewControllers = [
TwelveToTenMonths(nibName: nil, bundle:nil),
NineToSevenMonths(nibName: nil, bundle:nil),
SixToFourMonths(nibName: nil, bundle:nil),
ThreeToOneMonth(nibName: nil, bundle:nil),
];
var alert = UIAlertController(
title: "Welcome to WeddingApp!",
message: "Thank You for choosing the \nWedding App for your organization needs. \n\nFor more great tools please visit our website www.wedme.com",
preferredStyle: UIAlertControllerStyle.Alert);
alert.addAction(UIAlertAction(title: "Continue", style: UIAlertActionStyle.Default, handler: nil));
self.window!.rootViewController!.presentViewController(alert, animated: true, completion: nil);
return true
}
***NOTE: I also tried adding the following to the appDelegate instead of the above code but the alert continues to appear whenever I return to the app.....
func applicationDidBecomeActive(application: UIApplication) { //Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
showAlert();
}
func showAlert(){
println("alert");
var alert = UIAlertController(
title: "Welcome to WeddingApp!",
message: "Thank You for choosing the \nWedding App for your organization needs. \n\nFor more great tools please visit our website www.wedme.com",
preferredStyle: UIAlertControllerStyle.Alert);
alert.addAction(UIAlertAction(title: "Continue", style: UIAlertActionStyle.Default, handler: nil));
self.window!.rootViewController!.presentViewController(alert, animated: true, completion: nil);
}
Does anyone know a way around this???
In the first case, you never added the UIAlertController (you could try adding it as a child view controller).
https://stackoverflow.com/a/17012439/3324388
In the second case you assigned the root controller to the UIAlertController and never assign the root controller back to the UITabBarController. You need to set it back.
Have you tried using a UIAlertView instead?
https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIAlertView_Class/index.html
Try something like this:
func application(application: UIApplication!, didFinishLaunchingWithOptions launchOptions: NSDictionary!) -> Bool {
if self.window!.rootViewController as? UITabBarController != nil {
var tababarController = self.window!.rootViewController as UITabBarController
.
.
.
let alertController = UIAlertController(title: "Welcome...", message: "Thank You...", preferredStyle: .Alert)
tababarController.presentViewController(alert, animated: true, completion: nil);
}
return true
}