IOS KeyChainWrapper value is nil after stored - ios

Im a beginner in Swift 3.0. Recently i just convert existing swift project to 3.0 swift. After converrted, i have issues storing user& password using key chain wrapper.
i have checked the result of Bool, it just show false and the value is nil.
Can i know the possible causes? thanks for guiding.
//This is how i store
var saveSuccessful:Bool = KeychainWrapper.defaultKeychainWrapper().setString(txt_userId.text!, forKey: "userId")
let savePass:Bool = KeychainWrapper.setString(txt_password.text!, forKey: "password")
//this is how i get/retrieve
var password:String? = KeychainWrapper.stringForKey("password")

Take a look at my answer here. The problem is that the "Keychain Sharing" capability has to be enabled.

Related

In swift version 5.1.3 How is NSUserDefaults.standardUserDefaults().stringForKey used?

Hi I am learning by watching the video. Since it was an old version, it seemed that you were using NS, but I don’t know how to use it now, so I ask
Youtube video link
https://www.youtube.com/watch?v=PKOswUE731c >> 32:40
let userEmailStored = NSUserDefaults.standardUserDefaults().stringForKey("userEmail")
let userPasswordStored = NSUserDefulats.standardUserDefaults().stringForKey("userPassword")
In the current version, we know that you should use it without NS, but it is not used even if you subtract NS.
Cannot be used
I would appreciate it if you let me know.
You can use UserDefaults now and get the String values like this:
let userEmailStored = UserDefaults.standard.string(forKey: "userEmail")
let userPasswordStored = UserDefaults.standard.string(forKey: "userPassword")
You can use UserDefaults now and set the String values like this:
UserDefaults.standard.set("email", forKey: "userEmail")
UserDefaults.standard.set("password", forKey: "userPassword")

Save userDefault token after Login

I am new in software and I have a question.
I have LoginPage called LoginVC(screenshot as below).When the user opened the app first time, if the member login with his username and password or via Facebook account, next time he opened the app he will pass the login screen and show the "NewsVC" directly. If he logged out, he will see the Login Page again.
According to my investigations I must use UserDefault method and create a local database(for example SQLite). Probably it creates a access token for the entered users. But I don't know how I will do. Maybe there is the question about this problem in this site but because of I don't know in a detailed manner couldn't find the topic.
Can you explain this topic and share an example with a simple Swift 3 code.
Thanks in advance
LoginVC ScreenShot
Securitywise, it is considered a bad practice to store login tokens in UserDefaults, I'd suggest using Keychain API instead.
"Hackers" can relatively easy read data from UserDefaults and use your access token.
Keychain API is a bit hard to use, I'd suggest trying a 3rd party library, here is one example:
https://github.com/jrendel/SwiftKeychainWrapper
More info about securing your data on iOS:
https://github.com/felixgr/secure-ios-app-dev
If you are just learning - it is OK to use UserDefaults, but once you consider moving your app to production - refactor it to Keychain.
Try following Helper method
Set User ID
func setCurrentLoginID(_ struserid: String) {
UserDefaults.standard.set(struserid, forKey:"userID")
}
Check User Login or Not
func isUserLoggedIN() -> Bool {
let str = UserDefaults.standard.object(forKey: "userID") as! String
return str.characters.count > 0 ? true : false
}
Get User ID
func loggedUserId() -> String {
let str = UserDefaults.standard.object(forKey: "userID") as? String
return str == nil ? "" : str!
}
For Logout
func logout() {
UserDefaults.standard.set(nil, forKey: "userID")
}
Assuming you wanted to know how to implement this then you can store and get the value like below:-
let default = UserDefaults.standard
default.set(accessToken, forKey: "accessToken")
default.synchronized()
//Now get like this and use guard so that it will prevent your crash if value is nil.
guard let accessTokenValue = default.string(forKey: "accessToken") else {return}
print(accessTokenValue)

How to detect First launch of app on every new version upgrade?

I have a requirement of detecting the first launch of app after the user upgrades the app to a newer version. I need to perform certain task only on first launch of app after the user upgrades the app to a new version. Many links available online but none answer clearly to my query. How to achieve this in Swift 2 , iOS 9.
Most of the answers available says to maintain a key in NSUserDefaults and set its value to false and after first launch make it true. But the problem is after I upgrade my app the variable still will be true and thus my scenario fails on app upgrade. Any help would be much appreciated. Thanks!
Try this:
let existingVersion = NSUserDefaults.standardUserDefaults().objectForKey("CurrentVersionNumber") as? String
let appVersionNumber = NSBundle.mainBundle().objectForInfoDictionaryKey("CFBundleShortVersionString") as! String
if existingVersion != appVersionNumber {
NSUserDefaults.standardUserDefaults().setObject(appVersionNumber, forKey: "CurrentVersionNumber")
NSUserDefaults.standardUserDefaults().synchronize()
//You can handle your code here
}
updating Yogesh's perfect, yet simple solution to swift 4
let existingVersion = UserDefaults.standard.object(forKey: "CurrentVersionNumber") as? String
let appVersionNumber = Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as! String
if existingVersion != appVersionNumber {
print("existingVersion = \(String(describing: existingVersion))")
UserDefaults.standard.set(appVersionNumber, forKey: "CurrentVersionNumber")
// run code here.
}

iOS 9 ShareData between AppGroup through NSUserDefaults

I'm trying to add TodayExtension to my existing app and I want that app and extension will share data through NSUserDefaults.
According to this tutorial http://www.glimsoft.com/06/28/ios-8-today-extension-tutorial/
I did like it say:
Create new target with Today extension
Add new App-Group in Extension and App
Add code to app
Add code to extension
App Code
func updateExtensionData() {
let key = "identityCode"
let valueForStore:Float = 2.0
let extensiontDefaults = NSUserDefaults(suiteName: "group.company.mineSharedDefaults")
extensiontDefaults?.setFloat(valueForStore, forKey: key)
let results = extensiontDefaults?.synchronize()
let checkCode = extensiontDefaults?.objectForKey(key)
print("code for save \(valueForStore) synchronize result \(results) readed code \(checkCode!)")
}
Extension code
private func updateData() {
let key = "identityCode"
let extensionDefaults = NSUserDefaults(suiteName: "group.company.mineSharedDefaults")
let checkCode = extensionDefaults?.floatForKey(key)
print("synchronize result \(resut) readed code \(checkCode!)")
}
Issue
My issue is that extension always read 0 instead of 2. Clearly I'm missing something. There is some major difference between iOS8 and 9 in case of AppGroup and NSUserDefaults? I know that there is but between app an watchOS2.
For being more clear this is keys from App and Extension NSUserDefaults
App Keys
AppleKeyboards
AppleKeyboardsExpanded
AddingEmojiKeybordHandled
AppleLanguages
ApplePasscodeKeyboards
PreferredLanguages
AppleLocale
NSInterfaceStyle
MSVLoggingMasterSwitchEnabledKey
NSLanguages
AppleITunesStoreItemKinds
identityCode
AppleLanguagesDidMigrate
PKEnableStockholmSettings
Extension Keys
AppleKeyboards
AppleKeyboardsExpanded
AddingEmojiKeybordHandled
AppleLanguages
ApplePasscodeKeyboards
PreferredLanguages
AppleLocale
NSInterfaceStyle
MSVLoggingMasterSwitchEnabledKey
NSLanguages
AppleITunesStoreItemKinds
AppleLanguagesDidMigrate
PKEnableStockholmSettings
It's clear that key identityCode is not appearing in extension at all.
Ok I'm not sure what helps but I've done two thing:
rename current AppGroup identifier and create new one with name group.my.entire.bundle.id.of.my.app.sharedData instead of group.appname.sharedData
remove and create new AppGroup identifier
I can use "identityCode" as key.
CThere could be several things!
first; did you allow keychain sharing under "Capabilities" for both your container app and the extension, and did you set the Keychain groups to the same group?
I recommend using this: https://github.com/kefbytes/KeychainWrapper
download it, it's very simple, just add the swiftfile to your project
allow keychain sharing under cabailities,
set your serviceName and accesgroup like this
KeychainWrapper.serviceName = "give it a name in here"
(servicename is neccessary for it to work)
KeychainWrapper.accessGroup = "write your accessgroup name here"
(accesgroup is optional, since you are setting it under Capabilities)
save a value to a key like this
KeychainWrapper.setString("2", forKey: "identityCode"
and retrieve with
KeychainWrapper.stringForKey("identityCode")

How to save local data in a Swift app?

I'm currently working on a iOS app developed in Swift and I need to store some user-created content on the device but I can't seem to find a simple and quick way to store/receive the users content on the device.
Could someone explain how to store and access local storage?
The idea is to store the data when the user executes an action and receive it when the app starts.
The simplest solution for storing a few strings or common types is UserDefaults.
The UserDefaults class provides convenience methods for accessing common types such as floats, doubles, integers, Boolean values, and URLs.
UserDefaults lets us store objects against a key of our choice, It's a good idea to store these keys somewhere accessible so we can reuse them.
Keys
struct DefaultsKeys {
static let keyOne = "firstStringKey"
static let keyTwo = "secondStringKey"
}
Setting
let defaults = UserDefaults.standard
defaults.set("Some String Value", forKey: DefaultsKeys.keyOne)
defaults.set("Another String Value", forKey: DefaultsKeys.keyTwo)
Getting
let defaults = UserDefaults.standard
if let stringOne = defaults.string(forKey: DefaultsKeys.keyOne) {
print(stringOne) // Some String Value
}
if let stringTwo = defaults.string(forKey: DefaultsKeys.keyTwo) {
print(stringTwo) // Another String Value
}
Swift 2.0
In Swift 2.0 UserDefaults was called NSUserDefaults and the setters and getters were named slightly differently:
Setting
let defaults = NSUserDefaults.standardUserDefaults()
defaults.setObject("Some String Value", forKey: DefaultsKeys.keyOne)
defaults.setObject("Another String Value", forKey: DefaultsKeys.keyTwo)
Getting
let defaults = NSUserDefaults.standardUserDefaults()
if let stringOne = defaults.stringForKey(DefaultsKeys.keyOne) {
print(stringOne) // Some String Value
}
if let stringTwo = defaults.stringForKey(DefaultsKeys.keyTwo) {
print(stringTwo) // Another String Value
}
For anything more serious than minor config you should consider using a more robust persistent store:
CoreData
Realm
SQLite
They Say Use NSUserDefaults
When I was implementing long term (after app close) data storage for the first time, everything I read online pointed me towards NSUserDefaults. However, I wanted to store a dictionary and, although possible, it was proving to be a pain. I spent hours trying to get type-errors to go away.
NSUserDefaults is Also Limited in Function
Further reading revealed how the read/write of NSUserDefaults really forces the app to read/write everything or nothing, all at once, so it isn't efficient. Then I learned that retrieving an array isn't straight forward. I realized that if you're storing more than a few strings or booleans, NSUserDefaults really isn't ideal.
It's also not scalable. If you're learning how to code, learn the scalable way. Only use NSUserDefaults for storing simple strings or booleans related to preferences. Store arrays and other data using Core Data, it's not as hard as they say. Just start small.
Update: Also, if you add Apple Watch support, there's another potential consideration. Your app's NSUserDefaults is now automatically sent to the Watch Extension.
Using Core Data
So I ignored the warnings about Core Data being a more difficult solution and started reading. Within three hours I had it working. I had my table array being saved in Core Data and reloading the data upon opening the app back up! The tutorial code was easy enough to adapt and I was able to have it store both title and detail arrays with only a little extra experimenting.
So for anyone reading this post who's struggling with NSUserDefault type issues or whose need is more than storing strings, consider spending an hour or two playing with core data.
Here's the tutorial I read:
http://www.raywenderlich.com/85578/first-core-data-app-using-swift
If you didn't check "Core Data"
If you didn't check "Core Data"when you created your app, you can add it after and it only takes five minutes:
http://craig24.com/2014/12/how-to-add-core-data-to-an-existing-swift-project-in-xcode/
http://blog.zeityer.com/post/119012600864/adding-core-data-to-an-existing-swift-project
How to Delete from Core Data Lists
Delete Data from Coredata Swift
Okey so thanks to #bploat and the link to http://www.codingexplorer.com/nsuserdefaults-a-swift-introduction/
I've found that the answer is quite simple for some basic string storage.
let defaults = NSUserDefaults.standardUserDefaults()
// Store
defaults.setObject("theGreatestName", forKey: "username")
// Receive
if let name = defaults.stringForKey("username")
{
print(name)
// Will output "theGreatestName"
}
I've summarized it here http://ridewing.se/blog/save-local-data-in-swift/
Using NSCoding and NSKeyedArchiver is another great option for data that's too complex for NSUserDefaults, but for which CoreData would be overkill. It also gives you the opportunity to manage the file structure more explicitly, which is great if you want to use encryption.
For Swift 4.0, this got easier:
let defaults = UserDefaults.standard
//Set
defaults.set(passwordTextField.text, forKey: "Password")
//Get
let myPassword = defaults.string(forKey: "Password")
Swift 5+
None of the answers really cover in detail the default built in local storage capabilities. It can do far more than just strings.
You have the following options straight from the apple documentation for 'getting' data from the defaults.
func object(forKey: String) -> Any?
//Returns the object associated with the specified key.
func url(forKey: String) -> URL?
//Returns the URL associated with the specified key.
func array(forKey: String) -> [Any]?
//Returns the array associated with the specified key.
func dictionary(forKey: String) -> [String : Any]?
//Returns the dictionary object associated with the specified key.
func string(forKey: String) -> String?
//Returns the string associated with the specified key.
func stringArray(forKey: String) -> [String]?
//Returns the array of strings associated with the specified key.
func data(forKey: String) -> Data?
//Returns the data object associated with the specified key.
func bool(forKey: String) -> Bool
//Returns the Boolean value associated with the specified key.
func integer(forKey: String) -> Int
//Returns the integer value associated with the specified key.
func float(forKey: String) -> Float
//Returns the float value associated with the specified key.
func double(forKey: String) -> Double
//Returns the double value associated with the specified key.
func dictionaryRepresentation() -> [String : Any]
//Returns a dictionary that contains a union of all key-value pairs in the domains in the search list.
Here are the options for 'setting'
func set(Any?, forKey: String)
//Sets the value of the specified default key.
func set(Float, forKey: String)
//Sets the value of the specified default key to the specified float value.
func set(Double, forKey: String)
//Sets the value of the specified default key to the double value.
func set(Int, forKey: String)
//Sets the value of the specified default key to the specified integer value.
func set(Bool, forKey: String)
//Sets the value of the specified default key to the specified Boolean value.
func set(URL?, forKey: String)
//Sets the value of the specified default key to the specified URL.
If are storing things like preferences and not a large data set these are perfectly fine options.
Double Example:
Setting:
let defaults = UserDefaults.standard
var someDouble:Double = 0.5
defaults.set(someDouble, forKey: "someDouble")
Getting:
let defaults = UserDefaults.standard
var someDouble:Double = 0.0
someDouble = defaults.double(forKey: "someDouble")
What is interesting about one of the getters is dictionaryRepresentation, this handy getter will take all your data types regardless what they are and put them into a nice dictionary that you can access by it's string name and give the correct corresponding data type when you ask for it back since it's of type 'any'.
You can store your own classes and objects also using the func set(Any?, forKey: String) and func object(forKey: String) -> Any? setter and getter accordingly.
Hope this clarifies more the power of the UserDefaults class for storing local data.
On the note of how much you should store and how often, Hardy_Germany gave a good answer on that on this post, here is a quote from it
As many already mentioned: I'm not aware of any SIZE limitation
(except physical memory) to store data in a .plist (e.g.
UserDefaults). So it's not a question of HOW MUCH.
The real question should be HOW OFTEN you write new / changed
values... And this is related to the battery drain this writes will
cause.
IOS has no chance to avoid a physical write to "disk" if a single
value changed, just to keep data integrity. Regarding UserDefaults
this cause the whole file rewritten to disk.
This powers up the "disk" and keep it powered up for a longer time and
prevent IOS to go to low power state.
Something else to note as mentioned by user Mohammad Reza Farahani from this post is the asynchronous and synchronous nature of userDefaults.
When you set a default value, it’s changed synchronously within your
process, and asynchronously to persistent storage and other processes.
For example if you save and quickly close the program you may notice it does not save the results, this is because it's persisting asynchronously. You might not notice this all the time so if you plan on saving before quitting the program you may want to account for this by giving it some time to finish.
Maybe someone has some nice solutions for this they can share in the comments?
Swift 3.0
Setter :Local Storage
let authtoken = "12345"
// Userdefaults helps to store session data locally
let defaults = UserDefaults.standard
defaults.set(authtoken, forKey: "authtoken")
defaults.synchronize()
Getter:Local Storage
if UserDefaults.standard.string(forKey: "authtoken") != nil {
//perform your task on success }
For Swift 3
UserDefaults.standard.setValue(token, forKey: "user_auth_token")
print("\(UserDefaults.standard.value(forKey: "user_auth_token")!)")
For someone who'd not prefer to handle UserDefaults for some reasons, there's another option - NSKeyedArchiver & NSKeyedUnarchiver. It helps save objects into a file using archiver, and load archived file to original objects.
// To archive object,
let mutableData: NSMutableData = NSMutableData()
let archiver: NSKeyedArchiver = NSKeyedArchiver(forWritingWith: mutableData)
archiver.encode(object, forKey: key)
archiver.finishEncoding()
return mutableData.write(toFile: path, atomically: true)
// To unarchive objects,
if let data = try? Data(contentsOf: URL(fileURLWithPath: path)) {
let unarchiver = NSKeyedUnarchiver(forReadingWith: data)
let object = unarchiver.decodeObject(forKey: key)
}
I've write an simple utility to save/load objects in local storage, used sample codes above. You might want to see this.
https://github.com/DragonCherry/LocalStorage
NsUserDefaults saves only small variable sizes.
If you want to save many objects you can use CoreData as a native solution, or I created a library that helps you save objects as easy as .save() function. It’s based on SQLite.
SundeedQLite
Check it out and tell me your comments
This gives a great explanation for how to do this in Swift 5: https://www.hackingwithswift.com/example-code/system/how-to-save-user-settings-using-userdefaults
Summary:
To set a value:
let defaults = UserDefaults.standard
defaults.set("value", forKey: "key")
To get a String value:
let key = defaults.object(forKey: "StringKey") as? [String] ?? [String]()
To get integer value:
let key = defaults.integer(forKey: "IntegerKey")
I found this answer and it enabled me to save data, but since Swift 4.1 there has been a much easier way to do this using appstorage.
#AppStorage("studentNames") var studentName: String = "Put name here"
Each item must be unique, but using String you can store a large variety of data in here.
I've made a video tutorial to help you do this: http://youtube.com/watch?v=nLsJD6yL9Ps

Resources