Realm -> Swift Data 변경
사진, 인바디 정보, 운동 루틴으로 몸의 변화를 쉽게 관찰할 수 있는 헬스 기록 앱
2023.03 ~ 2023.08 (진행중)
| 날짜 | 버전 |
|---|---|
| 23.07.28 | v1.0.0 |
🔖 온보딩 플로우 - 앱에 대한 전반적인 설명 후, 온보딩을 넘어가면 다시 나타나지 않아요.
📈 몸무게, 체지방량, 골격근량 입력 및 분석 플로우 - 날마다 입력한 신체 정보를 차트로 한 눈에 비교할 수 있어요.
📸 오운완 사진 촬영 및 저장 플로우 - 날마다 사진을 찍고 저장해 몸의 변화를 한 눈에 비교할 수 있어요.
🎞️ 갤러리에서 사진 가져오기 및 저장 플로우 - 갤러리(전체 권한, 선택 권한)에서 가져와 몸의 변화를 한 눈에 비교할 수 있어요.
💪 운동 루틴 만들기 플로우 - 나만의 루틴을 만들고 확인할 수 있어요.
🏋️ 운동하기 플로우 - 날마다 루틴을 가져와 운동을 하거나 즉석에서 루틴을 만들어 운동을 하고 확인할 수 있어요.
─── WorkoutDone
│ ├── 📁 Resources
│ │ ├── 📁 Fonts
│ │ ├── Assets.xcassets
│ │ ├── LaunchScreen
│ │ ├── AppDelegate.swift
│ │ ├── SceneDelegate.swift
│ │ └── Info.plist
│ │
│ ├── 📁 Sources
│ │ ├── 📁 Presenter
│ │ │ └── 📁 Scene
│ │ │ ├── 📁 ViewController
│ │ │ ├── 📁 ViewModel
│ │ │ └── 📁 Cells
│ │ │ │ │ ├── 📁 Model
│ │ ├── 📁 Classes
│ │ └── 📁 Extensions
│ │
│ └── 📁 Utils
└── 📁 WorkoutDoneTests
🍎 네이밍
- UpperCamelCase 사용
// - example
structMyTicketResponseDTO{}classUserInfo{}- lowerCamelCase 사용하고 동사로 시작
// - example
privatefunc setDataBind(){}- pop, push, present, dismiss
- 동사 + To + 목적지 뷰 (다음에 보일 뷰)
- dismiss는 dismiss + 현재 뷰
// - example pop, push, present
popToFirstViewController()pushToFirstViewController()presentToFirstViewController()dismissFirstViewController()- register + 목적어
// - example
registerXib()registerCell()- 서비스함수명 + WithAPI
// - example
fetchListWithAPI()requestListWithAPI()fetch는 무조건 성공
request는 실패할 수도 있는 요청
- 동사원형 + 목적어 + WithAnimation
showButtonsWithAnimation()delegate 메서드는 프로토콜명으로 네임스페이스를 구분
좋은 예:
protocolUserCellDelegate{func userCellDidSetProfileImage(_ cell:UserCell)func userCell(_ cell:UserCell, didTapFollowButtonWith user:User)}protocolUITableViewDelegate{func tableview( ....)func tableview...}protocolJunhoViewDelegate{func junhoViewTouched()func junhoViewScrolled()}Delegate 앞쪽에 있는 단어를 중심으로 메서드 네이밍하기
나쁜 예:
protocolUserCellDelegate{
// userCellDidSetProfileImage() 가 옳음
func didSetProfileImage()func followPressed(user:User)
// `UserCell`이라는 클래스가 존재할 경우 컴파일 에러 발생 (userCell 로 해주자)
func UserCell(_ cell:UserCell, didTapFollowButtonWith user:User)}함수 이름 앞에는 되도록이면 get 을 붙이지 않습니다.
- lowerCamelCase 사용
letuserName:String- 각 case 에는 lowerCamelCase 사용
enumUserType{case viewDeveloper
case serverDeveloper
}약어로 시작하는 경우 소문자로 표기, 그 외에는 항상 대문자
// 좋은 예:
letuserID:Int?lethtml:String?letwebsiteURL:URL?leturlString:String?// 나쁜 예:
letuserId:Int?letHTML:String?letwebsiteUrl:NSURL?letURLString:String?setUI(): @IBOutlet속성 설정
setLayout(): 레이아웃 관련 코드
setDataBind(): 배열 항목 세팅. 컬렉션뷰 에서 리스트 초기 세팅할때
setAddTarget(): addtarget 모음
setDelegate(): delegate, datasource 모음
setCollectionView(): 컬렉션뷰 관련 세팅
setTableView(): 테이블뷰 관련 세팅
initCell(): 셀 데이터 초기화
registerXib(): 셀 xib 등록.setNotification(): NotificationCenter addObserver 모음
헷갈린다? set을 쓰세요 ^^🍎 코드 레이아웃
들여쓰기에는 탭(tab) 대신 4개의 space를 사용합니다.
콜론(
:)을 쓸 때에는 콜론의 오른쪽에만 공백을 둡니다.let names: [String: String]?let name: String연산자 오버로딩 함수 정의에서는 연산자와 괄호 사이에 한 칸 띄어씁니다.
func ** (lhs: Int, rhs: Int)
함수를 호출하는 코드가 최대 길이를 초과하는 경우에는 파라미터 이름을 기준으로 줄바꿈합니다. 파라미터가 3개 이상이면 줄바꿈하도록!!
단, 파라미터에 클로저가 2개 이상 존재하는 경우에는 무조건 내려쓰기합니다.
UIView.animate( withDuration:0.25, animations:{ // doSomething() }, completion:{ finished in // doSomething() })
if let구문이 길 경우에는 줄바꿈하고 한 칸 들여씁니다.iflet user =self.veryLongFunctionNameWhichReturnsOptionalUser(),let name = user.veryLongFunctionNameWhichReturnsOptionalName(), user.gender ==.female { // ... }
guard let구문이 길 경우에는 줄바꿈하고 한 칸 들여씁니다.else는 마지막 줄에 붙여쓰기guardlet user =self.veryLongFunctionNameWhichReturnsOptionalUser(),let name = user.veryLongFunctionNameWhichReturnsOptionalName(), user.gender ==.female else{return}guardlet self =selfelse{return}(X)guardlet self =selfelse{return}(O)
else 구문이 길 시 줄바꿈
클래스 선언 다음에 , extension 다음에 한 줄 띄어주기
빈 줄에는 공백이 포함되지 않도록 합니다. ( 띄어쓰기 쓸데없이 넣지 말기 )
모든 파일은 빈 줄로 끝나도록 합니다. ( 끝에 엔터 하나 넣기)
MARK 구문 위와 아래에는 공백이 필요합니다.
// MARK: Layout overridefunc layoutSubviews(){ // doSomething() } // MARK: Actions overridefunc menuButtonDidTap(){ // doSomething() }
모듈 임포트는 알파벳 순으로 정렬합니다. 내장 프레임워크를 먼저 임포트하고, 빈 줄로 구분하여 서드파티 프레임워크를 임포트합니다.
import UIKit
import Moya
import SnapKit
import SwiftyColor
import Thenimport UIKit
import SwiftyColor
import SwiftyImage
import JunhoKit
import Then
import URLNavigator🍎 클로저
파라미터와 리턴 타입이 없는 Closure 정의시에는
() -> Void를 사용합니다.좋은 예:
let completionBlock: (() -> Void)?나쁜 예:
let completionBlock: (() -> ())? let completionBlock: ((Void) -> (Void))?Closure 정의시 파라미터에는 괄호를 사용하지 않습니다.
좋은 예:
{ operation, responseObject in // doSomething() }
나쁜 예:
{(operation, responseObject)in // doSomething() }
Closure 정의시 가능한 경우 타입 정의를 생략합니다.
좋은 예:
...,completion:{ finished in // doSomething() }
나쁜 예:
...,completion:{(finished:Bool)->Voidin // doSomething() }completion:{ data ->Voidin // doSomething() }(X)
Closure 호출시 또다른 유일한 Closure를 마지막 파라미터로 받는 경우, 파라미터 이름을 생략합니다.
좋은 예:
UIView.animate(withDuration:0.5){ // doSomething() }
나쁜 예:
UIView.animate(withDuration:0.5, animations:{()->Voidin // doSomething() })
🍎 주석
코드는 가능하면 자체적으로 문서가 되어야 하므로, 코드와 함께 있는 인라인(inline) 주석은 피한다.
classViewController:UIViewController{
// MARK: - Property
// MARK: - UI Property
// MARK: - Life Cycle
// MARK: - Setting
// MARK: - Action Helper
// MARK: - @objc Methods
// MARK: - Custom Method
}
// MARK: - Extensions커스텀 메서드, 프로토콜, 클래스의 경우에 퀵헬프 주석 달기
/// (서머리 부분)
/// (디스크립션 부분)
classMyClass{letmyProperty:Intinit(myProperty:Int){self.myProperty = myProperty
}}
/**summary
(서머리 부분)
> (디스크립션 부분)
- parameters:
- property: 프로퍼티
- throws: 오류가 발생하면 customError의 한 케이스를 throw
- returns: "\\(name)는 ~" String
*/
func printProperty(property:Int){print(property)}- 참고 :
🍎 프로그래밍 권장사항
좋은 예:
letname:String="철수"letheight:Float="10.0"나쁜 예:
letname="철수"letheight="10.0"프로토콜을 적용할 때에는 extension을 만들어서 관련된 메서드를 모아둡니다.
좋은 예:
finalclassMyViewController:UIViewController{
// ...
}
// MARK: - UITableViewDataSource
extensionMyViewController:UITableViewDataSource{
// ...
}
// MARK: - UITableViewDelegate
extensionMyViewController:UITableViewDelegate{
// ...
}나쁜 예:
finalclassMyViewController:UIViewController,UITableViewDataSource,UITableViewDelegate{
// ...
}
// 프로토콜 여러개를 한곳에 몰아서 때려넣지 말자!🍎 기타규칙
self는 최대한 사용을 지양 →**알잘딱깔센 self…**viewDidLoad()에서는 함수호출만delegate 지정, UI관련 설정 등등 모두 함수와 역할에 따라서 extension 으로 빼기
필요없는 주석 및 Mark 구문들 제거
deinit{}모든 뷰컨에서 활성화guard let으로 unwrapping 할 시, nil 가능성이 높은 경우에는else{}안에print()해서 디버깅하기 쉽게 만들기return사용시 두 줄 이상 코드가 있을 시, 한 줄 띄고return사용func fetchFalse()->Bool{returnfalse}(O)func isDataValid(data:Data?)->Bool{guardlet data else{returnfalse}returntrue}(O)func isDataValid(data:Data?)->Bool{guardlet data else{returnfalse}returntrue}(X)
강제 언래핑 금지 (!)
RxSwift
- https://github.com/ReactiveX/RxSwift
Realm
- https://github.com/realm/realm-swift
SnapKit
- https://github.com/SnapKit/SnapKit
Then
- https://github.com/devxoul/Then
DeviceKit
- https://github.com/devicekit/DeviceKit
| 류창휘 (@ryuchanghwi) | 봉혜미 (@hyemi) |
|---|---|













