Как обратиться к @Published var в пределах: class NetworkManager: ObservableObject - PullRequest
0 голосов
/ 05 ноября 2019

Мне нужно определить testData: [Test], который ссылается на @Published var tests: [Test] в

классе NetworkManager: ObservableObject (см. Код).

Я пробовал следующееопределение:

/// The app does not compile with this definition
//let testData:[Test] = NetworkManager(tests: Test)

/// The app works with this definition, but shows no remote json data
let testData:[Test] = NetworkManager().tests

class NetworkManager: ObservableObject {

@Published var tests:[Test] = [Test]()

func getAllTests() {
    let file = URLRequest(url: URL(string: "https://my-url/remote.json")!)
    let task = URLSession.shared.dataTask(with: file) { (data, _, error) in
    guard error == nil else { return }

        do {
            let tests = try JSONDecoder().decode([Test].self, from: data!)

            DispatchQueue.main.async {
                    self.tests = tests
                print(tests)

            }
        } catch {
            print("Failed To decode: ", error)
        }
    }
    task.resume()
}
    init() {
        getAllTests()
}
    init(tests: [Test]) {
        self.tests = tests
}
}

Код ниже работает отлично

/// The app works with this definition and shows the local json data
let testData:[Test] = load("local.json")

func load<T:Decodable>(_ filename:String, as type:T.Type = T.self) -> 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)")
}
}

Однако, для первой части я получаю сообщение об ошибке:

"Невозможно преобразовать значение типа 'Test.Type 'к ожидаемому типу аргумента' [Test] '"

Что мне здесь не хватает? Любая помощь высоко ценится.

Дополнительная информация в ответ на ответ и вопрос, который показывает, как используется testData:

import SwiftUI
import Combine

struct Test: Hashable, Codable, Identifiable {
    var id:Int
    var imageName:String
    var imageUrl:String
    var category:Category
    var description:String

    enum Category: String, CaseIterable, Codable, Hashable {
        case t1 = "test1"
        case t2 = "test2"
        case t3 = "test3"
    }
}

class NetworkManager: ObservableObject {

      @Published var tests:[Test] = [Test]()
      private var subscriptions = Set<AnyCancellable>()

      func getAllTests() {
        let file = URLRequest(url: URL(string: "https://my-url/remote.json")!)
        URLSession
          .shared
          .dataTaskPublisher(for: file)
          .map(\.data)
          .decode(type: [Test].self, decoder: JSONDecoder())
          .replaceError(with: [])
          .receive(on: RunLoop.main)
          .assign(to: \.tests, on: self)
          .store(in: &subscriptions)
      }
      init() {
        getAllTests()
      }
      init(tests: [Test]) {
        self.tests = tests
      }
    }

let testData:[Test] = NetworkManager().tests

struct ContentView: View {

var categories:[String:[Test]] {
    .init(
        grouping: testData,
        by: {$0.category.rawValue}
    )
}

var body: some View {
    NavigationView{
        List (categories.keys.sorted(), id: \String.self) {key in TestRow(categoryName: "\(key) - Case".uppercased(), tests: self.categories[key]!)
            .frame(height: 320)
            .padding(.top)
            .padding(.bottom)
        }
        .navigationBarTitle(Text("TEST"))
    }
}

}

struct TestRow: View {

var categoryName:String
var tests:[Test]

    var body: some View {

        VStack {

            Text(self.categoryName)
                .font(.title)
                .multilineTextAlignment(.leading)

                ScrollView(.horizontal, showsIndicators: false) {
                    HStack(alignment: .top) {
                        ForEach(self.tests, id: \.self) { tests in

                            NavigationLink(destination:
                                TestDetail(test: tests)) {
                                TestItem(test: tests)
                                    .frame(width: 300)
                                    .padding(.trailing, 30)
                                  Spacer()

                        }}
                }
                .padding(.leading)
            }
        }

    }
}

struct TestDetail: View {

    var test:Test
    var body: some View {
        List{
        ZStack(alignment: .bottom) {

            Image(test.imageUrl)
                .resizable()
                .aspectRatio(contentMode: .fit)
            Rectangle()
                .padding()
                .frame(height: 80.0)
                .opacity(0.25)
                .blur(radius: 10)
            HStack{
                VStack(alignment: .leading) {
                    Text(test.imageName)
                        .padding()
//                    .color(.white)
                    .colorScheme(.light)
                    .font(.largeTitle)
            }
            .padding(.leading)
            .padding(.bottom)
            Spacer()
            }
        }
            .listRowInsets(EdgeInsets())
            VStack(alignment: .leading) {
                Text(test.description)
//                  .padding(.bottom)
//                  .color(.primary)
                    .colorScheme(.light)
                    .font(.body)
                    .lineLimit(nil)
                    .lineSpacing(12)

                HStack {
                    Spacer()
                    OrderButton()
                    Spacer()
                }.padding(.top, 50)
            }.padding(.top)
            .padding(.bottom)
        }
        .edgesIgnoringSafeArea(.top)
    .navigationBarHidden(true)
    }
}

struct TestItem: View {

    var test:Test
    var body:some View{
        VStack(spacing: 16.0)
        {
            Image(test.imageUrl)
            .resizable()
            .renderingMode(.original)
            .aspectRatio(contentMode: .fill)
            .frame(width: 300, height: 170)
            .cornerRadius(10)
            .shadow(radius: 10)

            VStack(alignment: .leading, spacing: 5.0)
            {
            Text(test.imageName)
//                .color(.primary)
                .font(.headline)
            Text(test.description)
                .font(.subheadline)
                //                .color(.secondary)
                .multilineTextAlignment(.leading)
                .lineLimit(2)
                .frame(height: 40)
            }
        }
    }
}

struct OrderButton : View {
    var body: some View {
        Button(action: {}) {
            Text("Order Now")
        }.frame(width: 200, height: 50)
            .foregroundColor(.white)
            .font(.headline)
            .background(Color.blue)
        .cornerRadius(10)
    }
}

class ImageLoader:ObservableObject
{
    @Published var data:Data = Data()
    func getImage(imageURL:String) {
        guard let test = URL(string: imageURL) else { return }

        URLSession.shared.dataTask(with: test) { (data, response, error) in
            DispatchQueue.main.async {
                if let data = data {
                    self.data = data
                }
            }
            print(data as Any)
        }.resume()
    }
    init(imageURL:String) {
        getImage(imageURL: imageURL)
    }
}

struct ContentView_Previews: PreviewProvider {
    @ObservedObject var imageLoader: ImageLoader
    init(test:String)
    {
        imageLoader = ImageLoader(imageURL: test)
    }
    static var previews: some View {
        ContentView()
    }
}

// local.json

[
{
"id":101,
"imageName":"test-f1a",
"imageUrl":"test-f1a",
"description":"test1a",
"category":"test1"
},
...
]

// remote.json

[
{
"id":101,
"imageName":"test-f1a",
"imageUrl":"https://my-url/test-f1a",
"description":"test1a",
"category":"test1"
},
...
]

Ответы [ 2 ]

0 голосов
/ 07 ноября 2019

Замена «grouping: testData» на «grouping: networkManager.tests» и использование «@ObservedObject var networkManager: NetworkManager = NetworkManager ()» делает определение testData избыточным и, таким образом, решает проблему. Спасибо @Josh Homann за его ответ и комментарий, которые помогли мне преодолеть эту проблему.

0 голосов
/ 06 ноября 2019

Начиная с iOS 13, URLSession был расширен с издателем, так что идиоматически ваш код становится:

import UIKit
import Combine

struct Test: Codable {
  var name: String
}

class NetworkManager: ObservableObject {

  @Published var tests:[Test] = [Test]()
  private var subscriptions = Set<AnyCancellable>()

  func getAllTests() {
    let file = URLRequest(url: URL(string: "https://my-url/remote.json")!)
    URLSession
      .shared
      .dataTaskPublisher(for: file)
      .map(\.data)
      .decode(type: [Test].self, decoder: JSONDecoder())
      .replaceError(with: [])
      .receive(on: RunLoop.main)
      .assign(to: \.tests, on: self)
      .store(in: &subscriptions)
  }
  init() {
    getAllTests()
  }
  init(tests: [Test]) {
    self.tests = tests
  }
}
...