Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

iOS Combine: using .delay in .flatMap results in data lost

I am learning Combine on iOS. This is my code:

struct ContentView: View {
let aiRepsonse = "View the latest news and breaking news today for U.S., world, weather, entertainment, politics and health at abc.com."

@State private var cancellableSet = Set<AnyCancellable>()
@State private var content = ""

func test() {
    content = ""
    
    aiRepsonse.publisher
        .flatMap{
            return Just($0).delay(for: 0.01, scheduler: RunLoop.main)}
        .sink { _ in
            if content.count != aiRepsonse.count{
                print("error! lost data! content.count is \(content.count),  aiRepsonse.count is \( aiRepsonse.count)")
                print("content    is : \(content)")
                print("aiRepsonse is : \(aiRepsonse)")
            }

        } receiveValue: { value in
            content += String(value)
        }
        .store(in: &cancellableSet)

}

var body: some View {
    VStack {
        Text(content)
            .frame(height: 200)
        Button(action: {
            test()
        }, label: {
            Text("Test")
        })
    }
}
}

If you press Test button several times in a short time, the error log will appear.
For example,

error! lost data! content.count is 104,  aiRepsonse.count is 117
content    is : View the latest news and breaking news today for U., wol, weather     eertainmet, politicsnd heathat ac.com.
aiRepsonse is : View the latest news and breaking news today for U.S., world,     weather, entertainment, politics and health at abc.com.

I don’t understand why some [Just($0).delay()] publishers do not emit character correctly. Is this a back-pressure problem? Thanks a lot.

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

>Solution :

Some data is lost because you are using a RunLoop as the schedular. From this post,

RunLoop.main runs callbacks only when the main run loop is running in the .default mode, which is not the mode used when tracking touch and mouse events. If you use RunLoop.main as a Scheduler, your events will not be delivered while the user is in the middle of a touch or drag.

If you use DispatchQueue.main, no data will be lost.

Another problem is that test might be called when the publisher created by the previous call to test has not completed. This is not much of a problem when the delay is 0.01, but will become a problem when the delay is larger. You will end up having two sinks appending to content at the same time, and content would be longer than expected. This can be solved by cancelling everything in cancellableSet first, before starting a new publisher.

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading