Swift 4 SwiftyJSON: Appending JSON results as Strings to an array - arrays

I am having issues with appending JSON results onto an empty array using SwiftyJSON. In the view controller, I have the following:
var jsonResults = [String]()
func runAPI() {
let jsonUrlString = "sampleUrl"
guard let url = URL(string: jsonUrlString) else {
return
}
URLSession.shared.dataTask(with: url) { (data, response, error) in
guard let data = data else {
return
}
do {
let json = try JSONSerialization.jsonObject(with: data, options: .mutableContainers)
self.jsonResults.append(JSON(json).stringValue) // SwiftyJSON used here
print(JSON(json)) // prints a string on the console
print(JSON(json).stringValue) // prints the same string on the console
} catch let jsonError {
print("Error serializing json:", jsonError)
}
}
.resume()
}
override func viewDidLoad() {
super.viewDidLoad()
runAPI()
print(jsonResults.count) // prints "0" :(
}
I was hoping if someone had any ideas on how to get a JSON result converted into a string and appended onto an array. Any input would be greatly appreciated.

Related

Return HTTP request data

I would like to return the var points from my method getAllPoints()
struct tempPoint: Decodable {
let idPoint:Int
let ptName:String
let ptLat:Double
let ptLong:Double
}
func getAllPoints(){
if let url = URL(string: "[MyWebService.com]") {
URLSession.shared.dataTask(with: url) { data, response, error in
if let data = data {
if let jsonString = String(data: data, encoding: .utf8) {
let jsonData = Data(jsonString.utf8)
do {
let points = try JSONDecoder().decode([tempPoint].self, from: jsonData)
} catch let error {
print(error)
}
print(jsonString)
}
}
}.resume()
}
}
Results that I got when I do print(points)
[
{"idPoint":6,"ptName":"Maison","ptLat":43.72623997050257,"ptLong":2.202591651830584},
{"idPoint":7,"ptName":"Wheelin","ptLat":42.75754326128173,"ptLong":8.137330631668685},
{"idPoint":8,"ptName":"Animoz","ptLat":45.76321863196126,"ptLong":7.137186047202841},
{"idPoint":9,"ptName":"Hesias","ptLat":45.767222865412144,"ptLong":6.132352002277385},
{"idPoint":10,"ptName":"Marcombes","ptLat":45.76018235160473,"ptLong":4.085466264251463},
{"idPoint":11,"ptName":"Leclan","ptLat":48.80950120948317,"ptLong":2.110623123039061},
{"idPoint":12,"ptName":"Cournon Skatepark","ptLat":39.74138613175866,"ptLong":4.2154977334348906}
]
I wonder if this is possible to return these at the end of my method.
I'm gonna use it in a pickerView and to do a CRUD that's why I would like to store them.
Thank You
use completion closure to "return" your points.
Try something like this:
func getAllPoints(completion: #escaping([tempPoint]) -> ()) { // <-- here
if let url = URL(string: "[MyWebService.com]") {
URLSession.shared.dataTask(with: url) { data, response, error in
if let data = data {
do {
let points = try JSONDecoder().decode([tempPoint].self, from: data)
completion(points) // <-- here return the results when done
return
} catch let error {
print(error) // <-- here todo deal with errors
}
}
completion([]) // <-- here todo deal with errors
}.resume()
} else {
completion([]) // <-- here todo deal with errors
}
}
and call the function like this:
getAllPoints() { results in
print("array of points: \(results)")
}
My understanding is that you wanted the api response at the position of calling your function. You can return your response using closures.
The typealias you are seeing in the below code are self defined data types. You can directly add closures in your function but to make it more simple for you I declare those closures with typealias as data types. You can read more about typealias and closures here.
After declaring our own closure types. We need to use them in our function as parameters.
struct TempPoint: Decodable {
let idPoint:Int
let ptName:String
let ptLat:Double
let ptLong:Double
}
typealias ApiResponse = ([TempPoint]) -> Void
typealias ApiError = (Error) -> Void
func getAllPoints(_ completion: #escaping ApiResponse, apiError: #escaping ApiError){
if let url = URL(string: "[MyWebService.com]") {
URLSession.shared.dataTask(with: url) { data, response, error in
if let data = data {
if let jsonString = String(data: data, encoding: .utf8) {
let jsonData = Data(jsonString.utf8)
do {
let points = try JSONDecoder().decode([TempPoint].self, from: jsonData)
completion(points)
} catch let error {
print(error)
apiError(error)
}
print(jsonString)
}
}
}.resume()
}
}
Our function after changes would look like above.
Now here is how we can use this function.
override func viewDidLoad() {
super.viewDidLoad()
getAllPoints { [weak self] (points) in
// you update you UI here, be sure to call in main thread when you are doing UI updates in closures.
print(points)
} apiError: { (error) in
print(error)
}
}
It would be efficient if things are loosely coupled which can be done with Delegate pattern.
protocol TempPointDelegate {
func didUpdatePoints(_ tempPoints: [tempPoint])
func didFailWithError(error: Error)
}
and in tempPoint Struct
struct tempPoint: Decodable {
let idPoint:Int
let ptName:String
let ptLat:Double
let ptLong:Double
}
var delegate: TempPointDelegate?
func getAllPoints(){
if let url = URL(string: "[MyWebService.com]") {
URLSession.shared.dataTask(with: url) { data, response, error in
if let data = data {
if let jsonString = String(data: data, encoding: .utf8) {
let jsonData = Data(jsonString.utf8)
do {
let points = try JSONDecoder().decode([tempPoint].self, from: jsonData)
self.delegate?.didUpdatePoints(points)
return
} catch let error {
self.delegate?.didFailWithError(error)
return
}
}
}
}.resume()
}
}
finally in your ViewController implement tempPointDelegate delegate
class MainViewController: tempPointDelegate{
override func viewDidLoad() {
super.viewDidLoad()
tempPoint.getAllPoints()
}
func didUpdatePoints(_ tempPoints: [tempPoint]) {
DispatchQueue.main.async {
//process the tempPoints here / call the func to handle tempPoints
}
}
func didFailWithError(error: Error) {
//process the error returned here.
}
}
First of all please name structs with starting uppercase letter (TempPoint).
Second of all the conversion to String and back to Data is pointless
In Swift 5.5 you can use async/await
func getAllPoints() async throws -> [TempPoint] {
guard let url = URL(string: "[MyWebService.com]") else { throw URLError(.badURL) }
let (data, _) = try await URLSession.shared.data(from : url)
return try JSONDecoder().decode([TempPoint].self, from: data)
}
And call it
Task {
do {
let points = try await getAllPoints()
} catch {
print(error)
}
}

How to Json decode API data with an array?

I am learning Swift and trying to get elevation data based on coordinates from the Open Elevation API.
I found a code to make the request and decode the data using structs.
My problem is that the API result includes the information in an array:
{"results": [{"latitude": 41.161758, "longitude": -8.583933, "elevation": 117}]}
What I have been able to program so far does save the data as an array in json.results, but only with one index including all of the data:
[API.MyResult(latitude: 41.16176, longitude: -8.583933, elevation: 117)]
("API" is the name of the file)
Here is my code:
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
let url = "https://api.open-elevation.com/api/v1/lookup?locations=41.161758,-8.583933"
getData(from: url)
}
private func getData(from url: String){
let task = URLSession.shared.dataTask(with: URL(string: url)!, completionHandler: {data, response, error in
guard let data = data, error == nil else {
print("error")
return
}
var result: Response?
//print(result)
do{
result = try JSONDecoder().decode(Response.self, from: data)
}
catch{
print(error.localizedDescription)
}
guard let json = result else {
return
}
print(json.results)
//print(json.results.latitude)
//print(json.results.longitude)
//print(json.results.elevation)
})
task.resume()
}
}
struct Response: Codable {
let results: [MyResult]
}
struct MyResult: Codable {
let latitude: Float
let longitude: Float
let elevation: Int
}
Trying to print out json.results.latitude leads to the error
"Value of type '[MyResult]' has no member 'latitude'"
I assume at some point, a variable has to be defined as an array.
What needs to be changed here?
result is indeed a single object, but the property results is an array (multiple objects).
A slightly different naming avoids the confusion.
Notes:
Never print literal "error" or error.localizedDescription in a Decoding context, always print the error instance.
Proceed to parse the result in the do scope
private func getData(from url: String){
guard let url = URL(string: url) else { print("Bad URL", url); return }
let task = URLSession.shared.dataTask(with: url) {data, _, error in
if let error = error { print(error); return }
do {
let response = try JSONDecoder().decode(Response.self, from: data!)
for result in response.results {
print(result.latitude)
print(result.longitude)
print(result.elevation)
}
}
catch {
print(error)
}
}
task.resume()
}

Could not get JSONArray in variable Swift

So basically I want to make a TableList from my REST service. The REST service can be decoded by this code block:
func getAllParkeergarages(_ completion: #escaping ([Parkeergarage]) -> ()) {
if let url = URL(string: "http://localhost:8080/parkeergarages") {
URLSession.shared.dataTask(with: url) { data, response, error in
if let data = data {
do {
let res = try JSONDecoder().decode([Parkeergarage].self, from: data)
print(res)
completion(res)
return
} catch let error {
print(error)
}
}
}.resume()
}
}
By using this codeblock I can print the whole JSON in my terminal:
getAllParkeergarages { (array) in
print(array)
}
To get the data in a TableView I need to have the data in a variable. But here is where I get stuck. I tried some different methodes like:
private var data: [Parkeergarage] = getAllParkeergarages { (array) in
return array
}
but is gives me an error: 'Cannot convert value of type '()' to specified type '[Parkeergarage]'. Can someone help me get the result of the function in the variable?
you should do
private var data: [Parkeergarage] = []
in viewDidLoad
override func viewDidLoad() {
super.viewDidLoad()
getAllParkeergarages { (array) in
self.data = array
self.tableView.reloadData()
}
}
I cannot explain any more.

Swift UIImage Array from JSON URL

I try to create an Array of UIImages from a URL Array received from a JSON Request to show them afterwards in a UITableView.
But somehow my UIImage Array stays Empty and is not receiving any Data.
The other Arrays for example memeURL are receiving all Data correct but memePics.count stays on 0.
Would be great if someone could show me what i am doing wrong.
Also if for this task there is a better way on how to do it - it would be also appreciated!
Var:
var memePics: [UIImage] = []
Loop to add Images to Array:
while(i < memeURL.count) {
MemeAPI.requestAPIImageFile(url: memeURL[i]) { (image, error) in
guard let image = image else {
print("PIC IS NIL")
return
}
self.memePics.append(image)
i+=1
}
}
RequestAPIImageFile Function:
class func requestAPIImageFile(url: URL, completionHandler: #escaping (UIImage?, Error?) -> Void) {
let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
guard let data = data else {
completionHandler(nil, error)
return}
let downloadedImage = UIImage(data: data)
completionHandler(downloadedImage, nil)
}
task.resume()
}
Add plus line out of callback
self.memePics.append(image)
}
i+=1
and use DispatchGroup to be notified on finish like
let g = DispatchGroup()
memeURL.forEach {
g.enter()
MemeAPI.requestAPIImageFile(url:$0) { (image, error) in
guard let image = image else {
print("PIC IS NIL")
return
}
self.memePics.append(image)
g.leave()
}
}
g.notify(queue:.main) {
print("All done")
}

Json data - white console - Xcode 9

I am trying a simple app in which I want to convert some values. It worked until I tried to convert the data in a dictionary, and when I hit run, it builds successfully, but the console does not print anything. Here is the code:
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let url = URL(string: "http://gnb.dev.airtouchmedia.com/rates.json")
let task = URLSession.shared.dataTask(with: url!) { (data, response, error) in
if error != nil
{
print("ERROR")
}
else {
if let content = data {
do {
//Array
let myJson = try JSONSerialization.jsonObject(with:content, options: JSONSerialization.ReadingOptions.mutableContainers) as AnyObject
//print(myJson)
if let rate = myJson["rate"] as? NSDictionary {
if let currency = rate["AUD"] {
print(currency)
}
}
}
catch {
}
}
}
}
task.resume()
}
because you are parsing JSON wrongly
try this
let myJson = try JSONSerialization.jsonObject(with:content, options: JSONSerialization.ReadingOptions.mutableContainers) as? [[String: AnyObject]] else { return }
for rate in myJson
guard let cur = user["from"] as? String,
let curRate = user["rate"] as? Double else { break }
if let cur = "AUD" {
print(curRate)
}
Update:
You are receiving Array of Objects in response,
so first you have to treat it as Array of object,
Then you have to loop through this objects and then inside that loop you have to extract the data you were looking for and play with it.

Resources