Skip to content

Commit 44d3c50

Browse files
committed
refactoring
1 parent ce5ae1b commit 44d3c50

File tree

6 files changed

+61
-43
lines changed

6 files changed

+61
-43
lines changed

Sources/swiftui-loop-videoplayer/fn/fn+.swift

Lines changed: 0 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -44,31 +44,6 @@ fileprivate func extractExtension(from name: String) -> String? {
4444
return nil
4545
}
4646

47-
/// Cleans up the resources associated with a video player.
48-
/// This function nullifies references to the player, player looper, and observers to facilitate resource deallocation and prevent memory leaks.
49-
/// - Parameters:
50-
/// - player: A reference to the AVQueuePlayer instance. This parameter is passed by reference to allow the function to nullify the external reference.
51-
/// - playerLooper: A reference to the AVPlayerLooper associated with the player. This is also passed by reference to nullify and help in cleaning up.
52-
/// - statusObserver: A reference to an observer watching the player's status changes. Passing by reference allows the function to dispose of it properly.
53-
/// - errorObserver: A reference to an observer monitoring errors from the player. It is managed in the same way as statusObserver to ensure proper cleanup.
54-
func cleanUp(player: inout AVQueuePlayer?, playerLooper: inout AVPlayerLooper?, errorObserver: inout NSKeyValueObservation?) {
55-
// Invalidate and remove references to observers
56-
errorObserver?.invalidate()
57-
errorObserver = nil
58-
59-
// Pause the player and release player-related resources
60-
player?.pause()
61-
player = nil
62-
playerLooper?.disableLooping()
63-
playerLooper = nil
64-
65-
// Debugging statement to confirm cleanup in debug builds
66-
#if DEBUG
67-
print("Cleaned up AVPlayer and observers.")
68-
#endif
69-
}
70-
71-
7247
/// Detects and returns the appropriate error based on settings and asset.
7348
/// - Parameters:
7449
/// - settings: The settings for the video player.

Sources/swiftui-loop-videoplayer/protocol/player/AbstractPlayer.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import AVFoundation
99
import CoreImage
1010

1111
@available(iOS 14, macOS 11, tvOS 14, *)
12-
@MainActor
12+
@MainActor @preconcurrency
1313
public protocol AbstractPlayer: AnyObject {
1414

1515
/// Retrieves the current item being played.

Sources/swiftui-loop-videoplayer/protocol/view/LoopPlayerViewProtocol.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import SwiftUI
1111
/// Protocol that defines the common functionalities and properties
1212
/// for looping video players on different platforms.
1313
@available(iOS 14, macOS 11, tvOS 14, *)
14-
@MainActor
14+
@MainActor @preconcurrency
1515
public protocol LoopPlayerViewProtocol {
1616

1717
#if canImport(UIKit)

Sources/swiftui-loop-videoplayer/protocol/view/LoopingPlayerProtocol.swift

Lines changed: 55 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import AppKit
1717
/// Conforming types are expected to manage a video player that can loop content continuously,
1818
/// handle errors, and notify a delegate of important events.
1919
@available(iOS 14, macOS 11, tvOS 14, *)
20-
@MainActor
20+
@MainActor @preconcurrency
2121
public protocol LoopingPlayerProtocol: AbstractPlayer, LayerMakerProtocol{
2222

2323
#if canImport(UIKit)
@@ -61,7 +61,7 @@ public protocol LoopingPlayerProtocol: AbstractPlayer, LayerMakerProtocol{
6161
func handlePlayerError(_ player: AVPlayer)
6262
}
6363

64-
extension LoopingPlayerProtocol {
64+
internal extension LoopingPlayerProtocol {
6565

6666
/// Updates the player to play a new asset and handles the playback state.
6767
///
@@ -80,22 +80,21 @@ extension LoopingPlayerProtocol {
8080
if wasPlaying {
8181
player.pause()
8282
}
83-
83+
8484
// Cleaning
8585
unloop()
86-
while player.items().count > 0 {
87-
player.advanceToNextItem()
88-
}
86+
clearPlayerQueue()
8987
removeAllFilters()
88+
9089

9190
// Replace the current item
9291
let newItem = AVPlayerItem(asset: asset)
9392
player.replaceCurrentItem(with: newItem)
9493
loop()
95-
96-
player.seek(to: .zero, completionHandler: { _ in
94+
95+
player.seek(to: .zero, completionHandler: { [weak self] _ in
9796
if wasPlaying {
98-
self.player?.play()
97+
self?.play()
9998
}
10099
})
101100
}
@@ -131,7 +130,7 @@ extension LoopingPlayerProtocol {
131130
/// - Parameters:
132131
/// - player: The AVQueuePlayer to be configured.
133132
/// - gravity: The AVLayerVideoGravity determining how the video content should be scaled or fit within the player layer.
134-
internal func configurePlayer(_ player: AVQueuePlayer, gravity: AVLayerVideoGravity) {
133+
func configurePlayer(_ player: AVQueuePlayer, gravity: AVLayerVideoGravity) {
135134
player.isMuted = true
136135
playerLayer.player = player
137136
playerLayer.videoGravity = gravity
@@ -162,6 +161,16 @@ extension LoopingPlayerProtocol {
162161
self?.handlePlayerError(player)
163162
}
164163
}
164+
165+
/// Removes observers for handling errors.
166+
///
167+
/// This method ensures that the error observer is properly invalidated and the reference is cleared.
168+
/// It is important to call this method to prevent memory leaks and remove any unwanted side effects
169+
/// from obsolete observers.
170+
func removeObservers() {
171+
errorObserver?.invalidate()
172+
errorObserver = nil
173+
}
165174

166175

167176
/// Responds to changes in the status of an AVPlayerItem.
@@ -184,6 +193,42 @@ extension LoopingPlayerProtocol {
184193
delegate?.didReceiveError(.remoteVideoError(error))
185194
}
186195

196+
/// Cleans up the player and its associated resources.
197+
///
198+
/// This function performs several cleanup tasks to ensure that the player is properly
199+
/// decommissioned. It pauses playback, removes any registered observers, stops any
200+
/// looping, removes all applied filters, and finally nils out the player to release it
201+
/// for garbage collection. This method is marked with `@preconcurrency` to indicate that
202+
/// it is safe to call in both concurrent and non-concurrent environments, preserving
203+
/// compatibility with older code that does not use Swift's new concurrency model.
204+
@preconcurrency
205+
func cleanUp() {
206+
207+
pause()
208+
209+
removeObservers()
210+
211+
unloop()
212+
213+
clearPlayerQueue()
214+
215+
removeAllFilters()
216+
217+
player = nil
218+
219+
#if DEBUG
220+
print("Cleaned up AVPlayer and observers.") // Debug log for confirming cleanup.
221+
#endif
222+
}
223+
224+
func clearPlayerQueue() {
225+
guard let items = player?.items() else { return }
226+
for item in items {
227+
// Additional cleanup or processing here
228+
player?.remove(item)
229+
}
230+
}
231+
187232
func setCommand(_ value: PlaybackCommand) {
188233
/// Sets the playback command for the video player.
189234
/// - Parameter value: The `PlaybackCommand` to set. This can be one of the following:

Sources/swiftui-loop-videoplayer/view/loop/player/ios/LoopingPlayerUIView.swift

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import AVKit
1515
import UIKit
1616

1717
@available(iOS 14.0, tvOS 14.0, *)
18-
@MainActor
18+
@MainActor @preconcurrency
1919
class LoopingPlayerUIView: UIView, LoopingPlayerProtocol {
2020

2121
/// `filters` is an array that stores CIFilter objects used to apply different image processing effects
@@ -71,9 +71,8 @@ class LoopingPlayerUIView: UIView, LoopingPlayerProtocol {
7171
///
7272
/// This method invalidates the status and error observers to prevent memory leaks,
7373
/// pauses the player, and clears out player-related references to assist in clean deinitialization.
74-
/// It also conditionally logs the cleanup process during debug mode.
7574
deinit {
76-
cleanUp(player: &player, playerLooper: &playerLooper, errorObserver: &errorObserver)
75+
cleanUp()
7776
}
7877
}
7978
#endif

Sources/swiftui-loop-videoplayer/view/loop/player/mac/LoopingPlayerNSView.swift

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import AppKit
1717
/// A NSView subclass that loops video using AVFoundation on macOS.
1818
/// This class handles the initialization and management of a looping video player with customizable video gravity.
1919
@available(macOS 11.0, *)
20-
@MainActor
20+
@MainActor @preconcurrency
2121
class LoopingPlayerNSView: NSView, LoopingPlayerProtocol {
2222

2323
/// `filters` is an array that stores CIFilter objects used to apply different image processing effects
@@ -73,9 +73,8 @@ class LoopingPlayerNSView: NSView, LoopingPlayerProtocol {
7373
///
7474
/// This method invalidates the status and error observers to prevent memory leaks,
7575
/// pauses the player, and clears out player-related references to assist in clean deinitialization.
76-
/// It also conditionally logs the cleanup process during debug mode.
7776
deinit {
78-
cleanUp(player: &player, playerLooper: &playerLooper, errorObserver: &errorObserver)
77+
cleanUp()
7978
}
8079
}
8180
#endif

0 commit comments

Comments
 (0)