Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/swift/19.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Swift 迅捷为什么可以';你不能在两个视图之间传递发布者吗?_Swift_Swiftui_Combine - Fatal编程技术网

Swift 迅捷为什么可以';你不能在两个视图之间传递发布者吗?

Swift 迅捷为什么可以';你不能在两个视图之间传递发布者吗?,swift,swiftui,combine,Swift,Swiftui,Combine,我只是想做一些这样的测试↓ 从第一个视图创建一个发布服务器 将其传递给第二视图 在第二个视图中使用某些属性绑定发布服务器,并尝试在屏幕上显示它 代码是↓ (第一视图) (第二视图) 结构响应视图:视图{ 让发布者:AnyPublisher @状态变量内容:String=“” var body:一些观点{ HStack{ VStack{ 文本(内容) .font(.system(大小:12)) .onAppear{{uu=self.publisher.assign(to:\.content,on:

我只是想做一些这样的测试↓

  • 从第一个视图创建一个发布服务器
  • 将其传递给第二视图
  • 在第二个视图中使用某些属性绑定发布服务器,并尝试在屏幕上显示它
  • 代码是↓ (第一视图)

    (第二视图)

    结构响应视图:视图{ 让发布者:AnyPublisher @状态变量内容:String=“” var body:一些观点{ HStack{ VStack{ 文本(内容) .font(.system(大小:12)) .onAppear{{uu=self.publisher.assign(to:\.content,on:self)} 垫片() } 垫片() } } } 但代码不起作用。请求失败,消息为blow↓

    2020-11-11 11:08:04.657375+0800 PandaServiceDemo[83721:1275181] Task <6B53516E-5127-4C5E-AD5F-893F1AEE77E8>.<1> finished with error [-999] Error Domain=NSURLErrorDomain Code=-999 "cancelled" UserInfo={NSErrorFailingURLStringKey=https://v.juhe.cn/joke/content/list.php?sort=asc&page=&pagesize=&time=1418816972&key=aa73ebdd8672a2b9adc9dbb2923184c8, NSLocalizedDescription=cancelled, NSErrorFailingURLKey=https://v.juhe.cn/joke/content/list.php?sort=asc&page=&pagesize=&time=1418816972&key=aa73ebdd8672a2b9adc9dbb2923184c8}
    
    2020-11-11:08:04.657375+0800 PandaServiceDemo[83721:1275181]任务。已完成,错误为[-999]错误域=nsurErrorDomain代码=-999“已取消”用户信息={NSErrorFailingURLStringKey=https://v.juhe.cn/joke/content/list.php?sort=asc&page=&pagesize=&time=1418816972&key=aa73ebdd8672a2b9adc9dbb2923184c8,NSLocalizedDescription=已取消,NSErrorFailingURLKey=https://v.juhe.cn/joke/content/list.php?sort=asc&page=&pagesize=&time=1418816972&key=aa73ebdd8672a2b9adc9dbb2923184c8}
    

    有人能告诉我发生了什么事,什么是做这件事的正确方法吗?

    好的,这项工作有两种方法:一种更好

    方式1:

        import SwiftUI
    
    
    
    
    struct ContentView: View {
        
        @State var urlForPublicsh: URL?
        
        
        var body: some View {
            
            VStack
            {
                Text(urlForPublicsh?.absoluteString ?? "nil")
                    .padding()
                
                Button("Change the Publisher") {urlForPublicsh = URL(string: "https://stackoverflow.com")}
                    .padding()
                
                
                SecondView(urlForPublicsh: $urlForPublicsh)
                
            }
            .onAppear()
            {
                urlForPublicsh = URL(string: "https://www.apple.com")
            }
      
        }
    }
    struct SecondView: View {
        @Binding var urlForPublicsh: URL?
        
        var body: some View {
            
            Text(urlForPublicsh?.absoluteString ?? "nil")
                .padding()
        }
    }
    
        import SwiftUI
    
    class UrlForPublicshModel: ObservableObject
    {
        static let shared = UrlForPublicshModel()
        @Published var url: URL?
    }
    
    
    
    struct ContentView: View {
        
    
        @StateObject var urlForPublicshModel = UrlForPublicshModel.shared
        
        var body: some View {
            
            VStack
            {
                Text(urlForPublicshModel.url?.absoluteString ?? "nil")
                    .padding()
                
                Button("Change the Publisher") {urlForPublicshModel.url = URL(string: "https://stackoverflow.com")}
                    .padding()
                
                
                SecondView()
                
            }
            .onAppear()
            {
                urlForPublicshModel.url = URL(string: "https://www.apple.com")
            }
      
        }
    }
    
    
    struct SecondView: View {
    
        @ObservedObject var urlForPublicshModel = UrlForPublicshModel.shared
        
        var body: some View {
            
            Text(urlForPublicshModel.url?.absoluteString ?? "nil")
                .padding()
    
        }
    }
    
    方式2:

        import SwiftUI
    
    
    
    
    struct ContentView: View {
        
        @State var urlForPublicsh: URL?
        
        
        var body: some View {
            
            VStack
            {
                Text(urlForPublicsh?.absoluteString ?? "nil")
                    .padding()
                
                Button("Change the Publisher") {urlForPublicsh = URL(string: "https://stackoverflow.com")}
                    .padding()
                
                
                SecondView(urlForPublicsh: $urlForPublicsh)
                
            }
            .onAppear()
            {
                urlForPublicsh = URL(string: "https://www.apple.com")
            }
      
        }
    }
    struct SecondView: View {
        @Binding var urlForPublicsh: URL?
        
        var body: some View {
            
            Text(urlForPublicsh?.absoluteString ?? "nil")
                .padding()
        }
    }
    
        import SwiftUI
    
    class UrlForPublicshModel: ObservableObject
    {
        static let shared = UrlForPublicshModel()
        @Published var url: URL?
    }
    
    
    
    struct ContentView: View {
        
    
        @StateObject var urlForPublicshModel = UrlForPublicshModel.shared
        
        var body: some View {
            
            VStack
            {
                Text(urlForPublicshModel.url?.absoluteString ?? "nil")
                    .padding()
                
                Button("Change the Publisher") {urlForPublicshModel.url = URL(string: "https://stackoverflow.com")}
                    .padding()
                
                
                SecondView()
                
            }
            .onAppear()
            {
                urlForPublicshModel.url = URL(string: "https://www.apple.com")
            }
      
        }
    }
    
    
    struct SecondView: View {
    
        @ObservedObject var urlForPublicshModel = UrlForPublicshModel.shared
        
        var body: some View {
            
            Text(urlForPublicshModel.url?.absoluteString ?? "nil")
                .padding()
    
        }
    }
    

    您需要存储订阅,否则它将被取消初始化并自动取消

    通常,这是这样做的:

    var cancellables: Set<AnyCancellable> = []
    // ...
    publisher
      .sink {...}
      .store(in: &cancellables)
    

    您应该小心使用上述方法,因为如果重新初始化
    ResponseView
    ,它将获得发布者的副本(大多数发布者都是值类型),因此它将启动一个新请求

    要避免这种情况,请将
    .share()
    添加到发布服务器:

    let publisher = URLSession(configuration: URLSessionConfiguration.default)
            .dataTaskPublisher(...)
            //...
            .share()
            .eraseToAnyPublisher()
    

    这里的问题是,订阅没有存储在任何地方。您必须将其存储在
    anycancelable
    var中并保留订阅

    在调试联合相关问题时使用
    .print()
    操作符。我发现它非常有用


    正确的方法是将发布者和订阅提取到一个
    observeObject
    中,并将其注入
    视图
    或使用
    @StateObject

    class DataProvider: ObservableObject {
        @Published var content: String = ""
        private var bag = Set<AnyCancellable>()
        private let publisher: AnyPublisher<String, Never>
        init() {
            publisher = URLSession(configuration: URLSessionConfiguration.default)
                .dataTaskPublisher(for: URLRequest(url: URL(string: "https://v.juhe.cn/joke/content/list.php?sort=asc&page=&pagesize=&time=1418816972&key=aa73ebdd8672a2b9adc9dbb2923184c8")!))
                .map(\.data.description)
                .print()
                .replaceError(with: "Error!")
                .receive(on: DispatchQueue.main)
                .eraseToAnyPublisher()
        }
        
        func loadData() {
            publisher.assign(to: \.content, on: self).store(in: &bag)
        }
    }
    
    struct ContentView: View {
        @StateObject var dataProvider = DataProvider()
        
        var body: some View {
            NavigationView {
                List {
                    NavigationLink(destination: ResponseView(dataProvider: dataProvider)) {
                        Text("Hello, World!")
                    }
                }.navigationBarTitle("Title", displayMode: .inline)
            }
        }
    }
    
    struct ResponseView: View {
        let dataProvider: DataProvider
        
        var body: some View {
            HStack {
                VStack {
                    Text(dataProvider.content)
                        .font(.system(size: 12))
                        .onAppear {
                            self.dataProvider.loadData()
                        }
                    Spacer()
                }
                Spacer()
            }
            
        }
    }
    
    类数据提供程序:observeObject{
    @已发布的变量内容:String=“”
    私有变量包=集合()
    私人出租出版商:AnyPublisher
    init(){
    publisher=URLSession(配置:URLSessionConfiguration.default)
    .dataTaskPublisher(用于:url请求(url:url(字符串):https://v.juhe.cn/joke/content/list.php?sort=asc&page=&pagesize=&time=1418816972&key=aa73ebdd8672a2b9adc9dbb2923184c8")!))
    .map(\.data.description)
    .print()
    .replaceError(带有:“Error!”)
    .receive(在:DispatchQueue.main上)
    .删除任何发布者()
    }
    func loadData(){
    publisher.assign(到:\.content,在:self上)。store(在:&bag中)
    }
    }
    结构ContentView:View{
    @StateObject var dataProvider=dataProvider()
    var body:一些观点{
    导航视图{
    名单{
    导航链接(目标:响应视图(数据提供程序:数据提供程序)){
    文本(“你好,世界!”)
    }
    }.navigationBarTitle(“标题”,显示模式:。内联)
    }
    }
    }
    结构响应视图:视图{
    让数据提供者:数据提供者
    var body:一些观点{
    HStack{
    VStack{
    文本(dataProvider.content)
    .font(.system(大小:12))
    奥纳佩尔先生{
    self.dataProvider.loadData()
    }
    垫片()
    }
    垫片()
    }
    }
    }
    

    请注意,我们使用了
    @StateObject
    来确保
    数据提供程序
    实例在视图更新时不会被破坏。

    就SwiftUI而言,您犯了一个根本错误:从视图创建发布服务器。这意味着每隔一次
    都会创建一个新发布服务器entView
    是实例化的,不管怎样,这种情况可能会发生很多次,SwiftUI不保证视图只实例化一次


    您需要做的是将发布的数据提取到某个对象中,该对象要么从上游注入,要么由SwiftUI通过
    @StateObject

    进行管理。谢谢,这对我帮助很大。顺便说一句,因为@StateObject只在iOS14中工作。如果在IOS13中,我们可以做什么?@Shaggon我能想到的一种方法是使用一个大模型在顶层创建,然后注入下游。这样,您只有一个实例,并且在每个级别上,您只能发送该大模型所需的部分。唯一(大)的缺点是,该模型可以通过这种方法变大……在您的示例中,您不妨将@StateObject放在ResponseView中
    class DataProvider: ObservableObject {
        @Published var content: String = ""
        private var bag = Set<AnyCancellable>()
        private let publisher: AnyPublisher<String, Never>
        init() {
            publisher = URLSession(configuration: URLSessionConfiguration.default)
                .dataTaskPublisher(for: URLRequest(url: URL(string: "https://v.juhe.cn/joke/content/list.php?sort=asc&page=&pagesize=&time=1418816972&key=aa73ebdd8672a2b9adc9dbb2923184c8")!))
                .map(\.data.description)
                .print()
                .replaceError(with: "Error!")
                .receive(on: DispatchQueue.main)
                .eraseToAnyPublisher()
        }
        
        func loadData() {
            publisher.assign(to: \.content, on: self).store(in: &bag)
        }
    }
    
    struct ContentView: View {
        @StateObject var dataProvider = DataProvider()
        
        var body: some View {
            NavigationView {
                List {
                    NavigationLink(destination: ResponseView(dataProvider: dataProvider)) {
                        Text("Hello, World!")
                    }
                }.navigationBarTitle("Title", displayMode: .inline)
            }
        }
    }
    
    struct ResponseView: View {
        let dataProvider: DataProvider
        
        var body: some View {
            HStack {
                VStack {
                    Text(dataProvider.content)
                        .font(.system(size: 12))
                        .onAppear {
                            self.dataProvider.loadData()
                        }
                    Spacer()
                }
                Spacer()
            }
            
        }
    }