count occurrences of a key in JSON data - ios

I want to count number of "items" in my json result for me to loop on it. Please see the code below which doesn't work for me:
var jsonResult:NSDictionary = NSJSONSerialization.JSONObjectWithData(data, options: .MutableContainers, error: nil) as NSDictionary
for var i = 0; i < jsonResult["items"].count; i++ {}
It gives me following compile error:
"AnyObject? doesnt have a member named count"
Can you please help me understand the reason behind it? and also,
Can you please let me know the work around it?
Thanks for your help

Because you're parsing dynamic data, the compiler can't make guarantees about what the JSON parser returns at runtime. For that reason, any value you retrieve from a JSON object is an optional you have to explicitly unwrap.
You can read about it at http://www.atimi.com/simple-json-parsing-swift-2/.
I believe there are libraries for JSON parsing in Swift now, that make this type of stuff a lot easier.

Because jsonResult["items"] this returnAnyObject? ,you have to convert it to real class it is.For example
var dic:NSMutableDictionary = NSMutableDictionary()
dic.setValue(["first","second","third"], forKey: "items")
var json:NSData = NSJSONSerialization.dataWithJSONObject(dic, options: NSJSONWritingOptions.PrettyPrinted, error: nil)!
var jsondic = NSJSONSerialization.JSONObjectWithData(json, options:NSJSONReadingOptions.MutableContainers, error: nil) as NSDictionary
var jsonItems = jsondic["items"] as NSArray
for var i = 0;i < jsonItems.count; i++ {
println(jsonItems[i] as String)
}
This is the key
var jsonItems = jsondic["items"] as NSArray
You may use "as?",so that if convert fail,you app will not crash
var jsonItems = jsondic["items"] as? NSArray
if(jsonItems != nil){
for var i = 0;i < jsonItems!.count; i++ {
println(jsonItems![i] as String)
}
}

Related

Append value into [[String: String]]()

I'm trying to add value into [[String: String]]() as follow:
let realm = try! Realm()
let nrcObj = realm.objects(ObjectNRC.self)
for nrc in nrcObj {
nrcTownshipCode["value"] = nrc.regionCode
nrcTownshipCode["display"] = nrc.regionCode
}
But when I did like that, I've encountered following error message.
Cannot subscript a value of type '[[String : String]]' with an index
of type 'String'
Please suggest me how to do it. Thanks.
What are you trying to do is to treat [[String: String]] as dictionary which is not! Actually, [[String: String]] is an array of dictionaries ([String: String]). So what would you need to do instead -for instance-:
for nrc in nrcObj {
nrcTownshipCode[0]["value"] = nrc.regionCode
nrcTownshipCode[0]["display"] = nrc.regionCode
}
means that you would iterate through nrcObj and append values for the first dictionary in your array. Note that it might not be your desired result, but it describes your issue and how you could fix it. For instance, you might want to add an additional variable to hold the iteration count and use it as an index for your array:
var i = 0
for nrc in nrcObj {
nrcTownshipCode[i]["value"] = nrc.regionCode
nrcTownshipCode[i]["display"] = nrc.regionCode
i += 1
}

Converting objective-c codes to swift 4

it's been 1.5 years i've been writing in obj-c, its a good language.. so i saw a medium article today about swift, really looking onto it, right now i am trying to convert all my obj-c codes to swift, fortunately everything is done expect for this part..
NSString *path = [[NSBundle mainBundle] pathForResource:#"list" ofType:#"plist"];
NSArray *plistData = [NSArray arrayWithContentsOfFile:path];
NSPredicate *filter = [NSPredicate predicateWithFormat:#"english ==[c] %#", self.userQuery.text]; // something like "Abbey"
NSArray *filtered = [plistData filteredArrayUsingPredicate:filter];
NSLog(#"found matches: %# : %#", filtered,[filtered valueForKey:#"kurdi"]);
if (filtered.count>0) {
NSDictionary *dic = filtered[0];
self.theMeaningLabel.text = dic[#"kurdi"];
} else {
self.theMeaningLabel.text = #"Yay!‌";
}
i am not being able to properly convert this into the new swift 4, it gives random errors
EDIT
after a few searches, i could just write two lines of code
here's my swift code
var path: String? = Bundle.main.path(forResource: "list", ofType: "plist")
var plistData = [Any](contentsOfFile: path!)
var filter = NSPredicate(format: "english ==[c] %#", searchText)
// something like "Abbey"
var filtered = plistData?.filter { filter.evaluate(with: $0) }
print("found matches: \(filtered) : \(filtered?.value(forKey: "kurdi"))")
if filtered?.count > 0 {
var dic = filtered[0]
// theMeaningLabel.text = dic["kurdi"]
}
else {
//theMeaningLabel.text = "Yay!‌"
}
but getting the
Argument labels '(contentsOfFile:)' do not match any available overloads
Final edit
var path = Bundle.main.path(forResource:"list", ofType: "plist")
var plistData = NSArray(contentsOfFile: path!)
let filter = NSPredicate(format: "english ==[c] %#", searchText)
var filtered = plistData?.filtered(using: filter)
// [error 9:10] no viable alternative at input 'NSLog(#"found matches: %# : %#"'
if filtered?.count > 0 {
var dic = filtered![0]
// theMeaningLabel.text = dic["kurdi"]
}
else {
// theMeaningLabel.text = "Yay!‌"
}
the code above is fine, but one error comes
if filtered?.count > 0 { // here
var dic = filtered![0]
// theMeaningLabel.text = dic["kurdi"]
}
else {
// theMeaningLabel.text = "Yay!‌"
}
getting
Binary operator '>' cannot be applied to operands of type 'Int?' and 'Int'
A literal translation from Objective-C to Swift is not recommended because in many cases it does not take advantage of the improved semantics of Swift.
As Apple removed all path manipulating API from String and added more URL related API to other classes use always URL if possible
let url = Bundle.main.url(forResource: "list", withExtension: "plist")!
The forced unwrapped url is safe since the file is required to be in the bundle. A crash will reveal a design error and must not happen at runtime.
Unlike the Foundation collection types the Swift collection types don't support the implicit property list conversion so the Swift way is to use Data and PropertyListSerialization
let data = try! Data(contentsOf: url)
let dataArray = try! PropertyListSerialization.propertyList(from: data, format: nil) as! [Any]
The forced try! is as safe as forced unwrapping the url for the same reasons.
[Any] is the most unspecified Swift Array. If the array is supposed to be an array of dictionaries write [[String:Any]] or even [[String: String]] if all values are String. If it's supposed to be an array of Strings write [String]. Swift encourages the developer to be as type specific as possible.
An NSPredicate can be replaced with the filter function in Swift. For example an array of strings can be filtered case insensitively with
let filtered = dataArray.filter { $0.compare(searchText, options:.caseInsensitive) == .orderedSame }
If the array contains dictionaries you need to specify the keys (same as NSPredicate)
let filtered = dataArray.filter { ($0["foo"] as! String).compare(searchText, options:.caseInsensitive) == .orderedSame }
Finally the ObjC expression
if (filtered.count > 0) { ...
can be written much more descriptive in Swift
if filtered.isEmpty == false { ...
or
if !filtered.isEmpty { ...
If you are looking for only one item you can even write
if let foundObject = dataArray.first(where: { $0.compare(searchText, options:.caseInsensitive) == .orderedSame }) { ...

Swift iOS NSDictionary setValue crash - but why?

I have this code (porting from another language, hence a bit different naming conventions, but please bear with this for now)
var FDefaultsList: NSDictionary = [String:String]();
let TmpKey: String = TmpKeyValue[0];
let TmpValue: String = TmpKeyValue[1];
if (TmpKey != "") && (TmpValue != "") {
//let TmpAnyObjectValue: AnyObject? = TmpValue;
//FDefaultsList.setValue(TmpAnyObjectValue, forKey: TmpKey);
FDefaultsList.setValue(TmpValue, forKey: TmpKey);
}
However, no matter the which setValue variation I use, the call to setValue throws an error (not meaningful as far as I can tell) and exits app (Xcode editor is taken to class AppDelegate: UIResponder, UIApplicationDelegate)
I guess I am using NSDictionary wrong? I am trying to read in a text file where each line is key=value strings
You should declare an actual NSMutableDictionary instead of casting to NSDictionary.
And you can use subscript which a bit simpler to use than setValue (which should actually be setObject):
var FDefaultsList = NSMutableDictionary()
let TmpKey: String = "a"
let TmpValue: String = "b"
if TmpKey != "" && TmpValue != "" {
FDefaultsList[TmpValue] = TmpKey
}
A more "Swifty" version could be:
var defaultsList = [String:String]()
let tmpKey = "a"
let tmpValue = "b"
if !tmpKey.isEmpty && !tmpValue.isEmpty {
defaultsList[tmpValue] = tmpKey
}

How to convert array object to JSON string . IOS 8 . Swift 1.2

I got an array of object like this
var AuditActivityDayListJson = Array<AuditActivityDayModel>()
class AuditActivityDayModel : Serializable {
var DayNumber : Int
var DayType : Int
var DayDateDisplay : String
var DayDate : String
override init() {
DayNumber = 0
DayType = 0
DayDateDisplay = ""
DayDate = ""
}
}
How can i convert it into json string like this
[{"DayType":1,"DayNumber":1,"DayDate":"2015-06-30", "DayDateDisplay":""},{"DayType":1,"DayNumber":2,"DayDate":"2015-07-01","DayDateDisplay":""}]
Thanks all for your answer . Please help.
If you want to use builtin functions like NSJSONSerialization (
https://developer.apple.com/library/ios/documentation/Foundation/Reference/NSJSONSerialization_Class/ )
you basically need to convert all your objects to arrays, dictionaries, strings and numbers
In your case this should work, converting your objects to dictionaries before converting to a JSON string:
let jsonCompatibleArray = AuditActivityDayListJson.map { model in
return [
"DayNumber":model.DayNumber,
"DayType":model.DayType,
"DayDateDisplay":model.DayDateDisplay,
"DayDate":model.DayDate
]
}
let data = NSJSONSerialization.dataWithJSONObject(jsonCompatibleArray, options: nil, error: nil)
let jsonString = NSString(data: data!, encoding: NSUTF8StringEncoding)
For more complex scenarios I recommend SwiftyJSON ( https://github.com/SwiftyJSON/SwiftyJSON ) which makes error handling and more much easier.

Can't figure out why I get a fatal error: unexpectedly found nil while unwrapping an Optional value

I keep getting this error :
fatal error: unexpectedly found nil while unwrapping an Optional value
and cannot figure out how to debug it!
Here's my code :
func readCSV() -> Array<String> {
// Creates a new array of strings
var csvArray : Array<String> = Array<String>()
if let url: NSURL = NSURL(string : "URLFROMCSV" ) {
// Creates an Input Stream that will load the datas from our URL
let data :NSData! = NSData(contentsOfURL: url)!
let stream : NSInputStream! = NSInputStream(data: data)
// Opens the receiving stream
stream.open()
// Sets a buffer with a given size size
let bufferSize = 1024
var buffer = Array <UInt8>(count: bufferSize, repeatedValue: 0)
// String variable initialization
var csvFullString : String = ""
// While the stream receives datas, parses datas, convert them into strings and then concatenate them into one big string
while (stream.hasBytesAvailable) {
let readSize = stream.read(&buffer, maxLength: bufferSize)
let csvRaw = NSString (bytes: &buffer, length: readSize, encoding: NSUTF8StringEncoding)
let csvString = csvRaw as String!
csvFullString = csvFullString + csvString
}
// Fills the array with each strings. Separation between strings is made when a Θ character is parsed
csvArray = csvFullString.componentsSeparatedByString("Θ")
// Delete each null string
for(var i = 0 ; i < csvArray.count; i++) {
if(csvArray[i] == "") {
csvArray.removeAtIndex(i)
}
}
}
return csvArray
}
After searching on the web, I'm pretty sure it has something to do with unwrapping elements but the fact is when I debug it, i don't get any nil value anywhere.
PS: Would like to upload a screen but can't because i don't have 10 reputation, so bad!
Thanks in advance!
EDIT : Line let data :NSData! = NSData(contentsOfURL: url)! got the error.
Terry
You're probably creating the error in one of these two lines (though it may show up later):
let data :NSData! = NSData(contentsOfURL: url)!
let stream : NSInputStream! = NSInputStream(data: data)
You're assigning an optional value to an implicitlyUnwrappedOptional type and then using it without checking if you have a valid value.
This is why if let exists. It's a little funny that you've started to indent as if you're using if let but aren't.
Try this instead:
if let url = NSURL(string : "http://gorillaapplications.com/etablissements.csv" ) {
// Creates an Input Stream that will load the datas from our URL
if let data = NSData(contentsOfURL: url) {
let stream = NSInputStream(data: data)
stream.open()
// rest of your code here
}
else {
println("Didn't get a data object")
}
}
else {
println("Didn't get a URL object")
}
You really need to grasp Optionals for Swift. I'd recommend reading my Optionals chapter in this iBook: https://itunes.apple.com/us/book/swift-optionals-generics-for/id943445214?mt=11&uo=4&at=11lMGu
Update:
Since you added a bit more in your comments above, you're saying you get the error on this line: let data: NSData! = NSData(contentsOfURL: url)!. This is because of the ! at the end, which tells Swift you're sure that this function will return a valid value, so just use it, without checking if it's nil first. In your case, the function is returning nil and so your app crashes. Using the sample code I've provided above, you'll see that you'll no longer get a crash, but your code will execute the "Didn't get a data object" line. You'll need to correctly handle the case where you can't load data from that URL.

Resources