본문 바로가기
💻 Development/SwiftUI

[SwiftUI] 0928 우당탕탕 Scrumdinger 만들기(완) - Trigger sound with AVFoundation ~ (추석연휴 복습분량 추가)

by Claudia 끌라우 2023. 9. 28.
반응형

* 공부했던 내용을 주관적으로 해석하여 '제가' 이해하기 쉽도록 작성하였습니다. 잘못된 정보가 있을 시 알려주시면 제게 큰 힘이 됩니다!


해당 작업을 하기 전에 작업 파일에 사운드 파일과 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

 

swiftui_mvvm.md

GitHub Gist: instantly share code, notes, and snippets.

gist.github.com


https://woozzang.tistory.com/29

 

[Swift] 값타입과 참조타입 ( Value Type vs Reference Type )

1차 수정: 2021.06.06 안녕하세요. 이번 글에서는 스위프트의 값타입과 참조타입의 차이점을 메모리에 저장되는 관점에서 다루어보겠습니다🐶 *속성감시자에서 값타입과 참조타입의 작동차이가

woozzang.tistory.com


[ 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

 

Adopting Swift concurrency | Apple Developer Documentation

Swift supports asynchronous functions that enable you to more easily write responsive and correct asynchronous code. In this article, you’ll learn how to define and call asynchronous functions, and explore how Swift concurrency simplifies complex asynchr

developer.apple.com

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

 

Codable | Apple Developer Documentation

A type that can convert itself into and out of an external representation.

developer.apple.com

https://zeddios.tistory.com/373

 

Swift ) 왕초보를 위한 Codable / JSON Encoding and Decoding

안녕하세요 :) Zedd입니다.< 최신버전 확인하기 / App Udpate >글쓰다가 갑자기 Codable 개삘Swift4가 나오기전에, JSON 파싱이 Swift4에서는 한줄로 끝난단다 하는 유튜브 영상을 봐서 오옷했던 기억이 있네

zeddios.tistory.com

https://zeddios.tistory.com/394

 

Swift ) 왕초보를 위한 Codable - CodingKey

안녕하세요 :) Zedd입니다.오늘은...Codable의 두번째 시간/?...저도 못다뤄본 기능이 많아서 zzzz굉장히 다뤄보고 싶어요 키키저번시간엔 기본적인 Codable의 사용법? 그러니까 JSON으로 만들어내고, JSON

zeddios.tistory.com


< 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

 

[swift] 12. 예외 처리 (throws, throw, do - try - catch)

1. 예외 처리 필요한 이유 - optional타입은 오류가 발생했을 때 오류에 대한 정보를 외부로 전달할 방법이 없음 2. 구현 방법 - 함수가 반환할 오류는 일관된 주제와 연관된 경우(문자열이 있을 때,

ios-development.tistory.com

- do-catch구문 : 오류에 대한 예외처리를 담당

 

 


[ Handling Error ]

통제할 수 없는 예기치 않은 문제에 대한 대응을 하기 위한 단계.

(eg) 네트워크 연결이 끊어짐. 파일에서 데이터를 읽는 데 여러 가지 방식으로 실패함)
스크럼딩거에서 문제가 발생할 경우 지침을 제공하여 이러한 오류를 처리할 수 있다.

< Section 1 Add an error wrapper structure >

데이터 지속성에서 스크럼 데이터를 파일 시스템에 읽고 쓰는 방식으로 스크럼딩거에 지속성을 추가했다.
그러나 파일에 읽기 권한이 없거나 앱에서 지정한 경로에 파일이 존재하지 않을 수 있다. 또는 파일의 인코딩이 올바른 형식이 아니어서 작업에서 오류가 발생할 수 있다.
작업이 실패하면 실패의 원인을 파악하면 사용자가 적절하게 대응할 수 있다.
이 섹션에서는 오류를 나중에 사용자에게 표시할 메시지와 연결하는 구조를 추가한다.

 


< Section 2 Create an Error View >


< Section 3 Report Errors>

에러가 발생 시 에러뷰를 띄워주고 이를 무시할 수 있는 버튼을 추가해주었음.

ErrorView
시작뷰


[ 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

 

Drawing Paths and Shapes | Apple Developer Documentation

Users receive a badge whenever they visit a landmark in their list. Of course, for a user to receive a badge, you’ll need to create one. This tutorial takes you through the process of creating a badge by combining paths and shapes, which you then overlay

developer.apple.com


< Section 3 Draw the progress ring >

 


마이크사용과 대화 인식을 위한 타겟추가

반응형

댓글