FMDB: NULL Values Are Retrieved as Empty Strings - ios

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.

Related

How to check if array contain null value and give default value swift

I have an array that's populated from a JSON response from an API server. Sometimes the values for a key in this array are Null
I am trying to take the given value and drop it into the detail text of a table cell for display.
The problem is that when I try to coerce the value into an String I get a crash, which I think is because I'm trying to coerce Null into a string.
What's the right way to do this?
ex-
my response is below type and I'm trying to fetch that array in self.imageArray variable
response = ["abc.jpg","null","xyzzy.jpg"]
self.imageArray = (self.dataArray.value(forKey: "product_image") as? [String])!
but at second iteration it gets crashed coz second value is null.
In your case only (Solution according to given question 'response'
array) :-
response = ["abc.jpg","null","xyzzy.jpg"]
Use following code
if !response.contains("") && !response.contains("null"){
// No null or empty string now
// your code
}else{
print("Contain null or empty value")
}
You can user compatcMap in this case.
let response = ["abc.jpg", nil, "xyzzy.jpg"]
let result = response.compactMap { $0 }
print(result)
If you are sure the value will be "null" in string type then you can use the following way.
let response = ["abc.jpg", "null", "xyzzy.jpg"]
let result = response.filter { $0 != "null" }
print(result)
the combined result is
let result = response.filter { $0 != "null" }.compactMap { $0 }
print(result)
This will handle the issue for both null and nil.
response = ["abc.jpg","null","xyzzy.jpg", nil]
var result = response.filter { return ($0 != "null" && $0 != nil) }
print(result) // return option result.
It should be like
var image = ["abc.jpg", nil, "xyzz.jpg"]
image = image.filter { $0 != nil}
print(image)
You have to filter out the nil values.
if you remove specific string "null"
var image = ["abc.jpg", "null", "xyzz.jpg"]
image = image.filter { $0 != "null"}
print(image)
If you know for sure that a valid value will always contain an image type like .jpg, then you can check against that and it will remove nil values, "null" or any other random strings, including empty strings " ".
response = ["abc.jpg","null","xyzzy.jpg", nil, " "]
self.imageArray = response.filter { $0?.contains(".jpg") ?? false }

Swift: Get number of rows in result set

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.

If let condition true when value is missing in optional type, swift

I have parser in Objc, parser returns NSDictionary. I am using this parser in swift class. But when some value is missing on that dictionary, it shows nil value. e.g. ->
wirlessData = {
"anon" = {
};
"channel" = {
"text" = 1;
};
}
I am checking through
if let wepauthValue = wirlessData["wepauth"] {
if let value = wepauthValue["text"] {
print("\(value)") // nil
}
}
I don't how it satisfy the if let condition. Any one faced this types of problem can help me out.
Thanks,
vikash
You don't need any special code to do this, because it is what a dictionary already does. When you fetch dict[key] you know whether the dictionary contains the key, because the Optional that you get back is not nil (and it contains the value).
So, if you just want to answer the question whether the dictionary contains the key, ask:
let keyExists = dict[key] != nil
If you want the value and you know the dictionary contains the key, say:
let val = dict[key]!
But if, as usually happens, you don't know it contains the key - you want to fetch it and use it, but only if it exists - then use something like if let:
if let val = dict[key] {
// now val is not nil and the Optional has been unwrapped, so use it
}
I have tested it and found that value is still optional.Take a look at screenshot below to understand it better.
"anon" would be an empty dictionary. An empty dictionary is not nil, it is a dictionary. Just an empty one. A JSON parser will never, ever give nil values unless you ask for a key that is not in a dictionary. For example wirlessData ["nonexistingkey"] would give you nil.
If you be more type-strong about it with the if..let's then:
if let anonValue = wirlessData["anon"] {
if let value = anonValue["text"] as? String {
// This won't execute if value isn't converted from `anonvalue["text"]` to String specifically. This includes null been a false match too
print("\(value)") // nil
}else{
print("Value did't match string at all")
}
}
or even more specifically in your case:
if let anonValue = wirlessData["anon"] {
if let value = anonValue["text"] as? Int {
// This won't execute if value isn't converted from `anonvalue["text"]` to String specifically. This includes null been a false match too
print("\(value)") // nil
}else{
print("Value did't match int at all")
}
}
The value your parser is returning not nil, its empty so you need to check on count if inner data type is dictionary or array, I have past 1 sample here
Please use below code and correct your logic accordingly to get it work properly
let wirlessData:[String:AnyObject] = [
"anon" : [],
"channel" : [
"text" : 1
]
]
if wirlessData["anon"]?.count > 0 {
if let value = wirlessData["anon"]!["text"] {
print("\(value)") // nil
}
}
Try this below code using type check operator (is) -
if wirlessData["anon"] is [String:AnyObject]
{
let anon = wirlessData["anon"]!
print(anon)
if anon["random"] is String {
let stringValue = anon["random"]!
print("\(stringValue)")
}
else if anon["random"] is Int
{
let intValue = anon["random"]!
print("\(intValue)") // nil
}
else
{
print(" may be value did't match string & Int or nil ")
}
}

Code Crashes when loading an empty attribute from Cloudkit - using Swift

I am trying to get access to a record value in CloudKit, here MyPin, it has a title & subtitle attribute/field value.
However it may happen that sometimes the record value is empty(here subtitle), and it crashes at the line when I call:
var tempS: String = Annot["Subtitle"] as! String
because Annot["Subtitle"] doesn exist ...
When I do
println(Annot["Subtitle"])
it returns nil
but if I do :
if (Annot["Subtitle"] == nil) {
println("just got a nil value")
}
I never enter the if statement:
Can someone help me how to identify if the record has an empty value?
Here is my line of codes:
let container = CKContainer.defaultContainer()
let publicData = container.publicCloudDatabase
let query = CKQuery(recordType: "MyPin", predicate: NSPredicate(format: "TRUEPREDICATE", argumentArray: nil))
publicData.performQuery(query, inZoneWithID: nil) { results, error in
if error == nil { // There is no error
for Annot in results {
var tempS: String = Annot["Subtitle"] as! String
}}
when you get Annot["Subtitle"] it will give you a CKRecordValue? return which has a base class of NSObjectProtocol. So in your case the field does exist but it's not a String so casting it using as! String will crash your app. Since the field exists the CKRecordValue will not be nil. However the content of that field is nil. When you print the field, it will output the .description of that field. In your case that is nil. Could you try this code instead:
if let f = Annot["Subtitle"] {
print("f = \(f) of type \(f.dynamicType)")
}
then set a breakpoint on the print line and when it stops try the following three statements in your output window:
po Annot
po f
p f
After the po Annot you should see what's in that record. Including your subtitle field. The po f is not so interesting. It will just output a memory address. The p f however will show you the actual type. If it's a string you should see something like: (__NSCFConstantString *) $R3 = 0xafdd21e0
P.S. Maybe you should call it record instead of Annot. It's a local variable so it should start with a lower case character. And it's still a record and not an Annot.
I think you are doing the right thing, but you don't see the println as it is executed in another thread (the completion part is executed asynchronously).
Try this:
if (Annot["Subtitle"] == nil) {
dispatch_async(dispatch_get_main_queue()) {
println("just got a nil value")
}
}
and see if it works!
The way I get values from cloudkit is this way. This both take care of the nil values and all other eventualities. Just note I have implemented a delegate to get my results back to the calling object asynchronously
privateDB.performQuery(query, inZoneWithID: nil) { (result, error) -> Void in
if error == nil{
for record in result{
let rec = record as! CKRecord
if let xxxVar = rec.valueForKey("fieldName") as? String{
myArray.append( xxxVar! ) //append unwrapped xxxVar to some result or whatever
}else{
//handle nil value
}
}
dispatch_async(dispatch_get_main_queue()) {
//do something with you data
self.delegate?.myResultCallBack(myArray)
return
}
}else{
dispatch_async(dispatch_get_main_queue()) {
self.delegate?.myErrorCallBack(error)
return
}
}
}
Beware, there are some changes in Swift2

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.

Resources