applicationWIllTerminate란?
iOS에서 라이프사이클 중 일반적으로 시스템에 의해 앱이 종료되기 전에 마지막 작업을 수행하거나 앱 데이터를 저장하거나 앱이 종료되기 전에 필요한 정리를 수행하기 위한 메서드입니다.
private func configButton() {
nextButton.rx.tap
.subscribe(onNext: { [weak self] in
guard let self = self else { return }
UserDefaultsManager.shared.setUserData(userData: user)
nextButton.tappedAnimation()
FirestoreService.shared.saveDocument(collectionId: .session, documentId: user.phoneNumber, data: User.tempUser) { _ in }
(UIApplication.shared.connectedScenes.first?.delegate as? SceneDelegate)?.changeRootView(TabBarController(), animated: true)
})
.disposed(by: disposeBag)
}
다음 버튼을 누르면 현재 로그인한 유저가 있는지 체크하는지 확인합니다.
func saveDocument<T: Codable>(collectionId: Collections, documentId: String, data: T, completion: @escaping (Result<Bool, Error>) -> Void) {
DispatchQueue.global().async {
do {
try self.dbRef.collection(collectionId.name).document(documentId).setData(from: data.self)
completion(.success(true))
print("Success to save new document at \(collectionId.name) \(documentId)")
} catch {
print("Error to save new document at \(collectionId.name) \(documentId) \(error)")
completion(.failure(error))
}
}
}
- 서버의 collection중 파라미터로 받은 collectionId를 받아 session에 접근하고 document중 유저의 번호를 저장시키는 코드입니다.
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let windowScene = (scene as? UIWindowScene) else { return }
window = UIWindow(windowScene: windowScene)
let curentUser = UserDefaultsManager.shared.getUserData()
if UserDefaultsManager.shared.isLogin() {
let checkService = CheckService()
let user: User = User.tempUser
checkService.checkUserId(userId: curentUser.userId) { isUser in
if isUser {
FirestoreService.shared.saveDocument(collectionId: .session, documentId: curentUser.phoneNumber, data: user) { _ in }
let rootViewController = TabBarController()
self.window?.rootViewController = rootViewController
} else {
let rootViewController = UINavigationController(rootViewController: SignViewController())
self.window?.rootViewController = rootViewController
}
}
} else {
let rootViewController = UINavigationController(rootViewController: SignViewController())
window?.rootViewController = rootViewController
}
window?.makeKeyAndVisible()
}
저는 유저가 한번 로그인을 하면 유저 디포트에 유저의 정보를 저장시켰습니다. 그래서 유저의 아이디값을 저장시켜놓고 만약 기기에 유저정보가 있다면 이제 서버의 유저의 정보가 있는지 체크를 해야합니다.
func checkUserId(userId: String, completion: @escaping (_ isRight: Bool) -> ()) {
Loading.showLoading(backgroundColor: .systemBackground.withAlphaComponent(0.8))
DispatchQueue.global().async {
self.dbRef.collection("users")
.whereField("id", isEqualTo: userId)
.getDocuments { snapShot, err in
guard err == nil, let documents = snapShot?.documents else {
return
}
if let document = documents.first {
let user = SignInViewModel().convertDocumentToUser(document: document)
UserDefaultsManager.shared.setUserData(userData: user)
}
completion(documents.first != nil)
}
}
}
서버와 통신을 하여 유저 collection에 유저기기의 id 값이 있는지 체크를 하여 컴플리션 핸들러로 빼서 bool 값을 리턴 받도록 하였습니다.
checkService.checkUserId(userId: curentUser.userId) { isUser in
if isUser {
FirestoreService.shared.saveDocument(collectionId: .session, documentId: curentUser.phoneNumber, data: user) { _ in }
let rootViewController = TabBarController()
self.window?.rootViewController = rootViewController
} else {
let rootViewController = UINavigationController(rootViewController: SignViewController())
self.window?.rootViewController = rootViewController
}
}
만약 유저가 있다면 이제부터 session Collection에 유저의 번호를 저장시킵니다.
만약 없다면 초기화면으로 돌아가게끔 코드를 작성했습니다.
func applicationWillTerminate(_ application: UIApplication) {
let checkService = CheckService()
checkService.disConnectSession()
sleep(3)
}
AppDelegate에서 앱이 종료될 시점에 disConnectSession이라는 메서드를 사용하여 앱의 종료시점에서 세션을 제거 했습니다. 이부분에서 sleep을 두지 않으면 서버와의 작업이 끝나지 않고 앱을 종료시켜버립니다.
func disConnectSession() {
let currentUser = UserDefaultsManager.shared.getUserData()
let phoneNumber = currentUser.phoneNumber
print(phoneNumber)
FirestoreService.shared.dbRef.collection("session").document(phoneNumber).delete { err in
if let err = err {
print("Error updating document: \(err)")
} else {
print("Document successfully updated")
}
}
}
- 일단 현재 유저의 정보를 가져왔습니다.
- 세션에는 현재 로그인한 유저의 번호를 저장시키도록 하였습니다.
- 그리고 파이어베이스의 session컬렉션에 있는 document(유저번호) 값을 삭제시킵니다.
FirestoreService.shared.loadDocument(collectionId: .session, documentId: number, dataType: User.self) { [weak self] result in
guard let self = self else { return }
switch result {
case .success(let user):
guard user != nil else { return }
showCustomAlert(alertType: .onlyConfirm, titleText: "경고", messageText: "다른기기에서 접속중입니다.", confirmButtonText: "확인", comfrimAction: { [weak self] in
guard let self = self else { return }
navigationController?.popViewController(animated: true)
})
case .failure(let err):
print("SingInVIewController 세션부분 에러입니다. error: \(err) ")
}
}
밑의 메서드를 이용하여 컴플리션으로 .success와 .failure을 받습니다.
만약 서버와 통신이 성공이되면 이제 user가 있는지 판단을 합니다.
있다면 알럿을 통해 이미 접속된 유저를 알려주고 navigation을 pop 시킵니다. 그렇게 그전 화면으로 돌아가게끔 작성했습니다.
func loadDocument<T: Codable>(collectionId: Collections, documentId: String, dataType: T.Type, completion: @escaping (Result<T?, Error>) -> Void) {
DispatchQueue.global().async {
self.dbRef.collection(collectionId.name).document(documentId).getDocument { (snapshot, error) in
if let error = error {
print("Error to load new document at \(collectionId.name) \(documentId) \(error)")
completion(.failure(error))
return
}
if let snapshot = snapshot, snapshot.exists {
do {
let documentData = try snapshot.data(as: dataType)
print("Success to load new document at \(collectionId.name) \(documentId)")
completion(.success(documentData))
} catch {
print("Error to decode document data: \(error)")
completion(.failure(error))
}
} else {
completion(.success(nil))
}
}
}
}
서버의 collection을 enum타입으로 정의하여 .session의 값이 들어가있습니다. session안에 documentId 값으로 유저의 번호가 있다면 completion(.success(documentData))로 유저의 정보를 넘깁니다.
만약 있다면 completion(.success(documentData))안에 User타입의 user값이 리턴되기에 있으면 막는로직을 통하여 로그인이 제한됩니다.