View in English

  • 메뉴 열기 메뉴 닫기
  • Apple Developer
검색
검색 닫기
  • Apple Developer
  • 뉴스
  • 둘러보기
  • 디자인
  • 개발
  • 배포
  • 지원
  • 계정
페이지에서만 검색

빠른 링크

5 빠른 링크

비디오

메뉴 열기 메뉴 닫기
  • 컬렉션
  • 주제
  • 전체 비디오
  • 소개

WWDC25 컬렉션으로 돌아가기

  • 소개
  • 요약
  • 자막 전문
  • 코드
  • AlarmKit API로 알람 맞추기

    따르르르르르르르릉! 레시피 앱의 카운트다운 타이머부터 여행 계획 앱의 기상 알람까지, iOS 및 iPadOS 26의 AlarmKit 프레임워크는 잠금 화면 및 Dynamic Island 등에 타이머와 알람을 가져옵니다. 앱의 알람을 생성 및 관리하고, 실시간 현황을 사용자 정의하며, 앱 인텐트 프레임워크를 사용하여 맞춤형 알림 작업을 수행하는 방법을 학습하세요. 이 비디오를 최대한 활용하려면 먼저 WWDC23에서 ‘ActivityKit 알아보기'를 시청하는 것이 좋습니다.

    챕터

    • 0:00 - 인사말
    • 0:32 - 개요
    • 1:39 - 승인
    • 3:06 - 생성
    • 16:32 - 라이프 사이클

    리소스

    • ActivityKit
    • AlarmKit
    • App Intents
    • Creating your first app intent
    • Human Interface Guidelines: Live Activities
    • Scheduling an alarm with AlarmKit
      • HD 비디오
      • SD 비디오

    관련 비디오

    WWDC25

    • 앱 인텐트 알아보기

    WWDC23

    • ActivityKit 알아보기
  • 비디오 검색…

    안녕하세요, System Experience 팀의 엔지니어인 Anton입니다 이 세션에서는 앱에서 알람을 생성하는 프레임워크인 AlarmKit을 소개해 드릴게요 이 비디오에서는 먼저 AlarmKit으로 빌드할 수 있는 경험을 소개하고 앱에서 이 프레임워크를 사용하도록 승인을 받는 방법과 알람을 만드는 방법 및 알람의 라이프사이클을 관리하는 방법을 설명하겠습니다 먼저 경험부터 설명할게요 알람은 고정되었거나 예정된 시간에 발생하는 일에 대한 두드러진 알림으로 일정이나 카운트다운을 기반으로 작동합니다 알람이 시작되면 무음 모드와 현재 포커스를 무시하고 실행되죠

    알람에는 맞춤형 알람 제목과 앱 이름이 함께 표시됩니다 사용자는 알람을 끄거나 선택적인 다시 알림 버튼으로 알람이 나중에 다시 울리도록 할 수 있습니다

    알람은 또한 앱 인텐트로 정의된 동작을 가진 맞춤형 버튼을 갖출 수 있죠

    알람은 스탠바이나 알람이 실행되는 시점에 iPhone에 페어링된 Apple Watch와 같은 다른 시스템 경험에서도 지원됩니다

    알람은 앱이 제공하는 실시간 현황을 통해 맞춤형 카운트다운 인터페이스를 지원합니다 이 인터페이스는 잠금 화면 Dynamic Island 스탠바이에 표시됩니다

    사용자는 기기에서 각 앱에 대해 알람 기능 활성화 여부를 선택하죠 알람에 대해 설명해 드렸으니 앱의 알람 예약에 대해 사용자로부터 승인을 받는 방법을 알려 드리겠습니다

    앱이 알람을 예약하려면 사용자가 승인을 제공하여 동의해야 합니다 승인은 수동으로 요청하거나 첫 알람 생성 시 자동으로 요청되도록 할 수 있습니다 사용자는 언제든지 설정 앱에서 승인 상태를 변경할 수 있습니다 승인을 설정하는 일은 간단합니다 앱의 Info.plist에 NSAlarmKitUsageDescription을 추가하여 알람 표시의 사용 사례를 설명하면 됩니다 앱이 알람을 어떻게 사용할지 간결하고 명확하게 설명하여 사용자가 적합한 결정을 내릴 수 있도록 하세요 수동으로 승인을 요청하려면 AlarmManager의 requestAuthorization API를 사용하세요

    이전에 사용자가 결정을 내린 적이 없다면 사용 설명이 포함된 메시지가 표시됩니다

    알람을 예약하기 전에 승인 상태를 확인하려면 AlarmManager 클래스의 authorizationState를 쿼리하세요

    승인 여부가 결정되지 않았다면 수동으로 승인을 요청할 수 있죠 승인이 부여되면 알람을 예약할 수 있습니다 거부되면 알람이 예약되지 않을 것임을 앱 인터페이스에 명확히 표시해야 합니다 알람을 위해 승인을 설정하는 방법을 설명했으니 이제 알람을 만드는 방법을 알려 드리겠습니다 알람을 만들기 위해 필요한 주요 요소는 카운트다운 시간 특정 날짜나 반복 패턴이 있는 일정 표시 구성 맞춤형 동작의 처리 그리고 관련 소리가 있습니다

    카운트다운 시간부터 시작하겠습니다 알람에는 알람 전 및 알람 후 카운트다운 간격이 설정될 수 있죠 알림이 처음 예약되면 알람 전 카운트다운 시간에 표시될 카운트다운 UI가 표시됩니다 알람 전 시간이 경과하면 알람이 발동되어 구성에 따라 맞춤화된 알람 UI가 표시됩니다

    알람에 대해 다시 알림이 선택되면 알람 후 간격 동안 카운트다운 UI가 다시 표시됩니다

    이 간격이 경과하면 알람이 다시 발동되며 사용자가 다시 알림을 선택하거나 무시할 수 있습니다

    이 예시에서는 알람 전 시간이 10분인 타이머를 설정할게요 타이머가 발동되고 반복되면 5분의 알람 후 시간이 지난 후 다시 발동됩니다 알람을 생성할 때 일정도 제공할 수 있으며 이 일정은 고정되거나 상대적일 수 있습니다

    고정된 일정은 알람이 울릴 특정 미래 날짜를 지정합니다 이 일정은 절대적이며 기기 시간대가 바뀌어도 변경되지 않습니다 이 예시에서는 WWDC 키노트에 맞춰 알람을 설정해 볼게요 6월 9일 오전 9시 41분에 대한 날짜 구성 요소를 생성합니다 그다음 이 구성 요소를 사용하여 날짜를 만듭니다

    그 후 fixed 이니셜라이저를 통해 날짜를 알람 일정에 전달할게요 알람에 상대적인 일정을 지정할 수도 있습니다 여기에는 시간이 포함되며 주간 반복 패턴을 추가할 수도 있습니다 상대적인 일정은 시간대의 변화를 고려합니다

    이 예시에서는 알람이 매주 월요일, 수요일, 금요일 오전 7시에 울리도록 설정하겠습니다 시간 부분의 시간과 분을 설정하고 일일 발생 빈도를 지정합니다

    알람 일정을 지정하는 방법을 다루었으니 알람 표시를 맞춤화하는 방법을 설명할게요 여러 요소를 맞춤화할 수 있습니다 제 앱에서 요리 타이머를 만들고자 합니다 앱이 울릴 때 표시되는 디자인을 맞춤화할게요 먼저 AlarmButton 구조체를 사용하여 알람 버튼을 만듭니다 이 구조체를 통해 버튼의 텍스트, 텍스트 색상 및 시스템 이미지를 지정할 수 있죠

    이 구조체를 사용하여 제목이 Dismiss, 텍스트 색상이 흰색인 중지 버튼을 정의하겠습니다

    또한 알람이 Dynamic Island에 표시될 때 사용될 SF Symbol도 포함시킬게요

    이제 알람 표시를 만들고 음식이 완성되었다는 알람 제목을 설정할 수 있습니다 아까 만든 중지 버튼도 포함할게요 다음으로 알람 속성을 생성하겠습니다 알람 표시를 렌더링하기 위해 필요한 정보라고 생각하시면 됩니다 방금 생성한 알람 표시를 presentation 매개변수에 전달합니다 마지막으로 알람 구성을 생성합니다 여기에는 알람을 예약하기 위해 필요한 모든 요소인 카운트다운 시간, 일정, 속성 등이 포함됩니다 속성을 알람 구성으로 전달하겠습니다

    이제 중지 버튼이 포함된 알람 표시를 설정했습니다

    알람을 통해 일종의 알림만 표시하려는 경우 지금까지 설명한 단계로 알람 표시를 설정하면 됩니다 하지만 알람에 카운트다운 인터페이스를 표시하려면 몇 가지 추가 단계가 필요합니다 제 요리 알람을 통해 이 단계를 설명할게요 먼저 카운트다운을 시작하는 반복 버튼을 알람에 추가할게요

    AlarmButton 구조체를 사용하여 반복 버튼을 생성하고 버튼 제목을 Repeat으로 설정합니다 또한 흰색 텍스트 색상과 반복 아이콘도 지정할게요 색상과 아이콘은 알람이 Dynamic Island에서 울릴 때 표시됩니다

    이번에는 알람 표시를 생성할 때 반복 버튼도 포함합니다

    secondaryButtonBehavior 매개변수는 보조 버튼이 알람을 타이머 반복, 다시 알림과 같은 카운트다운 상태로 전환하는지 아니면 맞춤형 작업을 실행하는지 지정합니다 반복 버튼이 탭되면 알람이 카운트다운으로 전환하도록 할게요 secondaryButtonBehavior를 countdown으로 설정하면 됩니다 이전과 마찬가지로 알람 표시가 있는 알람 속성을 만든 후 이 속성을 포함하는 AlarmConfiguration을 만듭니다 알람에 카운트다운을 시작하는 반복 버튼이 추가되었습니다 이제 카운트다운 UI를 표시하기 위해 실시간 현황을 구현할게요 알람이 카운트다운 기능을 지원하는 경우 앱은 실시간 현황을 사용하여 이를 구현해야 합니다 그 방법을 보여드리겠습니다 음식 조리가 완료되면 알려 주는 카운트다운을 만들겠습니다 카운트다운은 잠금 화면, Dynamic Island, 스탠바이에서 표시되죠

    실시간 현황을 만드는 방법을 알고 있다면 카운트다운을 위한 맞춤형 인터페이스를 만들 수 있습니다 카운트다운 실시간 현황을 앱의 위젯 확장 프로그램에 추가하세요

    위젯 확장 프로그램이 없어도 걱정하지 마세요 WWDC23의 ‘ActivityKit 알아보기’ 비디오를 시청하고 만들어 보세요 그다음 ActivityConfiguration을 설정하고 AlarmAttributes를 메타데이터 유형으로 지정하면 됩니다

    이 경우 ActivityConfiguration을 설정하고 알람 속성과 CookingData 메타데이터를 사용하도록 지정할 수 있습니다 먼저 잠금 화면에 집중하겠습니다 알람 카운트다운은 카운트다운에 있거나 일시 정지될 수 있죠 각 상태를 위한 뷰를 제공하겠습니다

    이를 위해 context 객체에서 알람의 현재 모드를 확인하고 적절한 뷰를 렌더링합니다

    먼저 카운트다운 상태를 처리하고 countdownView를 제공할게요 그다음 일시 정지 상태를 처리하고 pausedView를 제공합니다 이제 Dynamic Island 디자인의 확장 영역을 설정하겠습니다 이 영역에는 알람 제목과 카운트다운, 버튼을 포함합니다 그다음 Dynamic Island의 compact 및 minimal 뷰를 설정합니다 이 뷰에는 알람 카운트다운과 아이콘을 포함합니다 기본 실시간 현황 카운트다운을 설정했으니 이번에는 음식 조리 방식을 표시하는 아이콘을 추가할게요 이 추가 정보는 알람 메타데이터에서 제공할 수 있죠 CookingData는 AlarmMetadata 프로토콜을 준수하는 구조체입니다 조리 방법을 지정하기 위해 문자열 enum을 지정한 다음 frying과 grilling을 옵션으로 포함합니다 그 후 조리 방법을 저장하기 위한 속성을 생성합니다 이제 CookingData 메타데이터를 사용할 수 있습니다 앱에서 알람을 예약할 때 CookingData 객체를 생성한 다음 frying을 조리 방법으로 지정합니다 알람 속성을 생성할 때 이 맞춤형 메타데이터를 포함합니다

    실시간 현황에서 컨텍스트 속성을 통해 맞춤형 메타데이터의 조리 방법에 접근합니다

    조리 방법을 사용하여 아이콘을 생성할 수 있습니다 렌더링된 카운트다운에 조리 메타데이터의 조리 방식으로 생성된 볶는 아이콘이 표시됩니다 이 카운트다운을 잠금 화면과 Dynamic Island에서 사용할게요

    실시간 현황을 사용하여 알람 카운트다운을 설정했고 알람 메타데이터로 카운트다운을 위한 맞춤형 아이콘을 만들었죠

    알람이 카운트다운 기능을 지원하는 경우 시스템은 카운트다운 인터페이스가 표시되도록 합니다 그런데 기기가 재시동되고 처음으로 잠금 해제되기 전처럼 실시간 현황이 표시될 수 없는 상황도 있습니다 이러한 경우에도 시스템의 카운트다운 표시 방식을 맞춤화할 수 있습니다 이제 요리 알람 카운트다운의 시스템 표시를 설정하겠습니다 먼저 AlarmButton 구조체로 일시 정지 버튼을 정의하고 일시 정지 시스템 아이콘을 갖추도록 설정할게요

    그다음 카운트다운 표시를 만들고 음식이 조리 중임을 알려 주는 제목을 설정하고 일시 정지 버튼을 포함할게요

    카운트다운 표시를 정의했으니 이를 알람 속성의 일부로 포함하겠습니다 제 알람이 일시 정지 기능을 지원하므로 요리 카운트다운의 일시 정지 시스템 표시도 설정하겠습니다 카운트다운이 일시 정지되었을 때 사용자가 재개할 수 있도록 할게요 먼저 AlarmButton 구조체를 사용하여 재개 버튼을 정의하고 재생 아이콘을 포함하도록 설정할게요

    그 후 일시 정지 표시를 만들고 제목을 Paused로 설정할게요 또한 재개 버튼도 전달하겠습니다

    이제 일시 정지 표시를 알람 속성에 추가할 수 있습니다 알람, 카운트다운, 일시 정지 시스템 표시를 모두 처리했습니다 여기에서 색조 색상에 대해 설명하겠습니다 색소 색상은 표시 전체에서 사용되며 사용자가 알람을 앱과 연관시키도록 도와줍니다 색조 색상은 알람 속성의 일부로 전달합니다 알람 표시에서 보조 버튼의 채우기 색상을 설정하는 데 사용되거든요

    잠금 화면에서는 보조 버튼의 아이콘, 알람 제목, 카운트다운에 색조를 적용하는 데 사용됩니다 Dynamic Island에서도 유사한 방식으로 사용됩니다 알람의 디자인을 설정하는 방법을 다루었으니 이제 동작을 맞춤화하는 방법을 설명할게요 AlarmKit은 사용자가 알람 버튼을 탭할 때 자체 코드를 실행하는 기능을 제공합니다 이 기능은 앱 인텐트로 구현할 수 있습니다 중지 버튼이나 보조 버튼에 대한 맞춤형 앱 인텐트를 제공할 수 있습니다 이번에는 탭되면 앱을 여는 앱 인텐트를 실행하는 맞춤형 보조 버튼을 만들게요 이 경험을 구현하려면 알람 표시를 수정해야 합니다 먼저 AlarmButton 구조체를 사용하여 열기 버튼을 생성합니다 ‘Open’이라는 제목을 갖고 텍스트 색상은 흰색이며 Dynamic Island에 화살표 아이콘과 함께 표시되는 버튼입니다

    알람 표시를 생성할 때 이 열기 버튼을 보조 버튼으로 포함할게요

    이 버튼이 맞춤형 동작을 실행하도록 보조 버튼의 동작을 custom으로 바꿉니다

    맞춤형 보조 버튼이 완성되었습니다 앱 인텐트로 동작을 설정하여 버튼이 탭되면 앱이 열리도록 할게요 제가 사용하려는 앱 열기용 인텐트입니다 버튼이 탭된 알람의 식별자가 인텐트에 포함되어 있습니다 또한 openAppWhenRun 플래그를 true로 설정하여 인텐트가 실행될 때 앱이 열리도록 지정합니다 앱이 열리면 해당 식별자와 관련된 알람에 대한 자세한 뷰를 표시하는 것처럼 추가 작업을 실행할 수 있죠 방금 생성한 인텐트를 사용해 보겠습니다 알람을 예약할 때 알람 생성 후 추적할 수 있는 고유 식별자를 생성합니다 OpenInApp 인텐트의 인스턴스를 생성하고 알람의 고유 식별자를 전달합니다

    알람 구성을 만들 때 두 번째 인텐트를 포함하여 보조 버튼이 탭될 때 이 인텐트를 실행하라고 시스템에 알립니다

    알람에 맞춤형 열기 버튼을 추가했고 앱을 여는 앱 인텐트를 정의했으며 열기 버튼이 탭되었을 때 시스템이 인텐트를 실행하도록 지정했습니다 이제 알람의 소리를 구성하는 방법을 설명하겠습니다 소리 매개변수를 지정하지 않으면 알람은 기본 시스템 소리를 사용합니다 알람에 맞춤형 소리를 제공할 수도 있습니다 AlarmKit은 알람 표시를 위해 ActivityKit을 사용하므로 AlertSound 구조체를 사용하여 맞춤형 소리를 정의할 수 있습니다

    소리 파일의 이름을 지정해야 하며 이 파일은 앱의 메인 번들이나 앱 데이터 컨테이너의 Library/Sounds 폴더에 있어야 합니다 알람 설정을 완료하면 이제 시스템에서 알람을 관리할 수 있습니다 AlarmManager 클래스를 사용하면 됩니다 알람을 구성한 후 시스템을 통해 알람을 예약할 수 있습니다 예약하려면 알람의 고유 식별자와 이전에 생성한 구성을 사용합니다 이 식별자는 알람의 라이프사이클을 추적하는 데 사용됩니다

    알람의 라이프사이클을 완전히 제어할 수 있습니다 알람을 카운트다운 상태로 전환하거나 취소, 중지, 일시 정지 또는 재개할 수 있죠

    AlarmKit을 사용할 때 참고할 만한 모범 사례를 알려 드리겠습니다 알람은 요리 타이머처럼 특정 간격이 있는 카운트다운이나 기상 알림처럼 반복되는 알림에 적합합니다 그러므로 중요한 알림이나 시급한 알림과 같이 다른 우선 표시되는 알림을 대체할 수는 없습니다

    알람 표시는 명확하게 빌드해야 합니다 알람은 다른 작업보다 우선 표시되므로 사용자가 알람과 이와 관련하여 할 수 있는 작업을 쉽게 이해할 수 있어야 합니다

    알람에서 카운트다운 기능이 지원되는 경우 실시간 현황에 카운트다운의 핵심 요소를 포함하세요 이에는 남은 시간이나 취소 버튼, 일시 정지 또는 재개 버튼이 포함됩니다 오늘은 AlarmKit으로 알람을 만들고 시스템에서 알람의 라이프사이클을 관리하는 방법을 배웠습니다 오늘 배운 내용을 앱에서 활용해 보세요 AlarmKit을 사용하여 앱의 사용 사례에 맞는 알람을 구성하세요 실시간 현황을 사용하여 잠금 화면과 Dynamic Island를 위한 맞춤형 카운트다운 경험을 생성하세요 앱 인텐트를 사용하여 알람에 맞춤형 작업을 추가하세요

    이제 세션을 마치겠습니다 시청해 주셔서 감사합니다

    • 2:41 - Check authorization status

      // Check authorization status
      
      import AlarmKit
      
      func checkAuthorization() {
      
        switch AlarmManager.shared.authorizationState {
          case .notDetermined:
            // Manually request authorization
          case .authorized:
            // Proceed with scheduling
          case .denied:
            // Inform status is not authorized
        }
        
      }
    • 4:08 - Set up the countdown duration

      // Set up the countdown duration
      
      import AlarmKit
      
      func scheduleAlarm() {
      
        /* ... */
      
        let countdownDuration = Alarm.CountdownDuration(preAlert: (10 * 60), postAlert: (5 * 60))
      
        /* ... */
      }
    • 4:40 - Set a fixed schedule

      // Set a fixed schedule
      
      import AlarmKit
      
      func scheduleAlarm() {
      
        /* ... */
      
        let keynoteDateComponents = DateComponents(
          calendar: .current,
          year: 2025,
          month: 6,
          day: 9,
          hour: 9,
          minute: 41)
        let keynoteDate = Calendar.current.date(from: keynoteDateComponents)!
        let scheduleFixed = Alarm.Schedule.fixed(keynoteDate)
      
        /* ... */
      
      }
    • 5:13 - Set a relative schedule

      // Set a relative schedule
      
      import AlarmKit
      
      func scheduleAlarm() {
      
        /* ... */
      
        let time = Alarm.Schedule.Relative.Time(hour: 7, minute: 0)
        let recurrence = Alarm.Schedule.Relative.Recurrence.weekly([
          .monday,
          .wednesday,
          .friday
        ])
        
        let schedule = Alarm.Schedule.Relative(time: time, repeats: recurrence)
      
        /* ... */
      
      }
    • 5:43 - Set up alert appearance with dismiss button

      // Set up alert appearance with dismiss button
      
      import AlarmKit
      
      func scheduleAlarm() async throws {
          typealias AlarmConfiguration = AlarmManager.AlarmConfiguration<CookingData>
          
          let id = UUID()
          let duration = Alarm.CountdownDuration(preAlert: (10 * 60), postAlert: (5 * 60))
          
          let stopButton = AlarmButton(
              text: "Dismiss",
              textColor: .white,
              systemImageName: "stop.circle")
          
          let alertPresentation = AlarmPresentation.Alert(
              title: "Food Ready!",
              stopButton: stopButton)
          
          let attributes = AlarmAttributes<CookingData>(
              presentation: AlarmPresentation(
                  alert: alertPresentation),
              tintColor: Color.green)
          
          let alarmConfiguration = AlarmConfiguration(
              countdownDuration: duration,
              attributes: attributes)
          
          try await AlarmManager.shared.schedule(id: id, configuration: alarmConfiguration)
      }
    • 7:17 - Set up alert appearance with repeat button

      // Set up alert appearance with repeat button
      
      import AlarmKit
      
      func scheduleAlarm() async throws {
          typealias AlarmConfiguration = AlarmManager.AlarmConfiguration<CookingData>
          
          let id = UUID()
          let duration = Alarm.CountdownDuration(preAlert: (10 * 60), postAlert: (5 * 60))
          
          let stopButton = AlarmButton(
              text: "Dismiss",
              textColor: .white,
              systemImageName: "stop.circle")
          
          let repeatButton = AlarmButton(
              text: "Repeat",
              textColor: .white,
              systemImageName: "repeat.circle")
          
          let alertPresentation = AlarmPresentation.Alert(
              title: "Food Ready!",
              stopButton: stopButton,
              secondaryButton: repeatButton,
              secondaryButtonBehavior: .countdown)
          
          let attributes = AlarmAttributes<CookingData>(
              presentation: AlarmPresentation(alert: alertPresentation),
              tintColor: Color.green)
          
          let alarmConfiguration = AlarmConfiguration(
              countdownDuration: duration,
              attributes: attributes)
          
          try await AlarmManager.shared.schedule(id: id, configuration: alarmConfiguration)
      }
    • 9:15 - Create a Live Activity for a countdown

      // Create a Live Activity for a countdown
      
      import AlarmKit
      import ActivityKit
      import WidgetKit
      
      struct AlarmLiveActivity: Widget {
      
        var body: some WidgetConfiguration {
          ActivityConfiguration(for: AlarmAttributes<CookingData>.self) { context in
      
            switch context.state.mode {
            case .countdown:
              countdownView(context)
            case .paused:
              pausedView(context)
            case .alert:
              alertView(context)
            }
      
          } dynamicIsland: { context in 
      
            DynamicIsland {
              DynamicIslandExpandedRegion(.leading) {
                leadingView(context)
              }
              DynamicIslandExpandedRegion(.trailing) {
                trailingView(context)
              }
            } compactLeading: {
              compactLeadingView(context)
            } compactTrailing: {
              compactTrailingView(context)
            } minimal: {
              minimalView(context)
            }
      
          }
        }
      }
    • 10:26 - Create custom metadata for the Live Activity

      // Create custom metadata for the Live Activity
      
      import AlarmKit
      
      struct CookingData: AlarmMetadata {
        let method: Method
          
        init(method: Method) {
          self.method = method
        }
          
        enum Method: String, Codable {
          case frying = "frying.pan"
          case grilling = "flame"
        }
      }
    • 10:43 - Provide custom metadata to the Live Activity

      // Provide custom metadata to the Live Activity
      
      import AlarmKit
      
      func scheduleAlarm() async throws {
          typealias AlarmConfiguration = AlarmManager.AlarmConfiguration<CookingData>
          
          let id = UUID()
          let duration = Alarm.CountdownDuration(preAlert: (10 * 60), postAlert: (5 * 60))
          let customMetadata = CookingData(method: .frying)
          
          let stopButton = AlarmButton(
              text: "Dismiss",
              textColor: .white,
              systemImageName: "stop.circle")
          
          let repeatButton = AlarmButton(
              text: "Repeat",
              textColor: .white,
              systemImageName: "repeat.circle")
          
          let alertPresentation = AlarmPresentation.Alert(
              title: "Food Ready!",
              stopButton: stopButton,
              secondaryButton: repeatButton,
              secondaryButtonBehavior: .countdown)
          
          let attributes = AlarmAttributes<CookingData>(
              presentation: AlarmPresentation(alert: alertPresentation),
              metadata: customMetadata,
              tintColor: Color.green)
          
          let alarmConfiguration = AlarmConfiguration(
              countdownDuration: duration,
              attributes: attributes)
          
          try await AlarmManager.shared.schedule(id: id, configuration: alarmConfiguration)
      }
    • 11:01 - Use custom metadata in the Live Activity

      // Use custom metadata in the Live Activity
      
      import AlarmKit
      import ActivityKit
      import WidgetKit
      
      struct AlarmLiveActivity: Widget {
      
        var body: some WidgetConfiguration { /* ... */ }
      
        func alarmIcon(context: ActivityViewContext<AlarmAttributes<CookingData>>) -> some View {
          let method = context.attributes.metadata?.method ?? .grilling
          return Image(systemName: method.rawValue)
        }
      
      }
    • 12:03 - Set up the system countdown appearance

      // Set up the system countdown appearance
      
      import AlarmKit
      
      func scheduleAlarm() async throws {
        typealias AlarmConfiguration = AlarmManager.AlarmConfiguration<CookingData>
          
        let id = UUID()
        let duration = Alarm.CountdownDuration(preAlert: (10 * 60), postAlert: (5 * 60))
        let customMetadata = CookingData(method: .frying)
      
        let stopButton = AlarmButton(
          text: "Dismiss",
          textColor: .white,
          systemImageName: "stop.circle")
      
        let repeatButton = AlarmButton(
          text: "Repeat",
          textColor: .white,
          systemImageName: "repeat.circle")
      
        let alertPresentation = AlarmPresentation.Alert(
          title: "Food Ready!",
          stopButton: stopButton,
          secondaryButton: repeatButton,
          secondaryButtonBehavior: .countdown)
      
        let pauseButton = AlarmButton(
          text: "Pause",
          textColor: .green,
          systemImageName: "pause")
      
        let countdownPresentation = AlarmPresentation.Countdown(
          title: "Cooking",
          pauseButton: pauseButton)
      
        let attributes = AlarmAttributes<CookingData>(
          presentation: AlarmPresentation(
            alert: alertPresentation,
            countdown: countdownPresentation),
          metadata: customMetadata,
          tintColor: Color.green)
      
        let alarmConfiguration = AlarmConfiguration(
          countdownDuration: duration,
          attributes: attributes)
      
        try await AlarmManager.shared.schedule(id: id, configuration: alarmConfiguration)
        
      }
    • 12:43 - Set up the system paused appearance

      // Set up the system paused appearance
      
      import AlarmKit
      
      func scheduleAlarm() async throws {
        typealias AlarmConfiguration = AlarmManager.AlarmConfiguration<CookingData>
          
        let id = UUID()
        let duration = Alarm.CountdownDuration(preAlert: (10 * 60), postAlert: (5 * 60))
        let customMetadata = CookingData(method: .frying)
      
        let stopButton = AlarmButton(
          text: "Dismiss",
          textColor: .white,
          systemImageName: "stop.circle")
      
        let repeatButton = AlarmButton(
          text: "Repeat",
          textColor: .white,
          systemImageName: "repeat.circle")
      
        let alertPresentation = AlarmPresentation.Alert(
          title: "Food Ready!",
          stopButton: stopButton,
          secondaryButton: repeatButton,
          secondaryButtonBehavior: .countdown)
      
        let pauseButton = AlarmButton(
          text: "Pause",
          textColor: .green,
          systemImageName: "pause")
      
        let countdownPresentation = AlarmPresentation.Countdown(
          title: "Cooking",
          pauseButton: pauseButton)
      
        let resumeButton = AlarmButton(
          text: "Resume",
          textColor: .green,
          systemImageName: "play")
      
        let pausedPresentation = AlarmPresentation.Paused(
          title: "Paused",
          resumeButton: resumeButton)
      
        let attributes = AlarmAttributes<CookingData>(
          presentation: AlarmPresentation(
            alert: alertPresentation,
            countdown: countdownPresentation,
            paused: pausedPresentation),
          metadata: customMetadata,
          tintColor: Color.green)
      
        let alarmConfiguration = AlarmConfiguration(
          countdownDuration: duration,
          attributes: attributes)
      
        try await AlarmManager.shared.schedule(id: id, configuration: alarmConfiguration)
        
      }
    • 14:09 - Add a custom button

      // Add a custom button
      
      import AlarmKit
      import AppIntents
      
      func scheduleAlarm() async throws {
        typealias AlarmConfiguration = AlarmManager.AlarmConfiguration<CookingData>
          
        let id = UUID()
        let duration = Alarm.CountdownDuration(preAlert: (10 * 60), postAlert: (5 * 60))
        let customMetadata = CookingData(method: .frying)
        let secondaryIntent = OpenInApp(alarmID: id.uuidString)
      
        let stopButton = AlarmButton(
          text: "Dismiss",
          textColor: .white,
          systemImageName: "stop.circle")
      
        let openButton = AlarmButton(
          text: "Open",
          textColor: .white,
          systemImageName: "arrow.right.circle.fill")
      
        let alertPresentation = AlarmPresentation.Alert(
          title: "Food Ready!",
          stopButton: stopButton,
          secondaryButton: openButton,
          secondaryButtonBehavior: .custom)
      
        let pauseButton = AlarmButton(
          text: "Pause",
          textColor: .green,
          systemImageName: "pause")
      
        let countdownPresentation = AlarmPresentation.Countdown(
          title: "Cooking",
          pauseButton: pauseButton)
      
        let resumeButton = AlarmButton(
          text: "Resume",
          textColor: .green,
          systemImageName: "play")
      
        let pausedPresentation = AlarmPresentation.Paused(
          title: "Paused",
          resumeButton: resumeButton)
      
        let attributes = AlarmAttributes<CookingData>(
          presentation: AlarmPresentation(
            alert: alertPresentation,
            countdown: countdownPresentation,
            paused: pausedPresentation),
          metadata: customMetadata,
          tintColor: Color.green)
      
        let alarmConfiguration = AlarmConfiguration(
          countdownDuration: duration,
          attributes: attributes,
          secondaryIntent: secondaryIntent)
      
        try await AlarmManager.shared.schedule(id: id, configuration: alarmConfiguration)
        
      }
      
      public struct OpenInApp: LiveActivityIntent {
          public func perform() async throws -> some IntentResult { .result() }
          
          public static var title: LocalizedStringResource = "Open App"
          public static var description = IntentDescription("Opens the Sample app")
          public static var openAppWhenRun = true
          
          @Parameter(title: "alarmID")
          public var alarmID: String
          
          public init(alarmID: String) {
              self.alarmID = alarmID
          }
          
          public init() {
              self.alarmID = ""
          }
      }
    • 16:10 - Add a custom sound

      // Add a custom sound
      
      import AlarmKit
      import AppIntents
      
      func scheduleAlarm() async throws {
        typealias AlarmConfiguration = AlarmManager.AlarmConfiguration<CookingData>
        
        let id = UUID()
        let duration = Alarm.CountdownDuration(preAlert: (10 * 60), postAlert: (5 * 60))
        let customMetadata = CookingData(method: .frying)
        let secondaryIntent = OpenInApp(alarmID: id.uuidString)
      
        let stopButton = AlarmButton(
          text: "Dismiss",
          textColor: .white,
          systemImageName: "stop.circle")
      
        let openButton = AlarmButton(
          text: "Open",
          textColor: .white,
          systemImageName: "arrow.right.circle.fill")
      
        let alertPresentation = AlarmPresentation.Alert(
          title: "Food Ready!",
          stopButton: stopButton,
          secondaryButton: openButton,
          secondaryButtonBehavior: .custom)
      
        let pauseButton = AlarmButton(
          text: "Pause",
          textColor: .green,
          systemImageName: "pause")
      
        let countdownPresentation = AlarmPresentation.Countdown(
          title: "Cooking",
          pauseButton: pauseButton)
      
        let resumeButton = AlarmButton(
          text: "Resume",
          textColor: .green,
          systemImageName: "play")
      
        let pausedPresentation = AlarmPresentation.Paused(
          title: "Paused",
          resumeButton: resumeButton)
      
        let attributes = AlarmAttributes<CookingData>(
          presentation: AlarmPresentation(
            alert: alertPresentation,
            countdown: countdownPresentation,
            paused: pausedPresentation),
          metadata: customMetadata,
          tintColor: Color.green)
      
        let sound = AlertConfiguration.AlertSound.named("Chime")
      
        let alarmConfiguration = AlarmConfiguration(
          countdownDuration: duration,
          attributes: attributes,
          secondaryIntent: secondaryIntent,
          sound: sound)
      
        try await AlarmManager.shared.schedule(id: id, configuration: alarmConfiguration)
        
      }
      
      public struct OpenInApp: LiveActivityIntent {
          public func perform() async throws -> some IntentResult { .result() }
          
          public static var title: LocalizedStringResource = "Open App"
          public static var description = IntentDescription("Opens the Sample app")
          public static var openAppWhenRun = true
          
          @Parameter(title: "alarmID")
          public var alarmID: String
          
          public init(alarmID: String) {
              self.alarmID = alarmID
          }
          
          public init() {
              self.alarmID = ""
          }
      }
    • 0:00 - 인사말
    • AlarmKit은 타이머와 알람을 생성하는 데 사용할 수 있는 새로운 프레임워크입니다. 경험에 대해 알아보고 승인을 받고, 알람을 만들고, 알람의 라이프사이클을 관리하는 방법을 배우세요.

    • 0:32 - 개요
    • 알람은 무음 모드를 무시하고 울리는 예정된 알림으로, 맞춤형 제목과 앱 이름을 표시합니다. 알람은 카운트다운 UI도 표시할 수 있습니다. 사용자는 알람을 끄거나, 다시 알림을 설정하거나, 맞춤형 버튼을 통해 알람과 상호작용할 수 있습니다. 알람은 잠금 화면, Dynamic Island, 스탠바이, Apple Watch에 표시될 수 있으며 사용자는 앱마다 알람에 동의해야 합니다.

    • 1:39 - 승인
    • 앱에서 알람 예약을 활성화하려면 사용자가 이를 승인해야 합니다. 승인은 첫 알람 생성 시 자동으로 요청하거나 AlarmManager의 ‘requestAuthorization’ API를 통해 직접 요청할 수 있습니다. 알람이 사용되는 방법을 설명하는 ‘NSAlarmKitUsageDescription’을 Info.plist에 추가해야 합니다. 사용자는 설정을 통해 승인 상태를 변경할 수 있습니다. 알람을 예약하기 전에 앱에서 승인 상태를 확인할 수 있습니다. 거부된 경우, 앱은 알람이 예약되지 않음을 사용자에게 알려야 합니다.

    • 3:06 - 생성
    • 알람 생성에는 몇 가지 핵심 구성 요소가 포함됩니다. 첫째, 알람에는 카운트다운 간격이 설정될 수 있습니다. 이 기간에는 알람 전 및 알람 후 간격이 모두 포함될 수 있습니다. 카운트다운이 포함된 알람이 예약되면 알람 전 시간에 카운트다운 UI가 표시됩니다. 이 시간이 경과하면 알람이 울리며 맞춤형 알람 UI를 표시합니다. 알람에 대해 다시 알림이 설정되는 경우, 알람 후 시간 동안 알람이 다시 울리기 전에 카운트다운 UI가 다시 표시됩니다. 알람은 고정된 일정 또는 상대적인 일정을 통해 예약할 수 있습니다. 고정된 일정은 미래의 특정 날짜 및 시간을 지정하는 반면, 상대적인 일정은 일반적인 시간을 지정하며 매주 반복되는 패턴을 옵션으로 허용하므로 시간대 변경 시 알람이 자동으로 조정됩니다. 알람 예약과 더불어 알람의 디자인도 맞춤화할 수 있습니다. 맞춤화에는 ‘닫기’ 버튼과 같은 알람 버튼의 생성 및 구성, 알람 제목의 설정이 포함됩니다. 카운트다운 기능이 있는 알람의 경우 ‘반복’ 버튼을 추가할 수도 있습니다. 그런 다음 알람 표시와 속성을 정의하여 알람이 울릴 때 어떻게 표시되고 작동하는지 지정합니다. 알람에 카운트다운 기능이 포함되었다면 실시간 현황을 구현하여 잠금 화면, Dynamic Island 및 스탠바이에 카운트다운 UI를 표시해야 합니다. 여기에는 카운트다운을 위한 맞춤형 인터페이스를 생성하고 적절한 알람 속성으로 ‘ActivityConfiguration’을 설정하는 작업이 포함됩니다. 실시간 현황은 카운트다운의 활성화 또는 일시 정지 여부에 따라 다른 뷰를 표시할 수 있으며, 이를 통해 다양한 기기 상태에서 원활한 사용자 경험을 제공할 수 있습니다. 이 예시에서는 실시간 현황에 추가 정보를 전달하기 위한 맞춤형 메타데이터를 소개합니다. 이 메타데이터에는 맞춤형 enum이 포함되어 있으므로, 알람의 카운트다운 중에 잠금 화면과 Dynamic Island 모두에 enum의 값에 따라 아이콘을 표시할 수 있습니다. 이 세션 부분에서는 버튼이 탭되었을 때 특정 코드를 실행하도록(예: 앱을 열기 위한 코드) 맞춤형 앱 인텐트를 만드는 방법을 설명합니다. 또한 사용자가 맞춤형 소리를 선택하거나 기본 시스템 소리를 사용할 수 있도록 알람 소리를 구성하는 방법도 다룹니다.

    • 16:32 - 라이프 사이클
    • AlarmKit을 사용하면 ‘AlarmManager’ 클래스로 알람을 만들고, 예약하고, 관리할 수 있습니다. 개발자는 알람을 구성하고, 알람을 고유한 식별자를 통해 추적하고, 알람에 특정 상태(카운트다운, 일시 정지, 중지 등)를 적용할 수 있습니다. 모범 사례에는 카운트다운 및 반복되는 알림에 알람 사용하기, 알림을 명확하게 표시하기, 카운트다운 실시간 현황에 모든 필수 정보와 작업 포함하기가 있습니다.

Developer Footer

  • 비디오
  • WWDC25
  • AlarmKit API로 알람 맞추기
  • 메뉴 열기 메뉴 닫기
    • iOS
    • iPadOS
    • macOS
    • tvOS
    • visionOS
    • watchOS
    메뉴 열기 메뉴 닫기
    • Swift
    • SwiftUI
    • Swift Playground
    • TestFlight
    • Xcode
    • Xcode Cloud
    • SF Symbols
    메뉴 열기 메뉴 닫기
    • 손쉬운 사용
    • 액세서리
    • 앱 확장 프로그램
    • App Store
    • 오디오 및 비디오(영문)
    • 증강 현실
    • 디자인
    • 배포
    • 교육
    • 서체(영문)
    • 게임
    • 건강 및 피트니스
    • 앱 내 구입
    • 현지화
    • 지도 및 위치
    • 머신 러닝 및 AI
    • 오픈 소스(영문)
    • 보안
    • Safari 및 웹(영문)
    메뉴 열기 메뉴 닫기
    • 문서(영문)
    • 튜토리얼
    • 다운로드(영문)
    • 포럼(영문)
    • 비디오
    메뉴 열기 메뉴 닫기
    • 지원 문서
    • 문의하기
    • 버그 보고
    • 시스템 상태(영문)
    메뉴 열기 메뉴 닫기
    • Apple Developer
    • App Store Connect
    • 인증서, 식별자 및 프로파일(영문)
    • 피드백 지원
    메뉴 열기 메뉴 닫기
    • Apple Developer Program
    • Apple Developer Enterprise Program
    • App Store Small Business Program
    • MFi Program(영문)
    • News Partner Program(영문)
    • Video Partner Program(영문)
    • Security Bounty Program(영문)
    • Security Research Device Program(영문)
    메뉴 열기 메뉴 닫기
    • Apple과의 만남
    • Apple Developer Center
    • App Store 어워드(영문)
    • Apple 디자인 어워드
    • Apple Developer Academy(영문)
    • WWDC
    Apple Developer 앱 받기
    Copyright © 2025 Apple Inc. 모든 권리 보유.
    약관 개인정보 처리방침 계약 및 지침