Swift: Get number of rows in result set - ios

I want to get number of rows in result set. I am using FMDB for database operations. There is also one function hasAnotherRow() but it returns true everytime. Below is my code.
let selectQuery = "SELECT * FROM \(tableName) WHERE chrSync = 'Y'"
if let rs = database.executeQuery(selectQuery, withArgumentsIn: [])
{
// Here I want to check if rs has more than 0 rows
while(rs.next()){
let dir = rs.resultDictionary
let json = JSON(dir)
data.append(json)
print("Has another: \(rs.hasAnotherRow())") // It always returns true
}
let json = JSON(data)
return json
}
I am new in IOS, so please share me link if is there already answered.
Thanks.

I think here is the key (https://github.com/aws-amplify/aws-sdk-ios/tree/master/AWSCore/FMDB):
You must always invoke -[FMResultSet next] before attempting to access
the values returned in a query, even if you're only expecting one:
FMResultSet *s = [db executeQuery:#"SELECT COUNT(*) FROM myTable"];
if ([s next]) {
int totalCount = [s intForColumnIndex:0];
}
Swift
var s: FMResultSet? = db.executeQuery("SELECT COUNT(*) FROM myTable", withArgumentsIn: nil)
if s?.next() != nil {
var totalCount: Int? = s?.int(forColumnIndex: 0)
}
It can be used any SELECT, if the select returns rows then the last value for totalCount will have the number of rows in your FMResultSet.

Related

node-red-node-firebird returns a buffer instead of a string

I'm doing KPI's in node-red, and I'm using node-red-node-firebird to connect my database and get the results from it. For that I made a query to select the columns I need, one of those is:
NAME Varchar(40), with an example value: "Pizzas"
(example: Select NAME from mytable)
When I receive the query response on node-red, I store it inside the msg.payload. The problem is the result that I get it isn't the string "Pizzas" but a buffer "NAME":{"type":"Buffer","data":[80,105,122,122,97,115]}}.
How can I get the string and not the buffer?
I already tried a lot of things, among them:
On the query I have tried cast(NAME AS varchar(40)) AS NAME; [NAME] without success. Put msg.payload.data.toString('utf-8') in function node but nothing happens, the function:
var objectData = msg.objectData; //this is the query response
//------------Columns----------------------------
var fields = [];
var i = 0;
if(objectData.length > 0) {
var data = objectData[0];
for(var key in data) {
fields[i] = key;
i++;
}
//TRY nº1
objectData.foreach(function(obj){
if (Buffer.isBuffer(obj) === true) {
obj = obj.toString('utf-8');
}
})
}
//-----------------------------------------
msg.method = "POST";
msg.url = //My api request//;
msg.headers = {};
msg.headers["Content-Type"] = "application/json";
msg.headers.Authorization = //auth//;
msg.payload = {
'groupID': 'Group123',
'companyID': 1221,
'table': 'DemoTable',
'fields': fields,
'data': objectData, //problem
'delete': true,
};
//TRY nº2
msg.payload = msg.payload.data.toString('utf-8');
return msg;
I solved my problem changing the select to:
SELECT cast(name as varchar(100) character set win1252) NOME FROM mytable
Thanks for the help :)

Swift 5 - Cannot INSERT data into SQLite table after performing SELECT

I am experiencing an issue during iOS app development.
Xcode Version: 11
Swift Version: 5
There are two views for the app - First_View and Second_View.
On the First_View, there is a collection view which is to list out the data in table "custom_location" from SQLite. There is a button on First_View which button is to navigate to Second_View.
On Second_View, there is a collection view which is to list out data in table "all_location" from SQLite. User can select the item and that item will be inserted into table "custom_location".
The behavior of current issue:
If there is any record in table "custom_location", the First_View will display those data when the app is launched. Then I try to click the button to navigate the Second_View and select an item. However, that item cannot be inserted into "custom_location".
I notice if the "custom_location" in the beginning, the Second_View is able to insert data into "custom_location".
Related code for selecting and inserting from SQLite:
public static func getCustomLocationList() -> [Int] {
var customLocationList:[Int] = []
var locationId:Int = -1
var db :SQLiteConnect?
//Database connection path
let urls = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
let sqlitePath = urls[urls.count-1].absoluteString + Settings.dbName
db = SQLiteConnect(path: sqlitePath)
if let mydb = db{
let statement = mydb.fetch("custom_location_list", cond: "location_id > 0", order: "id DESC")
while sqlite3_step(statement) == SQLITE_ROW{
locationId = Int(sqlite3_column_int(statement, 1))
customLocationList.append(locationId)
}
}
return customLocationList
}
public static func insertCustomLocation(_ locationId: Int){
var db :SQLiteConnect?
//Database connection path
let urls = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
let sqlitePath = urls[urls.count-1].absoluteString + Settings.dbName
db = SQLiteConnect(path: sqlitePath)
if let mydb = db {
mydb.insert("custom_location_list", rowInfo: ["location_id":"\(locationId)"])
}
}
In my SQLiteConnect.swift, the function "fetch" and "insert" is:
func fetch(_ tableName :String, cond :String?, order :String?) -> OpaquePointer {
var statement :OpaquePointer? = nil
var sql = "select * from \(tableName)"
if let condition = cond {
sql += " where \(condition)"
}
if let orderBy = order {
sql += " order by \(orderBy)"
}
sqlite3_prepare_v2(self.db, sql.cString(using: String.Encoding.utf8), -1, &statement, nil)
return statement!
}
func insert(_ tableName :String, rowInfo :[String:String]) -> Bool {
var statement :OpaquePointer? = nil
let sql = "insert into \(tableName) " + "(\(rowInfo.keys.joined(separator: ","))) " + "values (\(rowInfo.values.joined(separator: ",")))"
if sqlite3_prepare_v2(self.db, sql.cString(using: String.Encoding.utf8), -1, &statement, nil) == SQLITE_OK {
if sqlite3_step(statement) == SQLITE_DONE {
return true
}
sqlite3_finalize(statement)
}
return false
}
This issue has been fixed.
The root cause is the DB connect is opened on other method and I set "return" in the middle of that function... Then the DB connection has not been terminated when the function is completed.

FMDB: NULL Values Are Retrieved as Empty Strings

I'm retrieving a customer record with FMDB and Swift using the (simplified) function below. When the optional value in the title column is NULLthe title member of the returned customer object is an empty string rather than nil, which is misleading. Can this be re-written such that NULL values are retrieved as nil? -- Ideally without testing for empty strings and setting nil explicitly (also wrong if the value is in fact an empty string)?
func getCustomerById(id: NSUUID) -> Customer? {
let db = FMDatabase(path: dbPath as String)
if db.open() {
let queryStatement = "SELECT * FROM Customers WHERE id = ?"
let result = db.executeQuery(queryStatement, withArgumentsInArray: [id.UUIDString])
while result.next() {
var customer = Customer();
customer.id = NSUUID(UUIDString: result.stringForColumn("customerId"))
customer.firstName = result.stringForColumn("firstName")
customer.lastName = result.stringForColumn("lastName")
customer.title = result.stringForColumn("title")
return customer
}
}
else {
println("Error: \(db.lastErrorMessage())")
}
return nil
}
The NULL values are returned as nil:
db.executeUpdate("create table foo (bar text)", withArgumentsInArray: nil)
db.executeUpdate("insert into foo (bar) values (?)", withArgumentsInArray: ["baz"])
db.executeUpdate("insert into foo (bar) values (?)", withArgumentsInArray: [NSNull()])
if let rs = db.executeQuery("select * from foo", withArgumentsInArray: nil) {
while rs.next() {
if let string = rs.stringForColumn("bar") {
println("bar = \(string)")
} else {
println("bar is null")
}
}
}
That outputs:
bar = bazbar is null
You might want to double check how the values were inserted. Specifically, were empty values added using NSNull? Or perhaps open the database in an external tool and verify that the columns are really NULL like you expected.

FMResultSet returning nil in another ViewController

I am using FMDB wrapper for my database.I can fetch data using FMResultSet,but when I am trying to return FMResultSet to another ViewController,it returns nil.I am calling my database from here
var resultSet: FMResultSet! = db.getUserById(1)
if(resultSet != nil) {
self.setUserInfo(resultSet)
}
and here is my database coding part
func getUserById(userId: Int) -> FMResultSet {
let sharedInstance = DatabaseHandler()
var database: FMDatabase? = nil
var resultSet: FMResultSet! = sharedInstance.database!.executeQuery("SELECT * FROM user_info WHERE user_id = ?", withArgumentsInArray: [userId])
if(resultSet != nil) {
while resultSet.next() {
var name: String = "USER_NAME"
var location = "USER_LOCATION"
println("Name: \(resultSet.stringForColumn(name))")
println("Location: \(resultSet.stringForColumn(location))")
}
}
sharedInstance.database!.close()
return resultSet
}
When I am printing those values,it shows the values in console,but when I am returning the resultSet,it appears to be nil
What have I done worng?
Your resultSet is only valid while the database is open. Return something other than the resultSet (i.e., a wrapper object/dictionary/whatever). Generally you iterate the resultSet and pull out what you need and use that. Alternately you hand out the resultSet to the caller and it calls .next and closes it when done.

Does Swift have a concept of a unique unordered collection of values?

I have two collections of phone numbers that I want to compare to see if any match. In other languages, I'd loop through one collection, add it to a collection var type that requires uniqueness, loop through the other and check for matches such as:
var phones = ["1","2","3"]
var phones2 = ["2","5","6"]
var uniqueCollection: Set = Set()
for var i = 0; i < phones.count; i++ {
if (uniqueCollection.containsKey(phones[i]) == false){
uniqueCollection.add(phones[i])
}
}
var anyMatch = false
for var j = 0; j < phones2.count; j++{
if uniqueCollection.containsKey(phones2[j]) {
anyMatch = true
}
}
So far I haven't found any way to do this as Swift Maps seem to be a transform, Dictionaries require a value to go with the keys, and don't have an explicit "containsKey()" type function, and it doesn't seem there's another collection like "a hash table" with a method to see if a var is in there.
http://www.weheartswift.com/higher-order-functions-map-filter-reduce-and-more/
http://nshipster.com/swift-comparison-protocols/
Assuming this doesn't exist, I'm planning to just go the long way of double loops, which will suck for performance if there's ever two large collections.
func checkMultisAnyMatch(personMultis: [[AnyObject]], parseMultis: [[AnyObject]]) -> Bool{
//Put all the phone #'s or emails for the person into an Array
//Put all the phone #'s or emails for the PF contact into an array
//Loop through the phones in the parseContact
//if any match, break the loop, and set anyPhoneMatch = true
var anyMatch = false
for var i = 0; i < personMultis.count; i++ {
//order is Id, label, value, type
//that means it's in the 3rd column, or #2 subscript
var personMulti:AnyObject? = personMultis[i][2]
if (personMulti != nil) {
for var j = 0; j < parseMultis.count; j++ {
//order is Id, label, value, type
var parseMulti:AnyObject? = parseMultis[j][2]
if parseMulti != nil {
if parseMulti! as NSString == personMulti! as NSString {
anyMatch = true
}//if 4
}//if 3
}//for 2
}//if
}//for
return anyMatch
}
Would NSSet work for you?
func intersectsSet(_ otherSet: NSSet) -> Bool
Returns a Boolean value that indicates whether at least one object in the receiving set is also present in another given set.
You can create an NSSet from an NSArray.
var set1 = NSSet(array:["number1", "number2", "number3"])
var set2 = NSSet(array:["number4", "number2", "number5"])
var set3 = NSSet(array:["number4", "number5", "number6"])
let contains1 = set1.intersectsSet(set2) // true
let contains2 = set1.intersectsSet(set3) // false

Resources