이번에는 당겨서 새로고침을 만들었습니다.
만들어야 하는 기능의 목표에 부합하는 자료를 찾기 힘들어서 생각보다 시간을 꽤 소요했던 작업이었습니다.
목표했던 기능은 다음과 같습니다.
- 당기면 ProgressView가 보이며 네트워크 다운로드 작업이 호출됩니다.
- Coingecko의 OpenAPI의 분당 요청 제한은 10~50회 이므로 유저의 무분별한 새로고침을 방지해야 했습니다.
- 트위터, 유튜브의 새로고침 ProgressView가 보여지는 시간은 너무 빠르지도 느리지도 않는 0.5초 정도 입니다. 이와 같은 사용자 경험을 만들고 싶었습니다.
구현한 기능은 다음과 같습니다.
- 당기면 네트워크 요청이 실행됩니다.
- @State var ifRefeshing = false 일 때만 네트워크 작업이 진행됩니다. 네트워크 작업 중에는 true, 작업이 끝나면 3초 후에 isRefeshing을 false로 업데이트합니다. 이를 통해 유저는 아무리 빨라도 3초에 한번씩만 네트워크 요청을 할 수 있으며 Coingecko의 분당 요청 제한을 준수 하게 됩니다.
- 네트워크 요청 가능 여부에 상관없이 유저는 당겨서 새로고침 동작을 할 수 있으며, 0.5초 후에 ProgressView가 들어가는 일관된 사용자 경험을 제공합니다. 이는 트위터에서도 관찰된 사용자 경험인데, 개인적으로 좋은 경험이라고 생각하여 구현하였습니다.
이번 작업에서 어려웠던 부분은 ProgressView가 있는 공간이 부드럽게 나타나고 사라지게 하는 것이었습니다.
if문으로 VStack을 하는 방법과 Zstack을 활용해서 offset값이 변하게 만드는 등의 다양한 시도를 해봤는데 움직임이 자연스럽지 않고 만족스러운 결과가 나오지 않았습니다.
그래서 해당 기능은 라이브러리 사용으로 대체하였습니다.
var body: some View {
RefreshableScrollView(loadingViewBackgroundColor: Color.theme.background, onRefresh: { done in
if !viewModel.isRefreshing { // isRefreshing 상태에서만 getCoin() 실행합니다.
viewModel.getCoin()
}
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
done() // 0.5초 후에 ProgressView 영역이 사라집니다.
}
}) {
contents (생략)
}
}
네트워크 작업이 진행되는 DataService.swift는 다음과 같이 업데이트해주었습니다.
class CoinDataService {
@Published var allCoins: [CoinModel] = []
@Published var isRefreshing: Bool = false // isRefreshing 상태 프로퍼티
var coinSubscription: AnyCancellable?
init() {
getCoin()
}
func getCoin() {
guard let url = URL(string: "코인겍코 API") else { return }
self.isRefreshing = true // isRefreshing 변수는 함수 호출과 함께 true로 설정됩니다.
coinSubscription = NetWorkingManager.download(url: url)
.decode(type: [CoinModel].self, decoder: JSONDecoder())
.sink(receiveCompletion: NetWorkingManager.handleCompletion, receiveValue: { [weak self] (returnCoins) in
self?.allCoins = returnCoins
// 네트워크 작업이 완료되고 3초 후에 isRefreshing을 false로 업데이트 합니다.
DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
self?.isRefreshing = false
}
self?.coinSubscription?.cancel()
})
}
}
ViewModel.swift 파일은 다음과 같이 업데이트 해주었습니다 .
class HomeViewModel: ObservableObject {
(생략)
@Published var isRefreshing: Bool = false
(생략)
private let dataService = CoinDataService()
private var cancellables = Set<AnyCancellable>()
init() {
addSubscribers()
}
func addSubscribers() {
(생략)
// isRefreshing Update
dataService.$isRefreshing
.sink { [weak self] returnBool in
self?.isRefreshing = returnBool
}
.store(in: &cancellables)
}
func getCoin() {
dataService.getCoin()
}
(생략)
}
'Project > SwiftUI 블록와이드' 카테고리의 다른 글
[SwiftUI Project] 예외처리: Json Data -> CoreData 백업하기 (0) | 2022.10.14 |
---|---|
[SwiftUI Project] Skeleton Placeholder & Blink Animation 만들기 (0) | 2022.10.04 |
[SwiftUI Project] HStack 빈 공간이 터치 안되는 문제 해결 (0) | 2022.10.02 |
[SwiftUI Project] LazyVStack 하이라이트 색 설정하기 (0) | 2022.10.02 |
[SwiftUI Project] NavigationView가 강제로 dismiss 되는 문제 해결 (0) | 2022.10.01 |
댓글