포스트

Final (11)

Extension을 사용한 Alert 구현

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
extension UIViewController {
    
    func showMessage(title: String, message: String, completion: (() -> Void)? = nil) {
        let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
        alert.addAction(UIAlertAction(title: "확인", style: .default, handler: { _ in
            completion?()
        }))
        alert.addAction(UIAlertAction(title: "취소", style: .default))
        present(alert, animated: true)
    }

    func showMessageWithCancel(title: String, message: String, completion: (() -> Void)? = nil) {
        let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
        alert.addAction(UIAlertAction(title: "확인", style: .default, handler: { _ in
            completion?()
        }))
        alert.addAction(UIAlertAction(title: "취소", style: .default))
        present(alert, animated: true)
    }
    
}

목적은 아래에서 서술

WriteVC에서 등록, 수정이 완료되었을때 alert 구현

기존에 했던 방식

사실 두뇌회전이 잘 되지않아, 미봉책으로 약 4초의 텀을 주고 dismiss를 하게 했다.

하지만 해당 문제는 서버상태에따라 달라지므로 허점투성이 이다.

문제를 파악하고 있었지만 completion Handler를 통해서 하면 되지않을까? 라고 생각을 했는데,

어제는 무슨 생각이 들었는지 안될것같다고 머리속으로 판단을 내서 시도조차 하지 않았다.

그래서 튜터님께 여쭤봤는데 completion Hanler를 사용해보는게 어떻겠냐고 하셨다.

생각만 하고 이게 맞나 싶다가 아니라고 못박고 하지않다가, 오늘 다시 튜터님께 말씀을 듣고 생각해보니 아이디어가 명확해져서 구현해본다.

우선 alert를 삼항연산자로 구분했던것을 위에 하나의 extension을 사용함으로써 코드를 간소화해준다.

image

두개는 같은 기능이지만 한줄로 간소화를 할 수있기에 효율적이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
private func reviewTapped() {
        guard
            let uid = Auth.auth().currentUser?.uid,
            let title = titleTextField.text,
            let content = contentTextView.text
        else {
            return
        }
        ProgressHUD.animate()
        uploadImages(images: selectedImages)
        
            .sink(receiveCompletion: { [weak self] completion in
                switch completion {
                case .finished:
                    break
                case .failure(let error):
                    self?.showMessage(title: "에러 발생", message: "\(error.localizedDescription)이 발생 했습니다.")
                }
            }, receiveValue: { [weak self] imageURLs in
                guard let self = self else { return }
                
                let dictionary: [String: Any] = [
                    db_uid: uid,
                    db_title: title,
                    db_storeAddress: self.addressText!,
                    db_storeName: self.storeTitleText!,
                    db_content: content,
                    db_rating: self.selectedRating,
                    db_imageURL: imageURLs,
                    db_isActive: false,
                    db_createdAt: self.isEditMode ? self.review!.createdAt : Timestamp(date: Date()),
                    db_updatedAt: Timestamp(date: Date())
                ]
                
                
                if isEditMode {
                    viewModel.editUserReview(uid: uid, storeAddress: self.addressText!, title: review!.title, userDict: dictionary) {
                        ProgressHUD.remove()
                        self.showMessage(title: "리뷰 수정", message: "리뷰가 수정 되었습니다.") {
                            self.navigationController?.popViewController(animated: true)
                        }
                    }
                } else {
                    viewModel.createReview(userDict: dictionary) {
                        ProgressHUD.remove()
                        self.showMessage(title: "리뷰 등록", message: "리뷰가 등록 되었습니다") {
                            self.dismiss(animated: true, completion: nil)
                        }
                    }
                }
            })
            .store(in: &cancellables)
    }

수정 완료.

Jun-10-2024 12-36-08

프로필 저장 완료시 dismiss 처리

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
func updateProfile(uid: String, nickName: String, profile: UIImage, completion: @escaping ((Result<(),Error>) -> Void)) {
        
        
        let storageRef = Storage.storage().reference(forURL: "gs://tteoppokki4u.appspot.com")
        let storageProfileRef = storageRef.child(db_user_profile).child(uid)
        guard let imageData = profile.jpegData(compressionQuality: 0.8) else { return }
        
        let metaData = StorageMetadata()
        metaData.contentType = "image/jpg"
        
        storageProfileRef.putData(imageData, metadata: metaData) { (metadata, error) in
            if let error = error {
                completion(.failure(error))
                return
            }
            
            storageProfileRef.downloadURL { (url, error) in
                if let error = error {
                    completion(.failure(error))
                    return
                }
                
                guard let downloadURL = url else { return }
                let values = [db_nickName: nickName, db_profileImageUrl: downloadURL.absoluteString]
                self.ref.child(db_user_users).child(uid).updateChildValues(values) { error, reference in
                    if let error = error {
                        completion(.failure(error))
                        return
                    }
                    completion(.success(()))
                }
            }
        }
        
    }

기존에는 Error만 전달 했다면, 지금은 Result를 사용하여 Success, Failure의 상태에 따라 void, error로 다르게 전달하게 했다.

성공시에 void를 치는 이유는 성공했을때 데이터 전달을 할필요가 없기 때문이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@objc func saveChanges() {
        ProgressHUD.animate()
        guard let uid = Auth.auth().currentUser?.uid else { return }
        guard let image = profileImage else { return }
        let userName = userNameTextField.text ?? ""
        
        userManager.updateProfile(uid: uid, nickName: userName, profile: image) { [weak self] result in
            switch result {
            case .success(()):
                ProgressHUD.dismiss()
                self?.showMessage(title: "수정 완료", message: "프로필 정보가 수정 되었습니다.") {
                    self?.navigationController?.popViewController(animated: true)
                }
            case .failure(let error) :
                ProgressHUD.dismiss()
                self?.showMessage(title: "에러 발생", message: "\(error.localizedDescription)가 발생했습니다.")
            }
        }
    }

switch~case 문을 사용하여 이벤트 핸들링.

Jun-10-2024 14-43-48

완료.

로그인 예외 상황처리

현재 로그아웃 하고 다시 로그인을 하게 되는경우 SignManager의

1
2
3
4
5
6
7
8
9
10
func saveUserData(user: UserModel) {
        let ref = Database.database().reference()
        let userData: [String: Any] = [
            db_uid: user.uid,
            db_nickName: "",
            db_email: user.email,
            db_profileImageUrl: "https://firebasestorage.googleapis.com/v0/b/tteoppokki4u.appspot.com/o/dummyProfile%2FdefaultImage.png?alt=media&token=b4aab21e-e19a-42b7-9d17-d92a3801a327"
        ]
        ref.child("users").child(user.uid).setValue(userData)
    }

이 메서드가 실행이 되면서 닉네임과 프로필 이미지 주소가 위의 내용으로 바뀌는 문제가 생긴다.

이부분을 해결하려고 한다.

지금 생각한건 realtimedatabase를 조회해서 해다 내용이 있으면 그대로 사용하는 함수를 구현하면 될듯하다.

1
2
3
4
func fetchUserData(uid: String, completion: @escaping ((any Error)?, DataSnapshot?) -> Void) {
        let ref = Database.database().reference()
        ref.child("users").child(uid).getData(completion: completion)
    }

값을 조회하는 함수를 만들어준다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
func googleLoginDidTapped(presentViewController: UIViewController) {
        
        GIDSignIn.sharedInstance.signIn(withPresenting: presentViewController) { [weak self] signInResult, error in
            if let error = error {
                self?.loginPublisher.send(completion: .failure(error))
            }
            
            
            guard let result = signInResult else { return }
            
            let user = result.user
            let idToken = user.idToken?.tokenString
            
            let credential = GoogleAuthProvider.credential(withIDToken: idToken!, accessToken: user.accessToken.tokenString)
            
            Auth.auth().signIn(with: credential) { result, error in
                if let error = error {
                    self?.loginPublisher.send(completion: .failure(error))
                }
                
                guard let user = result?.user else { return }
                
                let uid = user.uid
                let email = user.email
                
                self?.signManager.fetchUserData(uid: uid) { error, snapshot in
                    if let error = error {
                        self?.loginPublisher.send(completion: .failure(error))
                    }
                    
                    if let snapshot = snapshot {
                        if snapshot.exists() {
                            self?.loginPublisher.send(())
                        } else {
                            let model = UserModel(uid: uid, email: email!, isBlock: false, nickName: "", profileImageUrl: "https://firebasestorage.googleapis.com/v0/b/tteoppokki4u.appspot.com/o/dummyProfile%2FdefaultImage.png?alt=media&token=b4aab21e-e19a-42b7-9d17-d92a3801a327")
                            self?.signManager.saveUserData(user: model)
                        }
                    }
                }
                
            }
            
            self?.loginPublisher.send()
        }
    }

func authorizationController(controller: ASAuthorizationController, didCompleteWithAuthorization authorization: ASAuthorization) {
        
        guard let credential = authorization.credential as? ASAuthorizationAppleIDCredential else { return }
        
        let nonce = currentNonce
        
        signManager.saveApple(appleCredential: credential, nonce: nonce!) { [weak self] result in
            switch result {
            case .success(let result):
                if let user = result?.user {
                    let email = credential.email ?? ""
                    self?.signManager.fetchUserData(uid: user.uid) { error, snapshot in
                        if let error = error {
                            self?.loginPublisher.send(completion: .failure(error))
                        }
                        if let snapshot = snapshot {
                            if snapshot.exists() {
                                self?.loginPublisher.send(())
                            } else {
                                let model = UserModel(uid: user.uid, email: email, isBlock: false, nickName: "", profileImageUrl: "https://firebasestorage.googleapis.com/v0/b/tteoppokki4u.appspot.com/o/dummyProfile%2FdefaultImage.png?alt=media&token=b4aab21e-e19a-42b7-9d17-d92a3801a327")
                                self?.signManager.saveUserData(user: model)
                            }
                        }
                    }
                }
            case .failure(let error):
                self?.loginPublisher.send(completion: .failure(error))
            }
        }
        
    }
    

snapshot이 존재한다면 그냥 넘어가고

없으면 새롭게 생성하게 만들었다.

작동 확인 완료.

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.