Parse Youtube api in Swift 4 using Alamofire - ios

I'm trying to parse Youtube api in using Alamofire in Swift 4
so far i did get the results and all fine but i'm having trouble accessing the "items"
so basically i want to access title, description, medium thumbnails url and resultsPerPage.
i tried a lot of solutions but non if them worked for me specially i'm using Swift4 and Alamofire
JSON:
{
"kind":"youtube#searchListResponse",
"etag":"\"XI7nbFXulYBIpL0ayR_gDh3eu1k/N6oV8CScLhAtqc_fDnA3Nw4U3RA\"",
"nextPageToken":"CBkQAA",
"regionCode":"US",
"pageInfo":{
"totalResults":922098,
"resultsPerPage":25
},
"items":[
{
"kind":"youtube#searchResult",
"etag":"\"XI7nbFXulYBIpL0ayR_gDh3eu1k/Oxu5v7t2PHcDK4wvSo-xsIp3Raw\"",
"id":{ },
"snippet":{
"publishedAt":"2011-03-21T08:32:25.000Z",
"channelId":"UC1r4VtVE__5K6c_L_3Vlxxg",
"title":"fouseyTUBE",
"description":"",
"thumbnails":{
"default":{
"url":"https://yt3.ggpht.com/-oBs78-0JLws/AAAAAAAAAAI/AAAAAAAAAAA/zKWHSghRD3U/s88-c-k-no-mo-rj-c0xffffff/photo.jpg"
},
"medium":{
"url":"https://yt3.ggpht.com/-oBs78-0JLws/AAAAAAAAAAI/AAAAAAAAAAA/zKWHSghRD3U/s240-c-k-no-mo-rj-c0xffffff/photo.jpg"
},
"high":{
"url":"https://yt3.ggpht.com/-oBs78-0JLws/AAAAAAAAAAI/AAAAAAAAAAA/zKWHSghRD3U/s800-c-k-no-mo-rj-c0xffffff/photo.jpg"
}
},
"channelTitle":"fouseyTUBE",
"liveBroadcastContent":"none"
}
}
]
}
My Code :
let url = "https://www.googleapis.com/youtube/v3/search"
let parameters = ["q": searchText, "maxResults": 25, "part": "snippet", "type":"video", "key": "MY_YOUTUBE_API_KEY"] as [String : Any]
Alamofire.request(url, method: .get, parameters: parameters, encoding: URLEncoding.default, headers: nil).responseData { (dataResponse) in
if let err = dataResponse.error {
print("Failed to contact server", err)
return
}
guard let data = dataResponse.data else {return}
do{
let searchResult = try
JSONDecoder().decode(SearchResults.self, from: data)
print("Results Count:", searchResult.kind)
searchResult.items.forEach({ (data) in
print(searchResult.items["data"]["items"])
})
self.musics = searchResult.items
self.tableView.reloadData()
}catch let decodeErr {
print("Failed to Descode: ", decodeErr)
}
}
}
struct SearchResults: Decodable{
let kind: String
let items: [Music]
}
Music.Swift file
struct Music: Decodable {
var etag: String?
var kind: String?
//let time: String
}

I would suggest creating decodable struct for each nested elements like so,
struct PageInfo: Decodable {
var totalResults = 0
var resultsPerPage = 0
}
struct Snippet: Decodable {
var channelId = ""
var title = ""
var description = ""
var channelTitle = ""
var thumbnails: Thumbnail
}
struct ChannelURL: Decodable {
var url = ""
}
struct Thumbnail: Decodable {
var medium: ChannelURL
var high: ChannelURL
}
struct Item: Decodable {
var kind = ""
var etag = ""
var snippet: Snippet
}
struct Result: Decodable {
var kind = ""
var etag = ""
var pageInfo: PageInfo
var items: [Item]
}
And then proceed with the decoding. The following decoding works with your response above.
do {
let decoded = try JSONDecoder().decode(Result.self, from: data)
debugPrint(decoded)
//Now access the data
print(decoded.pageInfo.resultsPerPage) // 25
//since the items is array we take first for now
if let firstItem = decoded.items.first {
//to print the first one
print(firstItem.snippet.channelTitle) // "fouseyTUBE"
//same for URL
print(firstItem.snippet.thumbnails.medium.url) // https://yt3.ggpht.com/-oBs78-0JLws/AAAAAAAAAAI/AAAAAAAAAAA/zKWHSghRD3U/s240-c-k-no-mo-rj-c0xffffff/photo.jpg
}
} catch {
debugPrint("\(error.localizedDescription)")
}
This is the best case scenario were all data is present. So you will have to modify your structure accordingly making some values ``

Related

Swift JSON Nested out put for ListView

Iam trying to display the contents of the result.
The Data is returned as JSON Array.
I created a view model "Stocks" and want to access the "results". Currently it compiles but the data does not show up.
Help would be highly appreciated
import SwiftUI
struct Stocks: Hashable, Codable{
var results: [Results]
var status: String
struct Results: Hashable, Codable{
var ticker: String
var name: String
var market: String
var locale: String
var primary_exchange: String
var type: String
var active: Bool
var currency_name: String
var cik: String
var composite_figi: String
var share_class_figi: String
var last_update_utc: String
}
}
class ViewModel: ObservableObject{
#Published var stocks: [Stocks] = []
func fetch(){
guard let url = URL(string: "https://api.polygon.io/v3/reference/tickers?market=stocks&active=true&apiKey=<apikey>") else{return}
let task = URLSession.shared.dataTask(with: url) {[weak self]data, _, error in
guard let data = data, error == nil else{
return
}
// Convert JSON
do{
let stocks = try JSONDecoder().decode([Stocks].self, from: data)
DispatchQueue.main.async{
self?.stocks = stocks
}
}
catch{
print(error)
}
}
task.resume()
}
}
struct ContentView: View {
#StateObject var viewModel = ViewModel()
var body: some View {
NavigationView{
List{
ForEach(viewModel.stocks, id: \.self){resu in
ForEach(resu.results, id: \.self){st in
Text(st.currency_name)
}
}
}
}.navigationTitle("Stocks")
.onAppear{
viewModel.fetch()
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
Here is the Response object:
{
"results": [
{
"ticker": "A",
"name": "Agilent Technologies Inc.",
"market": "stocks",
"locale": "us",
"primary_exchange": "XNYS",
"type": "CS",
"active": true,
"currency_name": "usd",
"cik": "0001090872",
"composite_figi": "BBG000C2V3D6",
"share_class_figi": "BBG001SCTQY4",
"last_updated_utc": "2022-12-20T00:00:00Z"
},
{
"ticker": "AA",
"name": "Alcoa Corporation",
"market": "stocks",
"locale": "us",
"primary_exchange": "XNYS",
"type": "CS",
"active": true,
"currency_name": "usd",
"cik": "0001675149",
"composite_figi": "BBG00B3T3HD3",
"share_class_figi": "BBG00B3T3HF1",
"last_updated_utc": "2022-12-20T00:00:00Z"
},
I created a view model "Stocks" and want to access the "results". Currently it compiles but the data does not show up.
The naming of your structs is largely confusing.
According to the JSON you are going to receive one root object containing an array of Stock (supposed to be named in singular form) objects for key results.
And there is a struct member last_update_utc which does not match the key last_updated_utc.
Name your structs this way, I renamed the struct members as camelCase and as constants (let) and you can decode lastUpdatedUtc as Date with the .iso8601 strategy
struct Response: Hashable, Decodable {
let results: [Stock]
let status: String
struct Stock: Hashable, Decodable {
let ticker: String
let name: String
let market: String
let locale: String
let primaryExchange: String
let type: String
let active: Bool
let currencyName: String
let cik: String
let compositeFigi: String
let shareClassFigi: String
let lastUpdatedUtc: Date
}
}
and decode the JSON
class ViewModel: ObservableObject{
#Published var stocks: [Response.Stock] = []
func fetch() {
guard let url = URL(string: "https://api.polygon.io/v3/reference/tickers?market=stocks&active=true&apiKey=<apikey>") else{return}
let task = URLSession.shared.dataTask(with: url) {[weak self] data, _, error in
if let error { print(error); return }
// Convert JSON
do{
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
decoder.dateDecodingStrategy = .iso8601
let response = try decoder.decode(Response.self, from: data!)
DispatchQueue.main.async {
self?.stocks = response.results
}
}
catch{
print(error)
}
}
task.resume()
}
}
I even recommend to use async/await
#MainActor
class ViewModel: ObservableObject{
#Published var stocks: [Response.Stock] = []
func fetch() {
guard let url = URL(string: "https://api.polygon.io/v3/reference/tickers?market=stocks&active=true&apiKey=<apikey>") else{return}
Task {
do {
let (data, _) = try await URLSession.shared.data(from: url)
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
decoder.dateDecodingStrategy = .iso8601
let response = try decoder.decode(Response.self, from: data)
self.stocks = response.results
} catch {
print(error)
}
}
}
}

SwiftUI : Can't receive API response

I'm trying to get a response from this link : https://zsr.octane.gg/players/5f5ae840c6cbf591c568a477 but it won't work and I can't figure why.
There is my ContentView.swift :
struct ContentView: View {
#State private var players = [Player]()
var body: some View {
List(players, id: \._id) { item in
VStack(alignment: .leading) {
Text(item.name)
.font(.headline)
Text(item.country)
}
}.task {
await loadData()
}
}
func loadData() async {
guard let url = URL(string: "https://zsr.octane.gg/players/5f5ae840c6cbf591c568a477") else {
print("URL invalide")
return
}
do {
let (data, _) = try await URLSession.shared.data(from: url)
if let decodedResponse = try? JSONDecoder().decode(Response.self, from: data) {
players = decodedResponse.players
}
} catch {
print("Invalid data")
}
}
}
My Response struct :
struct Response: Codable {
var players: [Player]
}
Player struct :
struct Player: Codable {
var _id: String
var slug: String
var tag: String
var name: String
var country: String
var team: Team
var accounts: [Account]
var revelant: Bool
}
Team struct :
struct Team: Codable {
var _id: String
var slug: String
var name: String
var region: String
var image: String
var relevant: Bool
}
Account struct :
struct Account: Codable {
var platform: String
var id: String
}
Edit: the error that I have from the do catch is :
Invalid data with error: keyNotFound(CodingKeys(stringValue: "revelant", intValue: nil), Swift.DecodingError.Context(codingPath: [], debugDescription: "No value associated with key CodingKeys(stringValue: "revelant", intValue: nil) ("revelant").", underlyingError: nil))
I followed a tutorial which works well but when I replace the link and use my own structs nothing happens when I launch the app.
Thanks for your help.
Your response is a dictionary not an array to to log error use try instead of try? and log the error inside the catch part , this snippet is after the edit and it shows currently info of 1 player
One player
func loadData() async {
guard let url = URL(string: "https://zsr.octane.gg/players/5f5ae840c6cbf591c568a477") else {
print("URL invalide")
return
}
do {
let (data, _) = try await URLSession.shared.data(from: url)
let decodedResponse = try JSONDecoder().decode(Player.self, from: data)
players = [decodedResponse]
} catch {
print("Invalid data with error: ",error)
}
}
All players
May be you mean to drop 5f5ae840c6cbf591c568a477 from guard let url = URL(string: "https://zsr.octane.gg/players/5f5ae840c6cbf591c568a477") else { to get all players , check here complete working demo
import SwiftUI
struct ContentView: View {
#State private var players = [Player]()
var body: some View {
List(players, id: \._id) { item in
VStack(alignment: .leading) {
Text(item.name ?? "")
.font(.headline)
Text(item.country ?? "")
}
}.task {
await loadData()
}
}
func loadData() async {
guard let url = URL(string: "https://zsr.octane.gg/players") else {
print("URL invalide")
return
}
do {
let (data, _) = try await URLSession.shared.data(from: url)
let decodedResponse = try JSONDecoder().decode(Response.self, from: data)
players = decodedResponse.players
} catch {
print("Invalid data",error)
}
}
}
struct Response: Codable {
var players: [Player]
}
struct Player: Codable {
var _id: String
var slug: String
var tag: String
var name: String?
var country: String?
var accounts: [Account]?
}
struct Team: Codable {
var _id: String
var slug: String
var name: String
var region: String
var image: String
var relevant: Bool
}
struct Account: Codable {
var platform: String
var id: String
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
Note: a variable that may exist in a model or not should be marked as optional

How to parse Wikipedia JSON Dictionary with Swift?

I'm new to the Wikipedia API and I have been trying to parse the image url from the API. The JSON I'm trying to parse is as follows:
API:
https://en.wikipedia.org/w/api.php?action=query&titles=tokyo&prop=pageimages&format=json
JSON Result:
{"batchcomplete": "",
"query": {
"normalized": [
{
"from": "tokyo",
"to": "Tokyo"
}
],
"pages": {
"30057": {
"pageid": 30057,
"ns": 0,
"title": "Tokyo",
"thumbnail": {
"source": "https://upload.wikimedia.org/wikipedia/commons/thumb/b/b2/Skyscrapers_of_Shinjuku_2009_January.jpg/50px-Skyscrapers_of_Shinjuku_2009_January.jpg",
"width": 50,
"height": 27
},
"pageimage": "Skyscrapers_of_Shinjuku_2009_January.jpg"
}
}
}
}
Below are the struct I created to parse the data. I can see the url when I print it to the console, but since "source" is nested under "thumbnail", which lives in the value of the [String:Page] dict pair, I can't figure out a way to access it. How do I parse data in a dictionary like this? Thank you for your help in advance.
struct WikiAPIResults: Codable {
let batchcomplete: String?
let query: Query?
}
struct Query: Codable {
let normalized: [Normalized]?
let pages: [String:Pages]? // <- I can get to here
}
struct Normalized: Codable {
let from, to: String?
}
struct Pages: Codable {
let pageid, ns: Int?
let title: String?
let thumbnail: Thumbnail?
let pageimage: String?
}
struct Thumbnail: Codable {
let source: String? // <- But I want to grab this
let width, height: Int?
}
func fetchImageFromWikipedia(imageKeyword: String, completion: #escaping (WikiAPIResults) -> Void) {
var urlComponents = URLComponents(string: "https://en.wikipedia.org/w/api.php?")!
urlComponents.queryItems = [
"action": "query",
"titles": imageKeyword,
"prop": "pageimages",
"format": "json"].map { URLQueryItem(name: $0.key, value: $0.value) }
let task = URLSession.shared.dataTask(with: urlComponents.url!) { data, response, error in
let jsonDecoder = JSONDecoder()
if let data = data,
let result = try? jsonDecoder.decode(WikiAPIResults.self, from: data) {
completion(result)
}
}
task.resume()
}
fetchImageFromWikipedia(imageKeyword: "Tokyo") { result in
print(result.query?.pages?.values)
}
Do you mean gathering all thumbnail sources?
fetchImageFromWikipedia(imageKeyword: "Tokyo") { result in
let pages = result.query?.pages?.compactMap { $0.value } ?? []
let thumbnailSources = pages.compactMap { $0.thumbnail?.source }
print(thumbnailSources)
}
This works with your example perfectly:
import Foundation
struct Response: Decodable {
struct Query: Decodable {
struct NormalizedQuery: Decodable {
let from: String
let to: String
}
struct Page: Decodable {
struct Thumbnail: Decodable {
let source: URL
let width: Int
let height: Int
}
let id: Int
let ns: Int
let title: String
let thumbnail: Thumbnail
let pageImage: String
private enum CodingKeys: String, CodingKey {
case id = "pageid"
case ns
case title
case thumbnail
case pageImage = "pageimage"
}
}
let normalized: [NormalizedQuery]
let pages: [String : Page]
}
let batchComplete: String
let query: Query
private enum CodingKeys: String, CodingKey {
case batchComplete = "batchcomplete"
case query
}
}
let responseString = """
{
"batchcomplete" : "",
"query" : {
"normalized" : [
{
"from" : "tokyo",
"to" : "Tokyo"
}
],
"pages" : {
"30057" : {
"pageid" : 30057,
"ns" : 0,
"title" : "Tokyo",
"thumbnail" : {
"source" : "https://upload.wikimedia.org/wikipedia/commons/thumb/b/b2/Skyscrapers_of_Shinjuku_2009_January.jpg/50px-Skyscrapers_of_Shinjuku_2009_January.jpg",
"width" : 50,
"height" : 27
},
"pageimage" : "Skyscrapers_of_Shinjuku_2009_January.jpg"
}
}
}
}
"""
let responseData = responseString.data(using: .utf8)!
let response = try! JSONDecoder().decode(Response.self, from: responseData)
print(response)
// Response(batchComplete: "", query: __lldb_expr_18.Response.Query(normalized: [__lldb_expr_18.Response.Query.NormalizedQuery(from: "tokyo", to: "Tokyo")], pages: ["30057": __lldb_expr_18.Response.Query.Page(id: 30057, ns: 0, title: "Tokyo", thumbnail: __lldb_expr_18.Response.Query.Page.Thumbnail(source: https://upload.wikimedia.org/wikipedia/commons/thumb/b/b2/Skyscrapers_of_Shinjuku_2009_January.jpg/50px-Skyscrapers_of_Shinjuku_2009_January.jpg, width: 50, height: 27), pageImage: "Skyscrapers_of_Shinjuku_2009_January.jpg")]))

How do I decode this JSON Data in Swift?

How do I decode this JSON Data?
I've done it with the "drilling down" method where I kept calling each key and printing the value. I also tried the data model but it never worked. I probably did something wrong but I don't know what.
Thanks in advance, If you need more information, just ask, I'm fairly new to Swift and Stackoverflow.
{
"items":[
{
"id":16000014,
"name":"BO",
"starPowers":[
{
"id":23000090,
"name":"CIRCLING EAGLE"
},
{
"id":23000148,
"name":"SNARE A BEAR"
}
],
"gadgets":[
{
"id":23000263,
"name":"SUPER TOTEM"
},
{
"id":23000289,
"name":"TRIPWIRE"
}
]
},
{
"id":16000015,
"name":"PIPER",
"starPowers":[
{
"id":23000091,
"name":"AMBUSH"
},
{
"id":23000152,
"name":"SNAPPY SNIPING"
}
],
"gadgets":[
{
"id":23000268,
"name":"AUTO AIMER"
},
{
"id":23000291,
"name":"HOMEMADE RECIPE"
}
]
}
],
"paging":{
"cursors":{
}
}
}
Decoding JSON in swift is insanely easy. Just use the JSONDecoder class. Firstly, create a Codable class for your json response like this
struct Items: Codable {
let items: [Item]
let paging: Paging
}
struct Item: Codable {
let id: Int
let name: String
let starPowers, gadgets: [Gadget]
}
struct Gadget: Codable {
let id: Int
let name: String
}
struct Paging: Codable {
let cursors: Cursors
}
struct Cursors: Codable {
}
And then use it to parse your JSON like this
let decoder = JSONDecoder()
do {
let items = try decoder.decode(Items.self, from: jsonData)
print(items)
// Do something with the items here
} catch {
print(error.localizedDescription)
}
struct Name {
var id:Int
var name:String
}
struct Cursor {
// Your cursor model
}
struct Paging {
var cursors: Cursor
}
struct Items {
var id:Int
var name:String
var starPowers:[Name]
var gadgets:[Name]
}
struct MainModel {
var items : [Items]
var paging : Paging
}
You can decode that data using let yourData = try! JSONDecoder().decode(MainModel.self, from: jsonData) to get your desired JSON data.

SwiftUI List doesn't appear

Good morning,
I have an issue with my SwiftUI list.
After receiving the data from my JSON correctly and having it as a string afterwards, the informations doesn't seem to appear in my list view.
struct Lists: View{
#State private var Countries = [Country]()
var body: some View {
List(Countries, id: \.id) { item in
VStack(alignment: .leading) {
Text(item.Country)
.font(.headline)
Text(item.Country)
}
}.onAppear(perform: loadData)
}
func loadData() {
guard let url = URL(string: "https://api.covid19api.com/summary") else {
print("Invalid URL")
return
}
let request = URLRequest(url: url)
let jsonData = (try! String(contentsOf: url))
/*print(jsonData)*/
URLSession.shared.dataTask(with: request) { data, response, error in
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .iso8601
if let jsonData = data {
do {
let decodedResponse = try decoder.decode(AllCountries.self, from: jsonData)
print(decodedResponse.Countries)
} catch let error as NSError {
/*print("json error: \(error.localizedDescription)")*/
print("json error: \(error)")
}
}
}.resume()
}
Here are my structs for the object:
struct AllCountries: Decodable {
var Countries: [Country]
}
struct AllCountries: Decodable {
var Countries: [Country] }
struct Country: Decodable, Identifiable {
let id = UUID()
var Country, CountryCode, Slug: String
var NewConfirmed, TotalConfirmed, NewDeaths, TotalDeaths: Int
var NewRecovered, TotalRecovered: Int
var Date: Date
}
enum CodingKeys: String, CodingKey {
case Country = "Country"
case CountryCode = "CountryCode"
case Slug = "Slug"
case NewConfirmed = "NewConfirmed"
case TotalConfirmed = "TotalConfirmed"
case NewDeaths = "NewDeaths"
case TotalDeaths = "TotalDeaths"
case NewRecovered = "NewRecovered"
case TotalRecovered = "TotalRecovered"
case Date = "Date"
}
}
Here is the beginning of the result of the "data" when I print it:
[_IOS_Project.Country(id: EB629D42-8278-444C-878E-A6EAC46BD5D6, Country: "Afghanistan", CountryCode: "AF", Slug: "afghanistan", NewConfirmed: 546, TotalConfirmed: 28424, NewDeaths: 21, TotalDeaths: 569, NewRecovered: 330, TotalRecovered: 8292, Date: 2020-06-21 19:37:01 +0000), _IOS_Project.Country(id: 8DDDCA84-CE99-4374-A487-096BFDF8A467, Country: "Albania", CountryCode: "AL", Slug: "albania", NewConfirmed: 53, TotalConfirmed: 1891, NewDeaths: 1, TotalDeaths: 43, NewRecovered: 12, TotalRecovered: 1126, Date: 2020-06-21 19:37:01 +0000),
Could somebody point me to the right direction on this issue?
Thanks in advance :)
There seems to be a few problems with your code.
Firstly the naming of your variables. Variable names in Swift begin with a lowercase, structs and classes begin with uppercase.
Secondly you aren't assigning the response from the your URLRequest to the countries state variable, this is the main problem.
Thirdly, your pasted code doesn't seem to be formatted correctly.
I put your code into a fresh project and with a few tweaks I got it to work.
struct ContentView: View {
#State private var countries = [AllCountries.Country]()
var body: some View {
List(countries, id: \.id) { item in
VStack(alignment: .leading) {
Text(item.country)
.font(.headline)
Text(item.country)
}
}.onAppear(perform: loadData)
}
func loadData() {
guard let url = URL(string: "https://api.covid19api.com/summary") else {
print("Invalid URL")
return
}
let request = URLRequest(url: url)
URLSession.shared.dataTask(with: request) { data, response, error in
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .iso8601
if let jsonData = data {
do {
let decodedResponse = try decoder.decode(AllCountries.self, from: jsonData)
print(decodedResponse.countries)
// You should update this on the main thread
DispatchQueue.main.async {
// this assigns the values you received to the state variable countries
self.countries = decodedResponse.countries
}
} catch let error as NSError {
print("json error: \(error)")
}
}
}.resume()
}
}
Notice in the loadData function that I assign the response from the URLRequest to the countries variable. Because this is a #State variable it causes the screen to reload when it changes. You weren't doing this so your UI had no idea that it needed to update.
I also updated your variable names, so that they are lowercased.
struct AllCountries: Decodable {
var countries: [Country]
enum CodingKeys: String, CodingKey {
case countries = "Countries"
}
struct Country: Decodable, Identifiable {
let id = UUID()
var country, countryCode, slug: String
var newConfirmed, totalConfirmed, newDeaths, totalDeaths: Int
var newRecovered, totalRecovered: Int
var date: Date
enum CodingKeys: String, CodingKey {
case country = "Country"
case countryCode = "CountryCode"
case slug = "Slug"
case newConfirmed = "NewConfirmed"
case totalConfirmed = "TotalConfirmed"
case newDeaths = "NewDeaths"
case totalDeaths = "TotalDeaths"
case newRecovered = "NewRecovered"
case totalRecovered = "TotalRecovered"
case date = "Date"
}
}
}

Resources