네트워크 연결이 끊겼을 때의 처리가 구현되어 있지 않아, 네트워크 재연결이 되었을 때 BLE로 연동한 혈당 데이터가 동기화되지 않는 문제가 발생했습니다.

이 문제를 해결하기 위해서 네트워크 연결이 끊겼을 때 경고창을 띄우도록 구현했습니다.

서버에 재요청하는 시나리오는 다음과 같습니다.

  1. 서버에 요청 시, 요청이 끊기면 팝업 띄움
  2. 팝업에서 설정재시도 버튼 두 개를 생성
  3. 설정을 클릭 시, 설정으로 이동하고 앱으로 돌아왔을 때 재요청
  4. 재시도 버튼 클릭 시, 재요청
  5. 네트워크가 연결되면, 응답 데이터를 UI에 표시하고 아니면 다시 팝업 띄우기

샘플 코드

먼저 경고 팝업을 싱글톤으로 생성

final class CustomAlert {
    static let shared = CustomAlert()
    
    private init() { }
    
    func showNetworkAlert(function: @escaping () async -> Void) {
        let alert = UIAlertController(title: "네트워크 연결 오류", message: "네트워크 연결이 끊겼습니다.", preferredStyle: .alert)
        
        // 설정 이동 액션
        let settingsAction = UIAlertAction(title: "설정", style: .default) { _ in
            print("설정 진입!")
            
            guard let url = URL(string: UIApplication.openSettingsURLString) else { return }
            if UIApplication.shared.canOpenURL(url) {
                UIApplication.shared.open(url, options: [:], completionHandler: nil)
                
                self.retry(function: function)
            }
        }
        alert.addAction(settingsAction)
        
        // 재시도 액션
        let retryAction = UIAlertAction(title: "재시도", style: .default) { _ in
            print("retryAction 진입!")
            self.retry(function: function)
        }
        alert.addAction(retryAction)
        
        UIWindow.keyWindow?.rootViewController?.present(alert, animated: true, completion: nil)
    }
    
    // 재시도할 함수 실행 함수
    private func retry(function: @escaping () async -> Void) {
        Task {
            await function() // 연결 재시도
        }
    }
}

ViewModel에서 서버에 요청 후, 네트워크 연결이 끊겼을 시, 팝업을 표시하도록 구현

// PostTableViewModel
final class PostTableViewModel {
    var posts: [Post] = []
    var filteredPosts: [Post] = []
    
    func request(targetType: TargetType) async {
        switch targetType {
        case .getPosts:
            do {
                try await NetworkManager.shared.request(targetType: .getPosts) { [weak self] (result: Result<[Post], NetworkError>) in
                    switch result {
                    guard let self = self else { return }
                    case .success(let posts):
                        self.posts = posts
                        if posts.isEmpty {
                            print(NetworkError.noData)
                            return
                        }
                    case .failure(let error):
                        CustomAlert.shared.showNetworkAlert { [weak self] in
                            await self.request(targetType: targetType)
                        }
                    }
                }
            } catch {
		            // 네트워크 에러 발생 시, 경고 팝업 표시
                await MainActor.run {
                    CustomAlert.shared.showNetworkAlert { [weak self] in
                        await self?.request(targetType: targetType)
                    }
                }
            }
        }
    }
}

ViewController에서는 ViewModel을 통해 서버에 요청

// ViewController
func fetchPosts() {
    Task { [weak self] in
        guard let self = self else { return }
        await self.postTableViewModel.request(targetType: .getPosts)
        
        await MainActor.run { [weak self] in
            guard let self = self else { return }
            self.postTableView.reloadData()
        }
    }
}