# μμ΄λ‘ App - REAEME
# π μμ΄λ‘ App  
> π©π»βπ» 2023.01.30~ 2023.02.04
**μ μ μ λͺ¨λ°μΌκΈ°κΈ°μ μ€μκ° μμ΄λ‘, κ°μλλ₯Ό μΈ‘μ νλ μ ν리μΌμ΄μ
μ
λλ€.**
- 0.1μ΄ λ¨μλ‘ μ΅λ 60μ΄κΉμ§ μΈ‘μ ν μ μμ΅λλ€.
- μΈ‘μ κ°μ X, Y, Z μΆ λ³νλ₯Ό μ€μκ°μΌλ‘ λ³νλ κ·Έλνλ‘ λ³΄μ¬μ€λλ€.
- μ μ₯ λ λ°μ΄ν°λ‘ κ·Έλν μ λλ©μ΄μ
μ μ¬μνκ±°λ μ΅μ’
κ²°κ³Ό κ·Έλνλ‘ λ³Ό μ μμ΅λλ€.
---
## π λͺ©μ°¨
1. [ν μκ°](#-ν-μκ°)
2. [κΈ°λ₯ μκ°](#-κΈ°λ₯-μκ°)
3. [κ°λ°νκ²½ λ° μ μ©κΈ°μ ](#-κ°λ°νκ²½-λ°-μ μ©κΈ°μ )
4. [Class Diagram](#-class-diagram)
5. [ν΄λ ꡬ쑰](#-ν΄λ-ꡬ쑰)
6. [νμλΌμΈ](#-νμλΌμΈ)
7. [νλ‘μ νΈμμ κ²½ννκ³ λ°°μ΄ κ²](#-νλ‘μ νΈμμ-κ²½ννκ³ -λ°°μ΄-κ²)
8. [κ³ λ―Όν λΆλΆ](#-κ³ λ―Όν-λΆλΆ)
9. [νΈλ¬λΈ μν
](#-νΈλ¬λΈ-μν
)
10. [μ°Έκ³ λ§ν¬](#-μ°Έκ³ -λ§ν¬)
---
## π± ν μκ°
<table>
</tr>
<tr>
<td style="text-align:center" > <a href="https://github.com/sunny-maeng">μ¨λμΏ ν€</a> </td>
<td style="text-align:center" > <a href= "https://github.com/yuvinrho"> λ‘λΉ </td>
</tr>
<td style="text-align:center" > <img width="180px" img style="border: 2px solid lightgray; border-radius: 90px;-moz-border-radius: 90px;-khtml-border-radius: 90px;-webkit-border-radius: 90px;" src="https://avatars.githubusercontent.com/u/107384230?v=4"> </td>
<td style="text-align:center" > <img width="180px" img style="border: 2px solid lightgray; border-radius: 90px;-moz-border-radius: 90px;-khtml-border-radius: 90px;-webkit-border-radius: 90px;" src="https://avatars.githubusercontent.com/u/49301866?v=4"> </td>
</tr>
<tr>
<td style="text-align:center" > - λ°μ΄ν°λͺ©λ‘ νλ©΄ Viewμ Logic λ΄λΉ <br> - λ€μ보기 νλ©΄ View λ΄λΉ <br> - CoreDataλ₯Ό ν΅ν λ°μ΄ν° κ΄λ¦¬ <br> - λ°μ΄ν° νΈλ€λ§ Errorμ²λ¦¬ <br> - GraphView MVVM λμμΈν¨ν΄ μ μ© </td>
<td style="text-align:center" > - μΈ‘μ νλ©΄ Viewμ Logic λ΄λΉ <br> - λ€μ보기 νλ©΄ Logic λ΄λΉ <br> - μμ΄λ‘, κ°μλ μΈ‘μ <br> - κ·Έλν 그리기 <br> - FileManagerλ₯Ό ν΅ν λ°μ΄ν° κ΄λ¦¬ </td>
</tr>
<tr>
<td colspan= "2"> <strong><center>곡ν΅κΈ°μ¬</center></strong> </td>
</tr>
<tr>
<td colspan= "2" style="text-align:center" > - Modelνμ
νμ ꡬν <br> - μΈ‘μ ν λ°μ΄ν° λΉλκΈ°λ‘ μ μ₯ </td>
</table>
---
## π± κΈ°λ₯ μκ°
### 1. Main νλ©΄
- μμ΄λ‘, κ°μλ μΈ‘μ λ°μ΄ν° 리μ€νΈ νλ©΄μ
λλ€.
- μ μ₯λ λ°μ΄ν°λ₯Ό 10κ°μ© λΆλ¬μμ 보μ¬μ€λλ€.
|λ©μΈ νλ©΄|νμ΄μ§ ꡬν|
|:-:|:-:|
|<img src="https://i.imgur.com/U3LVbkv.png" width="200" height="400"/>|<img src="https://i.imgur.com/3Espvcq.gif" width="200" height="400"/>|
### 2. κ°μλ, μμ΄λ‘ μΈ‘μ νλ©΄
- μΈκ·Έλ¨ΌνΈ 컨νΈλ‘€λ¬μμ Accelerometer, Gyro μΌμλ₯Ό μ νν ν μΈ‘μ λ²νΌμ λλ₯΄λ©΄ 0.1μ΄ λ¨μλ‘ μΈ‘μ ν λ°μ΄ν°λ₯Ό κ·Έλνμ 보μ¬μ€λλ€.
- μ΅λ 1λΆ λμ λ°μ΄ν°λ₯Ό μΈ‘μ ν μ μμ΅λλ€.
- μ μ₯λ²νΌμ λλ₯΄λ©΄ μΈ‘μ ν κ²°κ³Όλ CoreData, FileManagerλ₯Ό μ΄μ©ν΄ λλ°μ΄μ€μ μ μ₯λ©λλ€.
- μ μ₯μ Activity Indicatorλ₯Ό νμν©λλ€.
- μ μ₯μ€ν¨μ μλμ νμν©λλ€.
|κ°μλ μΈ‘μ |μμ΄λ‘ μΈ‘μ |
|:-:|:-:|
|<img src="https://i.imgur.com/Cgq7hAh.gif" width="200" height="400"/>|<img src="https://i.imgur.com/j7hV3HY.gif" width="200" height="400"/>|
|μ μ₯ μ±κ³΅|μ μ₯ μ€ν¨|
|:-:|:-:|
|<img src="https://i.imgur.com/uzVadZu.gif" width="200" height="400"/>|<img src="https://i.imgur.com/jggWbFZ.gif" width="200" height="400"/>|
### 3. μ μ₯ν μμ΄λ‘, κ°μλ μΈ‘μ κ²°κ³Ό κ·Έλννλ©΄
- μ μ₯ν λ°μ΄ν°λ₯Ό κ·Έλνλ‘ νμΈν μ μμ΅λλ€.
- λ©μΈ νλ©΄μμ μ
μ ν°μΉνλ©΄ κ·Έλνλ₯Ό νμΈν μ μμ΅λλ€.
- μ
μ μ€μμ΄ννμ¬ Playλ²νΌμ λλ₯΄λ©΄ κ·Έλνλ₯Ό 리νλ μ΄ν΄μ νμΈν μ μμ΅λλ€.
|κ·Έλν|κ·Έλν 리νλ μ΄|
|:-:|:-:|
|<img src="https://i.imgur.com/raGTBZm.gif" width="200" height="400"/>|<img src="https://i.imgur.com/JlbLfgt.gif" width="200" height="400"/>|
### 4. λ€ν¬λͺ¨λ μ§μ
- μ¬μ©μλ₯Ό μν΄ λ€ν¬λͺ¨λλ₯Ό μ§μν©λλ€.
|λ©μΈνλ©΄|μΈ‘μ νκΈ° νλ©΄|κ·Έλν νλ©΄|
|:-:|:-:|:-:|
|<img src="https://i.imgur.com/xY5fwO5.png" width="200" height="400"/>|<img src="https://i.imgur.com/zqgWR1P.png" width="200" height="400"/>|<img src="https://i.imgur.com/i22rieg.gif" width="200" height="400"/>|
---
## π κ°λ°νκ²½ λ° μ μ©κΈ°μ
 
| UI | λ°μ΄ν° μ μ₯ | μν€ν
μ² |
| :--------: | :--------: | :--------: |
| <img height = 90, src = "https://i.imgur.com/q6rTXrE.png"> | <img height = 90, src = "https://i.imgur.com/DSnI74h.png"> <img height = 90, src = "https://i.imgur.com/p6nJlhN.png"> | <img height = 70, src = "https://i.imgur.com/FWud4LR.png"> <img height = 70, src = "https://i.imgur.com/TY8lr5s.png">
| UIKit <br> (Only Code) | CoreData / FileManager | MVC + λΆλΆ MVVM |
- **MVC μ±ν μ΄μ **
- MVCν¨ν΄μ κ°μ₯ λΉ λ₯΄κ² ꡬνν μ μλ λͺ¨λΈλ‘ Model, View, Controller κ΄μ¬μ¬λ₯Ό λΆλ¦¬νμ¬ μ μ§λ³΄μλ₯Ό μ½κ² ν μ μλ€κ³ μκ°ν΄ μ±ννμ΅λλ€.
- MVCν¨ν΄μ νν λ§νλ `Massiveν ViewController`κ° λ μ μλ λ¨μ μ΄ μμ§λ§ νμ¬ νλ‘μ νΈλ ν κ°μ νλ©΄μμ λ΄λΉνλ λ‘μ§μ΄ λ§μ§ μλ€κ³ νλ¨νμ΅λλ€.
- **MVVM μ μ© μ΄μ **
- κ·Έλνλ₯Ό 그리λ GraphicViewλ `BezierPath`λ₯Ό μ΄μ©ν΄ μ€μκ°μΌλ‘ λ·°λ₯Ό μλ°μ΄νΈ ν΄μΌνλλ°, `Model`μ μ§μ κ°μ§κ³ κ·Έλνλ₯Ό 그리λκ² μ©μ΄νλ€ νλ¨ν΄ `ViewModel`μ μ μ©νμ΅λλ€.
---
## π Class Diagram
### π MVC + λΆλΆ MVVM
||
|---|
### π DataStorage κ³μΈ΅κ΅¬μ‘°
|<img width = 500, src ="https://i.imgur.com/Dm9Q1HU.png">|
|---|
---
## π ν΄λ ꡬ쑰
```
GyroData
βββ Views
β βββ LineGraphView
β βββ ListCell
β βββ ListView
β βββ MeasurementView
β βββ ReviewView
β
βββ Controllers
βΒ Β βββ ListViewController
βΒ Β βββ MeasurementViewController
βΒ Β βββ ReviewViewController
β
βββ Models
βΒ Β βββ AxisValue
βΒ Β βββ Measurement
βΒ Β βββ Sensor
βΒ Β βββ SensorManager
β
βββ Extension +
βΒ Β βββ Date +
βΒ Β βββ JSONDecoder +
βΒ Β βββ JSONEncoder +
βΒ Β βββ UIAlertController +
βΒ Β βββ UILabel +
βΒ Β βββ UIStackView +
β
βββ DataStorage
βΒ Β βββ DataHandleableProtocol
βΒ Β βββ DataHandleError
βΒ Β βββ CoreData
βΒ Β βΒ Β βββ CoreDataManager+DataHandleable
βΒ Β βΒ Β βββ MeasurementCoreModel
βΒ Β βββ FileManager
βΒ Β βββ SensorFileManager
β
βββ Supporting Files
Β βββ AppDelegate
Β βββ SceneDelegate
Β βββ Base.lproj
Β βΒ Β βββ LaunchScreen.storyboard
Β βββ Info.plist
Β βββ Assets.xcassets
```
---
## β° νμλΌμΈ
### π Step1 - (μ΄ 2μΌ) 2023.01.30 ~ 2023.01.31
| | μ§ν λ΄μ© |
| :--------: | -------- |
| 1 | Model νμ λ° μμ± |
| 2 | `ListView` 그리기 λ° `ListViewController` λ‘μ§ κ΅¬ν |
| 3 | `MeasurementView` 그리기 λ° `MeasurementViewController` λ‘μ§ κ΅¬ν |
### π Step2 - (μ΄ 4μΌ) 2023.02.01 ~ 2023.02.04
| | μ§ν λ΄μ© |
| :--------: | -------- |
| 1 | λ°μ΄ν° μ²λ¦¬ νμ
ꡬν λ° μ μ© - `CoreDataManager`, `SensorFileManager` μμ± |
| 2 | `ReviewPageView` 그리기 λ° `ReviewPageViewController` λ‘μ§ κ΅¬ν |
| 3 | λ°μ΄ν° μ²λ¦¬ μ€ Error λ°μ Alert μ²λ¦¬ |
---
## π νλ‘μ νΈμμ κ²½ννκ³ λ°°μ΄ κ²
- [X] CoreDataλ₯Ό μ΄μ©ν TableView Paging
- `scrollView Delegate`μ `offset`μ μ¬μ©ν΄ ν
μ΄λΈλ·° λ§μ§λ§ μ€ν¬λ‘€ μ§μ μ νμΈν μ μμ΅λλ€.
- CoreDataμ `fetchLimit`, `fetchOffset`λ₯Ό μ¬μ©ν΄ 10κ°μ© λ°μ΄ν°λ₯Ό κ°μ Έ μ¬ μ μμ΅λλ€.
- [X] CoreMotionμ μ΄μ©ν μΌμ(μμ΄λ‘, κ°μλ)μΈ‘μ
- `CoreMotion`κ³Ό `Timer`λ₯Ό μ΄μ©νμ¬ μ€μ ν interverλ§λ€ λ°μ΄ν°λ₯Ό μΈ‘μ νκ³ νμμμμ΄ λλ©΄ μΈ‘μ μ μ μ§ν©λλ€.
- [X] `FileManager`λ₯Ό μ΄μ©ν΄ μΈ‘μ ν λ°μ΄ν° λλ°μ΄μ€μ μ μ₯
- μΈ‘μ νλ©΄μμ μ μ₯ λ²νΌμ λλ₯΄λ©΄ λλ°μ΄μ€μ jsonνμΌλ‘ μ μ₯ν©λλ€.
---
## π κ³ λ―Όν λΆλΆ
### 1οΈβ£ μΆνμ λ°μ΄ν°μμ΄ λ§μμ Έ λ°μ΄ν°μ²λ¦¬ κΈ°μ μ€νμ΄ λ³κ²½λ λ μμ μ μ©μ΄ν¨ κ³ λ €
νμ¬λ λ°μ΄ν° μ²λ¦¬λ₯Ό `CoreData`μ `FimeManager`λ₯Ό μ¬μ©νκ³ μμ΅λλ€.
μΆνμ λ°μ΄ν°μμ΄ λ§μμ Έ μλ²μ μ μ₯ν΄μΌνλ€λκ°, λ°μ΄ν°λ² μ΄μ€ κΈ°μ μ€νμ΄ λ³κ²½λ λ μμ μ΄ μ©μ΄νλλ‘ νκ³ μΆμμ΅λλ€.
κ·Έλμ `DataHandleable`λ‘ νλ‘ν μ½ μΆμννμ΅λλ€. μ΄ νλ‘ν μ½μ `DataType`μ `associatedtype`μΌλ‘ μ§μ ν΄μ μ¬μ©ν μ μμ΅λλ€. κ·Έλ¦¬κ³ λ°μ΄ν° μ²λ¦¬μ νμν CRUD μ€ νμ¬ νλ‘μ νΈμμ νμν λ°μ΄ν° μ μ₯(Create), λ°μ΄ν° κ°μ Έμ€κΈ°(Read), λ°μ΄ν° μμ (Delete) λ©μλλ₯Ό νμꡬν λ©μλλ‘ μ μΈλμ΄μμ΅λλ€.
μ ν¬ νλ‘μ νΈμμλ `Measurement`ꡬ쑰체(DTO)λ₯Ό μ΄μ©ν΄ λ°μ΄ν°λ₯Ό λ€λ£¨κ³ κ³ μκΈ° λλ¬Έμ νλ‘ν μ½ μμμ μ΄μ©ν΄ `DataType`μ `Measurement`λ‘ μ¬μ©νλ `MeasurementDataHandleable` νλ‘ν μ½μ μμ±νκ³ , `CoreData`μ `FimeManager`λ₯Ό κ΄λ¦¬νλ `Class`μμ `MeasurementDataHandleable`νλ‘ν μ½μ μ±ννλλ‘ νμ΅λλ€.
λ‘μ§ μ€, CRUD λ©μλλ‘ λ°μ΄ν°λ₯Ό μ²λ¦¬ν΄μΌν λ, μΆμννμ
μΈ`MeasurementDataHandleable`μ λ©μλλ₯Ό μ¬μ©νλλ‘ κ΅¬νν΄ λμμ μΆν λ°μ΄ν°μ²λ¦¬ κΈ°μ μ€νμ΄ λ³κ²½λλλΌλ μ΄ νλ‘ν μ½μ μ±νν΄ CRUDλ‘μ§μ ꡬννκ³ , μΈμ€ν΄μ€λ§ κ°μ λΌμμ€ ν μ¬μ©ν μ μμ΅λλ€.
### 2οΈβ£ μΈ‘μ ν λ°μ΄ν° μ μ₯μ Activity Indicator νμ
μΈ‘μ νλ©΄μμ μ μ₯λ²νΌμ λλ₯΄λ©΄ `Activity Indicator`κ° νλ©΄μ λμ€λ©΄μ λΉλκΈ°λ‘ μ μ₯λκ² κ΅¬ννμμ΅λλ€.
μ΄ κ³Όμ μμ `Activity Indicator`κ° λ³΄μ΄μ§ μκ³ λ°λ‘ μ μ₯λλ λ¬Έμ κ° μμ΅λλ€.
ν΄λΉ λ¬Έμ λ μλμ κ°μ΄ @escaping closureλ₯Ό μ΄μ©νμ¬ `storeDataInDevice` λ©μλκ° λλλ©΄ `Activity Indicator`κ° λ©μΆλλ‘ νμ¬ ν΄κ²°νμμ΅λλ€.
```swift
startActivityIndicator()
storeDataInDevice {
self.stopActivityIndicator()
DispatchQueue.main.async {
self.navigationController?.popViewController(animated: false)
}
```
### 3οΈβ£ Errorκ° λ°μνλ©΄ Userκ° μ±μ μνλ₯Ό μ μ μλλ‘ Alert μ²λ¦¬
μ μ₯λ μΈ‘μ κ°λ€μ λΆλ¬μ€κ±°λ μΈ‘μ ν κ°μ μ μ₯νκ³ μμ ν λ λ°μν μ μλ `Error`λ€μ `DataHandleError` μ΄κ±°νμΌλ‘ μ 리νκ³ , Errorμ `localizedDescription`μ `overriding` νμ΅λλ€.
Errorκ° λ°μνλ©΄ "Error" `Alert`μ λμ΄μ£Όκ³ , `overriding`ν localizedDescriptionμ Alertμ `Message`λ‘ μ¬μ©ν΄ μ μ κ° μ±μ μν©μ μ μ μλλ‘ κ΅¬ννμ΅λλ€.
|  |  |  |
| -------- | -------- | -------- |
---
## π μ°Έκ³ λ§ν¬
[곡μλ¬Έμ]
- [Developer - Article: Getting Raw Gyroscope Events](https://developer.apple.com/documentation/coremotion/getting_raw_gyroscope_events)
- [Developer - Core Motion](https://developer.apple.com/documentation/coremotion)
- [Developer - Core Data](https://developer.apple.com/documentation/coredata)
- [Developer - FileManager](https://developer.apple.com/documentation/foundation/filemanager)
- [Developer - Article: About Apple File System](https://developer.apple.com/documentation/foundation/file_system/about_apple_file_system)