Haru's 개발 블로그

[SwiftUI] VoiceViewModel 코드 분석하기 본문

IOS/SwiftUI

[SwiftUI] VoiceViewModel 코드 분석하기

Haru_29 2022. 5. 4. 09:21

앞서 진행한 CoreDataManger에 다음으로 현재 진행한 프로젝트 중 녹음기능이 담겨져있는 VoiceViewModel을 분석해보도록 하겠습니다. 순서는 코드의 플로우대로 진행할 예정입니다.

import Foundation
import AVFoundation

class VoiceViewModel : NSObject, ObservableObject , AVAudioPlayerDelegate{
    
    
    var audioRecorder : AVAudioRecorder!
    var audioPlayer : AVAudioPlayer!
    
    var indexOfPlayer = 0
    
    @Published var isRecording : Bool = false
    @Published var recordingsList = [Recording]()
    @Published var countSec = 0
    @Published var timerCount : Timer?
    @Published var blinkingCount : Timer?
    @Published var timer : String = "0:00"
    @Published var toggleColor : Bool = false
    
    
    var playingURL : URL?
    
    override init(){
        super.init()
        
        fetchAllRecording()
        
    }

여기서 저는 녹음기능을 활용하기 위해서 AVFoundation과 시간을 계산하기 위한 Foundation을 사용하게 되었습니다.

이때 class에 NSObject, ObserableObject, AVAudioPlayerDelegate을 상속받게 되는데 하나씩 알아 봅시다.

일단, NSObject란 무엇인가?

Objective-C의 프로토콜 중 하나인 NSOject는 모든 Objective-C 객체들의 근본이 되는 메서드의 집합을 가지고 있는 프로토콜입니다.

그리고 NSObect Protocol을 채택하는 객체들은 일급객체로 취급되는데 그로 인하여 요청받을 수 있는 특성은 아래와 같습니다.

- Class와 상속 계층 배의 클래스의 위치

- 프로토콜의 채택

- 특정 메세지에 대해 응답할 수 있는 능력

 

ObserableObject를 공부하기 더불어 Published도 같이 보도록 합시다.

일단 ObserableObject라는 프로토콜은 필수구현을 필요로 하지 않습니다. 전체적인 특성은 아래와 같습니다.

- Combine에 속해있다.

- Class에서만 사용이 가능하다.

- ObserableObject를 준수한 Class는 objectWillChange라는 프로퍼티를 사용할수 있다.(ObserableObjectPublisher 타입)

-> objectWillChange.send()를 이용하기 위해서(이때, send()함수는 변경된 사항이 있다고 알려주는 것 입니다.)

 

근데 가지고 있는 변수가 적으면 send()함수를 몇번 해줘서 간단하게 표현이 가능하지만 변수가 많아 지거나 수정 사항이 많아지면 신경써야 될 부분이 많아질 것입니다.

이를 대신할 기능으로 @Published 속성래퍼가 해주는 역할입니다.

이 속성래퍼는 해당 변수가 변경이 된다면 자동으로 objectWillChange.send()를 호출해줍니다.

 

기본적으로 SwiftUI는 struct 형식이기 때문에 뷰에 대한 참조를 가질수 없습니다.

그래서 SwiftUI에서는 매번 새로운 것을 그리는 것을 피한다고 합니다. 

그래서 뷰를 랜더, 즉 그리는 단계에 있어서는 성능 최적화나는 동작이 있고 필요할 때만 그린다고 합니다.

 

AVAudioPlayerDelegate는 간단합니다.

오디오 재생 이벤트 및 디코딩 오류에 응답하는 방법을 정의하는 프로토콜을 의미합니다.

 

func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) {
       
        for i in 0..<recordingsList.count {
            if recordingsList[i].fileURL == playingURL {
                recordingsList[i].isPlaying = false
            }
        }
    }

이 함수는 오디오를 마무리할때 쓰는 함수입니다. 전체적인 리스트를 카운팅해서 현재 재생중인 URL과 저장된 URL이 같으면 저장을 중단 하도록 하는 함수입니다.

 

 func startRecording() {
        
        let recordingSession = AVAudioSession.sharedInstance()
        do {
            try recordingSession.setCategory(.playAndRecord, mode: .default)
            try recordingSession.setActive(true)
        } catch {
            print("Cannot setup the Recording")
        }
        
        let path = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
        let fileName = path.appendingPathComponent("\(Date().toString(dateFormat: "YYYY-MM-dd 'at' HH:mm:ss"))")
        
        
        
        let settings = [
            AVFormatIDKey: Int(kAudioFormatMPEG4AAC),
            AVSampleRateKey: 12000,
            AVNumberOfChannelsKey: 1,
            AVEncoderAudioQualityKey: AVAudioQuality.high.rawValue
        ]
        
        
        do {
            audioRecorder = try AVAudioRecorder(url: fileName, settings: settings)
            audioRecorder.prepareToRecord()
            audioRecorder.record()
            isRecording = true
            
            timerCount = Timer.scheduledTimer(withTimeInterval: 1, repeats: true, block: { (value) in
                self.countSec += 1
                self.timer = self.covertSecToMinAndHour(seconds: self.countSec)
            })
            blinkColor()
            
        } catch {
            print("Failed to Setup the Recording")
        }
    }

여기 있는 내용을 하나씩 파헤쳐보록 하겠습니다.

처음 recordingSession을 변수를 설정하고 AVAudionSesson.sharedInstance()라는 함수를 받게 되는데 무엇인지 알아보도록 하겠습니다.

AVAudioSession

앱에서 오디오를 사용하려는 방식을 시스템에 전달하는 개체입니다.

AVAudioSession은 앱과 운영 체제, 기본 오디오 하드웨어 사이에서 중개자 역할을 합니다.

AVAudioSession의 특징은 다음과 같습니다.

- 오디오 재생은 지원하지만 오디오 녹음은 허용하지 않습니다.

- IOS에서 벨소리/무음 스위치를 무음 모드로 설정하면 앱에서 재생 중인 모든 오디오가 무음으로 설정됩니다.

- IOS에서 기기를 잠그면 앱의 오디오가 묵음됩니다.

- 앱이 오디오를 재생할 때 다른 모든 배경 오디오를 무음으로 합니다.

 

다음으로 path와 fileName을 변수를 설정해 파일입출력을 위한 위치 및 파일 이름을 생성합니다.

'IOS > SwiftUI' 카테고리의 다른 글

[SwiftUI] TCA 정리  (0) 2023.03.01
[SwiftUI] @StateObject  (0) 2022.06.17
[SwiftUI] SwiftUI에서 UserDefaults를 사용 -> @AppStorage  (0) 2022.06.16
[SwiftUI] Coredata 사용해보기  (0) 2022.05.04
[SwiftUI] persistentContainer란 무엇인가?  (0) 2022.05.03
Comments