Can not use sqlite (swift) commands outside viewDidLoad() - ios

Im using the framework SQLite from stephencelis
I have the problem that the commands can be only used within viewDidLoad().
But i have to use the defined variables (e.g. var carlist) in functions outside viewDidLoad.
Where is my logical problem?
My code snipped:
override func viewDidLoad() {
super.viewDidLoad()
let db = Database("/Users/**/Desktop/NEW/car/car.sqlite")
let cars = db["cars"]
let car_id = Expression<Int64>("car_id")
let car_model = Expression<String?>("car_model")
for car_list in cars {
println("car_model: \(car_list[car_model])")
var carlist[car_list[car_model]] // array
}
}
The error, if i move the lines after "let db" to outside: 'ViewController.Type' does not have a member named "db"

Your problem is, that you declare the variable inside the viewDidLoad method and Swift doesn't see the variable. You have to declare it outside of a method like that:
class YourClass{
//The DB-declaration
let db = Database("/Users/**/Desktop/NEW/car/car.sqlite")
override func viewDidLoad() {
super.viewDidLoad()
let cars = db["cars"]
let car_id = Expression<Int64>("car_id")
let car_model = Expression<String?>("car_model")
for car_list in cars {
println("car_model: \(car_list[car_model])")
var carlist[car_list[car_model]] // array
}
}
}
As you see, I've declared the variable outside of any function. That way you can access from anywhere inside your class. There are different kind of variables. You can see it like that:

Christian's answer is halfway there. The actual problem is the line that assigns cars. The db declaration cannot be referenced in the class scope directly because it's an instance variable. If you want cars to be a helper throughout the entire class, you can most simply use a lazy variable:
class MyViewController: UIViewController {
let db = Database("/Users/**/Desktop/NEW/car/car.sqlite")
lazy var cars = self.db["cars"]
let car_id = Expression<Int64>("car_id")
let car_model = Expression<String?>("car_model")
override func viewDidLoad() {
super.viewDidLoad()
for car_list in cars {
println("car_model: \(car_list[car_model])")
}
}
}
Alternatively, you could make it a computed variable (but this is less efficient because it'll initialize a new value each time):
var cars: Query { return db["cars"] }
Or you can assign the variable on viewDidLoad:
var cars: Query!
override func viewDidLoad() {
super.viewDidLoad()
cars = db["cars"]
}

Related

For-in loop requires '[UserVehicles]?' to conform to 'Sequence'; did you mean to unwrap optional? Swift

I have a data model which I made for API returns, it is something like this:
struct VehicleData: Codable {
let _embedded: Embedded
}
struct Embedded: Codable {
let userVehicles: [UserVehicles]
}
struct UserVehicles: Codable {
let id: String
let images: [String]
let userId: String
let vehicle: Vehicle
let originalPrice: OriginalPrice
let hasBasicInsurance: Bool
}
I have used callback function to pass it to my ViewController, now I want to get check in the useVehiclers list, how many vehicles hasBasicInsurance. basically, vehicleList?._embedded.userVehicles[i] = true
this is my function code to use the vehicle data in ViewController:
var vehicleManager = VehicleManager()
var vehicleList: VehicleData?
var i: Int = 0
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
vehicleManager.retrieveUserVehicle()
vehicleManager.onDataUpdate = { [weak self] (data: VehicleData) in
self?.useData(data: data)
}
tableView.dataSource = self
tableView.delegate = self
tableView.tableFooterView = UIView() //remove empty tableView cells
tableView.register(UINib(nibName: Constants.vehicleListCellNibName, bundle: nil), forCellReuseIdentifier: Constants.vehicleListToBeInsuredIdentifier)
}
func useData(data: VehicleData) {
vehicleList = data
// code below has issues....
for i in [vehicleList?._embedded.userVehicles] {
if let vechile = vehicleList?._embedded.userVehicles[i].hasBasicInsurance {
if vehicle == true {
i = i + 1
print(">>number of of insured vehidle: \(i)")
} else {
print(">>>number of of insured vehidle: \(i)")
}
}
}
}
Do you know how to fix it?
You need to supply a default value for optional as a good practise instead of force unwrap
for i in vehicleList?._embedded.userVehicles ?? [] { }
It's not clear from your code, but it looks like vehicleList is optional. It probably should not be (see Leo Dabus's comments). It is rare that it makes sense to have an optional array. That suggests there's some difference between an empty array and a missing array. If there is, then that's fine, but in most cases you should just use a non-optional array and make it empty.
Whether you fix that or not, the solution to this particular problem is to just use a non-optional value, and you have one: data. So change the loop to:
for i in data._embedded.userVehicles { ... }
From your updated question, you note "I want to get check in the useVehiclers list, how many vehicles hasBasicInsurance." It seems you want to put that value in i. If so, that would be:
func useData(data: VehicleData) {
vehicleList = data
i = data._embedded.userVehicles
.filter(\.hasBasicInsurance)
.count
}
You can also use for_each loop for this, for eg like this:
vehicleList?._embedded.userVehicles.for_each(x in /*Do your thing here*/)

Sending a variable to be modified in another class?

How can one send a variable from a viewcontroller to be modified by another viewcontroller?
I've tried setting the variable in the performSegue, but it does not get modified.
Sample code:
class VC1: ViewController{
var myVar: MyVar
....
prepare(for segue:...) {
let nextVC = segue.destination as! VC2
nextVC.var = myVar
}
....
}
class VC2: ViewController {
var var: MyVar
....
override func viewDidLoad() {
super.viewDidLoad()
var = MyVar("newValue")
}
}
After this code is executed, the value of myVar in VC1 is not changed to the new value. I believe it only gets a shallow copy of myVar, not a deep copy.
Is what I want achievable in swift?
Classes in Swift are pass by reference, whereas structs are pass by value. Assuming that MyVar is a class, you need to modify the properties of the class, ie:
myVar.property = "xyz"
instead of defining a new instance of the class as you have done in your question.
When you set var = MyVar("newValue") it assign new instance to var.
Examine the results from the following code in a playground. It should give you more insight into what you should expect, without the complication of segues and controllers.
class Variable {
var name:String
init(_ nameString:String) {
name = nameString
}
}
class Sender {
var myVar = Variable("Start name")
func showChanges() {
let receiver = Receiver(myVar)
print(myVar.name)
receiver.modify()
print(myVar.name)
receiver.replace()
print(myVar.name)
}
}
class Receiver {
var received: Variable
init(_ variable:Variable) {
received = variable
}
func modify() {
received.name = "Changed name"
}
func replace() {
received = Variable("New variable")
}
}
let s = Sender()
s.showChanges()

How to retrieve data from Realm's Results instance?

I just start to learn Realm data persistence, I start it from a iOS test project.
The realm object is declared like this:
class AchievementRecord: Object {
dynamic var dateID:String = "1111-00-00"
dynamic var date:String = "0000-00-00"
dynamic var apple:Int = Int(0)
override static func primaryKey() -> String? {
return "dateID"
}
}
I initialise the object in View Controller's viewDidLoad() method as this:
class AchievementRecord: Object {
dynamic var dateID:String = "1111-00-00"
dynamic var date:String = "0000-00-00"
dynamic var apple:Int = Int(0)
override static func primaryKey() -> String? {
return "dateID"
}
}
then I declare another function to obtain the save data as:
let appleOn_05 = defaultRealm.objects(AchievementRecord.self).filter("dateID = '05-06-2017'")
print(appleOn_05)
In the console, Xcode says:
Because I need to retrieve the apple's number, which is 22 in the console. How can I retrieve the apple's number to demo it on the screen, how can I do it? Thanks in advance.
Results works like native Swift collections in many ways. If you are fetching a single object, you can just access it with Results.first let appleOn_05 = defaultRealm.objects(AchievementRecord.self).filter("dateID = '05-06-2017'").first
Subclasses of Object work like any other native class instance in Swift, so you can access their properties using the dot syntax.
let apple = appleOn_05.apple
Combining the two:
if let appleOn_05 = defaultRealm.objects(AchievementRecord.self).filter("dateID = '05-06-2017'").first {
let apple = appleOn_05.apple
}

Pass array to singleton (swift)

class MySingleton{
static let shareInstance = MySingleton()
private init() {}
var myDetail = [Detail]()
}
class DetailTableViewController {
var expense = [Detail]()
override func viewDidLoad() {
super.viewDidLoad()
... put stuff in expense array ....
MySingleton.shareInstance.myDetail = expense //<--- doesn't work
// error is "cannot assign value of type '[Detail]' to type [MySingleton.Detail]"
}
}
How do I copy an array to my MySingleton?
right now i just pass my array around my classes using segue
From your error, it is likely you are defining Detail twice, once locally to the singleton, once globally for the viewController.

Realm Swift EXC_BAD_ACCESS During Segue with Unpersisted Object

I've got an app that I've started adding Realm to and I think I must be doing something wrong because I keep running into EXC_BAD_ACCESS when passing unpersisted objects between view controllers.
Here's a stripped down version of my app.
class TodoTask: Object {
dynamic var name: String = ""
let steps = List<Step>()
convenience init(name: String) {
self.init()
self.name = name
}
}
class Step: Object {
dynamic var name: String = ""
convenience init(name: String) {
self.init()
self.name = name
}
}
class TodoListController: UIViewController {
let todos = List<TodoTask>()
override func viewDidLoad() {
super.viewDidLoad()
var t1 = TodoTask(name: "Todo1")
let steps = [
Step("Do this"),
Step("Do that"),
]
t1.steps.appendContentsOf(steps)
var t2 = TodoTask(name: "Todo2")
let steps = [
Step("Do these"),
Step("Do those"),
]
t2.steps.appendContentsOf(steps)
todos.appendContentsOf([t1, t2])
// Display table of todos.
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if let detailsController = segue.destinationViewController as? TodoDetailViewController,
let selectedTask = getSelectedTask() {
detailsController.task = selectedTask
}
}
}
class TodoDetailViewController: UIViewController {
var task: TodoTask? // <<< EXC_BAD_ACCESS
// Display the task's steps.
}
Unfortunately, I can't figure out what triggers the EXC_BAD_ACCESS and it happens intermittently. I didn't copy a stacktrace (d'oh!) but I remember it being in the C++ destructor for some sort of Row object. This is odd to me because there doesn't seem to be a database file in my Documents folder.
I'm pretty confident this is a Realm-based error because I experienced no weird crashes until I converted my plain Swift objects to Realm objects.
My only hunch is that I might be using List wrong since the warning This method can only be called during a write transaction. is in the comments of the appendContentsOf method. However, I was under the impression that I could use Realm objects that weren't stored in a Realm just like normal Swift objects.
Am I using Realm objects wrong? Is there anything else I can look into?
I'm on Realm 0.95.2.
Thanks!
Edit:
func getSelectedTask() -> TodoTask? {
if let index = taskTableView.indexPathForSelectedRow {
return tasks[index]
}
return nil
}
The issue is that a Realm List should not be created directly. This class is only used to manage to-many relationships on Object models. Instead, you should use a standard Swift array to store the unpersisted Realm Objects.
So switch:
let todos = List<TodoTask>()
to
let todos = [TodoTask]()

Resources