Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 5 years ago.
Improve this question
I got a task to store multiple user details.
#IBOutlet weak var userName: UITextField!
#IBOutlet weak var email_id: UITextField!
#IBOutlet weak var password: UITextField!
I know in user defaults we can store a value for a particular key and retrieve it but i would like to know how to save multiple values. like the key should be the email_id i have mentioned above and all the three fields should be its values, also i need to maintain all the details even if there is n number of users.
You can save textField Data in dictionary as a key-value and append that dictionary to Array.So you will get the list of users detail.It will not override, every time it make empty dictionary after click on save Button and before append the data.
like that:
class ViewController: UIViewController {
var userDict = [String:Any]()
var userArray = [Any]()
override func viewDidLoad() {
super.viewDidLoad()
}
and save the data after save button click.
#IBAction func SaveBtnTapped(_ sender: Any) {
userDict = [:]
userDict.updateValue(userName.text, forKey: "username")
userDict.updateValue(email_id.text, forKey: "email")
userDict.updateValue(password.text, forKey: "password")
userArray.append(userDict)
}
The best way to do it in this situation is to use a local database, like sqlite. You can follow this tutorial for dealing with a local database. In your situation, you will need a user table to store your users.
If you prefer not to use a local database and use UserDefault instead, you can indeed store an array of objects in UserDefault. Simply use the following code
UserDefaults.standard.set([YourUserObj], forKey: "users")
let userArr = UserDefaults.standard.array(forKey: "users") as? [YourUserObj]
1.You can store multiple values using tuples.
Rule: A default object must be a property list—that is, an instance
of (or for collections, a combination of instances of): NSData,
NSString, NSNumber, NSDate, NSArray, or NSDictionary
If you want to store any other type of object, you should typically archive it to create an instance of NSData.
// you can create NSObject model to store into userdefaults
let users = [User(username: "user1", email: "user1#xxx.com", password: "******"), User(username: "user2", email: "user2#xxx.com", password: "******")]
var userDefaults = UserDefaults.standard
let encodedData: Data = NSKeyedArchiver.archivedData(withRootObject: teams)
userDefaults.set(encodedData, forKey: "users")
userDefaults.synchronize()
Get data by unarchiving like below
let decodedUserDetail = userDefaults.object(forKey: "users") as! Data
let decodedUsers= NSKeyedUnarchiver.unarchiveObject(with: decodedUserDetail) as! [Team]
print(decodedUsers)
2.You can store multiple values using Array of objects.
let userDetails =
[
"username" : "user1",
"email": "user1#xxx.com",
"password" : "******"
]
before storing this get existing details
let userDefault = NSUserDefaults.standardUserDefaults()
var storedUserDetails = userDefault.objectForKey("userDetails") as? [[String:AnyObject]]
storedUserDetails.append(userDetails )
userDefault.setObject(storedCredentials, forKey: "credentials")
userDefault.synchronize()
Create a model
Class User: NSObject, NSCoding{
var userName: String
var email_id: String
var password: String
init(userName: String, email_id: String, password: String){
self.userName = userName
self.email_id = email_id
self.password = password
}
required conveniece init(coder decoder: NSCoder){
let userName = decoder.decodeObject(forKey: "userName") as? String ?? ""
let email_id = decoder.decodeObject(forKey: "email_id") as? String ?? ""
let password = decoder.decodeObject(forKey: "password") as? String ?? ""
self.init(
userName=userName,
email_id=email_id,
password=password
)
}
func encode(with coder: NSCoder){
coder.encode(self.userName, forKet="userName")
coder.encode(self.email_id, forKet="email_id")
coder.encode(self.password, forKet="password")
}
}
After you can instanciate your model with your data
let currentUser = User(userName: userName.text!, email_id: email_id.text!, password: password.text!)
Or a list :
let listUsers = [User(userName: "user_1", email_id: "email#abc.com", password: "password")]
Now you can store the currentUser or listUsers with archivedData
let encodedData = NSKeyedArchiver.archivedData(withRootObject: currentUser)
UserDefaults.standard.set(encodedData, forKey: "currentUser")
UserDefaults.standard.synchronize()
let encodedDataForList = NSKeyedArchiver.archivedData(withRootObject: listUsers)
UserDefaults.standard.set(encodedData, forKey: "listUsers")
UserDefaults.standard.synchronize()
To get the stored user :
if let data = UserDefaults.standard.data(forKey: "currentUser"){
let _currentUser = (NSKeyedUnarchiver.unarchiveObject(with: data) as? User)!
}
For saving user's data, using local db is the most appropriate solution.But if you don't want to go into depth of Core data / Sqlite, use Realm framework.
With Realm you just have to have a user class with dynamic properties & call basic functions like
Initialize RealmDb with
let objRealm = try! Realm()
Create a user class
class UserDataModel: Object {
dynamic var name:String = ""
dynamic var ID = ""
// MARK : Primary Key
override static func primaryKey() -> String? {
return "ID"
}
}
Add user into DB with write function
let obj = UserDataModel(value: ["name": userName,
"ID": userID])
try! objRealm.write {
objRealm.add(obj)
}
Read object from Realm DB
return objRealm.objects(UserDataModel.self)
Related
I wrote an app includes a main program A and an action extension B.
Users can use action extension B to save some data into UserDefaults.
like this:
let defaults = UserDefaults(suiteName: "group.com.ZeKai")
defaults?.set(self.doubanID, forKey: "doubanID")
defaults?.set(self.doubanRating, forKey: "doubanRating")
defaults?.set(self.imdbRating, forKey: "imdbRating")
defaults?.set(self.rottenTomatoesRating, forKey: "rottenTomatoesRating")
defaults?.set(self.chineseTitle, forKey: "chineseTitle")
defaults?.set(self.originalTitle, forKey: "originalTitle")
defaults?.synchronize()
Wating to transfer this data to main program A while A is opened.
But if I use action extension twice to save data like data1 and data2, then I open the main program A, only data2 is received by A, means always new data overwrite the new data in UserDefaults.
so, I would like to know whether I can save multiple data in UserDefaults, and they will be all transferred to main program A?
Thanks in advance...
To save multiple data to UserDefaults you should use unique keys for that data. Also read about synchronization:
Because this method is automatically invoked at periodic intervals,
use this method only if you cannot wait for the automatic
synchronization (for example, if your application is about to exit) or
if you want to update the user defaults to what is on disk even though
you have not made any changes.
If the data is descriptive as in case you describe. Try to use structures to represent models.
Example of the user struct:
public struct User {
public var name: String
init(name: String) {
self.name = name
}
public init(dictionary: Dictionary<String, AnyObject>){
name = (dictionary["name"] as? String)!
}
public func encode() -> Dictionary<String, AnyObject> {
var dictionary : Dictionary = Dictionary<String, AnyObject>()
dictionary["name"] = name as AnyObject?
return dictionary
}
}
Usage of the structures and UserDefaults.
let user1 = User(name: "ZeKai").encode()
let user2 = User(name: "Oleg").encode()
let defaults = UserDefaults(suiteName: "User")
defaults?.set(user1, forKey: "User1")
defaults?.set(user2, forKey: "User2")
defaults?.synchronize()
let user1EncodeData = defaults?.dictionary(forKey: "User1")
let user = User(dictionary: user1EncodeData as! Dictionary<String, AnyObject>)
I am trying to create an AppSettings class to store user setttings in an user dictionary. My AppSettings class:
class AppSettings : NSObject
{
static var user: [String: Any]?
static let sharedSingleton : AppSettings = {
let instance = AppSettings()
return instance
}()
var userID: String?
var baseUrl: String?
override private init() {
super.init()
let userDefaults = UserDefaults.standard
AppSettings.user = userDefaults.object(forKey: "user") as! [String : Any]?
print(AppSettings.user ?? "User Defaults has no values")
self.saveSettings()
}
func saveSettings()
{
let userDefaults = UserDefaults.standard
if((AppSettings.user) != nil)
{
userDefaults.set(AppSettings.user, forKey: "user")
}
userDefaults.synchronize()
}
}
I am storing values in user this way:
AppSettings.user?["User_ID"] = String(describing: dictionary["User_ID"])
AppSettings.sharedSingleton.saveSettings()
let defaults = UserDefaults.standard.object(forKey: "user")
print(defaults ?? "User is null")
It shows that User is null. What's wrong?
Assuming your example is complete, the problem lies in this line
AppSettings.user?["User_ID"] = String(describing: dictionary["User_ID"])
The user variable is an optional, so unless you created an empty dictionary first this is what happens - if user is not nil, assign a value under "User_ID" key.
All you need to do is write AppSetting.user = [String:Any]() before trying to add values to it. Now, it may seem that you do it in init(), but it's not called unless you call sharedInstace.
On a side note - if you are making this a singleton, why do you expose the user variable as a static?
I need some help on something. I have two apps under my account.
I need to pass a custom object from App A to App B. How can I do this using app groups ?
I saw that using URL schemes is a old a way of doing it.
So what is the newest way of passing data two apps ?
IN YOUR APP A (SAVE OBJECT)
Under capabilities, Click "+" to add an App Group
In your Object Class, you need to extend the class you want to share to NSObject and NSCoding
class Person : NSObject, NSCoding {
var nom: String
var prenom: String
required init(name: String, prenom: String) {
self.nom = name
self.prenom = prenom
}
required init(coder decoder: NSCoder) {
self.nom = decoder.decodeObject(forKey: "name") as? String ?? ""
self.prenom = decoder.decodeObject(forKey: "prenom") as? String ?? ""
}
func encode(with coder: NSCoder) {
coder.encode(nom, forKey: "nom")
coder.encode(prenom, forKey: "prenom")
}
}
Save Data from your App A
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let person = Person(name: "Ugo", prenom: "Marinelli")
saveData(person: person)
}
func saveData(person : Person){
//Map Class to recognize it
NSKeyedArchiver.setClassName("Person", for: Person.self)
//Encode the object
let personEncoded = NSKeyedArchiver.archivedData(withRootObject: person)
//Push the object to the App Group
UserDefaults(suiteName: "group.tag.testAppGroup")!.set(personEncoded, forKey: "FirstLaunch")
}
IN YOUR APP B (GET OBJECT)
Add the same app group as the App A (see 1.1)
In the View controller you want to get the App A object :
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
//Class Mapping
NSKeyedUnarchiver.setClass(Person.self, forClassName: "Person")
//Get The encoded data from App Group
let personEncoded = UserDefaults(suiteName: "group.tag.testAppGroup")!.object(forKey: "FirstLaunch") as! NSData
//Decode to get our Person object
let personDecoded = NSKeyedUnarchiver.unarchiveObject(with: personEncoded as Data) as? Person
print(personDecoded?.prenom)
}
Note that you need to implement your Person class in both file, the best practice would be to create your own framework with your model in it
I have this weird issue. I'm saving custom class object in NSUserDefaults, and while retrieving the data I get nil for int variable of the object. Below is the custom class
class User {
var name: String?
var user_id: Int?
var account_id: Int?
var location: String?
}
I'm saving the object as,
let defaults = NSUserDefaults.standardUserDefaults()
var data = NSKeyedArchiver.archivedDataWithRootObject([user]) // I can see the int values for the user objects here
defaults.setObject(data, forKey: "all_users")
Retrieving the data as,
let defaults = NSUserDefaults.standardUserDefaults()
let data = defaults.dataForKey("all_users")
var users = [Users]()
if data != nil {
let userData = NSKeyedUnarchiver.unarchiveObjectWithData(data!) as! [Users]
for usr in userData {
print("\(usr.name!)") // Prints the name
print("\(usr.user_id!)") // Nil value here
users.append(usr)
}
}
I have absolutely no idea about the reason for this behavior.
Custom classes that have none property list items need to conform to NSCoding to be able to be saved in NSUserDefaults.
Here is a guide to conforming to NSCoding: http://nshipster.com/nscoding/
You will need both of these functions:
init(coder decoder: NSCoder) {
self.name = decoder.decodeObjectForKey("name") as String
self.user_id = decoder.decodeIntegerForKey("user_id")
self.account_id = decoder.decodeIntegerForKey("account_id")
self.location = decoder.decodeObjectForKey("self.location") as String
}
func encodeWithCoder(coder: NSCoder) {
coder.encodeObject(self.name, forKey: "name")
coder.encodeInt(self.user_id, forKey: "user_id")
coder.encodeInt(account_id, forKey: "account_id")
coder.encodeObject(self.location, forKey: "location")
}
Whenever I open my app, it doesn't load my array values because the != nil function isn't called. Is there anything I can do about this?
Code:
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
var toDoData = NSUserDefaults.standardUserDefaults()
if (toDoData.valueForKey("TDDATA") != nil){
todos = toDoData.valueForKey("TDDATA") as! NSArray as! [TodoModel]
}
if todos.count != 0{
toDoData.setValue(todos, forKeyPath: "TDDATA")
toDoData.synchronize()
}
}
Don't worry about the table. It populates perfectly. I just need the loading data issue fixed.
Code included in your answer helps a lot!
Thanks.
UPDATE:
Here is the TodoModel:
import Foundation
import UIKit
class TodoModel : NSObject, NSCoding {
var id: String
var image: String
var title: String
var desc: String
var scores: String
init (id: String, image: String, title: String, desc: String, scores: String) {
self.id = id
self.image = image
self.title = title
self.desc = desc
self.scores = scores
}
}
valueForKey and setValue:forKeyPath are KVC (Key Value Coding) methods (read here and here). It will not help you read/write to the user defaults database.
Looking in the NSUserDefaults documentation, there are a number of methods available for getting and setting values in the defaults database. Since you are using arrays, we will use:
arrayForKey to get.
setObject:forKey to set. (There is no array-specific setter)
EDIT: Try this in your viewDidAppear. Here we check if we have data, and if we do, we store it. If we don't have data, then check if the defaults database has some saved. If it does, use it instead. It would be advantageous to only load data from the defaults database in viewDidLoad, and then save in viewDidAppear or even better, a function which is called when a todo is added.
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
let defaults = NSUserDefaults.standardUserDefaults()
if todos.count > 0 {
// Save what we have
let data = NSKeyedArchiver.archivedDataWithRootObject(todos)
defaults.setObject(data, forKey: "TDDATA")
defaults.synchronize()
print("saved \(todos.count)")
} else if let storedTodoData = defaults.dataForKey("TDDATA"),
storedTodos = NSKeyedUnarchiver.unarchiveObjectWithData(storedTodoData) as? [TodoModel] {
// There was stored data! Use it!
todos = storedTodos
print("Used \(todos.count) stored todos")
}
}
In addition, we must implement the NSCoding protocol in your model. This should be something like this:
class TodoModel: NSObject, NSCoding {
var myInt: Int = 0
var myString: String?
var myArray: [String]?
required init?(coder aDecoder: NSCoder) {
myInt = aDecoder.decodeIntegerForKey("myInt")
myString = aDecoder.decodeObjectForKey("myString") as? String
myArray = aDecoder.decodeObjectForKey("myArray") as? [String]
}
func encodeWithCoder(aCoder: NSCoder) {
aCoder.encodeInteger(myInt, forKey: "myInt")
aCoder.encodeObject(myString, forKey: "myString")
aCoder.encodeObject(myArray, forKey: "myArray")
}
}
(Of course, replace myInt, myString, myArray, etc, with whatever properties your model might have.)