Add "blocking" to Swift for-loop - sqlite

I am using Swift in a project, and using SQLite.swift for database handling. I am trying to retrieve the most recent entry from my database like below:
func returnLatestEmailAddressFromEmailsTable() -> String{
let dbPath = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true).first as String
let db = Database("\(dbPath)/db.sqlite3")
let emails = db["emails"]
let email = Expression<String>("email")
let time = Expression<Int>("time")
var returnEmail:String = ""
for res in emails.limit(1).order(time.desc) {
returnEmail = res[email]
println("from inside: \(returnEmail)")
}
return returnEmail
}
I am trying to test the returned string from the above function like this:
println("from outside: \(returnLatestEmailAddressFromEmailsTable())")
Note how I print the value from both inside and outside of the function. Inside, it works every single time. I am struggling with the "from outside:" part.
Sometimes the function returns the correct email, but sometimes it returns "" (presumably, the value was not set in the for loop).
How can I add "blocking" functionality so calling returnLatestEmailAddressFromEmailsTable() will always first evaluate the for loop, and only after this return the value?

Related

Android: Why is Room so slow?

I am working on a simple database procedure in Kotlin using Room, and I can't explain why the process is so slow, mostly on the Android Studio emulator.
The table I am working on is this:
#Entity(tableName = "folders_items_table", indices = arrayOf(Index(value = ["folder_name"]), Index(value = ["item_id"])))
data class FoldersItems(
#PrimaryKey(autoGenerate = true)
var uid: Long = 0L,
#ColumnInfo(name = "folder_name")
var folder_name: String = "",
#ColumnInfo(name = "item_id")
var item_id: String = ""
)
And what I am just trying to do is this: checking if a combination folder/item is already present, insert a new record. If not, ignore it. on the emulator, it takes up to 7-8 seconds to insert 100 records. On a real device, it is much faster, but still, it takes around 3-4 seconds which is not acceptable for just 100 records. It looks like the "insert" query is particularly slow.
Here is the procedure that makes what I have just described (inside a coroutine):
val vsmFoldersItems = FoldersItems()
items.forEach{
val itmCk = database.checkFolderItem(item.folder_name, it)
if (itmCk == 0L) {
val newFolderItemHere = vsmFoldersItems.copy(
folder_name = item.folder_name,
item_id = it
)
database.insertFolderItems(newFolderItemHere)
}
}
the variable "items" is an array of Strings.
Here is the DAO definitions of the above-called functions:
#Query("SELECT uid FROM folders_items_table WHERE folder_name = :folder AND item_id = :item")
fun checkFolderItem(folder: String, item: String): Long
#Insert
suspend fun insertFolderItems(item: FoldersItems)
Placing the loop inside a single transaction should significantly reduce the time taken.
The reason is that each transaction (by default each SQL statement that makes a change to the database) will result in a disk write. So that's 100 disk writes for your loop.
If you begin a transaction before the loop and then set the transaction successful when the loop is completed and then end the transaction a single disk write is required.
What I am unsure of is exactly how to do this when using a suspended function (not that familiar with Kotlin).
As such I'd suggest either dropping the suspend or having another Dao for use within loops.
Then have something like :-
val vsmFoldersItems = FoldersItems()
your_RoomDatabase.beginTransaction()
items.forEach{
val itmCk = database.checkFolderItem(item.folder_name, it)
if (itmCk == 0L) {
val newFolderItemHere = vsmFoldersItems.copy(
folder_name = item.folder_name,
item_id = it
)
database.insertFolderItems(newFolderItemHere)
}
}
your_RoomDatabase.setTransactionSuccessful() //<<<<<<< IF NOT set then ALL updates will be rolled back
your_RoomDatabase.endTransaction()
You may wish to refer to:-
https://developer.android.com/reference/androidx/room/RoomDatabase
You may wish to especially refer to runInTransaction

Cannot get Realm result for objects filtered by the latest (nsdate) value of a property of a collection property swift (the example is clearer)

I Have the following model
class Process: Object {
#objc dynamic var processID:Int = 1
let steps = List<Step>()
}
class Step: Object {
#objc private dynamic var stepCode: Int = 0
#objc dynamic var stepDateUTC: Date? = nil
var stepType: ProcessStepType {
get {
return ProcessStepType(rawValue: stepCode) ?? .created
}
set {
stepCode = newValue.rawValue
}
}
}
enum ProcessStepType: Int { // to review - real value
case created = 0
case scheduled = 1
case processing = 2
case paused = 3
case finished = 4
}
A process can start, processing , paused , resume (to be in step processing again), pause , resume again,etc. the current step is the one with the latest stepDateUTC
I am trying to get all Processes, having for last step ,a step of stepType processing "processing ", ie. where for the last stepDate, stepCode is 2 .
I came with the following predicate... which doesn't work. Any idea of the right perform to perform such query ?
my best trial is the one. Is it possible to get to this result via one realm query .
let processes = realm.objects(Process.self).filter(NSPredicate(format: "ANY steps.stepCode = 2 AND NOT (ANY steps.stepCode = 4)")
let ongoingprocesses = processes.filter(){$0.steps.sorted(byKeyPath: "stepDateUTC", ascending: false).first!.stepType == .processing}
what I hoped would work
NSPredicate(format: "steps[LAST].stepCode = \(TicketStepType.processing.rawValue)")
I understand [LAST] is not supported by realm (as per the cheatsheet). but is there anyway around I could achieve my goal through a realm query?
There are a few ways to approach this and it doesn't appear the date property is relevant because lists are stored in sequential order (as long as they are not altered), so the last element in the List was added last.
This first piece of code will filter for processes where the last element is 'processing'. I coded this long-handed so the flow is more understandable.
let results = realm.objects(Process.self).filter { p in
let lastIndex = p.steps.count - 1
let step = p.steps[lastIndex]
let type = step.stepType
if type == .processing {
return true
}
return false
}
Note that Realm objects are lazily loaded - which means thousands of objects have a low memory impact. By filtering using Swift, the objects are filtered in memory so the impact is more significant.
The second piece of code is what I would suggest as it makes filtering much simpler, but would require a slight change to the Process model.
class Process: Object {
#objc dynamic var processID:Int = 1
let stepHistory = List<Step>() //RENAMED: the history of the steps
#objc dynamic var name = ""
//ADDED: new property tracks current step
#objc dynamic var current_step = ProcessStepType.created.index
}
My thought here is that the Process model keeps a 'history' of steps that have occurred so far, and then what the current_step is.
I also modified the ProcessStepType enum to make it more filterable friendly.
enum ProcessStepType: Int { // to review - real value
case created = 0
case scheduled = 1
case processing = 2
case paused = 3
case finished = 4
//this is used when filtering
var index: Int {
switch self {
case .created:
return 0
case .scheduled:
return 1
case .processing:
return 2
case .paused:
return 3
case .finished:
return 4
}
}
}
Then to return all processes where the last step in the list is 'processing' here's the filter
let results2 = realm.objects(Process.self).filter("current_step == %#", ProcessStepType.processing.index)
The final thought is to add some code to the Process model so when a step is added to the list, the current_step var is also updated. Coding that is left to the OP.

Problems with migration from swift2 to swift3 with ranges

I have strings and determine the ranges of indexes. I will need later for instance .last .count for these ranges. How should I initialise the range for string to be able to get functionality .last .count for these ranges (that is obvious in swift2 but not in swift3) ?
For example, I am often using the .count for range of string in my code in swift2, like this
var str = "Hello, playground"
let myRange = str.rangeOfString("Hello")
let myCountOfRange = myRange.count
Now it is not possible to do this in swift3
var str = "Hello, playground"
let myRange = str.range(of: "Hello")
let myCountOfRange = myRange.count // type index does not conform to protocol strideable
In Swift3, to find the size of a range you can do:
var str = "Hello, playground"
let myRange = str.range(of: "Hello")
let myCountOfRange = str[myRange!].characters.count
I don't know if this is the best way, but it works.
Alternatively:
let myCountOfRange = str.distance(from: myRange!.lowerBound, to: myRange!.upperBound)
Both require access to the original collection (ie. string), and that apparently is a limitation of Swift 3. The new model for collections and indices is discussed here.
If you want to store the ranges in an array and call .count and .last on them, you can convert the Range<Index> to a CountableRange<Int> while you still have access to the collection:
var str = "Hello, playground"
let myRange = str.range(of: "Hello")!
let lb = str.distance(from: str.startIndex, to: myRange.lowerBound) as Int
let ub = str.distance(from: str.startIndex, to: myRange.upperBound) as Int
let newRange = lb..<ub
newRange.count // 5
newRange.last // 4

Swift get value from UnsafeMutablePointer<Void> using UnsafePointer<String>

I am trying to pass contextInfo of typeUnsafeMutablePointer<Void> to UISaveVideoAtPathToSavedPhotosAlbum and use it in the callback function. For some reason I am unable to access contextInfo as a string using UnsafePointer<String>(x).memory when I am in the callback function.
I am pretty sure it is something simple I am missing but have spent way to many hours trying to figure this out.
Below is some code that I have tried.
The following code works.
var testStr:String = "hello"
takesAMutableVoidPointer(&testStr)
func takesAMutableVoidPointer(x: UnsafeMutablePointer<Void>){
var pStr:String = UnsafePointer<String>(x).memory
println("x = \(x)")
println("pStr = \(pStr)")
}
However the following code does not work.
var testStr:String = "hello"
if UIVideoAtPathIsCompatibleWithSavedPhotosAlbum(filePath){ //the filePath is compatible
println("Compatible")
//UISaveVideoAtPathToSavedPhotosAlbum(filePath, self, nil, nil)
UISaveVideoAtPathToSavedPhotosAlbum(filePath, self, "video:didFinishSavingWithError:contextInfo:", &testStr)
}
else{
println("Not Compatible")
}
func video(video: NSString, didFinishSavingWithError error:NSError, contextInfo:UnsafeMutablePointer<Void>){
var pStr:String = UnsafePointer<String>(contextInfo).memory
println("contextInfo = \(contextInfo)")
println("pStr = \(pStr)")
}
Once I get to the following line:
var pStr:String = UnsafePointer<String>(contextInfo).memory
I keep getting the following error:
Thread 1: EXC_BAD_ACCESS(code=1, address=0x0)
Any help with this would be greatly appreciated.
Thanks.
Update
Rintaro commented that testStr needs to be top level but the following code works.
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
var testStr:String = "hello"
takesAMutableVoidPointer(&testStr)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func takesAMutableVoidPointer(x: UnsafeMutablePointer<Void>){
var answer = UnsafePointer<String>(x).memory
println("x = \(x)")
println("answer = \(answer)")
}
}
I am trying not to use global variables unless I have to. I may have to but since I am able to execute the above code, it seems as though I do not need to use a global variable.
As discussed in OP comments, testStr has already been freed.
Is there any way to force the retaining of a variable that has been created in a function? Then release it later?
It's not impossible, but I don't know this is the best way to do that.
Anyway, try this with Playground or OS X "Command Line Tool" template:
import Foundation
func foo() {
var str:NSString = "Hello World"
let ptr = UnsafePointer<Void>(Unmanaged<NSString>.passRetained(str).toOpaque())
bar(ptr)
}
func bar(v:UnsafePointer<Void>) {
let at = dispatch_time(
DISPATCH_TIME_NOW,
Int64(2.0 * Double(NSEC_PER_SEC))
)
dispatch_after(at, dispatch_get_main_queue()) {
baz(v)
}
}
func baz(v:UnsafePointer<Void>) {
println("notified")
let str = Unmanaged<NSString>.fromOpaque(COpaquePointer(v)).takeRetainedValue()
println("info: \(str)")
}
foo()
println("started")
dispatch_main()
Unmanaged<NSString>.passRetained(str) increments the retain count.
Unmanaged<NSString>.fromOpaque(...).takeRetainedValue() decrements it, and extract the object.
I think, using pure Swift String is impossible. because String is struct and is allocated in stack memory. Maybe the buffer of it is allocated in heap, but we cannot access it directly.

firefox extension SQLite saving and getting

Ok now for the juicy stuff. All attempts failed to save my string so far.
Here is the code for saving it in sqllite in firefox extension:
var file = Components.classes["#mozilla.org/file/directory_service;1"]
.getService(Components.interfaces.nsIProperties)
.get("ProfD", Components.interfaces.nsIFile);
file.append("my_db_file_name.sqlite");
var storageService = Components.classes["#mozilla.org/storage/service;1"]
.getService(Components.interfaces.mozIStorageService);
var mDBConn = storageService.openDatabase(file);
mDBConn.execute("CREATE TABLE IF NOT EXISTS log_det (id INTEGER PRIMARY KEY AUTOINCREMENT, acc STRING)");
mDBConn.execute("INSERT INTO log_det (acc) VALUES(" + window['gluistr']+ ")");
mDBConn.drop();
And the code for retrieving the value:
var file = Components.classes["#mozilla.org/file/directory_service;1"]
.getService(Components.interfaces.nsIProperties)
.get("ProfD", Components.interfaces.nsIFile);
file.append("my_db_file_name.sqlite");
var storageService = Components.classes["#mozilla.org/storage/service;1"]
.getService(Components.interfaces.mozIStorageService);
var mDBConn = storageService.openDatabase(file);
var res = mDBConn.execute("SELECT * FROM log_det");
mDBConn.drop();
Is not working. Anybody knows why? Is "execute" ok or do I need "createStatement" or "executeSimpleSQL". I am confused.
Use executeSimpleSQL.
openDatabase returns a mozIStorageConnection instance, which does not have any method named execute. You can use executeSimpleSQL any time you want to execute a SQL statement without bound parameters (which is what you're doing).
You were probably thinking of mozIStorageStatement's execute method. executeSimpleSQL is not sufficient when bound parameters are necessary. Instead, you need to create a statement, bind any parameters, and then execute it:
var statement = mDBConn.createStatement(
"SELECT * FROM log_det WHERE column_name = :parameter");
statement.bindStringParameter(0, "value");
statement.execute();
statement.reset();
Also note that mozIStorageConnection does not have any method named drop. Maybe you meant to write mDBConn.close()?
All of this is covered here:
https://developer.mozilla.org/en/storage

Resources