How do I set NSUserDefault settings as Instance Variables? - swift

When I try to just set a constant based on the settings like below, it results in Optional("value").
let accesstoken = NSUserDefaults.standardUserDefaults().stringForKey("accessToken")
let userId = NSUserDefaults.standardUserDefaults().stringForKey("userId")
If I do it like the below, I get an error saying variable used within its own initial value. I can't seem to win here. What am I doing wrong?
var accesstoken = String()
var userId = Int()
if let atString = NSUserDefaults.standardUserDefaults().stringForKey("accessToken") {
accesstoken = atString
}
if let userIdString = NSUserDefaults.standardUserDefaults().stringForKey("userId") {
userId = userIdString
}

You can achieve what you want with a read only computed property combined with the nil coalescing operator "??". Try like this:
var accessToken: String {
return NSUserDefaults().stringForKey("accessToken") ?? ""
}
var userId: String {
return NSUserDefaults().stringForKey("userId") ?? ""
}
or if you need an Int for your userID
var userId: Int {
return NSUserDefaults().integerForKey("userId")
}

Related

How to change only the difference Concisely in FireStore

When updating FirebaseStorage, I make processing to change only by change.
Instead of sending the value of textField as it is, we create an optional variable called newOO separately, so that the value entered in newOO is changed, and nil is entered if it is not.
Here is the process of adding to the dictionary when there is a change in that variable (if it is not nil) and not adding it to the dictionary if there is no change (nil).
var newName: String? = "name"
var newAge: Int? = 20
var newID: String? = "123456789"
var dict = [String: Any]()
if let newName = newName {
dict["newName"] = newName
}
if let newAge = newAge {
dict["newAge"] = newAge
}
if let newID = newID {
dict["newID"] = newID
}
However, the more items there are, the more descriptions there are.
Is there a way to write this process more concisely?

Invalid value when query on Int in realm

I have a realm declaration like that :
#objc dynamic var roomId = UUID().uuidString
#objc dynamic var roomName = ""
#objc dynamic var roomType = ""
#objc dynamic var floor = 1
#objc dynamic var placeId : String?
I am trying to get a query of all rooms for a specific floor in a specific place from realm DB with this function :
static func getAllRoomNamesAndTypesForQuery (placeName: String? ,room : String? , floor : Int?) -> [[String]] {
var result : [[String]] = [[]]
if placeName != nil , floor != nil {
let placeId = Places.specificPlaceQueries(placeName: placeName)[0] as! String
let allRooms = Users.realm.objects(Rooms.self).filter("placeId == '\(placeId)' AND floor == '\(floor!)'")
var roomNames = [""]
var roomTypes = [""]
for number in 0..<allRooms.count {
roomNames.append(allRooms[number].roomName)
roomTypes.append(allRooms[number].roomType)
}
let sortedRoomNames = roomNames.sorted()
let sortedRoomTypes = roomTypes.sorted()
result = [sortedRoomNames , sortedRoomTypes]
}
return result
}
but it keeps showing me the following strange error
Expected object of type int for property 'floor' on object of type
'Rooms', but received: 1
I dunno how it rejects 1 as Int ?? anyone know where is the problem here??
You shouldn't be using String interpolation when creating NSPredicates, since even though it is supported, it is really easy to mess up the predicate format. Simply use %# for substituting variable values into the predicate.
let allRooms = Users.realm.objects(Rooms.self).filter("placeId == %# AND floor == %#",placeId, floor)
Some further improvements to your code: don't use nil check, then force unwrapping, use optional binding when working with Optionals.
if let placeName = placeName , let floor = floor {
Also don't add an initial value to Arrays when creating them, instead of var roomNames = [""] and var roomTypes = [""], do
var roomNames = [String]()
var roomTypes = [String]()
Can you try
let allRooms = Users.realm.objects(Rooms.self).filter {
$0.placeId == placeId
&& $0.floor == floor
}
First :
set this
var roomNames = [""];
var roomTypes = [""];
to this
var roomNames = [String]()
var roomTypes = [String]()

Realm/Swift: compilation error: Missing argument for parameter 'forPrimaryKey' in call

I'm trying to get my head around swift & realm, so I've created some kind of a test pad programme.
My model is defined like so
class RealmRecord: Object {
// properties
dynamic var id: Int = 0;
dynamic var text: String = ""
dynamic var var1: Double = 0.0
dynamic var var2: Int = 0
dynamic var var3: Double = 0.0
dynamic var var4: Int = 0
dynamic var cdate: Date = Date()
dynamic var cusr: String = ""
dynamic var mdate: Date = Date.distantPast
dynamic var musr: String = ""
dynamic var mcnt: Int = 0
// methods
convenience init(id: Int? = 0, text: String? = "", var1: Double? = 0.0,
var2: Int? = 0, var3: Double? = 0.0, var4: Int? = 0,
cusr: String? = "") {
self.init()
self.id = id!
self.text = text!
self.var1 = var1!
self.var2 = var2!
self.var3 = var3!
self.var4 = var4!
self.cdate = Date()
self.cusr = cusr!
self.mdate = Date.distantPast
self.musr = ""
self.mcnt = 0
} // init
override static func primaryKey() -> String? {
return "id"
} // primaryKey
} // RealmRecord
Persisting the data is accomplished by
try recRealm?.write {
recRealm?.add(self.rec, update: true)
} // try
But when adding the data retrieval via
if let inrec = self.recRealm?.object(RealmRecord.self) {
return inrec
} else {
return List<RealmRecord>()
} // if/else
I'm receiving an error message while compiling the code, reading
DataRealmRecord.swift:84:39: Missing argument for parameter 'forPrimaryKey' in call
Looking at the Realm documentation reveals only retrieving all persisted data--apparently without having a primary key defined--, or, alternatively, a single object, specified by the primary key.
Sifting through The Net brings up pretty much the same.
Given the model above, how can I retrieve all persisted data?
-- Sil68
EDIT
I've also defined a class facilitating this Realm model of mine, which basically carries out the following steps:
generate some random data;
persist data via the Realm model;
read data in again;
compare generated with read data.
The code
import Foundation
import RealmSwift
class DataRealmRecord {
// properties
private(set) var recDBPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first!
private(set) var recDBSubPath = "Persistency"
private(set) var recDBFile = "data.realm"
private(set) var recRealm: Realm?
private(set) var recRealmCfg: Realm.Configuration?
private(set) var rec = List<RealmRecord>()
private(set) var startTime = 0.0
private(set) var stopTime = 0.0
private(set) var runTime = 0.0
private(set) var outLog = ""
private(set) var realmOk = false
// methods
init() {
// assemble destination folder/database name
do {
try FileManager.default.createDirectory(atPath: recDBPath + "/" +
recDBSubPath,
withIntermediateDirectories: true,
attributes: nil)
recDBFile = recDBPath + "/" + recDBSubPath + "/" + recDBFile
realmOk = true
} catch let error as NSError {
outLog += error.localizedDescription
realmOk = false
} // do/try/catch
// configure realm database
if (realmOk) {
self.recRealmCfg = Realm.Configuration(fileURL: Foundation.URL(string: self.recDBFile))
do {
self.recRealm = try Realm(configuration: self.recRealmCfg!)
realmOk = true
} catch let error as NSError {
outLog += error.localizedDescription
realmOk = false
} // do/try/catch
} // if
} // init
// generate test data
func generateData(noRecs: Int? = 1000, simDat: SimulateData?) {
for i in 1...noRecs! {
let realmRec = RealmRecord(id: i,
text: String(format: "Record #%04d", i),
var1: simDat?.datnorm[i - 1] ?? 1.1,
var2: simDat?.datpois[i - 1] ?? 2,
var3: simDat?.datunif[i - 1] ?? 3.3,
var4: simDat?.datbern[i - 1] ?? 4,
cusr: "me")
self.rec.append(realmRec)
} // for
} // generateData
// retrieve test data from persistent storage
func loadData() -> List<RealmRecord> {
if let inrec = self.recRealm?.object(RealmRecord.self) {
return inrec
} else {
return List<RealmRecord>()
} // if/else
} // loadData
// save test data to persistent storage
func saveData() {
do {
try recRealm?.write {
recRealm?.add(self.rec, update: true)
} // try
} catch let error as NSError {
outLog += error.localizedDescription
} // do/try/catch
} // saveData
// compare two data sets
func compareData(rec1: List<RealmRecord>, rec2: List<RealmRecord>) -> Bool {
var rc = false
if rec1.count == rec2.count {
rc = true
for i in 0..<rec1.count {
rc = rc && (rec1[i] == rec2[i])
} // for
} // if
return rc
} // compareData
// run a full evaluation cycle
// (1) generate test data;
// (2) save test data to persistant storage;
// (3) retrieve test data from persistant storage;
// (4) compare generated data with retrieved data.
func fullCycle(noRecs: Int? = 1000, simDat: SimulateData?, prnData: Bool? = false) {
// start execution time measurement
self.startTime = Double(CFAbsoluteTimeGetCurrent())
// execute the full cycle
self.generateData(noRecs: noRecs, simDat: simDat) // (1)
self.saveData() // (2)
let rec2 = self.loadData() // (3)
let cmpRec = compareData(rec1: self.rec, rec2: rec2) // (4)
// stop execution time measurement & calculate elapsed time
self.stopTime = Double(CFAbsoluteTimeGetCurrent())
self.runTime = self.stopTime - self.startTime
} // fullCycle
} // DataRealmRecord
Issue at hand is, this code fails to compile due to the error message mentioned above (in method loadData()).
The Swift compiler is telling you that it thinks you're trying to call Realm.object(ofType:forPrimaryKey:), which retrieves a single object based on the value of its primary key. It sounds like you really want to call Realm.objects(_:) to retrieve all objects of a given type. Note that this returns a Results<T>, not a List<T>.

Constant string with interpolation

I have a string with interpolation like this
let userID = 123
let userProfileUrl = "website.com/user/\(userID)"
I would like make website.com/user/\(userID) a constant but still remain its interpolation, so that I can interpolate it with an userID.
I wonder if anyone knows a good way to do that
You can make userProfileUrl a lazy var. In this case you would need to specify the type of the userProfileUrl (i.e. String) and would need to use self.userID instead of userID
let userID = 123
lazy var userProfileUrl: String = "website.com/user/\(self.userID)"
Or if both properties are constants, and don't depend on an instance of the class you can place them outside of the class definition and it should work:
let userID = 123
let userProfileUrl = "website.com/user/\(userID)"
class MyClass {
}
You can also make userProfileUrl a computed property
let userID = 123
var userProfileUrl: String {
return "website.com/user/\(userID)"
}
If you don't like the extra lines that the computed property adds you could format it so that it's all on one line
let userID = 123
var userProfileUrl: String { return "website.com/user/\(userID)" }
var userProfileURL: (String) -> String = {
return "website.com/user/\($0)"
}
userProfileURL(userID)
This works but I would consider using an enum. You can now create a new case per endpoint.
enum Website {
case UserProfile(Int)
var base: String { return "http://website.com" }
var path: String {
switch self {
case let .UserProfile(userID):
return "user/\(userID)"
}
}
var url: URL { return URL(string: "\(base)/\(path)")! }
}
let userProfileUrl = Website.UserProfile(123).url
This might be a place where you want to use NSString's bridging to String and it's -initWithFormat:
let userProfileUrl = String(format: "website.com/user/%d", userId)

'self' used before super.init call

I'm new to swift and I don't understand how to initialize a class.
Succeeded is initialized in the class definition as false
if (succeeded && (time>1000)){
errormessage += ";connection slow"
}
Time is initialized as
time = data[3].toInt()
Where data is
var data = split(raw_data) {$0 == ","}
And raw_data is a string.
Class Definition:
class geocodeObject: NSObject {
init definition:
init(lat: String,long:String,userstate:String) {
(no super init of any kind)
Full code with things cut way:
class geocodeObject: NSObject {
//A type to store the data from the Reverse Geocoding API
//Not a retriever
//Options
let API_KEY_TEXAS = "9e4797c018164fdcb9a95edf3b10ccfc"
let DEV_MODE = true
//Loading status
var succeeded = false
var errormessage = "Not Initalized" //Not nesscarilly a failure, could be slow connection
var loadstate: String?
//Most important info
var street: String?; var housenumber: String?; var city: String?; var zip: String?
//Metadata
var time: IntegerLiteralType?; var statuscode: String?; var queryid: String?; var zip4: String?
//Other geographical data
var entirestreet: String?; var state: String?
init(lat: String,long:String,userstate:String) {
//userstate: State provided by user
//state: State provided by Reverse Geocoder
var url: String?
var extra: String?
if DEV_MODE{
extra = "&notStore=true"
}
else{
extra = ""
}
url = "http://geoservices.tamu.edu/Services/ReverseGeocoding/WebService/v04_01/HTTP/default.aspx?lat="+lat+"&lon="+long+"&apikey="+API_KEY_TEXAS+"&version=4.01"
if (userstate == "nil"){
url = url! + extra!
println("if")
}
else{
url = url! + "&state="+state!+extra!
println("else")
}
let raw_data = retrieveurl(url!)
var data = split(raw_data) {$0 == ","}
//data[1] is API version used.
statuscode = data[0]; queryid = data[2]; time = data[3].toInt(); entirestreet = data[4]; city = data[5]
state = data[6]; zip = data[7]; zip4 = data[8]
//Do street, housenumber, errormessage, succeeded
if (state != userstate){
println("user state not equal to state")
}
var splittedstreet = split(entirestreet!){$0 == " "}
housenumber = splittedstreet[0]
street = splittedstreet[1]
println(statuscode)
//Error message handling
switch String(statuscode!){
case "200":
errormessage = "Success"
case "400":
errormessage = "Unknown API key error"
case "401":
...
//Time handling
if (succeeded && (time>1000)){
errormessage += ";connection slow"
}
}
println("/GeocodingAPIWrapper.swift/.geocodeObject.init: Not Implemented")
}
}
It had been a while but the answer I found is that you should add super.init() as the first line inside your init block
init(lat: String,long:String,userstate:String) {
super.init()
...
This way I got rid of it and fulfills what the error is asking for.
As I understand this is that your variables are initialized during NSObject.init() so you can use assigned values inside your custom init(_) block
Swift 2.2 (still beta as of writing) currently displays this error if you accidentally forget to return nil from a guard's else:
required init?(dictionary: [String: AnyObject]) {
guard let someValue = dictionary["someValue"] as? Bool else { return /*nil*/ } //Nil should not be commented here
self.someValue = someValue
super.init(dictionary: dictionary) //`self` used before super.init call
}
Hopefully this helps someone