728x90
카카오톡과 같은 앱의 경우 네트워크가 연결되어 있어야 서비스가 제대로 작동할 수 있다.
니카내카를 개발하다가 네트워크 상태에 따라서 서버 데이터가 제대로 오지 않았을 때 유저가 인터랙션을 하게 되면 앱이 강제종료가 되는 이슈가 나타난다.
그래서 앱에 진입했을 때 현재 디바이스의 네트워크 연결 상태를 받아, 연결이 되지 않았다면 팝업창을 띄우고 그렇지 않은 경우에는 메인 화면 (지도)로 이동할 수 있도록 분기처리했다.
🤔 고민인 부분
앱을 최초 실행했을 때,
✅ 스플래쉬에서 분기처리하는 것은 가능하지만
❌ background -> foreground로 이동했을 때에도 네트워크 상태를 확인하고 싶은데 .. 아직 못해서 .. 고민중 ..
Network Manager File
네트워크 상태를 하나의 화면에서 확인하는 것이 아니라, 여러 화면에서 확인을 해야 하므로 싱글톤 패턴으로 구현했다.
import Foundation
import Network
enum ConnectionType {
case wifi
case cellular
case ethernet
case unknown
}
final class NetworkConnectionStatus {
static let shared = NetworkConnectionStatus()
private let queue = DispatchQueue.global()
private let monitor: NWPathMonitor
public private(set) var isConnected: Bool = false
public private(set) var connectionType: ConnectionType = .unknown
typealias completionHanlder = (Bool) -> Void
// monotior 초기화
private init() {
monitor = NWPathMonitor()
}
// Network Monitoring 시작
func startMonitoring(completionHandler: @escaping completionHanlder) {
monitor.start(queue: queue)
monitor.pathUpdateHandler = { path in
self.isConnected = path.status == .satisfied
self.getConnectionType(path)
completionHandler(self.isConnected)
}
}
// Network Monitoring 종료
func stopMonitoring() {
monitor.cancel()
}
// Network 연결 타입
private func getConnectionType(_ path: NWPath) {
if path.usesInterfaceType(.wifi) {
connectionType = .wifi
} else if path.usesInterfaceType(.cellular) {
connectionType = .cellular
} else if path.usesInterfaceType(.wiredEthernet) {
connectionType = .ethernet
} else {
connectionType = .unknown
}
}
}
Splash
앱을 진입하면 스플래쉬 화면을 보여주게 된다.
그리고 스플래쉬 화면에서 애니메이션이 play되고, 애니메이션이 동작한 이후로 화면전환 전에 네트워크 상태를 분기처리하고 각 상태에 맞게 화면을 보여주면 된다.
final class SplashViewController: BaseViewController {
// MARK: - UI Property
private let firstLabel = UILabel().then {
$0.text = "니"
$0.textColor = R.Color.black200
$0.font = NiCarNaeCarFont.title0.font
$0.alpha = 0
}
private let secondLabel = UILabel().then {
$0.text = "카"
$0.textColor = R.Color.green100
$0.font = NiCarNaeCarFont.title0.font
$0.alpha = 0
}
private let thirdLabel = UILabel().then {
$0.text = "내"
$0.textColor = R.Color.black200
$0.font = NiCarNaeCarFont.title0.font
$0.alpha = 0
}
private let fourthLabel = UILabel().then {
$0.text = "카"
$0.textColor = R.Color.blue100
$0.font = NiCarNaeCarFont.title0.font
$0.alpha = 0
}
// MARK: - Life Cycle
override func viewDidLoad() {
super.viewDidLoad()
setAnimation() 1️⃣
}
// MARK: - UI Method
override func configureUI() {
super.configureUI()
view.backgroundColor = R.Color.white
}
override func setLayout() {
view.addSubviews(firstLabel, secondLabel, thirdLabel, fourthLabel)
firstLabel.snp.makeConstraints { make in
make.top.equalTo(view.safeAreaLayoutGuide).inset(321)
make.leading.equalTo(view.safeAreaLayoutGuide).inset(32)
}
secondLabel.snp.makeConstraints { make in
make.top.equalTo(view.safeAreaLayoutGuide).inset(303)
make.leading.equalTo(firstLabel.snp.trailing)
}
thirdLabel.snp.makeConstraints { make in
make.top.equalTo(view.safeAreaLayoutGuide).inset(321)
make.leading.equalTo(secondLabel.snp.trailing)
}
fourthLabel.snp.makeConstraints { make in
make.top.equalTo(view.safeAreaLayoutGuide).inset(303)
make.leading.equalTo(thirdLabel.snp.trailing)
}
}
// MARK: - Custom Method
private func checkDeviceNetworkStatus() {
NetworkConnectionStatus.shared.startMonitoring { isConnected in 3️⃣
if isConnected { 4️⃣
print("🟢 네트워크 연결")
DispatchQueue.main.async {
if UserDefaults.standard.bool(forKey: Constant.UserDefaults.isNotFirst) {
let viewController = UINavigationController(rootViewController: MainMapViewController())
self.transition(viewController, transitionStyle: .presentCrossDissolve)
} else {
let viewController = UINavigationController(rootViewController: OnboardingViewController())
self.transition(viewController, transitionStyle: .presentCrossDissolve)
}
}
} else { 5️⃣
print("🟠 네트워크 연결 해제!")
DispatchQueue.main.async {
self.presentAlert()
}
}
}
}
private func setAnimation() {
showLabel(firstLabel) {
self.showLabel(self.secondLabel) {
self.showLabel(self.thirdLabel) {
self.showLabel(self.fourthLabel) {
self.checkDeviceNetworkStatus() 2️⃣
}
}
}
}
}
private func showLabel(_ component: UILabel, completion: @escaping () -> Void) {
UIView.animate(withDuration: 0.4, delay: 0, options: .curveEaseOut) {
component.transform = CGAffineTransform(translationX: 0, y: -16)
component.alpha = 1
} completion: { _ in
completion()
}
}
private func presentAlert() {
let alertController = UIAlertController(
title: "네트워크에 접속할 수 없습니다.",
message: "네트워크 연결 상태를 확인해주세요.",
preferredStyle: .alert
)
let endAction = UIAlertAction(title: "종료", style: .destructive) { _ in
// 앱 종료
UIApplication.shared.perform(#selector(NSXPCConnection.suspend))
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
exit(0)
}
}
let confirmAction = UIAlertAction(title: "확인", style: .default) { _ in
// 설정앱 켜주기
guard let url = URL(string: UIApplication.openSettingsURLString) else { return }
if UIApplication.shared.canOpenURL(url) {
UIApplication.shared.open(url)
}
}
alertController.addAction(endAction)
alertController.addAction(confirmAction)
self.present(alertController, animated: true, completion: nil)
}
}
위의 코드를 통해서 알 수 있듯,
- 스플래쉬 화면에 진입해서
- 스플래쉬 애니메이션이 실행된 다음
- 네트워크 상태를 받아와서
- 만약 연결이 되어 있다면 메인화면으로 이동하고
- 그렇지 않다면 팝업창을 띄우도록 한다.
여기서 팝업창을 띄우는 코드는 UIAlertAction을 통해서 구현했다.
private func presentAlert() {
let alertController = UIAlertController(
title: "네트워크에 접속할 수 없습니다.",
message: "네트워크 연결 상태를 확인해주세요.",
preferredStyle: .alert
)
let endAction = UIAlertAction(title: "종료", style: .destructive) { _ in
// 앱 종료
UIApplication.shared.perform(#selector(NSXPCConnection.suspend))
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
exit(0)
}
}
let confirmAction = UIAlertAction(title: "확인", style: .default) { _ in
// 설정앱 켜주기
guard let url = URL(string: UIApplication.openSettingsURLString) else { return }
if UIApplication.shared.canOpenURL(url) {
UIApplication.shared.open(url)
}
}
alertController.addAction(endAction)
alertController.addAction(confirmAction)
self.present(alertController, animated: true, completion: nil)
}
저거 위에 고민은 어케 저케 해보고 있긴한데 제법 ... 어렵다 ..
'iOS > 니카내카' 카테고리의 다른 글
[회고] 앱을 출시해보았습니다? (0) | 2022.10.04 |
---|---|
[앱 심사] Kids Category도 순순히 넘어가지 않는다. (0) | 2022.09.27 |
[니카내카] 기획 수정? (0) | 2022.09.14 |
[니카내카] Bottom Sheet Using UISheetPresentationController (2) | 2022.09.13 |
[니카내카] 앱에서 다른 앱 호출 (0) | 2022.09.13 |