Login Screen Always Shown - swift

I have a login screen that prompts the user to login. After successful login the first time, I need the app to eliminate the login screen when the user opens the app again. I am using NSUserDefaults to set the successful login as true. However, the login screen is always shown to the user. Please help. Thanks.
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
if (NSUserDefaults.standardUserDefaults().objectForKey("onoroff") != nil) {
self.performSegueWithIdentifier("segue", sender: nil)
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func button(sender: AnyObject) {
if ((loginid.text == "Hussain") && (passwordText.text == "1234" ))
{
NSUserDefaults.standardUserDefaults().setBool(true, forKey: "onoroff")
self.performSegueWithIdentifier("segue", sender: nil) }
else
{
failedtext.hidden = false }
}

Try adding
if (NSUserDefaults.standardUserDefaults().boolForKey("onoroff"))) {
self.performSegueWithIdentifier("segue", sender: nil)
}
in viewDidAppear method instead of viewDidLoad

Change objectForKey to boolForKey
if (NSUserDefaults.standardUserDefaults().boolForKey("onoroff")) {
self.performSegueWithIdentifier("segue", sender: nil)
}
Also do this
#IBAction func button(sender: AnyObject) {
if ((loginid.text == "Hussain") && (passwordText.text == "1234" ))
{
NSUserDefaults.standardUserDefaults().setBool(true, forKey: "onoroff")
NSUserDefaults.standardUserDefaults().synchronize()
self.performSegueWithIdentifier("segue", sender: nil)
}
else
{
failedtext.hidden = false
}
}

Related

How can I get an updated value from UserDefaults?

I have an Onboarding controller, when a user has completed Onboarding I am writing a value in UserDefaults and then dismissing the OnboardingViewController.
My base viewcontroller however is pushing them back to the Onboarding flow as UserDefaults.standard.bool(forKey: "ONBOARDING|COMPLETE") is returning false.
If I restart my app however is will return true and they are pushed to the correct VC.
My Base ViewController
class ViewController: UIViewController {
var onboardingComplete: Bool {
get {
return UserDefaults.standard.bool(forKey: "ONBOARDING|COMPLETE")
}
}
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
print(UserDefaults.standard.bool(forKey: "ONBOARDING|COMPLETE"))
print(onboardingComplete)
setBaseViewController()
}
fileprivate func setBaseViewController() {
if onboardingComplete {
print("has completed onboarding")
} else {
let layout = UICollectionViewFlowLayout()
navigationController?.present(OnboardingController(collectionViewLayout: layout), animated: true, completion: { })
}
}
}
My Onboarding controller has a method in which I am using to set the value as complete
#objc func handleCompleteOnboarding() {
dismiss(animated: true) {
DispatchQueue.main.async {
UserDefaults.standard.set(true, forKey: "ONBOARDING|COMPLETE")
}
}
}
You are setting the value in the completion handler.
Move this outside of dismiss.
#objc func handleCompleteOnboarding() {
UserDefaults.standard.set(true, forKey: "ONBOARDING|COMPLETE")
dismiss(animated: true)
}
You have essentially created a race condition between you setting the value and your viewDidAppear method being called.

Attempt to present <a> on <a> while a presentation is in progress

i know this has been asked before but i'm trying to figure out the issue with my project, as the title states (a) is trying to present on (a) i have checked all segue triggers to see if i accidentally set a segue to go to the same view controller that it is already on but this is not the case.
view controller 1 code
import UIKit
import UserNotifications
class NotificationViewController: UIViewController {
let isRegisteredForRemoteNotifications = UIApplication.shared.isRegisteredForRemoteNotifications
let current = UNUserNotificationCenter.current()
#IBAction func Notification(_ sender: Any) {
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound, .badge], completionHandler: {didAllow, error in})
var i = 0
while i < 1{
current.getNotificationSettings(completionHandler: { (settings) in
if settings.authorizationStatus == .notDetermined {
// Notification permission has not been asked yet, go for it!
}
if settings.authorizationStatus == .denied {
i = i + 1
DispatchQueue.main.async {
self.performSegue(withIdentifier: "ToLocation", sender: self)
// Notification permission was previously denied, go to settings & privacy to re-enable
}
}
if settings.authorizationStatus == .authorized {
i = i + 1
DispatchQueue.main.async {
self.performSegue(withIdentifier: "ToLocation", sender: self)
// Notification permission was already granted
}
}
})
}
}
override func viewDidLoad() {
current.getNotificationSettings(completionHandler: { (settings) in
if settings.authorizationStatus == .notDetermined {
// Notification permission has not been asked yet, go for it!
}
if settings.authorizationStatus == .denied {
DispatchQueue.main.async {
self.performSegue(withIdentifier: "ToLocation", sender: self)
// Notification permission was previously denied, go to settings & privacy to re-enable
}
}
if settings.authorizationStatus == .authorized {
DispatchQueue.main.async {
self.performSegue(withIdentifier: "ToLocation", sender: self)
// Notification permission was already granted
}
}
})
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
view controller 2 code
import UIKit
import CoreLocation
class LocationViewController: UIViewController {
#IBOutlet weak var textview: UITextView!
let locationManager = CLLocationManager()
#IBAction func OnLocation(_ sender: Any) {
locationManager.delegate = self as? CLLocationManagerDelegate
var i = 0
while i < 1{
switch CLLocationManager.authorizationStatus() {
case .notDetermined:
// Request when-in-use authorization initially
locationManager.requestWhenInUseAuthorization()
break
case .restricted, .denied:
i = i + 1
DispatchQueue.main.async {
self.performSegue(withIdentifier: "ToLogin", sender: self)
}
// Disable location features
//disableMyLocationBasedFeatures()
break
case .authorizedWhenInUse:
i = i + 1
DispatchQueue.main.async {
self.performSegue(withIdentifier: "ToLogin", sender: self)
}
// Enable basic location features
//enableMyWhenInUseFeatures()
break
case .authorizedAlways:
// Enable any of your app's location features
// enableMyAlwaysFeatures()
break
}
}
}
override func viewDidLoad() {
func locationManager(_ manager: CLLocationManager,
didChangeAuthorization status: CLAuthorizationStatus) { switch status {
case .restricted, .denied:
self.performSegue(withIdentifier: "ToLogin", sender: self)
break
case .authorizedWhenInUse:
self.performSegue(withIdentifier: "ToLogin", sender: self)
break
case .authorizedAlways:
self.performSegue(withIdentifier: "ToLogin", sender: self)
break
case .notDetermined:
break
}
}
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
error message
018-02-27 23:06:41.534749+1030 Request[27358:1351259] Warning: Attempt
to present on
while a presentation
is in progress!
No no no no no. This is not the way. sorry about this plenty of no´s but completionHandler result parameter of the requestAuthorization method from UNUserNotification may be executed on a background thread.
tips for this function
#IBAction func Notification(_ sender: Any)
*should start with lowercase
*provide a name that the action it accomplish can be inferred by that name
*change the type of the sender (if you know it), that way you can explicitly call properties or methods on that object without cast.
continuing with the rest of the code inside the scope of the function. requestAuthorization have the only responsibility of that - ask for a permission - and the response is didAllow or error. You never check this and continue launching another block that response is also another thread
bottom line: your calling requestAuthorization and then getNotificationSettings inside a loop (why?), you have a lot of luck if this code execute 30% of the time.
So you should separate some of the code involving permissions, read some lines about GRASP principle, also read every chapter here
Your error message is pretty clear while a presentation is in progress. That mean that you try to present LocationViewController from another LocationViewController, but it still not presented yet.
Move your presentation logic to viewWillAppear method. It will help
UPDATE
1) remove your existing segue from Notification button to LocationViewController in storyboard
2) Add segue from NotificationViewController to LocationViewController like it shown on picture
3) Set name for this segue
Now try to run your project

Swift, core data, add item from modally presented view

I am updating an app into Swift and have a tableView to which items can be added from a modally presented view. This works fine when the Save button is pressed, but when cancel is pressed the table view is presented with a blank line at the top, showing that an empty item was added. What am I missing?
The AddViewController protocol is:
protocol NewGaugeDelegate {
func didFinish(viewController:AddGaugeViewController, didSave:Bool)
}
and the didSave function is:
extension AddGaugeViewController {
#IBAction func cancelButtonWasTapped(_ sender: AnyObject) {
delegate?.didFinish(viewController: self, didSave: false)
}
#IBAction func saveButtonWasTapped(_ sender: AnyObject) {
addGauge()
delegate?.didFinish(viewController: self, didSave: true)
}
}
in the tableView controller, the protocol is accessed like this:
extension GaugeTableViewController: NewGaugeDelegate {
func didFinish(viewController: AddGaugeViewController, didSave: Bool) {
guard didSave,
let context = viewController.context,
context.hasChanges else {
dismiss(animated: true)
return
}
context.perform {
do {
try context.save()
} catch let error as NSError {
fatalError("Error: \(error.localizedDescription)")
}
self.coreDataStack.saveContext()
}
dismiss(animated: true)
}
}
Suggestions as to why the cancel function is not working properly would be appreciated.

performSegueWithIdentifier not working if called from viewDidLoad

I have a simple app with a loading screen. Here I check for some user details in NSUserDefaults and jump to either the login or the sign up screen.
The viewDidLoad() for the loading screen looks like this:
override func viewDidLoad()
{
super.viewDidLoad()
loadingVM = LoadingVM() as LoadingVM
print("LoadingVC")
checkStoredUser()
}
Here is the checkStoredUser()
func checkStoredUser()
{
storedUserStatus = loadingVM.returnStoredUserStatus()
if(storedUserStatus == true)
{
performSegueWithIdentifier("loadingToLoginVC", sender: self)
}
else
{
performSegueWithIdentifier("loadingToSignUpVC", sender: self)
}
}
As you can see, I decide where to go from here based on what the loadingVM.returnStoredUserStatus() returns. I am sure this returns what it's supposed to return but nothing happens.
Here is the prepareForSegue()
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?)
{
print("prepareForSegue")
if(segue.identifier == "loadingToSignUpVC")
{
let signUpViewCotroller = (segue.destinationViewController as! LocalSignUpVC)
}
else if(segue.identifier == "loadingToLoginVC")
{
print("loadingToLoginVC")
let loginViewCotroller = (segue.destinationViewController as! LoginVC)
}
}
I did some digging and found a weird suggestion that seems to be working but It's not very practical not to mention right to do it like this:
func checkStoredUser()
{
storedUserStatus = loadingVM.returnStoredUserStatus()
let delayTime = dispatch_time(DISPATCH_TIME_NOW, Int64(1.0 * Double(NSEC_PER_SEC)))
dispatch_after(delayTime, dispatch_get_main_queue())
{
if(self.storedUserStatus == false)
{
self.performSegueWithIdentifier("loadingToSignUpVC", sender: self)
}
else
{
self.performSegueWithIdentifier("loadingToLoginVC", sender: self)
}
}
}
Can anyone explain to me what's going on here, why doesn't this work and how to make it work properly? It's the first time I encounter this and I can't seem to be able to find any info on this.
EXPLANATION:
Your View hasn't appeared yet when you call your checkStoredUser().
EASY FIX:
Put it in viewDidAppear() like this:
override func viewDidAppear(animated:Bool) {
super.viewDidAppear(false)
checkStoredUser()
}

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.