Storing values locally using NSUserDefaults - ios

I have the following block of code
let defaults = NSUserDefaults.standardUserDefaults()
let username = self.emailText.text
let password = self.passwordText.text
defaults.setValue(username, forKey: defaultsKeys.userKey)
defaults.setValue(password, forKey: defaultsKeys.passwordKey)
var u = defaults.stringForKey(defaultsKeys.userKey)
var p = defaults.stringForKey(defaultsKeys.passwordKey)
username and password are correct when setting. However, when I pull them back u and p have the same value, and it's the password value.
I declare defaultKeys at the top of the page this way.
struct defaultsKeys {
static let userKey = ""
static let passwordKey = ""
}
I'm assuming there's a simple syntax error that I'm not seeing, because this seems like it should work.

Set different key names:
struct defaultsKeys {
static let userKey = "userKey"
static let passwordKey = "passwordKey"
}

You have the same key for both, just set a different key for each one:
struct defaultsKeys {
static let userKey = "USER_KEY"
static let passwordKey = "PWD_KEY"
}

struct defaultsKeys
{
static let userKey = "userKey"
static let passwordKey = "passwordKey"
}

Related

How can I create a UITableView for each property in a model?

I have a struct that looks something like this:
internal class RemoteProfileModel: Decodable {
let userId: String
let company: String
let email: String
let firstName: String
let lastName: String
let department: String
let jobTitle: String
let pictureUri: URL?
let headerUri: URL?
let bio: String
let updatedDate: Date
}
I need to list out these properties in a UITableView. I also need to use different cell types for some of the properties.
I'm thinking perhaps I should convert this struct to a dictionary of key/value pairs, and use the key to determine the cell type.
Is this possible? Is there another way to achieve this? I am unsure if it possible to convert a struct to a dictionary so am not sure this is the best way?
to convert a class to a dictionary,
class RemoteProfileModel: Decodable {
let userId: String
let company: String
let email: String
let firstName: String
let lastName: String
let department: String
let jobTitle: String
let pictureUri: URL?
let headerUri: URL?
let bio: String
let updatedDate: Date
init() {
userId = "666"
company = "AAPL"
email = "hehe#163.com"
firstName = "user"
lastName = "test"
department = "guess"
jobTitle = "poor iOS"
pictureUri = URL(string: "wrong")
headerUri = URL(string: "none")
bio = "China"
updatedDate = Date()
}
func listPropertiesWithValues(reflect: Mirror? = nil) -> [String: Any]{
let mirror = reflect ?? Mirror(reflecting: self)
if mirror.superclassMirror != nil {
self.listPropertiesWithValues(reflect: mirror.superclassMirror)
}
var yourDict = [String: Any]()
for (index, attr) in mirror.children.enumerated() {
if let property_name = attr.label {
//You can represent the results however you want here!!!
print("\(index): \(property_name) = \(attr.value)")
yourDict[property_name] = attr.value
}
}
return yourDict
}
}
Call like this:
let profile = RemoteProfileModel()
profile.listPropertiesWithValues()
In Swift Debugging and Reflection,
A mirror describes the parts that make up a particular instance

Is it possible to use dynamic variable for a class member variable

I have a class like
class Person {
var address:String
var number:String
var houseNo:String
var licenceNo:String
....
}
let jone = Person()
jone.number = "123456"
So in this i need to initialize the variable of person class one by one. And i have approx 30 variables in person class.
Is not there s simple way to do this ?
like I have all the keys coming from backend like "number = 123456". Is not there a way that i run a for loop and use something like.
for key in keys {
john."\(key)" = dict[key]
}
Is not there a way to shorten this lengthy procedure ?
You can try out this code
extension NSObject{
// fetch all class varible
func property() -> Mirror.Children {
return Mirror(reflecting: self).children
}
func propertyList() {
for (name, value) in property() {
guard let name = name else { continue }
print("\(name): \(type(of: value)) = '\(value)'")
}
}
}
Your class, set value like below code it's helpfull
class Person: NSObject {
var address:String = ""
var number:String = ""
var houseNo:String = ""
var licenceNo:String = ""
init(with response: [String: AnyObject]) {
for child in self.property() {
if let key = child.label, let value = response[key] {
self.setValue(value, forKey: key)
}
}
}
}
person.propertyList()
// Display all class property list
address: String = ''
number: String = ''
houseNo: String = ''
licenceNo: String = ''
Why don't use a Person method for load the backend response into a new Person object?
Somethind like that:
let jone = Person()
jone.loadDataFrom(response)
Or use a Person static method
let jone = Person.loadDataFrom(response)
static func loadDataFrom(response:Object) -> Person {
let p = Person()
...
set response data
...
return p
}

Store array globally in struct or other method

I have an array of struct elements that I would like to store globally so that I can access it in different classes without having to run the query that populates it over and over.
I have a struct:
struct collectionStruct {
var name : String
var description : String
var title : String
var image : PFFile
var id: String
}
and a variable:
var collectionArray = [collectionStruct]()
and some code to build the array:
for object in items {
let arrayName = object.object(forKey: fromName) as! String
let arrayDescription = object.object(forKey: fromDescription) as! String
let arrayTitle = object.object(forKey: fromTitle) as! String
let arrayImage = object.object(forKey: fromImage) as! PFFile
let arrayID = object.objectId as String!
collectionArray.append(collectionStruct(name: arrayName,
description: arrayDescription,
title: arrayTitle,
image: arrayImage,
id: arrayID!))
}
I was thinking of creating another struct to hold the array itself bt am a bit lost here. this is what I was thinking:
struct globalArray {
var collectionArray = [collectionStruct]()
}
but a probably way off
You should consider naming your struct CollectionStruct rather than collectionStruct - as that indicates it is a type. To access the array anywhere you could create a singleton. With a singleton, you ensure there is only one instance available by giving it a private constructor. Here is an example:
class Global {
private init() { }
static let sharedInstance = Global()
var collection = [CollectionStruct]()
}
To use it you would use the following:
Global.sharedInstance.collection
You can use singleton for global class that's able to be accessing from anywhere:
class GlobalArray {
static let shared = GlobalArray()
var collectionArray = [collectionStruct]()
}
and accessing like this to assign or read value:
GlobalArray.shared.collectionArray
You can just declare
var collectionArray = [collectionStruct]()
at the top level in any file (outside any object), and it will be available globally
e.g.
var collectionArray = [collectionStruct]()
class MyClass {
func printArray() {
print(collectionArray)
}
}

Firebase store provider photo URL (cannot convert value of type NSURL?)

I am new to Firebase and I want to store the provider photo URL however it come out the error 'Can only store objects of type NSNumber, NSString, NSDictionary, and NSArray.' I have tried different type of the method but it seems not working for example let profilePicUrl = profile.photoURL as String or let profilePicUrl = NSString(NSURL: profile.photoURL)
It is my method
func createFirebaseUser(){
let key = ref.child("user").childByAutoId().key
if let user = FIRAuth.auth()?.currentUser{
for profile in user.providerData{
let uid = profile.uid
let providerID = profile.providerID
let displayName = profile.displayName
let email = ""
let profilePicUrl = profile.photoURL
//let userDict :[String : AnyObject] = ["name": username!"profilePicUrl": profilePicUrl!]
let profile = Profile(uid: uid, displayName: displayName!,email: email, imageUrl: profilePicUrl)
let childUpdates = ["/user/\(key)": profile]
ref.updateChildValues(childUpdates, withCompletionBlock: { (error, ref) -> Void in
// now users exist in the database
print("user stored in firebase")
})
}
}
and it is the data model
import Foundation
class Profile {
private var _uid: String
private var _displayName: String
private var _email: String?
private var _gender: String!
private var _city: String!
private var _imageUrl: String!
var uid: String {
return _uid
}
var displayName: String {
get {
return _displayName
}
set {
_displayName = newValue
}
}
var email: String {
get {
return _email!
}
set {
_email = newValue
}
}
var gender: String {
get {
return _gender
}
set {
_gender = newValue
}
}
var city: String {
get {
return _city
}
set {
_city = newValue
}
}
var imageUrl: String {
get {
return _imageUrl
}
set {
_imageUrl = newValue
}
}
init(uid: String, displayName: String, email: String, imageUrl: String) {
_uid = uid
_displayName = displayName
_email = email
_imageUrl = imageUrl
}
You can only store the 4 types of NSObjects you mentioned in Firebase. But for the most part, data is just a string and storing strings is easy.
Assuming that your photoURL is an actual NSURL, you can save it as a string
let ref = myRootRef.childByAppendingPath("photo_urls")
let thisFileRef = ref.childByAutoId()
thisFileRef.setValue(photoURL.absoluteString)
and to go from a string to an NSURL
let url = NSURL(string: urlString)!
Also, it appears you a creating a new user in Firebase. You can greatly simplify your code like this
let usersRef = self.myRootRef.childByAppendingPath("users")
let thisUserRef = usersRef.childByAutoId()
var dict = [String: String]()
dict["displayName"] = "Bill"
dict["email"] = "bill#thing.com"
dict["gender"] = "male"
dict["photo_url"] = photoURL.absoluteString
thisUserRef.setValue(dict)
I would suggest making that code part of your User class so you can
let aUser = User()
aUser.initWithStuff(displayName, email, gender etc etc
aUser.createUser()
That really reduces the amount of code and keeps it clean.
If you are storing users, you should use the auth.uid as the key to each users node. This is more challenging in v3.x than it was in 2.x but hopefully Firebase will soon have a fix.
What you should really do is to store a relative path to your data into Firebase database and the prefix of the absolute URL separately (maybe in Firebase in some other node or somewhere else). This will help you to be flexible and being able to switch to a different storage without a lot of worries. Moreover, it should solve your current problem, because you will be storing raw strings in the Firebase and then in the app, you will merge prefix and the relative path together in order to produce the complete URL.
For example, let's assume that your URL to a photo looks like that:
http://storage.example.com/photos/photo1.jpg
Then, you can decompose this URL into:
prefix = http://storage.example.com/
relativeUrl = photos/photo1.jpg
And store the prefix for example in some settings node in the Firebase database and the relativeUrl in your photos' data.
Then in order to construct the complete URL you want to concatenate them together.

using integerForKey with strings swift

I am confused about using integer for key in swift. Can someone explain how this works and what value it will return? I would expect it to be able to return an integer associated with a key in a dictionary or array, but don't understand how it will return an integer for a string?
import Foundation
// adopted by delegate so it can be notified when settings change
protocol ModelDelegate {
func settingsChanged()
}
class Model {
private let regionsKey = "FlagQuizKeyRegions"
private let guessesKey = "FlagQuizKeyGuesses"
// reference to QuizViewController to notify it when settings change
private var delegate: ModelDelegate! = nil
var numberOfGuesses = 4 // number of guesses to display
private var enabledRegions = [
"Africa" : false,
"Asia" : false,
"Europe" : false,
"North_America" : true,
"Oceania" : false,
"South_America" : false
]
// variables to maintain quiz data
let numberOfQuestions = 10
private var allCountries: [String] = [] // list of all flag names
private var countriesInEnabledRegions: [String] = []
// initialize the settings from the app's NSUserDefaults
init(delegate: ModelDelegate, numberOfQuestions: Int) {
self.delegate = delegate
// get the NSUSerDefaults object for the app
let userDefaults = NSUserDefaults.standardUserDefaults()
// get number of guesses
let tempGuesses = userDefaults.integerForKey(guessesKey)
if tempGuesses != 0 {
numberOfGuesses = tempGuesses
}
It will return a Integer for a key which is a String. So let's say:
let myKey = "Countries"
let myCountries = ["USA","Brazil","Japan"]
NSUserDefaults.standardUserDefaults().setInteger(myCountries.count, forKey: myKey)
let numberOfCountries = NSUserDefaults.standardUserDefaults().integerForKey(myKey) // 3

Resources