Customize handling of asynchronous events by combining event-processing operators using Combine.

Combine Documentation

Posts under Combine tag

51 Posts
Sort by:
Post not yet marked as solved
1 Replies
763 Views
Context Let say we have an actor, which have a private struct. We want this actor to be reactive, and to give access to a publisher that publishes a specific field of the private struct. The following code seems to work. But I do have questions. public actor MyActor { private struct MyStruct { var publicField: String } @Published private var myStruct: MyStruct? /* We force a non isolated so the property is still accessible from other contexts w/o await in other modules. */ public nonisolated let publicFieldPublisher: AnyPublisher<String?, Never> init() { self.publicFieldPublisher = _myStruct.projectedValue.map{ $0?.publicField }.eraseToAnyPublisher() } } Question 1 & 2 In the init, I use _myStruct.projectedValue, which should be strictly equivalent to $myStruct. Except the latter does not compile. We get the following error: 'self' used in property access '$myStruct' before all stored properties are initialized Why? AFAICT myStruct should be init’d to nil, so where is the problem? And why does the former do compile? Question 3 With _myStruct.projectedValue, I get a warning at compile-time: Actor 'self' can only be passed 'inout' from an async initializer What does that mean? Is it possible to get rid of this warning? Is it “dangerous” (can this cause issues later)? Thanks!
Posted
by frizlab.
Last updated
.
Post not yet marked as solved
0 Replies
1k Views
I'm implementing (for the first time) a web service client in Swift using the Combine framework. For a method that makes a single call to a web service and then closes the connection, what is considered best practice – to return a Future or a Publisher? func getSomeThing() -> Future&lt;SomeThing...&gt; { } func getSomeThing() -> AnyPublisher&lt;SomeThing...&gt; { } In Flutter, a single async value would be a "Future" and multiple async values would be a "Stream". In the WWDC 2019 talk: "Introducing Combine" Apple presents a slide that hints at Futures as the async version of a single sync value. And Publishers as the async version of sync arrays. I understand that Futures in fact are Publishers. And that there may be some subtle technical nuances to consider (greedy/lazy etc). But thinking about the communicated intent, returning a Future seems to indicate a one hit wonder pipeline better than returning an AnyPublisher. But returning a Publisher seems easier and more in line with the framework, since it's pretty easy to eraseToAnyPublisher, for example from an URLSession.DataTaskPublisher, and just return that object. Mapping a DataTaskPublisher pipeline to a Future dosen't seem to be straight forward at all. Any best practice advise would be appreciated.
Posted
by weenzeel.
Last updated
.
Post not yet marked as solved
0 Replies
339 Views
I'm having trouble reasoning about and modifying the Detecting Human Actions in a Live Video Feed sample code since I'm new to Combine. // ---- [MLMultiArray?] -- [MLMultiArray?] ---- // Make an activity prediction from the window. .map(predictActionWithWindow) // ---- ActionPrediction -- ActionPrediction ---- // Send the action prediction to the delegate. .sink(receiveValue: sendPrediction) These are the final two operators of the video processing pipeline, where the action prediction occurs. In either the implementation for private func predictActionWithWindow(_ currentWindow: [MLMultiArray?]) -> ActionPrediction or for private func sendPrediction(_ actionPrediction: ActionPrediction), how might I access the results of a VNHumanBodyPoseRequest that's retrieved and scoped in a function called earlier in the daisy chain? When I did this imperatively, I accessed results in the VNDetectHumanBodyPoseRequest completion handler, but I'm not sure how data flow would work with Combine's programming model. I want to associate predictions with the observation results they're based on so that I can store the time range of a given prediction label.
Posted
by Curiosity.
Last updated
.
Post marked as solved
3 Replies
583 Views
I've been using Combine with UIKit and Cocoa. The following is a simple example. import UIKit import Combine class ViewController: UIViewController { // MARK: - Variables private var cancellableSet: Set<AnyCancellable> = [] @Published var loginText: String = "" @Published var passwordText: String = "" // MARK: - IBOutlet @IBOutlet weak var loginField: UITextField! @IBOutlet weak var passwordField: UITextField! // MARK: - Life cycle override func viewDidLoad() { super.viewDidLoad() NotificationCenter.default.publisher(for: UITextField.textDidChangeNotification, object: loginField) .sink { result in if let textField = result.object as? UITextField { if let text = textField.text { self.loginText = text } } } .store(in: &cancellableSet) NotificationCenter.default.publisher(for: UITextField.textDidChangeNotification, object: passwordField) .sink { result in if let textField = result.object as? UITextField { if let text = textField.text { self.passwordText = text } } } .store(in: &cancellableSet) Publishers.CombineLatest($loginText, $passwordText) .sink { (result0, result1) in if result0.count > 3 && result1.count > 3 { print("You are good") } else { print("No way!!!") } } .store(in: &cancellableSet) } } Now, I want to use Combine with SwiftUI. The following is SwiftUI equivalent, so far. import SwiftUI import Combine struct ContentView: View { @State var anycancellables = Set<AnyCancellable>() @State var userText: String = "" @State var passText: String = "" @State var canSave: Bool = false var body: some View { ZStack { VStack { Color.white }.onTapGesture { UIApplication.shared.endEditing() } VStack { TextField("Username", text: $userText) { }.onChange(of: userText) { newValue in } SecureField("Password", text: $passText) { }.onChange(of: passText) { newValue in } Spacer() .frame(height: 20.0) Button("Save") { print("Saved...") } .foregroundColor(canSave ? Color.black : Color.gray) .font(.system(size: 32.0)) .disabled(!canSave) }.padding(.horizontal, 20.0) } } } So where does Combine fit into the code? I want to enable the Save button if text counts of loginText and passwordText are both greater than 3, which is done at the top with UIKit. Muchos thankos.
Posted
by Tomato.
Last updated
.
Post marked as solved
4 Replies
473 Views
I could do it with completionHandler, but I'm trying to get server data with Combine. The following is what I have. // UIViewController // import UIKit import Combine class ViewController: UIViewController { // MARK: - Variables private var cancellableSet: Set<AnyCancellable> = [] // MARK: - Life cycle override func viewDidLoad() { super.viewDidLoad() let urlStr = "https://api.github.com/repos/ReactiveX/RxSwift/events" let viewModel = ViewModel(urlStr: urlStr, waitTime: 2.0) viewModel.fetchData(urlText: viewModel.urlStr, timeInterval: viewModel.waitTime) .sink { completion in print("complete") } receiveValue: { dataSet in print("count: \(dataSet)") } .store(in: &cancellableSet) print("Yeah...") } } struct DataModel: Hashable, Decodable { let id: String let type: String } // ViewModel // import UIKit import Combine class ViewModel: NSObject { var cancellables = [AnyCancellable]() var urlStr: String var waitTime: Double init(urlStr: String, waitTime: Double) { self.urlStr = urlStr self.waitTime = waitTime } func fetchData(urlText: String, timeInterval: Double) -> Future<[DataModel], Error> { return Future<[DataModel], Error> { [weak self] promise in guard let strongSelf = self else { return } if let url = URL(string: urlText) { var request = URLRequest(url: url) request.timeoutInterval = timeInterval let sessionConfiguration = URLSessionConfiguration.default let publisher = URLSession(configuration: sessionConfiguration).dataTaskPublisher(for: request) publisher.sink { completion in print("complete") } receiveValue: { (data: Data, response: URLResponse) in do { let dataModels = try JSONDecoder().decode([DataModel].self, from: data) promise(.success(dataModels)) } catch { print("Error while parsing: \(error)") promise(.failure("Failure" as! Error)) } } .store(in: &strongSelf.cancellables) } else { promise(.failure("Failure" as! Error)) } } } } If I run it, I don't get an error. The app doesn't crash, either. The view controller doesn't deliver anything. What am I doing wrong? Muchos thankos.
Posted
by Tomato.
Last updated
.
Post not yet marked as solved
1 Replies
337 Views
When crash by assertNoFailure, I cannot see the message. smaple code View import SwiftUI struct ContentView: View {   @ObservedObject private var viewModel = ContentViewModel()   var body: some View {       Button(action: { viewModel.crash() }, label: { Text("button") })   } } ViewModel import Foundation import Combine class ContentViewModel: ObservableObject {   var cancellables = Set<AnyCancellable>()   private enum AddOneError: Swift.Error {     case error   }   private func addOnePublisher(_ n: Int) -> AnyPublisher<Int, AddOneError> {     Deferred {       Future { (promise) in         guard n < 10 else {           promise(.failure(AddOneError.error))           return         }         promise(.success(n + 1))       }     }     .eraseToAnyPublisher()   }   func crash() {     addOnePublisher(10)       .assertNoFailure("I want to disply this message.")       .sink { (completion) in         switch completion {         case .finished:           break         case .failure(let error):           print(error)         }       } receiveValue: { (n) in         print(n)       }       .store(in: &cancellables)   } } when crash if I use .catch { error -> AnyPublisher<Int, Never> in fatalError("I can see this message") } instead of assertNoFailure I can see log Fatal error: I can see this message How can I see the prefix of assertNoFailure? And if not, when is it used?
Posted Last updated
.
Post not yet marked as solved
0 Replies
440 Views
Environment OS: macOS Monterey 12.0.1 Xcode: 13.1 Context & Issue I'm implementing a feature which calls an API to fetch new data as an observed value changes. In order to avoid too many API calls, I tried throttling the observed value publisher. But I found this throttling doesn't work if I apply the .eraseToAnyPublisher() method to the publisher. After some investigation by myself, I noticed the .eraseToAnyPublisher() causes many unnecessary subscriptions and unsubscriptions to the throttled publisher. So I'm wondering if I make any mistakes and would like to get helps. Minimal reproducible example import SwiftUI @main struct SwiftUITestApp: App { var body: some Scene { WindowGroup { ContentView() } } } class ViewModel: ObservableObject { @Published var value: Float = 0 } struct ContentView: View { @StateObject private var viewModel = ViewModel() @State private var text = "" var body: some View { VStack { Text(text) Slider(value: $viewModel.value, in: 0...1) } .padding() .onReceive( viewModel .$value // .eraseToAnyPublisher() // Uncomment this line causes the issue .throttle(for: 1, scheduler: DispatchQueue.main, latest: true) .print() ) { value in text = String(format: "%.2f", arguments: [value]) } } } Behavior without .eraseToAnyPublisher() If I don't apply .eraseToAnyPublisher(), this code works as expected and the .print() of the publisher will output something like: receive subscription: (Throttle) request unlimited request unlimited receive value: (0.0) receive cancel receive subscription: (Throttle) request unlimited request unlimited receive value: (0.0) receive value: (0.021933686) receive value: (0.28136003) receive value: (0.6122359) receive value: (0.66554797) receive value: (0.3587148) receive value: (0.2890992) Behavior with .eraseToAnyPublisher() If I apply .eraseToAnyPublisher(), the throttled publisher gets subscribed and unsubscribed many times. This makes the throttling look not working. The .print() of the publisher will output something like: receive subscription: (Throttle) request unlimited request unlimited receive value: (0.0) receive cancel receive subscription: (Throttle) request unlimited request unlimited receive value: (0.0) receive cancel receive subscription: (Throttle) request unlimited request unlimited receive value: (0.12430311) receive cancel receive subscription: (Throttle) request unlimited request unlimited receive value: (0.12430311) receive cancel receive subscription: (Throttle) request unlimited request unlimited receive value: (0.13761736) receive cancel receive subscription: (Throttle) Question Though I've already found a workaround (= not using .eraseToAnyPublisher()), I would like to understand why this happens. In my understanding, .eraseToAnyPublisher() only erases the type information and should not cause any behavioral changes. Thanks
Posted
by taitor.
Last updated
.
Post not yet marked as solved
0 Replies
332 Views
I'm trying out this code in a playground in Xcode 13.2 beta (13C5066c) import _Concurrency import Combine import PlaygroundSupport import Foundation extension Task where Success == Never, Failure == Never {   static func sleep(seconds: Double) async throws {     try await Self.sleep(nanoseconds: UInt64(1e9 * seconds))   } } Task {   let values = PassthroughSubject<Int, Never>()   Task {     var counter = 0     while true {       counter += 1       print("[SEND] \(counter)")       values.send(counter)       try! await Task.sleep(seconds: Double.random(in: 0.1...0.5))     }   }   for await value in values // vvvvvvvvvvvvv         .buffer(size: Int.max, prefetch: .keepFull, whenFull: .dropOldest) // ^^^^^^^^^^^^^         .values {     print("[RECV] \(value)")     try! await Task.sleep(seconds: 1)   } } PlaygroundPage.current.needsIndefiniteExecution = true This is modeled after real application code. For example, values could be a PassthroughSubject<Packet, NWError> (this is, in fact, what my app code looks like) I've noticed that when doing Publisher.values to convert a Publisher into an AsyncPublisher (so we can use for await), the values aren't buffered. In other words, if we are inside of the body of the for await loop and something is sent to the PassthroughSubject, that value is dropped unless we use .buffer beforehand. This is demonstrated in the playground code above. The outer task receives values, but takes 1 second to process them. The inner task sends values at a fast rate (100ms–500ms). This means that values received during that 1 second period are dropped.(without the .buffer call; with that call, the problem goes away) Is this intentional? I believe this should be more prevalent in documentation. The documentation says that AsyncStream has a buffer: An arbitrary source of elements can produce elements faster than they are consumed by a caller iterating over them. Because of this, AsyncStream defines a buffering behavior, allowing the stream to buffer a specific number of oldest or newest elements. By default, the buffer limit is Int.max, which means the value is unbounded. But AsyncPublisher conforms to AsyncSequence, and not AsyncStream. Maybe this is how this can be fixed?
Posted Last updated
.
Post not yet marked as solved
0 Replies
269 Views
I'm using the Mirror API to access the underlying publisher of properties wrapped with @Published. For some reason, when setting a published property, values aren't received by the publisher I'm accessing after the initial value. In the following code, I would expect the "mirrored" publisher to receive events for eternity. import Combine class Dinosaur: ObservableObject { @Published var angry: Bool = false } let raptor = Dinosaur() //let subscription1 = raptor.$angry // .print("NORMAL") // .sink { value in } var published = Mirror(reflecting: raptor).children.first!.value as! Published<Bool> let publisher = published.projectedValue let subscription2 = publisher .print("MIRRORED") .sink { value in } Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { _ in raptor.angry = !raptor.angry } But, it only has the following output. MIRRORED: receive subscription: (PublishedSubject) MIRRORED: request unlimited MIRRORED: receive value: (false) Interestingly, if the let subscription1 = ... line is uncommented, both sinks receive each successive value successfully. NORMAL: receive subscription: (PublishedSubject) NORMAL: request unlimited NORMAL: receive value: (false) MIRRORED: receive subscription: (PublishedSubject) MIRRORED: request unlimited MIRRORED: receive value: (false) MIRRORED: receive value: (true) NORMAL: receive value: (true) MIRRORED: receive value: (false) NORMAL: receive value: (false) ... etc It's almost like the compiler has to "see" the magic raptor.$angry to set up a proper subscription. What am I doing wrong?
Posted
by dwieeb.
Last updated
.
Post marked as solved
2 Replies
296 Views
If I want to subscribe to four @Published variables at the same time, I can do something like the following. Publishers.CombineLatest4($variable0, $variable1, $variable2, $variable3) I wonder if there is any solution to subscribing to more than four variables at the same time? Muchos thankos
Posted
by Tomato.
Last updated
.
Post marked as solved
2 Replies
555 Views
I'm trying to figure out how to use URLSession with the Combine framework. I have a class that is to fetch data as follows. import UIKit import Combine class APIClient: NSObject { var cancellables = [AnyCancellable]() @Published var models = [MyModel]() func fetchData(urlStr: String) -> AnyPublisher<[MyModel], Never> { guard let url = URL(string: urlStr) else { let subject = CurrentValueSubject<[MyModel], Never>([]) return subject.eraseToAnyPublisher() } let subject = CurrentValueSubject<[MyModel], Never>(models) URLSession.shared.dataTaskPublisher(for: url) .map { $0.data } .decode(type: [MyModel].self, decoder: JSONDecoder()) .replaceError(with: []) .sink { posts in print("api client: \(posts.count)") self.models = posts } .store(in: &cancellables) return subject.eraseToAnyPublisher() } } I then have a view model class that is to deliver data for my view controller as follows. import Foundation import Combine class ViewModel: NSObject { @IBOutlet var apiClient: APIClient! var cancellables = Set<AnyCancellable>() @Published var dataModels = [MyModel]() func getGitData() -> AnyPublisher<[MyModel], Never> { let urlStr = "https://api.github.com/repos/ReactiveX/RxSwift/events" let subject = CurrentValueSubject<[MyModel], Never>(dataModels) apiClient.fetchData(urlStr: urlStr) .sink { result in print("view model: \(result.count)") self.dataModels = result }.store(in: &cancellables) return subject.eraseToAnyPublisher() } } My view controller has an IBOutlet of ViewModel. import UIKit import Combine class ViewController: UIViewController { // MARK: - Variables var cancellables = [AnyCancellable]() @IBOutlet var viewModel: ViewModel! // MARK: - IBOutlet @IBOutlet weak var tableView: UITableView! // MARK: - Life cycle override func viewDidLoad() { super.viewDidLoad() viewModel.getGitData() .sink { posts in print("view controller: \(posts.count)") } .store(in: &cancellables) } } If I run it, it seems that ViewModel returns 0 without waiting for APIClient to return data. And the view controller doesn't wait, either. What am I doing wrong? Can I do it without using the completion handler? In case you need to know what MyModel is, it's a simple struct. struct MyModel: Decodable { let id: String let type: String } Muchos thanks
Posted
by Tomato.
Last updated
.
Post marked as solved
1 Replies
369 Views
I have the following lines of code to subscribe text changes over two text fields. import UIKit import Combine class ViewController: UIViewController { var cancellables = Set<AnyCancellable>() @Published var userText: String = "" @Published var passText: String = "" // MARK: - IBOutlet @IBOutlet var usernameTextField: UITextField! @IBOutlet var passwordTextField: UITextField! // MARK: - Life cycle override func viewDidLoad() { super.viewDidLoad() NotificationCenter.default.publisher(for: UITextField.textDidChangeNotification, object: usernameTextField) .sink(receiveValue: { (result) in if let myField = result.object as? UITextField { if let text = myField.text { self.userText = text } } }) .store(in: &cancellables) NotificationCenter.default.publisher(for: UITextField.textDidChangeNotification, object: passwordTextField) .sink(receiveValue: { (result) in if let myField = result.object as? UITextField { if let text = myField.text { self.passText = text } } }) .store(in: &cancellables) $userText .sink(receiveValue: { text in print(text) }) .store(in: &cancellables) } } In the last several lines, I am printing the text change for userText. Does Combine allow me to observe two variables (userText, passText) at the same time so that I can plug them into a function? If yes, how? Muchos Thankos.
Posted
by Tomato.
Last updated
.
Post marked as solved
3 Replies
397 Views
I'm still a beginner in using Combine. I practice it on and off. Anyway, I have a view model to see changes in two text fields in my view controller as follows. // ViewModel // import Foundation import Combine class LoginViewModel { var cancellable = [AnyCancellable]() init(username: String, password: String) { myUsername = username myPassword = password } @Published var myUsername: String? @Published var myPassword: String? func validateUser() { print("\(myUsername)") print("\(myPassword)") } } And my view controller goes as follows. // ViewController // import UIKit import Combine class HomeViewController: UIViewController { // MARK: - Variables var cancellable: AnyCancellable? // MARK: - IBOutlet @IBOutlet var usernameTextField: UITextField! @IBOutlet var passwordTextField: UITextField! // MARK: - Life cycle override func viewDidLoad() { super.viewDidLoad() cancellable = NotificationCenter.default.publisher(for: UITextField.textDidChangeNotification, object: usernameTextField) .sink(receiveValue: { result in if let textField = result.object as? UITextField { if let text = textField.text { let loginViewModel = LoginViewModel(username: text, password: "") loginViewModel.validateUser() } } }) } } So I use NSNotification as a publisher to see text changes over one of the text fields. And I cannot see text changes over two of them at the same time. Is there a better approach in seeing text changes over two text fields at the same time using Combine? Muchos thankos.
Posted
by Tomato.
Last updated
.
Post not yet marked as solved
0 Replies
798 Views
Hello, I'm trying to work out a simple example to fill table view data with Combine. The following is what I have. import Foundation struct MyModel: Decodable { let id: String let type: String } import UIKit import Combine class APIClient: NSObject { var cancellable: AnyCancellable? let sharedSession = URLSession.shared func fetchData(urlStr: String, completion: @escaping ([MyModel]?) -> Void) { guard let url = URL(string: urlStr) else { return } let publisher = sharedSession.dataTaskPublisher(for: url) cancellable = publisher.sink(receiveCompletion: { (completion) in switch completion { case .failure(let error): print(error) case .finished: print("Success") } }, receiveValue: { (result) in let decoder = JSONDecoder() do { let post = try decoder.decode([MyModel].self, from: result.data) completion(post) } catch let error as NSError { print("\(error)") completion(nil) } }) } } import Foundation class ViewModel: NSObject { @IBOutlet var apiClient: APIClient! var dataModels = [MyModel]() func getGitData(completion: @escaping () -> Void) { let urlStr = "https://api.github.com/repos/ReactiveX/RxSwift/events" apiClient.fetchData(urlStr: urlStr) { (models) in if let myModels = models { self.dataModels = myModels.map { $0 } } completion() } } } import UIKit import Combine class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource { // MARK: - Variables var cancellable: AnyCancellable? @IBOutlet var viewModel: ViewModel! @Published var models = [MyModel]() // MARK: - IBOutlet @IBOutlet weak var tableView: UITableView! // MARK: - Life cycle override func viewDidLoad() { super.viewDidLoad() viewModel.getGitData { self.models = self.viewModel.dataModels } cancellable = $models.sink(receiveValue: { (result) in DispatchQueue.main.async { [weak self] in guard let strongSelf = self else { return } strongSelf.tableView.reloadData() } }) } // MARK: - TableView func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return models.count } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "cell") let dataModel = models[indexPath.row] cell?.textLabel?.text = dataModel.id cell?.detailTextLabel?.text = dataModel.type return cell! } } I'm not quite comfortable with the lines of code under my view controller (ViewController) in using Combine. How can I make them better? Muchos thankos.
Posted
by Tomato.
Last updated
.
Post not yet marked as solved
5 Replies
582 Views
I am using a Combine URLSession to pull data from the Food Data Central API and the Data I am receiving is not being decoded with the JSON decoder. I know the Data is being received because I use the String(data:encoding: .utf8) as a debug print and I can see the downloaded data correctly in the console. I get an error message after the .decode completion failure that says "The data couldn't be read because it isn't in the correct format." I am guessing I have to add something like the "encoder: utf8" statement in the .decode function. Or maybe transform the data in the .tryMap closure before returning. But I have searched the documentation and other sources and have not found anywhere that discusses this. I am a fairly new to Swift (my first real app), I am hoping someone more-experienced can point me in the right direction. My code is as follows: private func fdcSearch(searchFor searchText: String) {         let query = "https://api.nal.usda.gov/fdc/v1/foods/search?api_key=***&amp;query=+Apple%20+Fuji"         let searchURL = "https://api.nal.usda.gov/fdc/v1/foods/search?"         let searchQuery = "&amp;query="+searchString         print(searchURL+devData.apiKey+searchQuery) //        guard let url = URL(string: "https://api.nal.usda.gov/fdc/v1/foods/search?api_key=***&amp;query=AppleFuji") else {         guard let url = URL(string: searchURL+devData.apiKey+searchQuery) else {             print("Guard error on url assignment") // debug statement             return         }         print("In fdcSearch") // debug statement         fdcSearchSubscription = URLSession.shared.dataTaskPublisher(for: url)             .subscribe(on: DispatchQueue.global(qos: .default))             .tryMap { (output) -&gt; Data in                 guard let response = output.response as? HTTPURLResponse, response.statusCode &gt;= 200 &amp;&amp; response.statusCode &lt; 300 else {                     print("bad server response") // debug statement                     throw URLError(.badServerResponse)                 }                 print("got output") // debug statement                 if let dataString = String(data: output.data, encoding: .utf8) { // debug statement                         print("got dataString: \n\(dataString)") // debug statement                     } // debug statement                 return output.data             }             .receive(on: DispatchQueue.main)             .decode(type: [FDCFoodItem].self, decoder: JSONDecoder())             .sink { (completion) in                 switch completion {                 case .finished:                     print("Completion finished") // debug statement                     break                 case .failure(let error):                     print("Completion failed") // debug statement                     print(error.localizedDescription)                 }             } receiveValue: { [weak self] (returnedFoods) in                 self?.foods = returnedFoods                 print("returnedFoods: \(returnedFoods)") // debug statement                 print("self?.foods: \(String(describing: self?.foods))") // debug statement             }     } Any suggestions on how to handle this?
Posted
by SIsraelit.
Last updated
.
Post marked as solved
1 Replies
843 Views
Let me say that I have an IBOutlet object like @IBOutlet weak var deleteButton: UIButton! RxCocoa can make this button tap observable like deleteButton.rx.tap It doesn't look like Combine lets us observe a button tap. Am I right? I find one approach found at the following URL. https://www.avanderlee.com/swift/custom-combine-publisher/ And Combine has no native approach? And you still have to use the IBAction?
Posted
by Tomato.
Last updated
.
Post marked as solved
1 Replies
302 Views
Hola, I have the following simple lines of code. import UIKit import Combine class ViewController: UIViewController { // MARK: - Variables var cancellable: AnyCancellable? @Published var labelValue: String? // MARK: - IBOutlet @IBOutlet weak var textLabel: UILabel! // MARK: - IBAction @IBAction func actionTapped(_ sender: UIButton) { labelValue = "Jim is missing" } // MARK: - Life cycle override func viewDidLoad() { super.viewDidLoad() cancellable = $labelValue .receive(on: DispatchQueue.main) .assign(to: \.text, on: textLabel) } } I just wonder what is the point of specifying the main thread with .receive? If I comment out the receive line, the app will still run without a problem. Muchos thankos
Posted
by Tomato.
Last updated
.
Post not yet marked as solved
0 Replies
288 Views
I have two API call's for data and if openWeatherData fails it prevents planData.trainingdata from loading. Is there a way to workaround this so planData still loads?          List(planData.trainingdata) { index in           ForEach(openWeatherData.openWeatherData) { day in ... } } Here's the API call for openWeather class DownloadOpenWeatherData: ObservableObject {   @Published var openWeatherData: [OpenWeatherDecoder] = []   let locationManager = LocationManager()   var openWeatherCancellabes = Set<AnyCancellable>()   init() {     print("OpenWeather: loading init")     locationManager.startLoc()     getOpenWeatherData(weatherUrl: "https://api.openweathermap.org/data/2.5/onecall?lat=\(locationManager.getLat())&lon=\(locationManager.getLong())&exclude=hourly,minutely&appid")     locationManager.stopLoc()   }   func getOpenWeatherData(weatherUrl: String) {     guard let weatherUrl = URL(string: weatherUrl) else { return }     print("getOpenWeather url \(weatherUrl)")     URLSession.shared.dataTaskPublisher(for: weatherUrl)       .subscribe(on: DispatchQueue.global(qos: .background))       .receive(on: DispatchQueue.main)       .tryMap { (data, response) -> Data in         print(response)         guard           let response = response as? HTTPURLResponse,            response.statusCode >= 200 && response.statusCode < 300 else {           throw URLError(.badServerResponse)         }         print("data \(data)")         return data       }       .decode(type: OpenWeatherDecoder.self, decoder: JSONDecoder())       .sink { (completion) in         print(completion)       } receiveValue: { (returnedWeatherData) in         self.openWeatherData = [returnedWeatherData]         print("returnedWeatherData \(returnedWeatherData)")       }       .store(in: &openWeatherCancellabes)   } }
Posted
by robevans.
Last updated
.
Post marked as solved
1 Replies
343 Views
I have an app that uses Realm as a data storage solution and Combine as a declarative framework. Ever since I upgraded to iOS 15, my app has become unusable because of what looks like a navigation bug. It works fine with iOS 14. The gist of what I believe the bug is, is that when I navigate to a child view and the parent view is updated, the navigation jumps back to the parent view instead of staying in the child view (https://www.youtube.com/watch?v=c-PAPy-RCqg) So in my example, ContentView loads data Realm and creates a list of DetailViews to display the data. When the DetailView is displayed, it writes some back to the Realm table. I have a sample app demonstrating the problem here: https://github.com/skywalkerdude/navsample. Since it is using Cocoapods, make sure you open NavSample.xcworkspace and not NavSample.xcodeproj.
Posted Last updated
.