optional type String? not unwrapped - json

I have a PHP page that is on my webserver that interacts with a mysql database called grabmapinfo.php
The output of the page is [{"companyname":"Brunos Burgers","companyphone":"7745632382","companytown":"858 Western Ave, Lynn, MA 01905"}]
Now I have this Swift code, which I want to get the info from the database, geocode the address to latitude and longitude, plot the annotation on the map, change the annotation image and title, and make a circle with a radius of 5 with the pin being in the center.
let locationManager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
self.locationManager.delegate = self
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest
self.locationManager.requestWhenInUseAuthorization()
self.locationManager.startUpdatingLocation()
self.buyerMapView1.showsUserLocation = true
let url = NSURL(string: "https://alanr917.000webhostapp.com/grabmapinfo.php")
var request = URLRequest(url:url! as URL)
URLSession.shared.dataTask(with: request as URLRequest, completionHandler: { (data:Data?, response:URLResponse?, error:Error?) -> Void in
if error != nil {
// Display an alert message
print(error)
return
}
do {
if let json = try JSONSerialization.jsonObject(with: data!, options: []) as? [[String:AnyObject]] {
for item in json {
// Get company info from DB
let companyname = item["companyname"] as? String
let companyphone = item["companyphone"] as? String
let companytown = item["companytown"] as? String
print("Company : \(companyname)")
print("Phone : \(companyphone)")
print("Address : \(companytown)")
let address = companytown
let geocoder = CLGeocoder()
geocoder.geocodeAddressString(address, completionHandler: {
(placemarks: [AnyObject]!, error: NSError!) -> Void in
if let placemark = placemarks?[0] as? CLPlacemark {
let pa = MKPointAnnotation()
pa.coordinate = placemark.location.coordinate
pa.title = companyname
pa.imageName = #imageLiteral(resourceName: "growerAnnotation")
self.buyerMapView1.addAnnotation(pa)
let center = annotation.coordinate
let circle = MKCircle(center: center, radius: 5) // change the 5 later to however many miles the grower purchased
self.buyerMapView1.add(circle)
}
})
}
}
} catch {
print(error)
}
})
}
But i get an error that says the optional type String? is not unwrapped and it errors out and wont build.
Does anyone see where I'm going wrong? Thanks!

companyTown is declared as an optional string and the geocodeAddressString method takes a string. You need to unwrap the option before calling it.
if let addressUnwrapped = address {
geocoder.geocodeAddressString(addressUnwrapped, completionHandler: {
(placemarks: [AnyObject]!, error: NSError!) -> Void in
...
})
}

Please check the comments through the code for more detailed explanation on the problems that I found in your code:
import UIKit
import CoreLocation
import MapKit
class ViewController: UIViewController, CLLocationManagerDelegate {
#IBOutlet weak var buyerMapView1: MKMapView!
let locationManager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.requestWhenInUseAuthorization()
locationManager.startUpdatingLocation()
buyerMapView1.showsUserLocation = true
// first unwrap your url
guard let url = URL(string: "https://alanr917.000webhostapp.com/grabmapinfo.php") else { return }
print("url:",url)
// no need to create a request. just a url is fine and you don't need to specify the parameters type. Let the compiler infer it.
URLSession.shared.dataTask(with: url) { data, response, error in
// unwrap your data and make sure there is no error
guard let data = data, error == nil else {
print(error ?? "nil")
return
}
// you should update the UI from the main queue
DispatchQueue.main.async {
print("data:", data)
do {
if let array = try JSONSerialization.jsonObject(with: data) as? [[String: Any]] {
for dict in array {
// make sure you unwrap your dictionary strings
let companyname = dict["companyname"] as? String ?? ""
let companyphone = dict["companyphone"] as? String ?? ""
let companytown = dict["companytown"] as? String ?? ""
print("Company:", companyname)
print("Phone:", companyphone)
print("Address:", companytown)
let address = companytown
let geocoder = CLGeocoder()
// again let the compiler infer the types vvv vvv
geocoder.geocodeAddressString(address) { placemarks, error in
if let placemark = placemarks?.first,
let coordinate = placemark.location?.coordinate {
let pa = MKPointAnnotation()
pa.coordinate = coordinate
pa.title = companyname
self.buyerMapView1.addAnnotation(pa)
let center = pa.coordinate // where does this coordinate come from??
let circle = MKCircle(center: center, radius: 5)
self.buyerMapView1.add(circle)
}
}
}
}
} catch {
print(error)
}
}
// you forgot to call resume to start your data task
}.resume()
}
}

Related

Decoding 1957 Dictionaries from an array

Goals
Accessing the Dictionaries within an array
Some how get all 1957 Dictionaries decoded without hand coding each ticker name.
The below image is data from https://rapidapi.com/Glavier/api/binance43/ to replicate the below image get Symbol Price Ticker needs to be selected.
With the help of another question which was answered here I have included code below which I am trying to change to accomplish the above goals.
CallApi.swift - this file calls the API and models it to PriceApiModel
import UIKit
class ViewController: UIViewController {
let headers = [
"X-RapidAPI-Key": "Sorry I cannot include this",
"X-RapidAPI-Host": "binance43.p.rapidapi.com"
]
let request = NSMutableURLRequest(url: NSURL(string: "https://binance43.p.rapidapi.com/ticker/price")! as URL,
cachePolicy: .useProtocolCachePolicy,
timeoutInterval: 10.0)
func getData() {
request.httpMethod = "GET"
request.allHTTPHeaderFields = headers
let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
if (error != nil) {
print("error")
} else {
let httpResponse = response as? HTTPURLResponse
do {
//let dictionary = try JSONSerialization.jsonObject(with: data!, options: [])
let model = try JSONDecoder().decode(PriceApiModel.self, from: data!)
//print(String(model.symbol) + "name") // please see output below
//print(dictionary)
} catch {
print("NOT WORKING ")
}
}
})
dataTask.resume()
}
}
PriceApiModel.swift - I am trying to find a way for this file to be a model for decoding the data
struct PriceApiModel: Hashable, Codable {
//changed the String type to Decimal
var price: String
// every property you are interested to decode needs a CodingKey.
// You can omit values you are not interested in
enum CodingKeys: CodingKey{
case askPrice
}
// here you decode your data into the struct
init(from decoder: Decoder) throws {
// get the container
let container = try decoder.container(keyedBy: CodingKeys.self)
// decode the askPrice into a String and cast it into a Decimal
let askPrice = String(try container.decode(String.self, forKey: .askPrice))
// check if casting was succesfull else throw
guard let askPrice = askPrice else{
throw CustomError.decodingError
}
// assign it
self.askPrice = askPrice
}
}
So I just tried out what you want to achieve here. First of all, you declared a service class (fetching data) as ViewController, by inheritance a UIViewController. It seems to me a bit odd just having this in a class because the UIViewController is not used. Secondly, I would recommend you to watch or read something about Codable for example Hackingforswift. It helped at least me :)
However, here is a Code that shows you a way how it could work:
OptionalObject is needed because of the data structure, holding everything within an array.
struct OptionalObject<Base: Decodable>: Decodable {
public let value: Base?
public init(from decoder: Decoder) throws {
do {
let container = try decoder.singleValueContainer()
self.value = try container.decode(Base.self)
} catch {
self.value = nil
}
}
}
struct PriceApiModel: Codable {
let price: String
let symbol: String
}
enum ServiceError: Error {
case failureAtDecoding
}
// MVVM Pattern https://www.hackingwithswift.com/books/ios-swiftui/introducing-mvvm-into-your-swiftui-project
class ServiceViewModel: ObservableObject {
// Publisher you can subscribe to it.
// Every time the Publisher changes view will re-render.
#Published var priceModel: [PriceApiModel] = []
let headers = [
"X-RapidAPI-Key": "",
"X-RapidAPI-Host": "binance43.p.rapidapi.com"
]
var request = URLRequest(
url: URL(string: "https://binance43.p.rapidapi.com/ticker/price")!,
cachePolicy: .useProtocolCachePolicy,
timeoutInterval: 5.0
)
init() {
self.getData { priceModel in
// As DocC says:
/// A value that represents either a success or a failure, including an
// So you have to "unwrap" it to handle success or failure
switch priceModel {
case let .success(result):
DispatchQueue.main.async {
self.priceModel = result
}
case let .failure(failure):
print(failure)
}
}
}
func getData(priceModel: #escaping (Result<[PriceApiModel], Error>) -> Void) {
request.httpMethod = "GET"
request.allHTTPHeaderFields = headers
let session = URLSession.shared
let dataTask = session.dataTask(with: request) { (data, response, error) -> Void in
if let error = error {
priceModel(.failure(error))
} else if let data = data {
let model = try? JSONDecoder().decode([OptionalObject<PriceApiModel>].self, from: data)
let editModel = model?.compactMap {
PriceApiModel(price: $0.value?.price ?? "nil", symbol: $0.value?.symbol ?? "nil")
}
if let editModel = editModel {
priceModel(.success(editModel))
} else {
priceModel(.failure(ServiceError.failureAtDecoding))
}
}
}
dataTask.resume()
}
}
struct ContentView: View {
// Initialize the ServiceViewModel as StateObject
#StateObject var viewModel: ServiceViewModel = .init()
var body: some View {
NavigationView {
List {
ForEach(viewModel.priceModel, id: \.symbol) { model in
HStack {
Text(model.symbol)
Spacer()
Text(model.price)
}
}
}
}
}
}
Hope I could help.

Unable to add parameter to API in swift

I am getting parameter value from other viewcontroller, and i am getting parameter valueperfectly but which i am unable to add to API
here if i hardcode eventStatus then its working
and eventStatus value from otherviewcontroller also coming perfectly which i am unable to add to API
if i hard code like this its working
var eventType = "Draft"
let string = Constants.GLOBAL_URL + "/get/allevents/?eventstatus=\(self.eventType)"
Code: here i am getting correct eventStatus value but while breakpoint its control goes to else, why?
class EventsViewController: UIViewController {
var eventsListArray = [AnyObject]()
// var eventType = "Draft"
var eventType: String!
var eventList : EventsModel? = nil
#IBOutlet weak var eventsTableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
getAllEventsList()
}
func getAllEventsList() {
DispatchQueue.main.async {
let headers = ["deviceid": deviceId,"userType": "personal","key": personalId]
DispatchQueue.main.async {
//let string = Constants.GLOBAL_URL + "/get/allevents/?eventstatus=\(self.eventType)"
let string = Constants.GLOBAL_URL + "/get/allevents"
var urlComponents = URLComponents(string: string)
let eventStatus = self.eventType
print("event status value in API call \(eventStatus)")
let requestEventType = URLQueryItem(name: "eventstatus", value: eventStatus)
urlComponents?.queryItems = [requestEventType]
let urlStr = urlComponents?.url
var request = URLRequest(url: urlStr!, cachePolicy: .useProtocolCachePolicy,timeoutInterval: 10.0)
request.httpMethod = "POST"
request.allHTTPHeaderFields = headers as! [String : String]
let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
DispatchQueue.main.async {
if error == nil {
let httpResponse = response as? HTTPURLResponse
if httpResponse!.statusCode == 200 {
do {
let jsonObject = try JSONSerialization.jsonObject(with: data!) as! [String :Any]
print("publish event \(jsonObject)")
self.eventList = EventsModel.init(fromDictionary: jsonObject)
DispatchQueue.main.async {
if self.eventList?.events.count != 0 {
DispatchQueue.main.async {
self.eventsTableView.reloadData()
}
}
else {
DispatchQueue.main.async {
Constants.showAlertView(alertViewTitle: "", Message: "No Events \(self.eventType)", on: self)
self.eventList?.events.removeAll()
self.eventsTableView.reloadData()
}
}
}
} catch { print(error.localizedDescription) }
} else {
Constants.showAlertView(alertViewTitle: "", Message: "Something went wrong, Please try again", on: self)
}
}
}
})
dataTask.resume()
}
}
}
}
where i am wrong, why eventStatus value not adding to API.. please suggest me
it looks like you have a POST request and you need to use request data instead URL parameters.
HTTP Request in Swift with POST method here you can see:
let parameters: [String: Any] = [
"eventstatus": yourValue
]
request.httpBody = parameters.percentEncoded()
You also need to create parameters with eventStatus. And put it in
request.httpBody = parameters.percentEncoded()
If this endpoint on your server waits on this data in request body than you could not add it like a URL parameter.
Also, don't forget these 2 extensions from the example
extension Dictionary {
func percentEncoded() -> Data? {
return map { key, value in
let escapedKey = "\(key)".addingPercentEncoding(withAllowedCharacters: .urlQueryValueAllowed) ?? ""
let escapedValue = "\(value)".addingPercentEncoding(withAllowedCharacters: .urlQueryValueAllowed) ?? ""
return escapedKey + "=" + escapedValue
}
.joined(separator: "&")
.data(using: .utf8)
}
}
extension CharacterSet {
static let urlQueryValueAllowed: CharacterSet = {
let generalDelimitersToEncode = ":#[]#" // does not include "?" or "/" due to RFC 3986 - Section 3.4
let subDelimitersToEncode = "!$&'()*+,;="
var allowed = CharacterSet.urlQueryAllowed
allowed.remove(charactersIn: "\(generalDelimitersToEncode)\(subDelimitersToEncode)")
return allowed
}()
}
The solution must be obvious from the discussion in the comments. Although you seem a little puzzled so, I'm adding this to help you out. The value of eventType is never getting allocated to a value, it is remaining as nil unless you set it's value at a point in your code before the API call. So here is a way for you to figure this out:
let string = "https://www.google.com" + "/get/allevents"
var urlComponents = URLComponents(string: string)
let eventStatus = self.eventType ?? "Published" // here value is defaulted to "Published"
let requestEventType = URLQueryItem(name: "eventstatus", value: eventStatus)
urlComponents?.queryItems = [requestEventType]
let urlStr = urlComponents?.url
print(urlStr?.absoluteString)
Here, we're setting a default value for eventType for the scenario where eventType is nil.

How to Parse JSON data from a URL and append to a UiLabel with Swift 5

I am trying to get data from Json url for this specific line "ConfirmedCount" from https://raw.githubusercontent.com/BlankerL/DXY-COVID-19-Data/master/json/DXYOverall.json to a UiLabel that I created but keeps getting error. I have been trying to do this for week now.
Here is my code:
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var labeltest: UILabel!
//the json file url
let URL_HEROES = "https://raw.githubusercontent.com/BlankerL/DXY-COVID-19-Data/master/json/DXYOverall.json";
//the label we create
#IBOutlet weak var labelTest: UILabel!
//A string array to save all the names
var nameArray = [String]()
override func viewDidLoad() {
super.viewDidLoad()
//calling the function that will fetch the json
getJsonFromUrl()
}
//this function is fetching the json from URL
func getJsonFromUrl(){
//creating a NSURL
let url = NSURL(string: URL_HEROES)
//fetching the data from the url
URLSession.shared.dataTask(with: (url as URL?)!, completionHandler: {(data, response, error) -> Void in
if let jsonObj = try? JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? NSDictionary {
//printing the json in console
print(jsonObj.value(forKey: "results")!)
//getting the avengers tag array from json and converting it to NSArray
if let heroeArray = jsonObj.value(forKey: "results") as? NSArray {
//looping through all the elements
for results in heroeArray{
//converting the element to a dictionary
if let heroeDict = results as? NSDictionary {
var confirmedCount: Int
//getting the name from the dictionary
if let confirmedCount = heroeDict.value(forKey: "confirmedCount") {
//adding the name to the array
self.nameArray.append((String (format: "1234", confirmedCount as! Int)))
}
}
}
}
OperationQueue.main.addOperation({
//calling another function after fetching the json
//it will show the names to label
self.showNames()
})
}
}).resume()
}
func showNames(){
//looping through all the elements of the array
for confirmedCount in nameArray{
labelTest.text = (confirmedCount)
}
}
}
This is the error that I am getting:
(
{
abroadRemark = "";
confirmedCount = 80422;
confirmedIncr = 120;
curedCount = 49923;
curedIncr = 2663;
currentConfirmedCount = 27515;
currentConfirmedIncr = "-2581";
deadCount = 2984;
deadIncr = 38;
generalRemark = "\U7591\U4f3c\U75c5\U4f8b\U6570\U6765\U81ea\U56fd\U5bb6\U536b\U5065\U59d4\U6570\U636e\Uff0c\U76ee\U524d\U4e3a\U5168\U56fd\U6570\U636e\Uff0c\U672a\U5206\U7701\U5e02\U81ea\U6cbb\U533a\U7b49";
note1 = "\U75c5\U6bd2\Uff1aSARS-CoV-2\Uff0c\U5176\U5bfc\U81f4\U75be\U75c5\U547d\U540d COVID-19";
note2 = "\U4f20\U67d3\U6e90\Uff1a\U65b0\U51a0\U80ba\U708e\U7684\U60a3\U8005\U3002\U65e0\U75c7\U72b6\U611f\U67d3\U8005\U4e5f\U53ef\U80fd\U6210\U4e3a\U4f20\U67d3\U6e90\U3002";
note3 = "\U4f20\U64ad\U9014\U5f84\Uff1a\U7ecf\U547c\U5438\U9053\U98de\U6cab\U3001\U63a5\U89e6\U4f20\U64ad\U662f\U4e3b\U8981\U7684\U4f20\U64ad\U9014\U5f84\U3002\U6c14\U6eb6\U80f6\U4f20\U64ad\U548c\U6d88\U5316\U9053\U7b49\U4f20\U64ad\U9014\U5f84\U5c1a\U5f85\U660e\U786e\U3002";
remark1 = "\U6613\U611f\U4eba\U7fa4\Uff1a\U4eba\U7fa4\U666e\U904d\U6613\U611f\U3002\U8001\U5e74\U4eba\U53ca\U6709\U57fa\U7840\U75be\U75c5\U8005\U611f\U67d3\U540e\U75c5\U60c5\U8f83\U91cd\Uff0c\U513f\U7ae5\U53ca\U5a74\U5e7c\U513f\U4e5f\U6709\U53d1\U75c5";
remark2 = "\U6f5c\U4f0f\U671f\Uff1a\U4e00\U822c\U4e3a 3\Uff5e7 \U5929\Uff0c\U6700\U957f\U4e0d\U8d85\U8fc7 14 \U5929\Uff0c\U6f5c\U4f0f\U671f\U5185\U53ef\U80fd\U5b58\U5728\U4f20\U67d3\U6027\Uff0c\U5176\U4e2d\U65e0\U75c7\U72b6\U75c5\U4f8b\U4f20\U67d3\U6027\U975e\U5e38\U7f55\U89c1";
remark3 = "\U5bbf\U4e3b\Uff1a\U91ce\U751f\U52a8\U7269\Uff0c\U53ef\U80fd\U4e3a\U4e2d\U534e\U83ca\U5934\U8760";
remark4 = "";
remark5 = "";
seriousCount = 6416;
seriousIncr = "-390";
suspectedCount = 520;
suspectedIncr = 143;
updateTime = 1583295001876;
}
)
Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value: file /Users/AbdalQaydi/Desktop/jnews/jnews/ViewController.swift, line 84
2020-03-04 00:26:49.829895-0500 jnews[12702:1192396] Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value: file /Users/AbdalQaydi/Desktop/jnews/jnews/ViewController.swift, line 84
(lldb)
It looks like it can parse the data but I don't know how to add the json data for line "confirmedCount" to label.
Please any help is really appreciated. I have been trying to figure out how to do this and just can't find a way.
Use Codable to parse the JSON data.
Models:
struct Root: Decodable {
let results: [Result]
let success: Bool
}
struct Result: Decodable {
let currentConfirmedCount, confirmedCount, suspectedCount, curedCount: Int
let deadCount, seriousCount, currentConfirmedIncr, confirmedIncr: Int
let suspectedIncr, curedIncr, deadIncr, seriousIncr: Int
let generalRemark, abroadRemark, remark1, remark2: String
let remark3, remark4, remark5, note1: String
let note2, note3: String
let updateTime: Int
}
Parse the data like,
if let url = URL(string: URL_HEROES) {
URLSession.shared.dataTask(with: url) { (data, response, error) in
if let data = data {
do {
let response = try JSONDecoder().decode(Root.self, from: data)
let confirmedCountArr = response.results.map { String($0.confirmedCount) }
nameArray.append(contentsOf: confirmedCountArr)
//rest of the code...
} catch {
print(error)
}
}
}.resume()
}

JSON Parsing sometimes crashes Swift

I am parsing JSON in my iOS app and sometimes when the network connection is weak but isn’t gone, the app will crash while trying to parse the JSON, because it says it had an error while force unwrapping a nil.
The code I use for that is here.
//
// MessageModel.swift
// truthordare
//
// Created by Dustin Palmatier on 11/2/19.
// Copyright © 2019 Hexham Network. All rights reserved.
//
import UIKit
protocol MessageModelProtocol: class {
func itemsDownloaded(items: NSArray)
}
class MessageModel: NSObject, URLSessionDataDelegate {
//properties
weak var delegate: MessageModelProtocol!
let urlPath = "Redacted" //this will be changed to the path where service.php lives
let deleteUrl = "REDACTED"
func downloadItems(TYPE: String, IDENTIFIER: String) {
let url: URL = URL(string: urlPath)!
var request = URLRequest(url: url)
request.httpMethod = "POST"
let postString = "type=\(TYPE)&identifier=\(IDENTIFIER)";
request.httpBody = postString.data(using: String.Encoding.utf8);
let defaultSession = Foundation.URLSession(configuration: URLSessionConfiguration.default)
let task = defaultSession.dataTask(with: request) { (data: Data?, response: URLResponse?, error: Error?) in
if error != nil {
print("Failed to download data")
}else {
print("Data downloaded")
self.parseJSON(data!)
}
}
task.resume()
}
func deleteItems(TYPE: String, SKU: String) {
let myUrl = URL(string: "https://truthordare.hexhamnetwork.com/api/92fFDd93D/erase.php");
var request = URLRequest(url:myUrl!)
request.httpMethod = "POST"// Compose a query string
let postString = "type=\(TYPE)&sku=\(SKU)";
request.httpBody = postString.data(using: String.Encoding.utf8);
let task = URLSession.shared.dataTask(with: request) { (data: Data?, response: URLResponse?, error: Error?) in
if error != nil
{
print("error=\(error ?? "Empty" as! Error)")
return
}
}
task.resume()
}
func parseJSON(_ data:Data) {
var jsonResult = NSArray()
do{
jsonResult = try JSONSerialization.jsonObject(with: data, options: [.allowFragments, .mutableContainers]) as! NSArray
} catch let error as NSError {
print(error)
}
var jsonElement = NSDictionary()
let tables = NSMutableArray()
for i in 0 ..< jsonResult.count
{
jsonElement = jsonResult[i] as! NSDictionary
let table = TableModel()
//the following insures none of the JsonElement values are nil through optional binding
if let sku = jsonElement["SKU"] as? String,
let message = jsonElement["MESSAGE"] as? String
{
table.sku = sku
table.message = message
}
tables.add(table)
}
DispatchQueue.main.async(execute: { () -> Void in
self.delegate.itemsDownloaded(items: tables)
})
}
}
It crashes once it gets to
self.delegate.itemsDownloaded(items: tables)
It says that it received a nil while force unwrapping
To initialize the delegate I called this within the classes that were calling this.
messageModel.delegate = self
To avoid the crash reliably declare delegate as regular optional
weak var delegate: MessageModelProtocol?
and call it
self.delegate?.itemsDownloaded(items: tables)
In Swift 4+ it's highly recommended to use the Codable protocol and in any Swift version a completion handler rather than protocol / delegate.
And don't use NS... collection types in Swift at all. Use native types. And .mutableContainers / .allowFragments is pointless in Swift if the expected type is a collection type. Omit the parameter.

Decoding JSON in Swift 4

I am working through the Apple App Development Guide and this is the code I am working with right now...
struct CategoryInfo: Codable {
var category: String
var description: String
var logo: String
var mobileCategoryName: String
enum Keys: String, CodingKey {
case category
case description = "descr"
case logo
case mobileCategoryName = "mobileCatName"
}
init(from decoder: Decoder) throws {
let valueContainer = try decoder.container(keyedBy: Keys.self)
self.category = try valueContainer.decode(String.self, forKey: Keys.category)
self.description = try valueContainer.decode(String.self, forKey: Keys.description)
self.logo = try valueContainer.decode(String.self, forKey: Keys.logo)
self.mobileCategoryName = try valueContainer.decode(String.self, forKey: Keys.mobileCategoryName)
}
}
override func viewDidLoad() {
super.viewDidLoad()
let categories = Industry_TableViewController()
categories.fetchCategoryInfo { (category) in
if let category = category {
print(category)
}
}
}
func fetchCategoryInfo(completion: #escaping(CategoryInfo?) -> Void) {
let url = URL(string: "XXXXX")!
let task = URLSession.shared.dataTask(with: url) {
(data, response, error) in
let jsonDecoder = JSONDecoder()
if let data = data,
let category = try? jsonDecoder.decode(CategoryInfo.self, from: data) {
completion(category)
} else {
print("Nothing reutrned or Not decoded")
completion(nil)
}
}
task.resume()
}
it works fine when my returned JSON is in the following format...
{"category":"Excavators","descr":"Compact, Mid-Sized, Large, Wheeled, Tracked...","logo":"excavators","mobileCatName":"Excavators"}
My struct is created and all the variables are populated correctly. But the API doesn't bring back one category at a time it brings back multiple like so...
[{"category":"Aerial Lifts","descr":"Aerial Lifts, Man Lifts, Scissor Lifts...","logo":"aeriallifts","mobileCatName":"Aerial Lifts"},{"category":"Aggregate Equipment","descr":"Crushing, Screening, Conveyors, Feeders and Stackers...","logo":"aggregateequipment","mobileCatName":"Aggregate"},{"category":"Agricultural Equipment","descr":"Tractors, Harvesters, Combines, Tillers...","logo":"agricultural","mobileCatName":"Agricultural"}]
And I am running into a wall trying to figure out how to get this decoded properly. I've gone down so many routes I don't even know what to search for any more. Can anyone help or point me in a direction.
You need to modify your function to parse an array of categories instead of a single one. You just need to pass the Array<CategoryInfo> metatype to the decode function and modify the function signature such that the completion handler also returns an array.
func fetchCategoryInfo(completion: #escaping ([CategoryInfo]?) -> Void) {
let url = URL(string: "XXXXX")!
let task = URLSession.shared.dataTask(with: url) {
(data, response, error) in
let jsonDecoder = JSONDecoder()
if let data = data,
let categories = try? jsonDecoder.decode([CategoryInfo].self, from: data) {
completion(categories)
} else {
print("Nothing reutrned or Not decoded")
completion(nil)
}
}
task.resume()
}
try? jsonDecoder.decode([CategoryInfo].self, from: data)