---
# System prepended metadata

title: "\U0001F919 Ground Rule"

---

# 🤙 Ground Rule
## 🧑‍🤝‍🧑 Rules
매일 11시 시작

## ⏰ 스크럼
- 당일 컨디션 공유, 특이사항
- 공부한거 공유
- 오늘 할일 공유

(매일 작성하자!)

## 📝 Commit convention (Karma Style)
feat: 새로운 기능 추가<br>
fix: 버그 수정<br>
docs: 문서 수정<br>
refactor: 코드 리팩토링<br>
style: 코드 포맷팅 (코드 변경이 없는 경우)<br>
test: 테스트 코드 작성<br>
chore: 소스 코드를 건들지 않는 작업(빌드 업무 수정)<br>
merge: 메서드 혹은 타입 이름 from 사용자<br>
asset: 에셋 추가 혹은 에셋 수정<br>
design: 사용자 UI 디자인 변경<br>
build: 빌드 구성 업데이트, 개발 도구 또는 사용자와 관련 없는 기타 변경 사항.


#### 🙌 01/31 스크럼
- 오늘의 컨디션
    - 인호: 낫배드 ~ 굿 사이
    - 애종: 괜츈
    - 하모: 굳

- 특이사항
    - 인호: 없음
    - 애종: 없음
    - 하모: 없음
- 오늘 할 일
    - [x] 모델 만들기
    - [x] UML 그리기
    - [x] 엠쁴엠
    - [ ] 개인 공부
        - [ ] 공부 후 10시에 만나서 스크럼 + 앞으로 역할 어떻게 나눌지 얘기
    - [ ] 네비게이션, 테이블뷰, 버튼들, 코어데이터 관련, 커스텀 그 차트뷰, FileManager

#### 🙌 02/01 스크럼
- 오늘의 컨디션
    - 인호: 낫배드 ~ 굿 사이
    - 애종: 괜츈
    - 하모: 굳

- 특이사항
    - 인호: 없음
    - 애종: 없음
    - 하모: 없음

- 오늘 할 일
    - [x] CoreData 구현
    - [x] FileManager 구현
    - [x] Core Motion, Timer 구현

#### 🙌 02/01 스크럼
- 오늘의 컨디션
    - 인호: 낫배드 ~ 굿 사이
    - 애종: 괜츈
    - 하모: 굳

- 특이사항
    - 없음
    - 애종: 없음
    - 하모: 없음
    
- 오늘 할 일
    - [x] 모델 만들기
    - [x] UML 그리기
    - [x] 엠쁴엠
    - [ ] 개인 공부
        - [ ] 공부 후 10시에 만나서 스크럼 + 앞으로 역할 어떻게 나눌지 얘기
    - [ ] 네비게이션, 테이블뷰, 버튼들, 코어데이터 관련, 커스텀 그 차트뷰, FileManager









View
Model

https://miro.com/welcomeonboard/UEFtZjE3Tjh6eDJDQ2Ywb2U4Q0JOMGVRcTBSQXNxTmVUYXlKY2RXQlMxdG41eVlSOHpCTk1jSGNMSGhFRG16dXwzNDU4NzY0NTM1NDcyNjUwOTE2fDI=?    share_link_id=989212750040


```swift
final class ViewController: UIViewController {
    @IBOutlet private weak var xLabel: UILabel!
    @IBOutlet private weak var yLabel: UILabel!
    @IBOutlet private weak var zLabel: UILabel!
    
    private let coreMotionManager = CMMotionManager()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        coreMotionManager.accelerometerUpdateInterval = 0.1
        coreMotionManager.gyroUpdateInterval = 0.1
        print(coreMotionManager.isAccelerometerActive)
    }
    
    @IBAction func tapGyroButton(_ sender: Any) {
        startGyroscopes()
    }
    
    @IBAction func tapGyroStopButton(_ sender: Any) {
        coreMotionManager.stopGyroUpdates()
    }
    
    @IBAction func tapAccButton(_ sender: Any) {
        startAccelerometers()
    }
    
    @IBAction func tapAccStopButton(_ sender: Any) {
        coreMotionManager.stopAccelerometerUpdates()
    }
    
    private func startAccelerometers() {
        if !coreMotionManager.isAccelerometerActive && !coreMotionManager.isGyroActive
        {
            guard let current = OperationQueue.current else { return }
            coreMotionManager.startAccelerometerUpdates(to: current) { [weak self] data, error in
                guard let self = self,
                      let data = data?.acceleration,
                      error == nil
                else {
                    return
                }
                
                self.xLabel.text = data.x.description
                self.yLabel.text = data.y.description
                self.zLabel.text = data.z.description
            }
        }
    }
    
    private func startGyroscopes() {
        if !coreMotionManager.isGyroActive && !coreMotionManager.isAccelerometerActive
        {
            guard let current = OperationQueue.current else { return }
            coreMotionManager.startGyroUpdates(to: current) { [weak self]data, error in
                guard let self = self,
                      let data = data?.rotationRate,
                      error == nil
                else {
                    return
                }
                
                self.xLabel.text = data.x.description
                self.yLabel.text = data.y.description
                self.zLabel.text = data.z.description
            }
        }
    }
}



```

## 그래프 그리기
# **UIBezierPath + CAShapeLayer로 그래프 그리기**

> 진행 순서
> 
> 1. CAShapeLayer를 만든다.
> 2. UIBezierPath를 만든다.
> 3. 2에서 만든 UIBezierPath로 내가 만들고 싶은 path만들어줌
> 4. 1에서 만든 CAShapeLayer의 path프로퍼티에 2에서 만든 UIBezierPath넣어준다.
> 5. CAShapeLayer를 addSubLayer해줌.

```swift


import UIKit

class LineGraphView: UIView {
    private let valueXLabel = {
        let label = UILabel()
        label.textColor = .systemRed
        label.text = "x: -3362"
        
        return label
    }()
    
    private let valueYLabel = {
        let label = UILabel()
        label.textColor = .systemGreen
        label.text = "y: -143"
        
        return label
    }()
    
    private let valueZLabel = {
        let label = UILabel()
        label.textColor = .systemBlue
        label.text = "z: 2497"
        
        return label
    }()
    
    var values: [CGFloat] = []
    var xvalues: [CGFloat] = [10, 20, 40, 0, 30, 10, 20, 30]
    var yvalues: [CGFloat] = [50, 70, 10, 60, 90, 50, 10, 10]
    
    init(frame: CGRect, values: [CGFloat]) {
        super.init(frame: frame)
        self.values = values
        
        configureView()
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    private func configureView() {
        self.layer.borderWidth = 2
        self.addSubview(valueXLabel)
        self.addSubview(valueYLabel)
        self.addSubview(valueZLabel)
    }
    
    override func draw(_ rect: CGRect) {
        super.draw(rect)
        
        drawPath(values, UIColor.systemRed.cgColor)
        drawPath(xvalues, UIColor.cyan.cgColor)
        drawPath(yvalues, UIColor.systemBlue.cgColor)
    }
    
    private func drawPath(_ values: [CGFloat], _ strokeColor: CGColor) {
        let graphLayer = CAShapeLayer() // 1
        let graphPath = UIBezierPath() // 2
        
        let xOffset: CGFloat = self.frame.width / CGFloat(values.count)
        var currentX: CGFloat = 0
        let startPosition = CGPoint(x: currentX, y: self.frame.height / 2)
        graphPath.move(to: startPosition)
        
        for i in 0..<values.count {
            currentX += xOffset
            let newPosition = CGPoint(x: currentX, y: self.frame.height / 2 - values[i])
            
            graphPath.addLine(to: newPosition)
        }
        
        graphLayer.fillColor = nil
        graphLayer.strokeColor = strokeColor
        graphLayer.lineWidth = 2
        
        let newPath = graphPath.cgPath
        
        graphLayer.path = newPath // 4
        self.layer.addSublayer(graphLayer) // 5
    }
}

```

## 핵앰디로 따라와 
- 뷰3개 & 뷰모델들
    - 첫번째 뷰 & 뷰모델(셀 & 셀 뷰모델)
    - 두번째 뷰 & 뷰모델
    - 세번째 뷰 & 뷰모델

    - 뷰모델에서 매니저들 메서드 갖다 쓰는
    - 첫번째 세번째 화면은 코어데이터, 파일매니저, 타이머 
    - 두번째는 측정기, 타이머
    이렇게 쓰일것같아요

- 무슨무슨 매니저들
    - 코어데이터 (CoreData)
    - 파일매니저 (FileManager)
    - 측정기, 타이머 (coreMotion & Timer)
    > 프로토콜 위주로 구현

- 그래프 그리는 커스텀 GraphView

- 모델(모델, 엔티티)
- CoreData Entity -> 날짜 시간 모션타입 ID

- JSON에 그 1800개 담는 식


## 리드미

# 📈 GyroData App
**사용자의 기기에서 가속도, 자이로 값을 측정하는 애플리케이션입니다.**

> 프로젝트 기간: 23.1.30 ~ 23.2.5 (7일)
> 팀원: 인호, 애종, 하모

## 📖 목차
1. [팀 소개](#-팀-소개)
2. [기능 소개](#-기능-소개)
3. [적용 기술](#-적용-기술)
4. [폴더 구조](#-폴더-구조)

## 🧑‍💻 팀 소개
|[인호](https://github.com/inho-98)|[애종](https://github.com/jonghancha)|[하모](https://github.com/lxodud)|
|:-:|:-:|:-:|
|<a href="https://github.com/inho-98"><img width="180px" src="https://user-images.githubusercontent.com/71054048/188081997-a9ac5789-ddd6-4682-abb1-90d2722cf998.jpg"></a>|<a href="https://github.com/inho-98"><img width="180px" src="https://i.imgur.com/7K7rXiB.jpg"></a>|<a href="https://github.com/inho-98"><img width="180px" src="https://i.imgur.com/PcNDqvt.png"></a>|
|`CoreData`, `ListScene`|`FileManager`, `MeasureScene, GraphView`|`CoreMotion`, `DetailScene, GraphView`|

## 📱 기능 소개
#### 1.메인 리스트![]()

|<img src=https://i.imgur.com/aZF0ooW.gif width=200>|<img src=https://i.imgur.com/hjt0Eas.gif  width=200>|
|:-:|:-:|
|페이징|삭제|
#### 2.가속도, 자이로 측정화면

|<img src=https://i.imgur.com/VK5R6sC.gif  width=200>|<img src=https://i.imgur.com/9O69BAP.gif  width=200>|
|:-:|:-:|
|가속도 측정|자이로 측정|
#### 3.측정내용 View, Play화면

|<img src=https://i.imgur.com/IOTl3cd.gif  width=200>|
|:-:|
|그래프 Play|

## ⚙️ 적용 기술
#### `MVVM`
- `ViewController`의 코드가 방대해지는 것을 방지하고, 뷰에 보여질 데이터 및 비즈니스 로직의 분리를 명확히 하기 위해 `MVVM`을 적용하였습니다.
- 데이터 및 비즈니스 로직은 `ViewModel`, 뷰의 상태 관리는 `ViewController`, 뷰를 생성하는 로직은 `View`로 분리하였습니다.

#### `CoreData` & `FileManager`
- `CoreData`에는 모델의 생성 일자, 측정 기간, 측정 타입`(Gyo, Acc), UUID`값을 저장합니다.
- `FileManager`에는 측정한 결과값 배열을 저장하고, 모델의 UUID값을 통해 접근합니다.

#### `CoreMotion`
- 사용자 기기의 `Gyro & Acc`값을 측정합니다.
- `Gyro & Acc` 측정 기능의 공통되는 부분을 프로토콜에 구현하였습니다.

#### `GraphView`
- `UIBezierPath`를 이용하여 그래프를 그려줍니다.
- 뷰에 격자를 보여주는 부분과 그래프를 보여주는 부분을 분리하여 구현하였습니다.

## 📂 폴더 구조
```
├── Info.plist
├── MotionDataModel.xcdatamodeld
│   └── MotionDataModel.xcdatamodel
│       └── contents
├── Resource
│   ├── Assets.xcassets
│   │   ├── AccentColor.colorset
│   │   │   └── Contents.json
│   │   ├── AppIcon.appiconset
│   │   │   └── Contents.json
│   │   └── Contents.json
│   └── Base.lproj
│       └── LaunchScreen.storyboard
└── Source
    ├── App
    │   ├── AppDelegate.swift
    │   └── SceneDelegate.swift
    ├── Error
    │   ├── CoreDataError.swift
    │   ├── FileManagingError.swift
    │   └── MotionManagerError.swift
    ├── GraphView
    │   ├── GraphBackgroundView.swift
    │   ├── GraphView.swift
    │   └── GraphViewModel.swift
    ├── Model
    │   ├── MotionCoordinate.swift
    │   ├── MotionData.swift
    │   ├── MotionMeasures.swift
    │   └── MotionType.swift
    ├── Protocol
    │   ├── CoreDataManageable.swift
    │   ├── FileManagerProtocol.swift
    │   └── MotionManagerable.swift
    ├── Scene
    │   ├── DetailScene
    │   │   ├── DetailView.swift
    │   │   ├── DetailViewController.swift
    │   │   └── DetailViewModel.swift
    │   ├── ListScene
    │   │   ├── DateFormatter +.swift
    │   │   ├── MainViewController.swift
    │   │   ├── MainViewModel.swift
    │   │   ├── MotionCellViewModel.swift
    │   │   └── MotionDataCell.swift
    │   └── MeasureScene
    │       ├── MeasureViewController.swift
    │       └── MeasureViewModel.swift
    └── Service
        ├── AccelerometerMotionManager.swift
        ├── FileHandleManager.swift
        ├── GyroMotionManager.swift
        └── MeasureTimer.swift
```