Twice JSON Request instead of one - json

My JSON request works fine but every time I do a request the network monitor shows me there are two requests made. What is wrong here? How can I optimize it to just one?
I tried to find the reason for this and it looks like it has to happen here with this request.
struct networkGoogleApi {
static var testVariable = [Items]()
static var gewählteKategorie = 0
mutating func fetchPoi(geoCordinate: String) {
var baseUrlToApiConnection = ""
baseUrlToApiConnection = {
switch networkGoogleApi.gewählteKategorie {
case 0:
return
"first url"
case 1:
return "other url"
default:
return "error"
}
}()
let urlString = "\(baseUrlToApiConnection)?at=\(geoCordinate)"
print(urlString)
performRequest(urlString: urlString)
}
func performRequest(urlString: String) {
if let url = URL(string: urlString) {
let session = URLSession(configuration: .default)
let task = session.dataTask(with: url) { data, response, error in
if error != nil {
print(error!)
return
}
if let safeData = data {
parseJSON(poiData: safeData)
}
}
//4. Start the task
task.resume()
}
}
}
func parseJSON(poiData: Data) {
let decoder = JSONDecoder()
do {
let decodedData = try decoder.decode(POIData.self, from: poiData)
networkGoogleApi.testVariable = decodedData.items
//print(networkGoogleApi.testVariable)
} catch {
print(error)
}
}
Breakpoint Event added.

Related

Swift - Creating a stack (Text view) from a function that returns an array

I am trying to create a list of text objects out of a function that returns an array of params. Everything seems to be working fine, getting the data, console shows the correct results, except the list itself which remains empty.
The function call:
import UIKit
import SwiftUI
struct SubdomainsList: View {
#State var SubDomains = VTData().funky(XDOMAIN: "giphy.com")
var body: some View {
VStack {
List{
Text("Subdomains")
ForEach(SubDomains, id: \.self) { SuDo in
Text(SuDo)
}
}
}
}
}
struct SubdomainsList_Previews: PreviewProvider {
static var previews: some View {
SubdomainsList()
}
}
The Json handlers:
struct VTResponse: Decodable {
let data: [VT]
}
struct VT: Decodable {
var id: String
}
The class:
class VTData {
func funky (XDOMAIN: String) -> Array<String>{
var arr = [""]
getDATA(XDOMAIN: "\(XDOMAIN)", userCompletionHandler: { (SubDomain) in
print(SubDomain)
arr.append(SubDomain)
return SubDomain
})
return arr
}
func getDATA(XDOMAIN: String, userCompletionHandler: #escaping ((String) -> String)) {
let token = "<TOKEN>"
guard let url = URL(string: "https://www.lalalla.com/subdomains") else {fatalError("Invalid URL")}
var request = URLRequest(url: url)
request.httpMethod = "GET"
request.setValue("\(token)", forHTTPHeaderField: "x-apikey")
let task = URLSession.shared.dataTask(with: request, completionHandler: { data, response, error in
guard let data = data else { return }
let decoder = JSONDecoder()
let result = try? decoder.decode(VTResponse.self, from: data)
if let result = result {
for SubDo in result.data {
let SubDomain = SubDo.id
userCompletionHandler(SubDomain)
}
}
else {
fatalError("Could not decode")
}
})
task.resume()
}
}
I'm getting no errors whatsoever, and the console output shows the correct results:
support.giphy.com
cookies.giphy.com
media3.giphy.com
i.giphy.com
api.giphy.com
developers.giphy.com
media.giphy.com
x-qa.giphy.com
media2.giphy.com
media0.giphy.com
It is also worth mentioning that when I add print(type(of: SubDomain)) to the code I'm getting a String rather than an array.
The preview:
preview
Any ideas?
try this approach, again, to extract the list of subdomain from your API, and display them in
a List using the asynchronous function getDATA(...):
class VTData {
// `func funky` is completely useless, remove it
func getDATA(XDOMAIN: String, completion: #escaping ([String]) -> Void) { // <-- here
let token = "<TOKEN>"
guard let url = URL(string: "https://www.virustotal.com/api/v3/domains/\(XDOMAIN)/subdomains") else {
print("Invalid URL")
return
}
var request = URLRequest(url: url)
request.httpMethod = "GET"
request.setValue("\(token)", forHTTPHeaderField: "x-apikey")
URLSession.shared.dataTask(with: request) { data, response, error in
guard let data = data else { return } // todo return some error msg
do {
let results = try JSONDecoder().decode(VTResponse.self, from: data)
return completion(results.data.map{ $0.id }) // <-- here
} catch {
print(error) // <-- here important
}
}.resume()
}
}
struct SubdomainsList: View {
#State var subDomains: [String] = [] // <--- here
var body: some View {
VStack {
List{
Text("Subdomains")
ForEach(subDomains, id: \.self) { SuDo in
Text(SuDo)
}
}
}
.onAppear {
// async function
VTData().getDATA(XDOMAIN: "giphy.com") { subs in // <--- here
subDomains = subs
}
}
}
}

How can i get authorization to retrieve data from API Swift?

I'm trying to retrieve some data from an API, but I got an error: "The given data was not valid JSON ", Status code: 401
I think that is an authentication problem. How can I set the auth credentials to make the GET request?
This is the code for retrieving the data from the JSON.
func loadData()
{
guard let url = URL(string: getUrl) else { return }
URLSession.shared.dataTask(with: url) { data, res, err in
do {
if let data = data {
let result = try JSONDecoder().decode([ItemsModel].self, from: data)
DispatchQueue.main.async {
self.items = result
}
} else {
print(" No Data ")
}
} catch( let error)
{
print(res)
print(String(describing: error))
}
}.resume()
}
This is the code for the view :
struct GetView: View {
#ObservedObject var viewModel = ViewModel()
var body: some View {
NavigationView {
VStack {
List(viewModel.items, id: \.id) { item in
Text(item.year)
}
} .onAppear(perform: {
viewModel.loadData()
})
.navigationTitle("Data")
}
}
}
To handle authentication you must implement a delegate for your URLSession:
func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: #escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
completionHandler(.performDefaultHandling, challenge.proposedCredential)
}
However, your 401 error may be due to your code not sending a valid GET request to the server. You probably want to decode the 'res' value to determine the status code:
if let response = res as? HTTPURLResponse {
if response.statusCode != 200 {
// data may be JSON encoded but you should get some for
// statusCode == 401
}
}
Without knowing the kind of service you are connecting to it is hard to speculate if you need a GET or a POST. The URL you use may require a query parameter.
I found the solution. This is the code for Basic Auth :
func loadData() {
//Credentials
let username = ""
let password = ""
let loginString = "\(username):\(password)"
let loginData = loginString.data(using: String.Encoding.utf8)!
let base64LoginString = loginData.base64EncodedString()
//Request
guard let url = URL(string: getUrl) else {return}
var request = URLRequest(url: url)
request.httpMethod = "GET"
request.setValue("Basic \(base64LoginString)", forHTTPHeaderField: "Authorization")
//Setup Session
let task = URLSession.shared.dataTask(with: request) { (data, response, error) in
do {
if let data = data {
print(String(data: data, encoding: .utf8)!)
let result = try JSONDecoder().decode([ItemsModel].self, from: data)
DispatchQueue.main.async {
self.items = result
}
}
else {
print(" No Data ")
}
} catch( let error)
{
print(String(describing: error))
}
}
task.resume()
}

Issue with Codable object to JSON conversion

Apologies if this is a basic question, I am new using Apis and JSON in Swift. I am attempting to submit a post request but am receiving:
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Invalid type in JSON write (__SwiftValue)'.
I believe that this is due to a incorrect/unconvertible type but I have tried multiple different permutations of the variables I am passing and it continues to fail.
Here is my ContentView:
struct ContentView: View {
#State var town: Space = Space(title: "test city", description: "is this working")
var body: some View {
Button(action: {
Api.postRequest(param: ["space" : town], urlString: Api.spacePostUrl) { (update) in
print("\(update)")
}
}) {
Text("Post Request")
}
}
}
The underlying data struct:
struct Space: Codable {
var title: String
var description: String
}
And my attempted API call:
class Api {
static let spacePostUrl = "http://localhost:3001/spaces"
static let spaceGetUrl = "http://localhost:3001/"
static func postRequest(param: [String : Codable], urlString: String, completion: #escaping (Int) -> ()) {
guard let url = URL(string: urlString) else { return }
let body = try? JSONSerialization.data(withJSONObject: param)
var request = URLRequest(url: url)
request.httpBody = body
request.httpMethod = "POST"
URLSession.shared.dataTask(with: request) { (data, request, error) in
guard let update = data else { return }
do {
let update = try JSONDecoder().decode(Int.self, from: update)
DispatchQueue.main.async {
completion(update)
}
}
catch {
print(error)
}
}
}
}
class Api {
static let spacePostUrl = "http://localhost:3001/spaces"
static let spaceGetUrl = "http://localhost:3001/"
static func postRequest(param: [String : Codable], urlString: String, completion: #escaping (Int) -> ()) {
guard let url = URL(string: urlString) else { return }
let body = try? JSONSerialization.data(withJSONObject: param)
var request = URLRequest(url: url)
request.httpBody = body
request.httpMethod = "POST"
let task = URLSession.shared.dataTask(with: request) { (data, request, error) in
guard let update = data else { return }
do {
let update = try JSONDecoder().decode(Space.self, from: update)
DispatchQueue.main.async {
completion(update)
}
}
catch {
print(error)
}
}
task.resume()
}
}

How write parseJSON universal function? Swift

I have different structs as my dataModels.
when i want to parse data with JsonDecoder().decode i need set a .Type.self in .decoder(SomeType.self , from: data)
I want write a support function which can return right Type respectively.
something like this
But I don't know how...
func check<T>(string: String) -> T
if string == "something" {
return Something.type
}
func parseJSON(from data: Data , with address: String)-> Codable? {
let type = check(string: address)
let decoder = JSONDecoder()
do {
let decodedData = try decoder.decode(type.self, from: data)
return decodedData
} catch let error {
print(error)
return nil
}
}
WHEN I WRITE THE code below, Everything works fine. But I always have to write parseJSON func with little difference over and over again
func fetch(from adress: String) {
guard let url = URL(string: adress) else {print("can't get URL from current urlAdress"); return}
let json = makeHttpBody(from: adress)
let jsonData = try? JSONSerialization.data(withJSONObject: json)
var request = URLRequest(url: url , cachePolicy: .useProtocolCachePolicy)
request.httpMethod = K.post
request.setValue(K.contentType, forHTTPHeaderField:K.applicationJson)
request.timeoutInterval = Double.infinity
request.httpBody = jsonData
let session = URLSession(configuration: .default)
let task = session.dataTask(with: request) { (data, responce, error) in
if error != nil {
print(error!.localizedDescription)
}
if let safeData = data {
if adress == K.balanceUrl {
if let parsedData = self.parseJsonBalance(from: safeData) {
self.delegate?.didUpdateData(with: self, with: parsedData)
}
} else if adress == K.projectsUrl {
if let parsedData = self.parseJsonProject(from: safeData) {
self.delegate?.didUpdateData(with: self, with: parsedData)
}
}
}
}
task.resume()
}
func makeHttpBody(from StringData: String) -> [String: Any] {
switch StringData {
case K.balanceUrl:
return K.authorization
case K.projectsUrl:
return K.projects
default:
return ["none" : "none"]
}
}
Your approach cannot work, you have to use a generic type constrained to Decodable. Checking for strings at runtime is not a good practice.
This is a reduced version of your code, the error is handed over to the caller
func parseJSON<T : Decodable>(from data: Data) throws -> T {
return try JSONDecoder().decode(T.self, from: data)
}
Then you can write (the return type must be annotated)
let result : [Foo] = try parseJSON(from: data)
Or if you want to specify the type in the function
func parseJSON<T : Decodable>(from data: Data, type : T.Type) throws -> T {
return try JSONDecoder().decode(T.self, from: data)
}
let result = try parseJSON(from: data, type: [Foo].self)

JSON Request for an UIImage from WordPress Post

I would like to download a picture of a json file online. This image should be displayed in a UIImageView.
At least I know that I need a JSON request but as I can bring the image in the ImageView I do not know
I've tried to download the image with a JSON request, but I have not managed it yet
My Code looks like this:
class HomeControllerTableView: UITableViewController{
var coursesPicture = [CoursePicture]()
var courseURL = [CourseURL]()
// MARK: - CoursePicture
struct CoursePicture: Codable {
let links: LinksPICTURE
enum CodingKeys: String, CodingKey {
case links = "_links"
}
}
// MARK: - Links
struct LinksPICTURE: Codable {
let wpFeaturedmedia: [WpFeaturedmedia]
enum CodingKeys: String, CodingKey {
case wpFeaturedmedia = "wp:featuredmedia"
}
}
// MARK: - WpFeaturedmedia
struct WpFeaturedmedia: Codable {
let href: String
}
struct CourseURL: Codable {
let guid: GUIDURL
}
// MARK: - GUID
struct GUIDURL: Codable {
let rendered: String
}
#IBOutlet weak var imageView: UIImageView!
var hello = ""
var hello2 = ""
override func viewDidLoad() {
super.viewDidLoad()
imageView.autoresizingMask = [.flexibleWidth, .flexibleHeight, .flexibleBottomMargin, .flexibleTopMargin, .flexibleBottomMargin]
imageView.contentMode = .scaleAspectFill
tableView.isScrollEnabled = false
tableView.rowHeight = UITableView.automaticDimension
tableView.backgroundColor = .clear
self.tableView.backgroundView = imageView
tableView.addSubview(imageView)
fetchJSON()
fetchJSONPicture()
request()
fetchJSONURL()
}
public func fetchJSONPicture() {
let urlString = "EXAMPLE_URL"
guard let url = URL(string: urlString) else { return }
URLSession.shared.dataTask(with: url) { (data, _, err) in
if let err = err {
print("Failed to get data from url:", err)
return
}
guard let data = data else { return }
do {
self.coursesPicture = [try JSONDecoder().decode(CoursePicture.self, from: data)]
DispatchQueue.main.async {
self.tableView.reloadData()
}
} catch let jsonErr {
print("Failed to decode:", jsonErr)
}
}.resume()
}
public func fetchJSONURL() {
let urlString = hello
guard let url = URL(string: urlString) else { return }
URLSession.shared.dataTask(with: url) { (data, _, err) in
DispatchQueue.main.async {
if let err = err {
print("Failed to get data from url:", err)
return
}
guard let data = data else { return }
do {
self.courseURL = [try JSONDecoder().decode(CourseURL.self, from: data)]
DispatchQueue.main.async {
self.tableView.reloadData()
}
} catch let jsonErr {
print("Failed to decode:", jsonErr)
}
}
}.resume()
if let url = URL(string: hello2) {
URLSession.shared.dataTask(with: url) { (data, urlResponse, error) in
if let data = data {
DispatchQueue.main.async {
self.imageView.image = UIImage(data: data)
}
}
}.resume()
}
}
}
func request() {
let coursePicture = coursesPicture[0]// Index out of Range error
let courseUrl = courseURL[0] // Index out of Range error
hello = coursePicture.links.wpFeaturedmedia[0].href
hello2 = courseUrl.guid.rendered
}
}
The JSON Request should bring me an URL from the JSON File and store it in a global variable. After that i use the global variable to get the URL from the Image.
The Picture that i would fetch is in a WordPress Post as contributing picture.
I would like to make this code as dynamic as possible but the problem is that i became an error that is called "Index out of range".
Can me help someone maybe?
Thanks