개발기 미라클알람 5분

앱을 열어도 AlarmKit 알람은 계속 울립니다

iOS는 불가능하다고 결론 냈습니다. 그런데 요구사항이 바뀌자 같은 플랫폼에서 그대로 됐습니다. 그 분석은 틀린 게 아니라, 다른 질문에 답한 것이었습니다.

Miracle Alarm은 소리 내어 답해야 꺼지는 알람입니다. 답하는 동안에도 알람이 계속 울려야 합니다. 그렇지 않으면 이 구조 전체가 단계만 늘어난 평범한 해제 버튼으로 주저앉습니다.

첫 분석의 결론은 iOS에서는 불가능하다는 것이었습니다. 그 결론은 틀렸는데, 어떻게 틀렸는지가 고친 방법보다 더 중요합니다.

처음 내린 결론: 불가능

논리 자체는 단순했습니다. AlarmKit은 알람이 앱을 깨워 화면을 넘겨주는 것을 허용하지 않습니다. 알람은 시스템이 만든 자체 UI를 띄우고, 앱이 그 자리를 차지할 수는 없습니다. 알람을 듣고, 질문을 받고, 답을 말하고, 정답 판정까지 받는 흐름 전체가 잠금 화면에서 끝나야 한다면 iOS에는 그럴 자리가 없습니다. Android에는 있고, iOS에는 없습니다.

주어진 질문에 대해서는 맞는 분석이었습니다.

전제가 움직였습니다

그 아래에서 요구사항이 바뀌었습니다. 흐름은 더 이상 “잠금 화면에서 전부 처리한다”가 아닙니다.

  1. 알람이 울리고 시스템 UI가 뜹니다.
  2. 잠금 화면에는 버튼이 하나만 있습니다. Start.
  3. Start를 누르면 앱이 열립니다.
  4. 질문은 앱 안에서 진행합니다. 여기서는 화면 전체가 우리 것입니다.

요구사항이 이렇게 바뀌면 “AlarmKit이 앱을 깨우고 잠금 화면을 넘겨주지 못한다”는 사실은 더 이상 장애물이 아닙니다. 그럴 필요 자체가 없어졌기 때문입니다.

이것이 성립하게 만든 발견

검증이 필요했고, 실제로 검증한 부분입니다.

사용자가 커스텀 Start 버튼으로 앱을 열어도 AlarmKit 알람은 계속 울립니다. 앱을 연다고 해서 멈추지 않습니다.

알람은 명시적으로 멈추라고 했을 때만, 그때만 멈춥니다.

import AlarmKit

// 사용자가 답하는 동안에도 알람은 계속 울리는 중입니다.
// 앱을 여는 것만으로는 아무것도 멈추지 않습니다.
func onAnswerAccepted(alarmID: UUID) throws {
    try AlarmManager.shared.stop(id: alarmID)
}

이 호출 하나가 해제 스위치 전부이고, 언제 부를지는 우리가 정합니다. 그래서 설계가 iOS에서도 명세 그대로 성립합니다. 잠금 화면에는 버튼 하나, 질문은 앱 안에서, 그리고 사용자가 실제로 답할 때까지 소리는 계속됩니다.

확인한 것

  • 커스텀 버튼으로 앱을 포그라운드로 올려도 AlarmKit 알람은 계속 울립니다. 문서에서 추론한 것이 아니라 동작으로 확인했습니다.
  • AlarmManager.shared.stop(id:)가 우리가 의존하는 유일한 명시적 정지입니다.
  • 앱을 여는 잠금 화면 버튼이면 충분합니다. 알람이 잠금 화면을 넘겨줄 필요는 애초에 없었습니다.
  • 따라서 앱 안의 질문 흐름은 아직 울리고 있는 알람 위에서 돌아갑니다. 제품이 요구한 것이 정확히 이것입니다.

아직 열려 있는 두 가지

둘 다 설계를 무너뜨리지는 않지만, 아직 해결되지 않았습니다.

  • AlarmKit의 자동 무음 타임아웃은 Apple이 문서화하지 않았습니다. 알람이 영원히 울리지는 않고 어느 시점에 시스템이 알아서 멈춥니다. 그게 얼마인지, 상황에 따라 달라지는지는 실기기에서 측정해야 합니다. 아직 측정하지 않았습니다.
  • stopIntent가 swipe-dismiss에서는 호출되지 않는다는 보고가 있습니다. 그래서 알람 종료 감지를 여기에 얹을 수 없습니다. 종료 시점을 알아야 하는 기능은 다른 방법으로 판단해야 하고, intent가 와주기를 기대하는 대신 그 전제로 설계했습니다.

정작 적어둘 만한 부분

첫 분석은 나쁜 분석이 아니었습니다. 다른 질문에 대한 분석이었습니다.

그 분석이 물은 것은 잠금 화면에서 상호작용 전체가 가능한가 였고, 답은 아니오였습니다. 새 질문은 앱 안에서 상호작용하는 동안 알람이 계속 울릴 수 있는가 였고, 답은 예였습니다. 같은 플랫폼, 같은 API, 같은 사람인데 결론이 반대입니다. 전제가 움직였는데 아무도 새 전제로 분석을 다시 돌리지 않았기 때문입니다.

문제는 “iOS에 대해 잘못 알았다”가 아닙니다. 결론이 자기 전제보다 오래 살아남아서, 이미 철회된 질문에 대한 답이었다는 사실을 잊은 채 플랫폼에 대한 사실처럼 계속 인용된 것입니다. 문서를 못 읽는 것보다 훨씬 저지르기 쉬운 실수이고, 그래서 분명히 적어둡니다. 요구사항이 바뀌면 예전 결론은 근거가 아닙니다. 아무도 더 이상 하지 않는 질문에 대한 낡은 답일 뿐입니다.

  • ios
  • alarmkit
  • flutter
  • spike

맨 위로