2023. 4. 1. 19:00ㆍiOS/프로젝트
녹음 테스트
재생을 테스트 했으니, 음성 메시지를 녹음할 수 있도록 그 기능을 구현해 보는 것이 목표
목표 달성
AudioSession 카테고리 설정 (앞선 설정과 동일)
/// 재생 + 녹음 (playAndRecord) 모드
try audioSession.setCategory(.playAndRecord)
User Permission 받기
func getUserPermission() {
audioSession.requestRecordPermission() { [unowned self] allowed in
DispatchQueue.main.async {
if allowed {
self.isRecordingAllowed = true
} else {
// TODO: 비동의의 경우는? 어떻게 처리?
print("Recording is not allowed by user")
}
}
}
}
- Info.plist 에 Privacy - Microphone Usage Description 항목 추가 후 유저에게 보여질 사용 목적(Description) 을 추가한다.
- 오디오 세션의 requestRecordPermission(_:) 라는 인스턴스 메소드를 사용해 사용자 허가를 요청한다.
예제 코드를 가져왔는데, unowned self 를 그대로 사용할 지 고민이었다. 싱글톤은 사용할 때 처음 만들어지고 앱이 살아있는 동안에는 메모리에서 제거되지 않는다고 하니, 이 싱글톤에서의 self는 unowned로 선언되어도 관계 없다고 판단해서 그대로 사용하기로 했다.
이제 새로운 질문 - 사용자가 허용을 하지 않거나 허용했다가 뒤늦게 거절하면 어떻게 되는 거지? - 이거는 나중에 유저 플로우 발전시키면서 해결해 보도록 하겠다. (일단 목표는 녹음하는 것이니까!)
AVAudioRecorder 사용하기
/// AVAudioRecorder: 오디오 데이터를 파일에 녹음하는 객체
var audioRecorder: AVAudioRecorder!
// TODO: 적절한 세팅값 찾기
let settings = [
AVFormatIDKey: Int(kAudioFormatMPEG4AAC),
AVSampleRateKey: 12000,
AVNumberOfChannelsKey: 1,
AVEncoderAudioQualityKey: AVAudioQuality.high.rawValue
]
do {
/// url에 audio data를 쓰게 된다
audioRecorder = try AVAudioRecorder(url: audioFilename, settings: settings)
audioRecorder.record()
self.isRecordingStarted = true
} catch {
print(error)
}
- url, settings 와 함께 AVAudioRecorder 생성


사실 settings 내용도 자세히 알아볼까 했는데, 우선 예제 코드가 잘 동작하기도 하고 오디오에 관한 깊은 조사가 필요할 것 같아 이것도 나중의 나에게 토스..하겠다.
- 녹음 시작
만들어진 audioRecorder의 record() 함수를 실행한다.
목표 달성을 위해 필요한 것들
오디오 파일 저장을 위한 파일 시스템 이용하기
오디오 파일을 저장하기 위해서는 사용자의 기기에 접근할 수 있는 인터페이스가 필요하다.
그 역할을 해 주는 것이 바로 FileManager 이다. 이를 이용해 파일 시스템에 접근할 수 있는 경로를 얻을 함수를 만든다.
func getDocumentsDirectory() -> URL {
/// FileManager: 파일 시스템의 콘텐츠에 접근할 수 있게 해 주는 인터페이스이자 파일 시스템과 상호작용할 수 있는 주요 수단 (Class)
/// 사용자에 의해 생성되는 모든 Contents는 document 디렉토리에 저장되도록 강하게 권장된다고 한다
/// default: FileManager의 싱글톤 객체를 만들어 준다
let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask) // -> [NSURL]
return paths[0]
}
- FileManager.default는 파일 매니저의 싱글톤 객체를 만들어 준다.
- 사용자에 의해 생성되는 모든 Contents는 document 디렉토리에 저장되도록 강하게 권장된다고 한다. 따라서 documentDirectory 의 path를 가져온다.
- userDomainMask는 공식 문서에 따르면 The user’s home directory—the place to install user’s personal items (~). 라고 되어 있다.

왜 paths가 여러 개이고, 그 중 맨 앞의 것을 가져와야 하는 지는 찾아봐도 잘 모르겠다. (과제 +1)
이제 위에서 얻은 파일 시스템 경로를 가지고 새로운 파일을 생성할 것이다.
일단 이름은 만들어진 날짜와 시간으로 설정하도록 했다.
// 파일 이름 생성
let formatter = DateFormatter()
formatter.dateFormat = "yy-MM-dd--HH-mm"
fileName = formatter.string(from: Date())
// 파일 이름과 확장자를 포함한 전체 경로 설정
let audioFilename = getDocumentsDirectory().appendingPathComponent("\(fileName).m4a")
appendingPathComponent 는 주어진 path 에 새로운 요소를 추가해 새로운 URL을 리턴해 준다.
/// url에 audio data를 쓰게 된다
audioRecorder = try AVAudioRecorder(url: audioFilename, settings: settings)
Info.plist 설정
아래의 설정을 true로 해 주어야 디렉토리에 저장이 되는 것을 확인할 수 있다.


관련 설명은 아래와 같다.

남아있는 질문들
- 유저가 녹음을 허용 했다가 유저가 허용 안함으로 바꾼 다음엔 에러가 나는지? 그걸 어디서 처리할지?
- 매번 유저 허가를 요청하지 말고, 허용 여부를 기억해서 처리하는 법?
- AudioRecorder의 적절한 settings 값은?
- 공식 문서에서 오디오 관련 작업 중 인터럽트가 발생했을 경우 그를 처리하는 코드를 그대로 가져왔는데, 아직은 엄두가 안 난다. 시간 날 때 해석해 보자!
- Paths 여러개인 이유와 왜 맨 앞을 가져오는지?
오디오 파일들을 저장하고 가져오는 등 관리할 방법이 필요한데, 어떻게 하지?
대답한 질문들
- 싱글톤의 인스턴스는 언제 생성될까?
→ 싱글톤의 인스턴스는 AudioManager.shared 하는 순간 생성된다. 따라서 나는 제일 첫 뷰의 onAppear에서 AudioManager.shared.getUserPermission() 을 처리해 주고 있는데 이렇게 shared 에 접근하는 순간 생성된다고 보면 된다.
- AVAudioPlayer는 다른 오디오 파일 마다 매번 새로 할당해 주어야 하나?
→ 검색 결과 stackoverflow 선배님덜에 따르면 AVAudioPlayer를 만드는 비용이 크지 않아서 매번 생성해 주어도 된다고 한다
이전 이야기
https://mila00a.tistory.com/55
개인 프로젝트 기록 - 1 (Ear Speaker)
Ear Speaker 테스트 귀로 전화를 받아야 하는 것이 주 아이디어이기에, Ear Speaker를 사용하는 것을 가장 먼저 테스트 해 보려 한다. 목표 달성 처음에 검색한 결과 /// Ear speaker로 재생하기 위한 설정 ?
mila00a.tistory.com
다음 이야기
https://mila00a.tistory.com/57
개인 프로젝트 기록 - 4 (CoreData)
Core Data 사용하기 어쨌든 한번만 사용하고 끝낼 게 아니기 때문에 정보들을 저장해야 합니다. 그래서 1) CoreData를 SwiftUI에서 사용하는 법을 이해하고, 2) 실제 나의 프로젝트에 적용해 볼 것입니다
mila00a.tistory.com
'iOS > 프로젝트' 카테고리의 다른 글
개인프로젝트 - 5 (SwiftLint) (0) | 2023.04.17 |
---|---|
개인 프로젝트 기록 - 4 (MessageUI) (0) | 2023.04.12 |
개인 프로젝트 기록 - 3 (CoreData) (0) | 2023.04.01 |
개인 프로젝트 기록 - 1 (Ear Speaker) (0) | 2023.04.01 |
개인 프로젝트 기록 - 0 (시작) (0) | 2023.04.01 |