일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 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 |
- 맥
- MainScheduler
- 앱버전구하기
- iOS앱배포
- FileOwner
- 앱배포
- Swift
- iOS계산기
- 딩동말씀
- 파이썬서버
- jQuery
- Xcode
- FLASK
- 스프링
- DispatchGroup
- 계산기앱만들기
- iOS배포
- 자바스크립트
- JavaScript
- 스위프트
- Python
- Xib
- spring
- 개발기록
- 계산기앱
- AJAX
- ios
- subscript
- 웹
- customclass
- Today
- Total
개발하는 뚝딱이
[swift] 스위프트를 더 스위프트스럽게 사용하기 본문
안녕하세요, 개발하는 뚝딱이입니다! 오늘은 Swift의 유니크한 특징을 공부했습니다.
Swift로 코드를 작성하지만, Swift의 장점을 잊고 습관처럼 코딩할 때가 많습니다.
그럴 때 한 번쯤 참고하면 좋을 글입니다.
원문은 아래 링크입니다.
medium.com/geekculture/when-you-write-code-in-swift-write-code-in-swift-abdac43d44fa
fore case
[Any]나 NSOrderSet의 경우, 타입을 체크할 필요 없이 아래와 같이 사용합니다.
let array: [Any] = [object1, object2, object3]
// Instead of:
for element in array {
if let element = element as? YourClass {
// do something
}
}
// Do:
for case let element as YourClass in array {
// do something
}
enumerated()
index와 원소에 접근하기 위해선 for i in 0..<array.count를 실행하는 것보다 .enumerated()를 실행하는 것이 좋습니다.
for i in 0..<array.count를실행하는 것이 편리할 땐 당연히 쓰는 것이 좋지만, 항상 최선이 아니란 것을 기억해주세요.
let array: [Any] = [object1, object2, object3]
// Instead of:
for i in 0..<array.count {
let element = array[i]
// do something
}
// Do:
for (i, element) in array.enumerated() {
// do something
first(where:)
filter는 스위프트에서 가장 인기 있는 메서드이지만, 때론 다른 메서드가 적절합니다.
조건에 해당하는 첫 번째 원소를 가져올 땐 first(where:)을 사용할 수 있습니다.
// Instead of:
if let element = array.filter { $0.title.contains(searchString) }.first {
// do something
}
// Do:
if let element = array.first(where: { $0.title.contains(searchString) }) {
// do something
}
contains(where:)
filter 대신에 사용할 메서드입니다.
// Instead of:
if array.filter { !$0.isActive }.count > 0 {
// do something
}
// Do:
if array.contains(where: { !$0.isActive }) {
// do something
}
isEmpty
배열이 비어있는지 확인하기 위해 count대신 isEmpty를 사용해보세요
// Instead of:
if array.count == 0 {
// do something
}
// Do:
if array.isEmpty {
// do something
}
forEach
복잡하지 않은 코드를 실행할 때 사용하면 좋은 간단한 메서드입니다
// Instead of:
for element in array {
doSomething(with: element)
}
// Do:
array.forEach { doSomething(with: $0) }
map, compactMap, filter, ... 의 keyPaths
아래와 같은 경우 KeyPaths는 간단합니다. 메서드들에 체이닝을 걸 수 있는 장점도 있습니다
// Instead of:
let filteredArray = array.filter { $0.isActive }
let titles = filteredArray.map { $0.title }
// Do:
let filteredArray = array.filter(\.isActive)
let titles = filteredArray.map(\.title)
// Or even:
let titles = array
.filter(\.isActive)
.map(\.title)
guard
if문을 많이 사용하는 경우에 쓰면 좋습니다
// Instead of:
if oneCondition {
if secondCondition {
performAction()
} else {
returnError()
}
} else {
returnError()
}
// Or even:
if oneCondition, secondCondition {
performAction()
} else {
returnError()
}
// Do this:
guard oneCondition, secondCondition else {
returnError()
return
}
performAction()
view raw
defer
defer은 많이 유용합니다. defer 블럭 코드는 return이 실행된 후 호출될 것입니다
func pop() -> Value? {
defer {
head = head?.next
}
return head?.value
}
func loadSomething(completion: () -> Void) {
defer {
isLoading = false
completion()
}
isLoading = true
guard someCondition else {
return
}
doSomething()
}
Calculated properties vs. methods
메서드를 만드는 것보다, calculated variable를 사용하는 것이 더 나을 때도 있습니다. 상황에 따라 다르므로 올바른 것을 선택해야 합니다.
확실하게 연산 처리를 해야 한다면 메서드를 사용하고, 그렇지 않은 경우 calculated variable를 고려해보세요.
// Instead of:
class YourManager {
static func shared() -> YourManager {
...
}
}
let manager = YourManager.shared()
// Or:
extension Date {
func formattedString() -> String {
// convert date to a readable string
}
}
let string = Date().formattedString()
// Do this:
class YourManager {
static var shared: YourManager {
...
}
}
let manager = YourManager.shared
// And this:
extension Date {
var formattedString: String {
// convert date to a readable string
}
}
let string = Date().formattedString
가능하면 self 사용을 피하기
굳이 self를 쓸 필요가 없을 땐 오남용 하지 않는 것이 좋습니다.
self는 꼭 써야만 하는 경우나, 코드 이해에 도움이 된다면 사용하는 것을 권장합니다
class Foo {
var index: Int
var name: String
init(index: Int, name: String) {
self.index = index // you have to use `self.` here
self.name = name
}
func increment() {
index += 1 // don't use `self.index += 1` here
}
}
언래핑을 할 때 같은 이름으로 네이밍하기
// Instead of
closure() { [weak self] in
guard let strongSelf = self else { return }
strongSelf.updateUI()
}
// Or:
func update(with name: String?) {
guard let strongName = name else { return }
self.nameLabel.text = strongName
}
// Do this;
closure() { [weak self] in
guard let self = self else { return }
self.updateUI()
}
// Or this:
func updateName(_ name: String?) {
guard let name = name else { return }
self.nameLabel.text = name
}
강력한 protocol extension
protocl의 메서드를 구현할 때 extension을 사용할 수 있습니다
protocol YourProtocol {
func requiredMethod()
func optionalMethod()
}
extension YourProtocol {
func optionalMethod() {
print("Default implementation")
}
}
class Foo: YourProtocol {
func requiredMethod() {
print("requiredMethod")
}
}
class Foo2: YourProtocol {
func requiredMethod() {
print("requiredMethod")
}
func optionalMethod() {
print("Custom implementation")
}
}
let foo: YourProtocol = Foo()
foo.optionalMethod() // prints "Default implementation"
let foo2: YourProtocol = Foo2()
foo2.optionalMethod() // prints "Custom implementation"
Protocol vs. Subclassing
상속 대신에 프로토콜을 사용하는 건 어떨까요? 필요에 따라 여러 개의 프로토콜을 준수하고, 따라서 코드 구현을 더욱 쉽게 할 수 있습니다.
struct나 enum은 subclassing을 사용할 수 없지만, protocol을 준수할 수 있다는 장점도 있습니다
Structs vs. Classes
가능하면 struct를 사용하는게 좋습니다. 멀티 쓰레드 환경에서 안전하게 사용할 수 있고, 빠르고, 기본 이니셜라이저를 제공하는 등의 장점이 많습니다. struct는 value type인 반면, class는 reference type입니다. struct는 개별의 copy 값을 가지지만, class는 하나의 데이터에 참조값을 가진 다는 것을 기억해야만 합니다.
'swift' 카테고리의 다른 글
[swift] 프로답게 스위프트로 코딩하기 (0) | 2021.08.17 |
---|---|
[swift] 프로토콜 초기구현 (0) | 2020.09.28 |
[swift] 프로토콜 (0) | 2020.09.21 |
[swift] 상속 (0) | 2020.09.05 |
[swift] 서브스크립트 (0) | 2020.06.25 |