- 👩💻 프로젝트 및 개발자 소개👨💻
- ⚙️ 개발환경 및 라이브러리
- 🟦 프로젝트 로고
- ❇️ 핵심 키워드
- 📱 구현 화면
- 👩🏻 코드 설명
- 📁 폴더 구조
- ⚡️ 트러블 슈팅
- 🔗 참고 링크
소개 : 6축 데이터(acc 3축 + gyro 3축)를 다뤄보고, 데이터를 생성하여 CoreData와 json파일로 저장하고, 불러오는 앱입니다.
프로젝트 기간 : 2022.12.26 ~ 2022.12.30
엘렌(Ellen) | 언체인(Unchain) | 예톤(Yeton) |
---|---|---|
- MVVM 아키텍처 구조 설계
- 측정 및 플레이 로직 구현
- AnalysisView 측정 화면 구현
- 그래프뷰 구현
- SwiftUI와 UIKit 데이터 바인딩
- 메인화면 구현
- 코어데이터 구현
- 파일매니저 구현
- PlayView 구현
Combine
FileManager
SwiftUI
Charts(SwiftUI)
CoreData
MVVM
UITableView
GraphView
DataFormatting
SwiftUI + Combine
UIKit
메인 화면 | 측정 화면 |
---|---|
Play 화면 | View 화면 |
---|---|
FileManager 저장
- 그래프를 그려주는 모델을 저장
- file url 경로를 타고 들어가면 아래와 같은 파일이 생성되어 있음
CoreData 저장
- 테이블 뷰 셀을 그려주는 모델을 CoreData에 저장
CoreData
GraphFileManager
: File에 json 데이터 형태로 그래프 관련 데이터를 저장하는 FileManager ManagerCoreDataManager
: CoreData에 key value 형태로 셀 관련 데이터를 저장하는 CoreData Manager
Model
CellModel
: Cell에 보여지는 모델 타입GraphModel
: Graph에 보여지는 모델 타입AnalysisType
: Acc와 Gyro 타입을 선택할 수 있는 enum
Manager
AnalysisManager
: 측정을 시작하고 종료하는 메서드를 관리하는 타입
Domain
GraphRecordViewModel
: 그래프와 관련된 모델을 전달받아 뷰컨트롤러에 전달해주는 뷰 모델GraphRecordViewController
: 일반 View와 Play View를 조건에 맞게 화면에 보여주는 뷰 컨트롤러AnalyzeListViewController
: 첫 번째 화면, 측정이 완료되면 기록되는 테이블 뷰를 가지고 있는 뷰 컨트롤러AnalyzeListViewModel
: 셀에 보여줄 데이터를 전달해주는 뷰 모델AnalysisTableViewCell
: 테이블 뷰에 보여질 오토레이아웃과 요소들을 설정한 셀HostingViewController
: SwiftUI의 뷰와 UIKit의 코드를 연결해주기 위해 생성한 뷰 컨트롤러AnalyzeViewController
: 측정 버튼을 누르면 그래프가 실시간으로 측정되어 그려지는 화면을 보여주는 뷰 컨트롤러AnalyzeViewModel
: 측정된 값을 코어데이터에 저장하는 뷰 모델
Extension
UIColor+
: 자주 사용되는 색을 지정할 수 있도록 확장UIView+
: 뷰컨트롤러에 뷰를 추가하는 기능을 확장Date+
: DateFormatter를 사용하여 날짜 형식 기능 확장
SupportFile
GyroData
: CoreData에 존재하는 Entity 타입
.
├── AppDelegate.swift
├── CoreData
│ ├── CoreDataManager.swift
│ └── GraphFileManager.swift
├── Domain
│ ├── Analyze
│ │ ├── AnalyzeViewController.swift
│ │ ├── AnalyzeViewModel.swift
│ │ └── HostingViewController.swift
│ ├── GraphView.swift
│ ├── Record
│ │ ├── GraphRecordView.swift
│ │ └── GraphRecordViewModel.swift
│ └── TableView
│ ├── AnalyzeListViewController.swift
│ ├── AnalyzeListViewModel.swift
│ └── Cell
│ └── AnalysisTableViewCell.swift
├── Extension
│ ├── UIColor+.swift
│ ├── UIDate+.swift
│ └── UIView+.swift
├── Manager
│ └── AnalysisManager.swift
├── Model
│ └── AnalysisType.swift
├── SceneDelegate.swift
├── SupportFile
│ ├── Assets.xcassets
│ │ ├── AccentColor.colorset
│ │ │ └── Contents.json
│ │ ├── AppIcon.appiconset
│ │ │ └── Contents.json
│ │ └── Contents.json
│ ├── Base.lproj
│ │ └── LaunchScreen.storyboard
│ ├── GyroData.xcdatamodeld
│ │ └── GyroData.xcdatamodel
│ │ └── contents
│ └── Info.plist
└── ios-wanted-GyroData.txt
처음 과제를 받고 차트를 그리려고 할 때 올해 WWDC에서 보았던 SwiftUI Charts가 생각이 났습니다. iOS16에서 사용할 수 있는 신 기술이기도 해서 한번 적용해보고 싶은 마음에 SwiftUIView를 그려서 UIHostingViewcontroller를 이용하여 사용했습니다. charts를 사용하여 만든 그래프뷰에 데이터가 바로 반영이 되지않는 트러블이 일어났습니다. 컴바인을 사용했기 때문에 데이터 바인딩이 잘 됐을거라고 판단을 했었지만 그것은 UIKit의 관점에서는 당연했지만 SwiftUIView는 프로퍼티레퍼로 바인딩된 데이터에만 뷰를 새로 그리기 때문에 다른방법을 찾아야 했습니다. 처음에는 ObservableObject를 이용하여 바인딩을 시도 했었는데 어떤 이유에선지 바인딩이 잘 되지 않았고 Environmentobject를 활용하기로 했습니다. 일반적인 데이터의 전달은 ObservableObject를 이용 하지만 모든 뷰가 동일 한 모델을 가리키고 데이터의 변화에 즉시 업데이트가 필요한 경우에 사용하게 됩니다. Environmentobject의 장점이 데이터의 변경에 따라 어떤 부분이든 동기화를 보장 한다는 것이기 때문입니다.
데이터 흐름을 오직 단방향으로 흐르게 구현했습니다. 데이터 흐름이 단순하여 새로운 기능이 추가되어도 리팩토링에 용이합니다. 또한 Swift의 프로토콜 프로그래밍 패러다임을 채택하여 ViewModel의 Input 시그널과 Output 시그널 모두를 Protocol을 사용하여 인터페이스화 했습니다.
처음 Model을 구상하였을 때에는 Model 내부 프로퍼티의 타입에 아래와 같이 Core Data에서 제공하는 기본 타입이 아닌 커스텀한 타입이 존재했습니다.
struct GyroDataModel: Codable {
let id: UUID
let analysisType: AnalysisType
let savedAt: [Date]
let measurementTime: Double
}
커스텀 한 타입인 AnalysisType, [Date]
등을 Core Data에 넣어주고자 했으나 이미 만들어진 entity와 파일을 수정하는 것에 어려움을 겪었습니다.
그래서 아이폰 앱마다 존재하는 공간을 관리하는 매니저
인 FileManager를 사용하는 방식을 추가하였습니다.
결론적으로 CoreData는 셀에 보여지는 기본적인 타입을 저장
하는 데 사용했고, FileManager는 아이폰 앱 내부 경로에 json 형태로 커스텀 한 타입을 저장
할 때 사용하여 두 가지 방식을 모두 채택하였습니다.