Last active
September 16, 2022 15:10
-
-
Save carlynorama/054f9eece02b21e05e82e64676dc0c01 to your computer and use it in GitHub Desktop.
NotificationCenter async ways to share with a view
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// ComparingApproaches.swift | |
// NotificationTasks | |
// | |
// Created by carlynorama on 9/15/22. | |
// | |
// | |
// https://www.donnywals.com/comparing-lifecycle-management-for-async-sequences-and-publishers/ (code appraoch has been depricated since written) | |
// for the sequence: https://talk.objc.io/episodes/S01E303-visualizing-async-algorithms-merging-async-streams | |
// for the stream: https://www.hackingwithswift.com/quick-start/concurrency/how-to-create-a-custom-asyncsequence | |
import Foundation | |
import Combine | |
import SwiftUI | |
struct ContainerView: View { | |
@State var showExampleView = false | |
var body: some View { | |
Button("Show example") { | |
showExampleView = true | |
}.sheet(isPresented: $showExampleView) { | |
Comparison() | |
} | |
} | |
} | |
//Note, these examples leave out making The VM an observable object and updating the publised var. | |
class ComparisonViewModel { | |
private var tasksToCancel:[Task<(), Never>] = [] | |
func tearDown() { | |
for task in tasksToCancel { | |
task.cancel() | |
} | |
} | |
func notificationCenterPublisher() -> AnyPublisher<UIDeviceOrientation, Never> { | |
NotificationCenter.default.publisher(for: UIDevice.orientationDidChangeNotification) | |
.map { _ in UIDevice.current.orientation } | |
.eraseToAnyPublisher() | |
} | |
func notificationCenterSequence() async -> AsyncMapSequence<NotificationCenter.Notifications, UIDeviceOrientation> { | |
await NotificationCenter.default.notifications(named: UIDevice.orientationDidChangeNotification) | |
.map { _ in await UIDevice.current.orientation } | |
} | |
var stream:AsyncStream<UIDeviceOrientation> { | |
return AsyncStream { continuation in | |
let streamObserver = Task { | |
let sequence = await notificationCenterSequence() | |
for await orientation in sequence { | |
print("\(orientation)") | |
continuation.yield(orientation) | |
} | |
} | |
tasksToCancel.append(streamObserver) | |
} | |
} | |
} | |
struct Comparison: View { | |
@State var isPortraitFromPublisher = false | |
@State var isPortraitFromSequence = false | |
@State var isPortraitFromLocalSequence = false | |
let viewModel = ComparisonViewModel() | |
var body: some View { | |
VStack { | |
Text("Portrait from publisher: \(isPortraitFromPublisher ? "yes" : "no")") | |
Text("Portrait from sequence: \(isPortraitFromSequence ? "yes" : "no")") | |
Text("Portrait from local sequence: \(isPortraitFromLocalSequence ? "yes" : "no")") | |
} | |
//Bespoke publisher. | |
.onReceive(viewModel.notificationCenterPublisher()) { orientation in | |
isPortraitFromPublisher = orientation == .portrait | |
} | |
//Turinging the notification into a custom async sequence. | |
.task { await watchForFlips() } | |
//Async stream. Requires teardown as written. | |
.task { | |
for await value in viewModel.stream { | |
isPortraitFromSequence = value == .portrait | |
} | |
} | |
.onDisappear(perform: viewModel.tearDown) | |
// can of course do it all inline. | |
// .task { | |
// let sequence = NotificationCenter.default.notifications(named: UIDevice.orientationDidChangeNotification) | |
// .map { _ in await UIDevice.current.orientation } | |
// for await orientation in sequence { | |
// isPortraitFromLocalSequence = orientation == .portrait | |
// print(orientation) | |
// } | |
// } | |
} | |
func watchForFlips() async { | |
let flipWatcher = FlipWatcher() | |
do { | |
for try await value in flipWatcher { | |
withAnimation { | |
isPortraitFromLocalSequence = value == .portrait | |
print("FlipWatcher: \(value)") | |
} | |
} | |
} catch { | |
} | |
} | |
} | |
struct FlipWatcher: AsyncSequence, AsyncIteratorProtocol { | |
typealias Element = UIDeviceOrientation | |
private var isActive = true | |
mutating func next() async throws -> Element? { | |
guard isActive else { return nil } | |
let sequence = await NotificationCenter.default.notifications(named: UIDevice.orientationDidChangeNotification) | |
.map { _ in await UIDevice.current.orientation } | |
for await orientation in sequence { | |
print("\(orientation)") | |
return orientation | |
} | |
return nil | |
} | |
func makeAsyncIterator() -> FlipWatcher { | |
self | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment