Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions .github/workflows/build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,19 @@ jobs:
ruby-version: 3.3.6
bundler-cache: true

- name: Set Xcode version
uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: "16.4"

- name: Check Xcode
run: xcode-select -p && xcodebuild -version

- name: Check Simulators
run: |
xcrun simctl list runtimes
xcodebuild -scheme Example-iOS -showdestinations

# - name: Check Cache
# uses: actions/cache@v2
# id: cocoapods-cache
Expand Down
4 changes: 2 additions & 2 deletions Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ PODS:
- Nimble (13.7.1):
- CwlPreconditionTesting (~> 2.2.0)
- Quick (7.6.2)
- ZonPlayer (1.0.0)
- ZonPlayer (1.1.0)

DEPENDENCIES:
- Nimble (= 13.7.1)
Expand Down Expand Up @@ -40,7 +40,7 @@ SPEC CHECKSUMS:
CwlPreconditionTesting: 67a0047dd4de4382b93442c0e3f25207f984f35a
Nimble: 317d713c30c3336dd8571da1889f7ec3afc626e8
Quick: b8bec97cd4b9f21da0472d45580f763b801fc353
ZonPlayer: 378854ff3d4b4d0f02f210fb4ba6fbf925a4af14
ZonPlayer: 8fc8075486c80850d07ba58484415a880f2a1790

PODFILE CHECKSUM: 87dcc43d2e4b1ed78b2b835c6c927b4bdee27a9e

Expand Down
4 changes: 2 additions & 2 deletions Pods/Local Podspecs/ZonPlayer.podspec.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions Pods/Manifest.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1,040 changes: 518 additions & 522 deletions Pods/Pods.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion Pods/Target Support Files/ZonPlayer/ZonPlayer-Info.plist

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 11 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,16 +25,24 @@ ZonPlayer is a player library base on AVPlayer with cache and remote control sup

```swift

let player: ZonPlayable = ZonPlayer.player(URLConvertible)
.session(ZonPlayer.Sessionable)
.cache(ZonPlayer.Cacheable) // Conform ZonPlayer.Cacheable to customize cache category.
let player: ZonPlayable = ZonPlayer.player(any URLConvertible)
.session(any ZonPlayer.Sessionable)
.cache(any ZonPlayer.Cacheable) // Conform ZonPlayer.Cacheable to customize cache category.
.remoteControl(self) { wlf, payload in // Conform ZonPlayer.RemoteControllable to customize background playback controller.
payload.title(String).artist(String)....
}
.onPaused(self) { wlf, payload in // Conform ZonPlayer.Observable to listen player.
}
.activate(in: ZonPlayerView)

// or
let player: ZonPlayable = ZonPlayer.player(any URLConvertible)
.on(\.session, any ZonPlayer.Sessionable)
.on(\.cache, any ZonPlayer.Cacheable)
.on(\.finish, .init { in })
.on(\.pause, .init(on: self, block: { wlf, player in }))
.activate()

// Conform ZonPlayer.Controllable to control player instance.
player.pause()
player.play()
Expand Down
4 changes: 1 addition & 3 deletions Sources/Core/Private/Builder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,12 @@
//

final class Builder: ZonPlayer.Settable, @unchecked Sendable {

let url: URLConvertible & Sendable
init(url: URLConvertible & Sendable) {
self.url = url
}

var progressInterval: TimeInterval = 1
var maxRetryCount: Int = 1

// MARK: - ZPObservable
var callbackQueue: DispatchQueue = .main
Expand All @@ -22,7 +20,7 @@ final class Builder: ZonPlayer.Settable, @unchecked Sendable {
var play: ZonPlayer.Delegate<(ZonPlayable, Float), Void>?
var pause: ZonPlayer.Delegate<ZonPlayable, Void>?
var finish: ZonPlayer.Delegate<(ZonPlayable, URL), Void>?
var error: ZonPlayer.Delegate<(ZonPlayable, ZonPlayer.Error), Void>?
var error: ZonPlayer.Delegate<(ZonPlayable?, ZonPlayer.Error), Void>?
var progress: ZonPlayer.Delegate<(ZonPlayable, TimeInterval, TimeInterval), Void>?
var duration: ZonPlayer.Delegate<(ZonPlayable, TimeInterval), Void>?
var background: ZonPlayer.Delegate<(ZonPlayable, Bool), Void>?
Expand Down
2 changes: 1 addition & 1 deletion Sources/Core/Private/CallbackCompositer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ final class CallbackCompositer: ZonPlayer.Observable, @unchecked Sendable {
}
}()

lazy var error: ZonPlayer.Delegate<(ZonPlayable, ZonPlayer.Error), Void>? = {
lazy var error: ZonPlayer.Delegate<(ZonPlayable?, ZonPlayer.Error), Void>? = {
.init().delegate(on: self) { wlf, input in
wlf._callback { $0.error?.call(input) }
wlf._monitor { $0.player(input.0, playFailed: input.1) }
Expand Down
29 changes: 0 additions & 29 deletions Sources/Core/Private/Faker.swift

This file was deleted.

46 changes: 23 additions & 23 deletions Sources/Core/ZonPlayer+Manager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ extension ZonPlayer {
}
}

lazy var logQueue: DispatchQueue = {
.init(label: "com.zonplayer.log")
}()

private init() {}

/**
Expand All @@ -39,38 +43,34 @@ extension ZonPlayer {
private lazy var _sessionQueue: DispatchQueue = {
.init(label: "com.zonplayer.session", qos: .userInitiated)
}()

lazy var logQueue: DispatchQueue = {
.init(label: "com.zonplayer.log")
}()
}
}

extension ZonPlayer.Manager {
func start(
setter: ZonPlayer.Settable,
in view: ZonPlayerView? = nil
) -> ZonPlayable {
) -> some ZonPlayable {
let url: URL
do {
let url = try setter.url.asURL()
let observer = CallbackCompositer(observer: setter, monitors: monitors, callbackQueue: logQueue)
let session: (ZonPlayer.Sessionable, DispatchQueue)? = {
if let session = setter.session { return (session, _sessionQueue) }
return nil
}()
return Player(
url: url,
session: session,
retry: setter.retry,
cache: setter.cache,
observer: observer,
remoteControl: setter.remoteControl,
view: view
)
url = try setter.url.asURL()
} catch {
let dummy = Faker()
setter.callbackQueue.async { setter.error?.call((dummy, .invalidURL(setter.url))) }
return dummy
url = URL(string: "https://zonplayer.faker").unsafelyUnwrapped
setter.callbackQueue.async { setter.error?.call((nil, .invalidURL(setter.url))) }
}
let observer = CallbackCompositer(observer: setter, monitors: monitors, callbackQueue: logQueue)
let session: (ZonPlayer.Sessionable, DispatchQueue)? = {
if let session = setter.session { return (session, _sessionQueue) }
return nil
}()
return Player(
url: url,
session: session,
retry: setter.retry,
cache: setter.cache,
observer: observer,
remoteControl: setter.remoteControl,
view: view
)
}
}
4 changes: 2 additions & 2 deletions Sources/Core/ZonPlayer+Monitorable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ extension ZonPlayer {

func playerPlayDidFinish(_ player: ZonPlayable, url: URL)

func player(_ player: ZonPlayable, playFailed error: ZonPlayer.Error)
func player(_ player: ZonPlayable?, playFailed error: ZonPlayer.Error)

func player(_ player: ZonPlayable, playProgressDidChange currentTime: TimeInterval, totalTime: TimeInterval)

Expand All @@ -35,7 +35,7 @@ extension ZonPlayer.Monitorable {
public func player(_ player: ZonPlayable, didPlay rate: Float) {}
public func playerDidPause(_ player: ZonPlayable) {}
public func playerPlayDidFinish(_ player: ZonPlayable, url: URL) {}
public func player(_ player: ZonPlayable, playFailed error: ZonPlayer.Error) {}
public func player(_ player: ZonPlayable?, playFailed error: ZonPlayer.Error) {}
public func player(
_ player: ZonPlayable,
playProgressDidChange currentTime: TimeInterval,
Expand Down
4 changes: 2 additions & 2 deletions Sources/Usage/Player/ZonPlayer+Observable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ extension ZonPlayer {
var play: Delegate<(ZonPlayable, Float), Void>? { get nonmutating set }
var pause: Delegate<ZonPlayable, Void>? { get nonmutating set }
var finish: Delegate<(ZonPlayable, URL), Void>? { get nonmutating set }
var error: Delegate<(ZonPlayable, ZonPlayer.Error), Void>? { get nonmutating set }
var error: Delegate<(ZonPlayable?, ZonPlayer.Error), Void>? { get nonmutating set }
var progress: Delegate<(ZonPlayable, TimeInterval, TimeInterval), Void>? { get nonmutating set }
var duration: Delegate<(ZonPlayable, TimeInterval), Void>? { get nonmutating set }
var background: Delegate<(ZonPlayable, Bool), Void>? { get nonmutating set }
Expand Down Expand Up @@ -63,7 +63,7 @@ extension ZonPlayer.Observable {
}

/// Listen to player failed because of an error.
public func onError<T: AnyObject>(_ target: T, block: ((T, (ZonPlayable, ZonPlayer.Error)) -> Void)?) -> Self {
public func onError<T: AnyObject>(_ target: T, block: ((T, (ZonPlayable?, ZonPlayer.Error)) -> Void)?) -> Self {
error = (error ?? .init()).delegate(on: target, block: block)
return self
}
Expand Down
8 changes: 8 additions & 0 deletions Sources/Usage/Player/ZonPlayer+Settable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,11 @@ extension ZonPlayer.Settable {
ZonPlayer.Manager.shared.start(setter: self, in: view)
}
}

extension ZonPlayer.Settable {
@discardableResult
public func on<Value>(_ keyPath: ReferenceWritableKeyPath<Self, Value>, _ value: Value) -> Self {
self[keyPath: keyPath] = value
return self
}
}
21 changes: 20 additions & 1 deletion Sources/Usage/ZonPlayer+Delegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,22 @@
extension ZonPlayer {
// From https://github.com/onevcat/Kingfisher/blob/277f1ab2c6664b19b4a412e32b094b201e2d5757/Sources/Utility/Delegate.swift#L71
public final class Delegate<Input, Output>: @unchecked Sendable {
public typealias Block = (Input) -> Output?

private var block: Block?
public init() {}

private var block: ((Input) -> Output?)?
public init(block: @escaping Block) {
self.block = block
}

public init<T: AnyObject>(on target: T, block: @escaping (T, Input) -> Output) {
self.block = { [weak target] input in
guard let target = target else { return nil }
return block(target, input)
}
}

@discardableResult
public func delegate<T: AnyObject>(on target: T, block: ((T, Input) -> Output)?) -> Self {
self.block = { [weak target] input in
Expand All @@ -20,6 +33,12 @@ extension ZonPlayer {
return self
}

@discardableResult
public func delegate(block: ((Input) -> Output)?) -> Self {
self.block = block
return self
}

public func call(_ input: Input) -> Output? { block?(input) }
public func callAsFunction(_ input: Input) -> Output? { call(input) }
}
Expand Down
2 changes: 1 addition & 1 deletion Sources/Usage/ZonPlayer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public enum ZonPlayer {
/// .cache(Cacheable) // Set cache policy like downloading before playback.
///
/// ```
public static func player(_ url: URLConvertible & Sendable) -> Settable {
public static func player(_ url: URLConvertible & Sendable) -> some Settable {
Builder(url: url)
}
}
Expand Down
4 changes: 2 additions & 2 deletions Tests/Playback/ControllerTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
final class ControllerTests: QuickSpec {
override static func spec() {
describe("Test player controller") {
it("play") {
it("Play") {
waitUntil(timeout: .seconds(5)) { done in
let player = ZonPlayer
.player(self._url)
Expand All @@ -26,7 +26,7 @@ final class ControllerTests: QuickSpec {
}
}

it("pause") {
it("Pause") {
waitUntil(timeout: .seconds(5)) { done in
let player = ZonPlayer
.player(self._url)
Expand Down
13 changes: 8 additions & 5 deletions Tests/Playback/InvalidInitializationTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,19 @@ final class InvalidInitializationTests: QuickSpec {
override static func spec() {
describe("Test player initialization") {
it("Invalid url") {
waitUntil { done in
waitUntil(timeout: .seconds(5)) { done in
var errorsCount = 2
let invalidURL = ""
let player = ZonPlayer
.player(invalidURL)
.cache(ZPC.Streaming())
.onError(_delegate) { _, payload in
guard case .invalidURL = payload.1 else {
self.__zon_triggerUnexpectedError()
return
switch payload.1 {
case .invalidURL, .playerTerminated:
errorsCount -= 1
default: return
}
done()
if errorsCount == 0 { done() }
}
.activate()
self._players.append(player)
Expand Down
Loading