Can't store values from callback function into class variables : Swift - swift

I have a callback function which retrieves stuff from a url however the information doesn't get stored into class variables.
var counts:Int?
var imagesComments : [NSArray] = []
loader.getFeed("1", completionHandler: { response in
if let dataArr = response["content"] as? NSArray{
for downloaded_images in dataArr{
if let image = downloaded_images as? NSDictionary{
let url = image["url"] as? String
// Get images and load into images
if let comments = image["comments"] as? NSArray{
dispatch_async(dispatch_get_main_queue()) {
self.imagesComments.append(comments)
}
}
self.loader.downloadImage(NSURL(string: self.baseUrl+url!)!, completionHandler: { response in
dispatch_async(dispatch_get_main_queue()) {
self.images.append(response)
self.pileView?.reloadContent()
}
})
}
}
}
})
After this code is run, I have a place where I print imagesComments and counts and both are empty/nil
JSON File:
{
"success": true,
"message": null,
"content": [{
"url": "6\/image_2.png",
"date_added": "2015-12-02 22:43:05",
"comments": ["Awesome Pic", "WOOHOOOOO THIS IS GREAT"],
"likes": []
}, {
"url": "2\/image_5.png",
"date_added": "2015-12-01 06:43:48",
"comments": ["EhHHHH"],
"likes": []
}]
}

Related

How to send data into post request parameters for the following JSON data?

I have checkboxes required for product return on a page with more than one product, and when I press a checkbox, I want to store the id and quantity of the selected product in the items in the post parameter that I want to send.But, I cannot send the correct data. How can I send correct data?
{
"reasonId" : "001",
"cancel" : "true",
"description": "" ,
"items": [
{
"id": "874a8064-bebf-41c3-98a8-6ac39a54156a",
"quantity" : 480
},
{
"id": "d7af8722-58cb-4bd0-9927-47de44ba2e0b",
"quantity" : 2
},
{
"id": "f799d66e-cfcd-4f2b-9603-1facf2fedbea",
"quantity" : 1
},
{
"id": "5ea0c31f-952a-4fbc-b623-9086030193ad",
"quantity" : 17
}
]
}
I can print the id and quantity of the selected product.
private func createCheckBox(_ model : OrderDetailItems, tag: Int) -> UIView {
let rootView = UIView()
returnItemCount = model.definition?.count
var checkBox: ViewCheckLabel!
checkBox = ViewCheckLabel(text: "",
range: "",
action: {
// CHECKK SELECT OR NOT SELECT
checkBox.buttonCheck.checkboxAnimation {
if checkBox.buttonCheck.isSelected {
print(model.quantity)
print(model.id)
} else {
}
}
})
This is my post request
func cancelOrderApi() {
if let parentVC = parentViewController as? MyProfileViewController {
parentVC.startActivityIndicator(mainView: parentVC.view)
let parameters : Parameters = [
"reasonId" : reasonId,
"cancel" : isOrdrReturnable,
"description": "",
"items" : ""
]
NetworkLayer.request(Router.returnOrder(parameters, orderReturnId ?? "")).responseDecodable(of: OrderCancel.self) { (response) in
switch response.result {
case .failure(let error):
Logger.shared.debugPrint("Error while fetching tags: \(String(describing: error))")
return
case .success(let response):
if response.code == 200 {
parentVC.showAlert(mesg: response.message ?? "Siparişiniz İade edilmiştir", title: "Başarılı")
} else {
parentVC.showAlert(mesg: response.message ?? "", title: "Hata")
Logger.shared.debugPrint(response.message ?? "")
}
Logger.shared.debugPrint(response)
}
DispatchQueue.main.async {
parentVC.stopActivityIndicator()
parentVC.profileTableView.reloadData()
}
}
}
}
I am not entirely sure what you want to achieve, since the question is unclear, however here is a tip how to create Data object (can be later inserted into Url request) from your JSON file. Firstly manually copy your JSON file somewhere to your project and give it name yourJSONFile , then you can create Data object with create createDataFromJson() function.
func createDataFromJson() -> Data? {
if let path = Bundle.main.path(forResource: "yourJSONFile", ofType: "json") {
do{
let data = try Data(contentsOf: URL(fileURLWithPath: path), options: .mappedIfSafe)
return data
} catch {
print("catched : \(error.localizedDescription) ❌")
return nil
}
} else {
return nil
}
}

Trying to make the search bar functionality filter the names of the contacts which are coming from a json object into an array of strings

Want to convert my json object into an array of just names so I can use the search bar to search for different names and filter it.
"data2": {
"id": 1,
"contacts": [
{
"name": "Molly",
"pictureUrl": "molly"
},
{
"name": "Cathy",
"pictureUrl": "molly"
},
{
"name": "Bob",
"pictureUrl": "bob"
},
{
"name": "Nick",
"pictureUrl": "clothes"
}
],
},
"error": 0,
"message": "Success"
This is the json file with the object:
var contact = [IndividualContact]()
init?(data2: Data) {
do {
if let json2 = try JSONSerialization.jsonObject(with: data2) as? [String: Any], let body = json2["data2"] as? [String: Any] {
if let contacts = body["contacts"] as? [[String: Any]] {
self.contact = ( contacts.map { IndividualContact(json2: $0) } )
//Expected: ["Molly", "Bob", "Cathy"]
}
}

Swift: Help For Json parsing codeable/decodable

I am new to iOS, and want to parse the JSON using Decodable but cant get through this, how should I work this out?
The view controller where I am trying to parse the data
class ViewController: UIViewController {
var servers = [Server]()
let apiUrl = "https://someurl/api/dashboard"
override func viewDidLoad() {
super.viewDidLoad()
guard let url = URL(string: self.apiUrl) else { return }
getDataFrom(url)
}
fileprivate func getDataFrom(_ url: URL) {
URLSession.shared.dataTask(with: url){ (data, response, error) in
guard let data = data else { return }
do {
let apiResponse = try JSONDecoder().decode(Server.self, from: data)
print(apiResponse)
} catch let jsonError {
print(jsonError)
}
}.resume()
}
}
The Server.swift file where I am confirming to the decodable protocol
struct Server: Decodable {
let current_page: Int
let data: [ServerData]
let first_page_url: String
}
struct ServerData: Decodable {
let hostname: String
let ipaddress: String
let customer: [Customer]
let latest_value: [LatestValue]
}
struct Customer: Decodable {
let name: String
let contact_person :String
let email: String
}
struct LatestValue: Decodable {
let systemuptime: String
let memtotal: Float
let memfree: Double
let loadaverage: Float
}
No value associated with key CodingKeys I get this error,
The response from the server
{
"servers": {
"current_page": 1,
"data": [
{
"hostname": "johndoes",
"ipaddress": "10.0.2.99",
"id": 7,
"latest_value_id": 1130238,
"customers": [
{
"name": "Jane Doe",
"contact_person": "John Doe",
"id": 2,
"email": "john.#example.com",
"pivot": {
"server_id": 7,
"customer_id": 2
}
}
],
"latest_value": {
"id": 1130238,
"server_id": 7,
"systemuptime": "80days:10hours:23minutes",
"memtotal": 3.7,
"memfree": 1.6400000000000001,
"loadaverage": 2.25,
"disktotal": {
"dev-mapper-centos-root_disktotal": "38",
"dev-mapper-proquote-xfs-lvm_disktotal": "200"
},
"diskused": "{\"dev-mapper-centos-root_diskused\":\"16\",\"dev-mapper-proquote-xfs-lvm_diskused\":\"188\"}",
"custom_field": "[]",
"additional_attributes": {
"fathom": {
"name": "fathom",
"status": 1
},
"trenddb": {
"name": "trenddb",
"status": 1
},
"trendwi": {
"name": "trendwi",
"status": 1
},
"appsrv": {
"name": "appsrv",
"status": 1
}
},
"created_at": "2019-06-15 02:25:02",
"updated_at": "2019-06-15 02:25:02"
}
}
]
},
"message": "Success"
}
You seem to have few different errors in your data structure.
First of all, you are trying to decode Server while your json has servers inside a dict {"servers": ... }, So use a parent root object for it.
Your latest_value inside ServerData is defined as array, while it should be LatestValue struct not [LatestValue].
There is no first_page_url element in your json, but your Server struct has the property, make it optional, so that JSONDecoder decodes it only if it is present.
Here is your refined data models.
struct Response: Decodable {
let servers: Server
}
struct Server: Decodable {
let current_page: Int
let data: [ServerData]
let first_page_url: String?
}
struct ServerData: Decodable {
let hostname: String
let ipaddress: String
let customers: [Customer]
let latest_value: LatestValue
}
struct Customer: Decodable {
let name: String
let contact_person :String
let email: String
}
struct LatestValue: Decodable {
let systemuptime: String
let memtotal: Float
let memfree: Double
let loadaverage: Float
}
And decode Response instead of decoding Server, like so,
do {
let apiResponse = try JSONDecoder().decode(Response.self, from: data)
let server = apiResponse.server // Here is your server struct.
print(server)
} catch let jsonError {
print(jsonError)
}

How do I map and filter JSON nested data dictionary below a certain number?

I've got a static JSON file, and am decoding it back successfully. However, I'm really struggling with storing it properly as it is nested a few layers deep. Currently the console prints out
["Hamilton", "Chermside", "Coorparoo"]
However, I need it to filter and return the values in each suburb that fall under 500000. So something like this would be great.
"Hamilton"
"oneBRU": 341000,
"twoBRU": 480000
"Chermside"
"oneBRU": 320000,
"twoBRU": 255000,
"threeBRU": 435000,
"twoBRH": 400000
The static JSON file is at the bottom. Thanks very much
var suburbsJson: [Suburb] = []
struct ResponseData: Codable {
var suburbs: [Suburb]
}
struct Suburb : Codable {
var _id: Int
var name: String
var postcode: Int
var prices: SuburbPrices
}
struct SuburbPrices: Codable {
let oneBRU: Int
let twoBRU: Int
let threeBRU: Int
let twoBRH: Int
let threeBRH: Int
let fourBRH: Int
}
func loadJson(filename fileName: String) -> [Suburb]? {
if let url = Bundle.main.url(forResource: fileName, withExtension: "json") {
do {
let data = try Data(contentsOf: url)
let decoder = JSONDecoder()
let jsonData = try decoder.decode(ResponseData.self, from: data)
self.suburbsJson = jsonData.suburbs
let suburb = suburbsJson.map { $0.name }
print(suburb)
// print only suburbs below 500000
return jsonData.suburbs
} catch {
print("error:\(error)")
}
}
return nil
}
JSON FILE
{
"suburbs": [
{
"_id": 1,
"name": "Hamilton",
"postcode": 4007,
"prices":
{
"oneBRU": 341000,
"twoBRU": 480000,
"threeBRU": 880000,
"twoBRH": 555000,
"threeBRH": 945000,
"fourBRH": 1200000
}
},
{
"_id": 2,
"name": "Chermside",
"postcode": 4032,
"prices":
{
"oneBRU": 320000,
"twoBRU": 255000,
"threeBRU": 435000,
"twoBRH": 400000,
"threeBRH": 585000,
"fourBRH": 860000
}
},
{
"_id": 3,
"name": "Coorparoo",
"postcode": 4151,
"prices":
{
"oneBRU": 323000,
"twoBRU": 359750,
"threeBRU": 535000,
"twoBRH": 500000,
"threeBRH": 750000,
"fourBRH": 970000
}
}
]
}
If you just need to print the values, you could do something like this:
func loadJson(filename fileName: String) -> [Suburb]? {
if let url = Bundle.main.url(forResource: fileName, withExtension: "json") {
do {
let data = try Data(contentsOf: url)
let decoder = JSONDecoder()
let jsonData = try decoder.decode(ResponseData.self, from: data)
self.suburbsJson = jsonData.suburbs
// print only suburbs below 500000
jsonData.suburbs.forEach { suburb in
print(suburb.name)
if suburb.prices.oneBRU < 500000 {
print("One BRU: \(suburb.prices.oneBRU)")
}
if suburb.prices.twoBRU < 500000 {
print("Two BRU: \(suburb.prices.twoBRU)")
}
if suburb.prices.threeBRU < 500000 {
print("Three BRU: \(suburb.prices.threeBRU)")
}
if suburb.prices.twoBRH < 500000 {
print("Two BRH: \(suburb.prices.twoBRH)")
}
if suburb.prices.threeBRH < 500000 {
print("Three BRH: \(suburb.prices.threeBRH)")
}
if suburb.prices.fourBRH < 500000 {
print("Four BRH: \(suburb.prices.fourBRH)")
}
}
return jsonData.suburbs
} catch {
print("error:\(error)")
}
}
return nil
}

Swift 4 decoding json using Codable

Can someone tell me what I'm doing wrong? I've looked at all the questions on here like from here How to decode a nested JSON struct with Swift Decodable protocol? and I've found one that seems exactly what I need Swift 4 Codable decoding json.
{
"success": true,
"message": "got the locations!",
"data": {
"LocationList": [
{
"LocID": 1,
"LocName": "Downtown"
},
{
"LocID": 2,
"LocName": "Uptown"
},
{
"LocID": 3,
"LocName": "Midtown"
}
]
}
}
struct Location: Codable {
var data: [LocationList]
}
struct LocationList: Codable {
var LocID: Int!
var LocName: String!
}
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let url = URL(string: "/getlocationlist")
let task = URLSession.shared.dataTask(with: url!) { data, response, error in
guard error == nil else {
print(error!)
return
}
guard let data = data else {
print("Data is empty")
return
}
do {
let locList = try JSONDecoder().decode(Location.self, from: data)
print(locList)
} catch let error {
print(error)
}
}
task.resume()
}
The error I am getting is:
typeMismatch(Swift.Array, Swift.DecodingError.Context(codingPath:
[], debugDescription: "Expected to decode Array but found a
dictionary instead.", underlyingError: nil))
Check the outlined structure of your JSON text:
{
"success": true,
"message": "got the locations!",
"data": {
...
}
}
The value for "data" is a JSON object {...}, it is not an array.
And the structure of the object:
{
"LocationList": [
...
]
}
The object has a single entry "LocationList": [...] and its value is an array [...].
You may need one more struct:
struct Location: Codable {
var data: LocationData
}
struct LocationData: Codable {
var LocationList: [LocationItem]
}
struct LocationItem: Codable {
var LocID: Int!
var LocName: String!
}
For testing...
var jsonText = """
{
"success": true,
"message": "got the locations!",
"data": {
"LocationList": [
{
"LocID": 1,
"LocName": "Downtown"
},
{
"LocID": 2,
"LocName": "Uptown"
},
{
"LocID": 3,
"LocName": "Midtown"
}
]
}
}
"""
let data = jsonText.data(using: .utf8)!
do {
let locList = try JSONDecoder().decode(Location.self, from: data)
print(locList)
} catch let error {
print(error)
}
After searching lots of thing internet, I certainly figured out this is the sweetest way to print well formatted json from any object.
let jsonString = object.toJSONString(prettyPrint: true)
print(jsonString as AnyObject)
Apple documentation about JSONEncoder ->
struct GroceryProduct: Codable {
var name: String
var points: Int
var description: String?
}
let pear = GroceryProduct(name: "Pear", points: 250, description: "A ripe pear.")
let encoder = JSONEncoder()
encoder.outputFormatting = .prettyPrinted
let data = try encoder.encode(pear)
print(String(data: data, encoding: .utf8)!)
/* Prints:
{
"name" : "Pear",
"points" : 250,
"description" : "A ripe pear."
}
*/