| SoundPlayerView |
|---|
![]() |
| SoundPlayerView |
|---|
![]() |
| AVKit을 이용한 사운드 재생 뷰 |
일부 앱의 경우 음악을 재생하는 기능이 있거나, 자동으로 BGM이 재생되기도 한다. 그 외에도 효과음을 사용하는 등 다양한 방식으로 사운드를 활용할 수 있는데, 오늘은 SwiftUI에서 사운드를 재생하기 위한 프레임워크인
AVKit에 대해 간단히 학습해 보려고 한다.핵심코드
func playSound(_ sound: SoundOption) { guard sound != .stop else { player?.stop() return } guard let url = Bundle.main.url(forResource: sound.rawValue, withExtension: ".mp3") else { return } do { player = try AVAudioPlayer(contentsOf: url) player?.play() } catch { print("오디오 재생 중 문제가 발생했습니다. \(error.localizedDescription)") } }
AVKit이란 비디오 및 오디오 콘텐츠를 재생하고 관리할 수 있도록 도와주는 프레임워크이다.
SwiftUI에서 AVKit을 사용하면 비디오 플레이어를 간단히 추가하고, 오디오 및 비디오를 재생하는 기능을 쉽게 구현할 수 있다.
이번에는 오디오만 활용하기로 하였다.
먼저 AVKit을 import한다.
import SwiftUI
import AVKit
다음으로 사운드를 출력하고 관리할 수 있는 클래스를 생성해준다.
이 때, 클래스에 ObservableObject 프로토콜을 채택해준다. 이는 SwiftUI에서 데이터 모델을 만들 때 사용하는 프로토콜로, 객체의 데이터가 변경될 때 SwiftUI의 뷰가 자동으로 업데이트 되도록 도와준다.
class SoundSetting: ObservableObject { ... }
그리고 Singleton Pattern을 만들어준다.
싱글톤 패턴은 다양한 뷰에서 하나의 객체를 특정 용도로 사용하기 위해 선언하는 디자인 유형으로, static 코드를 사용하여 외부 뷰에서 인스턴스를 생성하지 않고도 내부의 기능을 사용할 수 있도록 한다.
// 타입은 클래스 타입을 따르도록 한다.
static let instance: SoundSetting = SoundSetting()
사운드를 재생하기 위한 AVAudioPlayer타입의 변수도 선언해준다.
// 아직 어떤 사운드를 재생할지 값을 주지 않았기 때문에 옵셔널 타입으로 생성
var player = AVAudioPlayer?
이제 외부에서 사운드를 재생할 수 있도록 도와주는 함수를 생성한다.
이번에는 mp3 확장자를 가진 사운드를 미리 Xcode에 추가하여 작업을 진행하였다.
앞서 생성한 AVAudioPlayer타입의 프로퍼티에 재생할 사운드에 대한 정보를 주어야 한다.
나는 미리 추가한 사운드파일의 이름을 URL형식으로 지정해 주기로 했다.
func playSound() {
// forResource - 파일명
// withExtension - 파일확장자명
guard let url = Bundle.main.url(forResource: "summerbreeze", withExtension: ".mp3") else { return }
}
이제 생성된 url을 AVAudioPlayer 타입의 프로퍼티에 입력해준다.
이 때, AVAudioPlayer의 초기화 방법으로 (contentOf:) throws를 선택한다. 초기화 타입을 보면 알 듯이 throws값을 가지기 때문에 이를 처리하기 위해 do - catch를 사용해 주어야 한다.
func playSound() {
guard let url = Bundle.main.url(forResource: "summerbreeze", withExtension: ".mp3") else { return }
do {
// throws 이기 때문에 try 를 지정
player = try AVAudioPlayer(contentOf: url)
// url에 값이 있을 경우 재생
player?.play()
} catch {
// url 값이 문제가 있을 경우 에러를 프린트
print("오디오 재생 중 오류가 발생했습니다. \(error.localizedDescription)")
}
}
이제 메인뷰에 트리거를 생성하여 오디오를 재생할 수 있도록 한다.
이번에는 Button을 활용하여 재생을 진행하였다.
Button {
SoundSetting.instance.playSound()
} label: {
VStack(spacing: 10) {
Image(systemName: "pianokeys.inverse")
.font(.system(size: 80))
.foregroundStyle(Color.white)
.shadow(color: .blue, radius: 3)
Text("Summer Breeze")
.foregroundStyle(Color.blue)
}
}

이제 만들어진 버튼을 누르면 미리 지정해둔 사운드가 흘러나온다.
하지만 이렇게 하면 사운드를 정지하는 버튼도 없고, 다양한 사운드가 있을 경우 사용하기가 어렵다.
때문에 편의성을 늘리기 위해 클래스와 함수를 조금 수정해 주도록 한다.
다양한 사운드, 그리고 정지 버튼을 구현하기 위해 enum을 사용한다.
위에서 만들었던 SoundSetting이라는 클래스의 내부에 열거형을 생성해준다.
class SoundSetting: ObservableObject {
enum SoundOption: String {
case summer = "summerbreeze"
case comet = "comet"
case stop
}
// 중략...
}
열거형은 rawValue로 String을 지정하여 사운드파일의 이름을 지정해준다.
다음으로 함수를 수정해준다.
함수는 매개변수를 선언하여 매개변수 타입을 위에서 만든 열거형으로 설정해준다.
// 매개변수 타입을 열거형으로 지정
func playSound(sound: SoundOption) { ... }
그리고 guard문을 사용하여 매개변수의 값이 .stop이면 노래를 멈추고, 다른 값이면 값에 알맞은 노래를 재생하도록 변경한다.
func playSound(sound: SoundOption) {
// 매개변수 sound가 .stop인 경우 음악을 중지
guard sound != .stop else {
player?.stop()
return
}
// forResource의 값으로 매개변수 sound의 rawValue를 사용
guard let url = Bundle.main.url(forResource: sound.rawValue, withExtension: ".mp3") else { return }
do {
player = try AVAudioPlayer(contentOf: url)
player?.play()
} catch {
print("오디오 재생 중 오류가 발생했습니다. \(error.localizedDescription)")
}
}
이제 메인뷰에서 버튼을 추가하고 버튼의 액션을 알맞게 수정한다.
VStack(spacing: 80) {
// 1번 노래를 재생시키는 버튼
Button {
// playSound 함수의 매개변수로 SoundOption 값 입력
SoundSetting.instance.playSound(sound: .summer)
} label: {
VStack(spacing: 10) {
Image(systemName: "pianokeys.inverse")
.font(.system(size: 80))
.foregroundStyle(Color.white)
.shadow(color: .blue, radius: 3)
Text("Summer Breeze")
.foregroundStyle(Color.blue)
}
}
// 2번 노래를 재생시키는 버튼
Button {
// playSound 함수의 매개변수로 SoundOption 값 입력
SoundSetting.instance.playSound(sound: .comet)
} label: {
VStack(spacing: 10) {
Image(systemName: "pianokeys.inverse")
.font(.system(size: 80))
.foregroundStyle(Color.white)
.shadow(color: .indigo, radius: 3)
Text("Summer Breeze")
.foregroundStyle(Color.indigo)
}
}
// 노래 정지 버튼
Button {
// playSound 함수의 매개변수로 SoundOption 값 입력
SoundSetting.instance.playSound(sound: .stop)
} label: {
VStack(spacing: 10) {
Image(systemName: "stop.circle")
.font(.system(size: 80))
.foregroundStyle(Color.pink)
.shadow(color: .glay, radius: 3)
Text("Summer Breeze")
.foregroundStyle(Color.pink)
.fontweight(.bold)
}
}
}

오늘은 SwiftUI에서 앱에 사운드를 추가하는 AVKit에 대해 학습했다.
동영상도 다뤄볼까 했지만, 동영상은 사운드보다도 더 복잡해보여서 일단은 사운드만 학습했다.
원래는 블로그에 소리가 포함된 영상도 첨부하고 싶었지만, 소리와 함께 영상녹화가 잘 이루어지지 않아서 포기했다.
다음번에는 방법을 찾아서 소리도 함께 첨부된 영상을 첨부하고 싶다.