美文网首页
Swift Combine

Swift Combine

作者: walkerwzy | 来源:发表于2024-08-02 18:06 被阅读0次

    Publish

    @objc
    func startTimer() {
        print("Start")
        
        subscription = Timer
            .publish(every: 1, on: .main, in: .common)
            .autoconnect() // 连接???
            .scan(0, { (count, _) in  // 生成一个value, 这里跟reduce很像
                return count + 1
            })
            .sink(receiveCompletion: { completion in // 使用每次订阅到的value或事件
                switch completion {
                    case .failure(let error):
                        print("Stream has failed with error: \(error)")
                    case .finished:
                        print("Stream has completed")
                }
            }, receiveValue: { count in
                print("Updating the label to the current value: \(count.format)")
                self.countLbl.text = count.format
            })
    }
    
    @objc
    func stopTimer() {
        print("Stop")
        subscription?.cancel()
    }
    

    publisher(不是publish), assing

    final class LevelsManager {
        var level: Int = 0 {
            didSet {
                print("User's current level \(level)")
            }
        }
    }
    
    let lvlsManager = LevelsManager()
    let lvlsRange = (0...100)
    let cancellable = lvlsRange.
                    publisher.
                    assign(to: \.level, on: lvlsManager)
    

    event

    
    // notification, 从post变成了publisher
    
    let subscription = NotificationCenter.default
        .publisher(for: .UIApplicationDidBecomeActive)
        .sink { _ in
            print("App is active")
        }
    
    NotificationCenter.default
    .publisher(for: UITextField.textDidChangedNotification, object: inputTextField)
    .compactMap { ($0.object as? UITextField)?.text }
    .map { "The user entered \($0)" }
    //.sink { print($0) }
    // 上面是sink, 下面是assign
    .assign(to: \.text, on: label)
    .store(in: $subscriptions) // 这里是设了个set用来存储
    // var subscriptions = Set<AnyCancellable>()
    
    

    可见没有像rxSwift一样的把事件直接pulish的用法, 而用notification来中转, 说明目前只扩展了notification

    CurrentValue Subject

    
    // 假设有一个对象/结构体
    struct Personl{
        var firstName: String
        var lastName: String
        var occupation: string
    }
    
    extension Person{
    
        var message: String
            "\(firstName) \(lastName) is a \(occupation)"
        }
    
        var isValid: Bool{
            firstName.isEmpty && IlastName.isEmpty && loccupation.isEmpty
        }
    }
    
    // 你不能去new一个Person出来, 而是一个subject
    private let person = CurrentValueSubject<Person, Error>(Person(firstName: "", LastName:"", occupation:""))
    
    // 用.value来使用(current subject)
    NotificationCenter
     .default
     .publisher(for: UITextField.textDidChangeNotification, object: firstNameTxtField)
     .compactMap({ ($0.object as? UITextField)?.text  })
     .sink {[weak self] val in
        self?.person.value.firstName = val // 这里
     }
     .store(in: &subscriptions)
    
     // person也是可以被监听的
     person.sink { _ in 
     
     } receiveValue: { p in 
        print(p)
     }
     .store(in: &subscriptions)
    
     // 发停止监听信号, 如点击完成时:
     func didClickConfirm() {
        person.send(completion: .finished)
     }
    
    

    Passthrough Subjects

    let subject = PassthroughSubject<String, Never>()
    
    // passthrough subjects没有.value属性
    // 意味着它只能通用sink来取值
    subject.sink { value in
        print(value)
    }
    
    subject.send("Hello")
    subject.send("World")
    

    Multithread

    final class IMageDownloaderViewMOdel {
        let image = PassthroughSubject<UIImage, Never>()
        var subscriptions =  Set<AnyCancellable>()
    
        func downloadImage(url: String) {
            URLSession.shared.dataTaskPublisher(for: URL(string: url)!)
            .subscribe(on: DispatchQueue.global(qos: .background))
            .map { UIImage(data: $0.data) }
            .receive(on: DispatchQueue.main)
            .handleEvent(receiveSubscription: { _ in
                print("Download started")
                rint("Start subscription on the main thread: \(Thread.isMainThread)")
            }, receiveOutput: { image in
                print("Downloaded image")
            }, receiveCompletion: { completion in
                print("Download completed")
            })
            .sink(receiveCompletion: { completion in
                print("Finished subscription on the main thread: \(Thread.isMainThread)")
                switch completion {
                    case .failure(let error):
                        print("Stream has failed with error: \(error)")
                    case .finished:
                        print("Stream has completed")
                }
            }, receiveValue: { [weak self] image in
                print("Recieved subscription on the main thread: \(Thread.isMainThread)")
                self?.image.send(image)
                self?.image.send(completion: .finished)
            })
            .store(in: &subscriptions)
        }
    }
    
    // binding
    imgDownlloaderViewModel.image.assign(to: \.image, on: contentImageView)
    .store(in: &subscriptions)
    
    // trigger
    imgDownloderViewModel.downloadImage(url: "https://www.example.com/image.jpg")
    
    

    Memory Management

    上面的例子中, 有个assign(to: on:)方法, assign给了self的一个属性, 这就造成了循环引用, 导致这个viewmodel不能被释放掉, 如果存在这种情况, 就不要偷懒了, 还是用sink方法, 在回调里用weak self.

    SwiftUI

    • 一个@Published属性, 就是一个CurrentValueSubject

    自定义Publisher

    网络请求转成publisher, 使用eraseToAnyPublisher

    func request<T: Decodable>(url: URL) -> AnyPublisher<T, Error> {
        return URLSession.shared.dataTaskPublisher(for: url)
        .map(\.data)
        .decode(type: T.self, decoder: JSONDecoder())
        .eraseToAnyPublisher()
        .store(in: &subscriptions)
    }
    

    如果在里面catch了, 就可以返回AnyPublisher<T, Never>

    Future, Defered

    let future = Future<Int, Error> { promise in
        DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
            promise(.success(42))
        }
    }
    
    let publisher = future.eraseToAnyPublisher()
    
    // deferred
    let deferred = Deferred {
        Future<Int, Error> { promise in
            DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
                promise(.success(42))
            }
        }
    }
    
    let publisher = deferred.eraseToAnyPublisher()
    

    相关文章

      网友评论

          本文标题:Swift Combine

          本文链接:https://www.haomeiwen.com/subject/pgwahjtx.html