0

I have several steps need to be processed synchronously. And the value resulted from the process are consumed by the view. It's working on iOS 14, but it's crashing on iOS 13. I use a Combine to publish an event to update the value stored inside the view model.

This is the PublisherManager:

final class PublisherManager {
    static let shared = PublisherManager()
    private var cancellable = Set<AnyCancellable>()

    func mainPublisher() -> AnyPublisher<MainInput, Never> {
        mainSubject
            .eraseToAnyPublisher()
    }

    let mainSubject = PassthroughSubject<MainInput, Never>()

    enum MainInput {
        case updateValue()
    }
}

This is the view model:

final class ViewModel: ObservableObject {

    @Published var status: Status = .checking

    init() {
        setObserver()
        start()
    }

    private func setObserver() {
        PublisherManager.shared.mainPublisher()
            .receive(on: RunLoop.main)
            .sink { [weak self] action in
                guard let self = self else { return }
                switch action {
                case .updateValue:
                    self.updateValue()
                }
            }.store(in: &cancellable)
    }

    func start() {
        let dispatchGroup = DispatchGroup()
        let dispatchSemaphore = DispatchSemaphore(value: 1)

        dispatchGroup.enter()
        dispatchQueue.asyncAfter(deadline: DispatchTime.now() + 1) {
            dispatchSemaphore.wait()
            self.getValues { //--> A process to call API
                PublisherManager.shared.pushNotificationTroubleshooterSubject.send(.updateValue())
                dispatchSemaphore.signal()
                dispatchGroup.leave()
            }
        }
        
        dispatchGroup.notify(queue: .main) {
            // Notify
        }
    }
    
    private func updateValue() {
        status = .active
    }
}

When I run it, I got EXC_BAD_ACCESS in the AppDelegate but it doesn't print any error at all on the debugger. If I comment the status = .active code, it doesn't crash.

What am I doing wrong and how can I solve the problem?

mrjimoy_05
  • 3,452
  • 9
  • 58
  • 95
  • `DispatchGroup` and `DispatchSemaphore` to force an asynchronous task to become synchronous is a horrible practice. Especially in a `Combine` context which makes the handling of asynchronous tasks so pretty convenient . `DispatchGroup` is the wrong API anyway. It's been designed to control the timing in a **group** of (multiple) asynchronous tasks in a loop. – vadian Dec 06 '21 at 11:11
  • @vadian but then how to tackle it? I mean, I tried using the `dispatchQueue.sync` but it doesn't work otherwise – mrjimoy_05 Dec 06 '21 at 11:28
  • As I said, Combine provides operators to ***combine*** asynchronous tasks smoothly. – vadian Dec 06 '21 at 11:30
  • @vadian Hmm I am afraid I didn't quite catch that, can you please elaborate a bit more? – mrjimoy_05 Dec 06 '21 at 12:46

1 Answers1

0

Looks like what you did with DispatchGroup and DispatchSemaphore is wrong. The concept of combine this is an similar to asynchronous code execution. So instead of using DispatchGroup and DispatchSemaphore just use chain of publishers with combineLeates and do actions depend on received events

  • 1
    The answer is unclear, please edit your answer to add some detail, specially some code to describe the workflow. Do keep in mind the question specifies he requires timing the tasks as to keep from overloading the API call more often than once a second. – Conor Dec 06 '21 at 16:33