55//
66
77import Foundation
8+ import Combine
89@preconcurrency import AVFoundation
910import UIKit. UIImage
1011
@@ -46,6 +47,12 @@ actor CaptureService {
4647 /// A serial dispatch queue to use for capture control actions.
4748 private let sessionQueue = DispatchSerialQueue ( label: " com.melikyan.CameraView " )
4849
50+ /// Cancel the async notification loops using this collection; internal.
51+ private var cancellables = Set < AnyCancellable > ( )
52+
53+ /// In addition to `cancelables`, subject area change notifications should be reset every time an input device is changed.
54+ private var subjectAreaChangeTask : Task < Void , Never > ?
55+
4956 /// Sets the session queue as the actor's executor.
5057 nonisolated var unownedExecutor : UnownedSerialExecutor {
5158 sessionQueue. asUnownedSerialExecutor ( )
@@ -251,16 +258,19 @@ actor CaptureService {
251258 private func observeSubjectAreaChanges( of device: AVCaptureDevice ) {
252259 // Cancel the previous observation task.
253260 subjectAreaChangeTask? . cancel ( )
254- subjectAreaChangeTask = Task { [ weak self] in
261+ let task = Task { [ weak self] in
255262 // Signal true when this notification occurs.
256263 for await _ in NotificationCenter . default. notifications ( named: AVCaptureDevice . subjectAreaDidChangeNotification, object: device) . compactMap ( { _ in true } ) {
257264 guard let self else { return }
258265 // Perform a system-initiated focus and expose.
259266 try ? await focusAndExpose ( at: CGPoint ( x: 0.5 , y: 0.5 ) , isUserInitiated: false )
260267 }
261268 }
269+ subjectAreaChangeTask = task
270+ AnyCancellable {
271+ task. cancel ( )
272+ } . store ( in: & cancellables)
262273 }
263- private var subjectAreaChangeTask : Task < Void , Never > ?
264274
265275 private func focusAndExpose( at devicePoint: CGPoint , isUserInitiated: Bool ) throws {
266276 // Configure the current device.
@@ -304,7 +314,7 @@ actor CaptureService {
304314
305315 /// Observe capture-related notifications.
306316 private func observeNotifications( ) {
307- Task { [ weak self] in
317+ let task = Task { [ weak self] in
308318 for await error in NotificationCenter . default. notifications ( named: AVCaptureSession . runtimeErrorNotification)
309319 . compactMap ( { $0. userInfo ? [ AVCaptureSessionErrorKey] as? AVError } ) {
310320 // If the system resets media services, the capture session stops running.
@@ -316,6 +326,9 @@ actor CaptureService {
316326 }
317327 }
318328 }
329+ AnyCancellable {
330+ task. cancel ( )
331+ } . store ( in: & cancellables)
319332 }
320333
321334 // MARK: - Photo capture
0 commit comments