알람 앱은 그냥 만들어도 귀찮지만 리액트 네이티브로 만들면 더 귀찮다⏰ Part3 — iOS

안드로이드와 iOS에서 알람 앱을 만드는 방법과 그 위에 리액트 네이티브를 끼얹는 개발기

MJ Studio
MJ Studio
5 min readFeb 13, 2022

--

Content

Part 1 — 개요, TimePicker

Part 2 — 안드로이드

Part 3 — iOS ← Here 🤚

iOS 네이티브 구현

iOS는 구현 방법에서 꽤나 hacky 한 방법을 썼다.

iOS는 Notifcation API에 대해 이해가 쉽지 않은 OS이다.

흔히 착각하는 개념들은 APNs, FCM, Remote notification 등인데, Local Notification 은 iOS 10부터 도입된 UserNotifications API를 사용하여 독립적으로 구현될 수 있고 상기된 개념들과 무관하다.

어쨌든 모든 구현엔 UserNotifications API만 사용했다.

알람 기능

reserveAlarm 함수는 각 UNNotificationRequest 를 각 요일, 각 반복에 대하여 각기 다른 identifier로 설정한다. 이렇게 하면 동일한 알람들이 안드로이드에서와같이 자동으로 collapse 되지 않지만 딱히 이런 식으로 안 하고 반복되게 Notification을 오게 할 수는 없어 보인다.

대충 이렇게 UNCAlendarNotificationTrigger와 함께 적절히 시간을 설정해서 호출해 준다.

앞선 글에서 언급했듯이 iOS에서는 반복 알람을 안드로이드 구현과 조금 다르게 했는데

“alarm-{weekday}-{second}” 와 같이 각 request의 identifier가 구성되었다.

예를 들어, 월요일 6시 10분에 울리는 알람은 alarm-2–0, alarm-2–5 … 처럼 identifier를 갖는다.

뭔가 이런 식으로 알람을 반복해서 보내도 iOS에서 문제가 생기지 않을까하고 서늘한 느낌에 구글링을 해봤는데 역시나 Local Notification은 최대 64개가 예약이 되어있을 수 있다고 하니, 적절한 throttle 작업을 거쳐주길 바란다.

안드로이드에서는 AlarmWorker라는 설정된 요일과 관계없이 해당 시간대에 매일 실행되는 Work를 정의했지만 iOS에서는 그럴 필요 없이 해당되는 요일만 모두 설정했다.

알람 취소는 더 간단하다. 만약 모든 알람을 취소해 주고 싶다면

alarm-1–0alarm-7–55 까지 모든 가능한 identifier에 대해 취소를 해주면 된다.

여기서 주의해야 하는 것은 안드로이드에서 AlarmWorker가 반복 알림을 새롭게 다시 실행시켜주는 것과 다르게 iOS에서는 각 반복들이 모두 request로 큐에 들어가 있기 때문에

반복 알림에 대해 취소를 할 때 오늘 반복 알림만 취소를 해야 한다는 것이다. 따라서

alarm-week-5 alarm-week-55 만 취소해 주면 된다(내 구현은 1분만 5초마다 울리게 되어있어서 0 ~ 55까지의 값을 가진다).

이제 믿거나 말거나 알람이 잘 온다.

알람 탭과 딥링크

로컬 알람을 보낼 때 UNMutableNotificationContentuserInfo에 딥링크 정보를 담아서 예약한다.

나 같은 경우는 이 알람 컨텐츠에 Stop action을 넣어서 category를 추가했기 때문에 유저가 알람을 탭 한 건지 끄기를 누른 건지 UNUserNotificationCenterDelegate 의 메서드에서 didReceive 함수를 조금 신경 써서 구현해 줘야 했다.

만약 탭을 했다면 deeplink를 그대로 UIApplication의 open 메소드를 이용해 AppDelegateopenURL이 direct로 호출되게 해주고, 그렇지 않다면 안드로이드에서와같이 네이티브 모듈의 static string에 설정을 해준다.

이 static string을 RN에서 poll 해 오는 시점은 AppStateactive가 될 때이므로 유저가 background에서 foreground로 다시 와도 기분 좋게 딥링크를 poll 해올 수 있다.

마지막으로 iOS에서 Swift로 만든 Native module을 objective-c로 포팅 해주기 위해 대충 Objective-C 파일에 다음과 같이 RCT_EXTERN_MODULE, RCT_EXTERN_METHOD를 이용해 RN에서 호출할 수 있도록 정의해 주면 된다.

후기

알람 기능 구현 개발기가 끝났다. 개발하면서 가끔 재미있었지만 정말 귀찮은 기능이었다. 난 일단 개발을 하면서 빌드가 오래 걸리는 iOS를 자꾸 빌드 시키면서 개발해야 되는 게 싫다.

코틀린이랑 스위프트도 문법이 기억이 잘 안 나서 대충 감으로 구현했다. 여하튼 스위프트는 모르겠고 코틀린은 JS보단 좋은 언어이다.

여담으로 이 작업을 하면서 FCM 쪽 기능도 추가했는데 iOS에서 onReceiveRemoteNotification 함수가 OS 단에서 뭔 이상한 ApplicationPolicy 쪽에 서 throttling이 걸려 호출이 안되는 이슈를 코드 문제인 줄 알고 react-native-firebase/messaging 구현체를 Objective-C도 모르는 내가 샅샅이 뜯어봐야 했다. 그래서 최근에 Swift보다 Objective-C를 더 봤다.

곧 이 기능이 회사 앱에 출시되는데 버그 없이 잘 되려나 모르겠다.

--

--