There are two kinds of URLs
http://www.mocky.io/v2/5e91af4b3300008300e9cfd1 (this is which I made)
http://183.111.148.229/mob_json/mob_json.aspx?nm_sp=UP_MOB_CHECK_LOGIN¶m=1000|1000|1 (I need to get data from this URL)
Only number1. URL returns JSON data even if they have same structure. I couldn't find way to resolve this problem.
this is my network manager code
import Foundation
class NetworkManager: ObservableObject {
#Published var posts = [Post]()
func fetchData() {
if let url = URL(string: "http://www.mocky.io/v2/5e91af4b3300008300e9cfd1") {
let session = URLSession(configuration: .default)
let task = session.dataTask(with: url) { (data, response, error) in
if error == nil {
let decoder = JSONDecoder()
if let safeData = data {
do {
let results = try decoder.decode(Results.self, from: safeData)
DispatchQueue.main.async {
self.posts = results.Table
}
} catch {
print(error)
}
}
}
}
task.resume()
}
}
}
and my post data model
import Foundation
struct Results: Decodable {
let Table: [Post]
}
struct Post: Decodable, Identifiable {
var id: String {
return CD_FIRM
}
let CD_FIRM: String
let NM_FIRM: String
let CD_USER: String
let NM_USER: String
}
Can anyone can help me for this problem?
I want to get JSON data from URL number 2.
please help me =)
It looks like the issue with your second url is the pipes | at the end. One way to avoid this issue is to use URLComponents
So insert this at the start of fetchData
var urlComponents = URLComponents()
urlComponents.scheme = "http"
urlComponents.host = "183.111.148.229"
urlComponents.path = "/mob_json/mob_json.aspx"
urlComponents.queryItems = [
URLQueryItem(name: "nm_sp", value: "UP_MOB_CHECK_LOGIN"),
URLQueryItem(name: "param", value: "1000|1000|1")
]
And then get the url from the components
if let url = urlComponents.url {
let session = URLSession(configuration: .default)
//... same as before
Related
I am a beginner in iOS development. I was trying to use an api URl: https://www.arbeitnow.com/api/job-board-api in my job search iOS app. But nothing shows on my app. I tested the URL in POSTMAN and it returns json(but HTML in description part?). I wrote the code:
func getResults(completed: #escaping (Result<[Results], ErrorMessage>) -> Void) {
let urlString = "https://www.arbeitnow.com/api/job-board-api"
guard let url = URL(string: urlString) else {return}
let task = URLSession.shared.dataTask(with: url) { data, response, error in
if let _ = error {
completed(.failure(.invalidData))
return
}
guard let response = response as? HTTPURLResponse, response.statusCode == 200 else {
completed(.failure(.invalidResponse))
return
}
guard let data = data else {
completed(.failure(.invalidData))
return
}
do {
let deconder = JSONDecoder()
deconder.keyDecodingStrategy = .convertFromSnakeCase
let results = try deconder.decode([Results].self, from: data)
completed(.success(results))
} catch {
completed(.failure(.invalidData))
}
}
task.resume()
}
struct Results: Codable {
let slug, companyName, title, resultsDescription: String
let remote: Bool
let url: String
let tags, jobTypes: [String]
let location: String
let createdAt: Int
enum CodingKeys: String, CodingKey {
case slug
case companyName = "company_name"
case title
case resultsDescription = "description"
case remote, url, tags
case jobTypes = "job_types"
case location
case createdAt = "created_at"
}
}
I used the code in HomeViewController:
override func viewDidLoad() {
super.viewDidLoad()
title = "Home"
collectionView.backgroundColor = UIColor(named: "backgroundMain")
collectionView.register(SearchViewCell.self, forCellWithReuseIdentifier: cellId)
setupSearchBar()
Service.shared.getResults() { [weak self] result in
switch result {
case .success(let results):
print(results)
self?.jobResults = results
DispatchQueue.main.async {
self?.collectionView.reloadData()
}
case .failure(let error):
print(error)
}
}
}
888
I don't know what is wrong with my code. Can anyone help? Thanks!
You are discarding all meaningful error information, which will make this hard to diagnose. If you get an Error object, you should return that:
enum WebServiceError: Error {
case httpError(Data, Int)
}
func getResults(completion: #escaping (Result<[Results], Error>) -> Void) {
let urlString = "https://www.arbeitnow.com/api/job-board-api"
guard let url = URL(string: urlString) else {
completion(.failure(URLError(.badURL)))
return
}
let task = URLSession.shared.dataTask(with: url) { data, response, error in
guard
let data = data,
let response = response as? HTTPURLResponse,
error == nil
else {
completion(.failure(error ?? URLError(.badServerResponse)))
return
}
guard 200 ..< 300 ~= response.statusCode else {
completion(.failure(WebServiceError.httpError(data, response.statusCode)))
return
}
do {
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
let results = try decoder.decode([Results].self, from: data)
completion(.success(results.data))
} catch {
completion(.failure(error))
}
}
task.resume()
}
So, that will,
if there was a URLSession error, tell you what the error was;
if there was a non-2xx status code, tell you what the code was (and return the body of the response, too, in case you want to look at that); and
if there was a parsing error, tell you what the parsing error was.
Without something like this, that captures the salient error information, you are flying blind.
In this case, the error is that you are parsing for [Results], but the structure is a dictionary, whose key is data and whose value is a [Results]. You are missing an object for this dictionary that wraps the [Results].
struct ResponseObject: Decodable {
let data: [Posting]
let links: Links
let meta: Meta
}
struct Posting: Decodable {
let slug, companyName, title, description: String
let remote: Bool
let url: String
let tags, jobTypes: [String]
let location: String
let createdAt: Int
}
struct Links: Decodable {
let first: URL?
let last: URL?
let prev: URL?
let next: URL?
}
struct Meta: Decodable {
let currentPage: Int
let path: URL
let perPage: Int
let from: Int
let to: Int
let terms: String
let info: String
}
func getResults(completion: #escaping (Result<[Posting], Error>) -> Void) {
let urlString = "https://www.arbeitnow.com/api/job-board-api"
guard let url = URL(string: urlString) else {
completion(.failure(URLError(.badURL)))
return
}
let task = URLSession.shared.dataTask(with: url) { data, response, error in
guard
let data = data,
let response = response as? HTTPURLResponse,
error == nil
else {
completion(.failure(error ?? URLError(.badServerResponse)))
return
}
guard 200 ..< 300 ~= response.statusCode else {
completion(.failure(WebServiceError.httpError(data, response.statusCode)))
return
}
do {
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
let results = try decoder.decode(ResponseObject.self, from: data)
completion(.success(results.data))
} catch {
completion(.failure(error))
}
}
task.resume()
}
Your model does not match the JSON you receive from the link you provided:
Using:
struct Root: Codable{
let data: [WorkData]
let links: Links
let meta: Meta
}
// MARK: - Links
struct Links: Codable {
let first: String
let last, prev: String?
let next: String
}
// MARK: - Meta
struct Meta: Codable {
let currentPage, from: Int
let path: String
let perPage, to: Int
let terms, info: String
enum CodingKeys: String, CodingKey {
case currentPage = "current_page"
case from, path
case perPage = "per_page"
case to, terms, info
}
}
struct WorkData: Codable {
let slug, companyName, title, payloadDescription: String
let remote: Bool
let url: String
let tags, jobTypes: [String]
let location: String
let createdAt: Int
enum CodingKeys: String, CodingKey {
case slug
case companyName = "company_name"
case title
case payloadDescription = "description"
case remote, url, tags
case jobTypes = "job_types"
case location
case createdAt = "created_at"
}
}
should solve the problem
Usage:
let root = JsonDecoder().decode(Root.self, from: data)
let firstCompany = root.data[0]
Edit to adress the comment:
This should work!
let results = try decoder.decode([Results].self, from: data)
is your code isn´t it?
instead use:
let root = JsonDecoder().decode(Root.self, from: data)
how could data be missing here?
After that you should either map the root object to your Result type to keep your Viewmodel and completion Handler the way they are now. Or change Viewmodel and completion Handler instead.
I am able to parse and decode data coming from IMGUR website. However when I try to print out one of the properties from Data it doesn’t show anything. How do I fix this? Also I want to access the contents of images, specifically the links. How can I access the contents Of the image since it’s an array?
struct PhotoResponse:Codable {
let success: Bool
let status: Int
let data: [Data]
}
struct Data:Codable {
let id: String
let title: String
let views: Int
let link: String
let images: [Image]?
let animated: Bool?
}
struct Image: Codable {
let id: String
let imageDescription: String?
let link: String?
let size: Int
}
class NetworkService{
static let shared = NetworkService()
private let baseURL = "https://api.imgur.com/3/gallery"
private init() {}
func getJSON( searchName: String, completion: #escaping([Data]) -> Void){
let endPoints = baseURL + "/search/time/week/?q=\(searchName)"
guard let url = URL(string: endPoints ) else{
return
}
var request = URLRequest(url: url)
let headers = ["Authorization": ""]
request.httpMethod = "GET"
request.allHTTPHeaderFields = headers
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
URLSession.shared.dataTask(with: request) { data, response, error in
if let error = error {
print("Failed to query database", error)
}
guard let data = data else {
print("Data not received\(String(describing: error))")
return
}
let decoder = JSONDecoder()
do{
let decodedJson = try decoder.decode(PhotoResponse.self, from: data)
DispatchQueue.main.async {
completion(decodedJson.data)
}
}catch let error{
print("Json failed to decode\(String(describing: error))")
return
}
}.resume()
}
}
NetworkService.shared.getJSON(searchName: "cat") { (photos) in
for photo in photos {
print(photo.title)
print(photo.id)
}
Swift already has a Data struct, try renaming yours to something else, like MyData. Do the same with Image.
I'm currently learning API by doing a project that shows list of games from rawg.io referring to the website's doc.
To call the data from it, i created struct and defining keys in GameFeed.swift & GameDetail.swift
GameFeed.swift :
import Foundation
struct GameFeed: Codable {
var results:[GameDetail]
}
GameDetail.swift :
import Foundation
struct GameDetail: Codable {
var id:String?
var name:String?
var rating:String?
}
I also defining URLString with URL Link & JSONDecoder() in ViewController.swift
let urlString = "https://api.rawg.io/api/games"
let url = URL(string: urlString)
guard url != nil else {
return
}
let session = URLSession.shared
let dataTask = session.dataTask(with: url!){
(data, response, error) in
if error == nil && data != nil {
let decoder = JSONDecoder()
do {
let gameFeed = try decoder.decode(GameFeed.self, from: data!)
print(gameFeed)
}
catch {
print("Error Parsing JSON")
}
}
}
dataTask.resume()
}
When i launch it, i think the data is successfully pulled from website as it shown here
but the end result is "Error Parsing JSON"
How can i make it possible to retrieve the data and show it to console?
The problem is that you defined the id and the rating fields as strings, while in the JSON response, they are Int and Double, respectively.
You should change GameDetail.swift so it looks like this:
struct GameDetail: Codable {
var id:Int?
var name:String?
var rating:Double?
}
I am trying to figure out how to send a verification code to a user when signing up to verify their number. I am trying to follow online resources (code below), but it doesn't seem to work. When I put in a phone number as an 'xxxyyyzzzz' or 'xxx-yyy-zzzz' number (with a country code formatted just as the number, no '+') I get an error in my console that the phone number is not properly formatted. I'm not sure if this is the source of my issue, but as of now it seems the most probable. What confuses me about this is that online I am told that either of these formats will work. Any help? Code:
static let path = Bundle.main.path(forResource: "Config", ofType: "plist")
static let config = NSDictionary(contentsOfFile: path!)
private static let baseURLString = config!["serverUrl"] as! String
#IBOutlet var countryCodeField: UITextField! = UITextField()
#IBOutlet var phoneNumberField: UITextField! = UITextField()
#IBAction func sendVerification(_ sender: Any) {
if let phoneNumber = phoneNumberField.text,
let countryCode = countryCodeField.text {
SignUp2ViewController.sendVerificationCode(countryCode, phoneNumber)
}
}
static func sendVerificationCode(_ countryCode: String, _ phoneNumber: String) {
let parameters = [
"via": "sms",
"country_code": countryCode,
"phone_number": phoneNumber
]
let path = "start"
let method = "POST"
let urlPath = "\(baseURLString)/\(path)"
var components = URLComponents(string: urlPath)!
var queryItems = [URLQueryItem]()
for (key, value) in parameters {
let item = URLQueryItem(name: key, value: value)
queryItems.append(item)
}
components.queryItems = queryItems
let url = components.url!
var request = URLRequest(url: url)
request.httpMethod = method
let session: URLSession = {
let config = URLSessionConfiguration.default
return URLSession(configuration: config)
}()
let task = session.dataTask(with: request) {
(data, response, error) in
if let data = data {
do {
let jsonSerialized = try JSONSerialization.jsonObject(with: data, options: []) as? [String : Any]
print(jsonSerialized!)
} catch let error as NSError {
print(error.localizedDescription)
}
} else if let error = error {
print(error.localizedDescription)
}
}
task.resume()
}
You are calling a POST but passing the values into the URL as if it's a GET. You should use the httpBody for POST.
I have an Error in parsing JSON Format form an ASMX Web service,
My Code is
func getData() {
let url = URL(string: "http://192.168.11.188/getItems.asmx/theItems")
let theCategory = "ALL"
let theSubCategory = "ALL"
let postString = "theCategory=\(theCategory)&theSubCategory=\(theSubCategory)"
var request = URLRequest(url: url!)
request.httpMethod = "POST"
request.cachePolicy = NSURLRequest.CachePolicy.reloadIgnoringCacheData
request.httpBody = postString.data(using: String.Encoding.utf8)
URLSession.shared.dataTask(with: request) { (data, respons, error) in
if let error = error {
print("Error conning to server \(error)")
} else {
if let respons = respons as? HTTPURLResponse {
if respons.statusCode == 200 {
print(data!)
if let data = data {
do {
let json = try JSONDecoder().decode([ITEMS].self, from: data)
print(json)
} catch let parsingError {
print("Error parsing json \(parsingError)")
}
}
} else {
print("Error in responce code.... \(respons.statusCode)")
}
}
}
}.resume()
}
I am using the decoder struct in this code:
struct ITEMS: Codable {
let CODE:String
let CAT_ID:String
let SUB_ID:String
let PRODUCT_AR:String
let PRODUCT_EN:String
let OLD_PRICE:String
let NEW_PRICE:String
let UNIT:String
let BARCODE:String
let THE_DATE:String
let TIME:String
}
The JSON value is
{ ITEMS : [{"CODE":111,"CAT_ID":203,"SUB_ID":null,"PRODUCT_AR":"ITEM 1","PRODUCT_EN":"ITEM 1","OLD_PRICE":133.0035,"NEW_PRICE":109,"UNIT":null,"BARCODE":"328031002009","THE_DATE":"\/Date(1553673958397)\/","TIME":"11:05 AM"},
{"CODE":222,"CAT_ID":201,"SUB_ID":null,"PRODUCT_AR":"ITEM 2","PRODUCT_EN":"ITEM 2","OLD_PRICE":18.95,"NEW_PRICE":9.95,"UNIT":null,"BARCODE":"628103400012","THE_DATE":"\/Date(1553673958260)\/","TIME":"11:05 AM"}]}
but this code returns an Error
Error parsing JSON Error Domain=NSCocoaErrorDomain Code=3840 "JSON text did not start with array or object and option to allow fragments not set." UserInfo={NSDebugDescription=JSON text did not start with array or object and option to allow fragments not set.}
What am I doing wrong?
as you are writing this,
let json = try JSONDecoder().decode([ITEMS].self, from: data)
it must ask for array of ITEMS to be parsed, but your JSON is just an object not an array (It starts with {ITEMS: [array_here]}).
For solving this issue, you need to make your struct like below and parse data into that object.
struct ITEMS: Codable {
let CODE:String
let CAT_ID:String
let SUB_ID:String
let PRODUCT_AR:String
let PRODUCT_EN:String
let OLD_PRICE:String
let NEW_PRICE:String
let UNIT:String
let BARCODE:String
let THE_DATE:String
let TIME:String
}
struct MyAPIData: Codable {
let ITEMS: [ITEMS]
}
Now parse data using below line of code,
let json = try JSONDecoder().decode(MyAPIData.self, from: data)
There were wrong types used in your struct, here is the fixed struct
struct ITEM: Codable {
let CODE:Int // not String
let CAT_ID:Int // not String
let SUB_ID:Int? // its null in JSON, use either Int? or String?
let PRODUCT_AR: String
let PRODUCT_EN: String
let OLD_PRICE:Double // not String
let NEW_PRICE:Double // not String
let UNIT:String?
let BARCODE:String
let THE_DATE:String
let TIME:String
}
Since you JSON has a root element ITEMS, you need to decode using this struct
struct BaseItems: Codable {
let ITEMS: [ITEM] // Actual array of items are within this JSON element
}
Usage:
do {
let decoded = try JSONDecoder().decode(BaseItems.self, from: data)
print(decoded.ITEMS)
} catch {
print(error)
}