UserDefaults.standard.string() returns nil - ios

I "inherited" an iOS Xcode Project in Swift. I've never programmed in Swift or used Xcode before. I copied the project from one user account to another and checked it into a git repo.
Now I'm araid this (the copying, permissions ?) might be the cause for the App not being able to read its settings properly. Because UserDefaults.standard.string(forKey: "pref_Foo") returns nil although in the Settings.Bundle's Root.plist there's clearly a pref_Foo identifier.
The App has worked before, so I don't see where this suddenly comes from.
Since I'm not too familiar with XCode all I did up til now was debug into the Application to see that UserDefaults.standard.string(forKey: "pref_Foo") is nil.
How could I approach this problem?
Thank you!
EDIT: This is part of my code in ViewController.swift
override func viewDidLoad() {
super.viewDidLoad()
loadSettings()
...
}
func loadSettings()
{
if(UserDefaults.standard.object(forKey: "pref_Foo") == nil)
{
printf("Error")
}
}

You should use below code with validation, first delete app and install again. then use, you will get success.
var UserStatus:String {
get{
return UserDefaults.standard.object(forKey: "pref_Foo") as? String ?? "0"
}
set(status){
UserDefaults.standard.set(status, forKey: "pref_Foo")
UserDefaults.standard.synchronize()
}
}
Use of code is as below.
if (UserStatus == "pref_Foo"){
self.logoutSuccess()
} else {
self.loginSuccess()
}
Update or set value is so simple in one line code as given below.
UserStatus = "pref_Foo"

Related

Saving then accessing entity from managedObjectContext in CoreData occasionally crashes

I'm struggling to debug a hard-to-reproduce-locally crash in my app. The crash reports show the exception type is EXC_BAD_ACCESS (SIGSEGV) with objc_msgSend at the top of the crashed thread, perhaps suggesting "The process may have attempted to message a deallocated object" (as per Apple's documents).
I have not been able to recreate the crash or find any zombies using the Zombies instrument, despite using an older device and the same simulated device causing the most crashes.
From the trace I'm pretty sure I've been able to work out where the crash is occurring, but I'm not sure what I'm doing wrong.
My app is basically a group database. The app takes a username and password, sending them to a server to check if the person can be logged in. If valid, the app downloads the data on all the people in the group, then finds the logged in person from the downloaded data based on their username.
Because of the way the server code is set up, the username must be present in the downloaded data (if it all downloads correctly), yet my code occasionally fails to find them, and (from the crash logs) also occasionally this attempted finding of the person causes a crash.
coreDataStack.storeContainer.performBackgroundTask() { context in
OverallNetworkCalls.deleteAllLoadPeople(moContext: context) { result in
self.coreDataStack.saveNewContext(context: context)
if result == .success {
let person = AdministrativeHelperFunctions.returnCurrentUserFromUserName(moContext: context)
if let person = person {
DispatchQueue.main.async {
self.activityIndicator.stopAnimating()
}
UserDefaults.standard.set(person.objectID.uriRepresentation(), forKey: udl.CurrentUserMOID.rawValue)
} else {
DispatchQueue.main.async {
self.activityIndicator.stopAnimating()
self.loadingLabel.text = "Data was downloaded but your username isn't there. Try deleting and reloading the app, or contact admin."
}
}
}
func saveNewContext(context: NSManagedObjectContext) {
context.perform {
guard context.hasChanges else { return }
do {
try context.save()
} catch {
//logs the issue locally
}
}
}
func returnCurrentUserFromUserName(moContext: NSManagedObjectContext) -> Person? {
let userName = UserDefaults.standard.string(forKey: "username")
if let userName = userName {
//Create predicate, arrayForResults etc
do {
userNameMatchesArray = try moContext.fetch(fetchRequest)
} catch {
//logs the issue locally
}
if userNameMatchesArray.count == 1 {
return userNameMatchesArray[0]
} else {
return nil
}
} else {
return nil
}
}
My suspicion is that the returnCurrentUserFromUserName function is returning the user before the saveNewContext function has completed its task and that this is contributing to the issue, though given the same managedObjectContext is saving then retrieving the person I had originally assumed this wasn't a problem. I'm also not sure that using "context.perform" in the saveNewContext function is necessary/wise/useful; I'd value feedback on this too if anyone knows best practice inside a performBackgroundTask block.
A non-reproduceable bug is always the most frustrating to fix, and because of this anything I try to fix it won't be shown until I send it to my beta testers and get crashlogs back (or not!).
Thanks in advance for reviewing this problem.

NSLocale.current.description crashing in Xcode 9

I created a brand new single view application and added one line to the viewDidLoad method of the ViewController.swift file:
override func viewDidLoad() {
super.viewDidLoad()
_ = NSLocale.current.description
}
The NSLocale.current.description line crashes in Xcode 9 with no stack trace (just a EXC_BAD_ACCESS code=EXC_I386_GPFLT error message). The same project runs fine in Xcode 8.3.3. Anyone have any ideas why this is happening?
Here is my simulator region settings:
Based on the stack trace, it looks like it's trying to treat description as an ObjC property rather than a Swift property. Based on the source code, this shouldn't be happening. It's likely a bug in the latest Swift compiler that is producing the Swift libraries because it crashes on an iOS 9 device as well.
Be sure to file a bug with Swift since this seems to be a language bug. I've verified that it is still broken in the latest Swift 4 toolchain. In the meantime, you can get the same behavior of description using your own extension by simply duplicating the intended implementation like I've shown here.
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// let brokenDescription = NSLocale.current.description
// let otherBrokenDescription = Locale.current.description
let objcDescription = (Locale.current as NSLocale).debugDescription //"<__NSCFLocale: 0x1c00dbc10> \'en_US\'}"
let myDescription = Locale.current.myDescription // "en_US (current)"
}
}
extension Locale {
private var _kindDescription : String {
if self == Locale.autoupdatingCurrent {
return "autoupdatingCurrent"
} else if self == Locale.current {
return "current"
} else {
return "fixed"
}
}
public var myDescription: String {
return "\(identifier) (\(_kindDescription))"
}
public var myDebugDescription : String {
return "\(identifier) (\(_kindDescription))"
}
}
This was a bug fixed in Xcode 9 Beta 5.

UserDefaults is not saved with Swift

I'm trying to use the UserDefaults to persistently save a boolean value. This is my code :
public static var isOffline = UserDefaults.standard.bool(forKey: "isOffline") {
didSet {
print("Saving isOffline flag which is now \(isOffline)")
UserDefaults.standard.set(isOffline, forKey: "isOffline")
UserDefaults.standard.synchronize()
}
}
Why doesn't work? What is the problem in this code?
The problem is that when I try to retrieve "isOffline" key from UserDefaults I always get a false.
I set the isOffline in the .onChange method of the row (I'm using Eureka as framework to create forms). The flag keep the right value during the lifecycle of the app, but when I close it that value is probably removed in some way.
I had the same problem and the issue was in the "didSet" block itself. I don't know why, but it does not work with userDefaults - it does not persist it properly and after killing the application all changes were gone.
Synchronize() does not help. I found out, this method is no longer necessary and it will be deprecated in future (this is comment in UserDefaults class):
-synchronize is deprecated and will be marked with the NS_DEPRECATED macro in a future release.
By trial and error I found out, that it works, if I call it from main thread:
public static var isOffline = UserDefaults.standard.bool(forKey: "isOffline") {
didSet {
print("Saving isOffline flag which is now \(isOffline)")
DispatchQueue.main.async {
UserDefaults.standard.set(isOffline, forKey: "isOffline")
}
}
}
If anyone can explain, why it works on main thread and no other, I would be glad to hear it.
try to change
UserDefaults.standard.set(isOffline, forKey: "isOffline")
to
UserDefaults.standard.setValue(isOffline, forKey: "isOffline")
without the dispatch code
Do Like this,
public static var isOffline:Bool {
get {
return UserDefaults.standard.bool(forKey: "isOffline")
}
set(newValue) {
print("Saving isOffline flag which is now \(isOffline)")
UserDefaults.standard.set(newValue, forKey: "isOffline")
UserDefaults.standard.synchronize()
}
}
Don't ask me why, but I had this issue also in a project in Xcode 14.
After hours of debugging I realized that the length of my key exceeded 18 characters, it would no longer be saved. However, I couldn't reproduce this in a vanilla project, so I guess it had something to do with the project.
But anyway: if you're experiencing this, try decreasing the character count of your key.

Difficulty reading UserDefaults with Fastlane Snapshot

I'm using Fastlane's snapshot to create screenshots for an app I'm about to submit to the App Store.
It works "as advertised" for the most part, but it doesn't seem to like the way I access the UserDefaults within my app. On one test, it generates an Exit status: 65 error.
UI Testing Failure - com.me.MyApp crashed in (extension in MyApp):__ObjC.NSObject.defaultTime () -> Swift.Float
I find UserDefaults.standard.value(forKey: "defaultTime") to an invitation for a syntax error, so I created an extension to access UserDefaults. Here's what the extension looks like:
class CustomExtensions: NSObject {
/*
This is blank. Nothing else in here. No really...nothing else
*/
}
extension NSObject {
// User Defaults
func defaultTime() -> Float {
return UserDefaults.standard.value(forKey: "defaultTime") as! Float
}
// a bunch of other UserDefaults
}
Wihin the app, whenever I need defaultTime, I just type defaultTime(). Using this method to access UserDefaults values works fine in the Simulator and on the devices I've tested. I only encounter a problem with snapshot.
I've tried adding in sleep(1) within the test, but that doesn't seem to do anything. I welcome suggestions re: alternative means of accessing UserDefaults that enable me to access them easily throughout MyApp.
What's probably happening is that, in your simulator and on device, you're writing a value to user defaults for the key defaultTime before it is ever read. value(forKey: returns an optional, and if you force-unwrap it (or force down-cast as your are doing here), you will crash if the value is nil. Try either returning an optional:
func defaultTime() -> Float? {
return UserDefaults.standard.value(forKey: "defaultTime") as? Float
}
or using a default value:
func defaultTime() -> Float {
return UserDefaults.standard.value(forKey: "defaultTime") as? Float ?? 0.0
}

Swift Problems - NSUserDefault and Settings Bundle not responding

So I had set up a settings bundle to just do one thing. Allow the Users to choose between the The TouchUI and GestureUI in my app and for some odd reason, I am unable to get the Settings Bundle to control it. It stays with one and doesn't switch even when I have the If else Statement. originally I had var touchCheck = userDefaults.boolForKey("myGestureEnabledDisabled") but it didn't change boolean at all when i keep closing app(Multitask > SwipeUp app) and re running the app via springboard. The Settings App could have the bundle at NO but log says gestures are on. After watching a Tutorial, i changed boolForKey to valueForKey which causes the build to fail and there is no error in the code the way i have it below.
override func viewDidLoad() {
var userDefaults = NSUserDefaults.standardUserDefaults()
userDefaults.synchronize()
var touchCheck = Bool(userDefaults.valueForKey("myGestureEnabledDisabled"))
if touchCheck {
whenGuestureIsEnabled()
} else {
whenGestureIsDisabled()
self.navBar.hidden = false
}
}
func whenGuestureIsEnabled() {
NSLog("Gesture is suppose to be on")
}
func whenGestureIsDisabled() {
NSLog("Gesture is OFF")
}
Maybe from what I was thinking, I shouldn't do this in UIViewController but I had seen this in action in a youtube tutorial and it was in OBJ-C.
You should cast to bool, not to create a new one:
//notice (Bool) cast in the beginning
var touchCheck = (Bool)userDefaults.valueForKey("myGestureEnabledDisabled")

Resources