Trying to parse JSON in swift with swiftyJSON - ios

The issue is i am not able to populate an array with what I thought would be correct code to parse a JSON file with swiftyJSON.
As well as I am not sure if the process at which i send the request is correct.
JSON format: this should be simplified to what it truly represents: A dictionary with a "string" key and value of an array of dictionaries. then a string key with a value of a dictionary. then a string with a value of a string which i need.
{
"string1" : [
{ "string2" : {
"string3" : "DataINeed"
}
}
]
}
my code
func downloadSecondJSONData(completed: DownloadComplete)
{
let API_ServerKey = "My API Key"
let URL_Search = "URL Address"
let urlString = "\(URL_Search)"
let url = NSURL(string: urlString)!
Alamofire.request(.GET,url).responseJSON { (response) -> Void in
switch response.result
{
case .Success:
if let value = response.result.value
{
let json = JSON(value)
if let data = json["string1"]["string2"]["string3"].string
{
self.dataArray.append(data)
}
}
case .Failure(let error):
print(error)
}
completed()
}
}
func printData()
{
print(self.dataArray.count)
}
How I am attempting to call the methods
downloadFirstJSONData { () -> () in
self.randomMethod() //data i use with the downloadFirstJSONData is required to use in the downloadSecondJSONData
self.downloadSecondJSONData({ () -> () in
self.printData() //check if data has been stored
})
}

Looks like you're not accessing the array after the first dictionary.
I guess the safe way to access the first object would be:
if let array = json["string1"].array,
let firstDict = array.first,
let data = firstDict["string2"]["string3"].string {
self.dataArray.append(data)
}
But I suppose with SwiftyJSON you can also do a variation of what you had:
if let data = json["string1"][0]["string2"]["string3"].string {
self.dataArray.append(data)
}

Related

How to convert '__NSIArrayI' to JSON Array in Swift?

I am new to Swift 4 and working on an iOS app. I have the following JSON data retrieved from the server which will be extracted to data table.
[
{
"fund_nav_validity":"24 August to 31 August 2016\n",
"fund_nav_cost":10,
"fund_nav_sell":9.85,
"nav_id":118,
"fund_nav_buy":10,
"fund_id":1,
"nav_as_on_date":"24-Aug-16",
"fund_nav_market":9.95
},
{
"fund_nav_validity":"04 September to 07 September 2016\n",
"fund_nav_cost":10,
"fund_nav_sell":9.85,
"nav_id":117,
"fund_nav_buy":10,
"fund_id":1,
"nav_as_on_date":"01-Sep-16",
"fund_nav_market":9.95
}
]
I have done following task in the navdata function to retrieve the '__NSIArrayI' to JSON:
func getNAVData(){
Alamofire.request(url, method: .post).responseJSON{
response in
if response.result.isSuccess{
print("Success")
//let navDataJSON : JSON = JSON(response.result.value!
let navDataJSON : JSON = JSON(response.result.value!)
print(navDataJSON)
}else{
print("Failed")
}
}
}
I need to convert it to JSON Array which is done in java. How to do the similar task in Swift 4?
you are in the right way, in here you need to convert your JSON response to array the reason your json started with this type[{}] and check your JSON response contains the value or not in intially. try the below code
if response.result.isSuccess{
print("Success")
// convert your JSON to swiftyJSON array type if your JSON response as array as well as check its contains data or not
guard let resJson = JSON(responseObject.result.value!).array , !resJson.isEmpty else { return
}
// create the Swifty JSON array for global access like wise
var navDataJSON = [JSON]() //[JSON]() --> Swifty-JSON array memory allocation.
navDataJSON = resJson
}
You're getting the JSON array and if you want to convert to model Array then you can use create Model Struct and confirm to Decodable Protocol, without using SwiftyJSON using inbuilt solution to parse JSON.
import Foundation
// MARK: - Element
struct Model: Decodable {
let fundNavValidity: String
let fundNavCost: Int
let fundNavSell: Double
let navId: Int
let fundNavBuy: Int
let fundId: Int
let navAsOnDate: String
let fundNavMarket: Double
enum CodingKeys: String, CodingKey {
case fundNavValidity = "fund_nav_validity"
case fundNavCost = "fund_nav_cost"
case fundNavSell = "fund_nav_sell"
case navId = "nav_id"
case fundNavBuy = "fund_nav_buy"
case fundId = "fund_id"
case navAsOnDate = "nav_as_on_date"
case fundNavMarket = "fund_nav_market"
}
}
then in getNAVData method you can use JSONDecoder to covert to Model struct array like below
func getNAVData() {
Alamofire.request(url, method: .post).responseJSON {
response in
switch response.result {
case .success(let data):
do {
let modelArray = try JSONDecoder().decode([Model].self, from: data)
print(modelArray)
} catch {
print("Error: \(error)")
}
case .failure(let error):
print(error.localizedDescription)
}
}
}
If you want to use SwiftyJSON return responseData rather than responseJSON. This avoids a double conversion, and handle always a potential error
func getNAVData(){
Alamofire.request(url, method: .post).responseData { response in
switch response.result {
case .success(let data):
let navDataJSON = JSON(data).arrayValue
print(navDataJSON)
case .failure(let error): print(error)
}
}
}
The result navDataJSON is a JSON array.
However in Swift 4+ it's highly recommended to use the more efficient and built-in Codable protocol
struct Fund : Decodable {
let fundNavValidity, navAsOnDate : String
let fundNavCost, navId, fundNavBuy, fundId : Int
let fundNavSell, fundNavMarket : Double
}
func getNAVData(){
Alamofire.request(url, method: .post).responseData { response in
switch response.result {
case .success(let data):
do {
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
let navDataJSON = decoder.decode([Fund].self, from : data)
print(navDataJSON)
} catch { print(error) }
case .failure(let error): print(error)
}
}
}
The result is an array of Fund structs.

Alamofire Appending Data

I have a simple Alamofire request and i'm parsing it with SwiftyJSON. Here is code:
var fetchedData: [TestDB]? = []
func fetchData() {
Alamofire.request(url!, method: .get).validate().responseJSON { response in
switch response.result {
case .success(let value):
let json = JSON(value)
self.fetchedData = [TestDB]()
for dict in json["articles"].array! {
let data = TestDB()
if let image = dict["urlToImage"].string {
data.imageUrls = image
}
if let titlee = dict["title"].string {
data.titlee = titlee
}
if let desc = dict["description"].string {
data.desc = desc
}
self.fetchedData?.append(data)
// If i print fetchedData here, i can see it has right values.
// print(fetchedData)
}
case .failure(let error):
print(error)
}
}
// If i try to print fetchedData here, it's empty.
}
As i said in code, i can't append and use my datas. I think that's something with Alamofire being asynchronous. But couldn't figure out why. Any help is appreciated. Thanks!
When you use asynchronous method you have some way to proceed, for example:
use callback
use RAC/observation
Callback:
func fetchData(completion: (result: [TestDB]) -> ) {
Alamofire.request(url!, method: .get).validate().responseJSON { response in
self.fetchedData = [TestDB]()
// fill self.fetchedData then:
completion(self.fetchedData)
}
})
Where you call fetchData:
self.fetchData(completion: {
// update collectionview?
self.collectionView.reloadData()
})
RAC:
You can find all documentation here.
Plus:
Some suggestion:
self.fetchedData = TestDB is not necessary, probably is sufficient self.fetchData.removeAll()
use ObjectMappar to map any json response to object

Simple getJSON() function

SwiftyJSON is an extremely useful add-on for Swift, importable via various methods (CocoaPods, Carthage, etc.) and I use it within many of my projects as they commonly require JSON files. So I wanted a nice simple function that I could call with necessary arguments and get my raw String value from my JSON file.
Step 1. We will create one protocol with one constructor method in it and Model class
protocol JSONable {
init?(parameter: JSON)
}
class Style: JSONable {
let ID :String!
let name :String!
required init(parameter: JSON) {
ID = parameter["id"].stringValue
name = parameter["name"].stringValue
}
/* JSON response format
{
"status": true,
"message": "",
"data": [
{
"id": 1,
"name": "Style 1"
},
{
"id": 2,
"name": "Style 2"
},
{
"id": 3,
"name": "Style 3"
}
]
}
*/
}
Step 2. We will create extension of JSON which will convert JSON to model class type object
extension JSON {
func to<T>(type: T?) -> Any? {
if let baseObj = type as? JSONable.Type {
if self.type == .array {
var arrObject: [Any] = []
for obj in self.arrayValue {
let object = baseObj.init(parameter: obj)
arrObject.append(object!)
}
return arrObject
} else {
let object = baseObj.init(parameter: self)
return object!
}
}
return nil
}
}
Step 3. Use code with Alamofire or other code.
Alamofire.request(.GET, url).validate().responseJSON { response in
switch response.result {
case .success(let value):
let json = JSON(value)
var styles: [Style] = []
if let styleArr = json["data"].to(type: Style.self) {
styles = styleArr as! [Style]
}
print("styles: \(styles)")
case .failure(let error):
print(error)
}
}
I hope this will be useful.
Please refer to this link for more information on this.
https://github.com/SwiftyJSON/SwiftyJSON/issues/714
Here is the function I created, feel free to use it yourself (does of course require SwiftJSON to be correctly integrated within your Xcode Swift project)...
func getJSON(value: [String], fileName: String) -> String{
guard let path = Bundle.main.path(forResource: fileName, ofType: "json"),
let jsonData = NSData(contentsOfFile: path) else{
print("Couldn't find the file on disk or failed to read it")
return "ERR."
}
let jsonObject = JSON(data: jsonData as Data)
guard let jsonValue = jsonObject[value].string else{
print("FAILED to find JSON object")
return "ERR."
}
return jsonValue
}
An example usage of this function would be let myJsonValue = getJSON(value: ["people","person1"], fileName: "database") which would get the value person1 from the people group in the JSON file named database.json. So, if the database.json file looked something like this
{
"people" : {
"person1" : "Bob",
"person2" : "Steve",
"person3" : "Alan",
}
}
the function would return a value of "Bob"
Hope this is a help to anyone, or if you have any suggestions for it then please let me know! Constructive criticism appreciated always.

Extract JSON data from feed using Alamofire

I am using Alamofire to parse a JSON API, however, I can't figure out how to parse the response data from Alamofire.
When I try to loop through the fetched data, XCode gives me "Segmentation Fault: 11" error.
Here is my current code:
var tableData:NSArray // I have tried several variable types, NSDictionary, String etc.
--
override func viewDidLoad() {
super.viewDidLoad()
self.getJsonData()
}
func getJsonData() {
Alamofire.request(.GET, "https://hotell.difi.no/api/json/mattilsynet/smilefjes/tilsyn", parameters: [:])
.responseJSON { response in
if let JSON = response.result.value {
// print("JSON: \(response.result)")
for entry in JSON["entries"] {
print("\(entry)") // this is where everything crashes
}
}
self.doTableRefresh()
}
}
func doTableRefresh() {
dispatch_async(dispatch_get_main_queue(), {
self.tableView.reloadData()
return
})
}
What is the correct data format for this JSON result: https://hotell.difi.no/api/json/mattilsynet/smilefjes/tilsyn ? And how do I take the data and populate the tableview?
Convert the response to NSDictionary and NSArray:
func getJsonData() {
Alamofire.request(.GET, "https://hotell.difi.no/api/json/mattilsynet/smilefjes/tilsyn", parameters: [:])
.responseJSON { response in
if let JSON = response.result.value as? NSDictionary{
if let entries = JSON["entries"] as? NSArray{
for entry in entries {
if let entry = entry as? NSDictionary {
for (key, value) in entry {
print("\(key) - \(value)")
}
}
}
}
}
}
}
Without using SwiftyJSON, the idea is to know the right type for each object and successfully downcast.
response.result.value is a dictionary: [String:AnyObject], and the content of json["entries"] is an array of dictionaries: [[String:AnyObject]]. Etc.
Example:
func getJsonData() {
Alamofire.request(.GET, "https://hotell.difi.no/api/json/mattilsynet/smilefjes/tilsyn", parameters: [:])
.responseJSON { response in
if let json = response.result.value as? [String:AnyObject] {
if let entries = json["entries"] as? [[String:AnyObject]] {
for entry in entries {
print(entry) // each entry is a dictionary of type [String:AnyObject]
}
// example of accessing an entry:
if let firstEntry = entries.first, value = firstEntry["adrlinje1"] as? String {
print(value) // "Christian IV gate 3"
}
}
}
}
}
You have to specify the type of your expected value (With SwiftyJSON):
for entry in JSON["entries"] { // Here
print(entry.stringValue)
}

Return statement returns before information is received

I am having a little trouble with my swift code. The ending return statement runs before the the JSON value is stored so it keeps giving me nil. How can i do the return after the value been received?
func getArticleInfo(Id: String) -> String {
let url = val1 + val2 + val3
Alamofire.request(.GET, url).responseJSON { response in
switch response.result {
case .Success:
if let value = response.result.value {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0)) {
let json = JSON(value)
let singleAsset = json["url"].string
}
}
case .Failure(let error):
print(error)
}
}
return singleAsset
}
Thanks for the help the other problem I’m having. SEE BELOW
I am trying to get the categories to populate with all the information then call the vc.displayCatName() after its done. But it does it late and i have to refresh the page before i can see the information.
Above that is just me assigning the JSON values to the keys that populate categories BELOW. But the vc.displayCatName() is a function from another view controller but it gets run before the category values are populated. So the only way i see the values is if i refresh the page manually using the Pull to Refresh. So i want the information to be populated then vc.displayCatName() should run
self.getAsset(id!) { (result) -> Void in
print("this is result \(result)")
let categories = Categories (categoryName: catName, imageId: id, catIdNumber: catIdNumber, imageUrl: result)
vc.cats.append(categories)
}
}
}
dispatch_async(dispatch_get_main_queue()) {
vc.displayCatName()
}
}
The reason for this is because the call that you are making is asynchronous in nature. Instead consider using a completion handler.
func getArticleInfo(Id: String, success: (String) -> Void ) {
let url = "www.Test.com"
Alamofire.request(.GET, url).responseJSON { response in
switch response.result {
case .Success:
if let value = response.result.value {
let json = JSON(value)
let singleAsset = json["url"].string
success(singleAsset!)
}
case .Failure(let error):
print(error)
success("TEST")
}
}
}
To Call:
getArticleInfo("test") { (asset) -> Void in
print(asset)
}

Resources