JSON Request for an UIImage from WordPress Post - json

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

Related

Swift JSON with dynamic Keys

I am trying to parse JSON using Swift, which has dynamic keys. Tried several ways but still did not find the solution. Could you please help me ?
I am trying to parse NativeName, which is dynamic based on which language country name is present.
API: https://restcountries.com/v3.1/all
struct Objects: Codable {
let name: Name
let cca2 : String
let flag: String
}
struct Name: Codable {
let common, official: String
let nativeName: NativeName
}
struct NativeName: Codable {
var deu : Deu
}
struct Deu: Codable {
let official, common: String?
}
and here is JSON Model:
class ParsingService {
static let shared = ParsingService()
func fetchData() {
guard let url = URL(string: "https://restcountries.com/v3.1/all") else {
print("DEBUG: URL is nill")
return}
let session = URLSession.shared
let task = session.dataTask(with: url) { data, _, error in
guard let retrievedData = data, error == nil else {
print("DEBUG: Data is not available")
return}
print("DEBUG: Data is available \(retrievedData)")
guard let decodedData = self.JSONParsing(inputData: retrievedData) else {
print("DEBUG: Missing data")
return}
print("DEBUG: Data is there")
print("DEBUG: \(decodedData[0].cca2)")
print("DEBUG: \(decodedData[0].flag)")
print("DEBUG: \(decodedData[0].name.nativeName.deu.official)")
DispatchQueue.main.async {
print(decodedData.currencies)
}
}
task.resume()
}
func JSONParsing(inputData: Data)-> [Objects]? {
let decoder = JSONDecoder()
do {
let data = try? decoder.decode([Objects].self, from: inputData)
return data
} catch {
print("DEBUG: Cannot get data")
return nil
}
}
}
you could try this approach:
struct ContentView: View {
var body: some View {
Text("testing")
.onAppear {
fetchData() { results in
print("---> results: \(results.count) \n")
for i in 0..<3 {
print("---> results[\(i)]: \(results[i].name.nativeName)")
}
}
}
}
// todo deal with errors
func fetchData(completion: #escaping ([Objects]) -> Void) {
let url = URL(string: "https://restcountries.com/v3.1/all")
guard let url = url else { completion([]); return }
URLSession.shared.dataTask(with: url) { (data, response, error) in
guard let data = data else { completion([]); return }
do {
let results = try JSONDecoder().decode([Objects].self, from: data)
completion(results)
}
catch {
print("Error: \(error)")
completion([])
}
}.resume()
}
}
struct Objects: Codable {
let name: Name
let cca2 : String
let flag: String
}
struct Deu: Codable {
let official, common: String?
}
struct Name: Codable {
let common, official: String
let nativeName: NativeName? // <-- here
}
// -- here
struct NativeName: Codable {
var lang: [String: Deu]
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
lang = try container.decode([String: Deu].self)
}
func encode(to encoder: Encoder) throws {
// todo
}
}
Note, you could also use a Tuple, such as var lang: (key: String, value: Deu)

swiftUI / Xcode 12 fetch data from server

I'm very new to swiftUI and have been working through the landscapes app tutorial.
I have been trying to switch the data source from a bundled JSON file to a remote JSON source but have so far been lost on how to integrate what I've learnt about the URLSession with the tutorials load code.
Apple's code:
final class ModelData: ObservableObject {
#Published var landmarks: [Landmark] = load("landmarkData.json")
// #Published var landmarks: [Landmark] = apiCall.getLocations(locations)
}
func load<T: Decodable>(_ filename: String) -> T {
let data: Data
guard let file = Bundle.main.url(forResource: filename, withExtension: nil)
else {
fatalError("Couldn't find \(filename) in main bundle.")
}
do {
data = try Data(contentsOf: file)
} catch {
fatalError("Couldn't load \(filename) from main bundle:\n\(error)")
}
do {
let decoder = JSONDecoder()
return try decoder.decode(T.self, from: data)
} catch {
fatalError("Couldn't parse \(filename) as \(T.self):\n\(error)")
}
}
What I have to load from the remote source:
struct Location: Codable, Identifiable {
let id = UUID()
let country: String
let name: String
}
class apiCall {
func getLocations(completion:#escaping ([Location]) -> ()) {
guard let url = URL(string: "https://overseer.cyou/heritage/heritageData.json") else { return }
URLSession.shared.dataTask(with: url) { (data, _, _) in
let locations = try! JSONDecoder().decode([Location].self, from: data!)
print(locations)
DispatchQueue.main.async {
completion(locations)
}
}
.resume()
}
}
Can anyone show me how I go about doing this, ideally from a complete beginners point of view?
// framework support
import SwiftUI
import Combine
// List view setup
struct LocationsView: View {
#ObservedObject var viewModel = LocationModel()
var body: some View {
List(viewModel.locations) { location in
HStack {
VStack(alignment: .leading) {
Text(location.name)
.font(.headline)
Text(location.country)
.font(.subheadline)
}
}
}
}
}
// Location model
struct Location: Codable, Identifiable {
var id = UUID()
let country: String
let name: String
let locationId: Int = 0
enum CodingKeys: String, CodingKey {
case locationId = "id"
case country
case name
}
}
// Location view model class
class LocationModel: ObservableObject {
#Published var locations: [Location] = []
var cancellationToken: AnyCancellable?
init() {
getLocations()
}
}
extension LocationModel {
func getLocations() {
cancellationToken = self.request("https://overseer.cyou/heritage/heritageData.json")?
.mapError({ (error) -> Error in
print(error)
return error
})
.sink(receiveCompletion: { _ in },
receiveValue: {
self.locations = $0
})
}
// API request
private func request(_ path: String) -> AnyPublisher<[Location], Error>? {
guard let url = URL(string: path)
else { return nil }
let request = URLRequest(url: url)
return apiCall.run(request)
.map(\.value)
.eraseToAnyPublisher()
}
}
// API setup
struct apiCall {
struct Response<T> {
let value: T
let response: URLResponse
}
static func run<T: Decodable>(_ request: URLRequest) -> AnyPublisher<Response<T>, Error> {
return URLSession.shared
.dataTaskPublisher(for: request)
.tryMap { result -> Response<T> in
let value = try JSONDecoder().decode(T.self, from: result.data)
return Response(value: value, response: result.response)
}
.receive(on: DispatchQueue.main)
.eraseToAnyPublisher()
}
}

Need help by parsing with JSON

I am a total beginner with programming and Swift. One of the features my app should have is a label of the current temperature in New York City. The problem is I don't know if I am on the right path with my code. I tried things from many videos and articles but nothing seems to work. I am sure the answer is very easy. Thanks for any help!
I use the darksky api. This is my current code.
import UIKit
struct MyGitHub: Codable {
let temperature: Int
private enum CodingKeys: String, CodingKey {
case temperature
}
}
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
guard let gitUrl = URL(string: "here is my darksky api") else { return }
URLSession.shared.dataTask(with: gitUrl) { (data, response, error) in
guard let data = data else { return }
do {
let decoder = JSONDecoder()
let gitData = try decoder.decode(MyGitHub.self, from: data)
print (gitData.temperature)
} catch let err {
print ("Err", err)
}
} .resume()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
You need an umbrella struct for the root object and the value for key temperature is Double, not Int
struct Root: Decodable {
let currently : MyGitHub
}
struct MyGitHub: Decodable {
let temperature: Double
}
...
let gitData = try decoder.decode(Root.self, from: data)
print(gitData.currently.temperature)
You can try this
Alamofire.request(urlStr, method: .get, parameters:nil, encoding: JSONEncoding.default).responseJSON { response in
if let json = response.result.value as? [String:Any] {
if let main = json["currently"] as? [String:Any] {
if let temp = main["temperature"] as? NSNumber
{
// set lbl here
print(temp)
}
}
}
}
OR
struct Currently : Codable {
var currently:InnerItem
}
struct InnerItem : Codable {
var temperature:Double
}
with
guard let gitUrl = URL(string:urlStr) else { return }
URLSession.shared.dataTask(with: gitUrl) { (data, response, error) in
guard let data = data else { return }
do {
let decoder = JSONDecoder()
let gitData = try decoder.decode(Currently.self, from: data)
print ("sdhjsdjhjhdshjshjsjhddhsj" , gitData.currently.temperature)
} catch let err {
print ("Err", err)
}
} .resume()

Parsing JSON with Swift 4 Decodable

I have been getting the following error every time I try to parse JSON in my program. I can't seem to figure it out.
"Expected to decode String but found an array instead.", underlyingError: nil
Here is the code I have been struggling with:
struct Book: Decodable {
let id: Int
let title: String
let chapters: Int
var pages: [Page]?
}
struct Page: Decodable {
let id: Int
let text: [String]
}
struct Chapter: Decodable {
var chapterNumber: Int
}
func fetchJSON() {
let urlString = "https://api.myjson.com/bins/kzqh3"
guard let url = URL(string: urlString) else { return }
URLSession.shared.dataTask(with: url) { (data, _, err) in
if let err = err {
print("Failed to fetch data from", err)
return
}
guard let data = data else { return }
do {
let decoder = JSONDecoder()
let books = try decoder.decode([Book].self, from: data)
books.forEach({print($0.title)})
} catch let jsonErr {
print("Failed to parse json:", jsonErr)
}
}.resume()
}
Are you sure that's the real error message?
Actually the error is supposed to be
"Expected to decode String but found a dictionary instead."
The value for key text is not an array of strings, it's an array of dictionaries
struct Page: Decodable {
let id: Int
let text: [[String:String]]
}
The struct Chapter is not needed.
Alternatively write a custom initializer and decode the dictionaries containing the chapter number as key and the text as value into an array of Chapter
struct Book: Decodable {
let id: Int
let title: String
let chapters: Int
let pages: [Page]
}
struct Page: Decodable {
let id: Int
var chapters = [Chapter]()
private enum CodingKeys : String, CodingKey { case id, chapters = "text" }
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
id = try container.decode(Int.self, forKey: .id)
var arrayContainer = try container.nestedUnkeyedContainer(forKey: .chapters)
while !arrayContainer.isAtEnd {
let chapterDict = try arrayContainer.decode([String:String].self)
for (key, value) in chapterDict {
chapters.append(Chapter(number: Int(key)!, text: value))
}
}
}
}
struct Chapter: Decodable {
let number : Int
let text : String
}
This is working:
import UIKit
class ViewController: UIViewController {
private var books = [Book]()
struct Book: Decodable {
let id: Int
let title: String
let chapters: Int
var pages: [Page]
}
struct Page: Decodable {
let id: Int
let text: [[String:String]]
}
override func viewDidLoad() {
super.viewDidLoad()
fetchJSON()
}
func fetchJSON() {
let urlString = "https://api.myjson.com/bins/kzqh3"
guard let url = URL(string: urlString) else { return }
URLSession.shared.dataTask(with: url) { data, response, error in
if error != nil {
print(error!.localizedDescription)
return
}
guard let data = data else { return }
do {
let decoder = JSONDecoder()
self.books = try decoder.decode([Book].self, from: data)
DispatchQueue.main.async {
for info in self.books {
print(info.title)
print(info.chapters)
print(info.pages[0].id)
print(info.pages[0].text)
print("-------------------")
}
}
} catch let jsonErr {
print("something wrong after downloaded: \(jsonErr) ")
}
}.resume()
}
}
// print
//Genesis
//50
//1
//[["1": "In the beg...]]
//-------------------
//Exodus
//40
//2
//[["1": "In the beginning God created...]]
//
If you need to print the value of each chapter in the book, I can use something like this:
import UIKit
class ViewController: UIViewController {
private var books = [Book]()
struct Book: Decodable {
let id: Int
let title: String
let chapters: Int
var pages: [Page]
}
struct Page: Decodable {
let id: Int
let text: [[String:String]]
}
override func viewDidLoad() {
super.viewDidLoad()
fetchJSON()
}
func fetchJSON() {
let urlString = "https://api.myjson.com/bins/kzqh3"
guard let url = URL(string: urlString) else { return }
URLSession.shared.dataTask(with: url) { data, response, error in
if error != nil {
print(error!.localizedDescription)
return
}
guard let data = data else { return }
do {
let decoder = JSONDecoder()
self.books = try decoder.decode([Book].self, from: data)
DispatchQueue.main.async {
for info in self.books {
print(info.title)
print(info.chapters)
print(info.pages[0].id)
//print(info.pages[0].text)
for cc in info.pages[0].text {
for (key, value) in cc {
print("\(key) : \(value)")
}
}
print("-------------------")
}
}
} catch let jsonErr {
print("something wrong after downloaded: \(jsonErr) ")
}
}.resume()
}
}
//Genesis
//50
//1
//1 : In the beginning God ...
//2 : But the earth became waste...
//.
//.
//.
//31 : And God saw everything...
//-------------------
//Exodus
//40
//2
//1 : In the beginning God...
//2 : But the earth became...
//.
//.
//.
//31 : And God saw everything

JSON Parsing in Swift 4

I'm quite new to Swift and i am trying to return a string from my parsed JSON array. I'm not sure how i use the "arrayString" var in another class as i haven't "declared" it in the other class. I assume it has something to do with it being in URLSession. Any tips on what i could do?
struct Games: Decodable {
let videoLink: String
}
class BroadService: NSObject {
static let sharedInstance = BroadService()
func fetchBroadcasts(completion: #escaping ([Games]) -> ()) {
let jsonUrlString = "LINK IS HERE."
guard let url = URL(string: jsonUrlString) else { return }
URLSession.shared.dataTask(with: url) { (data, response, err) in
guard let data = data else { return }
do {
let json = try JSONDecoder().decode([Games].self, from: data)
let arrayString = (json[linkcell].videoLink)
} catch let jsonErr {
print("Error serializing json:", jsonErr)
}
}.resume()
}
This is my code at the minute, It's not the most neat thing in the world, but it gets the job done.
There are several issues with your code:
You needlessly extend NSObject.
You never call the completion block.
arrayString is not an array.
Your completion block should at least make the Games array optional to indicate an error.
Here's how your code should be:
struct Games: Decodable {
let videoLink: String
}
class BroadService {
static let sharedInstance = BroadService()
func fetchBroadcasts(completion: #escaping ([Games]?) -> ()) {
let jsonUrlString = "LINK IS HERE."
guard let url = URL(string: jsonUrlString) else {
completion(nil)
return
}
URLSession.shared.dataTask(with: url) { (data, response, err) in
guard let data = data else {
completion(nil)
return
}
do {
let games = try JSONDecoder().decode([Games].self, from: data)
completion(games)
} catch let jsonErr {
print("Error deserializing json:", jsonErr)
completion(nil)
}
}.resume()
}
}
Then you can call it as follows:
BroadService.sharedInstance.fetchBroadcasts { (games) in
if let games = games {
// Process all games if needed
for game in games {
let videoLink = game.videoLink
// do something
}
// Or access a specific game:
let game = games[someIndex]
let videoLink = game.videoLink
// do something
}
}