Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
Tags
- combine
- MVVM
- UICollectionView
- swiftUI
- swift documentation
- tableView
- Protocol
- rxswift
- SWIFT
- Refactoring
- 리팩토링
- Human interface guide
- ribs
- UITextView
- uitableview
- Clean Code
- Observable
- 애니메이션
- RxCocoa
- 리펙토링
- map
- 스위프트
- ios
- Xcode
- clean architecture
- 리펙터링
- 클린 코드
- uiscrollview
- collectionview
- HIG
Archives
- Today
- Total
김종권의 iOS 앱 개발 알아가기
[iOS - Swift] map, flatMap 이해하기, 직접 구현 방법 (+ json to model, json to dictionary) 본문
iOS 응용 (swift)
[iOS - Swift] map, flatMap 이해하기, 직접 구현 방법 (+ json to model, json to dictionary)
jake-kim 2022. 9. 6. 22:37Swift에서의 map의 역할
- map, flatMap 둘 다 클로저가 Optional일 때와 non-Optional일때의 기능이 다르므로 주의
- Optional일때의 기능 - 형변환 (flatMap은 형변환을 완료하고 unwrapping까지 수행)
- non-Optional일때의 기능 - 원소들에 하나하나씩 접근하여 변형을 주는 것
- non-Optional일때의 map 예시)
let someString = "123456"
let newString = someString
.map { String($0) + "a" }
print(newString) // ["1a", "2a", "3a", "4a", "5a", "6a"]
- non-Optional일때의 flatMap 예시)
- flatMap의 역할 - Sequence의 배열에 변형을 주는 역할 (flat - 납작하게 차원을 줄이는 역할)
- map은 각 요소(element)에 변형을 주지만, flatmap은 element에 접근하지 않고 현재 차원에만 변형을 줌
- map은 각 요소(element)에 변형을 주어 차원을 늘리지만, flatMap은 차원을 좁히는 역할
// ex1
let someString = "123456"
print(someString.map { String($0) + "a" }) // ["1a", "2a", "3a", "4a", "5a", "6a"]
print(someString.flatMap { String($0) + "a" }) // ["1", "a", "2", "a", "3", "a", "4", "a", "5", "a", "6", "a"]
// ex2
let arr = [[1,2,3], [3,4,5]]
print(arr.map { $0 + [0] }) // [[1, 2, 3, 0], [3, 4, 5, 0]]
print(arr.flatMap { $0 + [0] }) // [1, 2, 3, 0, 3, 4, 5, 0]
ex) 직접 map처럼 만든 메소드
- closure를 받아서, 해당 closure에 원래의 element값들을 하나씩 넣고 새로 생겨난 배열을 리턴
extension Sequence {
@inlinable public func myMap<T>(_ transform: (Element) -> T) -> [T] {
var result = [T]()
for e in self {
result.append(transform(e))
}
return result
}
}
ex) 직접 flatMap처럼 만든 메소드
- @inlinable로 코드 최적화
- 외부에서 클로저를 받으면 그 클로저를 element하나씩 수행하면서 새로운 배열에 담아서 리턴
- rethrows 키워드를 사용한 이유는, myFlatMap 함수에서 인자로 throws 클로저를 받는데, 이 클로저에서 예외처리를 myFlatMap에서 하지 않고 myFlatMap을 호출하는 쪽에서 직접 처리하도록 예외를 던지기 위함 (rethows 관련 글 참고)
extension Sequence {
@inlinable public func myFlatMap<T: Sequence>(
_ transform: (Element) throws -> T
) rethrows -> [T.Element] {
var result = [T.Element]()
for e in self {
result.append(contentsOf: try transform(e))
}
return result
}
}
- Optional일때의 map과 flatMap)
- Swift에서의 map, flatMap은 Sequence의 extension으로도 존재하지만, Optional의 extension으로도 존재
- Optional인 값을 map을 이용하면 차원을 늘리는게 아닌, 값의 변형이 가능
// swift에서 map, flatMap 선언부
@frozen public enum Optional<Wrapped> : ExpressibleByNilLiteral {
case none
case some(Wrapped)
public init(_ some: Wrapped)
@inlinable public func map<U>(_ transform: (Wrapped) throws -> U) rethrows -> U?
@inlinable public func flatMap<U>(_ transform: (Wrapped) throws -> U?) rethrows -> U?
}
ex) Optional일 경우 > 형변환
let someString3: String? = "123"
print(someString3.map(Int.init)) // Optional(Optional(123))
map을 이용하여 decoding해보면?
우선 map없이 decoding하는 방법에 대해 짧게 알아보면,
- decoding, encoding 용어
- decoding: 바이너리 파일(Data)을 model타입으로 변경하는 것
- encoding: model타입을 바이너리 파일(Data)로 변경하는 것
- swift에서 jsonString을 model이나 Dictionary형태로 변경하는 방법?
- jsonString > Data(utf8) > 원하는 형태로 변경
- jsonString > 모델 형태 변경
struct MyModel: Codable {
let type: String
let id: String
}
let jsonString: String = """
{
"type":"someType",
"id":"someID"
}
"""
// String -> Model
/// jsonString > Data(utf8) > model
let modelNormal = try? JSONDecoder().decode(MyModel.self, from: Data(jsonString.utf8))
print(modelNormal) // MyModel(type: "someType", id: "someID")
- jsonString > [String: Any] 형태 변경
// String -> [String: Any]
/// jsonString > Data(utf8) > [String: Any]
let dictNormal = try? JSONSerialization.jsonObject(with: Data(jsonString.utf8), options: []) as? [String:Any]
print(dictNormal) // ["id": someID, "type": someType]
Swift에서 map을 이용하여 디코딩 아이디어
- moya처럼 사용하여 디코딩되게끔하면 더욱 선언적이고 깔끔한 코드가 될 수 있지 않을까?
- * Moya의 map - RxMoya에서는 map을 디코딩할때 사용
map을 이용하여 디코딩 방법
- Sequence에 확장된 map 함수가 아닌, Optional에 확장된 map, flatMap함수를 사용하면 적절히 형변환이 가능
- jsonString타입이 Optional이면 형변환처럼 동작할테니 이점을 활용하여 decoding
ex) jsonString을 Model로 형변환
- map을 사용하면 선언형 프로그래밍이 되고, 더욱 이해하기 쉬운 코드로 표출
let jsonString: String? = """
{
"type":"someType",
"id":"someID"
}
"""
/// jsonString > Data(utf8) > model
let model = jsonString
.map(\.utf8)
.map(Data.init(_:))
.flatMap { try? JSONDecoder().decode(MyModel.self, from: $0) }
// Optional(MyModel(type: "someType", id: "someID"))
ex) jsonString을 [String: Any]로 형변환
let dict = jsonString
.map(\.utf8)
.map(Data.init(_:))
.flatMap { try? JSONSerialization.jsonObject(with: $0) as? [String: Any] }
// Optional(["type": someType, "id": someID])
cf) 마지막에 flatMap이 아닌 map을 써도 되지만, 인수로 들어가는 transform이 Optional일때의 map은 unwrapping을 해주지 않고, flatMap은 wrapping까지 해주는 점 때문에 flatMap을 사용
// flatMap대신 map을 쓴 경우: unwrapping되지 않음
let dict = jsonString
.map(\.utf8)
.map(Data.init(_:))
.map { try? JSONSerialization.jsonObject(with: $0) as? [String: Any] }
// Optional(Optional(["type": someType, "id": someID]))
* 전체 코드: https://github.com/JK0369/ExMapFlatMap
* 참고
https://github.com/Moya/Moya/blob/master/docs/Examples/Response.md
'iOS 응용 (swift)' 카테고리의 다른 글
[iOS - swift] WebSocket 사용 방법 (웹 소켓, URLSessionWebSocketTask, URLSessionWebSocketDelegate) (0) | 2022.09.10 |
---|---|
[iOS - Swift] Base64 String 사용 방법 (0) | 2022.09.09 |
[iOS - Swift] WKWebsiteDataStore 웹뷰 데이터(쿠키, 디스크, 메모리, 캐시, 초기화) 관리 방법 (0) | 2022.09.05 |
[iOS - swift] Cocoapods을 submodule로 추가해서 사용하는 방법 (:Path => '') (0) | 2022.09.02 |
[iOS - swift] WKWebView 웹뷰 디버깅 하는 방법 (Safari 사용) (0) | 2022.08.19 |
Comments