├── .gitignore ├── Style ├── images │ ├── 2.Yml.png │ └── 1.RunScript.png ├── SwiftLint.md ├── Style_Guide.md └── API_Design_Guidelines_With_Grammar.md ├── Architecture ├── images │ └── CleanDevelopment-12.jpg └── CleanArchitecture.md ├── Dependency_Manager ├── images │ ├── 2.RunScriptPhase.png │ ├── 3.UnitTestOrFramework.png │ ├── 4.UnitTestOrFramework2.png │ └── 1.LinkedFrameworksAndLibraries.png └── Carthage.md ├── Clean_Code ├── README.md ├── 형식_맞추기.md ├── 의미있는이름.md ├── 주석.md ├── Clean_code.md ├── 객체와_자료_구조.md.md └── 함수.md ├── DesignPattern └── Singleton.md ├── ETC ├── DataSource.md ├── Class_Diagram.md └── Layout.md ├── Store_Data └── Store_Data.md ├── Git └── Git_Commit_Message.md ├── RxSwift ├── Traits.md └── Observable_Disposing_Subject_Variable.md ├── Architecture_Pattern └── MVVM.md ├── Swift ├── ValueSemanticsAndPerformance.md └── Access_Control.md ├── Concurrency └── GrandCentralDispatch.md ├── README.md └── OOP └── SOLID.md /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store -------------------------------------------------------------------------------- /Style/images/2.Yml.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dev-yong/iOS-Programming-Reference/HEAD/Style/images/2.Yml.png -------------------------------------------------------------------------------- /Style/images/1.RunScript.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dev-yong/iOS-Programming-Reference/HEAD/Style/images/1.RunScript.png -------------------------------------------------------------------------------- /Architecture/images/CleanDevelopment-12.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dev-yong/iOS-Programming-Reference/HEAD/Architecture/images/CleanDevelopment-12.jpg -------------------------------------------------------------------------------- /Dependency_Manager/images/2.RunScriptPhase.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dev-yong/iOS-Programming-Reference/HEAD/Dependency_Manager/images/2.RunScriptPhase.png -------------------------------------------------------------------------------- /Dependency_Manager/images/3.UnitTestOrFramework.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dev-yong/iOS-Programming-Reference/HEAD/Dependency_Manager/images/3.UnitTestOrFramework.png -------------------------------------------------------------------------------- /Dependency_Manager/images/4.UnitTestOrFramework2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dev-yong/iOS-Programming-Reference/HEAD/Dependency_Manager/images/4.UnitTestOrFramework2.png -------------------------------------------------------------------------------- /Dependency_Manager/images/1.LinkedFrameworksAndLibraries.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dev-yong/iOS-Programming-Reference/HEAD/Dependency_Manager/images/1.LinkedFrameworksAndLibraries.png -------------------------------------------------------------------------------- /Clean_Code/README.md: -------------------------------------------------------------------------------- 1 | # Clean Code 2 | 3 | Uncle Bob의 [Clean Code - 애자일 소프트웨어 장인 정신 번역본](http://www.yes24.com/Product/goods/11681152) 을 읽고 정리한 내용입니다. 4 | 5 | ## Table of Contents 6 | 7 | - [Chapter 1. Clean Code](Clean_code.md) 8 | - [Chapter 2. 의미 있는 이름](의미있는이름.md) 9 | - [Chapter 3. 함수](함수.md) 10 | - [Chapter 4. 주석](주석.md) 11 | - [Chapter 5. 형식 맞추기](형식_맞추기.md) 12 | - [Chapter 6. 객체와 자료 구조](객체와_자료_구조.md) 13 | 14 | -------------------------------------------------------------------------------- /DesignPattern/Singleton.md: -------------------------------------------------------------------------------- 1 | # Singleton 2 | 3 | - 특정 클래스의 인스턴스가 오직 하나임을 보장하는 객체입니다. 4 | - 글로벌하게 액세스할 수 있는 클래스의 공유 인스턴스를 제공하기 위해 사용합니다. 5 | - e.g. `NSNotificationCenter`, `UIApplication` , ... 6 | - iOS Application은 기본적으로 Multi-threading을 이용하기 때문에, Singleton 객체는 thread-safe하게 만들어주어야합니다. 7 | 8 | ```swift 9 | class Singleton { 10 | static let sharedInstance = Singleton() 11 | } 12 | ``` 13 | 14 | ## Reference 15 | 16 | - https://github.com/ochococo/Design-Patterns-In-Swift 17 | - https://developer.apple.com/documentation/swift/cocoa_design_patterns/managing_a_shared_resource_using_a_singleton 18 | - [https://ko.wikipedia.org/wiki/%EC%8B%B1%EA%B8%80%ED%84%B4_%ED%8C%A8%ED%84%B4](https://ko.wikipedia.org/wiki/싱글턴_패턴) 19 | 20 | -------------------------------------------------------------------------------- /ETC/DataSource.md: -------------------------------------------------------------------------------- 1 | # Data Sources 2 | 3 | ## Overview 4 | 5 | - `DataSource`는 UI의 제어를 위임하는 대신, **데이터 제어를 위임**한다는 점을 제외하면 `Delegate`와 같습니다. 6 | - `DataSource`는 `NSView` 와 `UIView` objects (가시적인 데이터의 행으로 부터 source를 필요로 하는 Table view와 Outline view)를 보유한 outlet입니다. 7 | - View의 data source는 일반적으로 delegate의 역할을 하는 동일한 객체일 수도 있지만, 모든 객체일 수도 있습니다. 8 | - `DataSource` 는 view에 필요로하는 data를 제공하기 위하여, 그리고 advanced implmentation에서 유저가 직접적으로 변경한 데이터를 처리하기 위하여 하나 이상의 informal protocol method를 구현해야한다. 9 | - `DataSource`는 데이터를 요청하는 객체로 부터 메세지를 받기 위하여 존재해야 하는 객체입니다. 10 | - `DataSource`는 UI객체에 전달하는 객체의 지속성에 대한 책임을 가지고 있습니다. 11 | - 즉, 그러한 object들의 memory management에 책임이 있습니다. 12 | - View object (outline view, table view와 같은) 가 datasource의 데이터에 접근할 때마다, 데이터를 사용하는 한 객체를 보존한다. 13 | 14 | ### Reference 15 | 16 | - https://developer.apple.com/library/archive/documentation/General/Conceptual/CocoaEncyclopedia/DelegatesandDataSources/DelegatesandDataSources.html 17 | 18 | -------------------------------------------------------------------------------- /Store_Data/Store_Data.md: -------------------------------------------------------------------------------- 1 | # Data storage 2 | 3 | ## UserDefaults 4 | 5 | `NSUserDefaults` as the name suggests (vaguely) should be used for storing preferences and app settings only. You should not be storing critical data and or user data into them 6 | 7 | - Easy to store and retrieve data. 8 | - Useful for storing default values with minimum fuzz. 9 | - Not suitable for large data sets 10 | - Performance hit when you try to store and load large amount of data All or nothing approach 11 | 12 | ## CoreData 13 | 14 | CoreData is a full fledged persistent framework which supports large data transaction 15 | 16 | - Reliable framework to interact and query against data 17 | - Can be extremely fast when setup correctly (with relationships) 18 | - Powerful capabilities 19 | 20 | ## NSKeyedArchiver 21 | 22 | ## SQLite 23 | 24 | ## Realm 25 | 26 | ## Reference 27 | 28 | - https://stackoverflow.com/questions/47412289/userdefaults-or-core-data 29 | - [UserDefaults](https://developer.apple.com/documentation/foundation/userdefaults) 30 | - [Core Data](https://developer.apple.com/documentation/coredata) 31 | - [Core Data Programming Guide](https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/CoreData/index.html) 32 | - [NSKeyedArchiver](https://developer.apple.com/documentation/foundation/nskeyedarchiver) -------------------------------------------------------------------------------- /Clean_Code/형식_맞추기.md: -------------------------------------------------------------------------------- 1 | # Chapter 5. 형식 맞추기 2 | 3 | ## Table of Contents 4 | 5 | - [형식을 맞추는 목적](#형식을-맞추는-목적) 6 | - [적절한 행 길이를 유지하라](#적절한-행-길이를-유지하라) 7 | - [신문 기사처럼 작성하라](#신문-기사처럼-작성하라) 8 | - [개념은 빈 행으로 분리하라](#개념은-빈-행으로-분리하라) 9 | - [세로 밀집도](#세로-밀집도) 10 | - [수직 거리](#수직-거리) 11 | - [가로 형식 맞추기](#가로-형식-맞추기) 12 | 13 | ## 형식을 맞추는 목적 14 | 15 | - 코드 형식은 의사소통의 일환이다. 16 | - 원래 코드는 사라질지라도 개발자의 스타일과 규율은 사라지지 않는다. 17 | 18 | ## 적절한 행 길이를 유지하라 19 | 20 | - 일반적으로 큰 파일보다 작은 파일이 이해하기 쉽다. 21 | 22 | ### 신문 기사처럼 작성하라 23 | 24 | - 소스 파일의 이름은 간단하면서도 설명이 가능하게 짓는다. 25 | - 이름만 보고도 올바른 모듈을 살펴보고 있는지 아늰지를 판단할 정도로 신경 써서 짓는다. 26 | - 아래로 내려갈 수록 의도를 세세하게 묘사한다. 27 | - 마지막에는 가장 저차원 함수와 세부내역이 나온다. 28 | 29 | ### 개념은 빈 행으로 분리하라 30 | 31 | - 생각의 사이는 빈 행을 넣어 분리한다. 32 | 33 | ### 세로 밀집도 34 | 35 | - 서로 밀접한 코드 행은 세로로 가까이에 놓는다. 36 | 37 | ### 수직 거리 38 | 39 | - 같은 파일에 속할 정도로 밀접한 두 개넘은 세로 거러로 연관성을 표현한다. 40 | - 변수는 사용하는 위치에 최대한 가까이 선언한다. 41 | - 인스턴스 변수 42 | - 클래스 맨 처옴에 선언한다 43 | 44 | - 종속함수 45 | - 한 함수가 다른 함수를 호출한다면 두 함수는 세로로 가까이 배치한다. 46 | - 또한 가능하다면 호출하는 함수를 호출되는 함수보다 먼저 배치한다 47 | - 개념적 유사성 48 | - 개념적인 친화도가 높을수록 코드를 가까이 배치한다. 49 | - 세로 순서 50 | - 함수 호출 종속성은 아래 방향으로 유지한다. 51 | - 모듈이 고차원에서 저차원으로 자연스럽게 내려간다. 52 | 53 | ### 가로 형식 맞추기 54 | 55 | - 짧은 행을 선호한다. 56 | 57 | - 가로 공백과 밀집도 58 | - 연산자 우선순위를 강조하기 위해서도 공백을 사용한다. 59 | 60 | - 들여쓰기 61 | - 범위로 이루어진 계층ㅇㄹ 표현하기 위하여 코드를 들여쓴다. 62 | - 들여쓰는 정도는 계층에서 코드가 자리잡는 수준에 비례한다. -------------------------------------------------------------------------------- /Git/Git_Commit_Message.md: -------------------------------------------------------------------------------- 1 | # Git Commit Message 2 | 3 | 오롯이 혼자 작업하는 경우에는 크게 영향을 받지 않겠지만, 다른 이들과의 협업 시에는 커뮤니케이션이 매우 중요합니다. 4 | 5 | 변경된 코드를 다 보지 않아도, Commit Log만 보고도 어느정도의 내용을 알 수 있다면 어떨까요? 6 | 7 | 협업과 리뷰, 유지보수성이 올라갈 것입니다. 8 | 9 | - 제목과 본문을 한 줄 띄워 분리하기 10 | - 제목은 영문 기준 50자 이내로 11 | - 제목의 첫글자는 대문자로 12 | - 단, 제목의 마지막에 온점(.)은 금지 13 | - 제목은 **명령문**으로 (본문은 평서문) 14 | - 본문은 영문 기준 72자마다 줄 바꾸기 15 | - 본문은 **무엇을, 왜**에 맞춰 작성하기 16 | 17 | 하단의 내용은 [bitcoin에 commit된 log](https://github.com/bitcoin/bitcoin/commit/eb0b56b19017ab5c16c745e6da39c53126924ed6) 입니다. 18 | 19 | ```markdown 20 | // 제목은 대문자로 시작하며 영문 기준 50자 이내의 명령문으로 한다. 또한 온점은 금지 21 | Simplify serialize.h's exception handling 22 | // 제목과 본문을 한 줄 띄워 분리하기 23 | 24 | // 본문은 영문 기준 72자마다 줄 바꾸기 25 | // 본문은 무엇을, 왜에 맞춰 작성하기 26 | Remove the 'state' and 'exceptmask' from serialize.h's stream implementations, 27 | as well as related methods. 28 | 29 | As exceptmask always included 'failbit', and setstate was always called with 30 | bits = failbit, all it did was immediately raise an exception. Get rid of 31 | those variables, and replace the setstate with direct exception throwing 32 | (which also removes some dead code). 33 | 34 | As a result, good() is never reached after a failure (there are only 2 35 | calls, one of which is in tests), and can just be replaced by !eof(). 36 | 37 | fail(), clear(n) and exceptions() are just never called. Delete them. 38 | 39 | ``` 40 | 41 | ## Reference 42 | 43 | - https://chris.beams.io/posts/git-commit/ 44 | - https://meetup.toast.com/posts/106 45 | 46 | -------------------------------------------------------------------------------- /Clean_Code/의미있는이름.md: -------------------------------------------------------------------------------- 1 | # Chapter 2. 의미 있는 이름 2 | 3 | ## Table of Contents 4 | 5 | - [의미와 의도](#의미와-의도) 6 | - [의도를 분명히 밝혀라](#의도를-분명히-밝혀라) 7 | - [그릇된 정보를 피해라](#그릇된-정보를-피해라) 8 | - [의미 있게 구분하라](#의미-있게-구분하라) 9 | - [한 개념에 한 단어를 사용하라](#한-개념에-한-단어를-사용하라) 10 | - [의미 있는 맥락을 추가하라](#의미-있는-맥락을-추가하라) 11 | - [문법](#문법) 12 | - [발음하기 쉬운 이름을 사용하라](#발음하기-쉬운-이름을-사용하라) 13 | - [클래스 이름](#클래스-이름) 14 | - [메서드 이름](#메서드-이름) 15 | 16 | ## 의미와 의도 17 | 18 | ### 의도를 분명히 밝혀라 19 | 20 | - 주석이 필요하다면 의도를 분명히 드러내지 못했다는 말이다. 21 | - 코드에 대한 이해와 변경이 쉬워진다. 22 | - 상수만을 사용하지말고, 검색이 쉽게 변수명을 사용해라. 23 | - e.g. `workDaysPerWeek = 5` 24 | - 명료한 이름을 선택해라. 25 | 26 | ### 그릇된 정보를 피해라 27 | 28 | - 널리 쓰이는 의미가 있는 단어의 사용을 피한다. 29 | - e.g. 빗변(hypotenuse)를 hp라는 단어로 사용 30 | - 실제 List가 아니라면 변수명에 List를 붙이지 않는다. 31 | - e.g. List가 아닌 경우 accounts 또는 accountGroup 32 | - 흡사한 이름을 사용하지 않도록 주의한다. 33 | - 일관성이 떨어지는 표기법은 그릇된 정보이다. 34 | 35 | ### 의미 있게 구분하라 36 | 37 | - 이름이 달라야 한다면 그 의미도 달라져야 한다. 38 | - e.g. Customer와 CustomerObject 39 | 40 | ### 한 개념에 한 단어를 사용하라 41 | 42 | - 추상적인 개념 하나에 단어 하나를 선택한다. 43 | - e.g. fetch, retrieve, get 44 | 45 | ### 의미 있는 맥락을 추가하라 46 | 47 | - 클래스, 함수, 이름 공간에 넣어 맥락을 부여한다. 48 | - e.g. street, houseNumber, city, state, zipcode를 Address라는 class의 하위에 둔다. 49 | - 맥락을 개선함으로서, 함수를 쪼개기도 쉬워진다. 50 | 51 | ## 문법 52 | 53 | ### 발음하기 쉬운 이름을 사용하라 54 | 55 | - 올바른 영단어의 변수명을 사용하자. 56 | 57 | ```swift 58 | //generate date, year, month, day, hour, minute, second의 축약형 59 | ❌ func genymdhms() 60 | 61 | ✅ func generateTimestamp() 62 | ``` 63 | 64 | > 문법적으로 올바른, 잘 읽히는 네이밍을 하라는 것 같습니다. 65 | 66 | ### 클래스 이름 67 | 68 | - 명사나 명사구를 사용한다. 69 | - 단, Manager, Processor, Data, Info 와 같은 단어는 피한다. 70 | 71 | ### 메서드 이름 72 | 73 | - 동사나 동사구가 적합하다. -------------------------------------------------------------------------------- /Clean_Code/주석.md: -------------------------------------------------------------------------------- 1 | # Chapter 4. 주석 2 | 3 | ## Table of Contents 4 | 5 | - [좋은 주석](#좋은-주석) 6 | - [법적인 주석](#법적인-주석) 7 | - [정보를 제공하는 주석](#정보를-제공하는-주석) 8 | - [의도를 설명하는 주석](#의도를-설명하는-주석) 9 | - [의미를 명료하게 밝히는 주석](#의미를-명료하게-밝히는-주석) 10 | - [TODO 주석](#TODO-주석) 11 | - [나쁜 주석](#나쁜-주석) 12 | 13 | > "나쁜 코드에 주석을 달지 마라. 새로 짜라." - 브라이 언 W. 커니핸, P.J.폴라우거 14 | 15 | - 잘 달린 주석은 그 어떤 정보보다 유용하다. 16 | - **코드로 의도를 전부 표현할 수 없어 사용하는 것이 주석**이다. 17 | - 코드로 의도를 표현할 방법은 없을까? 18 | - 모든 표현을 코드로 할 수 있도록 하자. 19 | - 주석의 유지보수가 쉽지 않아 코드를 따라가지 못하여 그릇될 가능성도 커진다. 20 | - 부정확한 주석은 아예 없는 주석보다 나쁘다. 21 | 22 | ## 좋은 주석 23 | 24 | ### 법적인 주석 25 | 26 | - 저작권 정보와 소유권 정보 27 | 28 | ### 정보를 제공하는 주석 29 | 30 | - 기본적인 정보를 주석으로 제공하면 편리하다. 31 | - 함수 이름에 정보를 담는 편이 더 좋다. 32 | 33 | ```swift 34 | 🙂 35 | // 테스트 중인 Responder 인스턴스를 반환한다. 36 | protocol Responder { 37 | func responderInstance() 38 | } 39 | 40 | 함수 이름에 정보를 담는다. 41 | 😀 42 | protocol Responsder { 43 | func responderBeingTested() 44 | } 45 | ``` 46 | 47 | ### 의도를 설명하는 주석 48 | 49 | - 때때로 주석은 구현을 이해하게 도와주는 선을 넘어 결정에 깔린 의도까지 설명한다. 50 | 51 | ### 의미를 명료하게 밝히는 주석 52 | 53 | - 인수나 반환값이 표준 라이브러리나 변경하지 못하는 코드에 속한다면 의미를 명료하게 밝히는 주석이 유용하다. 54 | 55 | ```java 56 | assertTrue(a.compareTo(a) == 0); // a == a 57 | ``` 58 | 59 | ### TODO 주석 60 | 61 | - 같앞으로 할 일'을 //TODO 주석으로 남겨둔다. 62 | - 단, 나쁜 코드를 남겨두는 핑계가 되면 안된다. 63 | 64 | ## 나쁜 주석 65 | 66 | - 주절거리는 주석 67 | - 같은 이야기를 반복하는 주석 68 | - 코드의 내용을 주석으로 반복하지마라 69 | - 오해할 여지가 있는 주석 70 | - 의무적으로 다는 주석 71 | - 이력을 기록하는 주석 72 | - 소스 코드 관리 시스템에 전부 나온다. 73 | - 공로를 들리거나 저자를 표시하는 주석 74 | - 소스 코드 관리 시스템에 전부 나온다. 75 | - 있으나마나한 주석 76 | - 당연한 사실을 언급하며 새로운 정보를 제공하지 못하는 주석 77 | - 함수나 변수로표현할 수있다면 주석을 달지마라 78 | - 주석으로 처리한 코드 79 | - 다른 이가 보았을 때, 지워도 될지 의문을 갖게 된다. 80 | - 함수 헤더 81 | - 짧고 한 가지만 수행하며 이름을 잘 붙인 함수가 주석으로 헤더를 추가한 함수보다 훨씬 좋다. -------------------------------------------------------------------------------- /Clean_Code/Clean_code.md: -------------------------------------------------------------------------------- 1 | # Chapter 1. Clean Code 2 | 3 | ## Table of Contents 4 | 5 | - [코드가 존재하리라](#코드가-존재하리라) 6 | - [나쁜 코드](#나쁜-코드) 7 | - [Clean Code란?](#Clean-Code란) 8 | 9 | ## 코드가 존재하리라 10 | 11 | - 코드는 '요구사항을 표현하는 언어'라는 사실을 명심하자. 12 | 13 | ## 나쁜 코드 14 | 15 | - 나중에 고치겠다는 생각을 가져서는 안된다. **나중은 결코 오지 않는다.** 16 | 17 | > LeBlanc's Law : "Later equals never" 18 | 19 | ### 대가 20 | 21 | - 개발 속도를 크게 떨어트린다. 22 | 23 | - 나쁜 코드를 고치기 위해 또다른 얽히고설킨 나쁜 코드가 많이진다. 24 | - 쓰레기 더미가 점점 커지게 된다. 25 | 26 | - 팀의 생산성을 떨어트린다. 27 | - 시스템 설계 의도에 맞는 변경과 반하는 변경의 구분이 힘든 경우, 또다른 나쁜 코드를 만들어낸다. 28 | 29 | ### 태도 30 | 31 | - **좋은 코드를 사수하는 일은 프로그래머들의 책임**이다. 32 | - 나쁜 코드를 양산하면 엉망진창인 상태 때문에 개발 속도가 늦춰진다. 33 | - 개발 속도를 높이는 방법은 **언제나 코드를 최대한 깨끗하게 유지하는 습관**이다. 34 | 35 | ## Clean Code란 36 | 37 | - Clean code를 작성하기 위해서는 '청결'이라는 코드 감각을 활용하여 기법들을 적용하는 절제와 규율이 필요하다. 38 | - 보이스카웃 규칙 39 | - 캠프장은 처옴 왔을 때보다 더 깨끗하게 해놓고 떠나라. 40 | - 체크아웃할 때보다 좀 더 깨끗한 코드를 체크인한다면 코드는 절대 나빠지지 않 41 | 는다. 42 | - Bjarne Stroustrup 43 | - 우아한 코드 : Clean code는 보는 사람에게 즐거움을 선사해야한다. 44 | - 의존성을 최대한 줄여야 유지보수가 쉬워진다. 45 | - 오류는 명백한 전략에 의거해 철저히 처리한다. 46 | - Clean code는 세세한 사항까지 꼼꼼하게 처리하는 코드이다. 47 | - e.g. Memory leak, Race condition, Naming 48 | - Clean code는 **한 가지에 집중**한다. 49 | - Grady Booch 50 | - Clean code는 **가독성**이 좋아야한다. 51 | - 잘 쓴 문장처럼 읽혀야한다. 52 | - 코드는 반드시 필요한 내용만 담아야한다. 53 | - Dave Thomas 54 | - Clean code는 문학적이어야한다. (**가독성**이 좋아야한다.) 55 | - Clean code는 다른 사람이 고치기 쉬워야한다. 56 | - 읽기 쉬운 코드와 고치기 쉬운 코드는 다르다. 57 | - Clean code는 **테스트 케이스가 존재해야한다**. 58 | - Michael Feathers 59 | - Clean code는 **주의 깊게 작성한 코드**이다. 60 | - 깔끔하고 단정하게 정리되어잇으며 세세한 사항까지 신경쓴 코드이다. 61 | - Ron Jeffries 62 | - **중복을 피해라**. 63 | - **한 가지의 기능만 수행**한다. 64 | - 제대로 표현하라. 65 | - 작게 **추상화**하라. 66 | - Ward Cunningham 67 | - Clean code는 읽으면서 짐작대로 돌아가며 명백해야한다. 68 | - 모듈을 읽으면 다음의 상황이 보인다. -------------------------------------------------------------------------------- /Style/SwiftLint.md: -------------------------------------------------------------------------------- 1 | # Swift Lint 2 | 3 | ## Overview 4 | 5 | Swiftlint는 [Github Swift Style Guide](https://github.com/github/swift-style-guide)를 기반으로 하며, style을 따르지 않는 코드를 식별 및 표시를 해줍니다. 6 | 7 | Swift style guide ( [Github](https://github.com/github/swift-style-guide), [Ray Wenderlich](https://github.com/raywenderlich/swift-style-guide), [Google](https://google.github.io/swift/), [Swift API Design Guideline](https://swift.org/documentation/api-design-guidelines/) 등) 를 참고하여 코딩을 한다고 하지만, 모든 것을 외우고 있을 수 없어서 종종 놓치는 경우들이 존재합니다. 이러한 경우들을 Swift Lint를 이용해 손쉽게 잡을 수 있습니다. 8 | 9 | Swift Lint를 이용하게 되면, 코드 스타일의 **일관성 유지**를 추구할 수 있습니다. 단일 프로젝트, 프로젝트간, 팀간의 일관성 유지가 가능하며 **가독성** 또한 높아집니다. 10 | 11 | ## Table of Contents 12 | 13 | - [Installation and Usage](#Installation-and-Usage) 14 | - [Reference](#Reference) 15 | 16 | ## Installation and Usage 17 | 18 | #### 1. Installation 19 | 20 | 참고 : https://github.com/realm/SwiftLint#installation 21 | 22 | ```terminal 23 | $ brew install swiftlint 24 | ``` 25 | 26 | #### 2. Integrate to Xcode 27 | 28 | 참고 : https://github.com/realm/SwiftLint#xcode 29 | 30 | ![](images/1.RunScript.png) 31 | 32 | ```javascript 33 | // Run Script Phase 34 | if which swiftlint >/dev/null; then 35 | swiftlint 36 | else 37 | echo "warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint" 38 | fi 39 | ``` 40 | 41 | #### 3. Configuration 42 | 43 | SwiftLint를 입맛에 맞게 사용하기 위해서는 ".swiftlint.yml"를 만들어 수정하면 됩니다. 44 | 45 | > `line_lenght`라는 rule를 비활성화 시키고, Carthage와 Pods은 SwiftLint를 적용시키지 않는 예제입니다. 46 | 47 | ![](images/2.Yml.png) 48 | 49 | ```yaml 50 | //.swiftlint.yml 51 | disabled_rules: # rule identifiers to exclude from running 52 | - line_length 53 | included: # paths to include during linting. `--path` is ignored if present. 54 | excluded: # paths to ignore during linting. Takes precedence over `included`. 55 | - Carthage 56 | - Pods 57 | ``` 58 | 59 | ## Reference 60 | 61 | - https://github.com/realm/SwiftLint 62 | - https://academy.realm.io/kr/posts/slug-jp-simard-swiftlint/ -------------------------------------------------------------------------------- /RxSwift/Traits.md: -------------------------------------------------------------------------------- 1 | # RxSwift - Traits 2 | 3 | ## Table of Contents 4 | 5 | - Traits 6 | - RxSwift Traits 7 | - Single 8 | - Completable 9 | - Maybe 10 | - RxCocoa Traits 11 | 12 | ## Traits 13 | 14 | > 네이버 사전 : 특징, 특성, 특색 15 | 16 | - Traits는 interface의 경계를 넘어 커뮤니케이션을 도와주고 observable sequence property를 보장하는 것을 도와줍니다. 17 | 18 | - 일반적인 Observable을 사용할때보다 문맥 상의 의미와 syntatic sugar을 제공하고 use-case를 목표로 삼습니다. 19 | 20 | > Syntatic Sugar는 일을 읽기 쉽게하거나 표현하기 위해 디자인된 프로그래밍 언어 내의 syntax입니다(참고 : https://en.wikipedia.org/wiki/Syntactic_sugar) 21 | 22 | - 이러한 이유로, Traits는 전체적으로 `optional` 입니다. 23 | 24 | - Traits는 단일 read-only Observable property의 wrapper struct입니다. 25 | 26 | ```swift 27 | struct Single { 28 | let source: Observable 29 | } 30 | 31 | struct Driver { 32 | let source: Observable 33 | } 34 | ... 35 | ``` 36 | 37 | ## RxSwift Traits 38 | 39 | ### Single 40 | 41 | - **항상 하나의 element 혹은 하나의 error만을 방출**합니다. 42 | - Side Effect를 공유하지 않습니다. 43 | - `.success(value)` (= `.next` + `.completed`) 혹은 `.error` 만을 방출합니다. 44 | - 일반적인 Observable을 `.asSingler()` 을 이용하여 Single Trait으로 변형할 수 있습니다. 45 | 46 | ### Completable 47 | 48 | - **오직 complete 또는 error만 방출**합니다. 49 | - **Element를 방출하지 않습니다.** 50 | - Side Effect를 공유하지 않습니다. 51 | - 일반적인 Observable을 Completable Trait으로 변형할 수 없습니다. 52 | 53 | ### Maybe 54 | 55 | - Single과 Completable의 중간입니다. 56 | - **단일 element를 방출할 수 있고, element의 방출 없이 complete할 수 있으며, error를 방출**할 수 있습니다. 57 | - 이 3가지의 event들은 Maybe Trait을 종료합니다. 58 | - Complete된 Mabye는 종료되었기 때문에 element를 방출할 수 없고, element를 방출한 Maybe는 이 역시 종료되었기 때문에 complete될 수 없습니다. 59 | - Side Effect를 공유하지 않습니다. 60 | - 일반적인 Observable을 `.asMaybe()` 을 이용하여 Maybe Trait으로 변형할 수 있습니다. 61 | 62 | ## RxCocoa Traits 63 | 64 | ### Driver 65 | 66 | - Driver는 UI 계층에서의 reactive code를 적는 데에 있어 직관적인 방법을 제시하려는 의도를 가지고 있습니다. 67 | - **Error을 방출할 수 없습니다.** = **절대 실패하지 않습니다**. 68 | - **Observe는 main scheduler에서만 발생**합니다. 69 | - Side effect를 공유합니다. 70 | - Driver의 의도된 use-case는 application을 구동하는 sequence를 모델링하는 것입니다. 71 | - e.g. CoreData modle로부터 UI를 구동합니다. 72 | - e.g. 다른 UI elements로 부터 value를 이용하여 UI를 구동합니다. 73 | 74 | ## Reference 75 | 76 | - [RxSwift Documentation](https://github.com/ReactiveX/RxSwift/blob/master/Documentation) 77 | - [ReactiveX](http://reactivex.io/) 78 | - [RxSwift - fimuxd](https://github.com/fimuxd/RxSwift) -------------------------------------------------------------------------------- /Architecture_Pattern/MVVM.md: -------------------------------------------------------------------------------- 1 | # MVVM 2 | 3 | ## Overview 4 | 5 | **View의 추상화**를 만드는 것이 핵심입니다. View를 추상화하게 된다면 **Reusable**하며 **Testable**합니다. 6 | 7 | ### Model 8 | 9 | - 데이터, 비지니스 논리, 서비스 클라이언트 등으로 구성됩니다. 10 | 11 | ### View 12 | 13 | - 시각적인 UI 요소들을 의미합니다. 14 | - 종종 Tool에 의하여 declarative하게 정의됩니다. 15 | - Application에서 View는 UI플랫폼(UIKit)이 제공하는 컨트롤들(UILabel, UITextField, UIButton 등)을 조합해 사용자들에게 시각적으로 접근합니다. 16 | - View는 자신의 상태를 사용자에게 표현할 뿐 아니라, 사용자가 Application에 명령을 내릴 수단을 제공합니다. 17 | 18 | ### View Model 19 | 20 | - View Model이라는 이름은 "뷰의 모형"을 뜻합니다. "뷰의 모형(Model of a View)"은 즉 "추상화된 뷰(Abstraction of the View)"라는 의미입니다. 21 | 22 | - View를 추상화하기 위하여 추상화된 **View State**를 유지합니다. 23 | - 예를 들어, View Model은 읽기와 쓰기가 가능한 문자열 속성을 통하여 텍스트 입력 컨트롤(UITextField)을 추상화합니다. 24 | - 데이터 목록을 보여주는 컨트롤(UITableView, UICollectionView)에 대해서는 각 요소의 View State가 들어있는 컬렉션이 사용됩니다. 25 | 26 | - Model과 연관된 값에 대하여 View에 보여줄 때, 값의 변환을 위해 **Value Converter**를 갖습니다. 27 | - 모델이 제공하는 정보가 사용자에게 전달될 때, 혹은 그 반대의 경우 값이 그대로 사용되기도 하지만 그렇지 않은 경우도 존재합니다. 28 | - 예를 들어, `2018-02-11 22:54:01 +0000` 와 같은 값을 `1년 전` 과 같은 표현으로 변환합니다. 29 | 30 | - View와 사용자간의 관계(View 상태의 표현 및 사용자 interaction)를 위하여 View Model은 **Command**를 갖습니다. 31 | 32 | - 사용자는 Command를 통하여 Model의 행위를 실행할 수 있습니다. 33 | 34 | - **View State**, **Value Converter**, **Command**를 통하여 View Model은 "추상화된 뷰(뷰의 모델)"가 됩니다. 35 | - 따라서 개념적으로 사용자와 소통하게 됩니다. 36 | 37 | 38 | 39 | 개념적 공간에서 40 | 41 | 1. 사용자는 View Model의 속성을 이용하여 정보를 입력하고 42 | 2. 사용자는 View Model의 Command를 실행시키며 43 | 3. View Model은 자신의 속성을 갱신해 Command의 결과와 Appliation 상태변화를 사용자에게 표현합니다. 44 | 45 | 46 | 47 | 로그인 화면을 예로 들어봅시다. 48 | 49 | - 로그인 화면을 추상화하는 View Model은 이메일과 비밀번호에 대응하는 쓰기가 가능한 문자열 속성 2가지와 50 | - 로그인 버튼에 대응하는 Command를 갖게 됩니다. 51 | - 만약 서비스(모델)가 로그인 실패 코드를 반환한다면, 이 실패 코드는 Value Converter에 의하여 사용자 친화적 메세지로 변환되고, 52 | - 읽기 전용 문자열 속성을 통하여 출력됩니다. 53 | - 이제 추상화된 View(View Model)와 물리적인 View(View)를 연결해 줄 수단이 필요합니다. 54 | - MVC 패턴의 경우 Controller가 View와 Model 사이의 작업흐름을 제어할 것입니다. 55 | - MVVM 패턴에서는 작업 흐름의 제어보다는 View와 View Model의 상태를 동기화해줄 구성요소가 필요합니다. 바로 이것을 **Data binding**이라고 합니다. 56 | 57 | 58 | 59 | MVVM 패턴은 완성도 높은 Data binding 구조에 의존하게 됩니다. 60 | 61 | Data binding으로 인하여 View Model의 상태가 변경되면 View의 상태가 함께 변경이되며, 그 역이 보장됩니다. 62 | 63 | 64 | 65 | 추상화는 구체화에 대한 지식을 가지지 않기 때문에 View Model은 View에 대하여 알지 못합니다. 다른 관점에서 보면, View Model이 플랫폼에 대하여 독립적으로 재사용될 수 있음을 뜻합니다. Unit Test를 통하여 검증이 쉬우며 TDD의 도입 또한 용이합니다. 66 | 67 | > 추상화(abstraction) 68 | > 69 | > Unit Test, TDD 70 | > 71 | > Dependency Insection, Dependency Inversion principle 72 | 73 | ## Reference 74 | 75 | - https://blog.weirdx.io/post/39547 76 | - https://blogs.msdn.microsoft.com/johngossman/2005/10/08/introduction-to-modelviewviewmodel-pattern-for-building-wpf-apps/ 77 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /ETC/Class_Diagram.md: -------------------------------------------------------------------------------- 1 | # Class Diagram 2 | 3 | ## Table of Contents 4 | 5 | - [Generalization](#Generalization) 6 | - [Realization](#Realization) 7 | - [Dependency](#Dependency) 8 | - [Association](#Association) 9 | - [Aggregation](#Aggregation) 10 | - [Composition](#Composition) 11 | 12 | ![](https://upload.wikimedia.org/wikipedia/commons/0/0b/Uml_class_relation_arrows_en.svg.png) 13 | 14 | ## Generalization 15 | 16 | - 일반화 17 | 18 | - 슈퍼 클래스와 서브 클래스간의 Inheritance(상속) 관계를 나타냅니다. 19 | - 서브 클래스는 슈퍼 클래스로 Generalize(일반화) 할 수 있습니다. 20 | - 슈퍼 클래스를 서브클래스로 Specialize(구체화) 할 수 있습니다. 21 | 22 | ```swift 23 | class SuperClass { 24 | func foo() { 25 | } 26 | } 27 | 28 | class ChildClass: SuperClass { 29 | override func foo() { 30 | } 31 | } 32 | ``` 33 | 34 | ## Realization 35 | 36 | - 실체화 37 | - Interface(인터페이스)의 메서드를 실제 기능으로 구현하는 것입니다. 38 | 39 | ```swift 40 | protocol RealizationProtocol { 41 | func foo() 42 | } 43 | 44 | class Realization: RealizationProtocol { 45 | func foo() { 46 | } 47 | } 48 | ``` 49 | 50 | ## Dependency 51 | 52 | - 의존 53 | - 어떠한 클래스가 다른 클래스를 참조하는 것을 말합니다. 54 | - 단, 해당 객체의 참조를 계속 유지하지는 않습니다. 55 | 56 | ```swift 57 | class Human { 58 | //해당 객체의 참조를 계속 유지하지는 않는다. 59 | func use(_ tool: Tool) { 60 | tool.foo() 61 | } 62 | } 63 | 64 | class Tool { 65 | func foo() { 66 | } 67 | } 68 | ``` 69 | 70 | ## Association 71 | 72 | - 연관 73 | - 다른 객체의 참조를 갖는 필드를 의미합니다. 74 | - 방향성을 갖을 수 있습니다. 75 | 76 | ```swift 77 | class Person { 78 | let cars: [Car] 79 | 80 | func own(cars: [Car]) { 81 | self.cars = cars 82 | } 83 | } 84 | ``` 85 | 86 | ## Aggregation 87 | 88 | - 집약 89 | - Composition보다 약한 집합을 의미합니다. 90 | - part가 whole에 대해 독립적입니다. 91 | - whole 인스턴스는 part 인스턴스를 빌려 사용합니다. 92 | - whole 인스턴스가 소멸되어도, part 인스턴스도 소멸되지 않습니다. 93 | - whole 인스턴스가 복사되어도, part 인스턴스도 복사되지 않습니다. 94 | 95 | ```swift 96 | class Computer { 97 | let mainboard: MainBoard 98 | let cpu: CPU 99 | 100 | init(mainboard: MainBoard, cpu: CPU) { 101 | self.mainboard = mainboard 102 | self.cpu = cpu 103 | } 104 | } 105 | ``` 106 | 107 | `Computer` 가 메모리에서 사라진다하더라도 `Mainboard` 와 `CPU` 는 사라지지 않습니다. 108 | 109 | ## Composition 110 | 111 | - 합성 112 | - Aggregation보다 더 강한 집합을 의미합니다. 113 | - part가 whole에 종속적입니다. 114 | - whole 인스턴스가 part 인스턴스의 저체 수명을 책임집니다. 115 | - whole 인스턴스가 part 인스턴스를 생성합니다. 116 | - whole 인스턴스가 소멸되면, part 인스턴스도 소멸됩니다. 117 | - whole 인스턴스가 복사되면, part 인스턴스도 복사됩니다. 118 | 119 | ```swift 120 | class Computer { 121 | let mainboard: MainBoard 122 | let cpu: CPU 123 | 124 | init() { 125 | self.mainboard = Mainboard() 126 | self.cpu = CPU() 127 | } 128 | } 129 | ``` 130 | 131 | `Computer` 가 메모리에서 사라진다면, `Mainboard` 와 `CPU` 도 사라지게 됩니다. 132 | 133 | ## Reference 134 | 135 | - http://www.nextree.co.kr/p6753/ -------------------------------------------------------------------------------- /Swift/ValueSemanticsAndPerformance.md: -------------------------------------------------------------------------------- 1 | # Value Semantics & Performance 2 | 3 | ## Overview 4 | 5 | [Swift 성능 이해하기: Value 타입, Protocol과 스위프트의 성능 최적화 - 유용하님](https://academy.realm.io/kr/posts/letswift-swift-performance/) 의 글을 보고, 개인적으로 정리한 내용입니다. 6 | 7 | [WWDC15. Protocol-Oriented Programming in Swift](https://developer.apple.com/videos/play/wwdc2015/408/) 8 | 9 | [WWDC15. Building Better Apps with Value Types in Swift](https://developer.apple.com/videos/play/wwdc2015/414/) 10 | 11 | [WWDC16. Protocol and Value Oriented Programming in UIKit Apps](https://developer.apple.com/videos/play/wwdc2016/419/) 12 | 13 | 를 보며 내용을 첨삭할 예정입니다. 14 | 15 | ## Table of Contents 16 | 17 | - [Value Semantics](#Value Semantics) 18 | - [Considerations for Performance](#Considerations-for-Performance) 19 | - [Memory Allocation](#Memory-Allocation) 20 | - [Reference Counting](#Reference-Counting) 21 | - [Method dispatch](#Method-dispatch) 22 | - [Swift Abstract Techniques and Performance](#Swift Abstract Techniques and Performance) 23 | - [Reference](#Reference) 24 | 25 | ## Value Semantics 26 | 27 | `struct`, `enum`, `tuple` 28 | 29 | - 특징 30 | 31 | - **Heap을 사용하지 않습니다.** (only, stack!) 따라서 Reference Couting이 필요없습니다. 32 | 33 | > class의 경우, stack에 reference가, heap에 구조체가 할당됩니다. 34 | 35 | - 변수 할당 시 stack에 값 전체가 저장됩니다. 36 | - 다른 변수에 할당될 때, 값이 복사되어 영향을 미치지 않습니다. 37 | 38 | - Thread간 의도치 않은 공유로 부터 안전합니다. 39 | 40 | - '값'에 의해 구분되므로 'Value'가 중요합니다. `Equatable`을 이용하여 동치를 나타낼 수 있습니다. 41 | 42 | ```swift 43 | protocol Equatable { 44 | func ==(lhs: Self, rhs: Self) -> Bool 45 | } 46 | ``` 47 | 48 | 49 | 50 | ## Considerations for Performance 51 | 52 | ### Memory Allocation 53 | 54 | **👍Stack** or **👎Heap** 55 | 56 | - Heap 할당 시, thread safe해야 하므로 lock 등의 동기화 동작으로 인해 성능을 저하시킵니다. 57 | - Dictionary의 key가 Value type으로 stack에서만 메모리를 할당해서 Heap 할당 오버헤드가 없습니다. 58 | 59 | ### Reference Counting 60 | 61 | **👍No** or 👎**Yes** 62 | 63 | - 변수 복사 시 마다 실행되는 등 매우 빈번하게 실행됩니다. 64 | - Thread safe를 위하여 카운트를 atomic하게 증감해야합니다. 65 | 66 | ### Method dispatch 67 | 68 | **👍Static** or 👎**Dynamic** 69 | 70 | > static : method를 compile time에 호출하는가? 71 | > 72 | > dynamic : method를 run time에 호출하는가? 73 | > 74 | > 컴파일 타임 - 링크 타임 - 런타임 75 | 76 | - **static** 77 | 78 | - 컴파일 타임에 메서드 실제 코드 위치를 안다면 실행 중 찾는 과정 없이 그 주소로 점프 가능합니다. 79 | - Method Inlining이 가능합니다. 80 | 81 | - **dynamic** 82 | 83 | - 컴파일 타임에 알 수 없고, 런타임에만 파악 가능합니다. 84 | - 컴파일 최적화를 못하므로 속도 저하의 요소이빈다. 85 | 86 | - ##### static dispatch로 강제하기 87 | 88 | - `final`, `private`을 사용하여 상속성을 끊습니다. 89 | - `dynamic` 키워드의 사용을 줄입니다. 90 | - objc 연동을 최소화합니다. 91 | - WMO(Whole Module Optimization)을 사용합니다. 92 | 93 | ### Swift Abstract Techniques and Performance 94 | 95 | - `class` 96 | 97 | - Reference Semantics가 필요할 때 사용합니다. 98 | - `final class `는 static Mehod Dispatch입니다. 99 | 100 | - `struct` 101 | 102 | - Reference 타입이 있는 `struct`는 성능이 저하될 수 있습니다. 103 | 104 | - `protocol` 105 | 106 | - Value semantric에서도 적용 가능합니다. 107 | 108 | - 값의 관리는 Existentail Container로 이루어지게 되는데, 109 | 110 | > Existentail Container : Protocol type의 실제 값을 넣고 관리하는 구조 111 | 112 | - 3 words 이하 : 새로운 Existential container에 전체가 복사됩니다. 113 | - 3 words 이상 : 새로운 Existential container를 생성하고, 값 전체가 새로운 Heap 할당 후 복사됩니다.(값들은 Heap에, 해당 reference는 Existential container에 저장됩니다.) 114 | 115 | ## Reference 116 | 117 | - https://academy.realm.io/kr/posts/letswift-swift-performance/ -------------------------------------------------------------------------------- /Clean_Code/객체와_자료_구조.md.md: -------------------------------------------------------------------------------- 1 | # Chapter 6. 객체와 자료구조 2 | 3 | ## Table of Contents 4 | 5 | - [자료 추상화](#자료-추상화) 6 | - [자료 객체 비대칭](#자료-객체-비대칭) 7 | - [자료구조](#자료구조) 8 | - [객체](#객체) 9 | - [절차적인 코드](#절차적인-코드) 10 | - [객체 지향 코드](#객체-지향-코드) 11 | - [디미터 법칙](#디미터-법칙) 12 | - [기차 충돌](#기차 충돌) 13 | - [자료 전달 객체](#자료-전달-객체) 14 | 15 | ## 자료 추상화 16 | 17 | - **구현을 감추려면 추상화가 필요**하다. 18 | 19 | - 변수 사이에 함수라는 계층을 넣는다고 구현이 저절로 감추어지진 않는다. 20 | - *인터페이스를 제공하여 사용자가 구현을 모른 채 자료의 핵심을 조작할 수 있게 하는 것이 클래스*다. 21 | 22 | ```swift 23 | public protocol Point { 24 | func setCartesian(x: Double, y: Double) 25 | func radius() -> Double 26 | func theta() -> Double 27 | func setPolar(r: Double, theta: Double) 28 | } 29 | ``` 30 | 31 | - **자료를** 세세하게 공개하기보다는 **추상적으로 표현**하는 편이 좋다. 32 | 33 | ```swift 34 | public protocol Vehicle { 35 | //😐 구체적인 숫자 36 | func gallonsOfGasoline() 37 | //👍 추상적인 개념(백분율) 38 | func percentOfFuelRemaining() 39 | } 40 | ``` 41 | 42 | ## 자료 객체 비대칭 43 | 44 | ### 자료구조 45 | 46 | - 자료를 그대로 공개한다. 47 | - 별다른 함수는 제공하지 않는다. 48 | 49 | ### 객체 50 | 51 | - 자료를 추상화 뒤에 숨겨 놓는다. 52 | - 자료를 다루는 함수만 공개한다. 53 | 54 | ### 절차적인 코드 55 | 56 | - 자료 구조를 변경하지 않으면서 새 함수를 추가하기 쉽다. 57 | - 새로운 자료 구조를 추가하기 어렵다. 58 | 59 | ```swift 60 | //자료 구조 61 | //절차적인 클래스 62 | //자료를 그대로 공개하지만 별다른 함수를 제공하지는 않는다. 63 | //아무 메서드도 제공하지 않는다. 64 | class Square { 65 | var topLeft: CGPoint 66 | var side: Double 67 | init(topLeft: CGPoint, side: Double) { 68 | self.topLeft = topLeft 69 | self.side = side 70 | } 71 | } 72 | 73 | //객체 74 | //자료를 다루는 함수를 공개한다. 75 | //함수를 추가하여도 도형 클래스는 영향을 받지 않는다. 76 | //새 도형을 추가하려면 함수를 모두 고쳐야한다. 77 | class Geometry { 78 | let pi = Double.pi 79 | 80 | func area(shape: AnyObject) throws -> Double { 81 | switch shape { 82 | case is Square: 83 | guard let sqaure = shape as? Square else { 84 | fatalError() 85 | } 86 | return sqaure.side * sqaure.side 87 | default: break 88 | } 89 | fatalError() 90 | } 91 | } 92 | ``` 93 | 94 | ### 객체 지향 코드 95 | 96 | - 기존 함수를 변경하지 않으면서 새 클래스를 추가하기 쉽다. 97 | - 새로운 함수를 추가하기 어렵다. 98 | 99 | ```swift 100 | //객체 지향적인 클래스 101 | //Geometry Class는 필요하지 않다. 102 | //새로운 도형을 추가하여도 기존 함수에 아무런 영향을 미치지 않는다. 103 | protocol Shape { 104 | func area() -> Double 105 | } 106 | class Square: Shape { 107 | var topLeft: CGPoint 108 | var side: Double 109 | init(topLeft: CGPoint, side: Double) { 110 | self.topLeft = topLeft 111 | self.side = side 112 | } 113 | func area() -> Double { 114 | return side*side 115 | } 116 | } 117 | ``` 118 | 119 | ## 디미터 법칙 120 | 121 | > #### "모듈은 자신이 조작하는 객체의 속사정을 몰라야 한다." 122 | 123 | - 객체는 자료를 추상화 뒤에 숨기고, 자료를 다루는 함수만 공개한다. 124 | - 클래스 C의 메서드 f는 아래의 메서드만 호출해야한다. 125 | - 클래스 C의 메서드 126 | - f가 생성한 객체의 메서드 127 | - f의 인수로 넘어온 객체의 메서드 128 | - C인스터스 변수에 저장된 객체의 메서드 129 | 130 | ### 기차 충돌 131 | 132 | - 여러 객차가 한줄로 이어진 기차처럼 보인다. 133 | 134 | ```swift 135 | let outputDirection = context.option().searchDirection().absolutePath() 136 | ``` 137 | 138 | ```swift 139 | //함수 하나가 아는 지식이 굉장히 많다. 140 | let option = context.option() 141 | let scratchDirection = option.scratchDirection() 142 | let outputDirection = scratchDirection.absolutePath() 143 | //context, option, searchdirection이 144 | //객체라면 내부 구조를 숨겨야 하므로 디미터 법칙을 위반한다. 145 | //자료구조라면 내부 구조를 노출하므로 디미터 법칙이 적용되지 않는다. 146 | 147 | //디미터 법칙을 거론할 필요가 없어진다. 148 | let outputDirection = context.option.scratchDirection.absolutePath 149 | ``` 150 | 151 | ## 자료 전달 객체 152 | 153 | - DTO(Data Transfer Object) : 자료 전달 객체 154 | - 공개변수만 있고 함수가 없는 클래스 -------------------------------------------------------------------------------- /Clean_Code/함수.md: -------------------------------------------------------------------------------- 1 | # Chapter 3. 함수 2 | 3 | ## Table of Contents 4 | 5 | - [작게 만들어라](#작게-만들어라) 6 | - [한 가지만 해라](#한-가지만-해라) 7 | - [함수당 추상화 수준은 하나로](#함수당-추상화-수준은-하나로) 8 | - [위에서 아래로 코드 읽기](#위에서-아래로-코드-읽기) 9 | - [Switch 문](#Switch-문) 10 | - [서술적인 이름을 사용하라](#서술적인-이름을-사용하라) 11 | - [함수 Parameter](#함수-Parameter) 12 | - [단항형식](#단항형식) 13 | - [플래그 Parameter](#플래그-Parameter) 14 | - [이항 함수](#이항-함수) 15 | - [삼항 함수](#삼항-함수) 16 | - [Side Effect를 일으키지 마라](#Side-Effect를-일으키지-마라) 17 | - [출력 Parameter](#출력-Parameter) 18 | - [명령과 조회를 분리하라](#명령과-조회를-분리하라) 19 | - [오류 코드보다 예외를 사용하라](#오류-코드보다-예외를-사용하라) 20 | - [Try/Catch 블록 뽑아내기](#Try/Catch-블록-뽑아내기) 21 | - [반복하지 마라](#반복하지-마라) 22 | - [함수를 짜는 방법](#함수를-짜는-방법) 23 | 24 | ## 작게 만들어라 25 | 26 | - **함수를 작게 만들어라**. 27 | - 그리고 함수를 더 작게 만들어라. 28 | - `if`, `else`, `while` 등에 들어가는 불록은 한 줄이어야한다. 29 | 30 | ## 한 가지만 해라 31 | 32 | - **함수는 한가지만을 해야 한다. 그 한가지를 잘 해야 한다. 그 한가지만을 해야한다.** 33 | - 함수내에서 추상화 수준이 하나인 단계만 수행한다면 그 함수는 한 가지 작업만 한다. 34 | 35 | ## 함수당 추상화 수준은 하나로 36 | 37 | - 함수가 한 가지 작업만 하려면 함수 내 **모든 문장의 추상화 수준이 동일**해야한다. 38 | - 추상화의 수준이 섞여 있으면 안된다. 39 | 40 | ### 위에서 아래로 코드 읽기 41 | 42 | - **코드는 위에서 아래로 이야기 처럼 읽혀야 좋다**. 43 | - **위에서 아래로 추상화 단계가 한 단계씩 낮아진다**. 44 | 45 | ## Switch 문 46 | 47 | - 다형성을 이용하여 switch를 저차원 클래스에 숨기고 절대로 반복하지 않는다. 48 | 49 | > 추상화란 무엇인가? 50 | 51 | ## 서술적인 이름을 사용하라 52 | 53 | - 함수가 작고 단순할수록 서술적인 이름을 고르기도 쉬워진다. 54 | - 단, 이름을 붙일 때는 일관성이 있어야 한다. 55 | 56 | ## 함수 Parameter 57 | 58 | - 이상적인 parameter의 개수는 0개 이다. 59 | - 4개 이상은 특별한 이유가 있어도 사용하면 안된다. 60 | 61 | ### 단항형식 62 | 63 | - Parameter에 질문을 던지는 경우 64 | - e.g. `fileExists("Myfile")` 65 | - Parameter를 무언가로 변환해 결과를 반환하는 경우 66 | - 이벤트 함수 67 | - 이벤트 함수는 input parameter만 존재한다. ouput은 존재하지 않는다. 68 | - Parameter로 상태를 바꾼다. 69 | - 이벤트라는 사실이 코드에 명확이 드러나야한다. 70 | - 함수와 Parameter가 쌍을 이루어야한다. 71 | 72 | ### 플래그 Parameter 73 | 74 | - 함수로 bool 값을 넘기는 것은 함수에서 한 가지 일을 처리하는 규칙에 어긋난다. 75 | - 사용을 지양해야한다. 76 | 77 | ### 이항 함수 78 | 79 | - 자연적 순서가 존재하는 경우 사용한다. 80 | - e.g. `new Point(0,0)` 81 | - 불가피한 경우가 존재하겠지만, 단항함수로 바꾸도록 노력해라. 82 | 83 | ### 삼항 함수 84 | 85 | - Parameter가 2개인 함수보다 이해하기 어렵다. 86 | 87 | - 객체를 생성하여 인수를 줄이는 방법도 좋다. 88 | - 객체를 생성함으로써 개념을 표현하게 된다. 89 | 90 | ```swift 91 | ❌ makeCircle(x: Double, y: Double, radius: Double); 92 | ✅ makeCircle(center: CGPoint, radius: Double); 93 | ``` 94 | 95 | ## Side Effect를 일으키지 마라 96 | 97 | - Side Effect는 함수에서 한가지 일을 처리하는 규칙에 어긋난다. 98 | - Temporal coupling이나 Order dependency를 초래한다. 99 | 100 | > Temporal coupling ? 101 | > 102 | > Order dependency ? 103 | 104 | ### 출력 Parameter 105 | 106 | - 출력 Parameter의 사용을 지양한다. 107 | 108 | ```swift 109 | ❌ func appendFooter(report: String) 110 | ✅ report.appendFooter() 111 | ``` 112 | 113 | - 함수에서 상태를 변경해야 한다면 함수가 속한 객체 상태를 변경하는 방식을 택한다. 114 | 115 | ## 명령과 조회를 분리하라 116 | 117 | - 함수는 뭔가를 수행하거나 뭔가에 답하거나 둘 중 하나만 해야 한다. 118 | 119 | ```java 120 | ❌ 명령과 조회가 같이 있는 경우 121 | public boolean set(String attribute, String value); 122 | if (set("username", "unclebob"))... //의미가 명확하지 않다. 123 | 124 | 125 | ✅ 명령과 조회가 분리된 경우 126 | if (attributeExistsC'username")) { //조회 127 | setAttribute("username", "unclebob"); //명령 128 | ... 129 | } 130 | ``` 131 | 132 | ## 오류 코드보다 예외를 사용하라 133 | 134 | - 명령/조회 분리 규칙을 미묘하게 위반한다. 135 | - 오류 코드의 사용은 여러 단계로 중첩되는 코드를 야기한다. 136 | 137 | ```java 138 | try { 139 | deletePage(page); 140 | registry.deleteReference(page.name); 141 | configKeys.deleteKey(page.name.makeKey()); 142 | } catch (Exception e) { 143 | logger.log(e.getMessage()); 144 | } 145 | ``` 146 | 147 | ### Try/Catch 블록 뽑아내기 148 | 149 | - Try/Catch는 정상동작과 오류 처리 동작을 뒤섞는다. 150 | 151 | ```java 152 | public void delete(Page page) { 153 | try { 154 | deletePageAndAllReferences(page); 155 | } catch (Exception e) { 156 | logError(e); 157 | } 158 | } 159 | ``` 160 | 161 | ## 반복하지 마라 162 | 163 | - **중복되는 코드가 존재하면, 오류 발생 확률이 높아진다**. 164 | 165 | ## 함수를 짜는 방법 166 | 167 | - 처음부터 함수를 잘 짜낼 수 없다. 168 | - 리펙토링을 통하여 코드를 다듬고, 함수를 만들고, 이름을 바꾸고, 중복을 제거하며 메서드를 줄이고 순서를 바꾼다. 169 | 170 | -------------------------------------------------------------------------------- /Architecture/CleanArchitecture.md: -------------------------------------------------------------------------------- 1 | # Clean Architecture 2 | 3 | - [특징](#특징) 4 | - [Layer](#Layer) 5 | - [Dependency Rule](#Dependency-Rule) 6 | - [Entity](#Entity) 7 | - [UseCase](#UseCase) 8 | - [Interface Adapter](#Interface-Adapter) 9 | - [Framework and Driver](#Framework-and-Driver) 10 | 11 | ### "Architecture is about intent" 12 | 13 | > "아키텍쳐는 의도를 표현하는 것입니다." 14 | 15 | - 최상위 디렉토리 구조를 통하여 "Application이 무엇을 하는 가"를 알 수 있어야합니다. 16 | 17 | - 제대로 된 Architecture는 Application이 무엇을 위한 것이고, 무엇을 하는 것이지 알 수 있어야합니다. 18 | 19 | > ~~"이것은 iOS Application이다"~~를 뜻하는 것이 아닌, 20 | > 21 | > "이것은 Todo list를 위한 Application이며, Todo에 대한 CRUD를 할 수 있다."를 알아야합니다. 22 | 23 | e.g. 청사진을 보고, "이것은 도서관이다" 혹은 "이것은 교회이다."를 알 수 있습니다. 24 | 25 | - 상속은 매우 강한 관계입니다. Interface를 이용하여 수평확장을 합니다. 26 | 27 | ![CleanDevelopment-12](images/CleanDevelopment-12.jpg) 28 | 29 | ### 특징 30 | 31 | - 프레임워크 독립적 32 | - 테스트 용이함 33 | - 비지니스 규칙은 UI, 데이터베이스, 서버 등 기타 외부 요인 없이 테스트 가능합니다. 34 | - UI 독립적 35 | - 시스템의 나머지 부분을 변경할 필요 없이 UI를 쉽게 변경할 수 있습니다. 36 | - 데이터베이스 독립적 37 | - 어떠한 데이터베이스로도 바꿀 수 있습니다. 38 | - 비지니스 규칙은 데이터베이스에 얽매이지 않습니다. 39 | - 외부 기능 독립적 40 | - 비지니스 규칙은 외부 세계에 대하여 아무것도 모릅니다. 41 | 42 | 43 | ## Layer 44 | 45 | ![The Clean Architecture](https://blog.cleancoder.com/uncle-bob/images/2012-08-13-the-clean-architecture/CleanArchitecture.jpg) 46 | 47 | ### Dependency Rule 48 | 49 | - 바깥 영역으로 갈수록 HighLevel Software가 됩니다. 50 | - 바깥 영역은 Mechanism이고, 내부 영역은 Policy입니다. 51 | - **Dependency는 오직 내부**로만 향합니다. 52 | - 내부의 영역은 바깥 영역의 어떠한 것도 알아서는 안됩니다. 53 | - 바깥 영역의 어떠한 것도 내부 영역에 영향을 주어서는 안됩니다. 54 | - 내부 계층으로 향할수록 추상화의 수준이 상승합니다. 55 | - 내부로 이동해가면서 소프트웨어는 추상화되고 구수준의 정책을 캡슐화 합니다. 56 | 57 | ### Entity 58 | 59 | - Enterprise Business Rule을 캡슐화합니다. 60 | - 규칙이자 정책이며 Application이 없어도 존재할 수 있습니다. 61 | - **Application에 비종속적인 Business Rule**들입니다. 62 | - 단지 하나의 Application을 작성할 뿐이라면, 해당 Application의 Business Object가 됩니다. 63 | 64 | > Business Rule 65 | > 66 | > - 많은 어플리케이션에 적용이 되어야하는 글로벌 비지니스 규칙 (Entity) 67 | > - 특정 어플리케이션에만 적용되는 비지니스 규칙 (UseCase) 68 | 69 | - 가장 일반적이면서 고수준의 규칙을 캡슐화합니다. 70 | - Business Object는 오염되지 않아야합니다. 71 | - 바깥 영역의 것이 변경되더라도 바뀌지 않습니다. 72 | 73 | ### UseCase 74 | 75 | - **Application의 의도는 Use Case를 통하여 드러나게 됩니다**. 76 | - 따라서, **Application의 Architecture는 Use Case**들입니다. 77 | - UseCase 계층은 **Application Specific Business Rule**을 포함하며 시스템의 모든 UseCase를 캡슐화하고 구현합니다. 78 | - Application 없이 존재할 수 없습니다. 79 | - UseCase들은 Entity로 향하는 혹은 Entity로 부터 나오는 데이터 흐름을 조정합니다. 80 | - UseCase 계층의 변경이 보다 내부의 영역인 Entity에 영향을 주지 않아야 하며, 보다 외부의 영역인 Database, UI, Framework의 변경으로부터 영향을 받지 않아야합니다. 81 | - UseCase는 `Interactor`라는 Object로 Object화할 수 있습니다. 82 | - 즉, UseCase와 Interactor는 동일한 것입니다. 83 | 84 | ### Interface Adapter 85 | 86 | - Interface Adapter 계층의 소프트웨어는 UseCase와 Entity에서 사용하기 편한 형식을 외부의 것(e.g. Database, web etc)에서 사용하기 편한 형식으로 변환해주는 adapter의 집합입니다. 87 | 88 | ### Framework and Driver 89 | 90 | - 가장 바깥 계층으로, Database나 Web Framework 등과 같은 툴들 과 프레임워크들로 구성되어져 있습니다. 91 | 92 | - UI, Database, Framework 등은 Detail입니다. 93 | - Database는 저장소일 뿐, Business Rule과는 관련이 없습니다. 94 | 95 | > The database is a detail 96 | 97 | ### Reference 98 | 99 | - [The Clean Architecture - Uncle Bob](https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html) 100 | - [The Clean Architecture - Uncle Bob (한글 번역)](https://blog.coderifleman.com/2017/12/18/the-clean-architecture/?utm_medium=social&utm_source=gaerae.com&utm_campaign=개발자스럽다) 101 | - [Robert C. Martin - Clean Architecture and Design (2014)](https://amara.org/ko/videos/0AtjY87egE3m/url/1216370/?tab=video) 102 | - [The Principles of Clean Architecture by Uncle Bob Martin](https://www.youtube.com/watch?v=o_TH-Y78tt4&t=1113s) 103 | 104 | - [안드로이드에 Clean Architecture 적용하기](https://academy.realm.io/kr/posts/clean-architecture-in-android/) 105 | - [아키텍처와 의존성](https://blog.appkr.dev/learn-n-think/clean-architecture-and-dependency/) 106 | - [Introducing Clean Architecture](https://www.slideshare.net/rocboronat/introducing-clean-architecture-61200981?from_action=save) 107 | - [Architecting Android...The clean way?](https://fernandocejas.com/2014/09/03/architecting-android-the-clean-way/) 108 | -------------------------------------------------------------------------------- /ETC/Layout.md: -------------------------------------------------------------------------------- 1 | # Layout 2 | 3 | ## Table of Contents 4 | 5 | - [`updateConstraints()`](#updateConstraints) 6 | - [`updateConstraintsIfNeeded()`](#updateConstraintsIfNeeded) 7 | - [`setNeedsUpdateConstraints()`](#setNeedsUpdateConstraints) 8 | - [`layoutSubview()`](#layoutSubviews) 9 | - [`layoutIfNeeded()`](#layoutIfNeeded) 10 | - [`setNeedsLayout()`](#setNeedsLayout) 11 | 12 | ## `updateConstraints()` 13 | 14 | - View의 constraints를 업데이트합니다. 15 | 16 | - Constraints의 변화를 최적화하기 위하여 이 메소드를 오버라이드합니다. 17 | 18 | > 영향을 받는 변경이 발생한 직후에 즉각적으로 constraint를 업데이트하는 것이 깔끔하고 쉽습니다. 19 | > 20 | > 예를 들어, 버튼 탭에 대한 응답으로 constraint를 변경하기를 원할 때, 버튼의 action method에서 직접 변경합니다. 21 | > 22 | > Constraints의 변화가 너무 느리거나 View가 중복적인 변화를 생산할 때에만 오버라이드 해야합니다. 23 | 24 | - 변화를 스케쥴하려면, View에서 `setNeesUpdateConstraints()` 를 호출합니다. 25 | 26 | - 시스템은 layout이 발생하기 이전에 `updateConstraints()` 의 구현을 호출합니다. 27 | 28 | - 커스텀 뷰의 속성이 변경되지 않은 경우, 콘텐츠에 필요한 모든 constraints가 적용되는지 확인할 수 있습니다. 29 | 30 | - 구현은 가능한한 효율적이여야합니다. 31 | 32 | - Do not deactivate all your constraints, then reactivate the ones you need. 33 | 34 | > 모든 제약 조건을 비활성화하지 말고 필요한 제약 조건을 다시 활성화합니다…?? 35 | 36 | - Constraints를 추적하고 각 업데이트가 진행되는 동안 유효성을 검사 할 수있는 방법이 있어야합니다. 37 | 38 | - **변경해야 하는 항목만 변경합니다.** 39 | 40 | - 업데이트가 진행되는 동안 앱의 현재 상태에 대해 적절한 constraints를 반드시 갖추어야 합니다. 41 | 42 | - **`setNeedsUpdateConstraints()` 를 구현의 내부에 호출하면 안됩니다.** 43 | 44 | - `setNeedsUpdateConstraints()`를 호출하면 또 다른 업데이트 단계가 스케쥴되어 피드백 루프가 생성됩니다. 45 | 46 | - **`super.updateConstraints()` 를 구현의 마지막에 호출합니다.** 47 | 48 | ## `updateConstraintsIfNeeded()` 49 | 50 | - **수신 View 및 해당 SubView에 대한 constraints를 업데이트합**니다. 51 | - 새로운 Layout 단계가 뷰에 대해 발생될 때마다, 시스템은 view와 subview의 constraints이 현재 view 계층 구조 및 해당 constraints로 업데이트되도록 이 메소드를 호출합니다. 52 | - 이 메소드는 시스템에 의해 자동으로 호출되지만 가장 최신의 constraints를 검사해야하는 경우 수동으로 호출 할 수 있습니다. 53 | - **Subclass는 이 메소드를 오버라이드하면 안됩니다.** 54 | 55 | ## `setNeedsUpdateConstraints()` 56 | 57 | - **View의 constraints를 업데이트해야하는 가를 컨트롤**합니다. 58 | - Constraints에 영향을 미치는 식으로 커스텀 View의 속성이 변경이 된다면, 이 메소드를 호출하여 **미래의 어떤 시점에서 constraints를 업데이트해야 함**을 나타낼 수 있습니다. 59 | - 시스템은 `updateConstriants()` 를 정상적인 레이아웃 단계의 일부로 호출할 것입니다. 60 | - **Constraint의 변화들을 일괄처리하기 위한 최적화 도구로 사용**합니다. 61 | - Constraints가 필요하기 직전에 모든 constraints를 한번에 업데이트하면, Layout 단계간 View를 여러번 변경할 때 불필요하게 constraints를 재계산하지 않습니다. 62 | 63 | ## `layoutSubviews()` 64 | 65 | - Subclass들은 그들의 subview에 대한 정교한 레이아웃을 실행하기 위하여 이 메소드를 필요에 따라 오버라이드할 수 있습니다. 66 | - Subview의 Autoresigin과 Constraint-based 행위가 원하는대로 동작하지 않을 경우 이 메소드를 오버라이드해야합니다. 67 | - 구현을 통하여 **subview의 frame 사각형을 직접적으로 설정**할 수 있습니다. 68 | - **이 메소드를 직접적으로 호출하면 안됩니다.** 69 | - Layout을 강제적으로 업데이트하고 싶다면, 다음 드로잉 업데이트 이전에 `setNeedsLayout()` 을 호출하면 됩니다. 70 | - View들의 Layout을 즉각적으로 업데이트하고 싶다면, `layoutIfNeeded()` 를 호출하면 됩니다. 71 | - 이 메소드가 호출 될때 `updateConstraintsIfNeed()` 또한 호출됩니다. 72 | 73 | ## `layoutIfNeeded()` 74 | 75 | - **View의 Layout을 즉각적으로 업데이트를 강압**합니다. 76 | - Auto Layout을 사용 중일 때, Layout Engine은 constraints의 변화를 충족시키기 위해 필요에 따라 view의 위치를 업데이트합니다. 77 | - Root View로부터 메세지를 받는 View를 이용하여, Root부터 시작하여 View의 Subtree들을 배치합니다. 78 | - 만약 Layout 업데이트가 팬딩상태인 것이 없을 경우, layout을 변경하거나 layout 관련 callback을 호출하지 않고 종료합니다. 79 | - **synchronous합니다.** 80 | 81 | ## `setNeedsLayout()` 82 | 83 | - Receiver의 현재 Layout을 무효화하고 다음 업데이트 사이클 동안 Layout 업데이트를 발생시킵니다. 84 | - View의 subview들에 대한 레이아웃을 조정하려면 Main thread에서 이 메소드를 호출하면 됩니다. 85 | - 이 메소드는 요청을 기록하고 즉각적으로 반환합니다. 86 | - **즉각적인 업데이트를 강압하지 않고 대신 다음 업데이트 사이클을 기다립니다.** 87 | - 그 때문에, View들을 업데이트하기 이전에, 여러 View들의 Layout을 무효화할 수 있습니다. 88 | - 이 동작을 통해 모든 Layout 업데이트를 하나의 업데이트 주기로 통합 할 수 있습니다. 89 | - 일반적으로 성능향상에 도움이 됩니다. 90 | 91 | ## Reference 92 | 93 | - https://developer.apple.com/documentation/uikit/uiview/1622482-layoutsubviews 94 | - https://developer.apple.com/documentation/uikit/uiview/1622507-layoutifneeded 95 | - https://developer.apple.com/documentation/uikit/uiview/1622601-setneedslayout 96 | - https://developer.apple.com/documentation/uikit/uiview/1622512-updateconstraints 97 | - https://developer.apple.com/documentation/uikit/uiview/1622595-updateconstraintsifneeded 98 | - https://developer.apple.com/documentation/uikit/uiview/1622450-setneedsupdateconstraints 99 | - https://stackoverflow.com/questions/20609206/setneedslayout-vs-setneedsupdateconstraints-and-layoutifneeded-vs-updateconstra 100 | - https://www.objc.io/issues/3-views/advanced-auto-layout-toolbox/ -------------------------------------------------------------------------------- /Dependency_Manager/Carthage.md: -------------------------------------------------------------------------------- 1 | # Carthage 2 | 3 | ## Overview 4 | 5 | Carthage란 무엇인가, Cocoapod과의 차이, Carthage를 적용하는 방법에 대하여 기술하였습니다. 6 | 7 | ## Table of Contents 8 | 9 | - [Dependency Manager](#Dependency-Manager) 10 | - [Cocoapods](#Cocoapods) 11 | - [Carthage](#Carthage) 12 | - [Apply Carthage(feat. RxSwift)](#Apply-Carthage(feat.-RxSwift)) 13 | - [.gitignore](#.gitignore) 14 | - [Refrence](#Reference) 15 | 16 | # Dependency Manager 17 | 18 | https://stackoverflow.com/questions/27285783/package-manager-vs-dependency-manager 을 참고한다면, 19 | 20 | **Dependency Manager**는 하나의 프로젝트에 국한된 것으로, 다른 프로젝트에서 사용을 위해서는 다시 설정을 해줘야합니다. 21 | 22 | **Package Manager**는 시스템을 설정하는 것으로, 개발 환경을 설정해 놓으며 많은 프로젝트에 대하여 적용이 가능합니다. 23 | 24 | 위 Manager들은 25 | 26 | - Third Party를 가져와서 프로젝트에 통합하는 처리를 단순화, 표준화 합니다. 27 | 28 | > 사용하지 않는다면, 모든 동작을 수동으로 처리해야합니다. 29 | 30 | - 나중에 Third Party 라이브러리를 쉽게 업데이트 할수 있습니다. 31 | 32 | > 사용하지 않는다면, 라이브러리가 업데이트될 때마다 하나씩 수동으로 업데이트해야합니다. 33 | 34 | - 만약 Third Party 내에서 의존성이 존재할 경우, 적절히 호환 가능한 버전을 골라냅니다. 35 | 36 | > 사용하지 않는다면, 각 라이브러리마다 호환성을 따지며 추가해줘야합니다. 37 | 38 | ![Dependency Graph](https://koenig-media.raywenderlich.com/uploads/2015/06/Screen-Shot-2015-06-28-at-18.18.31.png) 39 | 40 | # CocoaPods 41 | 42 | https://github.com/CocoaPods/CocoaPods 43 | 44 | - 설치와 사용이 쉽습니다. 45 | - `pods` 를 사용하기 위해서는 `.xcworkspace` 파일을 생성하고, 사용해야합니다. 46 | - podspecs repositories는 중앙집중적이여서, 사라지거나 접근할 수 없을 때 문제가 발생할 수 있습니다. 47 | - 대부분의 라이브러리들이 지원을 하고 있습니다. 48 | 49 | # Carthage 50 | 51 | https://github.com/Carthage/Carthage 52 | 53 | - 초반 설정에 조금의 귀찮은 작업이 존재합니다. 54 | - workspace 사용에 대하여 강제성이 없습니다. 55 | - 반중앙집중적이며, Xcode Project에 의존성이 있습니다. 56 | - Carthage를 지원하지않는 라이브러리가 종종 존재합니다. :'( 57 | 58 | ## Apply Carthage(feat. RxSwift) 59 | 60 | #### 1. [Installing Carthage](https://github.com/Carthage/Carthage#installing-carthage) 61 | 62 | Carthage를 설치하는 여러 방법이 존재합니다. 이 게시물은 **Homebrew**를 이용한 설치에 대한 내용입니다. 63 | 64 | Terminal에서 아래와 같은 코드를 넣으면 carthage의 설치가 끝납니다. 65 | 66 | ``` 67 | $ brew update 68 | $ brew install carthage 69 | ``` 70 | 71 | 하지만, 저의 경우 아래와 같은 에러가 나왔습니다. 72 | 73 | ``` 74 | An unexpected error occurred during the `brew link` step 75 | The formula built, but is not symlinked into /usr/local 76 | Permission denied @ dir_s_mkdir - /usr/local/share/fish/vendor_completions.d 77 | Error: Permission denied @ dir_s_mkdir - /usr/local/share/fish/vendor_completions.d 78 | ``` 79 | 80 | 위 에러에 대한 해결방법입니다. (참고 : https://stackoverflow.com/questions/47513024/how-to-fix-permissions-on-home-brew-on-macos-high-sierra) 81 | 82 | ``` 83 | $ sudo mkdir /usr/local/Frameworks 84 | $ sudo chown $(whoami):admin /usr/local/Frameworks 85 | $ brew link carthage 86 | ``` 87 | 88 | #### 2. [Adding frameworks to an application fo iOS, tvOS, or watchOS](https://github.com/Carthage/Carthage#if-youre-building-for-ios-tvos-or-watchos) 89 | 90 | 1. Cartfile 생성 91 | 92 | 적용하고자 하는 프로젝트의 root로 이동한 후, Cartfile을 생성해줍니다. 93 | 94 | ``` 95 | $ touch Cartfile 96 | ``` 97 | 98 | 2. Cartfile 수정 99 | 100 | Cartfile을 Xcode에서 열어줍니다. 101 | 102 | ``` 103 | $ open -a Xcode Cartfile 104 | ``` 105 | 106 | 사용하고자 하는 Carthage를 Cartfile에 추가합니다. 107 | 108 | ``` 109 | //Cartfile 110 | github "ReactiveX/RxSwift" ~> 4.0 111 | ``` 112 | 113 | 3. `carthage update` 를 실행하면, 종속성을 가져 와서 각 폴더를 작성하거나 사전 컴파일된 프레임 워크를 다운로드합니다. `carthage update --platform ios` 를 이용하여 iOS만 빌드할 수 있습니다. 114 | 115 | 4. Carthage/Build 폴더에서 framework를 "Linked Frameworks and Libraries"에 추가해줍니다. 116 | ![](images/1.LinkedFrameworksAndLibraries.png) 117 | 118 | 5. **Run Script Phase** 를 수정합니다. 119 | 120 | ``` 121 | /usr/local/bin/carthage copy-frameworks 122 | 123 | $(SRCROOT)/Carthage/Build/iOS/.framework 124 | ``` 125 | 126 | ![](images/2.RunScriptPhase.png) 127 | 128 | 6. **Unit test or a framework** 129 | 130 | ![](images/3.UnitTestOrFramework.png) 131 | 132 | ![](images/4.UnitTestOrFramework2.png) 133 | 134 | ## .gitignore 135 | 136 | ``` 137 | # Created by https://www.gitignore.io/api/xcode,carthage,cocoapods,swiftpackagemanager 138 | # Edit at https://www.gitignore.io/?templates=xcode,carthage,cocoapods,swiftpackagemanager 139 | 140 | ### Carthage ### 141 | # Carthage 142 | # 143 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 144 | # Carthage/Checkouts 145 | 146 | Carthage/Build 147 | Carthage/Checkouts/ 148 | ``` 149 | 150 | ## Reference 151 | 152 | - https://medium.com/xcblog/swift-dependency-management-for-ios-3bcfc4771ec0 153 | 154 | - https://www.raywenderlich.com/416-carthage-tutorial-getting-started 155 | - https://www.mokacoding.com/blog/setting-up-testing-libraries-with-carthage-xcode7/ -------------------------------------------------------------------------------- /Style/Style_Guide.md: -------------------------------------------------------------------------------- 1 | # Style Guide 2 | 3 | ## Overview 4 | 5 | 이전에도 한번 정독하였지만, 시간이 지나니 잊혀지게 되어 점점 제멋대로 코딩을 하고 있는 것 같은 기분이 들었습니다. 복습도 하고 정리도 할 겸, Style Guide에 나온 것과 다르게 종종 놓치는 부분과 다시 한번 상기해봐야할 것들에 대해서 정리하였습니다. 6 | 7 | [Ray Wenderlich's Swift Style Guide](https://github.com/raywenderlich/swift-style-guide#access-control) 와 [Github's Swift Style Guide](https://github.com/github/swift-style-guide) 를 읽고 작성하였습니다. 8 | 9 | ## Table of Contents 10 | 11 | - [Delegates](#Delegates) 12 | - [Inferred Context and Type Inference](#Inferred-Context-and-Type-Inference) 13 | - [User of Self](#User-of-Self) 14 | - [Final](#Final) 15 | - [Access Control](#Access-Control) 16 | - [Function Declarations](#Function-Declarations) 17 | - [Golden Path](#Golden-Path) 18 | - [Reference](#Reference) 19 | 20 | ## Delegates 21 | 22 | 커스텀 delegate method를 만들 때, **이름이 없는 첫 parameter는 delegate source**여야합니다. 23 | 24 | ```swift 25 | ❌ func didSelectName(namePicker: NamePickerViewController, name: String) 26 | ✅ func namePickerView(_ namePickerView: NamePickerView, didSelectName name: String) 27 | 28 | ❌ func namePickerShouldReload() -> Bool 29 | ✅ func namePickerViewShouldReload(_ namePickerView: NamePickerView) -> Bool 30 | ``` 31 | 32 | ## Inferred Context and Type Inference 33 | 34 | 짧고 명확하게 코드를 작성하기 위하여 Compiler에서 추론된 context를 사용합니다. 35 | 36 | ```swift 37 | ❌ let selector = #selector(ViewController.viewDidLoad) 38 | ✅ let selector = #selector(viewDidLoad) 39 | 40 | ❌ view.backgroundColor = UIColor.red 41 | ✅ view.backgroundColor = .red 42 | 43 | ❌ let message: String = "Click the button" 44 | ✅ let message = "Click the button" 45 | 46 | ❌ let currentBounds: CGRect = computeViewBounds() 47 | ✅ let currentBounds = computeViewBounds() 48 | ``` 49 | 50 | ## User of Self 51 | 52 | Swift는 Object의 properties나 method를 호출할 필요가 없으므로 `self`의 사용을 피합니다. 53 | 54 | 단, Compiler가 요구한 경우에는 사용합니다. 55 | 56 | > 개인적으로 지키지 않았던 내용입니다. 57 | > 58 | > closure 내에서 아래와 같은 구문을 사용해왔었는데, 일종의 compiler 버그라고 합니다. 59 | > 60 | > https://github.com/apple/swift-evolution/blob/master/proposals/0079-upgrade-self-from-weak-to-strong.md#relying-on-a-compiler-bug 61 | > 62 | > ```swift 63 | > ❌ 64 | > guard let `self` = self else { 65 | > return 66 | > } 67 | > 68 | > ✅ 69 | > guard let self = self else { 70 | > return 71 | > } 72 | > ``` 73 | 74 | ## Final 75 | 76 | `final` 키워드를 사용하여 method, property, 또는 subscript에 대한 override를 compile time에 error를 보내줘 막을 수 있습니다. (참고 : [The Swift Programming Language - Inheritance](https://docs.swift.org/swift-book/LanguageGuide/Inheritance.html)) 77 | 78 | `final` 키워드를 class나 members에 사용하는 것은 주요 주제로 부터 산만하게 하게 할 수 있고, 필요하지 않을 수도 있습니다. 그럼에도 불구하고 `final`은 가끔 의도를 명확히 할 수 있으며 그럴만한 가치가 있습니다. 79 | 80 | ## Access Control 81 | 82 | 모든 access control은 주요 주제로 부터 산만하게 하게 할 수 있고, 필요하지 않을 수도 있습니다. `private` 과 `fileprivate` 을 적절하게 사용한다면 명확성을 추가하고 encapsulation을 촉진할 수 있습니다. 83 | 84 | > Using `private` and `fileprivate` appropriately, however, adds clarity and promotes encapsulation. 음.. promote의 뜻이 애매합니다. 85 | 86 | access control을 선행 property specifier로 지정합니다. `@IBAction`, `@IBOutlet`, `@discardableResult` 등과 같은 속성이나 `static` 만이 access control 앞에 올 수 있습니다. 87 | 88 | ```swift 89 | 🤔 fileprivate let message = "Great Scott!" 90 | ✅ private let message = "Great Scott!" 91 | 92 | class TimeMachine { 93 | ❌ lazy dynamic private var fluxCapacitor = FluxCapacitor() 94 | ✅ private dynamic lazy var fluxCapacitor = FluxCapacitor() 95 | } 96 | ``` 97 | 98 | ## Function Declarations 99 | 100 | 긴 함수의 경우, 각 parameter를 작성할 때 새로운 줄과 들여쓰기를 합니다. 101 | 102 | ```swift 103 | func reticulateSplines( 104 | spline: [Double], 105 | adjustmentFactor: Double, 106 | translateConstant: Int, comment: String 107 | ) -> Bool { 108 | // reticulate code goes here 109 | } 110 | ``` 111 | 112 | `(Void)`를 사용하지 않습니다. 또한 closure와 function의 output에 대해서는 `()` 대신 `Void`를 사용합니다. 113 | 114 | ```swift 115 | ❌ func updateConstraints() -> () {} 116 | ✅ func updateConstraints() -> Void {} 117 | 118 | 119 | ❌ typealias CompletionHandler = (result) -> () 120 | ✅ typealias CompletionHandler = (result) -> Void 121 | ``` 122 | 123 | ## Golden Path 124 | 125 | 조건문에 대하여 코딩할 때, 좌측 여백의 코드는 "golden" 또는 "happy" path여야합니다. 126 | 127 | 즉, `if` 구문으로 감싸지말고, `guard` 구문을 활용하면 됩니다. 128 | 129 | ```swift 130 | func computeFFT(context: Context?, inputData: InputData?) throws -> Frequencies { 131 | 132 | guard let context = context else { 133 | throw FFTError.noContext 134 | } 135 | guard let inputData = inputData else { 136 | throw FFTError.noInputData 137 | } 138 | 139 | // use context and input to compute the frequencies 140 | return frequencies 141 | } 142 | ``` 143 | 144 | ## Reference 145 | 146 | - https://github.com/raywenderlich/swift-style-guide 147 | - https://github.com/github/swift-style-guide 148 | - https://github.com/minsOne/swift-style-guide/blob/master/README_KR.md -------------------------------------------------------------------------------- /Concurrency/GrandCentralDispatch.md: -------------------------------------------------------------------------------- 1 | # Global Central Dispatch 2 | 3 | ## Table of Contents 4 | 5 | - [Concurrency](#Concurrency) 6 | 7 | - [Dispatch Queue](#Dispatch-Queue) 8 | 9 | - [Serial Queue](#Serial-Queue) 10 | - [Concurrent Queue](#Concurrent-Queue) 11 | 12 | - [Main Dispatch Queue](#Main-Dispatch-Queue) 13 | - [Global Dispatch Queue](#Global-Dispatch-Queue) 14 | - [Quality of Service](#Quality-of-Service) 15 | 16 | ## Parallelism and Concurrency 17 | 18 | - Concurrency는 structure에 관한 것인 반면 parallelism은 19 | 20 | ### Parallelism 21 | 22 | - 멀티 코어 디바이스에서 사용합니다. 23 | - 여러개의 thread를 "동시"에 수행합니다. 24 | - 반면, 멀티 코어 디바이스는 **parallelism** 를 통하여 여러개의 thread를 동시에 시수행합니다. 25 | 26 | ### Concurrency 27 | 28 | - 싱글 코어 디바이스에서 사용합니다. 29 | - **Time Slicing** 을 이용하여 30 | 1. 하나의 Thread를 수행하고 31 | 2. Context Switch를 실행 후 32 | 3. 또다른 Thread를 수행합니다. 33 | 34 | ![Image](https://koenig-media.raywenderlich.com/uploads/2014/01/Concurrency_vs_Parallelism.png) 35 | 36 | ## Asynchronous and Synchronous 37 | 38 | ### Asynchronous 39 | 40 | ### Synchronous 41 | 42 | ## Dispatch Queue 43 | 44 | - 작업 단위를 queue에 전송하면 GCD는 FIFO(First in, First out) 순으로 작업을 실행합니다. 45 | - GCD는 concurrency library로 multi-thread 코드를 작성할 수 있게 도와줍니다. 46 | - Dispatch queue는 **thread safe** 합니다. 47 | 48 | - 즉, 여러 thread들이 동시에 dispatch queue에 접근할 수 있습니다. 49 | 50 | - **Dispatch queue** 에 code 블럭 혹은 work item을 추가할 수 있고, 어떠한 thread에서 이것들을 실행할 것인지를 결정합니다. 51 | - 시스템과 이용가능한 시스템 리소스를 기반으로 하여 얼마나 많은 parallelism이 필요한지 결정합니다. 52 | - **Parallelism은 concurrency를 필요로합니다.** 53 | - 하지만, **concurrency는 parallelism을 보장하진 않습니다.** 54 | 55 | ### Serial Queue 56 | 57 | - **주어진 시간에 대하여 오직 하나의 task만이 실행**됩니다. 58 | 59 | ![Serial](https://koenig-media.raywenderlich.com/uploads/2014/09/Serial-Queue-Swift-480x272.png) 60 | 61 | ### Concurrent Queue 62 | 63 | - **동일한 시간에 여러개의 task를 실행**할 수 있습니다. 64 | - FIFO순으로 실행하기 때문에, task를 추가한 순서대로 시작하도록 보장됩니다. 65 | - 단, **task의 종료에 대한 순서는 알 수 없습니다.** 66 | 67 | ![Concurrent](https://koenig-media.raywenderlich.com/uploads/2014/09/Concurrent-Queue-Swift-480x272.png) 68 | 69 | ### Asynchronous 70 | 71 | ### Synchronous 72 | 73 | ### Main Dispatch Queue 74 | 75 | - **Main thread에서 실행**됩니다. 76 | - **Serail Queue**입니다. 77 | - **UI에 대한 모든 것을 전담**합니다. 78 | 79 | ### Global Dispatch Queue 80 | 81 | - 전체 시스템에서 공유하는 **Concurrent Queue** 입니다. 82 | 83 | #### Quality of Service 84 | 85 | - User-Interactive 86 | - User-Initiated 87 | - Utility 88 | - Background 89 | 90 | 91 | 92 | Asynchronous 93 | 94 | Dispatch queue에 다중의 작업 항목들을 넣고 다시 dispatch하면, 해당 작업을 수행할 thread 를 불러옵니다. 95 | 96 | Dispatch는 작업 항목을 queue에서 꺼내와 수행합니다. Queue에 존재하는 모든 작업 항목을 끝마쳤을 때, 시스템은 사용했던 thread를 도로 가져갑니다. 97 | 98 | ![image-20190315072254783](/Users/igwang-yong/Library/Application Support/typora-user-images/image-20190315072254783.png) 99 | 100 | Synchronous 101 | 102 | 자신만의 Thread를 가지고 있고, 그 thread는 해당 queue에서 코드를 실행하고 그것이 일어날 때까지 기다리고 싶어합니다. 해당 작업을 발송 대기열에 제출할 수 있지만 그 작업이 차단됩니다. 실행을 요청한 항목이 완료 될 때까지 기다립니다. 103 | 104 | 이 queue에 asynchronous 작업을 더 추가할 수 있습니다. 그리고 dsipatch는 queue에 잇는 항목들을 서비스하기 위하여 thread를 불러옵니다. 105 | 106 | Asynchronous 작업을 수행하다가 synchronous 작업을 수행해야할 시점이 오면, dispatch queue는 대기 중인 thread로 제어권을 전달하고 항목을 실행한 후 dispatch queue의 제어권은 dispatch에 의하여 제어되는 worker thread로 돌아갑니다. 107 | 108 | 디스패치 대기열은 대기중인 스레드로 제어를 전달하고 해당 항목을 실행 한 다음 디스패치 대기열을 제어하여 디스패치로 제어되는 작업자 스레드로 다시 되돌아갑니다. 109 | 110 | 111 | 112 | ![image-20190316215725397](/Users/igwang-yong/Library/Application Support/typora-user-images/image-20190316215725397.png) 113 | 114 | 115 | 116 | Syncronous 117 | 118 | Subsystem 사이의 serialize state를 위하여 synchronous 실행을 사용할 수 있습니다. Serial queue와 dispatch queue들은 자연적으로 연속적(serial)입니다. 그리고 이것을 mutual exclusion property로 사용할 수 있습니다. 119 | 120 | ```swift 121 | var count: Int { 122 | queue.sync { self.connections.count } 123 | } 124 | ``` 125 | 126 | deadlock을 발생시킬 수 있으니 주의해야합니다. 127 | 128 | ![image-20190316223442267](/Users/igwang-yong/Library/Application Support/typora-user-images/image-20190316223442267.png) 129 | 130 | global 변수는 atomic하게 초기화 됩니다. 하지만 클래스 property와 lazy property는 atomic 하지 않습니다. 131 | 132 | "There is no such thing as a benign race." 133 | 134 | Synchronization 포인트를 잊는 다면, 크래쉬를 일으키거나 유저의 데이터를 손상시킬 수 있습니다. 135 | 136 | ```swift 137 | class Foo { 138 | private let internalState: Int 139 | private let internalQueue: DispatchQueue 140 | var state: Int { 141 | get { 142 | return internalQueue.sync { internalState } 143 | } 144 | set { 145 | internalQueue.sync { internalState = newState } 146 | } 147 | } 148 | } 149 | ``` 150 | 151 | ## Reference 152 | 153 | https://www.raywenderlich.com/5370-grand-central-dispatch-tutorial-for-swift-4-part-1-2 154 | 155 | https://developer.apple.com/videos/play/wwdc2017/706/ 156 | 157 | https://developer.apple.com/videos/play/wwdc2016/720/ 158 | 159 | -------------------------------------------------------------------------------- /Swift/Access_Control.md: -------------------------------------------------------------------------------- 1 | # Access Control 2 | 3 | ## Overview 4 | 5 | 사내 Library 구현에 대하여 고려하던 도중, Access Control에 대한 지식이 부족하고 느껴 복습을 위하여 정리하였습니다. 6 | 7 | ## Table of Contents 8 | 9 | - [Modules and Source Files](#Modules-and–Source-Files) 10 | - [Access Level](#Access-Level) 11 | - [Custom Types](#Custom-Types) 12 | - [Function Types](#Function-Types) 13 | - [Enumeration Types](#Enumeration-Types) 14 | - [Subclassing](#Subclassing) 15 | - [Constants, Variables, Properties, and Subscripts](#Constants, Variables, Properties,-and-Subscripts) 16 | - [Getters and Setters](#Getters-and-Setters) 17 | - [Initializers](#Initializers) 18 | - [Default Initializers](#Default-Initializers) 19 | - [Default Memberwise Initializers for Structure Types](#Default-Memberwise-Initializers-for-Structure-Types) 20 | 21 | ## Modules and Source Files 22 | 23 | Swift의 access control model은 **module** 과 **source file** 에 기초합니다. 24 | 25 | **Module** 은 code distribution의 단일 단위입니다. framework 또는 apllication은 단일 단위로 빌드되고 옮겨지며 `import` 키워드를 이용하여 다른 module에 import시킬 수 있습니다. 26 | 27 | **Source File** 은 module 내에 있는 단일 Swift source code 파일입니다. 28 | 29 | ## Access Level 30 | 31 | Access Level은 entity가 정의된 source file에 상대적이며, 또한 source file이 속한 module에 대하여 상대적입니다. 32 | 33 | 크게 `open`, `public`, `internal`, `fileprivate`, `private` 로 구성되어 있으며 아래와 같은 원칙하에 사용됩니다. 34 | 35 | **"No entity can be defined in terms of another entity that has a lower (more restrictive) access level."** (Access Level이 더 낮은 다른 entity의 관점에서 어떤 entity도 정의할 수 없다.) 36 | 37 | #### `open` 38 | 39 | - class와 class member에 대해서 가능 / struct, enum은 불가능 40 | - 정의된 module 내의 모든 source file 및 해당 module을 import한 다른 module의 source file에서 접근 가능 41 | - `open class` 는 정의된 module 뿐만아니라 import 한 다른 module에서도 subclass를 만들 수 있음 42 | - class member는 정의된 module 뿐만아니라 subclass에 의하여 import 한 다른 module에서도 override될 수 있음 43 | 44 | #### `public` 45 | 46 | - 정의된 module 내의 모든 source file 및 해당 module을 import한 다른 module의 source file에서 접근 가능 47 | - 정의된 module에서만 subclass를 만들 수 있음 48 | - class member는 정의된 모듈내에서만 subclass에 의하여 override될수 있음 49 | 50 | #### `internal` 51 | 52 | - default access level 53 | - 정의된 module 내의 모든 source file에서 접근 가능 54 | - 정의된 module에서만 subclass를 만들 수 있음 55 | - class member는 정의된 모듈내에서만 subclass에 의하여 override될수 있음 56 | 57 | #### `fileprivate` 58 | 59 | - 정의된 source file 내에서만 접근 가능 60 | - 정의된 module에서만 subclass를 만들 수 있음 61 | - class member는 정의된 모듈내에서만 subclass에 의하여 override될수 있음 62 | 63 | #### `private` 64 | 65 | - 정의된 선언부 내에서 접근가능, 동일한 source file 내의 선언에 대한 extension 내에서만 접근 가능 66 | - 정의된 module에서만 subclass를 만들 수 있음 67 | - class member는 정의된 모듈내에서만 subclass에 의하여 override될수 있음 68 | 69 | ### Custom Types 70 | 71 | Type의 access control level은 해당 type의 member들의 기본 access level에 영향을 끼칩니다. 72 | 73 | ```swift 74 | public class SomePublicClass { // explicitly public class 75 | public var somePublicProperty = 0 // explicitly public class member 76 | var someInternalProperty = 0 // implicitly internal class member 77 | fileprivate func someFilePrivateMethod() {} // explicitly file-private class member 78 | private func somePrivateMethod() {} // explicitly private class member 79 | } 80 | 81 | class SomeInternalClass { // implicitly internal class 82 | var someInternalProperty = 0 // implicitly internal class member 83 | fileprivate func someFilePrivateMethod() {} // explicitly file-private class member 84 | private func somePrivateMethod() {} // explicitly private class member 85 | } 86 | 87 | fileprivate class SomeFilePrivateClass { // explicitly file-private class 88 | func someFilePrivateMethod() {} // implicitly file-private class member 89 | private func somePrivateMethod() {} // explicitly private class member 90 | } 91 | 92 | private class SomePrivateClass { // explicitly private class 93 | func somePrivateMethod() {} // implicitly private class member 94 | } 95 | ``` 96 | 97 | ### Function Types 98 | 99 | 함수는 가장 낮은 access level을 따라갑니다. SomePrivateClass가 `private` 으로 가장 낮으니, 함수의 access level도 `private` 으로 지정해야합니다. 100 | 101 | ```swift 102 | ❌ func someFunction() -> (SomeInternalClass, SomePrivateClass) {} 103 | ✅ private func someFunction() -> (SomeInternalClass, SomePrivateClass) {} 104 | ``` 105 | 106 | ### Enumeration Types 107 | 108 | enumeration의 case마다 다른 access level을 부여하는 것은 불가능하며 enumeration의 access level을 따라갑니다. 109 | 110 | Raw Value와 Associated Value의 경우, enumeration의 access level보다 높거나 같아야합니다. 111 | 112 | ## Subclassing 113 | 114 | subclass는 superclass보다 높은 access level을 갖을 수는 없지만, class member를 override 한 경우에는 가능합니다. 또한, class member가 보다 낮은 access level의 superclass member를 호출하는 것은 허용된 access level context 내에서는 유효합니다. 115 | 116 | ```swift 117 | public class A { 118 | fileprivate func someMethod() {} 119 | } 120 | 121 | internal class B: A { 122 | override internal func someMethod() { 123 | super.someMethod() 124 | } 125 | } 126 | ``` 127 | 128 | ## Constants, Variables, Properties, and Subscripts 129 | 130 | Constant, variable, property, 또는 subscript는 해당 type의 access level보다 낮은 level로 구성합니다. 131 | 132 | ```swift 133 | private var privateInstance = SomePrivateClass() 134 | ``` 135 | 136 | ### Getters and Setters 137 | 138 | setter 에게 getter보다 낮은 access level을 부여함으로서, read-write 범위를 제한할 수 있습니다. 139 | 140 | ```swift 141 | struct TrackedString { 142 | private(set) var numberOfEdits = 0 143 | var value: String = "" { 144 | didSet { 145 | numberOfEdits += 1 146 | } 147 | } 148 | } 149 | ``` 150 | 151 | `TrackedString` 과 `value` 는 기본 access level인 `internal` 를 받습니다. 152 | 153 | 그러나, `numberOfEdits` 는 `private(set)` 를 이용하여 setter에 낮은 access level을 부여하였습니다. 이를 통하여 선언부 내 혹은 동일한 source file 내의 선언에 대한 extension 내에서만 `numberOfEdits` 의 값을 set할 수 있습니다. 154 | 155 | ## Initializers 156 | 157 | custom initializer는 access level를 같거나 보다 낮은 것에 대하여 지정할 수 있습니다. 158 | 159 | 단, **required** 인 경우에는 동일한 access level을 가져야합니다. 160 | 161 | ### Default Initializers 162 | 163 | Default Initializer는 `public` 으로 정의되어져 있지 않다면, initialize하는 type과 동일한 access level을 갖습니다. `pulbic` 으로 type이 지정되어져 있다면, default initializer는 `internal` 로 간주됩니다. 164 | 165 | ### Default Memberwise Initializers for Structure Types 166 | 167 | 만약 struct의 stored property들 중 하나라도 private라면 default memberwise initializer는 private로 간주된다. 반면, initailizer는 internal의 접근 수준을 가진다. 168 | 169 | ```swift 170 | ❌ 171 | struct User { 172 | var id: String 173 | private var pw: String 174 | } 175 | 176 | User(id: "ID", pw: "pw") //Default Memberwise Initializers 177 | //'User' initializer is inaccessible due to 'private' protection level 178 | 179 | ✅ 180 | struct User { 181 | var id: String 182 | private var pw: String 183 | init(id: String, pw: String) { //Initializer, internal 184 | self.id = id 185 | self.pw = pw 186 | } 187 | } 188 | 189 | User(id: "ID", pw: "pw") 190 | ``` 191 | 192 | ## Refence 193 | 194 | - https://docs.swift.org/swift-book/LanguageGuide/AccessControl.html 195 | - http://minsone.github.io/mac/ios/swift-access-control-summary -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # iOS Programming Reference 2 | 3 | ## Overview 4 | 5 | iOS 개발과 관련된, 공부해야할 것들에 대해 모아놓은 repository입니다. 6 | 7 | ## Table of Contents 8 | 9 | - [Computer Science](#Computer-Science) 10 | - [Clean Code](#Clean-code) 11 | - [OOP](#OOP) 12 | - [Swift](#Swift) 13 | - [Dependency Manager](#Dependency-Manager) 14 | - [Style](#Style) 15 | - [Architecture](#Architecture) 16 | - [Architecture Pattern](#Architecture-Pattern) 17 | - [Design Pattern](#Design-Pattern) 18 | - [Debug](#Debug) 19 | - [Resource](#Resource) 20 | - [Testing](#Testing) 21 | - [Concurrent Programming](#Concurrent-Programming) 22 | - [Funtional Programming](#Funtional-Programming) 23 | - [RxSwift](#RxSwift) 24 | - [Git](#Git) 25 | - [Store Data](#Store-data) 26 | - [ETC](#ETC) 27 | 28 | # Computer Science 29 | 30 | - Data Structure 31 | - Algorithm 32 | - [ ] [WWDC18. Embracing Algorithms](https://developer.apple.com/videos/play/wwdc2018/223/) 33 | 34 | # Clean code 35 | 36 | - [ ] [Clean code](https://g.co/kgs/JYnTVF) 37 | 38 | > 개인 정리 39 | > 40 | > - [Clean Code 정리(is Editing)](Clean_Code/README.md) 41 | 42 | # OOP 43 | 44 | - [x] [OOD Principlies in Swift](https://github.com/ochococo/OOD-Principles-In-Swift) 45 | - [x] [생각하라, 객체지향처럼](http://woowabros.github.io/study/2016/07/07/think_object_oriented.html) 46 | 47 | > 개인 정리 48 | > 49 | > - [S.O.L.I.D](OOP/SOLID.md) 50 | 51 | # Swift 52 | 53 | - [x] [Swift 성능 이해하기: Value 타입, Protocol과 스위프트의 성능 최적화 - 유용하님](https://academy.realm.io/kr/posts/letswift-swift-performance/) 54 | - [x] [Protocol Oriented Programming - 조성규님](https://www.slideshare.net/slideshow/embed_code/key/JnOqM0ODnB27EB) 55 | - [ ] [WWDC15. Protocol-Oriented Programming in Swift](https://developer.apple.com/videos/play/wwdc2015/408/) 56 | - [ ] [WWDC15. Building Better Apps with Value Types in Swift](https://developer.apple.com/videos/play/wwdc2015/414/) 57 | - [ ] [WWDC16. Protocol and Value Oriented Programming in UIKit Apps](https://developer.apple.com/videos/play/wwdc2016/419/) 58 | - [ ] [WWDC18. Swift Generics (Expanded)](https://developer.apple.com/videos/play/wwdc2018/406/) 59 | - [ ] [WWDC18. Using Collections Effectively](https://developer.apple.com/videos/play/wwdc2018/229/) 60 | 61 | > 개인 정리 62 | > 63 | > - [Value Semantics & Performance](Swift/ValueSemanticsAndPerformance.md) (is Editing. Need to update about WWDC videos) 64 | > - [Access Control](Swift/Access_Control.md) 65 | 66 | 67 | # Dependency Manager 68 | 69 | - [x] [Cocoapod](https://github.com/CocoaPods/CocoaPods) 70 | - [x] [Carthage](https://github.com/Carthage/Carthage) 71 | - [ ] [Swift Package Manager](https://github.com/apple/swift-package-manager) 72 | - [ ] [WWDC18. Getting to Know Swift Package Manager](https://developer.apple.com/videos/play/wwdc2018/411/) 73 | 74 | > 개인 정리 75 | > 76 | > - [Carthage](Dependency_Manager/Carthage.md) 77 | 78 | # Style 79 | 80 | - [x] [Swift 개발자처럼 변수 이름 짓기](https://soojin.ro/blog/english-for-developers-swift) 81 | - [x] API Design Guidelines 82 | - [(eng)](https://swift.org/documentation/api-design-guidelines/) 83 | - [(kor)](https://minsone.github.io/swift-internals/api-design-guidelines/?utm_source=soojinro&utm_medium=referral) 84 | - [(kor) 요약본](https://soojin.ro/blog/swift-api-design-guidelines-abbr) 85 | - [x] [Ray wenderlich style guide](https://github.com/raywenderlich/swift-style-guide) 86 | - [x] [Github style guide (kor)](https://github.com/minsOne/swift-style-guide/blob/master/README_KR.md) 87 | - [x] [Swift Lint](https://github.com/realm/SwiftLint) 88 | 89 | > 개인 정리 90 | > 91 | > - [API Design Guidelines With Grammar](Style/API_Design_Guidelines_With_Grammar.md) 92 | > - [SwiftLint](Style/SwiftLint.md) 93 | > - [Style Guide](Style/Style_Guide.md) 94 | 95 | # Architecture 96 | 97 | - Clean Architecture 98 | - [x] [The Clean Architecture - Uncle Bob](http://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html) 99 | - [x] [CleanArchitectureRxSwift](https://github.com/sergdort/CleanArchitectureRxSwift) 100 | 101 | > 개인 정리 102 | > 103 | > - [Clean Architecture](Architecture/CleanArchitecture.md) 104 | 105 | # Architecture Pattern 106 | 107 | - [ ] [Bohdan Orlov's iOS Architecture Pattern](https://medium.com/ios-os-x-development/ios-architecture-patterns-ecba4c38de52) 108 | - [ ] [iOS Architecture Sample](https://github.com/giftbott/iOS-Architecture-Sample) 109 | - [ ] [Architecture + Clean Swift](https://tv.naver.com/v/4980400) 110 | - [ ] [iOS 애플리케이션 아키텍처 : MVVM, MVC, VIPER 전격 비교](https://academy.realm.io/kr/posts/krzysztof-zablocki-mDevCamp-ios-architecture-mvvm-mvc-viper/) 111 | - MVC 112 | - [ ] [Apple Model-View-Controller](https://developer.apple.com/library/archive/documentation/General/Conceptual/DevPedia-CocoaCore/MVC.html) 113 | - [ ] [Model-View-Controller (MVC) in iOS: A Modern Approach - Ray Wenderlich](https://www.raywenderlich.com/1073-model-view-controller-mvc-in-ios-a-modern-approach) 114 | - [ ] [Modern MVC](https://medium.com/ios-os-x-development/modern-mvc-39042a9097ca) 115 | - MVVM 116 | - [x] [MVVM 아키텍처 패턴 - Gyuwon님](https://blog.weirdx.io/post/39547) 117 | - [x] [MVVM 응용프로그램을 위한 프로젝트 구조화 - Gyuwon님](https://blog.weirdx.io/post/39596) 118 | - [ ] [Introduction to MVVM - Ash Furrow](https://www.objc.io/issues/13-architecture/mvvm/) 119 | - VIP 120 | - [ ] [Clean Swift](https://clean-swift.com/clean-swift-ios-architecture/) 121 | > 개인 정리 122 | > 123 | > - [MVVM](Architecture_Pattern/MVVM.md) (is Editing. Need to establish an accurate concept.) 124 | 125 | # Design Pattern 126 | 127 | - [ ] [Swift Design Pattern](https://github.com/ochococo/Design-Patterns-In-Swift) 128 | - [ ] [Collection for Design Patterns in Swift](https://medium.com/swiftworld/collection-for-design-patterns-in-swift-67265359aa47) 129 | 130 | # Debug 131 | 132 | - [ ] [WWDC17. Debugging with Xcode 9](https://developer.apple.com/videos/play/wwdc2017/404/) 133 | - [ ] [WWDC16. Visual Debugging with Xcode](https://developer.apple.com/videos/play/wwdc2016/410/) 134 | - [ ] [WWDC18. What’s New in Energy Debugging](https://developer.apple.com/videos/play/wwdc2018/228/) 135 | - [ ] [WWDC18. Advanced Debugging with Xcode and LLDB](https://developer.apple.com/videos/play/wwdc2018/412/) 136 | 137 | # Resource 138 | 139 | - [ ] [WWDC18. iOS Memory Deep Dive](https://developer.apple.com/videos/play/wwdc2018/416/) 140 | - [ ] [WWDC17. Writing Energy Efficient Apps](https://developer.apple.com/videos/play/wwdc2017/238/) 141 | 142 | # Testing 143 | 144 | - [ ] [회고 : 두번째 개인 프로젝트를 TDD로 진행하며… #1 - Wade님](https://medium.com/@junhyi.park/회고-두번째-개인-프로젝트를-tdd로-진행하며-1-5345775d85bb) 145 | - [ ] [Swift의 강력한 mock 객체 만들기](https://academy.realm.io/kr/posts/making-mock-objects-more-useful-try-swift-2017/) 146 | - [ ] [WWDC15. UI Testing in Xcode](https://developer.apple.com/videos/play/wwdc2015/406/) 147 | 148 | # Concurrent Programming 149 | 150 | - GCD 151 | - Operation Queue 152 | - [ ] [WWDC15. Advanced NSOperations](https://developer.apple.com/videos/play/wwdc2015/226/) 153 | - POSIX thread 154 | - Lock, Semaphore 155 | 156 | > 개인 정리 157 | > 158 | > - [GCD](Concurrency/GrandCentralDispatch.md) is Editing 159 | 160 | # Funtional Programming 161 | - [ ] [Swift와 함수형 프로그래밍의 역사](https://academy.realm.io/kr/posts/tryswift-rob-napier-swift-legacy-functional-programming/) 162 | - [ ] [Swift로 함수형 프로그래밍 시작하기](https://www.youtube.com/watch?v=H9aCQt2SPpQ) 163 | - [ ] [Functional Programming 이 뭐하는 건가요?](https://www.youtube.com/watch?time_continue=2&v=HZkqMiwT-5A) 164 | - [ ] [Why the Func: 왜 함수형 프로그래밍을 해야 하나요?](https://academy.realm.io/kr/posts/daniel-steinberg-altconf-2017-why-the-func/) 165 | 166 | # RxSwift 167 | 168 | - [x] [RxSwift 4시간만에 끝내기](https://www.youtube.com/watch?v=w5Qmie-GbiA&list=PL03rJBlpwTaAh5zfc8KWALc3ADgugJwjq) 169 | - [x] [RxSwift Study](https://github.com/fimuxd/RxSwift) 170 | - [ ] [Examples](https://github.com/DroidsOnRoids/RxSwiftExamples) 171 | 172 | > 개인 정리 173 | > 174 | > - [Observable, Disposing, Subject, Variable](RxSwift/Observable_Disposing_Subject_Variable.md) 175 | > - [Traits](RxSwift/Traits.md) 176 | 177 | # Git 178 | 179 | - [x] [좋은 git 커밋 메시지를 작성하기 위한 7가지 약속](https://meetup.toast.com/posts/106) 180 | - [ ] [Github cheat sheet](https://github.com/tiimgreen/github-cheat-sheet) 181 | - Flow 182 | - [ ] [우린 Git-flow를 사용하고 있어요](http://woowabros.github.io/experience/2017/10/30/baemin-mobile-git-branch-strategy.html) 183 | - [ ] [Git flow, GitHub flow, GitLab flow](https://ujuc.github.io/2015/12/16/git-flow-github-flow-gitlab-flow/) 184 | 185 | > 개인 정리 186 | > 187 | > - [Git Commit Message](Git/Git_Commit_Message.md) 188 | 189 | # Store Data 190 | 191 | - UserDefaults 192 | - CoreData 193 | - NSKeyedArchiver 194 | - SQLite 195 | - Realm 196 | 197 | > 개인 정리 198 | > 199 | > - [Store Data(is Editing)](Store_Data/Store_Data.md) 200 | 201 | # ETC 202 | 203 | - Localization 204 | - [ ] [WWDC18. New Localization Workflows in Xcode 10](https://developer.apple.com/videos/play/wwdc2018/404/) 205 | - Auto Layout, Animation 206 | - [ ] [WWDC18. High Performance Auto Layout](https://developer.apple.com/videos/play/wwdc2018/220/) 207 | - [ ] [WWDC15. Mysteries of Auto Layout, Part 1](https://developer.apple.com/videos/play/wwdc2015/218/) 208 | - [ ] [WWDC15. Mysteries of Auto Layout, Part 2](https://developer.apple.com/videos/play/wwdc2015/219/) 209 | - [ ] [WWDC17. Advanced Animations with UIKit](https://developer.apple.com/videos/play/wwdc2017/230/) 210 | 211 | - JWT, OAuth 212 | - [ ] [OAuth와 춤을](https://d2.naver.com/helloworld/24942) 213 | - [ ] [OAuth 2.0 대표 취약점과 보안 고려 사항 알아보기](https://meetup.toast.com/posts/105) 214 | 215 | > 개인 정리 216 | > 217 | > - [Class Diagram](ETC/Class_Diagram.md) 218 | > - [Layout](ETC/Layout.md) (is Edting. Need to update about objc.io's content) -------------------------------------------------------------------------------- /Style/API_Design_Guidelines_With_Grammar.md: -------------------------------------------------------------------------------- 1 | # API Design Guidelines With Grammar 2 | 3 | ## Overview 4 | 5 | 함수명, 변수명들을 일정 규칙과 문법을 따라 만든다면, 이름만을 보고 그것들의 역할을 어느정도 유추가 가능할 것입니다. 6 | 7 | [Swift API Design Guidelines](https://swift.org/documentation/api-design-guidelines/)와 노수진님의 [Swift 개발자처럼 변수 이름 짓기](https://soojin.ro/blog/english-for-developers-swift), [Bool 변수 이름 제대로 짓기 위한 최소한의 영어 문법](https://soojin.ro/blog/naming-boolean-variables) 를 읽고 입맛대로 정리한 내용입니다. 개인적인 주관이 다수 섞여있습니다. 8 | 9 | ## Table of Contents 10 | 11 | - [Fundamentals](#Fundamentals) 12 | - [Naming](#Naming) 13 | - [Promote Clear Usage](#Promote-Clear-Usage) 14 | - [Strive for Fluent Usage](#Strive-for-Fluent-Usage) 15 | - [Reference](#Reference) 16 | 17 | ## Fundamentals 18 | 19 | API를 디자인함에 있어 가장 중요한 것은 **"사용 시점"에서의 명료성**입니다. 이때, **명료성은 간결성보다 중요**합니다. 20 | 21 | 또한, 모든 선언문에는 **Documentation Comments을 작성**해줘야합니다. (참고 : [Markup Formatting Reference](https://developer.apple.com/library/archive/documentation/Xcode/Reference/xcode_markup_formatting_ref/)) 22 | 23 | > 간단한 용어로 API 기능을 설명하지 못한다면, **API 설계에 문제가 있을 가능성이 높습니다.** 24 | 25 | ## Naming 26 | 27 | ### Promote Clear Usage 28 | 29 | - **모호성을 피하기 위하여(명료성을 위하여) 필요한 단어를 포함**합니다. 30 | 31 | ```swift 32 | extension List { 33 | public mutating func remove(at position: Index) -> Element 34 | } 35 | //위 함수만 있음을 가정할 때, 36 | ❌ employees.remove(x) // x를 제거해라? x위치의 것을 지워라? 모호함. 37 | ✅ employees.remove(at: x) //x위치의 것을 지워라. 38 | ``` 39 | 40 | - **불필요한 단어는 생략**합니다. 41 | 42 | > 단, 간결성을 위하여 명료성을 해치면 안됩니다. 43 | 44 | ```swift 45 | ❌ 단순히 타입 정보를 반복하는 단어는 생략 46 | public mutating func removeElement(member: Element) -> Element? 47 | allViews.removeElement(cancelButton) //cancelButton Element를 지워라. 48 | 49 | ✅ 50 | public mutating func remove(member: Element) -> Element? 51 | allViews.remove(cancelButton) // cancelButton을 지워라. 52 | ``` 53 | 54 | ```swift 55 | struct User { 56 | ❌ let userID: String // User와 userID의 'user'가 중복되었습니다. 57 | ✅ let identifier: String 58 | } 59 | ``` 60 | 61 | - Type constraints가 아니라, **''역할''에 따라 variable, parameter, associated type의 이름을 정합니다.** 62 | 63 | ```swift 64 | ❌ var string = "Hello" 65 | ✅ var greeting = "Hello" 66 | 67 | protocol ViewController { 68 | ❌ associatedtype ViewType : View 69 | ✅ associatedtype ContentView : View 70 | } 71 | 72 | class ProductionLine { 73 | ❌ func restock(from widgetFactory: WidgetFactory) 74 | ✅ func restock(from supplier: WidgetFactory) 75 | } 76 | ``` 77 | 78 | 만약, associated type이 protocol contraint에 매우 밀접하게 결합되어 protocol 이름이 그 역할인 경우, 해당 protocol 이름에 `Protocol`을 붙임으로써 충돌을 피합니다. e.g. `Iterator` + **`Protocol`** 79 | 80 | ```swift 81 | protocol Sequence { 82 | associatedtype Iterator : IteratorProtocol 83 | } 84 | 85 | protocol IteratorProtocol { ... } 86 | ``` 87 | 88 | > The `IteratorProtocol` protocol is tightly linked with the `Sequence` protocol. Sequences provide access to their elements by creating an iterator, which keeps track of its iteration process and returns one element at a time as it advances through the sequence. 89 | 90 | - Parameter의 역할을 명확히 하도록, **취약한 type 정보를 보완**합니다. 91 | 92 | > 간결성을 추구하다가 명료성을 해치면 안됩니다. 93 | 94 | ```swift 95 | ❌ 96 | func add(_ observer: NSObject, for keyPath: String) 97 | grid.add(self, for: graphics) // 사용하는 곳이 모호합니다. 98 | 99 | ✅ 약하게 정의된 parameter의 앞에 역할을 설명하는 명사를 붙입니다. 100 | func addObserver(_ observer: NSObject, forKeyPath path: String) 101 | grid.addObserver(self, forKeyPath: graphics) // clear 102 | ``` 103 | 104 | ### Strive for Fluent Usage 105 | 106 | - method와 function을 **사용하는 곳에서 method와 function이 영어 문법 구문의 형태**가 되는 것을 선호합니다. 107 | 108 | ```swift 109 | ❌ x.insert(y, position: z) 110 | ✅ x.insert(y, at: z) //“x, y를 z에 삽입해라.” 111 | 112 | ❌ x.subViews(color: y) 113 | ✅ x.subViews(havingColor: y) //“x, 색상 y을 갖는subviews를 반환해라.” 114 | 115 | ❌ x.nounCapitalize() 116 | ✅ x.capitalizingNouns() //“x, 명사를 대문자화해라” 117 | 118 | 119 | //UIView 조동사 + 동사원형 120 | func willRemoveSubview(_ subview: UIView) 121 | willRemoveSubview(y) //UIView에서 y라는 subView가 지워질 것이다. 122 | /* 123 | `func willRemove(_ subview: UIView)는 "Parameter의 역할을 명확히 하도록, 취약한 type 정보를 보완합니다."를 충족하지 않습니다. 124 | */ 125 | ``` 126 | 127 | - **factory methods는 “make”로 시작**합니다. 128 | 129 | [Factory Method?](#Factory-Method) 130 | 131 | - ``` 132 | x.makeIterator() 133 | ``` 134 | 135 | - **사이드 이펙트에 따라 함수와 메소드 이름을 지정**합니다. 136 | 137 | [Side Effect?](#Side-Effect) 138 | 139 | - 사이드 이펙트가 없다(**Pure Function**)? **명사구** 140 | 141 | ```swift 142 | x.distance(to: y) 143 | ``` 144 | 145 | swift는 getter가 존재하지 않고, 타입 이름(명사)로 시작하면 됩니다. 146 | 147 | ```swift 148 | func distance(from location: CLLocation) -> CLLocationDistance 149 | ``` 150 | 151 | - 사이드 이펙트가 있다? **동사구문** 152 | 153 | ```swift 154 | print(x) // x를 출력해라. I/O가 있으므로 print도 Side Effect가 존재합니다. 155 | x.sort() // x를 정렬해라. x라는 값을 변경 시키기에 Side Effect가 존재합니다. 156 | x.append(y) // x에 y를 덧붙여라. x라는 값을 변경 시키기에 Side Effect가 존재합니다. 157 | ``` 158 | 159 | - **Mutating/nonmutating** 메소드 쌍을 **일관되게 이름을 지정**합니다. 160 | 161 | [Mutating, Non Mutating?](#Mutating,-Non-Mutating) 162 | 163 | > mutating method는 종종 유사한 구문의 nonmutating method가 존재하지만, 164 | > 165 | > mutating은 **인스턴스 자체(in-place)**를 업데이트합니다. 166 | > 167 | > nonmutating은 인스턴스 자체(in-place)를 업데이트하는 것 대신, **새로운 값을 반환**합니다. 168 | 169 | - 동사 170 | - mutating : 동사 171 | - nonmutating : 동사 + -ed 또는 -ing 172 | 173 | | Mutating | Nonmutating | 174 | | ------------- | -------------------- | 175 | | `x.sort()` | `z = x.sorted()` | 176 | | `x.append(y)` | `z = x.appending(y)` | 177 | 178 | ```swift 179 | mutating func sort() //mutating, in-place 180 | func sorted() -> [Element] //nonmutating, 새로운 배열을 반환 181 | ``` 182 | 183 | - 명사 184 | - mutating : from + 명사 185 | - non mutating : 명사 186 | 187 | | Mutating | Nonmutating | 188 | | -------------------- | --------------------- | 189 | | `y.formUnion(z)` | `x = y.union(z)` | 190 | | `c.formSuccessor(i)` | `j = c.successor(i)` | 191 | 192 | ```swift 193 | mutating func formUnion(_ other: S) where Element == S.Element, S : Sequence 194 | //mutating 195 | func union(_ other: S) -> Set.Element> where Element == S.Element, S : Sequence 196 | //non mutating 197 | ``` 198 | 199 | - `fetch`, `request`, `perfrom` 200 | 201 | - `fetch` : 실패하지 않는 작업, 결과를 바로 반환하는 경우에 사용합니다. 202 | 203 | - ```swift 204 | //PHAsset - Photos Framework 205 | class func fetchAssets(withLocalIdentifiers identifiers: [String], options: PHFetchOptions?) -> PHFetchResult 206 | ``` 207 | 208 | - `request` : 실패할 수 있는 작업, 요청을 거절할 수 있는 경우에 사용합니다. 209 | 210 | - ```swift 211 | //CLLocationManager 212 | func requestAlwaysAuthorization() 213 | // 사용자에 의하여 요청이 거절될 수도 있기 때문에 request를 사용합니다. 214 | //Requests permission to use location services whenever the app is running. 215 | ``` 216 | 217 | - `perform` or `execute` : 작업의 단위가 closure나 requesst로 wrapping되어있는 경우에 사용합니다. 218 | 219 | - ```swift 220 | //RunLoop 221 | func perform(_ block: @escaping () -> Void) 222 | ``` 223 | 224 | - **Boolean method와 properties**는 nonmutating일 경우 **receiver에 대한 주장**으로 읽어야합니다. 225 | 226 | > **Uses of Boolean methods and properties should read as assertions about the receiver** when the use is nonmutating 227 | > 228 | > Bool 메서드나 프로퍼티 이름은 인스턴스에 대한 평서문처럼 읽혀야한다. 229 | > 230 | > Objective-C에서는 `someObject` 는 `doSomething` (**message**)에 대하여 값의 '호출' 혹은 '요청'을 **받는** 입장이기에, **receiver** 라고 불렀습니다. 231 | > 232 | > Method 혹은 Property를 호출한다 = Receiver에게 message를 보낸다. 233 | > 234 | > ```objc 235 | > [someObject doSomething]; 236 | > //someObject.doSomething() 237 | > ``` 238 | > 239 | > 참고 : [Programming with Objective-C](https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/WorkingwithObjects/WorkingwithObjects.html) 240 | > 241 | > ( 노수진님의 첨삭을 통하여 수정하였습니다. 감사합니다 :] ) 242 | 243 | ```swift 244 | //Collection 245 | x.isEmpty //x가 비어있는가? 246 | //receiver : x 247 | //message : isEmpty 248 | //SKNode 249 | node1.intersects(node2) //node1이 node2와 교차하는가? 250 | //receiver : node1 251 | //Message : intersects 252 | ``` 253 | 254 | - 조동사 + 동사원형 255 | 256 | - can : "~할 수 있는가?" 257 | 258 | ```swift 259 | var canBecomeFirstResponder: Bool { get } //UIResponder: first responder가 될 수 있는가? 260 | ``` 261 | 262 | - should, will : "~해야하는가?" or "~할 것인가?" 263 | 264 | ```swift 265 | var shouldRefreshRefetchedObjects: Bool { get set } //NSFetchRequest: 가져온 값을 refresh 할 것인가? 266 | ``` 267 | 268 | - is + 269 | 270 | - 명사 : "(무엇)인가?" 271 | 272 | ```swift 273 | func isDescendant(of view: UIView) -> Bool //UIView: "view의 자식인가?" 274 | ``` 275 | 276 | - ~ing : "~하는 중인가?" 277 | 278 | ```swift 279 | var isExecuting: Bool { get } //Operation: "오퍼레이션의 작업이 현재 실행 중인가?" 280 | ``` 281 | 282 | - 형용사(자체) 283 | 284 | ```swift 285 | var isOpaque: Bool { get set } //UIView: "view가 불투명한가?" 286 | ``` 287 | 288 | - 과거분사 289 | 290 | ```swift 291 | var isSelected: Bool { get set } //UIView: "view가 선택되어졌는가?" 292 | var isHidden: Bool { get set } //UiView: "view가 숨겨졌는가? 293 | ``` 294 | 295 | - **단, 절대 is + 동사원형을 사용하면 안됩니다.** 296 | 297 | - **무언가를 설명하는 프로토콜은 명사**로 읽어야 합니다. (e.g. `Collection`) 298 | 299 | - **Capability를 설명하는 프로토콜은 able, ible 또는 ing 접미사**를 사용하여 이름을 지정해야 합니다. (e.g. `Equatable`, `ProgressReporting`). 300 | 301 | ------ 302 | 303 | 304 | 305 | #### Factory Method 306 | 307 | > https://en.wikipedia.org/wiki/Factory_method_pattern 308 | > 309 | > https://developer.apple.com/library/archive/documentation/General/Conceptual/CocoaEncyclopedia/ClassFactoryMethods/ClassFactoryMethods.html 310 | > 311 | > 아직은 잘 모르겠습니다. 공부가 필요합니다. 312 | 313 | #### Side Effect 314 | 315 | > 사이드 이펙트는 단순히 부정적인 것을 뜻하지 않습니다. 316 | > 317 | > **함수의 실행이 외부에 영향을 끼치는 경우**를 사이드 이펙트라고 합니다. 318 | > 319 | > - 실행중에 어떤 객체를 접근해서 변화가 일어나는 행위(라이브러리 I/O, 객체 변경 등) 320 | > - 함수 외부의 State 변화 321 | 322 | #### Mutating, Non Mutating 323 | 324 | > mutating의 사전적 의미는 "변화시키다"입니다. 325 | > 326 | > `mutating` 은 "해당 method가 instance의 property 값을 바꾼다.(mutate하다)"를 나타내기 위함입니다. 327 | > 328 | > immutable은 `let`, mutable은 `var` 키워드로서 설정을 해줍니다. 329 | > 330 | > ```swift 331 | > struct Point { 332 | > var x = 0.0, y = 0.0 333 | > mutating func moveBy(x deltaX: Double, y deltaY: Double) { 334 | > x += deltaX 335 | > y += deltaY 336 | > } 337 | > } 338 | > var somePoint = Point(x: 1.0, y: 1.0) 339 | > somePoint.moveBy(x: 2.0, y: 3.0) 340 | > print("The point is now at (\(somePoint.x), \(somePoint.y))") 341 | > // Prints "The point is now at (3.0, 4.0)" 342 | > ``` 343 | 344 | 노수진님의 조언을 통하여 보완되었습니다. 감사합니다 :] 345 | 346 | ## Reference 347 | 348 | - https://swift.org/documentation/api-design-guidelines/ 349 | - https://minsone.github.io/swift-internals/api-design-guidelines/?utm_source=soojinro&utm_medium=referral 350 | - https://soojin.ro/blog/english-for-developers-swift 351 | - https://soojin.ro/blog/naming-boolean-variables 352 | - https://soojin.ro/blog/swift-api-design-guidelines-abbr 353 | - https://en.wikipedia.org/wiki/Side_effect_(computer_science) 354 | - https://docs.swift.org/swift-book/LanguageGuide/Methods.html -------------------------------------------------------------------------------- /RxSwift/Observable_Disposing_Subject_Variable.md: -------------------------------------------------------------------------------- 1 | # RxSwift - Observable, Disposing, Subject, Variable 2 | 3 | ## Table of Contents 4 | 5 | - [Observable](#Observable) 6 | - [Basic](#Basic) 7 | - [Event](#Event) 8 | - [Disposing](#Disposing) 9 | - [Dispose Bag](#Dispose-Bag) 10 | - [Take Until](#Take-Until) 11 | - [Subject](#Subject) 12 | - [PublishSubject](#PublishSubject) 13 | - [ReplaySubject](#ReplaySubject) 14 | - [BehaviorSubject](#BehaviorSubject) 15 | - [Variable](#Variable) 16 | - [Reference](#Reference) 17 | 18 | ## Observable 19 | 20 | ### Basic 21 | 22 | - **모든 `Observable` 은 sequence**입니다. 23 | 24 | - Element를 **asynchronous**하게 받을 수 있습니다. 25 | 26 | - 어떠한 thread에서 element를 생성하여 `observer.on(.next(nextElement))` 로 보내던 간에, `observer.on` 이 끝나기 전까지는 보낼 수 없습니다. 27 | 28 | - 또한, `.next` 가 끝나기 전까지 `.completed` 혹은 `.error` 를 보낼 수 없습니다. 29 | 30 | 31 | ```swift 32 | someObservable 33 | .subscribe { (e: Event) in 34 | print("Event processing started") 35 | // processing 36 | print("Event processing ended") 37 | } 38 | 39 | //❌ 불가능 40 | //Event processing started 41 | //Event processing started 42 | //Event processing ended 43 | //Event processing ended 44 | 45 | //✅ 46 | //Event processing started 47 | //Event processing ended 48 | //Event processing started 49 | //Event processing ended 50 | //Event processing started 51 | //Event processing ended 52 | ``` 53 | 54 | - ` Observable`이 **생성되었더라도, `Observable`은 어떠한 작업도 수행하지 않습니다.** 55 | 56 | - `Observable`은 단지 **sequence가 생성되는 방법과 element 생성에 어떤 parameter가 사용되는지를 정의**합니다. 57 | - Sequence는 **`subscribe()` 가 호출되었을 때 생성되기 시작**합니다. 58 | 59 | ```swift 60 | let observable = Observable.create { observerOfString in 61 | print("Observable created") 62 | observerOfString.on(.next("😉")) 63 | observerOfString.on(.completed) 64 | return Disposables.create() 65 | } 66 | //subscribe()가 없으면 observable이 생성되었어도 아무일도 일어나지 않습니다. 67 | 68 | //observable을 subscrbie 함으로써 sequence가 생성되기 시작합니다. 69 | observable.subscribe({ (event) in 70 | switch event { 71 | case .next(let element): 72 | print("next(\(element))") 73 | case .error(let error): 74 | print(error) 75 | case .completed: 76 | print("completed") 77 | } 78 | }) 79 | //Observable created 80 | //next(😉) 81 | //completed 82 | ``` 83 | 84 | - 모든 subscriber는 대개 자체적으로 개별적인 element sequence를 생성합니다. 85 | - **Operator들은 기본적으로 stateless**입니다. 86 | - Stateless Operator가 Stateful Operator보다 훨씬 많습니다. 87 | 88 | ### Event 89 | 90 | - Sequence는 0개 혹은 그 이상의 element를 갖을 수 있습니다. 91 | - **`error` 또는 `complete` event를 받았을 때, sequecne는 element를 생산할 수 없습니다.** 92 | - Sequnce가 `completed` 또는 `error` event를 보내면, sequence element들을 계산하기 위한 **모든 내부 자원들은 해방됩니다**. 93 | 94 | ```swift 95 | enum Event { 96 | case next(Element) // next element of a sequence 97 | case error(Swift.Error) // sequence failed with error 98 | case completed // sequence terminated successfully 99 | } 100 | 101 | class Observable { 102 | func subscribe(_ observer: Observer) -> Disposable 103 | } 104 | 105 | protocol ObserverType { 106 | func on(_ event: Event) 107 | } 108 | ``` 109 | 110 | ## Disposing 111 | 112 | > 다시 한번 읽고 정리하자! (https://github.com/ReactiveX/RxSwift/blob/master/Documentation/GettingStarted.md#disposing) 113 | > 114 | 115 | - Sequence를 종료하고 resources를 해제하고자 할때, `dispose` 를 호출합니다. 116 | - `DisposeBag` , `takeUntil` , 혹은 또다른 메커니즘을 사용하는 더 좋은 방법들이 있습니다. 117 | 118 | ```swift 119 | func subscribe(onNext: ((Int) -> Void)? = default, onError: ((Error) -> Void)? = default, onCompleted: (() -> Void)? = default, onDisposed: (() -> Void)? = default) -> Disposable 120 | ``` 121 | 122 | ```swift 123 | let scheduler = SerialDispatchQueueScheduler(qos: .default) 124 | let subscription = Observable.interval(0.3, scheduler: scheduler) 125 | .subscribe { event in 126 | print(event) 127 | } 128 | 129 | Thread.sleep(forTimeInterval: 2.0) 130 | 131 | subscription.dispose() 132 | //dispose()를 호출함으로써 Sequence를 종료합니다. 133 | ``` 134 | 135 | - 위의 코드는 `dispose` 실행 이후에 어떤 것들을 출력할 수 있을까? 136 | 137 | - `scheduler` 가 **Serial Scheduler**(ex. `MainScheduler`)이며 `dispose` 가 **동일한 serial scheduler** 에서 호출이 된다면, 출력이 **불가능**합니다. 138 | - 그렇지 않다면, 출력이 **가능**합니다. 139 | - 이러한 프로세스가 다른 scheduler에 있다면 의미가 없습니다. 140 | 141 | > 음.. 계속 읽어보지만 아직 잘 이해가 가지 않습니다. :'( 142 | 143 | ```swift 144 | let scheduler = SerialDispatchQueueScheduler(qos: .default) 145 | let subscription = Observable.interval(0.3, scheduler: scheduler) 146 | .observeOn(MainScheduler.instance) 147 | .subscribe { event in 148 | print(event) 149 | } 150 | 151 | Thread.sleep(forTimeInterval: 2.0) 152 | 153 | subscription.dispose() // called from main thread 154 | //interval(, scheduler: )은 `scheduler`에 위치하고 있고, 155 | //subscribe와 dispose는 `MainScheduler`에 위치하고 있어 156 | //의미가 없습니다. 157 | ``` 158 | 159 | ### Dispose Bag 160 | 161 | - `DisposeBag` 의 할당이 해제될 때, `DisposeBag` 에 들어가 있는 disposeable들의 `dispose` 를 호출할 것입니다. 162 | - `DisposeBag` 은 `dispose` method가 존재하지 않습니다. 163 | - 즉각적인 정리가 필요하다면, 새롭게 할당해주면 됩니다. 164 | 165 | ```swift 166 | self.disposeBag = DisposeBag() 167 | ``` 168 | 169 | > Q. Driver와 Observable의 차이? (조금 더 이후의 내용) 170 | > 171 | > Q. Observable이 dispose되면 complete를 뿜게 되는가? ❌ 172 | > 173 | > Q. Observable이 error 혹은 complete를 뿜게 되면(event가 종료가 된다면), dispose가 되는가? ✅ 174 | > 175 | > - onNext: Action to invoke for each element in the observable sequence. 176 | > - onError: Action to invoke upon errored termination of the observable sequence. 177 | > - onCompleted: Action to invoke upon graceful termination of the observable sequence. 178 | > - onDisposed: Action to invoke upon any type of termination of sequence (if the sequence has gracefully completed, errored, or if the generation is canceled by disposing subscription). 179 | 180 | ### Take Until 181 | 182 | - 자동적으로 subscription을 dispose시키기 위한 방법 중 하나입니다. 183 | 184 | ```swift 185 | let subscription = Observable.interval(0.3, scheduler: scheduler) 186 | .takeUntil(rx.deallocated) 187 | .subscribe { event in 188 | print(event) 189 | } 190 | ``` 191 | 192 | ## Subject 193 | 194 | - `Observer` 임과 동시에 `Observable` 입니다 195 | - `Observer`이기 때문에 하나 이상의 ` Observable` 을 subscribe할 수 있습니다. 196 | - `Observable`이기 때문에 reemit함으로서 observe하는 item을 통과 시킬 수 있습니다. 또한, 새로운 item도 emit할 수 있습니다. 197 | - `PublishSubject`, `ReplaySubject`, `BehaviorSubject` 는 Dispose되더라도 Complete event를 자동으로 emit하지 않습니다. 198 | 199 | > Subject들의 예시를 위함입니다. 200 | > 201 | > ```swift 202 | > extension ObservableType { 203 | > 204 | > /** 205 | > Add observer with `id` and print each emitted event. 206 | > - parameter id: an identifier for the subscription. 207 | > */ 208 | > func addObserver(_ id: String) -> Disposable { 209 | > return subscribe(onNext: { print("Subscription: ", id, "Event:", $0) }, onCompleted: { print("\(id) is completed") }, onDisposed: {print("\(id) is disposed")}) 210 | > } 211 | > } 212 | > ``` 213 | 214 | ### PublishSubject 215 | 216 | - 값이 없는 상태로 시작하여, 새로운 값만을 subscriber에 emit합니다. 217 | - Sequence가 error 혹은 complete에 의하여 종료될 경우, 기존의 subscribe들에게 종료 Event를 emit합니다. 또한, 이후의 subscribe에게도 종료 Event를 reemit합니다. 218 | 219 | ![](https://cdn-images-1.medium.com/max/1200/0*Gb5XdAgf__b8G8h2.png) 220 | 221 | ```swift 222 | let disposeBag = DisposeBag() 223 | let subject = PublishSubject() 224 | 225 | subject.addObserver("1").disposed(by: disposeBag) 226 | subject.onNext("🐶") 227 | //Subscription: 1 Event: 🐶 228 | subject.onNext("🐱") 229 | //Subscription: 1 Event: 🐱 230 | 231 | subject.addObserver("2").disposed(by: disposeBag) 232 | subject.onNext("🅰️") 233 | //Subscription: 1 Event: 🅰️ 234 | //Subscription: 2 Event: 🅰️ 235 | subject.onNext("🅱️") 236 | //Subscription: 1 Event: 🅱️ 237 | //Subscription: 2 Event: 🅱️ 238 | 239 | subject.onCompleted() 240 | //1 is completed 241 | //1 is disposed 242 | //2 is completed 243 | //2 is disposed 244 | subject.addObserver("3").disposed(by: disposeBag) 245 | //3 is completed 246 | //3 is disposed 247 | ``` 248 | 249 | #### ReplaySubject 250 | 251 | - 모든 subscriber에게 새로운 event를 보내고, bufferSize의 수만큼 이전 event를 새로운 subscriber에게 방출합니다. 252 | 253 | - Buffer Size만큼 emit할 최신 element를 Caching하거나 Buffer를 이용합니다. 그 후 해당 Buffer를 새로운 subscriber에게 emit합니다. 254 | 255 | > Buffer에 emit할 것들이 저장되어져 있어, 종료된 ReplaySubject를 subscribe하게 되어도 Buffer Size 값만큼 emit 받을 수 있습니다. 256 | 257 | - Sequence가 error 혹은 complete에 의하여 종료될 경우, 기존의 subscribe들에게 종료 Event를 emit합니다. 또한, 이후의 subscribe에게도 종료 Event를 reemit합니다. 258 | 259 | 260 | ![](https://cdn-images-1.medium.com/max/1600/0*uVtsGFtLCDfd9FP3.png) 261 | 262 | ```swift 263 | let disposeBag = DisposeBag() 264 | let subject = ReplaySubject.create(bufferSize: 1) 265 | 266 | subject.addObserver("1").disposed(by: disposeBag) 267 | subject.onNext("🐶") 268 | subject.onNext("🐱") 269 | //Subscription: 1 Event: 🐶 270 | //Subscription: 1 Event: 🐱 271 | 272 | subject.addObserver("2").disposed(by: disposeBag) 273 | //bufferSize만큼의 이전 event를 받아옵니다.(1개 만큼의 이전 event를 받아옵니다.) 274 | //Subscription: 2 Event: 🐱 275 | subject.onNext("🅰️") 276 | //Subscription: 1 Event: 🅰️ 277 | //Subscription: 2 Event: 🅰️ 278 | subject.onNext("🅱️") 279 | //Subscription: 1 Event: 🅱️ 280 | //Subscription: 2 Event: 🅱️ 281 | 282 | subject.onCompleted() 283 | //1 is completed 284 | //1 is disposed 285 | //2 is completed 286 | //2 is disposed 287 | subject.addObserver("3").disposed(by: disposeBag) 288 | //Subscription: 3 Event: 🅱️ 289 | //3 is completed 290 | //3 is disposed 291 | ``` 292 | 293 | ### BehaviorSubject 294 | 295 | - 모든 subscriber에게 새로운 event를 보내고, 가장 최신의 값(혹은 초기값)을 새로운 subscriber에게 방출합니다. 296 | - 반드시 **초기값**이 존재하여야합니다. 297 | - Sequence가 error 혹은 complete에 의하여 종료될 경우, 기존의 subscribe들에게 종료 Event를 emit합니다. 또한, 이후의 subscribe에게도 종료 Event를 reemit합니다. 298 | 299 | 300 | ![](https://cdn-images-1.medium.com/max/1200/0*Vb0_X7aVyfuVd8G4.png) 301 | 302 | ```swift 303 | let disposeBag = DisposeBag() 304 | let subject = BehaviorSubject(value: "🔴") 305 | 306 | subject.addObserver("1").disposed(by: disposeBag) 307 | //Subscription: 1 Event: 🔴 308 | subject.onNext("🐶") 309 | //Subscription: 1 Event: 🐶 310 | subject.onNext("🐱") 311 | //Subscription: 1 Event: 🐱 312 | 313 | subject.addObserver("2").disposed(by: disposeBag) 314 | //Subscription: 2 Event: 🐱 315 | subject.onNext("🅰️") 316 | //Subscription: 1 Event: 🅰️ 317 | //Subscription: 2 Event: 🅰️ 318 | subject.onNext("🅱️") 319 | //Subscription: 1 Event: 🅱️ 320 | //Subscription: 2 Event: 🅱️ 321 | 322 | subject.addObserver("3").disposed(by: disposeBag) 323 | //Subscription: 3 Event: 🅱️ 324 | subject.onNext("🍐") 325 | //Subscription: 1 Event: 🍐 326 | //Subscription: 2 Event: 🍐 327 | //Subscription: 3 Event: 🍐 328 | subject.onNext("🍊") 329 | //Subscription: 1 Event: 🍊 330 | //Subscription: 2 Event: 🍊 331 | //Subscription: 3 Event: 🍊 332 | 333 | subject.onCompleted() 334 | //1 is completed 335 | //1 is disposed 336 | //2 is completed 337 | //2 is disposed 338 | //3 is completed 339 | //3 is disposed 340 | subject.addObserver("4").disposed(by: disposeBag) 341 | //4 is completed 342 | //4 is disposed 343 | ``` 344 | 345 | ## Variable 346 | 347 | - `BehaviorSubject` 를 wrapping한 것으로, 현재 값을 **State** 로 보유합니다. 348 | - "**Error가 발생하지 않는다**"를 보장하기 때문에 `.error` 를 사용할 수 없습니다. 349 | - 메모리 할당이 해제되었을 때, 자동적으로 complete되므로 `.completed` 를 사용할 수 없습니다. 350 | 351 | ```swift 352 | var disposeBag = DisposeBag() 353 | let subject = Variable("🔴") 354 | 355 | subject.asObservable().addObserver("1").disposed(by: disposeBag) 356 | //Subscription: 1 Event: 🔴 357 | subject.value = "🐶" 358 | //Subscription: 1 Event: 🐶 359 | subject.value = "🐱" 360 | //Subscription: 1 Event: 🐱 361 | 362 | subject.asObservable().addObserver("2").disposed(by: disposeBag) 363 | //Subscription: 2 Event: 🐱 364 | subject.value = "🅰️" 365 | //Subscription: 1 Event: 🅰️ 366 | //Subscription: 2 Event: 🅰️ 367 | subject.value = "🅱️" 368 | //Subscription: 1 Event: 🅱️ 369 | //Subscription: 2 Event: 🅱️ 370 | 371 | subject.asObservable().addObserver("3").disposed(by: disposeBag) 372 | //Subscription: 3 Event: 🅱️ 373 | subject.value = "🍐" 374 | //Subscription: 1 Event: 🍐 375 | //Subscription: 2 Event: 🍐 376 | //Subscription: 3 Event: 🍐 377 | subject.value = "🍊" 378 | //Subscription: 1 Event: 🍊 379 | //Subscription: 2 Event: 🍊 380 | //Subscription: 3 Event: 🍊z 381 | ``` 382 | 383 | ## Reference 384 | 385 | - [RxSwift Documentation](https://github.com/ReactiveX/RxSwift/blob/master/Documentation) 386 | - [ReactiveX](http://reactivex.io/) 387 | - [RxSwift - fimuxd](https://github.com/fimuxd/RxSwift) 388 | - [Learn & Master ⚔️ the Basics of RxSwift in 10 Minutes - Sebastian Boldt](https://medium.com/ios-os-x-development/learn-and-master-%EF%B8%8F-the-basics-of-rxswift-in-10-minutes-818ea6e0a05b) 389 | -------------------------------------------------------------------------------- /OOP/SOLID.md: -------------------------------------------------------------------------------- 1 | # S.O.L.I.D 2 | 3 | ## Table of Contents 4 | 5 | - [Bad Design](#Bad-Design) 6 | - [Rigidity](#Rigidity) 7 | - [Fragility](#Fragility) 8 | - [Immobility](#Immobility) 9 | 10 | - [The Single Responsibility Principle](#The-Single-Responsibility-Principle) 11 | - [Responsibility](#Responsibility) 12 | - [SRP Example](#SRP-Example) 13 | - [The Open Closed Principle](#The-Open-Closed-Principle) 14 | - [Primary attributes](#Primary-attributes) 15 | - [Abstraction](#Abstraction) 16 | - [Strategic Closure](#Strategic-Closure) 17 | - [OCP Example](#OCP-Example) 18 | - [The Liskov Substitution Principle](#The-Liskov-Substitution-Principle) 19 | - [Design by Contract](#Design-by-Contract) 20 | - [LSP Example](#LSP-Example) 21 | - [The Interface Segregation Principle](#The-Interface-Segregation-Principle) 22 | - [Example](#Example) 23 | - [The Dependency Inversion Principle](#The-Dependency-Inversion-Principle) 24 | - [Inversion](#Inversion) 25 | - [Layering](#Layering) 26 | 27 | ## Bad Design 28 | 29 | ### Rigidity 30 | 31 | - 경직, 딱딱함 32 | - 변화들이 시스템의 많은 부분들에 영향을 끼치기 때문에 바꾸기 어렵습니다. 33 | 34 | ### Fragility 35 | 36 | - 취약성 37 | - 변경을 주었을 때, 시스템의 예상치 못한 부분이 손상됩니다. 38 | 39 | ### Immobility 40 | 41 | - 고정 42 | - 현재의 어플리케이션에서 분리될 수 없기 때문에, 다른 어플리케이션에서 재사용하기 쉽지 않습니다. 43 | 44 | ## The Single Responsibility Principle 45 | 46 | ### "A class should have only one reason to change" 47 | 48 | > **"한 클래스는 하나의 책임만 갖는다."** 49 | 50 | - 책임을 분리함으로써 코드의 가독성 향상, 유지보수가 용이해집니다. 51 | - 또한, 다른 원리들을 적용하는 기초가 됩니다. 52 | 53 | #### Responsibility 54 | 55 | - SRP에서는 "변경될 이유(reason to change)"가 될 **책임(responsibility)**을 규정합니다. 56 | 57 | > "A class should have only one reason to change"를 직역하면 58 | > 59 | > "한 클래스는 오직 하나의 변경될 이유를 갖습니다." 입니다. 60 | > 61 | > 이는 곧, **한 클래스는 오직 하나의 책임을 갖는다.** 로 해석할 수 있습니다. 62 | 63 | - 만약 클래스를 변경하는 요인이 하나 이상이라면, 이것은 즉 클래스가 하나 이상의 책임이 있는 것입니다. 64 | 65 | #### SRP Example 66 | 67 | 밑의 10가지 기능들은 Car의 기능들입니다. 68 | 69 | ```swift 70 | //😨 Violate SRP 71 | class Car { 72 | func accelerate() {} 73 | func brake() {} 74 | func turnLeft() {} 75 | func turnRight() {} 76 | func addFuel() {} 77 | func changeOil() {} 78 | func rotateTires() {} 79 | func adjustDriverSeat() {} 80 | func turnOnAC() {} 81 | func playCD() {} 82 | } 83 | ``` 84 | 85 | 하지만, 3가지의 책임이 존재하고 있습니다. 86 | 87 | - 운전에 대한 책임 88 | - `accelerate()`, `brake()`, `turnLeft()`, `turnRight()` 89 | - 유지 관리에 대한 책임 90 | - `addFuel()`, `changeOil()`, `rotateTires()` 91 | - 편의 제공에 대한 책임 92 | - `adjustDriverSeat()`, `turnOnAC()`, `playCD()` 93 | 94 | ```swift 95 | //운전을 담당하는 Driving 96 | class Driving { 97 | func accelerate() {} 98 | func brake() {} 99 | func turnLeft() {} 100 | func turnRight() {} 101 | } 102 | //유지관리를 담당하는 Maintenance 103 | class Maintenance { 104 | func addFuel() {} 105 | func changeOil() {} 106 | func rotateTires() {} 107 | } 108 | //편의 제공을 담당하는 Convenience 109 | class Convenience { 110 | func adjustDriverSeat() {} 111 | func turnOnAC() {} 112 | func playCD() {} 113 | } 114 | //3가지의 책임들을 decouple할 수 있습니다. 115 | //👍 Conform SRP 116 | class Car { 117 | let driving: Driving 118 | let maintenance: Maintenance 119 | let convenience: Convenience 120 | 121 | init(driving: Driving, 122 | maintenance: Maintenance, 123 | convenience: Convenience) { 124 | self.driving = driving 125 | self.maintenance = maintenance 126 | self.convenience = convenience 127 | } 128 | 129 | func accelerate() { driving.accelerate() } 130 | func brake() { driving.brake() } 131 | func turnLeft() { driving.turnLeft() } 132 | func turnRight() { driving.turnRight() } 133 | 134 | func addFuel() { maintenance.addFuel() } 135 | func changeOil() { maintenance.changeOil() } 136 | func rotateTires() { maintenance.rotateTires() } 137 | 138 | func adjustDriverSeat() { convenience.adjustDriverSeat() } 139 | func turnOnAC() { convenience.turnOnAC() } 140 | func playCD() { convenience.playCD() } 141 | } 142 | ``` 143 | 144 | ## The Open Closed Principle 145 | 146 | ### "You should be able to extend a classes behavior, without modifying it." 147 | 148 | > class를 변경하지 않고 행동을 확장할 수 있어야합니다. 149 | > 150 | > ##### 소프트웨어 요소는 확장에는 열려 있으나 변경에는 닫혀 있어야한다. 151 | 152 | #### Primary attributes 153 | 154 | - **확장에는 열려있다**.(Open for Extension) 155 | - 모듈의 행동은 확장되어질 수 있습니다. 156 | - **변경에는 닫혀있다.**(Closed for Modification) 157 | - 모듈의 소스 코드는 침범되지 않는다. 158 | - 모듈에 대한 소스 코드를 변경하는 것은 아무도 허용되지 않는다. 159 | 160 | #### Abstraction 161 | 162 | - 추상화는 고정적이지만 행동의 제약이 없는 그룹을 나타냅니다. 163 | - 모듈은 고정적인 추상화를 의존하기 때문에, 수정에 대하여 닫혀 있을 수 있습니다. 164 | - 그러나, 모듈은 새로운 추상화에 대한 파생물을 만들어 냄으로써 확장될 수 있습니다. 165 | 166 | #### Strategic Closure 167 | 168 | - 명시적인 폐쇄를 위하여 추상화를 사용합니다. 169 | - 폐쇄를 달성하기 위하여 "Data Driven' 접근법을 이용합니다. 170 | 171 | #### OCP Example 172 | 173 | `MortorCycle` 과 SRP에 등장한 `Car`를 가속화하는 함수가 존재합니다. 174 | 175 | ```swift 176 | func accelateAll(cars: [Car]) { 177 | cars.forEach{ $0.accelerate() } 178 | } 179 | 180 | class MotorCycle { 181 | let driving = Driving() 182 | let maintenance = Maintenance() 183 | 184 | func accelerate() { driving.accelerate() } 185 | func brake() { driving.brake() } 186 | func turnLeft() { driving.turnLeft() } 187 | func turnRight() { driving.turnRight() } 188 | 189 | func addFuel() { maintenance.addFuel() } 190 | func changeOil() { maintenance.changeOil() } 191 | func rotateTires() { maintenance.rotateTires() } 192 | } 193 | 194 | //😨 Violate OCP 195 | func accelateAll(cars: [Car], motorCycles: [MotorCycle]) { 196 | cars.forEach { $0.accelerate() } 197 | motorCycles.forEach { $0.accelerate() } 198 | } 199 | ``` 200 | 201 | `accelateAll(cars: )` 함수는 또다른 탈것(e.g. Bike)이 존재할 경우, 존재하는 함수 자체를 변경시켜야하기 때문에 변경에 닫혀있지 않습니다. 즉, OCP를 만족시키지 못합니다. 202 | 203 | OCP를 만족시키기 위하여 추상적인 `Drivable` 를 생성하고, `Car` 와 `MotorCycle` 을 abstract의 파생물로 만들어줍니다. 204 | 205 | ``` swift 206 | protocol Drivable { 207 | func accelerate() 208 | func brake() 209 | func turnLeft() 210 | func turnRight() 211 | } 212 | 213 | class Car: Drivable { 214 | ... 215 | } 216 | 217 | class MotorCycle: Drivable { 218 | ... 219 | } 220 | 221 | //👍 Conform OCP 222 | func accelateAll(drivables: [Drivable]) { 223 | drivables.forEach { $0.accelerate() } 224 | } 225 | ``` 226 | 227 | `Drivable` 를 이용하여 모듈이 확장될 수 있으며, 변경에 대하여 폐쇄적이게 되었습니다. 228 | 229 | ## The Liskov Substitution Principle 230 | 231 | ### "Derived classes must be substitutable for their base classes." 232 | 233 | > 파생 클래스는 Base 클래스로 치환될 수 있어야합니다. 234 | > 235 | > **프로그램의 객체는 프로그램의 정확성을 깨뜨리지 않으면서 하위 타입의 인스턴스로 바꿀 수 있어야 한다.** 236 | 237 | #### Design by Contract 238 | 239 | - Class의 Method는 **Precondition** 와 **Postcondition** 을 선언합니다. 240 | - Method를 수행하려면 Precondition이 참이어야합니다. 241 | - Precondition이 참이라면, Method는 Postcondition이 참임을 보증합니다. 242 | - 파생물에서 루틴을 재정의할 때, Precondition을 더 약한 것에 의해서만 바꿀 수 있고 243 | Postcondition을 더 강한 것에 의해서만 바꿀 수 있습니다. 244 | - Base class interface 를 통하여 객체를 사용할 때, 사용자는 오직 Base Class의 Precondition과 Postcondition에 대해서만 압니다. 245 | - 그러므로, **파생된 객체는 사용자가 Base Class가 요구하는 것보다 더욱 강한 Precondition을 따를 것으로 예상해서는 안됩니다**. 246 | - 또한, **파생된 객체는 Base Class의 Postcondition들을 모두 부합해야합니다**. 247 | - 즉, 모든 Behavior과 Output은 Base Class에서 설정된 어떤 제약조건들에도 위반되지 않아야합니다. 248 | - 기본 클래스의 사용자는 파생 클래스의 출력에 대하여 혼동되지 않아야합니다. 249 | 250 | #### LSP Example 251 | 252 | - Precondition 253 | 254 | ```swift 255 | class Handler { 256 | func save(string: String) { 257 | // Save string in the Cloud 258 | } 259 | } 260 | 261 | class FilteredHandler: Handler { 262 | override func save(string: String) { 263 | //😨 Violate LSP - Precondition 264 | guard string.count > 5 else { return } // Precondition 265 | super.save(string: string) 266 | } 267 | } 268 | ``` 269 | 270 | Base Class인 `Handler` 를 사용하는 Client는 파생 Class인 `FilteredHandler` 와 동일한 로직을 에상하고 사용할 것입니다. 271 | 272 | 하지만, 파생 Class인 `FilteredHandler` 에는 Base Class인 `Handler` 보다 더욱 강한 Precondition을 따르고 있어, LSP를 위반하고 있습니다. 273 | 274 | ```swift 275 | class Handler { 276 | func save(string: String, minChars: Int = 0) { 277 | guard string.characters.count >= minChars else { return } 278 | // Save string in the Cloud 279 | } 280 | } 281 | 282 | class FilteredHandler: Handler { 283 | override func save(string: String) { 284 | super.save(string: string) 285 | } 286 | } 287 | ``` 288 | 289 | Base Class인 `Handler` 에 Precondition을 설정하여줍니다. 290 | 291 | - Postcondition 292 | 293 | 정사각형 `Square` 는 직사각형 `Rectangle` 의 일종으로 볼 수 있으며, `Rectangle` 이 될 수 있습니다. 294 | 295 | 하지만, `Square` 의 **행동**이 `Rectangle` 의 **행동**과 일치하지 않기 때문에, `Square` 는 명백히 `Rectangle` 이 아닙니다. 296 | 297 | 행동적으로, `Square` 은 `Rectangle` 이 아니며, 소프트웨어에서 중요한 것은 **행동(Behavior)** 입니다 298 | 299 | ```swift 300 | class Rectangle { 301 | var width: Float = 0 302 | var length: Float = 0 303 | 304 | var area: Float { 305 | return width * length 306 | } 307 | } 308 | 309 | class Square: Rectangle { 310 | override var width: Float { 311 | didSet { 312 | //😨 Violate LSP - Postcondition 313 | length = width 314 | } 315 | } 316 | } 317 | ``` 318 | 319 | 파생 Class인 `Square` 는 Base Class `Rectangle` 의 모든 Postcondition을 부합하지 않기 때문에 LSP를 위반하였습니다. 320 | 321 | ```swift 322 | func printArea(of rectangle: Rectangle) { 323 | rectangle.length = 5 324 | rectangle.width = 2 325 | print(rectangle.area) 326 | } 327 | 328 | let rectangle = Rectangle() 329 | printArea(of: rectangle) // 10 330 | // ------------------------------- 331 | let square = Square() 332 | printArea(of: square) // 4 333 | ``` 334 | 335 | `Square` 에 대하여 `printArea(of: )` 를 수행하면, `width * length` 의 결과 값이 10이 출력되어야하지만 Postcondition이 추가되어 `lengt` 를 `width` 의 값으로 변경해버려 4가 출력됩니다. 336 | 337 | `protocol` 을 이용하여 LCP를 따릅니다. 338 | 339 | ```swift 340 | //👍 Conform OCP 341 | protocol Polygon { 342 | var area: Float { get } 343 | } 344 | 345 | class Rectangle: Polygon { 346 | 347 | private let width: Float 348 | private let length: Float 349 | 350 | init(width: Float, length: Float) { 351 | self.width = width 352 | self.length = length 353 | } 354 | 355 | var area: Float { 356 | return width * length 357 | } 358 | } 359 | 360 | class Square: Polygon { 361 | 362 | private let side: Float 363 | 364 | init(side: Float) { 365 | self.side = side 366 | } 367 | 368 | var area: Float { 369 | return pow(side, 2) 370 | } 371 | } 372 | ``` 373 | 374 | ## The Interface Segregation Principle 375 | 376 | ### "Make fine grained interfaces that are client specific." 377 | 378 | > 클라이언트별로 세분화된 interface를 만듭니다. 379 | > 380 | > **특정 클라이언트를 위한 인터페이스 여러 개가 범용 인터페이스 하나보다 낫다.** 381 | 382 | - "Fat" 인터페이스를 갖는 클래스는 인터페이스가 응집도가 낮은 클래스입니다. 383 | - 클래스의 인터페이스는 맴버함수의 그룹으로 나눌 수 있습니다. 384 | 385 | #### Example 386 | 387 | - Fat interface (Protocol) 388 | 389 | ```swift 390 | protocol GestureProtocol { 391 | func didTap() 392 | func didDoubleTap() 393 | func didLongPress() 394 | } 395 | 396 | class Button: GestureProtocol { 397 | func didTap() { 398 | // send tap action 399 | } 400 | 401 | func didDoubleTap() { 402 | // send double tap action 403 | } 404 | 405 | func didLongPress() { 406 | // send long press action 407 | } 408 | } 409 | ``` 410 | 411 | 만일, `Button` 이 `didTap()` 만이 필요하다면, 나머지의 것들은 필요가 없습니다. 412 | 413 | 이럴 경우, 여러 개의 protocol로 쪼개어 줍니다. 414 | 415 | ```swift 416 | protocol TapProtocol { 417 | func didTap() 418 | } 419 | 420 | protocol DoubleTapProtocol { 421 | func didDoubleTap() 422 | } 423 | 424 | protocol LongPressProtocol { 425 | func didLongPress() 426 | } 427 | 428 | class SuperButton: TapProtocol, DoubleTapProtocol, LongPressProtocol { 429 | func didTap() { 430 | // send tap action 431 | } 432 | 433 | func didDoubleTap() { 434 | // send double tap action 435 | } 436 | 437 | func didLongPress() { 438 | // send long press action 439 | } 440 | } 441 | 442 | class PoorButton: TapProtocol { 443 | func didTap() { 444 | // send tap action 445 | } 446 | } 447 | ``` 448 | 449 | - Fat interface (Class) 450 | 451 | ```swift 452 | class Video { 453 | var title: String = "My Video" 454 | var description: String = "This is a beautiful video" 455 | var author: String = "Marco Santarossa" 456 | var url: String = "https://marcosantadev.com/my_video" 457 | var duration: Int = 60 458 | var created: Date = Date() 459 | var update: Date = Date() 460 | } 461 | 462 | func play(video: Video) { 463 | // load the player UI 464 | // load the content at video.url 465 | // add video.title to the player UI title 466 | // update the player scrubber with video.duration 467 | } 468 | ``` 469 | 470 | `Video` 클래스를 구현하였고, `play(video:)` 를 이용하여 재생하고자 합니다. 이 때, `play(video:)` 는 `title`, `description`, `duration` 정보만이 필요합니다. Protocol을 이용하여 이 구조 또한 나누어줍니다. 471 | 472 | ```swift 473 | protocol Playable { 474 | var title: String { get } 475 | var url: String { get } 476 | var duration: Int { get } 477 | } 478 | 479 | class Video: Playable { 480 | var title: String = "My Video" 481 | var description: String = "This is a beautiful video" 482 | var author: String = "Marco Santarossa" 483 | var url: String = "https://marcosantadev.com/my_video" 484 | var duration: Int = 60 485 | var created: Date = Date() 486 | var update: Date = Date() 487 | } 488 | 489 | func play(video: Playable) { 490 | // load the player UI 491 | // load the content at video.url 492 | // add video.title to the player UI title 493 | // update 494 | } 495 | ``` 496 | 497 | ## The Dependency Inversion Principle 498 | 499 | ### "Depend on abstractions, not on concretions." 500 | 501 | > ##### 추상화에 의존해야지 구체화에 의존하면 안된다. 502 | 503 | - 상위 계층의 모듈은 하위 계층의 모듈에 의존하면 안된다. 상위 계층이던 하위 계층이던 추상화에 의존해야합니다. 504 | - 추상화는 세부사항에 의존해서는 안된다. 세부사항이 추상화에 의존해야합니다. 505 | 506 | ### Inversion 507 | 508 | - 전통적인 소프트웨어 개발방법은 상위 계층이 하위 계층에 의존하고, 추상화가 세부사항에 의존하였습니다. 509 | - 잘 설계된 객체 지향 프로그램의 종속 구조는 일반적으로 전통적인 절차적 방법에서 비롯되는 종속 구조에 관해서 "**역전(Inversion)**"됩니다. 510 | - 잘 설계된 객체 지향 프로그램은 상위 계층이 하위 계층에 의존하지 않고, 511 | - 세부사항이 추상화에 의존합니다. 512 | 513 | ### Layering 514 | 515 | - 잘 구조화된 객체 지향 아키텍처들은 명확하게 정의된 계층을 가지고 있으며, 각 계층은 잘 정의되고 통제된 인터페이스임에도 불구하고 일관된 서비스 집합을 제공합니다. 516 | 517 | - Dependency is transitive. 518 | 519 | > 종속성은 타동적이다 520 | 521 | #### Example 522 | 523 | ```swift 524 | class Handler { 525 | let fm = FilesystemManager() 526 | 527 | func handle(string: String) { 528 | fm.save(string: string) 529 | } 530 | } 531 | 532 | class FilesystemManager { 533 | 534 | func save(string: String) { 535 | // Open a file 536 | // Save the string in this file 537 | // Close the file 538 | } 539 | } 540 | ``` 541 | 542 | 하위 계층 모듈 `FilesystemManager` 는 다른 프로젝트에서도 재사용할 수 있습니다. 543 | 544 | 하지만, 상위 계층 모듈인 `Handler` 는 하위 계층 모듈인 `FilesysemManager` 에 의존하고 있기 때문에, 재사용되어지기 힘듭니다 545 | 546 | 이러한 경우 다른 Storage에 재사용이 불가능하기도 합니다. 547 | 548 | ```swift 549 | class Handler { 550 | 551 | let storage: Storage 552 | 553 | init(storage: Storage) { 554 | self.storage = storage 555 | } 556 | 557 | func handle(string: String) { 558 | storage.save(string: string) 559 | } 560 | } 561 | 562 | protocol Storage { 563 | 564 | func save(string: String) 565 | } 566 | 567 | class FilesystemManager: Storage { 568 | 569 | func save(string: String) { 570 | // Open a file in read-mode 571 | // Save the string in this file 572 | // Close the file 573 | } 574 | } 575 | 576 | class DatabaseManager: Storage { 577 | 578 | func save(string: String) { 579 | // Connect to the database 580 | // Execute the query to save the string in a table 581 | // Close the connection 582 | } 583 | } 584 | ``` 585 | 586 | `Storage` 프로토콜을 사용함으로써 세부사항 `FilesystemManger`, `DatabaseManager` 를 추상화에 의존하게 만들었으며 상위 계층 하위계층 모두 추상화에 의존하게 되어 의존성의 역전이 일어나게 됩니다. 587 | 588 | ## Reference 589 | 590 | - [Single Responsibility Principle](https://drive.google.com/file/d/0ByOwmqah_nuGNHEtcU5OekdDMkk/view) 591 | - [Open-Closed Principle](https://drive.google.com/file/d/0BwhCYaYDn8EgN2M5MTkwM2EtNWFkZC00ZTI3LWFjZTUtNTFhZGZiYmUzODc1/view) 592 | - [Liskov Substitution Principle](https://drive.google.com/file/d/0BwhCYaYDn8EgNzAzZjA5ZmItNjU3NS00MzQ5LTkwYjMtMDJhNDU5ZTM0MTlh/view) 593 | - [Interface Segregation Principle](https://drive.google.com/file/d/0BwhCYaYDn8EgOTViYjJhYzMtMzYxMC00MzFjLWJjMzYtOGJiMDc5N2JkYmJi/view) 594 | - [Dependency Inversion Principle](https://drive.google.com/file/d/0BwhCYaYDn8EgMjdlMWIzNGUtZTQ0NC00ZjQ5LTkwYzQtZjRhMDRlNTQ3ZGMz/view) 595 | 596 | - https://github.com/ochococo/OOD-Principles-In-Swift 597 | - https://clean-swift.com/single-responsibility-principle-for-class/ 598 | - https://marcosantadev.com/solid-principles-applied-swift/ --------------------------------------------------------------------------------