Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make thread safe for iOS #114

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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
223 changes: 105 additions & 118 deletions ios/Plugin/Plugin.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ enum MyError: Error {
@objc(NativeAudio)
public class NativeAudio: CAPPlugin {

var audioList: [String: Any] = [:]
private var audioList: [String: Any] = [:]
private let lock = NSLock()
var fadeMusic = false
var session = AVAudioSession.sharedInstance()

Expand All @@ -32,17 +33,20 @@ public class NativeAudio: CAPPlugin {
}

@objc func configure(_ call: CAPPluginCall) {
self.fadeMusic = call.getBool(Constant.FadeKey, false)
do {
if call.getBool(Constant.FocusAudio, false) {
try self.session.setCategory(AVAudioSession.Category.playback)
} else {
try self.session.setCategory(AVAudioSession.Category.ambient)
if let fade = call.getBool(Constant.FadeKey) {
self.fadeMusic = fade
}
if let focus = call.getBool(Constant.FocusAudio) {
do {
if focus {
try self.session.setCategory(AVAudioSession.Category.playback)
} else {
try self.session.setCategory(AVAudioSession.Category.ambient)
}
} catch {
print("Failed to set setCategory audio")
}
} catch {
print("Failed to set setCategory audio")
}
call.resolve()
}

@objc func preload(_ call: CAPPluginCall) {
Expand All @@ -53,29 +57,23 @@ public class NativeAudio: CAPPlugin {
let audioId = call.getString(Constant.AssetIdKey) ?? ""
let time = call.getDouble("time") ?? 0
if audioId != "" {
let queue = DispatchQueue(label: "com.getcapacitor.community.audio.complex.queue", qos: .userInitiated)

queue.async {
if self.audioList.count > 0 {
let asset = self.audioList[audioId]

if asset != nil {
if asset is AudioAsset {
let audioAsset = asset as? AudioAsset

if self.fadeMusic {
audioAsset?.playWithFade(time: time)
} else {
audioAsset?.play(time: time)
}
call.resolve()
} else if asset is Int32 {
let audioAsset = asset as? NSNumber ?? 0
AudioServicesPlaySystemSound(SystemSoundID(audioAsset.intValue ))
call.resolve()
DispatchQueue.global(qos: .userInitiated).async {
self.lock.lock()
defer { self.lock.unlock() }

if let asset = self.audioList[audioId] {
if let audioAsset = asset as? AudioAsset {
if self.fadeMusic {
audioAsset.playWithFade(time: time)
} else {
call.reject(Constant.ErrorAssetNotFound)
audioAsset.play(time: time)
}
call.resolve()
} else if let audioAsset = asset as? NSNumber {
AudioServicesPlaySystemSound(SystemSoundID(audioAsset.intValue))
call.resolve()
} else {
call.reject(Constant.ErrorAssetNotFound)
}
}
}
Expand All @@ -84,42 +82,40 @@ public class NativeAudio: CAPPlugin {

@objc private func getAudioAsset(_ call: CAPPluginCall) -> AudioAsset? {
let audioId = call.getString(Constant.AssetIdKey) ?? ""
if audioId == "" {
if audioId.isEmpty {
call.reject(Constant.ErrorAssetId)
return nil
}
if self.audioList.count > 0 {
let asset = self.audioList[audioId]
if asset != nil && asset is AudioAsset {
return asset as? AudioAsset
}

lock.lock()
defer { lock.unlock() }

if let asset = self.audioList[audioId], asset is AudioAsset {
return asset as? AudioAsset
}

call.reject(Constant.ErrorAssetNotFound + " - " + audioId)
return nil
}

@objc func getDuration(_ call: CAPPluginCall) {
guard let audioAsset: AudioAsset = self.getAudioAsset(call) else {
guard let audioAsset = self.getAudioAsset(call) else {
return
}

call.resolve([
"duration": audioAsset.getDuration()
])
call.resolve(["duration": audioAsset.getDuration()])
}

@objc func getCurrentTime(_ call: CAPPluginCall) {
guard let audioAsset: AudioAsset = self.getAudioAsset(call) else {
guard let audioAsset = self.getAudioAsset(call) else {
return
}

call.resolve([
"currentTime": audioAsset.getCurrentTime()
])
call.resolve(["currentTime": audioAsset.getCurrentTime()])
}

@objc func resume(_ call: CAPPluginCall) {
guard let audioAsset: AudioAsset = self.getAudioAsset(call) else {
guard let audioAsset = self.getAudioAsset(call) else {
return
}

Expand All @@ -128,7 +124,7 @@ public class NativeAudio: CAPPlugin {
}

@objc func pause(_ call: CAPPluginCall) {
guard let audioAsset: AudioAsset = self.getAudioAsset(call) else {
guard let audioAsset = self.getAudioAsset(call) else {
return
}

Expand All @@ -148,7 +144,7 @@ public class NativeAudio: CAPPlugin {
}

@objc func loop(_ call: CAPPluginCall) {
guard let audioAsset: AudioAsset = self.getAudioAsset(call) else {
guard let audioAsset = self.getAudioAsset(call) else {
return
}

Expand All @@ -158,19 +154,19 @@ public class NativeAudio: CAPPlugin {

@objc func unload(_ call: CAPPluginCall) {
let audioId = call.getString(Constant.AssetIdKey) ?? ""
if self.audioList.count > 0 {
let asset = self.audioList[audioId]
if asset != nil && asset is AudioAsset {
let audioAsset = asset as! AudioAsset
audioAsset.unload()
self.audioList[audioId] = nil
}
lock.lock()
defer { lock.unlock() }

if let asset = self.audioList[audioId], asset is AudioAsset {
(asset as? AudioAsset)?.unload()
self.audioList[audioId] = nil
}

call.resolve()
}

@objc func setVolume(_ call: CAPPluginCall) {
guard let audioAsset: AudioAsset = self.getAudioAsset(call) else {
guard let audioAsset = self.getAudioAsset(call) else {
return
}

Expand All @@ -181,95 +177,86 @@ public class NativeAudio: CAPPlugin {
}

@objc func isPlaying(_ call: CAPPluginCall) {
guard let audioAsset: AudioAsset = self.getAudioAsset(call) else {
guard let audioAsset = self.getAudioAsset(call) else {
return
}

call.resolve([
"isPlaying": audioAsset.isPlaying()
])
call.resolve(["isPlaying": audioAsset.isPlaying()])
}

private func preloadAsset(_ call: CAPPluginCall, isComplex complex: Bool) {
let audioId = call.getString(Constant.AssetIdKey) ?? ""
guard !audioId.isEmpty else { return }

let channels: NSNumber?
let volume: Float?
let delay: NSNumber?
let isUrl: Bool?

if audioId != "" {
let assetPath: String = call.getString(Constant.AssetPathKey) ?? ""

if complex {
volume = call.getFloat("volume") ?? 1.0
channels = NSNumber(value: call.getInt("channels") ?? 1)
delay = NSNumber(value: call.getInt("delay") ?? 1)
isUrl = call.getBool("isUrl") ?? false
} else {
channels = 0
volume = 0
delay = 0
isUrl = false
}
let assetPath: String = call.getString(Constant.AssetPathKey) ?? ""

if complex {
volume = call.getFloat("volume") ?? 1.0
channels = NSNumber(value: call.getInt("channels") ?? 1)
delay = NSNumber(value: call.getInt("delay") ?? 1)
isUrl = call.getBool("isUrl") ?? false
} else {
channels = 0
volume = 0
delay = 0
isUrl = false
}

if audioList.isEmpty {
audioList = [:]
}
DispatchQueue.global(qos: .userInitiated).async {
self.lock.lock()
defer { self.lock.unlock() }

let asset = audioList[audioId]
let queue = DispatchQueue(label: "com.getcapacitor.community.audio.simple.queue", qos: .userInitiated)
if self.audioList[audioId] == nil {
var basePath: String?
if isUrl == false {
let assetPathSplit = assetPath.components(separatedBy: ".")
basePath = Bundle.main.path(forResource: assetPathSplit[0], ofType: assetPathSplit[1])
} else {
let url = URL(string: assetPath)
basePath = url?.path
}

queue.async {
if asset == nil {
var basePath: String?
if isUrl == false {
let assetPathSplit = assetPath.components(separatedBy: ".")
basePath = Bundle.main.path(forResource: assetPathSplit[0], ofType: assetPathSplit[1])
if let basePath = basePath, FileManager.default.fileExists(atPath: basePath) {
if !complex {
let pathUrl = URL(fileURLWithPath: basePath)
let soundFileUrl: CFURL = pathUrl as CFURL
var soundId = SystemSoundID()
AudioServicesCreateSystemSoundID(soundFileUrl, &soundId)
self.audioList[audioId] = NSNumber(value: Int32(soundId))
call.resolve()
} else {
let url = URL(string: assetPath)
basePath = url!.path
}

if FileManager.default.fileExists(atPath: basePath ?? "") {
if !complex {
let pathUrl = URL(fileURLWithPath: basePath ?? "")
let soundFileUrl: CFURL = CFBridgingRetain(pathUrl) as! CFURL
var soundId = SystemSoundID()
AudioServicesCreateSystemSoundID(soundFileUrl, &soundId)
self.audioList[audioId] = NSNumber(value: Int32(soundId))
call.resolve()
} else {
let audioAsset: AudioAsset = AudioAsset(owner: self, withAssetId: audioId, withPath: basePath, withChannels: channels, withVolume: volume as NSNumber?, withFadeDelay: delay)
self.audioList[audioId] = audioAsset
call.resolve()
}
} else {
call.reject(Constant.ErrorAssetPath + " - " + assetPath)
let audioAsset = AudioAsset(owner: self, withAssetId: audioId, withPath: basePath, withChannels: channels, withVolume: volume as NSNumber?, withFadeDelay: delay)
self.audioList[audioId] = audioAsset
call.resolve()
}
} else {
call.reject(Constant.ErrorAssetExists)
call.reject(Constant.ErrorAssetPath + " - " + assetPath)
}
} else {
call.reject(Constant.ErrorAssetExists)
}
}
}

private func stopAudio(audioId: String) throws {
if self.audioList.count > 0 {
let asset = self.audioList[audioId]

if asset != nil {
if asset is AudioAsset {
let audioAsset = asset as? AudioAsset
lock.lock()
defer { lock.unlock() }

if self.fadeMusic {
audioAsset?.playWithFade(time: audioAsset?.getCurrentTime() ?? 0)
} else {
audioAsset?.stop()
}
if let asset = self.audioList[audioId] {
if let audioAsset = asset as? AudioAsset {
if self.fadeMusic {
audioAsset.playWithFade(time: audioAsset.getCurrentTime())
} else {
audioAsset.stop()
}
} else {
throw MyError.runtimeError(Constant.ErrorAssetNotFound)
}
} else {
throw MyError.runtimeError(Constant.ErrorAssetNotFound)
}
}
}