-
Notifications
You must be signed in to change notification settings - Fork 3
iOS 설계 및 구현
TTOzzi edited this page Dec 20, 2020
·
2 revisions
- 네트워크 통신 담당
- URLSession의 dataTaskPublisher를 통해 data task를 publisher로 감싸서 받아온 <(Data, URLResponse), Error>를 <Data, NetworkError> 형태로 반환
- 네트워킹 도중 발생하는 error에 대한 예외 처리를 위해 Custom Error 타입인 NetworkError 구현
enum NetworkError: Error {
case invalidURL
case unsuccessfulResponse
case unknownError(message: String)
}
- NetworkService를 통해 요청을 보내고 응답을 받아 ViewModel이 필요로 하는 형태로 가공
- 네트워크 요청을 보낼 구체적인 EndPoint 지정과 요청을 보내기 위한 encoding 및 응답 decoding을 통해 가공이 일어남
- UseCase의 요청, 응답에 대한 error 또는 데이터를 가공하는 과정에서 error 발생 시 예외처리의 구분을 위해 Custom Error 타입인 UseCaseError 구현
enum UseCaseError: Error {
case networkingError // NetworkService에서 네트워크 통신 중 에러가 발생했을 경우
case decodingError // 받아 온 응답 decoding 실패 시
case encodingError // Post 요청 등 body에 실어보낼 데이터 encoding 실패 시
case unknownError // 그 밖의 error 발생 시
}
- View에서 발생한 이벤트를 받아 그에 맞게 UseCase를 활용해 요청을 보내고, 응답을 받아 View가 필요로 하는 형태로 가공
- ViewModel은 SwiftUI에서 제공하는 ObservableObject 프로토콜을 채택해 View가 필요로 하는 데이터의 형태를 State로 업데이트
- View에서는 ViewModel을 @StateObject / @ObservedObject로 가지면서 ViewModel의 State를 관찰
- View는 ViewModel의 State의 변화에 따라 스스로를 다시 그리도록 하여 View-ViewModel 간 Binding이 이루어짐
final class TrackViewModel: ObservableObject {
enum Input {
case like // view에서 발생하는 이벤트
}
struct State {
var track: TrackInfo // view를 그리는데 필요한 값
}
@Published var state: State // State의 값에 변화가 생겼을 경우 ObservableObject의 objectWillChange를 호출
func send(_ input: Input) { // view에서 이벤트가 발생하면 send 메서드를 통해 이벤트를 뷰모델로 전달
switch input { // view에서 발생한 이벤트에 맞게 동작
case .like:
state.track.liked == 0 ? like() : cancelLike()
}
}
}
- 이벤트가 발생하면 ViewModel에 이벤트를 전달
- ViewModel과 Binding 되어 ViewModel의 State에 맞게 화면을 그림
- 예시 : 특정 곡의 '좋아요' 버튼이 눌렸을 때
좋아요 버튼 누르기 전 | 좋아요 버튼 누른 후 |
---|---|
struct PlayerControls: View {
@ObservedObject private var viewModel: TrackViewModel
...
let track: TrackInfo = viewModel.state.track
Button {
viewModel.send(.like) // 좋아요 이벤트가 발생했음을 viewModel에게 전달
} label: { // state.track의 liked 속성에 따라 하트 상태 변경
Image(systemName: track.liked ? "heart.fill" : "heart"
}
}
- 추상화된 이벤트 로그 타입(EventLogType), 이벤트를 전송/보관하는 객체의 구조(Local/ServerStorageType)를 프로토콜로 제공
- 전체 이벤트를 관리하는 EventLogger를 구체 타입으로 제공
- Reachability를 활용해 기기의 네트워크 연결 상태를 확인
- 테스트 가능한 구조를 만들기 위해 ReachabilityObserver를
ReachabilityObserving
프로토콜로 추상화
- 생성 시 전체 이벤트를 관리하게 될 객체 (구체 타입으로 제공 됨)
- 생성 시 로컬 저장 객체, 이벤트 서버 전송 객체를 주입 받음
- ReachabilityObserver를 활용해 기기의 네트워크 연결 상태를 확인
- 네트워크 연결상태에 따라 로그를 기기에 저장, 서버에 전송
open class EventLogger: EventLoggerType {
public let local: LocalStorageType?
public let server: ServerStorageType?
private let reachability: ReachabilityObserving
private var networkState: Connection = .unavailable
public init(local: LocalStorageType?,
server: ServerStorageType?,
reachability: ReachabilityObserving) { // 테스트 가능한 구조를 만들기 위해 의존성을 가지는 객체들을 추상화하여 주입
self.local = local
self.server = server
self.reachability = reachability
reachability.setUpNotify { [weak self] in
self?.networkState = $0
if $0 == .wifi || $0 == .cellular {
local?.sendToServer() // 기기의 네트워크 연결이 활성화되면 기기에 저장했던 로그를 서버로 전송
}
}
server?.setFailureHandler { event in
local?.save(event) // 서버 전송 중 실패했을 경우 기기에 로그를 저장
}
}
public func send<T: EventLogType>(_ event: T) {
switch networkState { // 네트워크 연결 상태에 따라 로그를 기기에 저장, 서버에 전송
case .cellular, .wifi:
server?.send(event)
default:
local?.save(event)
}
}
}
- LocalStorage는 네트워크 연결 상태가 끊어졌을 때 CoreData를 사용해 로컬에 저장
- ServerStorage는 네트워크 연결 상태가 .wifi 또는 .cellular 일 때 서버에 전송
- LocalStorage와 ServerStorage 모두 추상화된 프로토콜
LocalStorageType
,ServerStorageType
으로 제공