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 = "¬Store=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
Related
I have the following code which has 3 objects which are then part of a 4th object. I'm getting errors trying to create the init method for the aggregate object (GTFS) because I'm passing (or trying to pass) the type of the 3 component objects (Stop, Route, Trip). I'm not sure why those have to be initialized before just their types being used.
protocol GTFSObject {
static var fileName: String { get }
init(csvRow: [String: String])
}
struct Stop: GTFSObject {
static let fileName = "stops.txt"
let stopID: String
let stopCode: String
let stopName: String
let stopDesc: String
let stopLat: Double
let stopLon: Double
let locationType: Int
let parentStation: String
init(csvRow: [String: String]) {
self.stopID = csvRow["stop_id"]!
self.stopCode = csvRow["stop_code"]!
self.stopName = csvRow["stop_name"]!
self.stopDesc = csvRow["stop_desc"]!
self.stopLat = Double(csvRow["stop_lat"]!)!
self.stopLon = Double(csvRow["stop_lon"]!)!
self.locationType = Int(csvRow["location_type"]!)!
self.parentStation = csvRow["parent_station"]!
}
}
struct Trip: GTFSObject {
static let fileName = "trips.txt"
let routeID: String
let serviceID: String
let tripID: String
init(csvRow: [String: String]) {
tripID = csvRow["trip_id"] ?? ""
routeID = csvRow["route_id"] ?? ""
serviceID = csvRow["service_id"] ?? ""
}
}
struct Route: GTFSObject {
static let fileName = "trips.txt"
let routeID: String
let agencyID: String
let routeShortName: String
let routeLongName: String
let routeDesc: String
let routeType: Int
let routeURL: String
let routeColor: String
let routeTextColor: String
init(csvRow: [String: String]) {
routeID = csvRow["route_id"] ?? ""
agencyID = csvRow["agency_id"] ?? ""
routeShortName = csvRow["route_short_name"] ?? ""
routeLongName = csvRow["route_long_name"] ?? ""
routeDesc = csvRow["route_desc"] ?? ""
routeType = Int(csvRow["route_type"] ?? "0") ?? 0
routeURL = csvRow["route_url"] ?? ""
routeColor = csvRow["route_color"] ?? ""
routeTextColor = csvRow["route_text_color"] ?? ""
}
}
class GTFS {
let routes: [Route]
let stops: [Stop]
let trips: [Trip]
init(gtfsFolderUrl: URL) {
self.stops = init_struct_from_url(gtfsFolderUrl: gtfsFolderUrl, type: Stop.self)
self.trips = init_struct_from_url(gtfsFolderUrl: gtfsFolderUrl, type: Trip.self)
self.routes = init_struct_from_url(gtfsFolderUrl: gtfsFolderUrl, type: Route.self)
}
private func init_struct_from_url<GTFSType>(gtfsFolderUrl: URL, type: GTFSType.Type) -> [GTFSType] where GTFSType : GTFSObject{
var returnList: [GTFSType] = []
let rows = try! NamedCSV(url: GTFS_FOLDER_URL.appendingPathComponent(type.fileName), delimiter: CSVDelimiter.comma, loadColumns: false).rows
for row in rows {
returnList.append(type.init(csvRow: row))
}
return returnList
}
}
The error I get is
'self' used in method call 'init_struct_from_url' before all stored properties are initialized
I don't know why I have to initialize the properties of the struct just to pass the type of the struct to this other function. What am I missing?
It's not about the three types that you are passing to init_struct_from_url. It's about the call init_struct_from_url itself.
You are actually calling:
self.init_struct_from_url(...)
^^^^^
The error is saying that that use of self is not allowed, because self is not initialised. If init_struct_from_url uses a property in self, it could see an uninitialised value.
Since init_struct_from_url doesn't actually use self at all and is just a helper function, you can make it an inner function of init:
init(gtfsFolderUrl: URL) {
func structFromUrl<GTFSType>(gtfsFolderUrl: URL, type: GTFSType.Type) -> [GTFSType] where GTFSType : GTFSObject{
var returnList: [GTFSType] = []
let rows = try! NamedCSV(url: GTFS_FOLDER_URL.appendingPathComponent(type.fileName), delimiter: CSVDelimiter.comma, loadColumns: false).rows
for row in rows {
returnList.append(type.init(csvRow: row))
}
return returnList
}
self.stops = structFromUrl(gtfsFolderUrl: gtfsFolderUrl, type: Stop.self)
self.trips = structFromUrl(gtfsFolderUrl: gtfsFolderUrl, type: Trip.self)
self.routes = structFromUrl(gtfsFolderUrl: gtfsFolderUrl, type: Route.self)
}
this is my struct
struct CustomerDetail: Codable {
var customerID: String = ""
var firstname: String = ""
var lastname: String = ""
var age: Int = 0
var birthday: Int = 0
var country: String = ""
var pound: Bool = true
var feetboolean: Bool = true
var currentweight: Int = 0
var desiredweight: Int = 0
var sex: Bool = false
var feet: Int = 0
var inches: Int = 0
var cm: Float = 0.0
}
this is my write function
struct writetofirebase {
var customerdetails: CustomerDetail = CustomerDetail()
let db = Firestore.firestore()
var delegate: writefirebase?
func write(){
print(customerdetails)
do {
if let data = try? JSONEncoder().encode(customerdetails){
try db.collection("collection name").document("document name").setData(from: data)
delegate?.didSuccessfulWrite(true)
}else{
print("failed to encode")
}
} catch let error {
delegate?.didFailWithError(error)
}
}
}
this is my error
https://i.stack.imgur.com/cOpQK.png
this is my firebase structured data
https://i.stack.imgur.com/cOpQK.png
The error is saying that the data it is receiving inside the setData method is not compatible and therefore malformed for the request. You should be updating or setting the data appropriately by defining a custom dictionary or passing the object directly.
db.collection("collection name").document("document name").setData(data, merge: true)
Source: https://firebase.google.com/docs/firestore/manage-data/add-data#set_a_document
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>.
I want to unwrap these 6 optional variables, and if they are null i want to give them a blank String value. This is so I can send these variables packaged into a parameters array that's sent to an API.
I'm still a beginner at Swift, and this is the only easiest way I have understood how to implement this, but the inner coder in me is saying this looks redundant and crappy as ****.
Can someone help me condense this or make it simpler?
if let fbEmail = self.fbEmail {
}else{
self.fbEmail = ""
}
if let fbDob = self.fbDob {
}else{
self.fbDob = ""
}
if let fbGender = self.fbGender {
}else{
self.fbGender = ""
}
if let fbUserIp = self.fbUserIp {
}else{
self.fbUserIp = ""
}
if let fbFacebookId = self.fbFacebookId {
}else{
self.fbFacebookId = ""
}
if let fbFacebookAccessToken = self.fbFacebookAccessToken {
}else{
self.fbFacebookAccessToken = ""
}
You can do that in exactly 6 lines of code:
self.fbEmail = self.fbEmail ?? ""
self.fbDob = self.fbDob ?? ""
self.fbGender = self.fbGender ?? ""
self.fbUserIp = self.fbUserIp ?? ""
self.fbFacebookId = self.fbFacebookId ?? ""
self.fbFacebookAccessToken = self.fbFacebookAccessToken ?? ""
Edit: what's up with the ?? syntax: It's a shortcut "if nil assign another value":
let c = a ?? b
will assign c = a if a != nil, otherwise c = b.
You can unwrap more than one at a time. But if you do, you will have no way of knowing which one is nil. You will only know that either some are nil or none are.
Aside from that do they all need to be optional? Can't you init them with the default values you are giving them when they are nil?
Snippets:
Just avoid the issue altogether.
// just init with a default value without them being an optional
var fbEmail : String = ""
var fbDob : String = ""
Replace checking for nil with .isEmpty
var string : String = ""
string.isEmpty // returns true
string = "SomeString"
string.isEmpty // returns false
Optionals with starting values.
// init wit default values while still optional
var fbEmail : String? = ""
var fbDob : String? = ""
Unwrap more than one at a time.
if let unwrfbEmail = fbEmail, let unwrfbDob = fbDob {
// stuff available here
} else {
// handle the error
}
guard let unwrfbEmail = fbEmail, let unwrfbDob = fbDob else {
// put return/break here and handle the error
}
// stuff available here
A container for all values. They are optionals but when set to nil they will reset to a default value. You can also declare them as forced unwrapped optionals !. Or unwrap them all at once with a method found above.
Obviously copy paste and alter the didSet to all variables.
// container stuct that uses didSet to avoid nil and reset to default values
struct container {
var fbEmail : String? = "" {
didSet {
if fbEmail == nil {
fbEmail = ""
}
}
}
var fbDob : String? = ""
var fbGender : String? = ""
var fbUserIp : String? = ""
var fbFacebookId : String? = ""
var fbFacebookAccessToken : String? = ""
}
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")
}