Отказ от ответственности: очень простой вопрос ниже. Я пытаюсь изучить основы разработки IOS.
В настоящее время я пытаюсь проанализировать данные из API в проекте SwiftUI и не могу это сделать.
Код выглядит следующим образом:
import SwiftUI
struct Poem: Codable {
let title, author: String
let lines: [String]
let linecount: String
}
class FetchPoem: ObservableObject {
// 1.
@Published var poems = [Poem]()
init() {
let url = URL(string: "https://poetrydb.org/random/1")!
// 2.
URLSession.shared.dataTask(with: url) {(data, response, error) in
do {
if let poemData = data {
// 3.
let decodedData = try JSONDecoder().decode([Poem].self, from: poemData)
DispatchQueue.main.async {
self.poems = decodedData
}
} else {
print("No data")
}
} catch {
print("Error")
}
}.resume()
}
}
struct ContentView: View {
@ObservedObject var fetch = FetchPoem()
let joined = fetch.poem.lines.joined(separator: "\n")
var body: some View {
Text(fetch.poem.title)
.padding()
Text( \(joined) )
.padding()
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
В настоящее время сборка не работает. Это бросает мне следующие ошибки:
Initializer 'init(_:)' requires that 'Binding<Subject>' conform to 'StringProtocol'
Referencing subscript 'subscript(dynamicMember:)' requires wrapper 'ObservedObject<FetchPoem>.Wrapper'
Value of type 'FetchPoem' has no dynamic member 'poem' using key path from root type 'FetchPoem'
Более того, я пытаюсь добавить массив «Строки» в одну основную переменную String «Joined». Однако я не уверен, что это работает... Ошибка: «Строковая интерполяция может появляться только внутри строкового литерала». Хотелось бы помощи, если кто знает...
Есть идеи? Вся помощь приветствуется.
** Отредактированный код - Q2
import SwiftUI
struct Poem: Codable, Hashable {
let title, author: String
let lines: [String]
let linecount: String
}
class FetchPoem: ObservableObject {
@Published var poems = [Poem]()
func getPoem() {
let url = URL(string: "https://poetrydb.org/random/1")!
// 2.
URLSession.shared.dataTask(with: url) {(data, response, error) in
do {
if let poemData = data {
// 3.
let decodedData = try JSONDecoder().decode([Poem].self, from: poemData)
DispatchQueue.main.async {
self.poems = decodedData
}
} else {
print("No data")
}
} catch {
print("Error")
}
}.resume()
}
}
struct ContentView: View {
@ObservedObject var fetch = FetchPoem()
var body: some View {
VStack {
if let poem = fetch.poems.first {
Button("Refresh") {getPoem}
Text("\(poem.author): \(poem.title)").bold()
Divider()
ScrollView {
VStack {
ForEach(poem.lines, id: \.self) {
Text($0)
}
}
}
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
Скорее всего, вы хотели этого (потому что poems
— это массив) — протестировано с Xcode 12.1/iOS 14.1.
struct Poem: Codable, Hashable {
let title, author: String
let lines: [String]
let linecount: String
}
// ...
struct ContentView: View {
@ObservedObject var fetch = FetchPoem()
var body: some View {
List {
ForEach(fetch.poems, id: \.self) {
Text("\($0.author): \($0.title)")
}
}
}
}
... , а затем можно было бы обернуть это в NavigationView/NavigationLink
с отображением каждого стихотворения в представлении назначения.
Из отображения строк следующим образом:
var body: some View {
VStack {
if let poem = fetch.poems.first {
Text("\(poem.author): \(poem.title)").bold()
Divider()
ScrollView {
VStack {
ForEach(poem.lines, id: \.self) {
Text($0)
}
}
}
}
}
}
Переместите все из init
в FetchPoem в отдельную функцию, например. fetch
, который вы сможете вызвать и из init
для первоначального извлечения стихотворения, и из кнопки просмотра для извлечения следующего случайного стихотворения.
Будет ли это выглядеть как отредактированный код выше? Кстати, спасибо за вашу помощь!
Я опубликую новый ... Спасибо в любом случае
Спасибо. Невероятно полезное предложение... Последний вопрос: как бы вы предложили добавить к этому кнопку «Обновить»? Я безуспешно пытался использовать кнопку ("Обновить") {FetchPoem} над именем автора.