Swift Combine removeDuplicates() not working as expected

Viewed 3060

I am trying to filter only .portrait and .landscape orientation and then apply distinctUntilChanged i.e. removeDuplicates()

But removeDuplicates() seems to be never called as my debugger never stops inside comparator

and onReceive callback is called.

 // MARK: - Publishers
    private var orientationChange = NotificationCenter.default.publisher(for: UIDevice.orientationDidChangeNotification)
        .map { toOrientation($0) }
        .filter { filterOrientation($0) }
        //.removeDuplicates()
        .removeDuplicates(by: { o1, o2 in
           _ = filterOrientation(o1)
           _ = filterOrientation(o2)
            return false
        })
        .receive(on: DispatchQueue.main)

How to allow only .portrait, .landscape, .portrait, .landscape sequences and not .portrait, .portrait, .landscape etc

Update

I need to use this, but it adds unnecessary boilerplate code:

.onReceive(orientationChange) { orientation in
                guard orientation != self.lastOrientation else { return }
                self.lastOrientation = orientation

}

Here are internal implementations of helper functions

fileprivate func toOrientation(_ notification: UIKit.Notification) -> UIDeviceOrientation {
    (notification.object as? UIDevice)?.orientation ?? .unknown
}

fileprivate func filterOrientation(_ orientation: UIDeviceOrientation, orientations: [UIDeviceOrientation] = [.landscapeLeft, .landscapeRight, .portrait, .portraitUpsideDown]) -> Bool {

    return orientations.contains(orientation)
}
1 Answers

One simple solution when using Publishers in SwiftUI View is to add @State

// MARK: - Publishers
    @State private var orientationChange = NotificationCenter.default.publisher(for: UIDevice.orientationDidChangeNotification)
        .map { toOrientation($0) }
        .filter { filterOrientation($0) }
        .removeDuplicates()
        .receive(on: DispatchQueue.main)

Otherwise this publisher will be recreated on each view redraw so it loses orientation change events and cannot removeDuplicates as there is always one event emitted while rotating

Other way if you use ViewModel : ObservableObject I think it will be good to move this publisher there and add @ObservedObject property in SwiftUI view.

Related