티스토리 뷰

특정 푸시를 눌렀을 때 특정 화면으로 전환

 

따로 로직을 설정하지 않으면 단순히 앱이 켜지는 동작만 함.

 

하지만 코드를 작성해준다면 특정 화면으로 전환시킬 수 있음.

 

전환시키기 위해서는 우선 현재 사용자가 머물고 있는 최상단 뷰컨트롤러를 알아야함.

extension UIViewController {
    
    var topViewController: UIViewController? {
        return self.topViewController(currentViewController: self)
    }
    
    func topViewController(currentViewController: UIViewController) -> UIViewController {
        
        if let tabBarController = currentViewController as? UITabBarController, let selectedViewController = tabBarController.selectedViewController {
            
            return self.topViewController(currentViewController: selectedViewController)
        } else if let navigationController = currentViewController as? UINavigationController, let visibleViewController = navigationController.visibleViewController {
            
            return self.topViewController(currentViewController: visibleViewController)
        } else if let presentedViewController = currentViewController.presentedViewController {
            
            return self.topViewController(currentViewController: presentedViewController)
        } else {
            
            return currentViewController
        }
    }
}

세 가지 경우를 생각하면 됨.

 

1. 탭바 2. 네비게이션 3. 그냥

 

그래서 각각 탭바컨트롤러와 네비게이션컨트롤러로 타입캐스팅이 되는지 확인하고, 되는 경우에는 selected와 visibleViewController를 구해서 리턴해줌.

 

이 코드를 이용해서 푸시가 왔을 때 일정 작성 화면을 띄워보겠음.

    func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
        print("사용자가 푸시를 클릭함")
        
        print(response.notification.request.content.body) // 바디는 알림메세지의 내용
        print(response.notification.request.content.userInfo)// 파이어베이스에서 등록했던 키 밸류
        
        guard let topViewController = (UIApplication.shared.connectedScenes.first?.delegate as? SceneDelegate)?.window?.rootViewController?.topViewController else { return }
        
        print("현재 최상단 뷰컨트롤러: \(topViewController)")
        
        let userInfo = response.notification.request.content.userInfo
        
        if userInfo[AnyHashable("Calendar")] as? String == "true" {
            print("일정화면 띄우기")
            if topViewController is CalendarViewController {
                topViewController.present(AddCalendarViewController(), animated: true)
            } else {
                topViewController.tabBarController?.selectedIndex = 1
                topViewController.present(AddCalendarViewController(), animated: true)
            }
        } else {
            print("이상한 알림임")
        }
    }

window의 rootViewController에 접근해서 최상단 뷰컨트롤러를 구해서 userInfo에 따라 처리해봤음.

 

파이어베이스에서 키가 Calendar, 값이 true인 알림을 보냈다.

 

메인 화면을 최상단 뷰컨트롤러로 설정하고 푸시를 누르게 되면 일정화면으로 바로 넘어갔음.

 

화면 전환이 복잡한 경우에는 위와 같이 경우의 수를 전부 따져주기가 매우 힘듦

 

그래서 아예 처음 화면부터 시작하는 것이 경우의 수를 줄일 수 있는 방법임.

 

 

 

Foreground에서 특정 화면에 대해서만 푸시 받지 않기

 

카카오톡같은 경우를 보면 현재 채팅중인 대화창에 대해서는 푸시알림이 뜨지 않음.

 

이처럼 모든 화면에서 푸시 알림이 떠야 하는건 아니기때문에 willPresent에서 처리해줄 수 있음.

    //포그라운드 알림 수신, 화면마다 푸시마다 설정할 수도 있음(카카오톡처럼)
    func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
        
        guard let topViewController = (UIApplication.shared.connectedScenes.first?.delegate as? SceneDelegate)?.window?.rootViewController?.topViewController else { return }
        
        if topViewController is CalendarViewController {
            completionHandler([.badge])
        } else {
            completionHandler([.sound, .list, .banner])
        }
    }

위처럼 작성하면 푸시가 왔을 때 최상단 뷰컨트롤러가 일정 화면이라면 뱃지만 추가되고, 아닌 경우에만 소리와 배너 알림이 뜸.

 

 

 

뱃지, 알림 센터의 스택 제거

 

카카오톡을 예시로 들어보면, 채팅방을 들어갔을 때 해당 채팅방에 대한 뱃지와 알림 스택들이 제거되는 것을 확인할 수 있음.

 

화면별로 다시 나뉠 수 있겠지만, 일단 앱을 들어갔을때 알림들을 지우기 위해서는 아래와 같이 작성하면됨.

전달되었던 알림들이 전부 지워지고, 뱃지도 0으로 되어 사라질 것임.

 

 

토큰 제거

 

사용자가 회원 탈퇴를 하거나 로그아웃을 하는 경우에는 토큰을 지워줘야함.

 

Installations.installations().delete { error in
  if let error = error {
    print("Error deleting installation: \(error)")
    return
  }
  print("Installation deleted");
}

 

 

Remote Notification 참고

 

remote notification을 사용하면 앱의 모든 사용자에게 푸시 알림을 보낼 수 있는 것으로 알고 있었음.

 

근데 사용자들의 디바이스 토큰을 전부 알아야 보낼 수 있음.

 

remote notification은 디바이스 토큰을 서버에게 줘서 해당 토큰을 가지고 각 사용자에게 알림을 보내는 방식임

 

근데 개별 서버가 없는 경우에는 개별 사용자에게 푸시가 불가능하다고 생각해야됨.

 

왜냐하면 개별 토큰을 획득하더라도 해당 토큰이 어떤 유저의 토큰인지 특정할 수 없기 때문임.

 

 

 

Method Swizzling

 

런타임 시점에 기존 메서드를 다른 메서드로 바꾸어 실행하는 것을 말함.

 

주로 앱의 생명주기에 분석 기능을 통합하여 함께 실행하는 등에 사용된다고 함.

 

예를 들어, viewWillAppear에서 같이 실행하고 싶은 코드가 있다고 생각해볼때 내부에 작성해주면됨.

 

근데 뷰컨트롤러가 정말 많다면 하나하나 작성하기 힘듦

 

그럴때 method swizzling을 이용해 한 번에 실행시킬 수 있음.

 

하지만 몇 가지 주의해야 될 사항들이 있음.

 

firebase 등과 같은 툴에서 이미 method swizzling이 발생하고 있는 경우가 있는데, 이때 method swizzling이 여러 번 발생하면 앱이 동작하지 않을 수도 있음.

 

또한 런타임에서 메서드가 바뀌는 형태이므로 iOS버전에 따라 동작하지 않을 수도 있음.

 

뷰의 생명주기같은 메서드와 사용할 때 반드시 실행되어야 할 코드들이 실행되지 않을 수도 있으므로 주의해야함.

 

구현은 어렵지 않음.

 

단순히 두 개의 메서드를 바꿔주기만 하면됨.

    class func swizzleMethod() {
        
        let origin = #selector(viewWillAppear)
        let change = #selector(changeViewWillAppear)
        
        //메서드가 있는지 확인
        guard let originMethod = class_getInstanceMethod(UIViewController.self, origin), let changeMethod = class_getInstanceMethod(UIViewController.self, change) else {
            print("함수를 찾을 수 없음")
            return
        }
        method_exchangeImplementations(originMethod, changeMethod)
    }
    
    @objc func changeViewWillAppear() {
        print("changeViewWillAppear로 바뀜")
    }

 

이렇게 작성했으면 이 메서드를 각 뷰컨트롤러에서 작성해주는 것은 옳지 않음.

 

앱이 실행될 때 한 번만 실행해주는 것이 맞음.

'TIL' 카테고리의 다른 글

[TIL] 2022 / 10 / 24 - Rx  (0) 2022.10.24
[TIL] 2022 / 10 / 13 - Realm Migration  (1) 2022.10.13
[TIL] 2022 / 09 / 06  (0) 2022.09.06
[TIL] 2022 / 09 / 05  (0) 2022.09.05
[TIL] 2022 / 09 / 02 (GCD)  (0) 2022.09.02
댓글
최근에 올라온 글
Total
Today
Yesterday