* 공부했던 내용을 주관적으로 해석하여 '제가' 이해하기 쉽도록 작성하였습니다. 잘못된 정보가 있을 시 알려주시면 제게 큰 힘이 됩니다!
해당 작업을 하기 전에 작업 파일에 사운드 파일과 AVPlayer 모델을 넣어주었다.
(이에 대한 설명은 자세히 나와있지 않았음)
import SwiftUI
import AVFoundation // AVFoundation 추가해서 사운드가 들어갈 수 있게 했음
struct MeetingView: View {
@Binding var scrum: DailyScrum
@StateObject var scrumTimer = ScrumTimer()
private var player: AVPlayer { AVPlayer.sharedDingPlayer }
var body: some View {
ZStack {
RoundedRectangle(cornerRadius: 16.0)
.fill(scrum.theme.mainColor)
VStack {
MeetingHeaderView(secondsElapsed: scrumTimer.secondsElapsed,
secondRemaining: scrumTimer.secondsRemaining,
theme: scrum.theme)
Circle()
.strokeBorder(lineWidth: 24)
MeetingFooterView(speakers: scrumTimer.speakers, skipAction: scrumTimer.skipSpeaker)
}
}
.padding()
.foregroundColor(scrum.theme.accentColor)
.onAppear {
scrumTimer.reset(lengthInMinutes: scrum.lengthInMinutes, attendees: scrum.attendees)
scrumTimer.speakerChangedAction = {
player.seek(to: .zero) // 사운드가 처음부터 틀어질 수 있게 설정
player.play() //재생
}
scrumTimer.startScrum()
}
.onDisappear {
scrumTimer.stopScrum()
}
.navigationBarTitleDisplayMode(.inline)
}
}
struct MeetingView_Previews: PreviewProvider {
static var previews: some View {
MeetingView(scrum: .constant(DailyScrum.sampleData[0]))
}
}
< 정리 >
@StateObject
Wrapping a reference type property as a @StateObject keeps the object alive for the life cycle of a view.
(참조 유형 속성을 @StateObject로 래핑하면 뷰의 수명 주기 동안 개체가 살아 있는 상태로 유지됩니다.)
onAppear(perform:)
onAppear(perform:) performs an action when a view appears on screen.
(onAppear(perform:)는 뷰가 화면에 표시될 때 동작을 수행합니다.)
onDisappear(perform:)
onDisappear(perform:) performs an action when a view disappears from the screen.
(onDisappear(perform:)는 뷰가 화면에서 사라질 때 동작을 수행합니다.)
https://youtu.be/uQtM6StTsQg?si=dnipVXA7dH9fjX48
ObservableObject관련 MVVM 이해하기 좋은 영상
MVVM이란?
Model -> View -> ViewModel 의 약자임. 데이터를 이동하는 구조 중 하나
하지만 SwiftUI에서 MVVM구조 사용을 지양하자는 움직임이 있다고 함.
https://gist.github.com/unnnyong/439555659aa04bbbf78b2fcae9de7661
https://woozzang.tistory.com/29
[ Updating app data ]
새로운 기능 추가
- 새로운 일일 스크럼 회의를 만들기 위해 보기 추가
- 과거 회의를 추적하기 위해 회의 타이머를 업데이트
이 두 기능 모두 앱의 여러 보기에 표시되는 공유 데이터를 수정한다.
이러한 기능을 추가하면 @State, 바인딩 및 SOT로 몇 가지 기본적인 SwiftUI 개념을 다시 눈여겨 볼 필요가 있다.
< Section 1 Use the edit view to create a new scrum >
- 새로운 일일 스크럼을 만들기 위한 뷰를 추가
- 스크럼을 만들기 위해 시트를 보여주는 스크럼뷰에 버튼을 추가
(사용자가 스크럼을 만든 후, 앱은 스크럼 배열에 새 스크럼을 추가할 것)
활용되는 개념 : Binding
- ScrumsView를 새로 만들고 Binding를 활용하여 isPresentingNewScrumView: Bool 을 걸어준다.
그리고 초기값을 False로 설정 / 기존에 만들었던 toolbar 버튼의 액션에 토글을 걸어준다.
- 이후 NewScrumSheet 뷰를 새로 그려주고 연결해줌
스크럼 배열은 바인딩이므로, 이 보기에서 배열을 업데이트하면 앱에 포함된 SOT가 업데이트됨.
< Section 2 Add scrum history >
- 회의가 끝나면, MeetingView는 회의 날짜, 기간 및 참석자를 기록하는 기능 추가.(History)
- DetailView를 업데이트하여 기록 목록을 표시
ScrumdingerApp의 DailyScrum 항목 배열은 앱 데이터에 대한 SOT이다.
ScrumsView에는 배열에 바인딩이 있으며, 배열의 개별 항목에 대한 바인딩을 DetailView에 전달한다.
세부 뷰는 MeetingView에 바인딩을 전달한다.
바인딩은 모델 데이터를 단일 진실 소스와 동기화하여 모든 View가 동일한 데이터를 반영한다.
import Foundation
struct History: Identifiable { //History에 대한 구조를 만들고
let id: UUID
let date: Date
var attendees: [DailyScrum.Attendee]
init(id: UUID = UUID(), date: Date = Date(), attendees: [DailyScrum.Attendee]) {
self.id = id
self.date = date
self.attendees = attendees
} //위의 구조에 대하여 이니셜라이징 해준다
}
그리고 MeetingView에 history를 연결해줘야함,
기존의 일일히 걸어주었던 코드를
위와 같이 간단하게 줄일 수 있었다.
완성된 영상은 아래와 같다.
< Adopting Swift concurrency >
https://developer.apple.com/tutorials/app-dev-training/adopting-swift-concurrency
Concurrency -> 동시성
https://youtu.be/zRJOte7TaPw?si=RcxzqieugHHiO0Mx
하나의 쓰레드에서 여러 일을 처리할 때 비효율적임
-> 다른 쓰레드로 보내서 동시에(concurrency) 일을 시키면 더 빠르게 일을 처리할 수 있지 않을까?
동기(Synchronou) / 비동기(Asynchronou)가 무엇인고?
비동기
작업을 다른 쓰레드로 보낸 뒤 일을 시작시키고 그 작업의 완료여부와 상관 없이 다시 원래의 쓰레드로 돌아옴 (안기다림)
안기다려도 다음 일을 진행시킬 수 있다.
동기
작업을 다른 쓰레드로 보낸 뒤 일을 시작시키고, 그 일이 끝나길 기다렸다가 다시 돌아옴
- 대부분 서버와의 통신, 네트워크 처리를 할 때 많이 등장
Serial(직렬) vs Concurrent(동시)
직렬 - 메인 쓰레드에서 분산처리 시킨 일을 다른 하나의 쓰레드로 보냄
동시(Concurrent) - 몇개의 쓰레드로 보낼지는 시스템이 알아서 처리함, 다만 여러개의 쓰레드로 분산해서 처리함
동시처리가 더 효율적이어보이는데 그럼에도 불구하고 왜 직렬처리가 필요할까?
-> 작업에 순서가 중요한 일이 있을 수 있다.
직렬처리 : 순서가 중요한 일을 처리할 때 사용
동시처리 : 각자 독립적이지만 유사한(중요도와 작업의 성격 등) 여러개의 작업을 처리할때 사용 (eg) List로 나타내는 셀 디자인에서)
그럼, 비동기와 동시 처리는 같은말일까? -> 절대 아님
비동기 : 작업을 보내는 쓰레드에 관련된 말 / 보낸 작업을 기다릴지 말지 결정하는 개념
동시처리 : 메인 쓰레드에서 다른 쓰레드로 작업을 보낼 때 하나에 보낼지 여러개에 보낼지 구분하는 개념
예시
서버에서 가져온 이미지를 갤러리 형식으로 불러오는데, 순서대로 불러온다?
-> 개느려짐 왜냐 하나하나 순서대로 불러오니까
1번쓰레드(메인쓰레드) 는 유저가 보는 화면을 주관하는 쓰레드이기 때문에 일을 많이 시킬 수록 좋지 않다
성능과 반응 / 앱의 최적화 관련된 개념임! (GCD / Operation)
< Persistence and concurrency - Persisting data >
< Section 1 Add codable conformance >
Attendee, History, DailyScrum을 Codable하게 하는 작업 추가
Codable이란? (참고했던 문서 및 글들)
Codable은 Swift의 Decodable 프로토콜과 Encodable 프로토콜의 결합된 프로토콜로, 사용자 정의 타입에 대한 데이터 디코딩 및 저장 또는 전송할 데이터 인코딩의 표준 방법을 함께 제공합니다. (???)
Decodable : 자신을 외부표현(external representation)에서 디코딩 할 수 있는 타입
Encodable : 자신을 외부표현(external representation)으로 인코딩 할 수 있는 타입
외부표현 예시 : JSON파일
https://developer.apple.com/documentation/swift/codable
https://zeddios.tistory.com/373
https://zeddios.tistory.com/394
< Section 2 Create a data store >
앱의 스크럼 데이터를 관리하기 위해 ScrumStore라는 새 클래스를 만든다.
ScrumStore에는 앱이 표시하는 DailyScrum 구조의 배열을 보유하는 단일 속성이 있다. 이 속성은 @Published로 표시되어 있으므로 뷰가 이 값에 바인딩할 수 있다. 바인딩으로 데이터 전달하기에서 배운 것처럼, 바인딩을 사용하면 SwiftUI가 최신 값을 반영하도록 뷰를 업데이트한다.
-> 모델 폴더에 ScrumStore 파일을 새로 만들어 추가해준다
import SwiftUI
class ScrumStore: ObservableObject { //옵저버블 오브젝트 -> 관찰 가능한 오브젝트/ 상태가 계속 변화하는걸 주변에 알리는 녀석
@Published var scrums: [DailyScrum] = []
private static func fileURL() throws -> URL {
try FileManager.default.url(for: .documentDirectory,
in: .userDomainMask,
appropriateFor: nil,
create: false)
.appendingPathComponent("scrums.data")
}
}
< Section 3 Add a method to load data > : 이게 정확히 뭘 처리하는 과정인지 이해가 안감
scrums.data에서 Json데이터를 읽어올 수 있는 방법을 추가해준다.
이 때 파일에서 읽어오는 과정이 느릴 수 있음으로, 비동기 처리방식을 활용해준다.
함수를 비동기식으로 만들면 파일 시스템이 데이터를 읽는 동안 가만히 앉은 상태로 대기하는 대신(동기처리로 처리가 다 될 때 까지 기다리려서 느려짐) 시스템이 사용자 인터페이스 업데이트의 우선 순위를 효율적으로 지정할 수 있다.
-> 위의 설명까지는 얼추 이해감 / 근데 스크럼딩거에서 무슨 역할을 하는건지는 모르겠음.
< Section 4 Add a method to save data>
데이터를 저장하는 방법을 추가해줌
< Section 5 Load data on app launch>
이 섹션에서는 이전 섹션에서 작성한 load() 메서드를 사용하여 앱의 루트 뷰가 화면에 표시될 때 데이터를 로드합니다.
< Section 6 Save data in inactive state >
앱의 작동 상태를 모니터링하고 값이 비활성으로 변경되면 사용자 데이터를 저장하는 코드를 작성하여 이 튜토리얼을 완료합니다.
< Section 7 Save data in inactive state >
시뮬레이터에서 앱을 실행하고 스크럼을 추가합니다. 그런 다음 홈 화면으로 돌아가서 앱을 종료한다.
앱을 다시 실행하면 변경 사항을 확인할 수 있다.
https://ios-development.tistory.com/16
- do-catch구문 : 오류에 대한 예외처리를 담당
[ Handling Error ]
통제할 수 없는 예기치 않은 문제에 대한 대응을 하기 위한 단계.
(eg) 네트워크 연결이 끊어짐. 파일에서 데이터를 읽는 데 여러 가지 방식으로 실패함)
스크럼딩거에서 문제가 발생할 경우 지침을 제공하여 이러한 오류를 처리할 수 있다.
< Section 1 Add an error wrapper structure >
데이터 지속성에서 스크럼 데이터를 파일 시스템에 읽고 쓰는 방식으로 스크럼딩거에 지속성을 추가했다.
그러나 파일에 읽기 권한이 없거나 앱에서 지정한 경로에 파일이 존재하지 않을 수 있다. 또는 파일의 인코딩이 올바른 형식이 아니어서 작업에서 오류가 발생할 수 있다.
작업이 실패하면 실패의 원인을 파악하면 사용자가 적절하게 대응할 수 있다.
이 섹션에서는 오류를 나중에 사용자에게 표시할 메시지와 연결하는 구조를 추가한다.
< Section 2 Create an Error View >
< Section 3 Report Errors>
에러가 발생 시 에러뷰를 띄워주고 이를 무시할 수 있는 버튼을 추가해주었음.
[ Drawing Timer View ]
< Section 1 Create the meeting timer view >
overlay
오버레이의 기본 맞춤은 . center이므로 원 앞의 레이어에 텍스트 보기를 추가하면 텍스트가 원의 중앙에 표시된다.
< Section 2 Draw an arc segment >
import SwiftUI
struct SpeakerArc: Shape {
let speakerIndex: Int
let totalSpeakers: Int
private var degreesPerSpeaker: Double {
360.0 / Double(totalSpeakers)
}
private var startAngle: Angle {
Angle(degrees: degreesPerSpeaker * Double(speakerIndex) + 1.0)
}
private var endAngle: Angle {
Angle(degrees: startAngle.degrees + degreesPerSpeaker - 1.0)
}
func path(in rect: CGRect) -> Path {
let diameter = min(rect.size.width, rect.size.height) - 24.0
let radius = diameter / 2.0
let center = CGPoint(x: rect.midX, y: rect.midY)
return Path { path in
path.addArc(center: center, radius: radius, startAngle: startAngle, endAngle: endAngle, clockwise: false)
}
}
}
https://developer.apple.com/tutorials/swiftui/drawing-paths-and-shapes
< Section 3 Draw the progress ring >
마이크사용과 대화 인식을 위한 타겟추가
댓글