how to use updateValue to add an object in Swift - ios

I have a User Struct that I'm casting to Json to be able to get into NSUserDefaults...
import Foundation
struct User {
var name = ""
var stores: [Store] = []
init?(json: [String: AnyObject]) {
if let name = json["name"] as? String,
storesJSON = json["stores"] as? [[String: AnyObject]]
{
self.name = name
self.stores = storesJSON.map { Store(json: $0)! }
} else {
return nil
}
}
init() { }
func toJSON() -> [String: AnyObject] {
return [
"name": name,
"stores": stores.map { $0.toJSON() }
]
}
}
and I am using a Data Manager class (Singleton) to add a new User. But I can't figure out what to pass into updateValue in my addPerson function below? Alternatively is there another way to get this object into NSUserDefaults?
import Foundation
class DataManager {
static let sharedInstance = DataManager()
var users = [String : User]()
init() {
let userDefaults = NSUserDefaults.standardUserDefaults()
if let var userFromDefaults = userDefaults.objectForKey("users") as? [String : User] {
users = userFromDefaults
}
else {
// add default values later
}
}
var userList: [String] {
var list: [String] = []
for userName in users.keys {
list.append(userName)
}
list.sort(<)
return list
}
func addPerson(newUserName: String) {
users.updateValue(User(), forKey: newUserName)
// saveData()
}

You should change your interface of the addPerson function, use addPerson(newUser: User) instead of using addPerson(newUserName: String) as #iosDev82 said:
// Because your addPerson function needs two parameters: a name and a user object
func addPerson(newUser: User) {
users.updateValue(newUser, forKey: newUser.name)
// saveData()
}
so you can:
let newName = textField.text.capitalizedString
let newUser = User(["name": newName, "stores" : []])
DataManager.sharedInstance.addPerson(newUser)

I think you already know how to create a User object. And that is what you should pass as an argument to your following function. Something like this.
var aUser = User(["name": textField.text. capitalizedString])
DataManager.sharedInstance.addPerson(aUser)
func addPerson(newUser: User) {
users[newUser.name] = newUser
// saveData()
}

Related

The array of String property in a managed object constantly nil

First, I'm extracting the content from the text fields into a dictionary:
var dict: [String: String] = [:]
for metricPair in metricStackView.arrangedSubviews {
if metricPair.subviews[0] is UITextField {
let unitTextField = metricPair.subviews[0] as! UITextField
let valueTextField = metricPair.subviews[1] as! UITextField
if let textContent = unitTextField.text, let valueTextContent = valueTextField.text {
let trimmedKey = textContent.trimmingCharacters(in: .whitespacesAndNewlines)
let trimmedValue = valueTextContent.trimmingCharacters(in: .whitespacesAndNewlines)
dict.updateValue(trimmedValue, forKey: trimmedKey)
}
}
}
And I'm saving it to Core Data:
let goal = Goal(context: self.context)
goal.date = Date()
for item in dict {
goal.metrics?.append(item.key)
}
goal.progress.insert(progress)
My managed object looks like this:
extension Goal {
#nonobjc public class func createFetchRequest() -> NSFetchRequest<Goal> {
return NSFetchRequest<Goal>(entityName: "Goal")
}
#NSManaged public var date: Date
#NSManaged public var metrics: [String]?
#NSManaged public var progress: Set<Progress>
}
I keep getting nil for the metrics property of [String] type even before the context is saved. When I log item.key in:
for item in dict {
goal.metrics?.append(item.key)
}
the content is showing up properly.
As far as I can tell from the above code, metrics array is not initialized.
Try replacing this line:
goal.metrics?.append(item.key)
With these lines:
if case nil = goal.metrics?.append(item.key) {
goal.metrics = [item.key]
}
or with simply these lines:
if goal.metrics == nil {
goal.metrics = []
}
goal.metrics?.append(item.key)

set value of user rating to Firebase Database

I try to send the voting from users to firebase and save them under the specific user.
class User: NSObject {
var id: String?
init(dictionary: [String: AnyObject]) {
self.id = dictionary["id"] as? String
}
var ref: DatabaseReference!
var numberOfGood = 0
init(id: String? = nil) {
self.id = id
ref = Database.database().reference().child("users").childByAutoId()
}
init(snapshot: DataSnapshot){
ref = snapshot.ref
if let value = snapshot.value as? [String : Any] {
numberOfGood = value["numberOfGood"] as! Int
}
}
func save() {
let postDictionary = [
"id" : self.id,
"numberOfGood" : self.numberOfGood,
] as [String : Any]
self.ref.setValue(postDictionary)
}
}
Inside the viewController where to vote I handle the voting itself like this:
class UserRatingClass {
var numberOfGood = 0
var ref = Database.database().reference().child("users").childByAutoId()
func good() {
numberOfGood += 1
ref.child("numberOfGood").setValue(numberOfGood)
}
}
var userRating: UserRatingClass! {
didSet {
let x = userRating.numberOfGood
self.good.setTitle("\(x) 👍", for: [])
}
}
#IBAction func goodReview(_ sender: UIButton) {
userRating.good()
let x = userRating.numberOfGood
self.good.setTitle("\(x) 👍", for: [])
}
I tried different ways like
var StringGood = String(user?.numberOfGood)
self.ref.child("users").child(StringGood).setValue(x)
inside the buttonActionFunction but by this I'm always getting Cannot invoke initializer for type 'String' with an argument list of type '(Int?)' as an error...
Edit: I call the User.swift class like this:
var user: User?

Can't save custom class array to UserDefaults

I'm trying to save a custom class array to UserDefaults but it doesn't work. I get nil back on if let. I looked everywhere online. I'm using Swift 4.2
extension UserDefaults {
func saveReciters(_ reciters: [Reciter]) {
do {
let encodedData = try NSKeyedArchiver.archivedData(withRootObject: reciters, requiringSecureCoding: false)
self.set(encodedData, forKey: UD_RECITERS)
} catch {
debugPrint(error)
return
}
}
func getReciters() -> [Reciter] {
if let reciters = self.object(forKey: UD_RECITERS) as? Data {
return NSKeyedUnarchiver.unarchiveObject(with: reciters) as! [Reciter]
} else {
print("EMPTY RECITERS")
return [Reciter]()
}
}
}
UserInfo={NSDebugDescription=Caught exception during archival: -[_SwiftValue encodeWithCoder:]: unrecognized selector sent to instance 0x600001babcc0
Thats my class:
class Reciter: NSCoding {
private(set) public var name: String
private(set) public var image: UIImage?
private(set) public var surahs: [Surah]
private(set) public var documentID: String
private let quranData = QuranData()
init(name: String, image: UIImage?, surahCount: Int?, documentID: String) {
self.name = name
self.image = image
self.documentID = documentID
if let surahCount = surahCount {
surahs = Array(quranData.getAllSurahs().prefix(surahCount))
} else {
surahs = quranData.getAllSurahs()
}
}
func encode(with aCoder: NSCoder) {
}
required init?(coder aDecoder: NSCoder) {
}
}
On my Surah class i get nil back. All other properties i get back succesfully
Most often I see developer's use codeable, here I am using user as an example:
YourDataModel.swift
struct User: Codable {
var userId: String = ""
var name: String = ""
var profileImageData: Data? }
UserDefaults.swift
import Foundation
extension UserDefaults {
/// The current user of the application, see `./Models/User.swift`
var currentUser: User? {
get {
guard let userData = self.object(forKey: #function) as? Data else { return nil }
return try? JSONDecoder().decode(User.self, from: userData)
}
set {
guard let newuser = newValue else { return }
if let userData = try? JSONEncoder().encode(newuser) {
self.set(userData, forKey: #function)
}
}
}
}
Transform the data into json data... #function is the function or value name i.e.
// For the case the user doesn't yet exist.
if ( UserDefaults.standard.currentUser == nil ) {
// Create a new user
user = User()
// Generate an id for the user, using a uuid.
user?.userId = UUID().uuidString
} else {
// otherwise, fetch the user from user defaults.
user = UserDefaults.standard.currentUser
}

Call func with various struct

I want to create one func which i can used with various struct.
I have several struct and I want use one func with all my struct.
I work with Firestore and want use this one func to access the Firestore.
My first struct:
struct Profile {
var name = ""
var surname = ""
var email = ""
var dictionary: [String: Any] {
return [
"name": name,
"surname": surname,
"email": email
]
}
}
extension Profile: DocumentSerializable {
init?(dictionary: [String: Any], id: String) {
let name = dictionary["name"] as? String ?? ""
let surname = dictionary["surname"] as? String ?? ""
let email = dictionary["email"] as? String ?? ""
self.init(name: name,
surname: surname,
email: email)
}
}
My second struct:
struct FavoriteList {
var favoriteList: [String]
var id: String
var dictionary: [String: Any] {
return [
"favoriteList": favoriteList,
"id": id
]
}
}
extension FavoriteList: DocumentSerializable {
init?(dictionary: [String : Any], id: String) {
let favoriteList = dictionary["favorite"] as? [String] ?? [""]
let id = id
self.init(favoriteList: favoriteList, id: id)
}
}
And my func which I used now to load data from firestore:
func observeQuery() {
guard let query = query else { return }
let time = DispatchTime.now() + 0.5
listener = query.addSnapshotListener { [unowned self] (snapshot, error) in
if let snapshot = snapshot {
DispatchQueue.main.asyncAfter(deadline: time) {
let profileModels = snapshot.documents.map { (document) -> Profile in
if let profileModel = Profile(dictionary: document.data(), id: document.documentID) {
return profileModel
} else {
fatalError("Error!")
}
}
self.profile = profileModels
self.document = snapshot.documents
self.tableView.reloadData()
}
}
}
}
So how I can make func observeQuery to use my structs Profile or FavouriteList?
You can use Generic Functions :
func observeQuery<T>(someObject: T) {
if someObject is Profile {
//do something
} else if someObject is FavouriteList {
//do something
}
}

How to save any class in NSUserDefaults [duplicate]

This question already has answers here:
Saving array using NSUserDefaults crashes app
(1 answer)
How to store custom objects in NSUserDefaults
(7 answers)
Closed 7 years ago.
I tried several different method that I can to save this class in NSUserDefaults. I don't know how to save class with override function. How can I make it?
class CountryEntity: AnyObject {
private(set) var id: UInt = 0
private(set) var name = ""
override func cityData(data: Dictionary<String, AnyObject>!) {
id = data.uint(key: "id")
name = data.string(key: "name")
}
}
I tried like that but it doesn't help me
private static var _selectedCountryEntity: AnyObject? = NSUserDefaults.standardUserDefaults().objectForKey(countryNameKey) {
didSet {
let savedData = NSKeyedArchiver.archivedDataWithRootObject(selectedCountryEntity as! NSData)
NSUserDefaults.standardUserDefaults().setObject(savedData, forKey: countryNameKey)
NSUserDefaults.standardUserDefaults().synchronize()
}
}
static var selectedCountryEntity: AnyObject? {
get {
return _selectedCountryEntity
}
set {
// if newValue != _selectedCountryTuple {
_selectedCountryEntity = newValue
// }
}
}
To store custom classes in NSUserDefaults, the data type needs to be a subclass of NSObject and should adhere to NSCoding protocol.
1) Create a custom class for your data
class CustomData: NSObject, NSCoding {
let name : String
let url : String
let desc : String
init(tuple : (String,String,String)){
self.name = tuple.0
self.url = tuple.1
self.desc = tuple.2
}
func getName() -> String {
return name
}
func getURL() -> String{
return url
}
func getDescription() -> String {
return desc
}
func getTuple() -> (String,String,String) {
return (self.name,self.url,self.desc)
}
required init(coder aDecoder: NSCoder) {
self.name = aDecoder.decodeObjectForKey("name") as! String
self.url = aDecoder.decodeObjectForKey("url") as! String
self.desc = aDecoder.decodeObjectForKey("desc") as! String
}
func encodeWithCoder(aCoder: NSCoder) {
aCoder.encodeObject(self.name, forKey: "name")
aCoder.encodeObject(self.url, forKey: "url")
aCoder.encodeObject(self.desc, forKey: "desc")
}
}
2) To save data use following function:
func saveData()
{
let data = NSKeyedArchiver.archivedDataWithRootObject(custom)
let defaults = NSUserDefaults.standardUserDefaults()
defaults.setObject(data, forKey:"customArray" )
}
3) To retrieve:
if let data = NSUserDefaults().dataForKey("customArray"),
custom = NSKeyedUnarchiver.unarchiveObjectWithData(data) as? [CustomData] {
// Do something with retrieved data
for item in custom {
print(item)
}
}
Note: Here I am saving and retrieving an array of trhe custom class objects.

Resources