Что касается TestModel
Decodable протокола (в вашем контексте) предполагает, что вы создаете структуру модели со всеми свойствами, которые вы получаете через JSON. При запросе http://numbersapi.com/2/trivia?json
вы получите что-то вроде:
{
"text": "2 is the number of stars in a binary star system (a stellar system consisting of two stars orbiting around their center of mass).",
"number": 2,
"found": true,
"type": "trivia"
}
Это означает, что ваша модель должна выглядеть следующим образом:
struct TestModel: Decodable {
let text: String
let number: Int
let found: Bool
let type: String
}
Что касается делегатов
In SwiftUI этот подход недостижим. Вместо этого разработчикам необходимо адаптировать функции платформы Combine: обертки свойств @ObservedObject
, @Published
и ObservableObject
протокол. Вы хотите поместить свой logi c в некую структуру. Плохая новость, что (в настоящее время) ObservableObject
является протоколом AnyObject
(т.е. Протокол только для классов ). Вам нужно будет переписать свой TestManager
как класс как:
class TestManager: ObservableObject {
// ...
}
Только тогда вы сможете использовать его в своем CurrentView
, используя @ ObservedObject обертку свойств:
struct ContentView: View {
@ObservedObject var manager = TestManager()
// ...
}
Что касается TestManager
Ваш logi c теперь исключает delegate
как таковой, и вам нужно использовать TestModel
для передачи данных на CustomView
. Вы можете изменить TestManager
, добавив новое свойство с помощью @ Published property wrapper:
class TestManager: ObservableObject {
let urlString = "http://numbersapi.com/2/trivia?json"
// 1
@Published var model: TestModel?
func get(){
if let url = URL(string: urlString){
let session = URLSession(configuration: .default)
let task = session.dataTask(with: url) { [weak self] (data, response, error) in
// 2
DispatchQueue.main.async {
if let safeData = data {
if let parsedData = self?.parseJson(safeData) {
// 3
self?.model = parsedData
}
}
}
}
task.resume()
}
}
private func parseJson(_ jsonData: Data) -> TestModel? {
let decoder = JSONDecoder()
do {
let decodedData = try decoder.decode(TestModel.self, from: jsonData)
return decodedData
} catch {
return nil
}
}
}
- Чтобы иметь возможность доступа к вашей модели «извне», в вашем случае
ContentView
. - Используйте
DispatchQueue.main.async{ }
для асинхронных c задач, потому что Publishing changes from background threads is not allowed; make sure to publish values from the main thread (via operators like receive(on:)) on model updates.
- Просто используйте вашу проанализированную модель.
Затем в ContentView
используйте свой TestManager
вот так:
struct ContentView: View {
@ObservedObject var manager = TestManager()
var body: some View {
ZStack{
Color(.green)
.edgesIgnoringSafeArea(.all)
VStack {
Text("Trivia is: \(self.manager.model?.text ?? "Unknown")")
Button(action:{ self.manager.get() }) {
Text("Get number 2")
.font(.title)
.foregroundColor(.white)
.padding()
.overlay(RoundedRectangle(cornerRadius: 30)
.stroke(Color.white, lineWidth: 5))
}
}
}
}
}
Что касается HTTP
Вы используете ссылку http://numbersapi.com/2/trivia?json
, которая не разрешена Apple , пожалуйста, используйте https
или добавьте ключ App Transport Security Settings
с параметром Allow Arbitrary Loads
, установленным на YES
, в свой Info.Plist . Но делайте это очень осторожно , так как http-ссылка просто не будет работать.
Дальнейшие действия
Вы можете реализовать обработку ошибок самостоятельно, основываясь на описании выше.
Полный код (копипаст и go):
import SwiftUI
struct ContentView: View {
@ObservedObject var manager = TestManager()
var body: some View {
ZStack{
Color(.green)
.edgesIgnoringSafeArea(.all)
VStack {
Text("Trivia is: \(self.manager.model?.text ?? "Unknown")")
Button(action:{ self.manager.get() }) {
Text("Get number 2")
.font(.title)
.foregroundColor(.white)
.padding()
.overlay(RoundedRectangle(cornerRadius: 30)
.stroke(Color.white, lineWidth: 5))
}
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
class TestManager: ObservableObject {
let urlString = "http://numbersapi.com/2/trivia?json"
@Published var model: TestModel?
func get(){
if let url = URL(string: urlString){
let session = URLSession(configuration: .default)
let task = session.dataTask(with: url) { [weak self] (data, response, error) in
DispatchQueue.main.async {
if let safeData = data {
if let parsedData = self?.parseJson(safeData) {
self?.model = parsedData
}
}
}
}
task.resume()
}
}
private func parseJson(_ jsonData: Data) -> TestModel? {
let decoder = JSONDecoder()
do {
let decodedData = try decoder.decode(TestModel.self, from: jsonData)
return decodedData
} catch {
return nil
}
}
}
struct TestModel: Decodable {
let text: String
let number: Int
let found: Bool
let type: String
}