Hello everyone,
I have a bit of an issue that I expected to be fairly simple to solve. I have scoured the internet but have run out of solutions to test out.
Task:
I am trying to setup a very simple Dictionary API call from my iOS application with this link: https://api.dictionaryapi.dev/api/v2/entries/en/hello
And show the result of it in a basic View (using Text{}) with SwiftUI.
Problem:
The code compiles, but my ContentView is blank.
Debugging so far:
I followed a tutorial on setting up JSON parsing with the "Codable" protocol and was able to successfully set it up using a link that was a Movie API database. Upon further investigation on why the MovieAPI results were working and the DictionaryAPI results weren't, I noticed the the MovieAPI response was a JSONObject and the DictionaryAPI response was a JSONArray which starts with [ ]
After more googling I was sent to the solution to utilize something like this:
try! JSONDecoder().decode([DictionaryWord].self, from: jsonData)
But it also did not work and my view is still blank. I will be putting the code below as well for further reference. Thank you
import SwiftUI
struct ContentView: View {
@State private var results = [DictionaryWord]()
var body: some View {
VStack{
Text("API Example iOS Application")
List(results, id: \.word) { item in
VStack(alignment: .leading) {
Text(item.word)
.font(.headline)
Text(item.phonetic)
}
}
.task {
await loadData()
}
}
}
let freeDictionaryURL = "https://api.dictionaryapi.dev/api/v2/entries/en/hello"
func loadData() async {
guard let url = URL(string: freeDictionaryURL) else {
print("Invalid URL")
return
}
do {
let (data, _) = try await URLSession.shared.data(from: url)
if let decoded: [WelcomeElement] = try? JSONDecoder().decode([DictionaryWord].self, from: data) {
results = decoded
}
} catch {
print("Invalid data")
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
// MARK: - WelcomeElement
struct DictionaryWord: Codable {
var word, phonetic: String
var phonetics: [Phonetic]
var origin: String
var meanings: [Meaning]
}
// MARK: - Meaning
struct Meaning: Codable {
var partOfSpeech: String
var definitions: [Definition]
}
// MARK: - Definition
struct Definition: Codable {
var definition, example: String
var synonyms, antonyms: [String?]
}
// MARK: - Phonetic
struct Phonetic: Codable {
var text: String
var audio: String?
}
Try this code, works for me.
struct ContentView: View {
@State private var results = [DictionaryWord]()
var body: some View {
VStack {
Text("API Example iOS Application")
List(results) { item in
VStack(alignment: .leading) {
Text(item.word).font(.headline)
ForEach(item.phonetics) { phone in // <--- here
if phone.text != nil {
Text(phone.text!)
}
}
}
}
}
.task {
await loadData()
}
}
let freeDictionaryURL = "https://api.dictionaryapi.dev/api/v2/entries/en/hello"
func loadData() async {
guard let url = URL(string: freeDictionaryURL) else {
print("Invalid URL")
return
}
do {
let (data, _) = try await URLSession.shared.data(from: url)
// --- here
let decoded: [DictionaryWord] = try JSONDecoder().decode([DictionaryWord].self, from: data)
results = decoded
} catch {
print(error) // <--- important
}
}
}
struct DictionaryWord: Identifiable, Codable { // <--- here
let id = UUID()
let word: String
let phonetics: [Phonetic]
let meanings: [Meaning]
let license: License
let sourceUrls: [String]
enum CodingKeys: String, CodingKey {
case word, phonetics, meanings, license, sourceUrls
}
}
struct License: Codable {
let name: String
let url: String
}
struct Meaning: Codable {
let partOfSpeech: String
let definitions: [Definition]
let synonyms, antonyms: [String]?
}
struct Definition: Codable {
let definition: String
let synonyms, antonyms: [String]?
let example: String?
}
struct Phonetic: Identifiable, Codable { // <--- here
let id = UUID()
let audio: String
let sourceURL: String?
let license: License?
let text: String?
enum CodingKeys: String, CodingKey {
case audio
case sourceURL = "sourceUrl"
case license, text
}
}