Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/cozzin/ios-study-note

๐Ÿง ๋ถ€์ŠคํŠธ์บ ํ”„ iOS ๋ฆฌ๋ทฐ์–ด ํ™œ๋™ ๋‚ด์šฉ ์ •๋ฆฌ
https://github.com/cozzin/ios-study-note

boostcamp-ios ios oop swift

Last synced: 2 days ago
JSON representation

๐Ÿง ๋ถ€์ŠคํŠธ์บ ํ”„ iOS ๋ฆฌ๋ทฐ์–ด ํ™œ๋™ ๋‚ด์šฉ ์ •๋ฆฌ

Awesome Lists containing this project

README

        

# iOS Study Note
๋ถ€์ŠคํŠธ์บ ํ”„ iOS 6๊ธฐ ๋ฆฌ๋ทฐ์–ด ์ง„ํ–‰ํ•˜๋ฉด์„œ ๋‚ด์šฉ ์—…๋ฐ์ดํŠธ ์ค‘ ์ž…๋‹ˆ๋‹ค.

# ๊ณต์‹๋ฌธ์„œ
* [API Design Guidelines](https://swift.org/documentation/api-design-guidelines/)
* [About App Development with UIKit](https://developer.apple.com/documentation/uikit/about_app_development_with_uikit)
* [App and Environment](https://developer.apple.com/documentation/uikit/app_and_environment)

# OOP
## ๋ช…๋ น-์ฟผ๋ฆฌ ๋ถ„๋ฆฌ
- `Query`: ์งˆ๋ฌธํ•ด์„œ ๋‹ต์„ ์š”์ฒญ
- `Command `: ๋ณ€๊ฒฝ์„ ์š”์ฒญ

์ฝ”๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์ž…์žฅ์—์„œ Query - Command ๊ฐ€ ๋ถ„๋ฆฌ๋˜์ง€ ์•Š๋Š”๋‹ค๋ฉด ํ˜ผ๋ž€์„ ๊ฒช์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
๋‹จ์ˆœํ•œ ์˜ˆ๋ฅผ ๋“ค์–ด๋ณด๋ฉด

```swift
func isEmpty() -> Bool {
count += 1
return myArray.isEmpty
}
```

isEmpty()๋ฅผ ํ˜ธ์ถœํ•œ ์‚ฌ์šฉ์ž๋Š” count๊ฐ€ ์ฆ๊ฐ€๋˜๋Š” ๊ฒƒ์„ ์ธ์ง€ํ•˜์ง€ ๋ชปํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
๋งŒ์•ฝ์— count๊ฐ€ ์ฆ๊ฐ€ํ•œ๋‹ค๋Š” ๊ฒƒ์„ ์—ผ๋‘ํ•ด๋‘๊ณ  ์žˆ๋”๋ผ๋„
`1. empty ์ธ๊ฐ€?` + `2. count๊ฐ€ ์ฆ๊ฐ€ํ•˜๊ฒ ๊ตฐ` ์œผ๋กœ ์ƒ๊ฐํ•ด์•ผํ•ด์„œ ์ฝ”๋“œ๋ฅผ ์ดํ•ดํ•˜๊ณ  ์œ ์ง€๋ณด์ˆ˜ ํ•˜๋Š”๋ฐ ์–ด๋ ค์›€์„ ๊ฒช์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์œ„์˜ ์ฝ”๋“œ๋ฅผ ๋‚˜๋ˆ ๋ณด๋ฉด ์•„๋ž˜์™€ ๊ฐ™์ด ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
```swift
func isEmpty() -> Bool {
return myArray.isEmpty
}

func increase() {
count += 1
}
```

์ƒํ™ฉ์— ๋”ฐ๋ผ ์ด๊ฒŒ ๋ฒˆ๊ฑฐ๋กœ์šธ ์ˆ˜๋Š” ์žˆ๋Š”๋ฐ, ์œ ์ง€๋ณด์ˆ˜์— ํฐ ๋„์›€์ด ๋œ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค.

# ๋””์ž์ธํŒจํ„ด
## ํŒฉํ† ๋ฆฌ ํŒจํ„ด
### ์‹ฌํ”Œ ํŒฉํ† ๋ฆฌ
ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ์ฃผ์–ด์ง„ type์— ๋”ฐ๋ผ ๊ฐ์ฒด๋ฅผ ๋‹ค๋ฅด๊ฒŒ ์ƒ์„ฑ
http://ufx.kr/blog/191

### ์ถ”์ƒ ํŒฉํ† ๋ฆฌ
์ƒํ™ฉ์— ๋”ฐ๋ผ ํŒฉํ† ๋ฆฌ๋ฅผ ๊ฐˆ์•„๋ผ์šฐ๋ฉด์„œ ๊ฒฐ๊ณผ๋ฌผ์„ ๋‹ค๋ฅด๊ฒŒ ์ƒ์„ฑ
https://johngrib.github.io/wiki/abstract-factory-pattern/

## Mediator

https://ko.wikipedia.org/wiki/%EC%A4%91%EC%9E%AC%EC%9E%90_%ED%8C%A8%ED%84%B4

# Architecture
## MVC
![image](https://user-images.githubusercontent.com/11647461/132593358-187369d3-0dd4-4042-9644-043a0f508eae.png)

### ์—ญํ• ๋ถ„๋ฆฌ
* Model: ๋น„์ฆˆ๋‹ˆ์Šค๋กœ์ง ์ฒ˜๋ฆฌ. ๋ฐ์ดํ„ฐ/์•Œ๊ณ ๋ฆฌ์ฆ˜/๋„คํŠธ์›Œํ‚น
* View: ๋””์Šคํ”Œ๋ ˆ์ด/์ด๋ฒคํŠธ ์บก์ณ/๋น„์ฃผ์–ผ ํ‘œํ˜„
* Controller: ์กฐํ•ฉ/๋ธ๋ฆฌ๊ฒŒ์ด์…˜/ํŠน์ดํ•œ ์ž‘์—…

### Loose Coupling ์ง€ํ–ฅ
* ๋ฉ”์„ธ์ง€๋ฅผ ๋ณด๋‚ผ ๋•Œ MVC ๊ณ„์ธต์„ ๊ฑด๋„ˆ๋„์ง€ ์•Š๊ธฐ
* ํ•˜๋‚˜์˜ ์˜ค๋ธŒ์ ํŠธ์— MVC ์—ญํ• ์„ ์„ž์ง€ ์•Š๊ธฐ
* ๋ทฐ ํด๋ž˜์Šค์—์„œ ๋ชจ๋ธ ๋ฐ์ดํ„ฐ๋ฅผ ์„ ์–ธํ•˜์ง€ ์•Š๊ธฐ

### ๋ฉ”์„ธ์ง€ ๊ด€๋ฆฌ
* ๋ชจ๋ธ๋ผ๋ฆฌ ์ง์ ‘์ ์ธ ์–‘๋ฐฉํ–ฅ ๋ฐ์ดํ„ฐ ์ „์†ก X
* ๋ชจ๋ธ์—์„œ Notify ํ•  Controller๊ฐ€ ์—ฌ๋Ÿฌ๊ฐœ์ผ ๋•Œ Key-Value Observing ์‚ฌ์šฉ

### ์ถœ์ฒ˜
* https://developer.apple.com/library/archive/documentation/General/Conceptual/DevPedia-CocoaCore/MVC.html
* ์ฝ”๋“œ์Šค์ฟผ๋“œ ํ•™์Šต์ž๋ฃŒ

## MVP
![image](https://user-images.githubusercontent.com/11647461/132594410-2750da4c-7268-4077-a1dd-e9fa277b9dbb.png)

### ์—ญํ• ๋ถ„๋ฆฌ
* Model: ๋น„์ฆˆ๋‹ˆ์Šค๋กœ์ง ์ฒ˜๋ฆฌ
* View: UIKit ์š”์†Œ๋“ค. UIView, UIViewController
* Presenter: Model, View ์‚ฌ์ด์˜ Mediator.

### MVC์™€ ๋‹ค๋ฅธ์ 
* MVC์™€๋Š” ๋‹ค๋ฅด๊ฒŒ Presenter๊ฐ€ View๋ฅผ ์ฐธ์กฐํ•˜์ง€ ์•Š๋Š”๋‹ค.
* UIView๋ฅผ Presenter๊ฐ€ ์ง์ ‘ ๋‹ค๋ฃจ์ง€ ์•Š๊ณ  Delegate๋ฅผ ํ†ตํ•ด์„œ View ์—…๋ฐ์ดํŠธ๊ฐ€ ํ•„์š”ํ•  ๋•Œ ์•Œ๋ ค์ฃผ๋Š” ๋ฐฉ๋ฒ•

### ์ถœ์ฒ˜
* https://medium.com/ios-os-x-development/ios-architecture-patterns-ecba4c38de52
* https://velog.io/@chagmn/iOS-Architecture-iOS-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98-%ED%8C%A8%ED%84%B4-2-MVP

# Swift
## Access Control
https://docs.swift.org/swift-book/LanguageGuide/AccessControl.html

## Lazy Stored Properties
view๊ฐ€ load๋œ ํ›„์— ํŠน์ • ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•ด์„œ ์‚ฌ์šฉํ•˜๊ณ  ์‹ถ์€ ๊ฒฝ์šฐ Optional Type ์‚ฌ์šฉ + viewDidLoad ์—์„œ ํ• ๋‹นํ•ด์„œ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ์Œ
```swift
final class ViewController: UIViewController {
private var model: Model?

override func viewDidLoad() {
super.viewDidLoad()
model = Model()
}

func useModel() {
model?.use()
}
}
```

`lazy var`๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด Optional Type์ด ์•„๋‹ˆ๊ณ  ๊ธฐ์กด ์ฒ˜๋Ÿผ ํ•„์š”ํ•œ ์‹œ์ ์— init ๋จ.

```swift
final class ViewController: UIViewController {
private lazy var model: Model = Model()

func useModel() {
model.use()
}
}
```

## Enum
### Associated Values
enum์— ๊ฐ’์„ ๋‹ด์•„์„œ ํ‘œํ˜„ํ•  ์ˆ˜ ์žˆ์Œ
```swift
enum Barcode {
case upc(Int, Int, Int, Int)
case qrCode(String)
}
```

ํŒจํ„ด ๋งค์นญํ•ด์„œ case๋ฅผ ๋‚˜๋ˆ„๊ณ  Associated Values๋ฅผ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ์Œ
```swift
switch productBarcode {
case .upc(let numberSystem, let manufacturer, let product, let check):
print("UPC: \(numberSystem), \(manufacturer), \(product), \(check).")
case .qrCode(let productCode):
print("QR code: \(productCode).")
}
// Prints "QR code: ABCDEFGHIJKLMNOP."
```

https://docs.swift.org/swift-book/LanguageGuide/Enumerations.html

## ๊ณ ์ฐจํ•จ์ˆ˜
* ๋งค์นญ๋˜๋Š” ์ฒซ๋ฒˆ์งธ Element ๊ฐ€์ ธ์˜ค๊ธฐ: https://developer.apple.com/documentation/swift/array/1848165-first
* ๋งค์นญ๋˜๋Š” Element ์กด์žฌ ์—ฌ๋ถ€: https://developer.apple.com/documentation/swift/array/2297359-contains

## Delegation
* ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ•œ ๊ฐ์ฒด๋ฅผ ๊ฐ™์ด ๋‹ด์•„์„œ ๋ณด๋‚ด์ฃผ๋ฉด delegate ๊ตฌํ˜„ํ•  ๋•Œ ํŽธ๋ฆฌํ•จ
* `delegate?.doSomething(self, additional:)` ์ด๋Ÿฐ์‹์œผ๋กœ ํ˜ธ์ถœํ•จ

```swift
protocol DiceGame {
var dice: Dice { get }
func play()
}
protocol DiceGameDelegate: AnyObject {
func gameDidStart(_ game: DiceGame)
func game(_ game: DiceGame, didStartNewTurnWithDiceRoll diceRoll: Int)
func gameDidEnd(_ game: DiceGame)
}
```

```swift
class SnakesAndLadders: DiceGame {
let finalSquare = 25
let dice = Dice(sides: 6, generator: LinearCongruentialGenerator())
var square = 0
var board: [Int]
init() {
board = Array(repeating: 0, count: finalSquare + 1)
board[03] = +08; board[06] = +11; board[09] = +09; board[10] = +02
board[14] = -10; board[19] = -11; board[22] = -02; board[24] = -08
}
weak var delegate: DiceGameDelegate?
func play() {
square = 0
delegate?.gameDidStart(self)
gameLoop: while square != finalSquare {
let diceRoll = dice.roll()
delegate?.game(self, didStartNewTurnWithDiceRoll: diceRoll)
switch square + diceRoll {
case finalSquare:
break gameLoop
case let newSquare where newSquare > finalSquare:
continue gameLoop
default:
square += diceRoll
square += board[square]
}
}
delegate?.gameDidEnd(self)
}
}
```
https://docs.swift.org/swift-book/LanguageGuide/Protocols.html

## Naming

Type, Procotocl์€ UpperCamelCase, ๋‚˜๋จธ์ง€๋Š” lowerCamelCase
![image](https://user-images.githubusercontent.com/11647461/135270908-6eacd780-6f81-4a24-ae14-da9a5c4c57fd.png)
https://swift.org/documentation/api-design-guidelines/

## Weak, Unowned

https://docs.swift.org/swift-book/LanguageGuide/AutomaticReferenceCounting.html

# Foundation
## NotificationCenter
### [addObserver(_:selector:name:object:)](https://developer.apple.com/documentation/foundation/notificationcenter/1415360-addobserver)
object ํŒŒ๋ผ๋ฏธํ„ฐ์˜ ์—ญํ• : ํŠน์ • ๊ฐ์ฒด๋กœ๋ถ€ํ„ฐ ๋ฐœ์ƒํ•œ ๋…ธํ‹ฐ๋งŒ ํ•„ํ„ฐํ•ด์„œ ์ˆ˜์‹ . ๋งŒ์•ฝ nil ์ด๋ฉด ํ•„ํ„ฐ ์—†์ด ์ˆ˜์‹ . ํ•„ํ„ฐ๋ง์ด ํ•„์š” ์—†์œผ๋ฉด nil๋กœ ์‚ฌ์šฉ ๊ฐ€๋Šฅ.

> `anObject`
The object that sends notifications to the observer. Specify a notification sender to deliver only notifications from this sender.
>
>When nil, the notification center doesnโ€™t use sender names as criteria for delivery.

### [post(name:object:userInfo:)](https://developer.apple.com/documentation/foundation/notificationcenter/1410608-post)
object ํŒŒ๋ผ๋ฏธํ„ฐ์˜ ์—ญํ• : ๋…ธํ‹ฐ๋ฅผ ๋ฐœ์ƒ์‹œํ‚จ ๊ฐ์ฒด๋ฅผ ์ „๋‹ฌ. (sender ๊ฐœ๋…). ์ˆ˜์‹ ํ•˜๋Š” ์ชฝ์—์„œ ๊ตฌ๋ถ„์ด ํ•„์š” ์—†์œผ๋ฉด nil ์ „๋‹ฌํ•ด๋„ ์ •์ƒ์ž‘๋™ํ•จ.

> `anObject`
The object posting the notification.

### object ์ „๋‹ฌํ•˜๋Š” ์˜ˆ์ œ

```swift
extension NSNotification.Name {
static let modelDidUpdateTitle: NSNotification.Name = .init("didUpdateTitle")
}
```

```swift
class ViewController: UIViewController {

private lazy var model: Model = Model()

override func viewDidLoad() {
NotificationCenter.default.addObserver(self, selector: #selector(didUpdateTitle(_:)), name: .modelDidUpdateTitle, object: model)
}

@objc private func didUpdateTitle(_ notification: Notification) {
guard let updatedTitle = notification.userInfo?["updatedTitle"] as? String else {
return
}
// ์ถ”๊ฐ€ ์ž‘์—… ...
}

private func updateTitle() {
model.update(title: "testTitle")
}
}
```

```swift
final class Model {
private var title: String = ""

func update(title: String) {
self.title = title
NotificationCenter.default.post(name: .modelDidUpdateTitle, object: nil, userInfo: ["updatedTitle": title])
}
}
```

### object์— struct๋ฅผ ๋„ฃ์œผ๋ฉด ์ž‘๋™ํ•˜์ง€ ์•Š์Œ
- ์ด์œ : https://stackoverflow.com/a/56826646
- objective-c ์‹œ์ ˆ์— ๋งŒ๋“ค์–ด์ง„ API ์ธ๋ฐ `id` ํƒ€์ž…์„ Swift๋กœ ๊ฐ€์ ธ์˜ค๋‹ค๋ณด๋‹ˆ `Any`๋กœ ํฌํŒ…๋จ.
- ์‹ค์ œ๋กœ๋Š” `class`๋ฅผ ์ „๋‹ฌํ•ด์ค˜์•ผํ•จ

# UIKit
## UITouch
* ํ„ฐ์น˜ํ•œ View ๊ฐ€์ ธ์˜ค๊ธฐ: https://developer.apple.com/documentation/uikit/uitouch/1618109-view

## UIGestureRecognizer
* [UIGestureRecognizerDelegate](https://developer.apple.com/documentation/uikit/uigesturerecognizerdelegate) ํ†ตํ•ด์„œ ์ด๋ฒคํŠธ ์กฐ์ž‘ ๊ฐ€๋Šฅ

## IBInspectable

IBDesignable + IBInspectable ์‚ฌ์šฉํ•˜๋ฉด ์ธํ„ฐํŽ˜์ด์Šค ๋นŒ๋”์—์„œ Layer ์†์„ฑ๋„ ๊ฐ„ํŽธํ•˜๊ฒŒ ๋ณ€๊ฒฝ ๊ฐ€๋Šฅ

![image](https://user-images.githubusercontent.com/11647461/132595579-bdb4d92a-96e9-4564-8cf8-1e5cbfd5b286.png)

```Swift
import UIKit

@IBDesignable extension UIView {
@IBInspectable var borderColor: UIColor? {
set {
layer.borderColor = newValue?.cgColor
}
get {
guard let color = layer.borderColor else {
return nil
}
return UIColor(cgColor: color)
}
}
@IBInspectable var borderWidth: CGFloat {
set {
layer.borderWidth = newValue
}
get {
return layer.borderWidth
}
}
@IBInspectable var cornerRadius: CGFloat {
set {
layer.cornerRadius = newValue
clipsToBounds = newValue > 0
}
get {
return layer.cornerRadius
}
}
}
```

https://stackoverflow.com/a/35372610

## UIGraphicsImageRenderer
* ์ด๋ฏธ์ง€๋ฅผ ์ง์ ‘ ๊ทธ๋ฆด ๋•Œ `UIGraphicsBeginImageContextWithOptions` / `UIGraphicsEndImageContext`๋กœ Context๋ฅผ ์—ด๊ณ  ๋‹ซ์•„์ค˜์•ผ ํ•จ

```swift
let rect = CGRect(origin: .zero, size: size)
UIGraphicsBeginImageContextWithOptions(rect.size, false, 0.0)
UIColor.darkGray.setFill()
UIRectFill(CGRect(x: 1, y: 1, width: 140, height: 140))
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
```

* `UIGraphicsImageRenderer`๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ํด๋กœ์ € ์•ˆ์—์„œ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜๊ณ  ๋”ฐ๋กœ ์—ด๊ณ  ๋‹ซ๋Š” ํ•จ์ˆ˜ ํ˜ธ์ถœ์ด ํ•„์š”์—†๋Š” ์žฅ์ ์ด ์žˆ์Œ: ๊ฐ€๋…์„ฑ ์ฆ๊ฐ€, ์‹ค์ˆ˜ํ•˜์ง€ ์•Š์„ ์ˆ˜ ์žˆ์Œ

```swift
let renderer = UIGraphicsImageRenderer(size: CGSize(width: 200, height: 200))
let image = renderer.image { (context) in
UIColor.darkGray.setFill()
context.fill(CGRect(x: 1, y: 1, width: 140, height: 140))
}
```

https://developer.apple.com/documentation/uikit/uigraphicsimagerenderer

# Networking

## Http ํ†ต์‹ 
* [NSAllowsArbitraryLoads](https://developer.apple.com/documentation/bundleresources/information_property_list/nsapptransportsecurity/nsallowsarbitraryloads): ๋ชจ๋“  ๋„๋ฉ”์ธ HTTP ํ†ต์‹  ํ—ˆ์šฉ
* [NSExceptionDomains](https://developer.apple.com/documentation/bundleresources/information_property_list/nsapptransportsecurity/nsexceptiondomains): ํŠน์ • ๋„๋ฉ”์ธ๋งŒ HTTP ํ†ต์‹  ํ—ˆ์šฉ

## Throttle
- ์—„๋ฐ€ํžˆ ๋งํ•˜๋ฉด ๋„คํŠธ์›Œํฌ๋Š” ์•„๋‹ˆ์ง€๋งŒ ๋„คํŠธ์›Œํฌ ๊ธฐ๋Šฅ๊ณผ ๋ฐ€์ ‘ํ•ด์„œ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ์Œ
https://eunjin3786.tistory.com/80

# Xcode
## Debugging

- https://github.com/sujinnaljin/Improving_Productivity