Starbucks Caramel Frappuccino
본문 바로가기
  • 그래 그렇게 조금씩
SwiftUI/SwiftUI(Intermediate)

5. AVAudioPlayer 사운드 재생/ 열거형 (원시값,연관값)

by Toughie 2023. 5. 23.

⭐️AVAudioPlayer 사운드 재생, 열거형⭐️

앱 내에서 사운드를 재생해야 하는 경우는 생각보다 많다. (많은 사람들이 무음모드로 사용하지만..)

특정 버튼을 눌렀을 때 효과음을 준다거나, 배경 음악을 깔아주면 앱의 퀄리티가 훨씬 높아진다고 생각한다. (better UX)

Swift Student Challenge를 하면서, 그리고 아카데미 프로젝트를 진행하면서 효과음을 많이 사용했는데,

돌이켜보면 코드도 난잡했고 정리가 안되어 있었다. 그래서 간단하지만 깔끔하게 사운드 재생하는 방법을 정리하고자 한다.

 

외부 URL로 사운드를 재생할 수도 있지만, 여기서는 에셋 파일을 재생하는 방법으로 진행한다.

예시코드


1. 여러 뷰에서 사운드를 재생하고, 중지 해야하는 경우가 있을 수 있기에, 싱글톤 패턴으로 클래스를 만들어 준다.

2. 사운드 재생을 위해서는 AVKit(AVFoundation 기반 UI 프레임워크) / AVFoundation이 필요한데, 간단하게 AVKit으로 해보자
영상/사운드에 있어 세밀한 커스텀이나 조정이 필요한 경우에는 AVFoundation을 사용해야 할 것이다.


3. 사운드 재생은 'URL 생성' -> 'URL'을 통한 AVAUdioPlayer 생성 -> player 재생 흐름으로 이어진다.

 

4. URL 생성 과정에서 오디오 파일의 이름과 확장자가 String으로 필요한데, 이를 관리하기 위해 Nested Type으로 정리해줬다.

 

*왜 ObservableObject 프로토콜을 채택하지 않았나요?
-> 보통 이런 클래스는 ObservableObject를 채택하고 뷰에서 @StateObject로 초기화 해주는 경우가 많지만,
현재 사운드 재생의 경우 화면의 요소가 리프레시 될 일이 없기 때문이다.
(사운드 관련 로직이 UI 렌더링과 직접적인 관련이 없는 상황이라 채택하지 않았음)

 

소리에 따라 화면는 UI를 개발한다면 채택해야 할지도..

//  Created by Toughie on 2023/05/23.
//

import SwiftUI
import AVKit

final class SoundManager {
    
    static let shared = SoundManager()
    
    private init() {}
    
    var player: AVAudioPlayer?
    
    //열거형 원시값 활용
    enum SoundNames: String {
        case twinkle1
        case twinkle2
    }
    
    enum SoundOptions: String {
        case mp3 = ".mp3"
        case wav = ".wav"
    }
    
    func playSound(name: SoundNames, option: SoundOptions) {
        guard let url = Bundle.main.url(forResource: name.rawValue, withExtension: option.rawValue) else { return }
        
        do {
            player = try AVAudioPlayer(contentsOf: url)
            player?.play()
        } catch let error {
            print("Error playing sound: \(error.localizedDescription)")
        }
    }
    
    func stopSound() {
        player?.stop()
    }
}

사운드 재생 / 중지

//사운드 재생
SoundManager.shared.playSound(name: .twinkle1, option: .mp3)

//사운드 중지
SoundManager.shared.stopSound()

 

위 코드에서 열거형 원시값을 활용했기 때문에, 열거형의 연관값도 간단하게 정리해보고 넘어가자.

//케이스마다 연관된 값들을 저장할 수 있음.
enum Media {
    case song(title: String, artist: String)
    case movie(title: String, director: String)
}

let favoriteMedia: Media = .song(title: "Event Horizon", artist: "Younha")

switch favoriteMedia {
//연관값을 let으로 연관값 바인딩 후 사용
//case let .song(title, artist): 이렇게 바인딩도 가능

case .song(let title, let artist):
    print("Favorite Song: \(title) by \(artist)")
case .movie(let title, let director):
    print("Favorite Movie: \(title) directed by \(director)")
}