# Braze Developer Guide Full Text Consolidated full markdown text for all pages in the Developer Guide collection. # Braze 개발자 가이드 Source: /docs/ko/developer_guide/home/index.md Braze 개발자 가이드 개발자는 여기에서 Braze SDK에 대해 알아야 할 모든 것을 찾을 수 있습니다. 각 SDK는 자체 공개 GitHub 리포지토리에서 호스팅되며, Braze 기능을 테스트하거나 자체 애플리케이션과 함께 구현하는 데 사용할 수 있는 완전히 빌드 가능한 샘플 앱이 포함되어 있습니다. 자세히 알아보려면 참조, 리포지토리 및 샘플 앱 을 확인하세요. Braze로 구축하는 다른 개발자들과 교류하고, 배우고, 영감을 얻고 싶으신가요? Braze 개발자 커뮤니티 에 가입하세요! 이 랜딩 페이지에서는 개발자가 Braze에서 사용할 수 있는 모든 통합을 찾을 수 있습니다. Featured: - Web - Android - Swift # 시작하기 Source: /docs/ko/developer_guide/getting_started/index.md
이 가이드를 따라가거나 [Braze Learning](https://learning.braze.com)에서 [마케터](https://learning.braze.com/path/marketer) 및 [개발자](https://learning.braze.com/path/developer) 학습 경로와 같은 안내 과정을 확인할 수 있습니다.

# 개발자용 SDK 개요 Source: /docs/ko/developer_guide/getting_started/sdk_overview/index.md # [![Braze 학습 과정](https://www.braze.com/docs/ko/ko/assets/img/bl_icon3.png?5f6465f63e399dec15d7020b6f4d2452)](https://learning.braze.com/path/developer/sdk-integration-basics){: style="float:right;width:120px;border:0;" class="noimgborder"}개발자용 SDK 개요 {#braze-learning-course-image_buster-assetsimgbl_icon3png-httpslearningbrazecompathdevelopersdk-integration-basics-stylefloatrightwidth120pxborder0-classnoimgbordersdk-overview-for-developers} > Braze SDK 통합을 시작하기 전에 정확히 무엇을 구축하고 통합하는지 궁금할 수 있습니다. 요구 사항에 맞게 SDK를 추가로 커스터마이즈하는 방법도 궁금할 수 있습니다. 이 문서는 모든 SDK 관련 질문에 대한 답을 찾는 데 도움을 줄 수 있습니다. SDK에 대한 기본적인 개요를 찾고 계신 마케터인가요? 대신 [마케터 개요](https://www.braze.com/docs/ko/ko/user_guide/get_started/sdk_overview/)를 확인하세요. 간단히 말해서, Braze SDK는: * 사용자 데이터를 수집하고 통합된 고객 프로필로 동기화합니다 * 세션 데이터, 기기 정보, 푸시 토큰을 자동으로 수집합니다 * 마케팅 참여 데이터 및 비즈니스에 특화된 커스텀 데이터를 캡처합니다 * 푸시 알림, 인앱 메시지 및 콘텐츠 카드 메시징 채널을 지원합니다 Braze SDK 통합 기본 사항과 핵심 기능에 대한 간략한 소개는 다음 동영상을 시청하세요. ## 앱 성능 {#app-performance} Braze는 앱 성능에 부정적인 영향을 미치지 않습니다. Braze SDK는 설치에 필요한 공간이 매우 작습니다. 수동 네트워크 제어를 허용하는 것 외에도 네트워크 품질에 따라 사용자 데이터를 플러시하는 속도를 자동으로 변경합니다. 또한 네트워크 효율성을 최대로 유지하면서 데이터를 신속하게 기록할 수 있도록 SDK에서 API 요청을 자동으로 일괄 처리합니다. 마지막으로, 각 API 호출에서 클라이언트로부터 Braze로 전송되는 데이터의 양은 매우 적습니다. ## SDK 호환성 {#sdk-compatibility} Braze SDK는 앱에 있는 다른 SDK를 방해하지 않으면서 매우 원활하게 작동하도록 설계되었습니다. 다른 SDK와의 비호환성으로 인한 문제가 발생한다고 생각되면 Braze 고객지원에 문의하세요. ## 기본 분석 및 세션 처리 {#default-analytics-and-session-handling} 특정 사용자 데이터(예: 처음 사용한 앱, 마지막으로 사용한 앱, 총 세션 수, 기기 OS 등)는 SDK에서 자동으로 수집됩니다. 통합 가이드에 따라 SDK를 구현하면 이 [기본 데이터 수집](https://www.braze.com/docs/ko/ko/user_guide/data/unification/user_data/sdk_data_collection/) 기능을 활용할 수 있습니다. 이 목록을 확인하면 사용자에 대한 동일한 정보를 두 번 이상 저장하지 않도록 하는 데 도움이 됩니다. 세션 시작 및 세션 종료를 제외한 모든 자동 추적 데이터는 데이터 포인트 사용량에 포함되지 않습니다. **Note:** 모든 기능이 구성 가능하지만 기본 데이터 수집 모델을 완전히 구현하는 것이 좋습니다.
사용 사례에 필요한 경우 통합이 완료된 후 [특정 데이터의 수집을 제한](#blocking-data-collection)할 수 있습니다. ## 데이터 업로드 및 다운로드 {#data-upload-and-download} Braze SDK는 데이터(세션, 커스텀 이벤트 등)를 캐시하고 주기적으로 업로드합니다. 데이터가 업로드된 후에만 대시보드에서 값이 업데이트됩니다. 업로드 간격은 기기의 상태를 고려하며 네트워크 연결 품질에 따라 결정됩니다. | 네트워크 연결 품질 | 데이터 플러시 간격| |---|---| | 매우 양호 |10초| | 양호 |30초| | 미흡 |60초| {: .reset-td-br-1 .reset-td-br-2 aria-label="Data upload and download" } 네트워크 연결이 없으면 네트워크 연결이 다시 설정될 때까지 데이터가 기기에 로컬로 캐시됩니다. 연결이 다시 설정되면 데이터가 Braze에 업로드됩니다. Braze는 세션이 시작될 때 사용자가 속한 Segment에 따라 SDK로 데이터를 전송합니다. 세션 중에는 새 인앱 메시지가 업데이트되지 않습니다. 그러나 세션 중 사용자 데이터는 클라이언트에서 전송되는 대로 계속 처리됩니다. 예를 들어, 이탈 사용자(앱을 마지막으로 사용한 지 7일이 넘은 사용자)는 앱에 다시 접속한 첫 번째 세션에서 이탈 사용자를 대상으로 하는 콘텐츠를 계속 받게 됩니다. ## 데이터 수집 차단 {#blocking-data-collection} SDK 통합에서 특정 데이터의 자동 수집을 차단하거나 이를 수행하는 프로세스를 허용 목록에 추가할 수 있습니다(단, 권장하지 않음). 분석 데이터를 제거하면 플랫폼의 개인화 및 타겟팅 기능이 저하되므로 데이터 수집 차단은 권장되지 않습니다. 예를 들어: - SDK 중 하나에서 위치를 완전히 통합하지 않으면 언어 또는 위치를 기반으로 메시지를 개인화할 수 없습니다. - 시간대를 통합하지 않으면 사용자의 시간대에 맞춰 메시지를 보내지 못할 수 있습니다. - 특정 기기의 시각적 정보를 통합하지 않으면 메시지 콘텐츠가 해당 기기에 최적화되지 않을 수 있습니다. 제품의 기능을 최대한 활용하려면 SDK를 완전히 통합할 것을 적극 권장합니다. SDK의 특정 부분을 통합하지 않거나 사용자에 대해 [`disableSDK`](https://js.appboycdn.com/web-sdk/latest/doc/modules/braze.html#disablesdk)를 사용할 수 있습니다. 이 메서드는 `disableSDK()` 호출 이전에 기록된 데이터를 동기화하며, 이 페이지 및 향후 페이지 로드에서 이후 모든 Braze Web SDK 호출을 무시합니다. 나중에 데이터 수집을 재개하려면 [`enableSDK()`](https://js.appboycdn.com/web-sdk/latest/doc/modules/braze.html#enablesdk) 메서드를 사용하여 데이터 수집을 재개할 수 있습니다. 이에 대한 자세한 내용은 [웹 추적 비활성화](https://www.braze.com/docs/ko/ko/developer_guide/analytics/managing_data_collection/?sdktab=web) 문서에서 확인할 수 있습니다. [`setDeviceObjectAllowlist`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.configuration/-braze-config/-builder/set-device-object-allowlist.html?query=fun%20setDeviceObjectAllowlist(deviceObjectAllowlist:%20EnumSet%3CDeviceKey%3E):%20BrazeConfig.Builder)를 사용하여 설정된 허용 목록에 따라 기기 오브젝트 키 또는 값의 하위 집합만 전송하도록 SDK를 구성할 수 있습니다. [`setDeviceObjectAllowlistEnabled`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.configuration/-braze-config/-builder/set-device-object-allowlist-enabled.html?query=fun%20setDeviceObjectAllowlistEnabled(enabled:%20Boolean):%20BrazeConfig.Builder)를 통해 활성화해야 합니다. **Important:** 허용 목록이 비어 있으면 Braze로 전송되는 기기 데이터가 **없습니다**. `Braze.Configuration`의 [`configuration.devicePropertyAllowList`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/configuration-swift.class/devicepropertyallowlist)에 적격 필드 집합을 할당하여 SDK에서 수집하는 기기 필드에 대한 허용 목록을 지정할 수 있습니다. 전체 필드 목록은 [`Braze.Configuration.DeviceProperty`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/configuration-swift.class/deviceproperty)에서 정의합니다. 모든 기기 필드 수집을 해제하려면 이 속성의 값을 빈 집합(`[]`)으로 설정합니다. **Important:** 기본적으로 모든 필드는 Braze Swift SDK에 의해 수집됩니다. 일부 기기 속성을 제거하면 SDK 기능이 비활성화될 수 있습니다. 자세한 사용법은 Swift SDK 설명서의 [스토리지](https://www.braze.com/docs/ko/ko/developer_guide/storage/?tab=swift)를 참조하세요. ## 어떤 버전의 SDK를 사용하고 있나요? {#what-version-of-the-sdk-am-i-on} 대시보드에서 **설정 > 앱 설정**으로 이동하여 특정 앱의 SDK 버전을 확인할 수 있습니다. **라이브 SDK 버전**에는 최소 5% 이상의 사용자가 최근 라이브 애플리케이션에서 사용하는 가장 높은 Braze SDK 버전이 나열됩니다. ![워크스페이스에 Swifty라는 앱이 있습니다. 라이브 SDK 버전은 6.6.0입니다.](https://www.braze.com/docs/ko/ko/assets/img/live-sdk-version.png?a647431a93a71779132d1868f65c6003){: style="max-width:80%"} **Tip:** iOS 앱이 있는 경우 **라이브 SDK 버전**이 Swift SDK의 첫 번째 릴리스 버전인 5.0.0 이상이면 기존 [Objective-C iOS SDK](https://www.braze.com/docs/ko/ko/developer_guide/platforms/legacy_sdks/ios/initial_sdk_setup/overview/) 대신 [Swift SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=swift)를 사용 중임을 확인할 수 있습니다. # 플랫폼 개요 Source: /docs/ko/developer_guide/getting_started/platform_overview/index.md # [![Braze 학습 과정](https://www.braze.com/docs/ko/ko/assets/img/bl_icon3.png?5f6465f63e399dec15d7020b6f4d2452)](https://learning.braze.com/path/developer){: style="float:right;width:120px;border:0;" class="noimgborder"}시작하기: 플랫폼 개요 {#braze-learning-course-image_buster-assetsimgbl_icon3png-httpslearningbrazecompathdeveloper-stylefloatrightwidth120pxborder0-classnoimgbordergetting-started-platform-overview} > 이 문서에서는 Braze 플랫폼의 기본적인 부분과 기능에 대해 설명합니다. 이 문서의 링크는 필수 Braze 주제로 연결됩니다. **Tip:** 이 문서와 함께 무료 [개발자 학습 경로](https://learning.braze.com/path/developer) 과정을 확인해 보세요. ## Braze란 무엇인가요? {#what-is-braze} Braze는 고객 참여 플랫폼입니다. 사용자 데이터를 수집하고, 사용자 행동과 패턴을 파악하며, 이를 기반으로 조치를 취할 수 있게 합니다. 플랫폼은 세 가지 주요 구성 요소로 이루어져 있습니다: SDK, 대시보드, 그리고 REST API입니다. Braze에 대한 보다 일반적인 개요를 찾고 있는 마케터라면, [마케터를 위한 시작하기 섹션](https://www.braze.com/docs/ko/ko/user_guide/get_started/)을 참조하세요. ![Braze에는 다양한 레이어가 있습니다. 전체적으로 SDK, API, 대시보드, 파트너 통합으로 구성되어 있습니다. 각각 데이터 수집 레이어, 분류 레이어, 오케스트레이션 레이어, 개인화 레이어 및 동작 레이어의 일부에 기여합니다. 동작 레이어에는 푸시, 인앱 메시지, 연결된 카탈로그, 웹훅, SMS, 이메일 등 다양한 채널이 있습니다.](https://www.braze.com/docs/ko/ko/assets/img/getting-started/getting-started-vertically-integrated-stack.png?7db6090d44479dae3468b2bc7ef53b82){: style="max-width:55%;float:right;margin-left:15px;"} ### SDK [Braze SDK](#integrating-braze)는 모바일 및 웹 애플리케이션에 통합되어 강력한 마케팅, 사용자 관리 및 분석 도구를 제공할 수 있습니다. 간단히 말해, 완전히 통합되면 SDK는 다음을 수행합니다: * 사용자 데이터를 수집하여 통합된 사용자 프로필로 동기화합니다 * 세션 데이터, 기기 정보, 푸시 토큰을 자동으로 수집합니다 * 마케팅 참여 데이터 및 비즈니스에 특화된 커스텀 데이터를 캡처합니다 * 보안을 고려한 설계 및 서드파티에서 침투 테스트를 완료했습니다 * 배터리가 부족하거나 네트워크 속도가 느린 기기에 최적화되어 있습니다 * 보안 강화를 위한 서버 측 JWT 서명을 지원합니다 * 시스템에 대한 쓰기 전용 액세스 권한을 보유합니다(사용자 데이터 검색 불가) * 푸시 알림, 인앱 메시지 및 Content Cards 메시징 채널을 지원합니다 ### 대시보드 사용자 인터페이스 {#dashboard-user-interface} 대시보드는 Braze 플랫폼의 중심에서 모든 데이터 및 상호작용을 제어하는 UI입니다. 마케터는 대시보드를 사용하여 업무를 수행하고 콘텐츠를 제작합니다. 개발자는 대시보드를 사용하여 API 키 및 푸시 알림 자격 증명과 같은 앱 통합을 위한 설정을 관리합니다. 이제 막 시작했다면 팀 관리자가 본인 및 Braze에 액세스해야 하는 다른 모든 팀원을 [대시보드 사용자](https://www.braze.com/docs/ko/ko/user_guide/administer/personal/)로 추가해야 합니다. ### REST API Braze API를 사용하면 대규모로 데이터를 Braze 안팎으로 이동할 수 있습니다. API를 사용하여 백엔드, 데이터 웨어하우스 및 기타 퍼스트파티 및 서드파티 소스에서 업데이트를 가져올 수 있습니다. 또한 API를 사용하여 웹 기반 애플리케이션에서 직접 세분화 목적으로 커스텀 이벤트를 추가할 수 있습니다. API를 통해 메시지를 트리거하고 발송할 수 있으므로 기술 리소스에서 캠페인의 일부로 복잡한 JSON 메타데이터를 포함할 수 있습니다. 또한 API는 모바일 및 웹 SDK가 아닌 HTTP를 통해 사용자가 직접 수행한 작업을 기록할 수 있는 웹 서비스도 제공합니다. 웹훅과 결합하면 앱 경험 안팎에서 사용자의 작업을 추적하고 활동을 트리거할 수 있습니다. [API 가이드](https://www.braze.com/docs/ko/ko/api/home/)에는 사용 가능한 Braze API 엔드포인트와 해당 용도가 나열되어 있습니다. Braze의 구성 요소에 대한 자세한 내용은 다음을 참조하세요: [시작하기: 아키텍처 개요](https://www.braze.com/docs/ko/ko/developer_guide/getting_started/architecture_overview/). ## 데이터 분석 및 조치 {#data-analysis-and-action} Braze에 저장된 데이터는 Braze 고객인 동안 보관되며 세분화, 개인화 및 타겟팅에 사용할 수 있습니다. 이를 통해 해당 정보를 더 이상 사용하지 않도록 선택할 때까지 사용자 프로필 데이터(예: 세션 활동 또는 구매)에 대한 조치를 취할 수 있습니다. 예를 들어, 스트리밍 서비스에서는 각 가입자가 서비스 가입 첫날(수년 전일지라도)부터 시청한 콘텐츠를 추적하고 해당 데이터를 사용하여 관련 메시지를 전달할 수 있습니다. ![Braze 대시보드의 '최근 구매자' Segment와 'Linda를 위한 추천 상품' 이메일이 표시된 휴대폰 화면이 나란히 배치되어 있습니다.](https://www.braze.com/docs/ko/ko/assets/img/getting-started/getting-started-segment.png?49ccc2dc1192203b8b8c942cc1899a61){: style="max-width:80%"} ### 앱 분석 {#app-analytics} Braze 대시보드는 분석 측정기준과 사용자가 설정한 커스텀 이벤트를 기반으로 실시간으로 업데이트되는 그래프를 표시합니다. A/B 테스트, 커스텀 리포팅, 분석 및 자동화된 인텔리전스를 활용한 일관된 측정과 최적화는 고객 참여와 차별화를 지원합니다. ### 사용자 세분화 {#user-segmentation} 세분화를 사용하면 인앱 행동, 인구 통계 데이터 등의 강력한 필터를 기반으로 사용자 그룹을 생성할 수 있습니다. 또한, 원하는 동작이 기본적으로 캡처되지 않는 경우 Braze에서는 인앱 사용자 작업을 "커스텀 이벤트"로 정의할 수 있습니다. "커스텀 속성"을 통한 사용자 특성도 마찬가지입니다. 대시보드에 사용자 Segment가 생성되면 사용자가 정의된 기준을 충족하거나 충족하지 못할 때 Segment 안팎으로 이동하게 됩니다. 예를 들어, 인앱에서 돈을 지출하고 마지막으로 앱을 사용한 지 2주가 넘은 모든 사용자를 포함하는 Segment를 생성할 수 있습니다. 데이터 모델에 대한 자세한 내용은 여기를 확인하세요: [시작하기: 분석 개요](https://www.braze.com/docs/ko/ko/developer_guide/getting_started/architecture_overview/). ## 멀티 채널 메시징 {#multichannel-messaging} Segment를 정의한 후에는 Braze 메시징 도구를 사용하여 역동적이고 개인화된 방식으로 사용자의 참여를 유도할 수 있습니다. Braze는 채널에 구애받지 않는 사용자 중심의 데이터 모델로 설계되었습니다. 메시징은 앱 또는 사이트 내부(예: 인앱 메시지 전송 또는 Content Cards 캐러셀 및 배너와 같은 그래픽 요소를 통해) 또는 앱 경험 외부(예: 푸시 알림 또는 이메일 전송)에서 이루어집니다. 예를 들어, 마케터는 이전 섹션에서 정의한 예시 Segment에 푸시 알림과 이메일을 보낼 수 있습니다. ![앱이나 웹사이트 외부 또는 내부의 모든 채널에서 개인화된 메시지를 생성하고 트리거합니다.](https://www.braze.com/docs/ko/ko/assets/img/getting-started/messaging-channels.png?984cc41c1b4056ebc839f99e21797d1d){: style="border:none" } | 채널 | 설명 | | ---------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | | [Content Cards](https://www.braze.com/docs/ko/ko/user_guide/channels/content_cards/)* | 고객을 방해하지 않고 고도로 타겟팅된 동적 인앱 알림을 전송하세요. | | [이메일](https://www.braze.com/docs/ko/ko/user_guide/channels/email/) | 서식 있는 텍스트 편집기, 드래그 앤 드롭 편집기를 사용하거나 기존 HTML 템플릿 중 하나를 업로드하여 이메일을 작성하고 서식 있는 HTML 메시지를 전송하세요. | | [인앱 메시지](https://www.braze.com/docs/ko/ko/in-app_messages/) | Braze의 맞춤형 기본 사용자 인터페이스를 사용하여 눈에 거슬리지 않는 인앱 알림을 전송하세요. | | [푸시](https://www.braze.com/docs/ko/ko/user_guide/channels/push/) | iOS용 Apple 푸시 알림 서비스(APNs)나 Android용 Firebase 클라우드 메시징(FCM)을 사용하여 메시징 캠페인이나 뉴스 항목에서 푸시 알림을 자동으로 트리거합니다. | | [SMS, MMS, RCS](https://www.braze.com/docs/ko/ko/user_guide/channels/sms_mms_and_rcs/)* | SMS, MMS 또는 RCS를 사용하여 트랜잭션 알림을 보내고, 프로모션을 공유하고, 리마인더를 보내는 등 다양한 작업을 수행하세요. | | [웹 푸시](https://www.braze.com/docs/ko/ko/user_guide/channels/push/platform_specific_resources/web/) | 사용자가 현재 사이트에서 활성 상태가 아니더라도 웹 브라우저 알림을 전송합니다. | | [웹훅](https://www.braze.com/docs/ko/ko/about_webhooks/) | 웹훅을 사용하여 앱 이외의 동작을 트리거해 다른 시스템 및 애플리케이션에 실시간 데이터를 제공합니다. | | [WhatsApp](https://www.braze.com/docs/ko/ko/user_guide/channels/whatsapp/whatsapp_setup/)* | 인기 있는 P2P 메시징 플랫폼인 WhatsApp을 활용하여 사용자 및 고객과 직접 소통하세요. | {: .reset-td-br-1 .reset-td-br-2 aria-label="멀티 채널 메시징" } *추가 기능으로 제공됩니다.* ### 커스텀 가능한 구성 요소 {#customizable-components}

## Braze 통합 {#integrating-braze} Braze는 신속한 통합을 위해 설계되었습니다. 고객사 전체를 기준으로 평균 가치 실현 기간은 6주입니다. 통합 프로세스에 대한 자세한 내용은 [시작하기: 통합 개요](https://www.braze.com/docs/ko/ko/developer_guide/getting_started/integration_overview/)를 참조하세요. ## 북마크에 추가할 리소스 {#resources-to-bookmark} 기술 리소스로서, Braze의 많은 세부 사항에 참여하게 됩니다. 다음은 설명서 외부에서 북마크에 추가할 수 있는 좋은 리소스입니다. 앞으로 Braze를 사용하면서 용어에 대해 궁금한 점이 있을 때를 대비해 [관련 용어](https://www.braze.com/docs/ko/ko/user_guide/get_started/terms_to_know/) 용어집을 잘 보관해 두세요. | 리소스 | 학습 내용 | |---|---| | [SDK 디버깅](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/debugging/) | 통합 문제를 해결할 때, SDK 디버깅 도구가 유용합니다. 항상 준비해 두세요! | | [Braze 공용 GitHub](https://github.com/braze-inc/) | 자세한 통합 정보와 샘플 코드는 GitHub 리포지토리에서 확인할 수 있습니다. | | [Android SDK GitHub 리포지토리](https://github.com/braze-inc/braze-android-sdk/) | Android SDK GitHub 리포지토리입니다. | | [Android SDK 참조](https://appboy.github.io/appboy-android-sdk/kdoc/index.html) | Android SDK용 클래스 문서입니다. | | [iOS(Swift) SDK GitHub 리포지토리](https://github.com/braze-inc/braze-swift-sdk) | Swift SDK GitHub 리포지토리입니다. | | [iOS(Swift) SDK 참조](https://braze-inc.github.io/braze-swift-sdk/) | iOS SDK용 클래스 문서입니다. | | [웹 SDK GitHub 리포지토리](https://github.com/braze-inc/braze-web-sdk) | 웹 SDK GitHub 리포지토리입니다. | | [웹 SDK 참조](https://js.appboycdn.com/web-sdk/5.0/doc/modules/braze.html) | 웹 SDK용 클래스 문서입니다. | | [SDK 체인지로그](https://www.braze.com/docs/ko/ko/developer_guide/changelogs/) | Braze는 중요한 문제와 주요 OS 업데이트에 대한 릴리스 외에도 매월 예측 가능한 릴리스를 제공합니다. | | [Braze API Postman 컬렉션](https://documenter.getpostman.com/view/4689407/SVYrsdsG?version=latest) | 여기에서 Postman 컬렉션을 다운로드하세요. | | [Braze 시스템 상태 모니터](https://braze.statuspage.io/) | 상태 페이지는 인시던트나 장애가 발생할 때마다 업데이트됩니다. 알림을 구독하려면 이 페이지로 이동하세요. | {: .reset-td-br-1 .reset-td-br-2 aria-label="북마크에 추가할 리소스" } # 통합 개요 Source: /docs/ko/developer_guide/getting_started/integration_overview/index.md # [![Braze 학습 과정](https://www.braze.com/docs/ko/ko/assets/img/bl_icon3.png?5f6465f63e399dec15d7020b6f4d2452)](https://learning.braze.com/sdk-integration-basics){: style="float:right;width:120px;border:0;" class="noimgborder"}시작하기: 통합 개요 {#braze-learning-course-image_buster-assetsimgbl_icon3png-httpslearningbrazecomsdk-integration-basics-stylefloatrightwidth120pxborder0-classnoimgbordergetting-started-integration-overview} > 이 문서에서는 온보딩 프로세스에 대한 기본적인 개요를 제공합니다. ![발견, 통합, 품질 보증, 유지 관리 등 4개의 원이 "가치 실현 시간"을 중심으로 구성된 벤 다이어그램입니다.](https://www.braze.com/docs/ko/ko/assets/img/getting-started/getting-started-integrate-flower.png?ea5115f1b341c19262b76cbc61681a7f){: style="max-width:50%;float:right;margin-left:15px;border:none;"} 기술 리소스로서 Braze를 기술 스택에 통합하여 팀의 역량을 강화할 수 있습니다. 온보딩은 크게 네 단계로 나뉩니다: * [발견 및 계획](#discovery): 팀과 협력하여 범위를 조정하고, 데이터 및 Campaign의 구조를 계획하며, 적절한 워크스페이스 구조를 구축합니다. * [통합](#integration): SDK와 API를 통합하고, 메시징 채널을 활성화하며, 데이터 가져오기 및 내보내기를 설정하여 계획을 실행합니다. * [품질 보증](#qa): Braze 플랫폼과 앱 또는 사이트 간의 데이터 및 메시징 루프가 예상대로 작동하는지 확인합니다. * [유지 관리](#maintenance): 마케팅 팀에 Braze를 넘긴 후에도 모든 것이 원활하게 운영되도록 계속 관리해야 합니다.
**Tip:** 모든 조직이 각기 다른 요구사항을 가지고 있다는 것을 알고 있으며, Braze는 특정 요구사항에 맞는 다양한 커스터마이징 옵션을 제공할 수 있도록 제작되었습니다. 통합 시간은 사용 사례에 따라 달라질 수 있습니다. ## 발견 및 계획 {#discovery} 이 단계에서는 팀과 협력하여 온보딩 작업의 범위를 정하고 공통의 목표에 맞춰 모든 이해관계자를 조율합니다. 팀은 사용 사례에 대한 포괄적인 계획을 세우고, 올바른 데이터를 바탕으로 모든 것이 예상대로 구축될 수 있도록 보장합니다. 이 단계에는 프로젝트 리드, CRM 리드, 프론트엔드 및 백엔드 엔지니어링, 제품 소유자, 마케터가 참여합니다. 발견 및 계획 단계에는 평균적으로 약 6주가 소요됩니다. 엔지니어링 리드는 이 단계에서 일주일에 2~4시간 정도 시간을 투자하게 됩니다. 제품을 개발하는 개발자는 발견 및 계획 단계에서 일주일에 10~20시간을 Braze에 투자하게 됩니다. **Tip:** 회사의 온보딩 기간에 Braze는 기술 개요 세션을 진행합니다. 엔지니어는 이 세션에 참석할 것을 적극 권장합니다. 기술 개요 세션에서는 플랫폼 아키텍처의 확장성에 대한 대화를 나누고, 비슷한 규모의 기업이 유사한 사용 사례로 어떻게 성공했는지 실제 사례를 확인할 수 있습니다. ![이메일, 장바구니, 이미지, 지리 위치 등 다양한 채널에 대한 아이콘입니다.](https://www.braze.com/docs/ko/ko/assets/img/getting-started/data-graphic-2.png?4857888e3b2e88b8212850d9df985318){: style="max-width:40%;float:right;margin-left:15px;"} ### Campaign 계획 {#campaign-planning} CRM 팀은 가까운 시일 내에 출시할 메시징 사용 사례를 계획합니다. 여기에는 다음이 포함됩니다: * [채널](https://www.braze.com/docs/ko/ko/user_guide/channels/) (예: 푸시 알림 또는 인앱 메시지) * [전달 방법](https://www.braze.com/docs/ko/ko/user_guide/messaging/campaigns/schedule_your_campaign/) (예: 예약 전달 또는 실행 기반 전달) * [타겟 오디언스](https://www.braze.com/docs/ko/ko/user_guide/audience/segments/) * [성공 측정기준](https://www.braze.com/docs/ko/ko/user_guide/messaging/messaging_fundamentals/conversion_events/) 예를 들어, 어제 첫 세션을 기록한 고객 세그먼트에 매일 오전 10시에 이메일을 보내는 신규 고객 Campaign이 있을 수 있습니다. 전환 이벤트(성공 측정기준)는 세션을 기록하는 것입니다.
**Important:** Campaign 계획 단계가 완료될 때까지 통합을 시작할 수 없습니다. 이 단계에서는 통합 단계에서 구성해야 하는 Braze의 구성요소를 결정합니다. ### 데이터 요구 사항 만들기 {#creating-data-requirements} 그런 다음, CRM 팀은 계획한 Campaign을 시작하는 데 필요한 데이터를 정의하여 데이터 요구 사항을 만들어야 합니다. 이름, 이메일, 생년월일, 국가 등과 같은 많은 일반적인 유형의 사용자 속성은 Braze SDK가 통합된 후 자동으로 추적됩니다. 다른 유형의 데이터는 커스텀 데이터로 정의해야 합니다. 개발자는 팀과 협력하여 추적할 추가적인 커스텀 데이터를 정의합니다. 커스텀 데이터는 사용자 기반을 분류하고 세분화하는 방식에 영향을 미칩니다. 성장 스택 전반에 걸쳐 이벤트 분류를 설정하여 데이터가 Braze로 송수신될 때 시스템과 호환되도록 데이터를 구조화합니다. **Tip:** 여러 도구에서 데이터 명명법을 일관되게 유지하세요. 예를 들어, 데이터 웨어하우스에서 "구매 기간 한정 혜택"을 특정 방식으로 기록할 수 있습니다. 이 형식에 맞게 Braze에서 커스텀 이벤트가 필요한지 결정해야 합니다. [자동 수집 데이터 및 커스텀 데이터](https://www.braze.com/docs/ko/ko/developer_guide/analytics/)에 대해 자세히 알아보세요. ### 커스터마이징 계획 {#customizations-planning} 원하는 커스터마이징에 대해 마케터와 상의합니다. 예를 들어 기본 Braze Content Cards를 구현하고 싶으신가요? 브랜드 가이드라인에 맞게 모양과 느낌을 약간 조정하고 싶으신가요? 구성요소에 대한 완전히 새로운 UI를 개발하고 Braze가 그 분석을 추적하도록 하고 싶으신가요? 커스터마이징 수준에 따라 다양한 수준의 범위가 필요합니다. ### 대시보드 액세스 권한 얻기 {#getting-dashboard-access} Braze 대시보드는 웹 UI 인터페이스입니다. 마케터는 대시보드를 사용하여 업무를 수행하고 콘텐츠를 제작합니다. 개발자는 대시보드를 사용하여 API 키 및 푸시 알림 자격 증명과 같은 앱 통합을 위한 설정을 관리합니다. 팀 관리자가 대시보드에 사용자로 본인 및 Braze에 액세스해야 하는 다른 모든 팀원을 추가해야 합니다. ### 워크스페이스 및 API 키 {#workspaces-and-api-keys} 팀 관리자는 다른 [워크스페이스](https://www.braze.com/docs/ko/ko/user_guide/administer/global/create_and_manage_workspaces/)도 만들 수 있습니다. 워크스페이스는 사용자, Segments, API 키와 같은 데이터를 한 곳에 그룹화합니다. 모범 사례로 동일한 앱의 서로 다른 버전 또는 매우 유사한 앱만 하나의 워크스페이스에 모으는 것을 권장합니다. 중요한 점은 워크스페이스가 여러 플랫폼(예: iOS 및 Android)에 대한 API 키를 제공한다는 것입니다. 연관된 API 키를 사용하여 SDK 데이터를 특정 워크스페이스에 연결할 수 있습니다. 워크스페이스로 이동하여 각 앱의 API 키에 액세스합니다. 각 API 키에 범위를 지정한 작업을 수행할 수 있는 올바른 권한이 있는지 확인합니다. 자세한 내용은 [API 프로비저닝 문서](https://www.braze.com/docs/ko/ko/api/basics/#rest-api-key)를 참조하세요. **Important:** 개발과 프로덕션을 위해 서로 다른 환경을 설정하는 것이 중요합니다. 테스트 환경을 설정하면 온보딩 및 QA 과정에서 실제 비용의 지출을 방지할 수 있습니다. 테스트 환경을 구축하려면 테스트 워크스페이스를 설정하고 프로덕션 워크스페이스에 테스트 데이터를 채우지 않도록 해당 API 키를 사용해야 합니다. ## 통합 {#integration} ![데이터 소스에서 사용자 기기로의 정보 흐름을 나타내는 추상적인 피라미드 그래픽입니다.](https://www.braze.com/docs/ko/ko/assets/img/getting-started/data-graphic.png?de1762afab01f3ce2b61da8f5c8d8f3a){: style="max-width:45%;float:right;margin-left:15px;"} Braze는 iOS 앱, Android 앱, 웹 앱 등을 지원합니다. React Native 또는 Unity와 같은 크로스플랫폼 래퍼 SDK를 사용할 수도 있습니다. 일반적으로 고객은 1~6주 내에 통합을 완료합니다. 많은 고객이 기술 역량과 가용 시간에 따라 단 한 명의 엔지니어만으로 Braze를 통합했습니다. 전적으로 구체적인 통합 범위와 팀이 Braze 프로젝트에 얼마나 많은 시간을 할애하는지에 따라 달라집니다. 다음에 익숙한 개발자가 필요합니다: * 앱 또는 사이트의 네이티브 레이어에서 작업하기 * REST API를 호출하는 프로세스 생성 * 통합 테스트 * JSON 웹 토큰 인증 * 일반적인 데이터 관리 기술 * DNS 레코드 설정 ### CDP 통합 파트너 {#cdp-integration-partners} 많은 고객이 Braze 온보딩을 통합 파트너로서 고객 데이터 플랫폼(CDP)과도 통합할 수 있는 기회로 활용합니다. Braze는 데이터 추적 및 분석을 제공하며, CDP는 추가적인 데이터 라우팅 및 오케스트레이션을 제공할 수 있습니다. Braze는 [mParticle](https://www.braze.com/docs/ko/ko/partners/data_and_analytics/customer_data_platform/mparticle/mparticle/), [Segment](https://www.braze.com/docs/ko/ko/partners/data_and_analytics/customer_data_platform/segment/segment/) 등 다양한 CDP와 원활하게 통합할 수 있습니다. CDP와 병렬 통합을 수행하는 경우, CDP의 SDK에서 Braze SDK로 호출을 매핑합니다. 기본적으로 다음을 수행합니다: * `changeUser`([Android](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze/-i-braze/change-user.html), [iOS](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/changeuser(userid:sdkauthsignature:fileid:line:)/), [웹](https://js.appboycdn.com/web-sdk/latest/doc/modules/braze.html#changeuser))에 식별 호출을 매핑하고 속성을 설정합니다. * `requestImmediateDataFlush`([Android](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze/-i-braze/request-immediate-data-flush.html?query=abstract%20fun%20requestImmediateDataFlush()), [iOS](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/requestimmediatedataflush()), [웹](https://js.appboycdn.com/web-sdk/latest/doc/modules/braze.html#requestimmediatedataflush))에 데이터 플러시 호출을 매핑합니다. * 커스텀 이벤트 또는 구매를 기록합니다. 선택한 플랫폼에 따라 Braze SDK와 선택한 CDP 간의 통합 예제를 사용할 수 있습니다. 자세한 내용은 [CDP 기술 파트너 목록](https://www.braze.com/docs/ko/ko/partners/data_and_analytics/)을 참조하세요. ### Braze SDK 통합 {#braze-sdk-integration} Braze SDK는 두 가지 중요한 기능을 제공합니다: 사용자 데이터를 수집하여 통합 고객 프로필에 동기화하고, 푸시 알림, In-App Messages, Content Cards와 같은 메시징 채널을 지원합니다. **Tip:** 앱 또는 사이트에 완전히 통합된 Braze SDK는 완벽하게 구현된 수준의 정교한 마케팅을 제공합니다. Braze SDK 통합을 연기하면 설명서에 나온 일부 기능을 사용할 수 없습니다. **Note:** 추가 보안 계층을 추가하려면 [SDK 인증](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/authentication/)을 활성화하여 무단 SDK 요청을 방지할 수 있습니다. 이 기능은 웹, iOS, Android, React Native, Flutter, Unity, Cordova, .NET MAUI(Xamarin) 및 Expo를 포함한 모든 주요 플랫폼에서 사용할 수 있습니다. SDK를 구현하는 동안 다음을 수행합니다: * 지원하려는 각 플랫폼에 대한 SDK 통합 코드를 작성합니다. * 각 플랫폼에 대한 메시징 채널을 활성화하여 이메일, SMS, 푸시 알림 및 기타 채널에서 고객과의 상호 작용 데이터를 Braze SDK가 추적하도록 합니다. * 계획된 UI 구성요소 커스터마이징(예: 커스텀 Content Cards)을 생성합니다. 완전한 커스텀 콘텐츠의 경우 SDK의 자동 데이터 수집이 새 구성요소를 인식하지 못하므로 분석을 기록해야 합니다. 기본 구성요소를 참고하여 이 구현을 패턴화할 수 있습니다. ### Braze API 사용 {#using-the-braze-api} Braze를 사용하는 동안 여러 시점에 다양한 작업을 위해 REST API를 사용합니다. Braze API는 다음과 같은 경우에 유용합니다: 1. 기록 데이터 가져오기 2. Braze에서 트리거되지 않는 지속적인 업데이트. 예를 들어, 사용자가 앱에 로그인하지 않고도 사용자 프로필이 VIP로 업그레이드되는 경우 API는 이 정보를 Braze에 전달해야 합니다. [Braze API](https://www.braze.com/docs/ko/ko/api/basics/)로 시작하세요. **Important:** API를 사용하는 동안 요청을 배치로 처리하고 델타 값만 전송해야 합니다. Braze는 전송되는 모든 속성을 다시 작성합니다. 값이 변경되지 않은 커스텀 속성은 업데이트하지 마세요. ### 제품 분석 설정 {#setting-up-product-analytics} Braze는 기본적으로 데이터를 다룹니다. Braze의 데이터는 사용자 프로필에 저장됩니다. 데이터 포인트는 마케터가 단순히 모을 수 있는 "모든" 데이터가 아니라 올바른 데이터를 수집할 수 있도록 지원하는 구조입니다. [데이터 포인트](https://www.braze.com/docs/ko/ko/user_guide/data/infrastructure/data_points/)에 익숙해지세요. ### 레거시 사용자 데이터 마이그레이션 {#migrating-legacy-user-data} Braze [`/users/track` 엔드포인트](https://www.braze.com/docs/ko/ko/api/endpoints/user_data/post_user_track/)를 사용하여 Braze 외부에 기록된 과거 데이터를 마이그레이션할 수 있습니다. 일반적으로 가져오는 데이터의 예로 푸시 토큰과 과거 구매 내역이 있습니다. 이 엔드포인트는 일회성 가져오기 또는 정기적인 배치 업데이트에 사용할 수 있습니다. 대시보드에 한 번의 [CSV 업로드](https://www.braze.com/docs/ko/ko/user_guide/data_and_analytics/user_data_collection/user_import/#importing-a-csv)를 통해 사용자를 가져오고 고객 속성 값을 업데이트할 수도 있습니다. CSV 업로드는 마케터에게 유용한 반면, REST API를 사용하면 더 큰 유연성을 확보할 수 있습니다. ### 세션 추적 설정 {#setting-up-session-tracking} Braze SDK는 "세션 열기" 및 "세션 닫기" 데이터 포인트를 생성합니다. Braze SDK는 정기적으로 데이터를 플러시하기도 합니다. 세션 추적 기본값은 아래 링크를 참조하세요. 모두 커스터마이징할 수 있습니다([Android](https://www.braze.com/docs/ko/ko/developer_guide/analytics/tracking_sessions/?tab=android), [iOS](https://www.braze.com/docs/ko/ko/developer_guide/analytics/tracking_sessions/?tab=swift), [웹](https://www.braze.com/docs/ko/ko/developer_guide/analytics/tracking_sessions/?tab=web)). ### 커스텀 이벤트, 속성 및 구매 이벤트 추적 {#tracking-custom-events-attributes-and-purchase-events} 팀과 협력하여 커스텀 이벤트, 사용자 속성, 구매 이벤트 등 계획한 데이터 스키마를 설정합니다. [커스텀 데이터 스키마](https://www.braze.com/docs/ko/ko/user_guide/data/activation/events/custom_events/)는 대시보드를 사용하여 입력되며 SDK 통합 중에 구현한 내용과 정확히 일치해야 합니다. **Tip:** Braze에서 `external_id`라고 하는 사용자 ID는 알려진 모든 사용자에 대해 설정해야 합니다. 사용자가 앱을 열었을 때 변경되지 않고 액세스할 수 있어야 여러 기기와 플랫폼에서 사용자를 추적할 수 있습니다. 모범 사례는 [사용자 수명 주기](https://www.braze.com/docs/ko/ko/user_guide/data/unification/user_data/user_profile_lifecycle/) 문서를 참조하세요. ### 기타 도구 {#other-tools} 사용 사례에 따라 설정해야 하는 다른 도구가 있을 수 있습니다. 예를 들어 사용자 스토리를 구현하기 위해 [지오펜스](https://www.braze.com/docs/ko/ko/user_guide/engagement_tools/locations_and_geofences#about-locations-and-geofences/)와 같은 도구를 구성해야 할 수 있습니다. 필수 통합 단계를 완료한 후 이러한 추가 도구를 설정할 수 있는 고객이 가장 성공적인 것으로 나타났습니다. ## 품질 보증 {#qa} 통합을 실행할 때 설정한 모든 기능이 예상대로 작동하는지 확인하기 위해 품질 보증을 수행합니다. 이 QA는 데이터 수집과 메시지 채널이라는 두 가지 일반적인 범주로 분류됩니다. **Important:** QA를 시작하기 전에 프로덕션 및 테스트 환경이 설정되어 있는지 확인하세요. | **QA 데이터 수집** | **QA 메시징** | |---------------------------|---------------------------------------------------------------| | 데이터 수집, 저장, 내보내기 방식에 대한 품질 보증을 수행합니다. | 사용자에게 메시지가 올바르게 전송되고 있는지, 모두 잘 보이는지 확인합니다. | | 테스트를 실행하여 데이터가 제대로 저장되었는지 확인합니다. | 사용자 Segments를 만듭니다. | | 세션 데이터가 Braze 내에서 의도한 워크스페이스에 올바르게 귀속되는지 확인합니다. | Campaigns와 Canvases를 성공적으로 시작합니다. | | 세션 시작과 종료가 기록되고 있는지 확인합니다. | 올바른 Campaigns가 올바른 사용자 Segments에 표시되고 있는지 확인합니다. | | 사용자 속성 정보가 사용자 프로필에 대해 올바르게 기록되었는지 확인합니다. | 푸시 토큰이 올바르게 등록되었는지 확인합니다. | | 사용자 프로필에 대해 커스텀 데이터가 올바르게 기록되고 있는지 테스트합니다. | 푸시 토큰이 올바르게 제거되었는지 확인합니다. | | 익명 사용자 프로필을 만듭니다. | 푸시 Campaigns가 기기에 올바르게 전송되고 참여가 기록되는지 테스트합니다. | | `changeUser()` 메서드가 호출될 때 익명 사용자 프로필이 알려진 사용자 프로필이 되는지 확인합니다. | In-App Messages가 전달되고 측정기준이 기록되는지 테스트합니다. | | | Content Cards가 전달되고 측정기준이 기록되는지 테스트합니다. | | | 연결된 콘텐츠를 활성화합니다(예: AccuWeather). | | | 모든 메시지 채널 통합이 제대로 작동하는지 확인합니다. | {: .reset-td-br-1 .reset-td-br-2 aria-label="Quality assurance #qa" } **Note:** SDK 통합에 대한 QA를 수행하는 동안 [SDK 디버거](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/debugging/)를 사용하면 앱에 대한 상세 로깅을 켜지 않고도 문제를 해결할 수 있습니다. ### 마케터에게 Braze 전달 {#passing-braze-off-to-marketers} 플랫폼이나 사이트를 통합한 후에는 마케팅 팀을 참여시켜 플랫폼의 소유권을 넘겨주는 것이 좋습니다. 이 프로세스는 회사마다 다르지만 다음과 같은 사항이 포함될 수 있습니다: * 복잡한 [Liquid 로직](https://www.braze.com/docs/ko/ko/user_guide/personalization_and_dynamic_content/liquid/#about-liquid) 구성 * [이메일 IP 워밍](https://www.braze.com/docs/ko/ko/user_guide/channels/email/email_setup/ip_warming/) 지원 * 다른 이해관계자가 추적되는 데이터의 종류를 이해하는지 확인 ### 미래를 위한 개발 {#develop-for-the-future} 코드베이스를 상속받았는데 초기 개발자의 생각을 전혀 짐작할 수 없었던 적이 있나요? 더 나쁜 경우, 코드를 작성하고 완전히 이해했다가 1년 후에 다시 돌아왔을 때 완전히 당황한 적이 있나요? Braze를 온보딩할 때 데이터, 사용자 프로필, 범위 내 통합과 범위 외 통합, 커스터마이징 작동 방식 등에 관해 내린 종합적인 결정은 그 당시에는 생생하고 명확하게 느껴질 것입니다. 팀이 Braze를 확장하려고 하거나 다른 기술 리소스가 Braze 프로젝트에 할당되면 이러한 정보가 모호해집니다. 기술 개요 세션에서 학습한 정보를 확고히 할 수 있는 리소스를 만드세요. 이 리소스는 팀에 새로 합류하는 개발자의 온보딩 시간을 줄이는 데 도움이 됩니다(또는 현재 Braze 구현을 확장해야 할 때 스스로에게 상기시키는 역할을 합니다). ## 유지 관리 {#maintenance} 마케터에게 인계한 후에도 유지 관리를 위한 리소스 역할을 계속 수행합니다. Braze SDK에 영향을 줄 수 있는 iOS 및 Android 업데이트에 주의를 기울이고 서드파티 공급자가 최신 상태인지 확인해야 합니다. Braze [GitHub](https://github.com/braze-inc/)를 통해 Braze 플랫폼의 업데이트를 추적합니다. 때때로 관리자가 긴급 업데이트 및 버그 수정에 대한 이메일을 Braze에서 직접 받기도 합니다. ## SDK 사용량 제한 {#sdk-rate-limits} ### 2024-2025년 월간 활성 사용자, 전체 MAU, 웹 MAU 및 모바일 MAU {#monthly-active-users-cy-24-25-universal-mau-web-mau-and-mobile-mau} 2024-2025년 월간 활성 사용자, 전체 MAU, 웹 MAU 및 모바일 MAU를 구매한 고객의 경우, Braze는 세션, 사용자 속성, 이벤트 및 기타 사용자 프로필 데이터를 업데이트하는 데 SDK가 사용하는 API 요청에 대해 서버 측 사용량 제한을 적용합니다. 이는 플랫폼의 안정성을 보장하고 빠르고 안정적인 서비스를 유지하기 위한 것입니다. * 시간당 사용량 제한은 계정의 예상 SDK 트래픽에 따라 설정되며, 이는 구매한 월간 활성 사용자 수(MAU), 업종, 계절성 또는 기타 요인에 따라 달라질 수 있습니다. 시간당 사용량 제한에 도달하면 Braze는 다음 시간까지 요청을 스로틀링합니다. * 모든 사용량 제한 요청은 SDK에 의해 자동으로 재시도됩니다. * SDK 요청은 구현에서 수집된 커스텀 데이터의 양과 상관관계가 있습니다. 시간당 사용량 제한에 지속적으로 근접하거나 한도에 도달했다면 다음을 고려해 보세요: * 과도한 데이터 수집을 줄이기 위해 SDK 통합을 검토합니다. * 마케팅 사용 사례에 필수적이지 않은 커스텀 데이터를 차단합니다. * 버스트 사용량 제한은 매우 짧은 시간(즉, 몇 초 이내)에 많은 양의 요청이 도착할 때 적용되는 단기간의 사용량 제한입니다. 버스트 제한이 발생하면 조치를 취할 필요가 없으며, SDK가 곧 다시 시도합니다. * 지속적인 사용량 제한은 버스트 기간보다 긴 롤링 기간(예: 몇 분) 동안 지속적인 요청량을 제어하며, 버스트 제한과 시간당 사용량 제한 사이의 지속적인 트래픽을 부드럽게 하는 데 도움이 됩니다. ### 사용량 제한 확인하기 {#finding-your-rate-limits} 예상 SDK 처리량을 기준으로 현재 제한을 확인하려면 **설정** > **API 키** > **API 및 SDK 제한**으로 이동하세요. 사용 내역을 보려면 **설정** > **API 키** > **API 및 SDK 대시보드**로 이동하세요. ### 더 높은 사용량 제한 요청하기 {#requesting-higher-rate-limits} 더 높은 Braze 사용량 제한이 필요한 경우, Braze 고객지원 또는 고객 성공 매니저에게 연락하고 다음 세부정보를 포함하세요: * 임시 또는 영구적인 증가가 필요한지 여부. * 증가가 필요한 이유. * 영향을 받는 엔드포인트 및 환경. * 대략적인 트래픽 양과 일정(시작 날짜, 기간 및 피크 시간 포함). * 호출을 배치하거나 시간에 따라 트래픽을 분산할 수 있는지 여부. 요청을 제출한 후, Braze는 이를 검토하고 결과를 업데이트합니다. ### 변경 사항 및 지원 {#changes-and-support} Braze는 시스템 안정성을 보호하거나 계정의 데이터 처리량을 늘리기 위해 사용량 제한을 변경할 수 있습니다. 사용량 제한 및 사용량 제한이 비즈니스에 미치는 영향에 대한 질문이나 우려 사항은 Braze 고객지원 또는 고객 성공 매니저에게 문의하세요. # 아키텍처 개요 Source: /docs/ko/developer_guide/getting_started/architecture_overview/index.md # 시작하기: 아키텍처 개요 {#getting-started-architectural-overview} > 이 문서에서는 Braze 기술 스택의 다양한 부분과 구성요소를 설명하며, 관련 문서에 대한 링크를 포함합니다. Braze는 기본적으로 데이터를 다룹니다. Braze 플랫폼은 SDK, REST API 및 파트너 통합을 통해 데이터를 집계하고 활용할 수 있도록 지원합니다. ![Braze에는 다양한 레이어가 있습니다. 전체적으로 SDK, API, 대시보드, 파트너 통합으로 구성되어 있습니다. 각각 데이터 수집 레이어, 분류 레이어, 오케스트레이션 레이어, 개인화 레이어 및 작업 레이어의 일부에 기여합니다. 작업 레이어에는 푸시, 인앱 메시지, 연결된 카탈로그, 웹훅, SMS, 이메일 등 다양한 채널이 있습니다.](https://www.braze.com/docs/ko/ko/assets/img/getting-started/braze_listen_understand_act.png?e78b24fbe4134b6d2666eddda17e18dc){: style="display:block;margin:auto;" } * [데이터 수집](#ingestion): Braze는 다양한 소스에서 데이터를 가져옵니다. * [분류](#classification): 마케팅 팀은 이러한 측정기준을 사용하여 사용자 기반을 동적으로 세분화합니다. * [오케스트레이션](#orchestration): Braze는 이상적인 시간에 다양한 오디언스 세그먼트에 메시지를 지능적으로 조정합니다. * [작업](#action): 마케팅 팀은 데이터를 기반으로 SMS 및 이메일과 같은 다양한 메시징 채널을 통해 콘텐츠를 생성합니다. * [개인화](#personalization): 데이터는 오디언스에 대한 개인화된 정보로 실시간 변환됩니다. * [내보내기](#exporting-data): 그런 다음, Braze는 이 메시징에 대한 사용자의 참여를 추적하고 이를 플랫폼에 다시 공급하여 루프를 생성합니다. 실시간 보고서 및 분석을 통해 이 데이터에 대한 인사이트를 얻을 수 있습니다. 이 모든 기능이 함께 작동하여 사용자 기반과 브랜드 사이에서 성공적인 상호 작용을 만들어 목표를 달성할 수 있습니다. Braze는 수직 통합 스택이라는 맥락에서 이 모든 작업을 수행합니다. 각 레이어를 하나씩 살펴보겠습니다. ## 데이터 수집 {#ingestion} Braze는 Snowflake, Kafka, MongoDB 및 Redis를 활용한 스트리밍 데이터 아키텍처를 기반으로 구축되었습니다. 여러 소스의 데이터는 SDK와 API를 통해 Braze에 로드할 수 있습니다. 플랫폼은 데이터가 중첩되거나 구조화되는 방식에 관계없이 실시간으로 모든 데이터를 처리할 수 있습니다. Braze의 데이터는 고객 프로필에 저장됩니다. **Tip:** Braze는 사용자가 익명 상태일 때부터 앱에 로그인하여 알려진 상태가 될 때까지의 전체 여정에서 데이터를 추적할 수 있습니다. 각 사용자에 대해 Braze에서 `external_id`라고 하는 사용자 ID를 설정해야 합니다. 이 ID는 사용자가 앱을 열 때 변경되지 않고 접근할 수 있어야 하며, 이를 통해 기기와 플랫폼 전반에서 사용자를 추적할 수 있습니다. 모범 사례에 대해서는 [사용자 수명 주기 문서](https://www.braze.com/docs/ko/ko/user_guide/data/unification/user_data/user_profile_lifecycle/)를 참조하세요. ![Braze는 API에서 백엔드 데이터 소스를, SDK에서 프론트엔드 데이터 소스를, Braze 클라우드 데이터 수집에서 데이터 웨어하우스 데이터를, 파트너 통합에서 데이터를 가져옵니다. 이 데이터는 Braze API를 통해 내보내집니다.](https://www.braze.com/docs/ko/ko/assets/img/getting-started/import-export.png?781820845d13ad1965129e15392f0605){: style="display:block;margin:auto;" } **Note:** 이 사람 중심의 고객 프로필 데이터베이스는 실시간 대화형 속도를 지원합니다. Braze는 데이터가 도착하면 값을 미리 계산하고 빠른 검색을 위해 경량 문서 형식으로 결과를 저장합니다. 플랫폼이 처음부터 이러한 방식으로 설계되었기 때문에 대부분의 메시징 사용 사례에 적합합니다. 특히 연결된 콘텐츠, 제품 카탈로그 및 중첩된 속성과 같은 다른 데이터 개념과 결합할 때 더욱 그렇습니다. ### 데이터 소스 분석 {#data-source-breakdown} Braze는 다양한 기능을 위해 서로 다른 데이터 저장 시스템을 사용합니다. 어떤 기능이 어떤 데이터 소스를 사용하는지 이해하는 것은 데이터 관리 및 문제 해결에 중요합니다. #### MongoDB 기반 기능 {#mongodb-powered-features} - 커스텀 이벤트(SDK와 API로 추적됨) - 커스텀 속성 - 고객 프로필 - 구매 이벤트 - 대부분의 세분화 및 타겟팅 기능 #### Snowflake 기반 기능 {#snowflake-powered-features} - [SQL 세그먼트 확장](https://www.braze.com/docs/ko/ko/user_guide/audience/segments/segment_extension/sql_segments/) - [예측 스위트](https://www.braze.com/docs/ko/ko/user_guide/brazeai/) - [개인화된 경로](https://www.braze.com/docs/ko/ko/user_guide/messaging/canvas/canvas_components/experiment_step/personalized_paths/) 및 [개인화된 배리언트](https://www.braze.com/docs/ko/ko/user_guide/engagement_tools/testing/multivariant_testing/optimizations/#personalized-variant) - [AI 개인화된 아이템 추천](https://www.braze.com/docs/ko/ko/user_guide/brazeai/item_recommendations/creating_recommendations/ai/) - [추정 실제 열람률](https://www.braze.com/docs/ko/ko/user_guide/message_building_by_channel/email/reporting_and_analytics/email_reporting/#estimated-real-open-rate)(커스텀 이벤트를 사용하지 않음) **Important:** **데이터 제거 고려사항:** 커스텀 이벤트는 MongoDB에 저장되며 Snowflake 데이터와는 별개입니다. 잘못된 커스텀 이벤트 데이터를 제거해야 하는 경우 MongoDB에서 처리해야 합니다. Snowflake 기반 기능(예: SQL 세그먼트 확장 및 기타 Snowflake 기반 기능)은 Snowflake의 데이터를 사용하며, 이는 별도로 처리됩니다. 한 시스템에서 데이터를 제거한다고 해서 다른 시스템에서 자동으로 제거되는 것은 아닙니다. ### Braze API를 통한 백엔드 데이터 소스 {#backend-data-sources-through-the-braze-api} Braze는 [REST API](https://www.braze.com/docs/ko/ko/api/endpoints/user_data/)를 통해 사용자 데이터베이스, 오프라인 트랜잭션, 데이터 웨어하우스에서 데이터를 가져올 수 있습니다. ### Braze SDK를 통한 프론트엔드 데이터 소스 {#frontend-data-sources-through-braze-sdk} Braze는 [Braze SDK](https://www.braze.com/docs/ko/ko/user_guide/get_started/sdk_overview/)를 통해 사용자의 기기와 같은 프론트엔드 데이터 소스에서 퍼스트파티 데이터를 자동으로 캡처합니다. SDK는 새로운(익명) 사용자를 처리하고 수명 주기 동안 고객 프로필의 데이터를 관리합니다. ### 파트너 통합 {#partner-integrations} Braze에는 "Alloys"라고 부르는 150개 이상의 기술 파트너가 있습니다. [상호 운용 가능한 기술 및 데이터 API](https://www.braze.com/docs/ko/ko/partners/home/)로 구성된 강력한 네트워크를 통해 데이터 피드를 보완할 수 있습니다. ### Braze 클라우드 데이터 수집을 통한 직접 웨어하우스 연결 {#direct-warehouse-connection-through-braze-cloud-data-ingestion} [Braze 클라우드 데이터 수집](https://www.braze.com/docs/ko/ko/user_guide/data/unification/cloud_ingestion/)을 통해 단 몇 분 만에 데이터 웨어하우스에서 플랫폼으로 고객 데이터를 스트리밍하여 관련 사용자 속성, 이벤트 및 구매를 동기화할 수 있습니다. 클라우드 데이터 수집 통합은 중첩된 JSON 및 오브젝트 배열을 포함하는 복잡한 데이터 구조를 지원합니다. 클라우드 데이터 수집은 Snowflake, Amazon Redshift, Databricks 및 Google BigQuery에서 데이터를 동기화할 수 있습니다. ## 분류 {#classification} 분류 레이어를 통해 팀은 Braze를 통과하는 데이터를 기반으로 [세그먼트](https://www.braze.com/docs/ko/ko/user_guide/audience/segments/)라고 하는 오디언스를 동적으로 분류하고 구축할 수 있습니다. **Note:** 분류, 오케스트레이션, 개인화 레이어는 마케팅 팀이 많은 작업을 수행하는 곳입니다. 주로 웹 인터페이스인 Braze 대시보드를 통해 이러한 레이어와 상호 작용합니다. 개발자는 이러한 레이어를 설정하고 커스터마이징하는 역할을 합니다. 이름, 이메일, 생년월일, 국가 등과 같은 일반적인 유형의 사용자 속성은 기본적으로 SDK에 의해 자동으로 추적됩니다. 개발자는 팀과 협력하여 사용 사례에 맞게 추적할 추가적인 커스텀 데이터를 정의합니다. 커스텀 데이터는 사용자 기반이 분류되고 세분화되는 방식에 영향을 미칩니다. 구현 과정에서 이 데이터 모델을 설정하게 됩니다. [자동 수집 데이터 및 커스텀 데이터](https://www.braze.com/docs/ko/ko/developer_guide/analytics/)에 대해 자세히 알아보세요. ## 오케스트레이션 {#orchestration} 오케스트레이션 레이어를 통해 마케팅 팀은 사용자 데이터 및 이전 참여를 기반으로 사용자 여정을 설계할 수 있습니다. 이 작업은 대부분 대시보드 인터페이스를 통해 이루어지지만, [API를 통해 Campaign을 시작](https://www.braze.com/docs/ko/ko/api/api_campaigns/#api-campaigns)할 수 있는 옵션도 있습니다. 예를 들어, 대시보드에서 마케터가 설계한 메시지와 Campaign을 보내는 시점을 백엔드에서 Braze에 알리고, 백엔드 로직에 따라 트리거할 수 있습니다. API 트리거 메시지의 예로는 비밀번호 재설정 또는 배송 확인이 있습니다. **Note:** API 트리거 Campaign은 고급 트랜잭션 사용 사례에 적합합니다. 이를 통해 마케터가 Campaign 카피, 다변량 테스트 및 재적격성 규칙을 Braze 대시보드 내에서 관리하면서 서버 및 시스템에서 해당 콘텐츠의 전달을 트리거할 수 있습니다. 메시지를 트리거하는 API 요청에는 실시간으로 메시지에 템플릿화할 추가 데이터를 포함할 수도 있습니다. ### 피처 플래그 {#feature-flags} Braze에서는 [피처 플래그](https://www.braze.com/docs/ko/ko/developer_guide/feature_flags/)를 통해 일부 사용자에 대한 기능을 원격으로 활성화 또는 비활성화할 수 있습니다. 이를 통해 마케터는 전체 오디언스에 아직 롤아웃하지 않은 기능에 대한 메시징을 사용자 기반의 올바른 세그먼트에 타겟팅할 수 있습니다. 그뿐만 아니라 피처 플래그를 사용하면 추가 코드 배포나 앱 스토어 업데이트 없이 프로덕션에서 기능을 켜고 끌 수 있습니다. 이를 통해 새로운 기능을 안심하고 안전하게 롤아웃할 수 있습니다. ## 개인화 {#personalization} 개인화 레이어는 메시지에서 동적 콘텐츠를 제공하는 기능을 나타냅니다. 널리 사용되는 개인화 언어인 Liquid를 사용하여 팀은 기존 데이터를 동적으로 가져와 각 수신자에게 맞춤화된 메시지를 표시할 수 있습니다. 또한 [연결된 콘텐츠](https://www.braze.com/docs/ko/ko/user_guide/messaging/design_and_edit/personalize/connected_content/)를 사용하면 웹 서버에서 접근할 수 있는 모든 정보나 API를 통해 직접 푸시 알림이나 이메일 등 전송하는 메시지에 삽입할 수 있습니다. 연결된 콘텐츠는 Liquid를 기반으로 구축되며 친숙한 구문을 사용합니다. 이 동적 콘텐츠는 프로그래밍 가능하기 때문에 마케터는 계산된 값, 다른 호출의 응답 또는 제품 카탈로그 항목을 포함할 수 있습니다. 구현 중에 이러한 시스템을 설정한 후에는 마케팅 팀이 기술 팀의 지원 없이 또는 거의 지원 없이도 이를 수행할 수 있습니다. ## 작업 {#action} 작업 레이어에서는 사용자에 대한 실제 메시징을 수행합니다. 작업 레이어의 목적은 이전에 논의된 모든 레이어를 통해 사용할 수 있는 데이터를 기반으로 적절한 시간에 적절한 사용자에게 적절한 메시지를 보내는 것입니다. 메시징은 앱 또는 사이트 내부(예: 인앱 메시지 전송 또는 Content Cards 캐러셀 및 배너와 같은 그래픽 요소를 통해) 또는 앱 경험 외부(예: 푸시 알림 또는 이메일 전송)에서 이루어집니다. ### 메시징 채널 {#messaging-channels} Braze는 채널에 구애받지 않는 사용자 중심의 데이터 모델을 통해 진화하는 기술 환경을 처리하도록 설계되었습니다. 대시보드는 메시지 전달 및 트랜잭션 트리거를 관리합니다. 예를 들어, 마케터는 사용자가 특정 위치 근처에 설정된 지오펜스에 들어갈 때 새로 개장한 매장의 쿠폰을 제공하는 SMS 메시지를 트리거하거나, 사용자가 좋아하는 프로그램의 새 시즌이 나왔음을 알리는 이메일을 보낼 수 있습니다. [Braze SDK](https://www.braze.com/docs/ko/ko/user_guide/get_started/sdk_overview/)는 푸시, 인앱 메시지, Content Cards 등 추가적인 메시징 채널을 지원합니다. 마케팅 팀이 Braze 대시보드를 사용하여 지원되는 모든 메시징 채널에서 Campaign을 조정할 수 있도록 SDK를 앱 또는 사이트에 통합합니다. ![](https://www.braze.com/docs/ko/ko/assets/img/getting_started/channels.png?eb6bc0b731b35124297603041fba54ed) ## 데이터 내보내기 {#exporting-data} 중요한 점은 Braze와의 모든 최종 사용자 상호 작용이 추적되어 참여와 도달 범위를 측정할 수 있다는 것입니다. Braze가 이러한 모든 소스에서 데이터를 집계한 후 다양한 도구를 사용하여 데이터를 기술 스택으로 다시 내보내 루프를 닫을 수 있습니다. ### Currents [Currents](https://www.braze.com/docs/ko/ko/user_guide/data/distribution/braze_currents/)는 세분화된 스트리밍 내보내기를 제공하여 스택의 다른 대상에 지속적으로 공급하는 Braze 선택적 애드온입니다. Currents는 사용자별 이벤트당 원시 데이터 피드로, 5분마다 또는 15,000개의 이벤트마다(둘 중 먼저 도래하는 시점) 데이터를 내보냅니다. Currents의 다운스트림 대상 예로는 Segment, S3, Redshift, Mixpanel 등이 있습니다. ### Snowflake 데이터 공유 {#snowflake-data-sharing} Snowflake의 [보안 데이터 공유](https://www.braze.com/docs/ko/ko/partners/data_and_analytics/data_warehouses/snowflake/) 기능을 사용하면 일반적인 데이터 제공업체 관계에서 발생하는 워크플로 마찰, 장애 지점, 불필요한 비용에 대한 걱정 없이 Snowflake 포털의 데이터에 안전하게 접근할 수 있습니다. 모든 공유는 Snowflake의 고유한 서비스 레이어 및 메타데이터 저장소를 통해 수행됩니다. 실제로 데이터는 계정 간에 복사되거나 전송되지 않습니다. 이것은 중요한 개념입니다. 공유된 데이터는 소비자 계정에 저장 공간을 차지하지 않으므로 월간 데이터 스토리지 요금에 기여하지 않기 때문입니다. 소비자에게 부과되는 유일한 요금은 공유 데이터를 쿼리하는 데 사용되는 컴퓨팅 리소스(즉, 가상 웨어하우스)에 대한 요금입니다. ### Braze 내보내기 API {#braze-export-apis} Braze API는 집계 분석을 프로그래밍 방식으로 내보내고 개별 사용자 데이터를 내보낼 수 있는 [엔드포인트](https://www.braze.com/docs/ko/ko/api/endpoints/export/)를 제공합니다. 모든 규모의 오디언스 및 세그먼트에 대해 이 데이터를 내보낼 수 있습니다. ### CSV {#csvs} 마지막으로, 대시보드에서 집계 수준 데이터를 [CSV](https://www.braze.com/docs/ko/ko/user_guide/data/distribution/export_braze_data/)로 직접 다운로드할 수 있는 옵션이 있습니다. CSV 옵션을 사용하면 팀원이 Braze에서 데이터를 쉽게 내보낼 수 있습니다. **Tip:** CSV 내보내기에는 기본적으로 500,000개의 행 제한이 있지만, API에는 이와 관련된 제한이 없습니다. ## 종합 정리 {#putting-it-all-together} 사용자 중 한 명인 Mel이 방금 제품 발표 소식을 받았다고 가정해 보겠습니다. 그 이면에서는 Braze 플랫폼의 모든 레이어가 함께 작동하여 이 과정이 원활하게 진행되도록 했습니다. Mel의 정보는 CSV 가져오기를 통해 기존 고객 참여 플랫폼에서 Braze로 가져왔습니다. 통합 후 Mel이 앱과 상호 작용할 때마다 더 많은 데이터가 고객 프로필에 추가되었습니다. 제품 발표는 앱에서 유사한 항목에 좋아요를 표시한 모든 고객에게 전송되었습니다. 이 데이터를 커스텀 이벤트로 정의했습니다. SDK가 이 이벤트를 추적하고 사용자 기반을 적절히 세분화했습니다. Braze는 이 발표를 보낼 최적의 시간을 조정하고, Mel의 선호하는 이름을 사용하여 발표를 개인화했습니다. Mel이 발표를 열면 새 제품을 위시리스트에 추가합니다. Braze는 Mel이 이메일을 클릭한 것을 자동으로 추적합니다. SDK는 Mel이 새 제품을 위시리스트에 추가했음을 추적합니다. 사용자가 브랜드와 상호 작용할 때마다 여러분과 사용자는 서로에 대해 더 많이 알아가게 됩니다. ![](https://www.braze.com/docs/ko/ko/assets/img/getting-started/putting-it-all-together.png?2de8ff7b0d97c99fb7f38a3bc32c7e0b) # LLM을 활용한 구축 Source: /docs/ko/developer_guide/getting_started/build_with_llm/index.md # LLM을 활용한 구축 {#building-with-an-llm} > AI 코딩 어시스턴트를 활용하여 Braze 통합 워크플로우를 가속화하세요. Context7을 통해 IDE를 Braze Docs MCP 서버에 연결하고, 개발 환경에서 정확하고 최신의 SDK 가이드를 직접 확인하세요. AI 코딩 어시스턴트는 통합 코드 작성, 문제 해결, Braze SDK 기능 탐색을 도와줄 수 있지만, 올바른 컨텍스트가 제공될 때에만 가능합니다. Braze Docs MCP 서버는 AI 어시스턴트에 Braze 설명서에 대한 직접 접근 권한을 제공하여, 최신 SDK 참조 자료를 기반으로 정확한 코드 스니펫을 생성하고 기술적 질문에 답변할 수 있도록 합니다. ## Braze Docs MCP에 연결하기 {#connecting-to-the-braze-docs-mcp} [Context7](https://context7.com/braze-inc/braze-docs)은 AI 어시스턴트와 Braze 설명서 라이브러리 사이의 가교 역할을 합니다. IDE의 MCP 구성에 Context7을 추가하면 AI 어시스턴트가 전체 Braze 설명서를 쿼리하여 관련 SDK 참조 자료, 코드 예제 및 통합 가이드를 필요할 때마다 검색할 수 있습니다. ### Context7 설정하기 {#setting-up-context7} Context7을 통해 AI 어시스턴트를 Braze Docs MCP에 연결하려면 IDE의 `mcp.json` 파일에 다음 구성을 추가하세요. [Cursor](https://cursor.com/)에서 **Settings** > **Tools and Integrations** > **MCP Tools** > **Add Custom MCP**로 이동한 후 다음 스니펫을 추가하세요: ```json { "mcpServers": { "context7": { "command": "npx", "args": ["-y", "@upstash/context7-mcp@latest"] } } } ``` 구성을 저장하고 Cursor를 다시 시작하세요. 프롬프트에 `use context7`을 포함하면 AI 어시스턴트가 Context7을 통해 Braze 설명서에 접근할 수 있습니다. Claude Desktop에서 **Settings** > **Developer** > **Edit Config**로 이동한 후, `claude_desktop_config.json` 파일에 다음 내용을 추가하세요: ```json { "mcpServers": { "context7": { "command": "npx", "args": ["-y", "@upstash/context7-mcp@latest"] } } } ``` 구성을 저장하고 Claude Desktop을 다시 시작하세요. VS Code의 `settings.json` 또는 `.vscode/mcp.json` 파일에 다음 내용을 추가하세요: ```json { "mcpServers": { "context7": { "command": "npx", "args": ["-y", "@upstash/context7-mcp@latest"] } } } ``` 구성을 저장하고 VS Code를 다시 시작하세요. **Note:** Context7은 [Braze MCP 서버](https://www.braze.com/docs/ko/ko/developer_guide/mcp_server/)와 다릅니다. Context7은 AI 어시스턴트에게 **Braze 설명서**에 대한 접근 권한을 제공하며, Braze MCP 서버는 Campaign(캠페인), Segments, 분석 등 **Braze 워크스페이스 데이터**에 대한 읽기 전용 접근 권한을 제공합니다. 두 가지를 함께 사용하면 보다 완벽한 AI 지원 개발 환경을 경험할 수 있습니다. ## Braze SDK 개발을 위한 프롬프트 작성하기 {#writing-prompts-for-braze-sdk-development} Context7을 설정한 후, 프롬프트에 `use context7`을 포함하여 AI 어시스턴트가 Braze 설명서를 컨텍스트로 가져오도록 지시하세요. 다음 예시는 일반적인 SDK 작업에 효과적인 프롬프트를 작성하는 방법을 보여줍니다. ### React Native SDK {#react-native-sdk} 이 프롬프트는 [Braze React Native SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=react%20native)의 일반적인 통합 작업을 보여줍니다. #### SDK 초기화 {#initializing-the-sdk} ```text Using the Braze React Native SDK, show me how to initialize the SDK in my App.tsx with an API key and custom endpoint. Include the configuration for automatic session tracking. Use context7. ``` #### 속성을 포함한 커스텀 이벤트 로깅 {#logging-custom-events-with-properties} ```text I need to track user activity in my React Native app using the Braze React Native SDK. Show me how to log a custom event called "ProductViewed" with properties for product_id, category, and price. Use context7. ``` #### 푸시 알림 설정 {#setting-up-push-notifications} ```text Using the Braze React Native SDK, walk me through requesting push notification permissions on both iOS and Android 13+. Include the code for registering the push token with Braze. Use context7. ``` #### 인앱 메시지 처리 {#handling-in-app-messages} ```text Show me how to subscribe to in-app messages using the Braze React Native SDK, including how to log impressions and button clicks programmatically. Use context7. ``` ### 웹 SDK {#web-sdk} 이 프롬프트는 [Braze 웹 SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=web)의 일반적인 통합 작업을 보여줍니다. #### SDK 초기화 ```text Using the Braze Web SDK, show me how to initialize the SDK with braze.initialize(), including the API key, base URL, and options for enabling logging and automatic in-app message display. Use context7. ``` #### 커스텀 이벤트 및 구매 추적 {#tracking-custom-events-and-purchases} ```text Using the Braze Web SDK, create a JavaScript module that logs a custom event called "VideoPlayed" with properties for video_id, duration_seconds, and completion_percentage. Also show how to log a purchase with product ID, price, currency code, and quantity. Use context7. ``` #### 웹 푸시 등록 {#registering-for-web-push} ```text Using the Braze Web SDK, provide the HTML and JavaScript needed to register a user for web push notifications after they click a "Subscribe to updates" button. Include the service worker setup. Use context7. ``` #### 사용자 속성 관리 {#managing-user-attributes} ```text Using the Braze Web SDK, show me how to set standard user attributes (first name, email, country) and custom user attributes (favorite_genre, subscription_tier) for the current user. Use context7. ``` ## 일반 텍스트 설명서 {#plain-text-documentation} Braze 개발자 가이드 설명서를 AI 도구 및 LLM에 최적화된 일반 텍스트 파일로 이용할 수 있습니다. 이 파일들은 HTML 렌더링의 부담 없이 AI 어시스턴트가 구문 분석하고 이해할 수 있는 형식으로 Braze 설명서를 제공합니다. | 파일 | 설명 | |------|-------------| | [llms.txt](https://www.braze.com/docs/ko/ko/developer_guide/llms.txt) | 제목과 설명이 포함된 Braze 개발자 설명서 페이지 색인입니다. 사용 가능한 설명서를 찾기 위한 출발점으로 활용하세요. | | [llms-full.txt](https://www.braze.com/docs/ko/ko/developer_guide/llms-full.txt) | LLM이 소비할 수 있도록 포맷팅된 단일 일반 텍스트 파일로 제공되는 완전한 Braze 개발자 설명서입니다. | {: .reset-td-br-1 .reset-td-br-2 aria-label="일반 텍스트 설명서" } 이 파일들은 AI 도구가 설명서에 접근할 수 있도록 하는 새로운 표준인 [llms.txt 표준](https://llmstxt.org/)을 따릅니다. 프롬프트에서 이 파일들을 직접 참조하거나, 내용을 LLM에 붙여넣어 컨텍스트로 활용할 수 있습니다. # 커스터마이징 개요 Source: /docs/ko/developer_guide/getting_started/customization_overview/index.md # 커스터마이징 개요 {#customization-overview} > Braze의 거의 모든 기능은 완벽하게 커스터마이징할 수 있습니다! 이 커스터마이징 가이드의 문서에서는 구성과 커스터마이징의 조합을 통해 Braze 경험을 개선하는 방법을 보여줍니다. 이 과정에서 마케팅 팀과 엔지니어링 팀은 긴밀히 협력하여 Braze 메시징 채널을 정확히 어떻게 커스터마이징할지 조율해야 합니다. **Note:** Braze SDK는 강력한 툴킷이지만, 크게 두 가지 중요한 기능을 제공합니다. 여러 플랫폼에서 통합 고객 프로필로 사용자 데이터를 수집 및 동기화하고, 인앱 메시지, 푸시 알림, Content Cards와 같은 메시징 채널을 처리합니다. 커스터마이징 가이드의 문서에서는 이미 [SDK 구현 프로세스](https://www.braze.com/docs/ko/ko/developer_guide/home/)를 완료했다고 가정합니다. 모든 Braze 구성요소는 접근성, 적응성 및 커스터마이징이 가능하도록 설계되었습니다. 따라서 기본 `BrazeUI` 구성요소로 시작하여 브랜드 요구 사항과 사용 사례에 맞게 커스터마이징하는 것을 권장합니다. Braze에서는 관련 노력과 제공되는 유연성 수준에 따라 커스터마이징을 세 가지 접근 방식으로 분류합니다. 이러한 접근 방식을 "Crawl", "Walk", "Run"이라고 합니다. - **Crawl:** 기본 스타일링 옵션을 활용하여 빠르고 적은 노력으로 구현합니다. - **Walk:** 기본 템플릿에 커스텀 스타일을 추가하여 브랜드 경험에 더 잘 어울리도록 합니다. - **Run:** 스타일부터 동작, 크로스채널 연결에 이르기까지 메시징의 모든 부분을 커스터마이징합니다. ![캡션 이미지 및 이미지 전용 Content Cards를 보여주는 샘플 금융 앱](https://www.braze.com/docs/ko/ko/assets/img_archive/cc_pyrite_crawl.png?5178761170e9c604d535a626ebb023b9){: style="max-width:35%;float:right;margin-left:15px;border:none;"} Crawl 접근 방식은 커스터마이징의 권한을 마케터에게 직접 부여합니다. 앱이나 사이트에 Braze 메시징 채널을 통합하기 위해 사전에 약간의 개발 작업이 필요하지만, 이 접근 방식을 사용하면 더 빠르게 시작하고 실행할 수 있습니다. 마케터는 대시보드를 통해 메시지의 콘텐츠, 오디언스 및 타이밍을 결정합니다. 하지만 스타일링 옵션은 제한적입니다. 이 접근 방식은 개발자 리소스가 제한적이거나 간단한 콘텐츠를 빠르게 공유하려는 팀에 가장 적합합니다.
커스터마이징 개요
커스터마이징 설명
노력 낮음
개발자 작업 0~1시간
카드 스타일 기본 Braze 템플릿을 사용합니다.
동작 기본 동작 옵션 중에서 선택합니다.
분석 추적 분석은 Braze에서 캡처됩니다.
키-값 페어 선택 사항이며, 추가 UI/UX 커스터마이징을 지원합니다.
![커스터마이징된 Content Cards를 보여주는 샘플 금융 앱](https://www.braze.com/docs/ko/ko/assets/img_archive/cc_pyrite_walk.png?f4e47488e8475fff8cc3d02d4241de74){: style="max-width:35%;float:right;margin-left:15px;border:none;"} 하이브리드 구현 방식인 Walk 접근 방식에서는 마케팅 팀과 개발자 팀이 함께 참여하여 앱 또는 사이트의 브랜딩에 맞춥니다. 구현 과정에서 개발자는 메시지 채널의 모양과 느낌을 브랜드에 더 부합하도록 업데이트하는 커스텀 코드를 작성합니다. 여기에는 글꼴 유형, 글꼴 크기, 둥근 모서리 및 색상 변경이 포함됩니다. 이 접근 방식은 여전히 기본 옵션을 사용하되, 프로그래밍 방식의 템플릿 스타일링을 적용합니다. 마케터는 Braze 대시보드에서 직접 오디언스, 콘텐츠, 클릭 시 동작 및 만료를 계속 관리할 수 있습니다.
커스터마이징 개요
커스터마이징 설명
노력 낮음
개발자 작업 0~4시간
UI Braze 템플릿을 사용하거나 개발자가 직접 만든 템플릿을 사용합니다.
동작 기본 동작 옵션 중에서 선택합니다.
분석 추적 기본 분석은 Braze에서 캡처됩니다.
키-값 페어 선택 사항이며, 추가 UI/UX 커스터마이징을 지원합니다.
![이메일 캡처가 포함된 커스텀 Content Cards를 보여주는 샘플 금융 앱](https://www.braze.com/docs/ko/ko/assets/img_archive/cc_pyrite_run.png?571e20da6976b22e1c63ba76529a87f9){: style="max-width:35%;float:right;margin-left:15px;border:none;"} Run 접근 방식에서는 개발자가 주도적으로 사용자 경험을 완전히 제어합니다. 커스텀 코드가 메시지의 모양, 동작 방식, 다른 메시징 채널과의 상호 작용 방식(예: 푸시 알림을 기반으로 Content Card 트리거)을 결정합니다. 새로운 유형의 Content Cards나 맞춤형 UI가 포함된 인앱 메시지 등 완전히 새로운 커스텀 콘텐츠를 생성하는 경우, Braze SDK는 자동으로 [분석을 추적하지](https://www.braze.com/docs/ko/ko/developer_guide/analytics/) 않습니다. 마케터가 Braze 대시보드에서 노출 횟수, 클릭 수, 해제 등의 측정기준에 계속 액세스할 수 있도록 프로그래밍 방식으로 분석을 처리해야 합니다. SDK에서 이 데이터를 Braze에 다시 전달하도록 Braze SDK의 분석 메서드를 호출하세요. 각 메시징 채널에는 이를 용이하게 하는 분석 문서가 있습니다.
커스터마이징 개요
커스터마이징 설명
노력 사용 사례에 따라 다릅니다.
개발자 작업 적은 노력: 1~4시간
보통 노력: 4~8시간
많은 노력: 8시간 이상
UI 커스텀
동작 커스텀
분석 추적 커스텀
키-값 페어 필수
**Tip:** 개발자와 구현자가 Braze용 커스텀 콘텐츠를 만들 때 마케터와 부서 간 협업의 기회가 생깁니다. 예를 들어 특정 구성요소에 대한 새로운 UI나 새로운 기능을 개발하는 경우, 새로운 동작과 백엔드와의 통합 방법을 문서화하여 팀이 성공할 수 있도록 준비하세요. # Braze SDK 튜토리얼 Source: /docs/ko/developer_guide/tutorials/index.md
# Braze SDK 통합 Source: /docs/ko/developer_guide/sdk_integration/index.md # ![Braze 로고](https://www.braze.com/docs/ko/ko/assets/Braze_Primary_Icon_BLACK.svg?919c4e99e8ae11eafc3470e63b4fedce){: style="float:right;width:120px;border:0;" class="noimgborder"}Braze SDK 통합 {#braze-logo-image_buster-assetsbraze_primary_icon_blacksvg-stylefloatrightwidth120pxborder0-classnoimgborderintegrate-the-braze-sdk} > Braze SDK를 통합하는 방법을 알아보세요. 각 SDK는 자체 공개 GitHub 리포지토리에서 호스팅되며, Braze 기능을 테스트하거나 자체 애플리케이션과 함께 구현하는 데 사용할 수 있는 완전히 빌드 가능한 샘플 앱이 포함되어 있습니다. 자세히 알아보려면 [참조, 리포지토리 및 샘플 앱](https://www.braze.com/docs/ko/ko/developer_guide/references/)을 확인하세요. SDK에 대한 보다 일반적인 정보는 [시작하기: 통합 개요](https://www.braze.com/docs/ko/ko/developer_guide/getting_started/integration_overview/)를 참조하세요. 미러링된 SDK README 콘텐츠는 [리포지토리 가이드](https://www.braze.com/docs/ko/ko/developer_guide/sdk_repository_guides/)를 참조하세요. **Tip:** SDK를 통합한 후에는 [SDK 인증](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/authentication/)을 활성화하여 무단 SDK 요청을 방지함으로써 추가적인 보안 계층을 적용할 수 있습니다. SDK 인증은 웹, Android, Swift, React Native, Flutter, Unity, Cordova, .NET MAUI(Xamarin) 및 Expo에서 사용할 수 있습니다. ## About the Web Braze SDK The Web Braze SDK lets you collect analytics and display rich in-app messages, push, and Content Card messages to your web users. For more information, see [Braze JavaScript reference documentation](https://js.appboycdn.com/web-sdk/latest/doc/modules/braze.html). **Note:** This guide uses code samples from the Braze Web SDK 4.0.0+. To upgrade to the latest Web SDK version, see [SDK Upgrade Guide](https://github.com/braze-inc/braze-web-sdk/blob/master/UPGRADE_GUIDE.md). ## Integrate the Web SDK You can integrate the Web Braze SDK using the following methods. For additional options, see [other integration methods](#web_other-integration-methods). - **Code-based integration:** Integrate the Web Braze SDK directly in your codebase using your preferred package manager or the Braze CDN. This gives you full control over how the SDK is loaded and configured. - **Google Tag Manager:** A no-code solution that lets you integrate the Web Braze SDK without modifying your site's code. For more information, see [Google Tag Manager with the Braze SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/google_tag_manager/). **Important:** We recommend using the [NPM integration method](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?subtab=package%20manager&sdktab=web). Benefits include storing SDK libraries locally on your website, providing immunity from ad-blocker extensions, and contributing to faster load times as part of bundler support. ### Step 1: Install the Braze library You can install the Braze library using one of the following methods. However, if your website uses a `Content-Security-Policy`, review the [Content Security Policy](https://www.braze.com/docs/ko/ko/developer_guide/platforms/web/content_security_policy/) before continuing. **Important:** While most ad blockers do not block the Braze Web SDK, some more-restrictive ad blockers are known to cause issues. If your site uses NPM or Yarn package managers, you can add the [Braze NPM package](https://www.npmjs.com/package/@braze/web-sdk) as a dependency. Typescript definitions are now included as of v3.0.0. For notes on upgrading from 2.x to 3.x, see our [changelog](https://github.com/braze-inc/braze-web-sdk/blob/master/UPGRADE_GUIDE.md). ```bash npm install --save @braze/web-sdk # or, using yarn: # yarn add @braze/web-sdk ``` Once installed, you can `import` or `require` the library in the typical fashion: ```typescript import * as braze from "@braze/web-sdk"; // or, using `require` const braze = require("@braze/web-sdk"); ``` Add the Braze Web SDK directly to your HTML by referencing our CDN-hosted script, which loads the library asynchronously. **Important:** The default **Prevent Cross-Site Tracking** setting in Safari can prevent in-app message types like Banners and Content Cards from displaying when you use the CDN integration method. To avoid this issue, use the NPM integration method so that Safari does not classify these messages as cross-site traffic and your web users can see them in all supported browsers. ### Step 2: Initialize the SDK After the Braze Web SDK is added to your website, initialize the library with the API key and [SDK endpoint URL](https://www.braze.com/docs/ko/ko/user_guide/administrative/access_braze/sdk_endpoints) found in **Settings** > **App Settings** within your Braze dashboard. For a complete list of options for `braze.initialize()`, along with our other JavaScript methods, see [Braze JavaScript documentation](https://js.appboycdn.com/web-sdk/latest/doc/modules/braze.html#initialize). **Note:** **Custom domains for Web SDK requests are not supported**: The Web SDK `baseUrl` must be a Braze SDK endpoint (for example, `sdk.iad-05.braze.com`). Braze does not support routing Web SDK traffic through a customer-owned domain via CNAME records. If you need Web SDK requests to originate from your own domain, contact Braze support. ```javascript // initialize the SDK braze.initialize('YOUR-API-KEY-HERE', { baseUrl: "YOUR-SDK-ENDPOINT-HERE", enableLogging: false, // set to `true` for debugging allowUserSuppliedJavascript: false, // set to `true` to support custom HTML messages }); // Enable automatic display of in-app messages // Required if you want in-app messages to display automatically when triggered braze.automaticallyShowInAppMessages(); // if you use Content Cards braze.subscribeToContentCardsUpdates(function(cards){ // cards have been updated }); // optionally set the current user's external ID before starting a new session // you can also call `changeUser` later in the session after the user logs in if (isLoggedIn){ braze.changeUser(userIdentifier); } // `openSession` should be called last - after `changeUser` and `automaticallyShowInAppMessages` braze.openSession(); ``` **Important:** **In-App Message Display**: To display in-app messages automatically when they're triggered, you must call `braze.automaticallyShowInAppMessages()`. Without this call, in-app messages don't display automatically. If you want to manage message display manually, remove this call and use `braze.subscribeToInAppMessage()` instead. For more information, see [In-app message delivery](https://www.braze.com/docs/ko/ko/developer_guide/in_app_messages/delivery/). #### Troubleshooting missing sessions for anonymous users If you're seeing "Session missing" behavior, or you're not able to track the session for users who stay anonymous on web, make sure your integration calls `braze.openSession()` during initialization. - **Scenario:** Anonymous users can return a Braze ID, but session data is blank or missing. - **Cause:** The implementation doesn't call `braze.openSession()`. - **Resolution:** Always call `braze.openSession()` after initialization (and after `braze.changeUser()` if you set an external ID). For more information, see [Step 2: Initialize the SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=web&tab=code-based%20integration#step-2-initialize-the-sdk). **Important:** Anonymous users on mobile or web devices may be counted towards your [MAU](https://www.braze.com/docs/ko/ko/user_guide/data_and_analytics/reporting/understanding_your_app_usage_data/#monthly-active-users). As a result, you may want to conditionally load or initialize the SDK to exclude these users from your MAU count. ### Prerequisites Before you can use this integration method, you'll need to [create an account and container for Google Tag Manager](https://support.google.com/tagmanager/answer/14842164). ### Step 1: Open the tag template gallery In [Google Tag Manager](https://tagmanager.google.com/), choose your workspace, then select **Templates**. In the **Tag Template** pane, select **Search Gallery**. ![The templates page for an example workspace in Google Tag Manager.](https://www.braze.com/docs/ko/ko/assets/img/web-gtm/search_tag_template_gallery.png?3f889b02db28eee130a2e6afd58a27f4){: style="max-width:95%;"} ### Step 2: Add the initialization tag template In the template gallery, search for `braze-inc`, then select **Braze Initialization Tag**. ![The template gallery showing the various 'braze-inc' templates.](https://www.braze.com/docs/ko/ko/assets/img/web-gtm/template_gallery_results.png?166c46fcb5f46eeff8bd50727b6bf3ed){: style="max-width:80%;"} Select **Add to workspace** > **Add**. ![The 'Braze Initialization Tag' page in Google Tag Manager.](https://www.braze.com/docs/ko/ko/assets/img/web-gtm/add_to_workspace.png?426a14d8047898ccb343ac4476d38e3b){: style="max-width:70%;"} ### Step 3: Configure the tag From the **Templates** section, select your newly added template. ![The "Templates" page in Google Tag Manager showing the Braze Initialization Tag template.](https://www.braze.com/docs/ko/ko/assets/img/web-gtm/select_tag_template.png?54a3dd6d14a80b1b1f45d57a67134b24){: style="max-width:95%;"} Select the pencil icon to open the **Tag Configuration** dropdown. ![The Tag Configuration tile with the 'pencil' icon shown.](https://www.braze.com/docs/ko/ko/assets/img/web-gtm/gtm-initialization-tag.png?18e7bc10a22d55f5c681ee7a7266c767) Enter the minimum required information: | Field | Description | | ------------- | ----------- | | **API Key** | Your [Braze API Key](https://www.braze.com/docs/ko/ko/api/basics/#about-rest-api-keys), found in the Braze dashboard under **Settings** > **App Settings**. | | **API Endpoint** | Your REST endpoint URL. Your endpoint will depend on the Braze URL for [your instance](https://www.braze.com/docs/ko/ko/api/basics/#endpoints). | | **SDK Version** | The most recent `MAJOR.MINOR` version of the Web Braze SDK listed in the [changelog](https://www.braze.com/docs/ko/ko/developer_guide/changelogs/?sdktab=web). For example, if the latest version is `4.1.2`, enter `4.1`. For more information, see [About SDK version management](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/version_management/). | {: .reset-td-br-1 .reset-td-br-2 aria-label="Step 3: Configure the tag" } For additional initialization settings, select **Braze Initialization Options** and choose any options you need. ![The list of Braze Initialization Options in under 'Tag Configuration'.](https://www.braze.com/docs/ko/ko/assets/img/web-gtm/braze_initialization_options.png?7081bcb24b9eda18d19d4b8634dd59e8){: style="max-width:65%;"} ### Step 4: Choose initialization options The Braze Initialization Tag exposes the following options. Most of these map directly to the [Web SDK `InitializationOptions`](https://js.appboycdn.com/web-sdk/latest/doc/modules/braze.html#initializationoptions), and some correspond to Web SDK methods that the tag will call during initialization. Select the options that match your integration needs: | GTM option | Web SDK configuration or method | Description | | --- | --- | --- | | **Allow HTML In-App Messages** | `allowUserSuppliedJavascript` | Enables HTML in-app messages, Banners, and user-supplied JavaScript click actions. Required for [HTML in-app messages](https://www.braze.com/docs/ko/ko/user_guide/channels/in_app_messages/message_types/custom_html/) and [Banners](https://www.braze.com/docs/ko/ko/developer_guide/banners/placements/?sdktab=web) that use custom HTML. Only enable this when you trust the HTML and JavaScript content, as it allows user-supplied JavaScript execution. | | **App Version Number** | `appVersion`, `appVersionNumber` | App version for segmentation (for example, `1.2.3.4`). | | **Automatically Open New Session** | `braze.openSession()` | Opens a new session after the SDK is initialized by calling this method for you. | | **Automatically show new in app messages** | `braze.automaticallyShowInAppMessages()` | Automatically displays new in-app messages when they arrive from the server by calling this method after initialization. | | **Disable Automatic Push Token Maintenance** | `disablePushTokenMaintenance` | Stops the SDK from syncing push tokens with the Braze backend on new sessions. | | **Disable Automatic Service Worker Registration** | `manageServiceWorkerExternally` | Use if you register and control the service worker yourself. | | **Disable Cookies** | `noCookies` | Uses localStorage instead of cookies for user/session data. Prevents cross-subdomain recognition. | | **Disable Font Awesome** | `doNotLoadFontAwesome` | Prevents the SDK from loading Font Awesome from the CDN. Use if your site has its own Font Awesome. | | **Enable SDK Authentication** | `enableSdkAuthentication` | Enables [SDK Authentication](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/authentication/). | | **Enable Web SDK Logging** | `enableLogging` | Enables console logging for debugging. Remove before production. | | **Minimum Interval Between Triggered Messages** | `minimumIntervalBetweenTriggerActionsInSeconds` | Minimum seconds between trigger actions (default: 30). | | **Open Cards in New Tab** | `openCardsInNewTab` | Opens Content Card links in a new tab when using the default Feed UI. | | **Service Worker Location** | `serviceWorkerLocation` | Custom path for the service worker file (default: `/service-worker.js`). | | **Session Timeout (seconds)** | `sessionTimeoutInSeconds` | Session timeout in seconds (default: 1800). | {: .reset-td-br-1 .reset-td-br-2 .reset-td-br-3 aria-label="Step 4: Choose initialization options" } **Note:** To enable [Custom HTML in-app messages](https://www.braze.com/docs/ko/ko/user_guide/channels/in_app_messages/message_types/custom_html/) when using the Google Tag Manager Braze Initialization Tag, select **Allow HTML In-App Messages** in **Braze Initialization Options**. This checkbox maps to the `allowUserSuppliedJavascript` initialization option in `braze.initialize()` and sets it to `true`. The Google Tag Manager Braze Initialization Tag uses this label instead of the option name. For options not exposed in the GTM template (such as `contentSecurityNonce`, `localization`, or `devicePropertyAllowlist`), use [runtime initialization](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=web) instead. ### Step 5: Set to Trigger on *all pages* The initialization tag should be run on all pages of your site. This allows you to use Braze SDK methods and record web push analytics. ### Step 6: Verify your integration You can verify your integration using either of the following options: - **Option 1:** Using Google Tag Manager's [debugging tool](https://support.google.com/tagmanager/answer/6107056?hl=en), you can check if the Braze Initialization Tag is triggering correctly on your configured pages or events. - **Option 2:** Check for any network requests made to Braze from your web page. Additionally, the global `window.braze` library should now be defined. ## Filtering bot traffic {#bot-filtering} MAU can include a percentage of bot users, which inflates your monthly active user count. While the Braze Web SDK includes built-in detection for some common web crawlers (such as search engine bots and social media preview bots), it is especially important to stay proactive with robust solutions to detect bots, as SDK updates alone cannot consistently detect every new bot. ### Limitations of SDK-side bot detection The Web SDK includes basic user-agent-based bot detection that filters out known crawlers. However, this approach has limitations: - **New bots emerge constantly**: AI companies and other actors regularly create new bots that may disguise themselves to avoid detection. - **User-agent spoofing**: Sophisticated bots can mimic legitimate browser user-agents. - **Custom bots**: Non-technical users can now easily create bots using large language models (LLMs), making bot behavior unpredictable. ### Implementing bot filtering **Important:** The solutions outlined below are general suggestions. Tailor bot filtering logic to your unique environment and traffic patterns. The most robust solution is to implement your own bot filtering logic before initializing the Braze SDK. Common approaches include: #### Require user interaction Consider delaying SDK initialization until a user performs a meaningful interaction, such as accepting a cookie consent banner, scrolling, or clicking. This approach is often easier to implement and can be highly effective at filtering bot traffic. **Important:** Delaying SDK initialization until user interaction might cause Banners and Content Cards to also not display until that interaction occurs. #### Custom bot detection Implement custom detection based on your specific bot traffic patterns, such as: - Analyzing user-agent strings for patterns you've identified in your traffic - Checking for headless browser indicators - Using third-party bot detection services - Monitoring behavioral signals specific to your site **Example of conditional initialization:** ```javascript // Only initialize Braze if your custom bot detection determines this is not a bot if (!isLikelyBot()) { braze.initialize('YOUR-API-KEY-HERE', { baseUrl: "YOUR-SDK-ENDPOINT-HERE" }); braze.automaticallyShowInAppMessages(); braze.openSession(); } ``` ### Best practices - Regularly analyze your MAU data and web traffic patterns to identify new bot behavior. - Test thoroughly to ensure your bot filtering doesn't prevent legitimate users from being tracked. - Update your filtering logic based on the bot traffic patterns you observe in your environment. ## Optional configurations ### Logging To quickly enable logging, you can add `?brazeLogging=true` as a parameter to your website URL. Alternatively, you can enable [basic](#web_basic-logging) or [custom](#web_custom-logging) logging. For a centralized overview across all platforms, see [Verbose logging](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/verbose_logging). #### Basic logging Use `enableLogging` to log basic debugging messages to the JavaScript console before the SDK is initialized. ```javascript enableLogging: true ``` Your method should be similar to the following: ```javascript braze.initialize('API-KEY', { baseUrl: 'API-ENDPOINT', enableLogging: true }); braze.openSession(); ``` Use `braze.toggleLogging()` to log basic debugging messages to the JavaScript console after the SDK is initialized. Your method should be similar to the following: ```javascript braze.initialize('API-KEY', { baseUrl: 'API-ENDPOINT', }); braze.openSession(); ... braze.toggleLogging(); ``` **Important:** Basic logs are visible to all users, so consider disabling, or switch to [`setLogger`](#web_custom-logging), before releasing your code to production. #### Custom logging Use `setLogger` to log custom debugging messages to the JavaScript console. Unlike basic logs, these logs are not visible to users. ```javascript setLogger(loggerFunction: (message: STRING) => void): void ``` Replace `STRING` with your message as a single string parameter. Your method should be similar to the following: ```javascript braze.initialize('API-KEY'); braze.setLogger(function(message) { console.log("Braze Custom Logger: " + message); }); braze.openSession(); ``` ## Upgrading the SDK **Note:** This guide uses code samples from the Braze Web SDK 4.0.0+. To upgrade to the latest Web SDK version, see [SDK Upgrade Guide](https://github.com/braze-inc/braze-web-sdk/blob/master/UPGRADE_GUIDE.md). When you reference the Braze Web SDK from our content delivery network, for example, `https://js.appboycdn.com/web-sdk/a.a/braze.min.js` (as recommended by our default integration instructions), your users receive minor updates (bug fixes and backward compatible features, versions `a.a.a` through `a.a.z` in the above examples) automatically when they refresh your site. However, when we release major changes, we require you to upgrade the Braze Web SDK manually to ensure that breaking changes do not impact your integration. Additionally, if you download our SDK and host it yourself, you don't receive any version updates automatically and should upgrade manually to receive the latest features and bug fixes. You can keep up-to-date with our latest release [following our release feed](https://github.com/braze-inc/braze-web-sdk/tags.atom) with the RSS Reader or service of your choice, and see [our changelog](https://github.com/braze-inc/braze-web-sdk/blob/master/CHANGELOG.md) for a full accounting of our Web SDK release history. To upgrade the Braze Web SDK: - Update the Braze library version by changing the version number of `https://js.appboycdn.com/web-sdk/[OLD VERSION NUMBER]/braze.min.js`, or in your package manager's dependencies. - If you have web push integrated, update the service worker file on your site - by default, this is located at `/service-worker.js` at your site's root directory, but the location may be customized in some integrations. You must access the root directory to host a service worker file. You must update these two files in coordination with each other for proper functionality. ## Other integration methods ### Accelerated Mobile Pages (AMP) **See more** #### Step 1: Include AMP web push script Add the following async script tag to your head: ```js ``` #### Step 2: Add subscription widgets Add a widget to the body of your HTML that allows users to subscribe and unsubscribe from push. ```js ``` #### Step 3: Add `helper-iframe` and `permission-dialog` The AMP Web Push component creates a popup to handle push subscriptions, so you must add the following helper files to your project to enable this feature: - [`helper-iframe.html`](https://cdn.ampproject.org/v0/amp-web-push-helper-frame.html) - [`permission-dialog.html`](https://cdn.ampproject.org/v0/amp-web-push-permission-dialog.html) #### Step 4: Create a service worker file Create a `service-worker.js` file in the root directory of your website and add the following snippet: #### Step 5: Configure the AMP web push HTML element Add the following `amp-web-push` HTML element to your HTML body. Keep in mind, you need to append your [`apiKey` and `baseUrl`](https://documenter.getpostman.com/view/4689407/SVYrsdsG) as query parameters to `service-worker-URL`. ```js ``` ### Asynchronous Module Definition (AMD) #### Disable support If your site uses RequireJS or another AMD module-loader, but you prefer to load the Braze Web SDK through one of the other options in this list, you can load a version of the library that does not include AMD support. This version of the library can be loaded from the following CDN location: #### Module loader If you use RequireJS or other AMD module-loaders we recommend self-hosting a copy of our library and referencing it as you would with other resources: ```javascript require(['path/to/braze.min.js'], function(braze) { braze.initialize('YOUR-API-KEY-HERE', { baseUrl: 'YOUR-SDK-ENDPOINT' }); // Required if you want in-app messages to display automatically braze.automaticallyShowInAppMessages(); braze.openSession(); }); ``` ### Electron {#electron} Electron does not officially support web push notifications (see: this [GitHub issue](https://github.com/electron/electron/issues/6697)). There are other [open source workarounds](https://github.com/MatthieuLemoine/electron-push-receiver) you may try that have not been tested by Braze. ### Jest framework {#jest} When using Jest, you may see an error similar to `SyntaxError: Unexpected token 'export'`. To fix this, adjust your configuration in `package.json` to ignore the Braze SDK: ``` "jest": { "transformIgnorePatterns": [ "/node_modules/(?!@braze)" ] } ``` ### SSR frameworks {#ssr} The Web SDK runs in a browser environment. In SSR frameworks, initialize Braze in a client-only component so your server never executes SDK code. #### Framework-agnostic dynamic import If your framework isn't listed in this section, you can dynamically import Braze from a client-only lifecycle hook. ```javascript // MyComponent/braze-exports.js // Export the parts of the SDK that you need. export { initialize, openSession } from "@braze/web-sdk"; // MyComponent/MyComponent.js useEffect(() => { import("./braze-exports.js").then(({ initialize, openSession }) => { initialize("YOUR-API-KEY-HERE", { baseUrl: "YOUR-SDK-ENDPOINT", enableLogging: true, }); openSession(); }); }, []); ``` If you're using webpack, you can dynamically import only specific SDK exports. ```javascript // MyComponent.js useEffect(() => { import( /* webpackExports: ["initialize", "openSession"] */ "@braze/web-sdk" ).then(({ initialize, openSession }) => { initialize("YOUR-API-KEY-HERE", { baseUrl: "YOUR-SDK-ENDPOINT", enableLogging: true, }); openSession(); }); }, []); ``` #### Shared hook for Next.js and Remix Create a reusable `useBraze` hook and call it near your app root. ```tsx // hooks/useBraze.ts import { useEffect, useRef } from "react"; export function useBraze() { const didInit = useRef(false); useEffect(() => { if (didInit.current) { return; } didInit.current = true; import("@braze/web-sdk") .then((braze) => { const initialized = braze.initialize("YOUR-API-KEY-HERE", { // Use your Braze Web SDK endpoint, such as sdk.iad-01.braze.com. baseUrl: "YOUR-SDK-ENDPOINT", enableLogging: false, }); if (!initialized) { return; } // Optional: Identify signed-in users before opening a session. // braze.changeUser("external-id"); // Optional: Automatically display in-app messages. // braze.automaticallyShowInAppMessages(); braze.openSession(); }) .catch((error) => { console.error("Unable to load Braze SDK:", error); }); }, []); } ``` #### Next.js (App Router) Call `useBraze` in a client component that wraps your app. ```tsx // app/components/AppRoot.tsx "use client"; import type { ReactNode } from "react"; import { useBraze } from "../hooks/useBraze"; export function AppRoot({ children }: { children: ReactNode }) { useBraze(); return <>{children}; } ``` ```tsx // app/layout.tsx import type { ReactNode } from "react"; import { AppRoot } from "./components/AppRoot"; export default function RootLayout({ children, }: { children: ReactNode; }) { return ( {children} ); } ``` #### Next.js (Pages Router) Call `useBraze` at the top of your custom app component. ```tsx // pages/_app.tsx import type { AppProps } from "next/app"; import { useBraze } from "../hooks/useBraze"; export default function App({ Component, pageProps }: AppProps) { useBraze(); return ( ); } ``` #### Remix Call `useBraze` at the top of your root route component. For local Remix validation examples, run `PORT=4013 npm run dev`. ```tsx // app/root.tsx import { Outlet } from "@remix-run/react"; import { useBraze } from "./hooks/useBraze"; export default function App() { useBraze(); return ; } ``` #### Logging events and updating users After `useBraze` initializes the SDK at your app root, other client components can call Braze methods. A common pattern is to call them inside user actions, such as `onClick` or `onSubmit`. In the example, the SDK methods are loaded inside the click handler instead of at the top of the file. This keeps the Web SDK out of server code and loads only what that action needs. The `webpackExports` comment tells webpack which methods to include, so your bundle stays smaller. ```tsx // app/components/BuyButton.tsx "use client"; export function BuyButton() { const handleClick = async () => { const { logCustomEvent, logPurchase, getUser } = await import( /* webpackExports: ["logCustomEvent", "logPurchase", "getUser"] */ "@braze/web-sdk" ); getUser()?.setCustomUserAttribute("last_purchase_date", "2026-05-04"); logCustomEvent("clicked_buy", { source: "product_page" }); logPurchase("sku_123", 19.99, "USD"); }; return ; } ``` This example shows a `BuyButton` component that logs activity when someone clicks **Buy**. First, it imports only `logCustomEvent`, `logPurchase`, and `getUser` at click time. Then it updates a user attribute, logs a custom event, and logs a purchase. This pattern helps you keep initialization centralized in `useBraze`, while still tracking meaningful actions from any client component. If you're using Remix with Vite and package-root imports fail at runtime, use the existing Vite workaround. For more information, see [Vite](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=web#web_vite). For a full list of available methods, see the [Braze JavaScript reference documentation](https://js.appboycdn.com/web-sdk/latest/doc/modules/braze.html). ### Tealium iQ Tealium iQ offers a basic turnkey Braze integration. To configure the integration, search for Braze in the Tealium Tag Management interface, and provide the Web SDK API key from your dashboard. For more details or in-depth Tealium configuration support, check out our [integration documentation](https://www.braze.com/docs/ko/ko/partners/data_and_infrastructure_agility/customer_data_platform/tealium/#about-tealium) or contact your Tealium account manager. ### Vite {#vite} If you use Vite and see a warning around circular dependencies or `Uncaught TypeError: Class extends value undefined is not a constructor or null`, you may need to exclude the Braze SDK from its [dependency discovery](https://vitejs.dev/guide/dep-pre-bundling.html#customizing-the-behavior): ``` optimizeDeps: { exclude: ['@braze/web-sdk'] }, ``` ### Other tag managers Braze may also be compatible with other tag management solutions by following our integration instructions within a custom HTML tag. Contact a Braze representative if you need help evaluating these solutions. ## Integrating the Android SDK ### Step 1: Update your Gradle build configuration In your project's repository configuration (for example, `settings.gradle`, `settings.gradle.kts`, or top-level `build.gradle`), add [`mavenCentral()`](https://docs.gradle.org/current/kotlin-dsl/gradle/org.gradle.api.artifacts.dsl/-repository-handler/maven-central.html) to your list of repositories. This syntax is the same for both Groovy and Kotlin DSL. ```groovy repositories { mavenCentral() } ``` Next, add Braze to your dependencies. In the following examples, replace `SDK_VERSION` with the current version of your Android Braze SDK. For the full list of versions, see [Changelogs](https://www.braze.com/docs/ko/ko/developer_guide/changelogs/?sdktab=android). **Note:** - For Kotlin DSL (`build.gradle.kts`), use the `implementation("...")` syntax. - For Groovy (`build.gradle`), use the `implementation '...'` syntax. - For [version catalogs](https://developer.android.com/build/migrate-to-catalogs), add entries to your `gradle/libs.versions.toml` file and reference them using the generated accessors. If you don't plan on using Braze UI components, add the following to your dependencies. ```groovy dependencies { implementation 'com.braze:android-sdk-base:SDK_VERSION' // (Required) Adds dependencies for the base Braze SDK. implementation 'com.braze:android-sdk-location:SDK_VERSION' // (Optional) Adds dependencies for Braze location services. } ``` ```kotlin dependencies { implementation("com.braze:android-sdk-base:SDK_VERSION") // (Required) Adds dependencies for the base Braze SDK. implementation("com.braze:android-sdk-location:SDK_VERSION") // (Optional) Adds dependencies for Braze location services. } ``` In your `gradle/libs.versions.toml` file: ```toml [versions] braze = "SDK_VERSION" [libraries] braze-android-sdk-base = { group = "com.braze", name = "android-sdk-base", version.ref = "braze" } braze-android-sdk-location = { group = "com.braze", name = "android-sdk-location", version.ref = "braze" } ``` Then, in your `build.gradle` or `build.gradle.kts` file, add the following dependencies. This syntax is the same for both Groovy and Kotlin DSL. ```groovy dependencies { implementation(libs.braze.android.sdk.base) // (Required) Adds dependencies for the base Braze SDK. implementation(libs.braze.android.sdk.location) // (Optional) Adds dependencies for Braze location services. } ``` If you plan on using Braze UI components, add the following to your dependencies. ```groovy dependencies { implementation 'com.braze:android-sdk-ui:SDK_VERSION' // (Required) Adds dependencies for the Braze SDK and Braze UI components. implementation 'com.braze:android-sdk-location:SDK_VERSION' // (Optional) Adds dependencies for Braze location services. } ``` ```kotlin dependencies { implementation("com.braze:android-sdk-ui:SDK_VERSION") // (Required) Adds dependencies for the Braze SDK and Braze UI components. implementation("com.braze:android-sdk-location:SDK_VERSION") // (Optional) Adds dependencies for Braze location services. } ``` In your `gradle/libs.versions.toml` file: ```toml [versions] braze = "SDK_VERSION" [libraries] braze-android-sdk-ui = { group = "com.braze", name = "android-sdk-ui", version.ref = "braze" } braze-android-sdk-location = { group = "com.braze", name = "android-sdk-location", version.ref = "braze" } ``` Then, in your `build.gradle` or `build.gradle.kts` file, add the following dependencies. This syntax is the same for both Groovy and Kotlin DSL. ```groovy dependencies { implementation(libs.braze.android.sdk.ui) // (Required) Adds dependencies for the Braze SDK and Braze UI components. implementation(libs.braze.android.sdk.location) // (Optional) Adds dependencies for Braze location services. } ``` ### Step 2: Configure your `braze.xml` **Note:** As of December 2019, custom endpoints are no longer given out, if you have a pre-existing custom endpoint, you may continue to use it. For more details, refer to our list of available endpoints. Create a `braze.xml` file in your project's `res/values` folder. If you are on a specific data cluster or have a pre-existing custom endpoint, you need to specify the endpoint in your `braze.xml` file as well. The contents of that file should resemble the following code snippet. Make sure to substitute `YOUR_APP_IDENTIFIER_API_KEY` with the identifier found in the **Manage Settings** page of the Braze dashboard. Log in at [dashboard.braze.com](https://dashboard.braze.com) to find your [cluster address](https://www.braze.com/docs/ko/ko/user_guide/administrative/access_braze/sdk_endpoints). ```xml YOUR_APP_IDENTIFIER_API_KEY YOUR_CUSTOM_ENDPOINT_OR_CLUSTER ``` ### Step 3: Add permissions to `AndroidManifest.xml` Next, add the following permissions to your `AndroidManifest.xml`: ```xml ``` **Note:** With the release of Android M, Android switched from an install-time to a runtime permissions model. However, both of these permissions are normal permissions and are granted automatically if listed in the app manifest. For more information, visit Android's [permission documentation](https://developer.android.com/training/permissions/index.html). ### Step 4: Enable delayed initialization (optional) To use delayed initialization, the minimum Braze SDK version is required: **Note:** While delayed initialization is enabled, all network connections are canceled, preventing the SDK from sending data to the Braze servers. #### Step 4.1: Update your `braze.xml` Delayed initialization is disabled by default. To enable, use one of the following options: In your project's `braze.xml` file, set `com_braze_enable_delayed_initialization` to `true`. ```xml true ``` To enable delayed initialization at runtime, use the following method. ```java Braze.enableDelayedInitialization(context); ``` ```kotlin Braze.enableDelayedInitialization(context) ``` **Note:** When delayed initialization is enabled and a push notification contains a deep link action, the deep link does not resolve. #### Step 4.2: Configure push analytics (optional) When delayed initialization is enabled, push analytics are queued by default. However, you can choose to [explicitly queue](#explicitly-queue-push-analytics) or [drop](#drop-push-analytics) push analytics instead. ##### Explicitly queue {#explicitly-queue-push-analytics} To explicitly queue push analytics, choose one of the following options: In your `braze.xml` file, set `com_braze_delayed_initialization_analytics_behavior` to `QUEUE`: ```xml QUEUE ``` Add `QUEUE` to your [`Braze.enableDelayedInitialization()`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze/-braze/-companion/enable-delayed-initialization.html) method: ```java Braze.enableDelayedInitialization(context, DelayedInitializationAnalyticsBehavior.QUEUE); ``` ```kotlin Braze.enableDelayedInitialization(context, DelayedInitializationAnalyticsBehavior.QUEUE) ``` ##### Drop {#drop-push-analytics} To drop push analytics, choose one of the following options: In your `braze.xml` file, set `com_braze_delayed_initialization_analytics_behavior` to `DROP`: ```xml DROP ``` Add `DROP` to the [`Braze.enableDelayedInitialization()`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze/-braze/-companion/enable-delayed-initialization.html) method: ```java Braze.enableDelayedInitialization(context, DelayedInitializationAnalyticsBehavior.DROP); ``` ```kotlin Braze.enableDelayedInitialization(context, DelayedInitializationAnalyticsBehavior.DROP) ``` #### Step 4.3: Manually initialize the SDK After your chosen delay period, use the [`Braze.disableDelayedInitialization()`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze/-braze/-companion/disable-delayed-initialization.html) method to manually initialize the SDK. ```java Braze.disableDelayedInitialization(context); ``` ```kotlin Braze.disableDelayedInitialization(context) ``` ### Step 5: Enable user session tracking When you enable user session tracking, calls to `openSession()`, `closeSession()`,[`ensureSubscribedToInAppMessageEvents()`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.ui.inappmessage/-braze-in-app-message-manager/ensure-subscribed-to-in-app-message-events.html), and `InAppMessageManager` registration can be handled automatically. To register activity lifecycle callbacks, add the following code to the `onCreate()` method of your `Application` class. ```java public class MyApplication extends Application { @Override public void onCreate() { super.onCreate(); registerActivityLifecycleCallbacks(new BrazeActivityLifecycleCallbackListener()); } } ``` ```kotlin class MyApplication : Application() { override fun onCreate() { super.onCreate() registerActivityLifecycleCallbacks(BrazeActivityLifecycleCallbackListener()) } } ``` For the list of available parameters, see [`BrazeActivityLifecycleCallbackListener`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze/-braze-activity-lifecycle-callback-listener/index.html). ## Testing session tracking **Tip:** You can also use the [SDK Debugger](https://www.braze.com/docs/ko/ko/developer_guide/debugging) to diagnose SDK issues. If you experience issues while testing, enable [verbose logging](#android_enabling-logs), then use logcat to detect missing `openSession` and `closeSession` calls in your activities. 1. In Braze, go to **Overview**, select your app, then in the **Display Data For** dropdown choose **Today**. ![The "Overview" page in Braze, with the "Display Data For" field set to "Today".](https://www.braze.com/docs/ko/ko/assets/img_archive/android_sessions.png?a80518af3562397d27154b59f66b87f1) 2. Open your app, then refresh the Braze dashboard. Verify that your metrics have increased by 1. 3. Navigate through your app and verify that only one session has been logged to Braze. 4. Send the app to the background for at least 10 seconds, then bring it to the foreground. Verify that a new session was logged. ## Optional configurations ### Runtime configuration To set your Braze options in code rather than your `braze.xml` file, use [runtime configuration](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze/-braze/-companion/configure.html). If a value exists in both places, the runtime value will be used instead. After all required settings are supplied at runtime, you can delete your `braze.xml` file. In the following example, a [builder object](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.configuration/-braze-config/-builder/index.html) is created and then passed to [`Braze.configure()`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze/-braze/-companion/configure.html). Note that only some of the available runtime options are shown—refer to our [KDoc](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.configuration/-braze-config/-builder/index.html) for the full list. ```java BrazeConfig brazeConfig = new BrazeConfig.Builder() .setApiKey("api-key-here") .setCustomEndpoint("YOUR_CUSTOM_ENDPOINT_OR_CLUSTER") .setSessionTimeout(60) .setHandlePushDeepLinksAutomatically(true) .setGreatNetworkDataFlushInterval(10) .build(); Braze.configure(this, brazeConfig); ``` ```kotlin val brazeConfig = BrazeConfig.Builder() .setApiKey("api-key-here") .setCustomEndpoint("YOUR_CUSTOM_ENDPOINT_OR_CLUSTER") .setSessionTimeout(60) .setHandlePushDeepLinksAutomatically(true) .setGreatNetworkDataFlushInterval(10) .build() Braze.configure(this, brazeConfig) ``` **Tip:** Looking for another example? Check out our [Hello Braze sample app](https://github.com/braze-inc/braze-android-sdk/blob/master/samples/hello-braze/src/main/java/com/braze/helloworld/CustomApplication.java). ### Google Advertising ID The [Google Advertising ID (GAID)](https://support.google.com/googleplay/android-developer/answer/6048248/advertising-id?hl=en) is an optional user-specific, anonymous, unique, and resettable ID for advertising, provided by Google Play services. GAID gives users the power to reset their identifier, opt-out of interest-based ads within Google Play apps, and provides developers with a simple, standard system to continue to monetize their apps. The Google Advertising ID is not automatically collected by the Braze SDK and must be set manually via the [`Braze.setGoogleAdvertisingId()`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze/-i-braze/set-google-advertising-id.html) method. ```java new Thread(new Runnable() { @Override public void run() { try { AdvertisingIdClient.Info idInfo = AdvertisingIdClient.getAdvertisingIdInfo(getApplicationContext()); Braze.getInstance(getApplicationContext()).setGoogleAdvertisingId(idInfo.getId(), idInfo.isLimitAdTrackingEnabled()); } catch (Exception e) { e.printStackTrace(); } } }).start(); ``` ```kotlin suspend fun fetchAndSetAdvertisingId( context: Context, scope: CoroutineScope = GlobalScope ) { scope.launch(Dispatchers.IO) { try { val idInfo = AdvertisingIdClient.getAdvertisingIdInfo(context) Braze.getInstance(context).setGoogleAdvertisingId( idInfo.id, idInfo.isLimitAdTrackingEnabled ) } catch (e: Exception) { e.printStackTrace() } } } ``` **Important:** Google requires the Advertising ID to be collected on a non-UI thread. ### Location tracking To enable Braze location collection, set `com_braze_enable_location_collection` to `true` in your `braze.xml` file: ```xml true ``` **Important:** Starting with Braze Android SDK version 3.6.0, Braze location collection is disabled by default. ### Logging By default, the Braze Android SDK log level is set to `INFO`. You can [suppress these logs](#android_suppressing-logs) or [set a different log level](#android_enabling-logs), such as `VERBOSE`, `DEBUG`, or `WARN`. #### Enabling logs To help troubleshoot issues in your app, or reduce turnaround times with Braze Support, you can enable verbose logs for the SDK. When you send verbose logs to Braze Support, ensure they begin as soon as you launch your application and end far after your issue occurs. For a centralized overview, see [Verbose logging](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/verbose_logging). To learn how to interpret log output, see [Reading verbose logs](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/reading_verbose_logs). Keep in mind, verbose logs are only intended for your development environment, so you'll want to disable them before releasing your app. **Important:** Enable verbose logs before any other calls in `Application.onCreate()` to ensure your logs are as complete as possible. To enable logs directly in your app, add the following to your application's `onCreate()` method before any other methods. ```java BrazeLogger.setLogLevel(Log.MIN_LOG_LEVEL); ``` ```kotlin BrazeLogger.logLevel = Log.MIN_LOG_LEVEL ``` Replace `MIN_LOG_LEVEL` with the **Constant** of the log level you'd like to set as your minimum log level. Any logs at a level `>=` to your set `MIN_LOG_LEVEL` will be forwarded to Android's default [`Log`](https://developer.android.com/reference/android/util/Log) method. Any logs `<` your set `MIN_LOG_LEVEL` will be discarded. | Constant | Value | Description | |-------------|----------------|---------------------------------------------------------------------------| | `VERBOSE` | 2 | Logs the most detailed messages for debugging and development. | | `DEBUG` | 3 | Logs descriptive messages for debugging and development. | | `INFO` | 4 | Logs informational messages for general highlights. | | `WARN` | 5 | Logs warning messages for identifying potentially harmful situations. | | `ERROR` | 6 | Logs error messages for indicating application failure or serious issues. | | `ASSERT` | 7 | Logs assertion messages when conditions are false during development. | {: .reset-td-br-1 .reset-td-br-2 .reset-td-br-3 aria-label="Enabling logs" } For example, the following code will forward log levels `2`, `3`, `4`, `5`, `6`, and `7` to the `Log` method. ```java BrazeLogger.setLogLevel(Log.VERBOSE); ``` ```kotlin BrazeLogger.logLevel = Log.VERBOSE ``` To enable logs in the `braze.xml`, add the following to your file: ```xml MIN_LOG_LEVEL ``` Replace `MIN_LOG_LEVEL` with the **Value** of the log level you'd like to set as your minimum log level. Any logs at a level `>=` to your set `MIN_LOG_LEVEL` will be forwarded to Android's default [`Log`](https://developer.android.com/reference/android/util/Log) method. Any logs `<` your set `MIN_LOG_LEVEL` will be discarded. | Constant | Value | Description | |-------------|----------------|---------------------------------------------------------------------------| | `VERBOSE` | 2 | Logs the most detailed messages for debugging and development. | | `DEBUG` | 3 | Logs descriptive messages for debugging and development. | | `INFO` | 4 | Logs informational messages for general highlights. | | `WARN` | 5 | Logs warning messages for identifying potentially harmful situations. | | `ERROR` | 6 | Logs error messages for indicating application failure or serious issues. | | `ASSERT` | 7 | Logs assertion messages when conditions are false during development. | {: .reset-td-br-1 .reset-td-br-2 .reset-td-br-3 aria-label="Enabling logs" } For example, the following code will forward log levels `2`, `3`, `4`, `5`, `6`, and `7` to the `Log` method. ```xml 2 ``` #### Verifying verbose logs To verify that your logs are set to `VERBOSE`, check if `V/Braze` occurs somewhere in your logs. If it does, then verbose logs have been successfully enabled. For example: ``` 2077-11-19 16:22:49.591 ? V/Braze v9.0.01 .bo.app.d3: Request started ``` #### Suppressing logs To suppress all logs for the Braze Android SDK, set the log level to `BrazeLogger.SUPPRESS` in your application's `onCreate()` method _before_ any other methods. ```java BrazeLogger.setLogLevel(BrazeLogger.SUPPRESS); ``` ```kotlin BrazeLogger.setLogLevel(BrazeLogger.SUPPRESS) ``` ### Multiple API keys The most common use case for multiple API keys is separating API keys for debug and release build variants. To easily switch between multiple API keys in your builds, we recommend creating a separate `braze.xml` file for each relevant [build variant](https://developer.android.com/studio/build/build-variants.html). A build variant is a combination of build type and product flavor. By default, new Android projects are configured with [`debug` and `release` build types](https://developer.android.com/reference/tools/gradle-api/8.3/null/com/android/build/api/dsl/BuildType) and no product flavors. For each relevant build variant, create a new `braze.xml` in the `src//res/values/` directory. When the build variant is compiled, it will use the new API key. ```xml REPLACE_WITH_YOUR_BUILD_VARIANT_API_KEY ``` **Tip:** To learn how to set up the API key in your code, see [Runtime configuration](https://www.braze.com/docs/ko/ko/developer_guide/sdk_initalization/?sdktab=android). ### Exclusive in-app message TalkBack In adherence to the [Android accessibility guidelines](https://developer.android.com/guide/topics/ui/accessibility), the Braze Android SDK offers Android Talkback by default. To ensure that only the contents of in-app messages are read out loud—without including other screen elements like the app title bar or navigation—you can enable exclusive mode for TalkBack. To enable exclusive mode for in-app messages: ```xml true ``` ```kotlin val brazeConfigBuilder = BrazeConfig.Builder() brazeConfigBuilder.setIsInAppMessageAccessibilityExclusiveModeEnabled(true) Braze.configure(this, brazeConfigBuilder.build()) ``` ```java BrazeConfig.Builder brazeConfigBuilder = new BrazeConfig.Builder() brazeConfigBuilder.setIsInAppMessageAccessibilityExclusiveModeEnabled(true); Braze.configure(this, brazeConfigBuilder.build()); ``` ### R8 and ProGuard [Code shrinking](https://developer.android.com/build/shrink-code) configuration is automatically included with your Braze integration. Client apps that obfuscate Braze code must store release mapping files for Braze to interpret stack traces. If you want to continue to keep all Braze code, add the following to your ProGuard file: ``` -keep class bo.app.** { *; } -keep class com.braze.** { *; } ``` ## Integrating the Swift SDK You can integrate and customize the Braze Swift SDK using the Swift Package Manager (SPM), CocoaPods, or manual integration methods. For more information about the various SDK symbols, see [Braze Swift reference documentation](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/). ### Prerequisites Before you start, verify your environment is supported by the [latest Braze Swift SDK version](https://github.com/braze-inc/braze-swift-sdk#version-information). ### Step 1: Install the Braze Swift SDK We recommend using the [Swift Package Manager (SwiftPM)](https://swift.org/package-manager/) or [CocoaPods](http://cocoapods.org/) to install the Braze Swift SDK. Alternatively, you can install the SDK manually. #### Step 1.1: Import SDK version Open your project and navigate to your project's settings. Select the **Swift Packages** tab and click on the add button below the packages list. ![](https://www.braze.com/docs/ko/ko/assets/img/swiftpackages.png?1987d5a2a722497bf1f2f38ab52aa14a) **Note:** Starting in version 7.4.0, the Braze Swift SDK has additional distribution channels as [static XCFrameworks](https://github.com/braze-inc/braze-swift-sdk-prebuilt-static) and [dynamic XCFrameworks](https://github.com/braze-inc/braze-swift-sdk-prebuilt-dynamic). If you'd like to use either of these formats instead, follow the installation instructions from its respective repository. Enter the URL of our iOS Swift SDK repository `https://github.com/braze-inc/braze-swift-sdk` in the text field. Under the **Dependency Rule** section, select the SDK version. Finally, click **Add Package**. ![](https://www.braze.com/docs/ko/ko/assets/img/importsdk_example.png?38a74eb0e009cc281bd78ac130e2089c) #### Step 1.2: Select your packages The Braze Swift SDK separates features into standalone libraries to provide developers with more control over which features to import into their projects. | Package | Details | | --------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `BrazeKit` | Main SDK library providing support for analytics and push notifications. | | `BrazeLocation` | Location library providing support for location analytics and geofence monitoring. | | `BrazeUI` | Braze-provided user interface library for in-app messages, Content Cards, and Banners. Import this library if you intend to use the default UI components. | {: .reset-td-br-1 .reset-td-br-2 aria-label="Step 1.2: Select your packages" } {: .ws-td-nw-1} ##### About Extension libraries **Warning:** [BrazeNotificationService](https://braze-inc.github.io/braze-swift-sdk/tutorials/braze/b2-rich-push-notifications) and [BrazePushStory](https://braze-inc.github.io/braze-swift-sdk/tutorials/braze/b3-push-stories) are extension modules that provide additional functionality and should not be added directly to your main application target. Instead follow the linked guides to integrate them separately into their respective target extensions. | Package | Details | | -------------------------- | ------------------------------------------------------------------------------------- | | `BrazeNotificationService` | Notification service extension library providing support for rich push notifications. | | `BrazePushStory` | Notification content extension library providing support for Push Stories. | {: .reset-td-br-1 .reset-td-br-2 aria-label="About Extension libraries" } {: .ws-td-nw-1} Select the package that best suits your needs and click **Add Package**. Make sure you select `BrazeKit` at a minimum. ![](https://www.braze.com/docs/ko/ko/assets/img/add_package.png?2fa980e608a800e7e3649f34d79f2ea7) #### Step 1.1: Install CocoaPods For a full walkthrough, see CocoaPods' [Getting Started Guide](https://guides.cocoapods.org/using/getting-started.html). Otherwise, you can run the following command to get started quickly: ```bash $ sudo gem install cocoapods ``` If you get stuck, checkout CocoaPods' [Troubleshooting Guide](http://guides.cocoapods.org/using/troubleshooting.html). #### Step 1.2: Constructing the Podfile Next, create a file in your Xcode project directory named `Podfile`. **Note:** Starting in version 7.4.0, the Braze Swift SDK has additional distribution channels as [static XCFrameworks](https://github.com/braze-inc/braze-swift-sdk-prebuilt-static) and [dynamic XCFrameworks](https://github.com/braze-inc/braze-swift-sdk-prebuilt-dynamic). If you'd like to use either of these formats instead, follow the installation instructions from its respective repository. Add the following line to your Podfile: ``` target 'YourAppTarget' do pod 'BrazeKit' end ``` `BrazeKit` contains the main SDK library, providing support for analytics and push notifications. We suggest you version Braze so pod updates automatically grab anything smaller than a minor version update. This looks like `pod 'BrazeKit' ~> Major.Minor.Build`. If you want to automatically integrate the latest Braze SDK version, even with major changes, you can use `pod 'BrazeKit'` in your Podfile. ##### About additional libraries The Braze Swift SDK separates features into standalone libraries to provide developers with more control over which features to import into their projects. In addition to `BrazeKit`, you may add the following libraries to your Podfile: | Library | Details | | --------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `pod 'BrazeLocation'` | Location library providing support for location analytics and geofence monitoring. | | `pod 'BrazeUI'` | Braze-provided user interface library for in-app messages, Content Cards, and Banners. Import this library if you intend to use the default UI components. | {: .reset-td-br-1 .reset-td-br-2 aria-label="About additional libraries" } {: .ws-td-nw-1} ###### Extension libraries [BrazeNotificationService](https://braze-inc.github.io/braze-swift-sdk/tutorials/braze/b2-rich-push-notifications) and [BrazePushStory](https://braze-inc.github.io/braze-swift-sdk/tutorials/braze/b3-push-stories) are extension modules that provide additional functionality and should not be added directly to your main application target. Instead, you will need to create separate extension targets for each of these modules and import the Braze modules into their corresponding targets. | Library | Details | | -------------------------------- | ------------------------------------------------------------------------------------- | | `pod 'BrazeNotificationService'` | Notification service extension library providing support for rich push notifications. | | `pod 'BrazePushStory'` | Notification content extension library providing support for Push Stories. | {: .reset-td-br-1 .reset-td-br-2 aria-label="Extension libraries" } {: .ws-td-nw-1} #### Step 1.3: Install the SDK To install the Braze SDK CocoaPod, navigate to the directory of your Xcode app project within your terminal and run the following command: ``` pod install ``` At this point, you should be able to open the new Xcode project workspace created by CocoaPods. Make sure to use this Xcode workspace instead of your Xcode project. ![A Braze Example folder expanded to show the new `BrazeExample.workspace`.](https://www.braze.com/docs/ko/ko/assets/img/braze_example_workspace.png?67ad8a74ccb61f01df9949a0a37af957) #### Updating the SDK using CocoaPods To update a CocoaPod, simply run the following command within your project directory: ``` pod update ``` #### Step 1.1: Download the Braze SDK Go to the [Braze SDK release page on GitHub](https://github.com/braze-inc/braze-swift-sdk/releases), then download `braze-swift-sdk-prebuilt.zip`. !["The Braze SDK release page on GitHub."](https://www.braze.com/docs/ko/ko/assets/img/swift/sdk_integration/download-braze-swift-sdk-prebuilt.png?0498d856a092a68779f1bd3945e685ca) #### Step 1.2: Choose your frameworks The Braze Swift SDK contains a variety of standalone XCFrameworks, which gives you the freedom to integrate the features you want—without needing to integrate them all. Reference the following table to choose your XCFrameworks: | Package | Required? | Description | | -------------------------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `BrazeKit` | Yes | Main SDK library that provides support for analytics and push notifications. | | `BrazeLocation` | No | Location library that provides support for location analytics and geofence monitoring. | | `BrazeUI` | No | Braze-provided user interface library for in-app messages, Content Cards, and Banners. Import this library if you intend to use the default UI components. | | `BrazeNotificationService` | No | Notification service extension library that provides support for rich push notifications. Do not add this library directly to your main application target, instead [add the `BrazeNotificationService` library separately](https://braze-inc.github.io/braze-swift-sdk/tutorials/braze/b2-rich-push-notifications). | | `BrazePushStory` | No | Notification content extension library that provides support for Push Stories. Do not add this library directly to your main application target, instead [add the `BrazePushStory` library separately](https://braze-inc.github.io/braze-swift-sdk/tutorials/braze/b3-push-stories). | | `BrazeKitCompat` | No | Compatibility library containing all the `Appboy` and `ABK*` classes and methods that were available in the `Appboy-iOS-SDK` version 4.X.X. For usage details, refer to the minimal migration scenario in the [migration guide](https://braze-inc.github.io/braze-swift-sdk/documentation/braze/appboy-migration-guide/). | | `BrazeUICompat` | No | Compatibility library containing all the `ABK*` classes and methods that were available in the `AppboyUI` library from `Appboy-iOS-SDK` version 4.X.X. For usage details, refer to the minimal migration scenario in the [migration guide](https://braze-inc.github.io/braze-swift-sdk/documentation/braze/appboy-migration-guide/). | | `SDWebImage` | No | Dependency used only by `BrazeUICompat` in the minimal migration scenario. | {: .reset-td-br-1 .reset-td-br-2 .reset-td-br-3 aria-label="Step 1.2: Choose your frameworks" } {: .ws-td-nw-1 .reset-td-br-1 .reset-td-br-2 aria-label="Step 1.2: Choose your frameworks" } #### Step 1.3: Prepare your files Decide whether you want to use **Static** or **Dynamic** XCFrameworks, then prepare your files: 1. Create a temporary directory for your XCFrameworks. 2. In `braze-swift-sdk-prebuilt`, open the `dynamic` directory and move `BrazeKit.xcframework` into your directory. Your directory should be similar to the following: ```bash temp_dir └── BrazeKit.xcframework ``` 3. Move each of your [chosen XCFrameworks](#swift_step-2-choose-your-frameworks) into your temporary directory. Your directory should be similar to the following: ```bash temp_dir ├── BrazeKit.xcframework ├── BrazeKitCompat.xcframework ├── BrazeLocation.xcframework └── SDWebImage.xcframework ``` #### Step 1.4: Integrate your frameworks Next, integrate the **Dynamic** or **Static** XCFrameworks you [prepared previously](#swift_step-3-prepare-your-files): In your Xcode project, select your build target, then **General**. Under **Frameworks, Libraries, and Embedded Content**, drag and drop the [files you prepared previously](#swift_step-3-prepare-your-files). !["An example Xcode project with each Braze library set to 'Embed & Sign.'"](https://www.braze.com/docs/ko/ko/assets/img/swift/sdk_integration/embed-and-sign.png?43e0687cf39aa5ba7d61add4e7cee300) **Note:** Starting with the Swift SDK 12.0.0, you should always select **Embed & Sign** for the Braze XCFrameworks for both the static and dynamic variants. This ensures that the frameworks resources are properly embedded in your app bundle. **Tip:** To enable GIF support, add `SDWebImage.xcframework`, located in either `braze-swift-sdk-prebuilt/static` or `braze-swift-sdk-prebuilt/dynamic`. #### Common errors for Objective-C projects If your Xcode project only contains Objective-C files, you may get "missing symbol" errors when you try to build your project. To fix these errors, open your project and add an empty Swift file to your file tree. This will force your build toolchain to embed [Swift Runtime](https://support.apple.com/kb/dl1998) and link to the appropriate frameworks during build time. ```bash FILE_NAME.swift ``` Replace `FILE_NAME` with any non-spaced string. Your file should look similar to the following: ```bash empty_swift_file.swift ``` ### Step 2: Set up delayed initialization (optional) You can choose to delay when the Braze Swift SDK is initialized, which is useful if your app needs to load a configuration or wait for user consent before starting the SDK. Delayed initialization makes sure Braze push notifications and push tokens received before SDK initialization are enqueued and processed once the SDK is initialized. To use delayed initialization, the minimum Braze SDK version is required: #### Step 2.1: Prepare for delayed initialization Call `Braze.prepareForDelayedInitialization()` as early as possible in your app's lifecycle, ideally in or before `application(_:didFinishLaunchingWithOptions:)`. This makes sure that push notifications received before the SDK is initialized are properly captured and processed later. **Note:** This only applies to push notifications from Braze. Other push notifications are handled normally by system delegates. ```swift func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { // Prepare the SDK for delayed initialization Braze.prepareForDelayedInitialization() // ... Additional non-Braze setup code return true } ``` ```swift @main struct MyApp: App { @UIApplicationDelegateAdaptor var appDelegate: AppDelegate var body: some Scene { WindowGroup { ContentView() } } } class AppDelegate: NSObject, UIApplicationDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool { // Prepare the SDK for delayed initialization Braze.prepareForDelayedInitialization() // ... Additional non-Braze setup code return true } } ``` ```objc - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { // Prepare the SDK for delayed initialization [Braze prepareForDelayedInitialization]; // ... Additional non-Braze setup code return YES; } ``` When using delayed initialization, push notification automation is implicitly enabled. You can [customize the push automation](#swift_step-23-customize-push-automation-optional) configuration by passing a `pushAutomation` parameter. #### Step 2.2: Configure push analytics behavior (optional) When delayed initialization is enabled, push analytics are queued by default. However, you can choose to explicitly queue or drop push analytics instead. ##### Explicitly queue To explicitly queue push analytics (default behavior), pass `.queue` to the `analyticsBehavior` parameter. Push analytics events that are queued prior to initialization will be processed and flushed to the server upon initialization. ```swift Braze.prepareForDelayedInitialization(analyticsBehavior: .queue) ``` ```objc [Braze prepareForDelayedInitializationWithAnalyticsBehavior:BRZPushEnqueueBehaviorQueue]; ``` ##### Drop To drop push analytics received before SDK initialization, pass `.drop` to the `analyticsBehavior` parameter. With this option, any push analytics event that occurs while the SDK is not initialized will be ignored. ```swift Braze.prepareForDelayedInitialization(analyticsBehavior: .drop) ``` ```objc [Braze prepareForDelayedInitializationWithAnalyticsBehavior:BRZPushEnqueueBehaviorDrop]; ``` #### Step 2.3: Customize push automation (optional) You can customize the push automation configuration by passing a `pushAutomation` parameter. By default, all automation features are enabled except `requestAuthorizationAtLaunch`. ```swift // Enable all push automation featuresBraze.prepareForDelayedInitialization(pushAutomation: true) // Or customize specific automation options let automation = Braze.Configuration.Push.Automation() automation.automaticSetup = true automation.requestAuthorizationAtLaunch = false Braze.prepareForDelayedInitialization(pushAutomation: automation) ``` ```objc // Enable all push automation features [Braze prepareForDelayedInitializationWithPushAutomation:[[BRZConfigurationPushAutomation alloc] initWithAutomationEnabled:YES]]; // Or customize specific automation options BRZConfigurationPushAutomation *automation = [[BRZConfigurationPushAutomation alloc] init]; automation.automaticSetup = YES; automation.requestAuthorizationAtLaunch = NO; [Braze prepareForDelayedInitializationWithPushAutomation:automation analyticsBehavior:BRZPushEnqueueBehaviorQueue]; ``` #### Step 2.4: Initialize the SDK After your chosen delay period (for example, after fetching configuration from a server or after user consent), initialize the SDK as normal: ```swift func initializeBraze() { let configuration = Braze.Configuration(apiKey: "YOUR-API-KEY", endpoint: "YOUR-ENDPOINT") // Enable push automation to match the delayed initialization configuration configuration.push.automation = true let braze = Braze(configuration: configuration) // Store the Braze instance for later use AppDelegate.braze = braze } ``` ```objc - (void)initializeBraze { BRZConfiguration *configuration = [[BRZConfiguration alloc] initWithApiKey:@"YOUR-API-KEY" endpoint:@"YOUR-ENDPOINT"]; // Enable push automation to match the delayed initialization configuration configuration.push.automation = [[BRZConfigurationPushAutomation alloc] initWithAutomationEnabled:YES]; Braze *braze = [[Braze alloc] initWithConfiguration:configuration]; // Store the Braze instance for later use AppDelegate.braze = braze; } ``` **Note:** When the SDK is initialized, all queued push notifications, push tokens, and deep links are automatically processed. ### Step 3: Update your app delegate **Important:** The following assumes you've already added an `AppDelegate` to your project (which are not generated by default) and you are not using the delayed initialization feature. If you don't plan on using an `AppDelegate`, be sure to initialize the Braze SDK as early as possible, like during the app's launch. If you are using the delayed initialization feature, refer to [Step 2.4](#swift_step-24-initialize-the-sdk) for initializing the SDK and ignore this step. Add the following line of code to your `AppDelegate.swift` file to import the features included in the Braze Swift SDK: ```swift import BrazeKit ``` Next, add a static property to your `AppDelegate` class to keep a strong reference to the Braze instance throughout your application's lifetime: ```swift class AppDelegate: UIResponder, UIApplicationDelegate { static var braze: Braze? = nil } ``` The SDK requires your application to retain a strong reference to the Braze instance throughout its usage. To prevent any unexpected side effects, ensure that you have fully captured that reference before accessing or modifying any properties or methods on the Braze instance. Finally, in `AppDelegate.swift`, add the following snippet to your `application:didFinishLaunchingWithOptions:` method: ```swift let configuration = Braze.Configuration( apiKey: "YOUR-APP-IDENTIFIER-API-KEY", endpoint: "YOUR-BRAZE-ENDPOINT" ) let braze = Braze(configuration: configuration) AppDelegate.braze = braze ``` Update `YOUR-APP-IDENTIFIER-API-KEY` and `YOUR-BRAZE-ENDPOINT` with the correct value from your **App Settings** page. Check out our [API identifier types](https://www.braze.com/docs/ko/ko/api/identifier_types/?tab=app%20ids) for more information on where to find your app identifier API key. Add the following line of code to your `AppDelegate.m` file: ```objc @import BrazeKit; ``` Next, add a static variable to your `AppDelegate.m` file to keep a reference to the Braze instance throughout your application's lifetime: ```objc static Braze *_braze; @implementation AppDelegate + (Braze *)braze { return _braze; } + (void)setBraze:(Braze *)braze { _braze = braze; } @end ``` The SDK requires your application to retain a strong reference to the Braze instance throughout its usage. To prevent any unexpected side effects, ensure that you have fully captured that reference before accessing or modifying any properties or methods on the Braze instance. Finally, within your `AppDelegate.m` file, add the following snippet within your `application:didFinishLaunchingWithOptions:` method: ```objc BRZConfiguration *configuration = [[BRZConfiguration alloc] initWithApiKey:"YOUR-APP-IDENTIFIER-API-KEY" endpoint:"YOUR-BRAZE-ENDPOINT"]; Braze *braze = [[Braze alloc] initWithConfiguration:configuration]; AppDelegate.braze = braze; ``` Update `YOUR-APP-IDENTIFIER-API-KEY` and `YOUR-BRAZE-ENDPOINT` with the correct value from your **Manage Settings** page. Check out our [API documentation](https://www.braze.com/docs/ko/ko/api/api_key/#the-app-identifier-api-key) for more information on where to find your app identifier API key. ## Optional configurations ### Logging For a centralized overview across all platforms, see [Verbose logging](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/verbose_logging). To learn how to interpret log output, see [Reading verbose logs](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/reading_verbose_logs). #### Log levels The default log level for the Braze Swift SDK is `.error`—it's also the minimum supported level when logs are enabled. These are the full list of log levels: | Swift | Objective-C | Description | | ----------- | ------------------------ | ------------------------------------------------------------ | | `.debug` | `BRZLoggerLevelDebug` | Log debugging information + `.info` + `.error`. | | `.info` | `BRZLoggerLevelInfo` | Log general SDK information (user changes, etc.) + `.error`. | | `.error` | `BRZLoggerLevelError` | Log errors. | | `.disabled` | `BRZLoggerLevelDisabled` | No logging occurs. | {: .reset-td-br-1 .reset-td-br-2 .reset-td-br-3 aria-label="Log levels" } {: .reset-td-br-1 .reset-td-br-2 .reset-td-br-3 aria-label="Log levels" } #### Setting the log level You can assign the log level at runtime in your `Braze.Configuration` object. For complete usage details, see [`Braze.Configuration.Logger`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/configuration-swift.class/logger-swift.class). ```swift let configuration = Braze.Configuration( apiKey: "", endpoint: "" ) // Enable logging of general SDK information (such as user changes, etc.) configuration.logger.level = .info let braze = Braze(configuration: configuration) ``` ```objc BRZConfiguration *configuration = [[BRZConfiguration alloc] initWithApiKey:self.APIKey endpoint:self.apiEndpoint]; // Enable logging of general SDK information (such as user changes, etc.) [configuration.logger setLevel:BRZLoggerLevelInfo]; Braze *braze = [[Braze alloc] initWithConfiguration:configuration]; ``` ## Integrating the Cordova SDK ### Prerequisites Before you start, verify your environment is supported by the [latest Braze Cordova SDK version](https://github.com/braze-inc/braze-cordova-sdk?tab=readme-ov-file#minimum-version-requirements). ### Step 1: Add the SDK to your project **Warning:** Only add the Braze Cordova SDK using the methods below. Do not attempt to install using other methods as it could lead to a security breach. If you're on Cordova 6 or later, you can add the SDK directly from GitHub. Alternatively, you can download a ZIP of the [GitHub repository](https://github.com/braze-inc/braze-cordova-sdk) and add the SDK manually. If you don't plan on using location collection and geofences, use the `master` branch from GitHub. ```bash cordova plugin add https://github.com/braze-inc/braze-cordova-sdk#master ``` If you plan on using location collection and geofences, use the `geofence-branch` from GitHub. ```bash cordova plugin add https://github.com/braze-inc/braze-cordova-sdk#geofence-branch ``` **Tip:** You can switch between `master` and `geofence-branch` at anytime by repeating this step. ### Step 2: Configure your project Next, adding the following preferences to the `platform` element in your project's `config.xml` file. ```xml ``` ```xml ``` Replace the following: | Value | Description | | --------------------- | -------------------------------------------------------------------------------------------------------------------------------- | | `BRAZE_API_KEY` | Your [Braze REST API key](https://www.braze.com/docs/ko/ko/user_guide/administrative/app_settings/api_settings_tab/#rest-api-keys). | | `CUSTOM_API_ENDPOINT` | A custom API endpoint. This endpoint is used to route your Braze instance data to the correct App Group in your Braze dashboard. | {: .reset-td-br-1 .reset-td-br-2 aria-label="Step 2: Configure your project" } The `platform` element in your `config.xml` file should be similar to the following: ```xml ``` ```xml ``` ## Platform-specific syntax The following section covers the platform-specific syntax when using Cordova with iOS or Android. ### Integers Integer preferences are read as string representations, like in the following example: ```xml ``` Due to how the Cordova 8.0.0+ framework handles preferences, integer-only preferences (such as sender IDs) must be set to strings prepended with `str_`, like in the following example: ```xml ``` ### Booleans Boolean preferences are read by the SDK using `YES` and `NO` keywords as a string representation, like in the following example: ```xml ``` Boolean preferences are read by the SDK using `true` and `false` keywords as a string representation, like in the following example: ```xml ``` ## Optional configurations {#optional} You can add any of the following preferences to the `platform` element in your project's `config.xml` file: | Method | Description | | ------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `ios_api_key` | Sets the API key for your application. | | `ios_api_endpoint` | Sets the [SDK endpoint](https://www.braze.com/docs/ko/ko/api/basics/#endpoints) for your application. | | `ios_disable_automatic_push_registration` | Sets whether automatic push registration should be disabled. | | `ios_disable_automatic_push_handling` | Sets whether automatic push handling should be disabled. | | `ios_enable_idfa_automatic_collection` | Sets whether the Braze SDK should automatically collect the IDFA information. For more information, see [the Braze IDFA method documentation](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/set(identifierforadvertiser:)/). | | `enable_location_collection` | Sets whether the automatic location collection is enabled (if the user permits). The `geofence-branch` | | `geofences_enabled` | Sets whether geofences are enabled. | | `ios_session_timeout` | Sets the Braze session timeout for your application in seconds. Defaults to 10 seconds. | | `sdk_authentication_enabled` | Sets whether to enable the [SDK Authentication](https://www.braze.com/docs/ko/ko/developer_guide/platform_wide/sdk_authentication#sdk-authentication) feature. | | `display_foreground_push_notifications` | Sets whether push notifications should be displayed while the application is in the foreground. | | `ios_disable_un_authorization_option_provisional` | Sets whether `UNAuthorizationOptionProvisional` should be disabled. | | `trigger_action_minimum_time_interval_seconds` | Sets the minimum time interval in seconds between triggers. Defaults to 30 seconds. | | `ios_push_app_group` | Sets the app group ID for iOS push extensions. | | `ios_forward_universal_links` | Sets whether the SDK automatically recognizes and forwards universal links to the system methods. Required for deep links from push notifications to work on iOS. Defaults to disabled. | | `ios_log_level` | Sets the minimum logging level for `Braze.Configuration.Logger`. | | `ios_use_uuid_as_device_id` | Sets if a randomly generated UUID should be used as the device ID. | | `ios_flush_interval_seconds` | Sets the interval in seconds between automatic data flushes. Defaults to 10 seconds. | | `ios_use_automatic_request_policy` | Sets whether the request policy for `Braze.Configuration.Api` should be automatic or manual. | | `should_opt_in_when_push_authorized` | Sets if a user’s notification subscription state should automatically be set to `optedIn` when push permissions are authorized. | {: .reset-td-br-1 .reset-td-br-2 aria-label="Optional configurations #optional" } **Tip:** For more detailed information, see [GitHub: Braze iOS Cordova plugin](https://github.com/braze-inc/braze-cordova-sdk/blob/master/src/ios/BrazePlugin.m). | Method | Description | | ----------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `android_api_key` | Sets the API key for your application. | | `android_api_endpoint` | Sets the [SDK endpoint](https://www.braze.com/docs/ko/ko/api/basics/#endpoints) for your application. | | `android_small_notification_icon` | Sets the notification small icon. | | `android_large_notification_icon` | Sets the notification large icon. | | `android_notification_accent_color` | Sets the notification accent color using a hexadecimal representation. | | `android_default_session_timeout` | Sets the Braze session timeout for your application in seconds. Defaults to 10 seconds. | | `android_handle_push_deep_links_automatically` | Sets whether the Braze SDK automatically handles push deep links. Required for deep links from push notifications to work on Android. Defaults to disabled. | | `android_log_level` | Sets the log level for your application. The default log level is 4 and will minimally log info. To enable verbose logging for debugging, use log level 2. | | `firebase_cloud_messaging_registration_enabled` | Sets whether to use Firebase Cloud Messaging for push notifications. | | `android_fcm_sender_id` | Sets the Firebase Cloud Messaging sender ID. | | `enable_location_collection` | Sets whether the automatic location collection is enabled (if the user permits). | | `geofences_enabled` | Sets whether geofences are enabled. | | `android_disable_auto_session_tracking` | Disable the Android Cordova plugin from automatically tracking sessions. For more information, see [Disabling automatic session tracking](#cordova_disable-automatic-session-tracking) | | `sdk_authentication_enabled` | Sets whether to enable the [SDK Authentication](https://www.braze.com/docs/ko/ko/developer_guide/platform_wide/sdk_authentication#sdk-authentication) feature. | | `trigger_action_minimum_time_interval_seconds` | Sets the minimum time interval in seconds between triggers. Defaults to 30 seconds. | | `is_session_start_based_timeout_enabled` | Sets whether the session timeout behavior to be based either on session start or session end events. | | `default_notification_channel_name` | Sets the user-facing name as seen via `NotificationChannel.getName` for the Braze default `NotificationChannel`. | | `default_notification_channel_description` | Sets the user-facing description as seen via `NotificationChannel.getDescription` for the Braze default `NotificationChannel`. | | `does_push_story_dismiss_on_click` | Sets whether a Push Story is automatically dismissed when clicked. | | `is_fallback_firebase_messaging_service_enabled` | Sets whether the use of a fallback Firebase Cloud Messaging Service is enabled. | | `fallback_firebase_messaging_service_classpath` | Sets the classpath for the fallback Firebase Cloud Messaging Service. | | `is_content_cards_unread_visual_indicator_enabled` | Sets whether the Content Cards unread visual indication bar is enabled. | | `is_firebase_messaging_service_on_new_token_registration_enabled` | Sets whether the Braze SDK will automatically register tokens in `com.google.firebase.messaging.FirebaseMessagingService.onNewToken`. | | `is_push_deep_link_back_stack_activity_enabled` | Sets whether Braze will add an activity to the back stack when automatically following deep links for push. | | `push_deep_link_back_stack_activity_class_name` | Sets the activity that Braze will add to the back stack when automatically following deep links for push. | | `should_opt_in_when_push_authorized` | Sets if Braze should automatically opt-in the user when push is authorized. | {: .reset-td-br-1 .reset-td-br-2 aria-label="Optional configurations #optional" } **Tip:** For more detailed information, see [GitHub: Braze Android Cordova plugin](https://github.com/braze-inc/braze-cordova-sdk/blob/master/src/android/BrazePlugin.kt). The following is an example `config.xml` file with additional configurations: ```xml ``` ```xml ``` ## Disabling automatic session tracking (Android only) {#disable-automatic-session-tracking} By default, the Android Cordova plugin automatically tracks sessions. To disable automatic session tracking, add the following preference to the `platform` element in your project's `config.xml` file: ```xml ``` To start tracking sessions again, call `BrazePlugin.startSessionTracking()`. Keep in mind, only sessions started after the next `Activity.onStart()` will be tracked. ## About the Flutter Braze SDK After you integrate the Braze Flutter SDK on Android and iOS, you'll be able to use the Braze API within your [Flutter apps](https://flutter.dev/) written in Dart. This plugin provides basic analytics functionality and lets you integrate in-app messages and Content Cards for both iOS and Android with a single codebase. ## Integrating the Flutter SDK ### Prerequisites Before you integrate the Braze Flutter SDK, you'll need to complete the following: | Prerequisite | Description | | --- | --- | | Braze API app identifier | To locate your app's identifier, go to **Settings** > **APIs and Identifiers** > **App Identifiers**. For more information see, [API Identifier Types](https://www.braze.com/docs/ko/ko/api/identifier_types/#app-identifier).| | Braze SDK endpoint | Your SDK endpoint URL (for example, `sdk..braze.com`). Your endpoint will depend on the [Braze URL for your instance](https://www.braze.com/docs/ko/ko/developer_guide/rest_api/basics/#endpoints).| | Flutter SDK | Install the official [Flutter SDK](https://docs.flutter.dev/get-started/install) and ensure it meets the Braze Flutter SDK's [minimum supported version](https://github.com/braze-inc/braze-flutter-sdk#requirements). | {: .reset-td-br-1 .reset-td-br-2 aria-label="Prerequisites" } ### Step 1: Integrate the Braze library Add the Braze Flutter SDK package from the command line. This will add the appropriate line to your `pubspec.yaml`. ```bash flutter pub add braze_plugin ``` ### Step 2: Complete native SDK setup #### 2.1 Set up Android ##### Provide credentials at compile time Create a `braze.xml` file in your project's `android/res/values` folder. The API key and endpoint are provided at runtime from Dart, so they are not required in this file. To enable delayed initialization, add `com_braze_enable_delayed_initialization` to the file: ```xml true ``` ##### Provide credentials at runtime Alternatively, you can enable delayed initialization programmatically in your `MainActivity.kt`: ```kotlin import com.braze.Braze class MainActivity : FlutterActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) Braze.enableDelayedInitialization(context = this) } } ``` Add the required permissions to your `AndroidManifest.xml` file: ```xml ``` #### 2.2 Set up iOS Within your existing `application(_:didFinishLaunchingWithOptions:)` method, add a call to `BrazePlugin.configure(_:postInitialization:)` to store your configuration. The Braze instance is created later when `initialize()` is called from Dart. The API key and endpoint are not set here. Add the following code to your `AppDelegate.swift`: ```swift import BrazeKit import braze_plugin // ... override func application( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil ) -> Bool { // ... your existing didFinishLaunchingWithOptions setup ... BrazePlugin.configure( { configuration in configuration.logger.level = .info // Set other non-API-key configurations here, such as: // configuration.push.automation = true // configuration.sessionTimeout = 60 }, postInitialization: { braze in // Optional: Customize the Braze instance after creation. // For example, set a custom in-app message presenter: // let customPresenter = CustomInAppMessagePresenter() // braze.inAppMessagePresenter = customPresenter } ) return true } ``` Add the following code to your `AppDelegate.m`: ```objc @import BrazeKit; @import braze_plugin; // ... - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { [BrazePlugin configure:^(BRZConfiguration *configuration) { configuration.logger.level = BRZLoggerLevelInfo; // Set other non-API-key configurations here, such as: // configuration.push.automation = ... // configuration.sessionTimeout = 60; } postInitialization:^(Braze *braze) { // Optional: customize the Braze instance after creation. }]; return YES; } ``` **Important:** `BrazePlugin.configure()` only stores your configuration. No Braze instance exists until `initialize()` is called from Dart, so do not call any Braze SDK methods in the AppDelegate after `configure()`. #### 2.1 Set up Android To connect to Braze servers, create a `braze.xml` file in your project's `android/res/values` folder. Paste the following code and replace the API identifier key and endpoint with your values: ```xml YOUR_APP_IDENTIFIER_API_KEY YOUR_CUSTOM_ENDPOINT_OR_CLUSTER ``` Add the required permissions to your `AndroidManifest.xml` file: ```xml ``` #### 2.2 Set up iOS Add the Braze SDK imports at the top of the `AppDelegate.swift` file: ```swift import BrazeKit import braze_plugin ``` In the same file, create the Braze configuration object in the `application(_:didFinishLaunchingWithOptions:)` method and replace the API key and endpoint with your app's values. Then, create the Braze instance using the configuration, and create a static property on the `AppDelegate` for easy access: ```swift static var braze: Braze? = nil override func application( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil ) -> Bool { // Setup Braze let configuration = Braze.Configuration( apiKey: "", endpoint: "" ) // - Enable logging or customize configuration here configuration.logger.level = .info let braze = BrazePlugin.initBraze(configuration) AppDelegate.braze = braze return true } ``` Import the Braze SDK at the top of the `AppDelegate.m` file: ```objc @import BrazeKit; @import braze_plugin; ``` In the same file, create the Braze configuration object in the `application:didFinishLaunchingWithOptions:` method and replace the API key and endpoint with your app's values. Then, create the Braze instance using the configuration, and create a static property on the `AppDelegate` for easy access: ```objc - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { // Setup Braze BRZConfiguration *configuration = [[BRZConfiguration alloc] initWithApiKey:@"" endpoint:@""]; // - Enable logging or customize configuration here configuration.logger.level = BRZLoggerLevelInfo; Braze *braze = [BrazePlugin initBraze:configuration]; AppDelegate.braze = braze; [self.window makeKeyAndVisible]; return YES; } #pragma mark - AppDelegate.braze static Braze *_braze = nil; + (Braze *)braze { return _braze; } + (void)setBraze:(Braze *)braze { _braze = braze; } ``` ### Step 3: Set up the plugin Import the plugin and create a single instance of `BrazePlugin`: ```dart import 'package:braze_plugin/braze_plugin.dart'; final BrazePlugin braze = BrazePlugin(); ``` Then call `initialize()` with your app identifier API key and SDK endpoint to create the Braze instance. See the options below for where to call this method in your app. #### Standard initialization To initialize the SDK when your app starts, call `initialize()` in `initState()`: ```dart @override void initState() { super.initState(); braze.initialize("", ""); } ``` #### Delayed initialization To defer SDK initialization until a later point in the session — for example, after the user grants consent or completes login — call `initialize()` when you're ready: ```dart // ... void onUserConsent() { braze.initialize("", ""); } ``` **Warning:** Push notifications and deep links received before `initialize()` is called are not processed on iOS. On Android, deep links from push notifications do not resolve while the SDK is waiting to be initialized. If your app relies on push or deep links at launch, use [standard initialization](#standard-initialization) instead. #### Platform-specific API keys Since your Android and iOS apps use different API keys, use platform detection: ```dart import 'dart:io' show Platform; if (Platform.isAndroid) { braze.initialize("", ""); } else if (Platform.isIOS) { braze.initialize("", ""); } ``` #### Re-initialization You can call `initialize()` multiple times to re-initialize the SDK with a different API key and endpoint mid-session. Each call tears down the previous Braze instance and creates a new one. **Important:** To avoid undefined behaviors, only allocate and use a single instance of the `BrazePlugin` in your Dart code. All SDK method calls made before `initialize()` are ignored on iOS, so call `initialize()` before using any other Braze methods. To import the plugin into your Dart code, use the following: ```dart import 'package:braze_plugin/braze_plugin.dart'; ``` Then, initialize an instance of the Braze plugin by calling `new BrazePlugin()` like in [our sample app](https://github.com/braze-inc/braze-flutter-sdk/blob/master/example/lib/main.dart). **Important:** To avoid undefined behaviors, only allocate and use a single instance of the `BrazePlugin` in your Dart code. ## Testing the integration You can verify that the SDK is integrated by checking session statistics in the dashboard. If you run your application on either platform, you should see a new session in the dashboard (in the **Overview** section). Open a session for a particular user by calling the following code in your app. ```dart BrazePlugin braze = BrazePlugin(); braze.initialize("", ""); braze.changeUser("{some-user-id}"); ``` ```dart BrazePlugin braze = BrazePlugin(); braze.changeUser("{some-user-id}"); ``` Search for the user with `{some-user-id}` in the dashboard under **Audience** > **Search Users**. There, you can verify that session and device data have been logged. ## About the React Native Braze SDK Integrating the React Native Braze SDK provides basic analytics functionality and lets you integrate in-app messages and Content Cards for both iOS and Android with one codebase. ## New Architecture compatibility The following minimum SDK version is compatible with all apps using [React Native's New Architecture](https://reactnative.dev/docs/the-new-architecture/landing-page): Starting with SDK version 6.0.0, Braze uses a React Native Turbo Module, which is compatible with both the New Architecture and legacy bridge architecture. This means no additional setup is required. **Warning:** If your iOS app conforms to `RCTAppDelegate` and follows our previous `AppDelegate` setup, review the samples in [Complete native setup](#reactnative_step-2-complete-native-setup) to prevent crashes when subscribing to events in the Turbo Module. ## React and React Native version requirements Braze doesn't publish separate minimum React versions beyond what the React Native SDK supports. To integrate the SDK, use React Native version 0.71 or later. For the full list of supported React Native versions, see the [React Native SDK GitHub repository](https://github.com/braze-inc/braze-react-native-sdk?tab=readme-ov-file#version-support). When you upgrade React, React Native, or the Braze SDK, review the SDK [CHANGELOG](https://github.com/braze-inc/braze-react-native-sdk/blob/master/CHANGELOG.md) for breaking changes before you deploy. ## Integrating the React Native SDK ### Prerequisites For supported React Native versions and upgrade guidance, see [React and React Native version requirements](#react-and-react-native-version-requirements). ### Step 1: Integrate the Braze library ```bash npm install @braze/react-native-sdk ``` ```bash yarn add @braze/react-native-sdk ``` ### Step 2: Complete native setup If your app uses Expo, see [Using the Expo plugin](#reactnative-using-the-expo-plugin). If your app uses pure React Native, see [Using React Native CLI](#reactnative-using-react-native-cli). Choose one setup method in each version tab: Expo plugin or React Native CLI. #### Method 1: Using the Expo plugin {#reactnative-using-the-expo-plugin} ##### 2.1 Install the Braze Expo plugin Ensure that your version of the Braze Expo plugin is at least 4.1.0. For the full list of supported versions, see the [Braze Expo plugin repository](https://github.com/braze-inc/braze-expo-plugin?tab=readme-ov-file#version-support). The following code snippet shows the command to install the Braze Expo plugin: ```bash npx expo install @braze/expo-plugin ``` ##### 2.2 Add the plugin to your app.json In your `app.json`, add the Braze Expo plugin. The API key and endpoint are no longer set here. Provide them at runtime through `Braze.initialize()` from JavaScript. Add the following optional configuration parameters based on your implementation needs: | Method | Type | Description | | --------------------------------------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | | `enableBrazeIosPush` | boolean | iOS only. Whether to use Braze to handle push notifications on iOS. | | `enableFirebaseCloudMessaging` | boolean | Android only. Whether to use Firebase Cloud Messaging for push notifications. | | `firebaseCloudMessagingSenderId` | string | Android only. Your Firebase Cloud Messaging sender ID. | | `sessionTimeout` | integer | The Braze session timeout for your application in seconds. | | `enableSdkAuthentication` | boolean | Whether to enable the [SDK Authentication](https://www.braze.com/docs/ko/ko/developer_guide/platform_wide/sdk_authentication#sdk-authentication) feature. | | `logLevel` | integer | The log level for your application. The default log level is 8 and minimally logs info. To enable verbose logging for debugging, use log level 0. | | `minimumTriggerIntervalInSeconds` | integer | The minimum time interval in seconds between triggers. Defaults to 30 seconds. | | `enableAutomaticLocationCollection` | boolean | Whether automatic location collection is enabled (if the user permits). | | `enableGeofence` | boolean | Whether geofences are enabled. | | `enableAutomaticGeofenceRequests` | boolean | Whether geofence requests should be made automatically. | | `dismissModalOnOutsideTap` | boolean | iOS only. Whether a modal in-app message is dismissed when the user clicks outside of the in-app message. | | `androidHandlePushDeepLinksAutomatically` | boolean | Android only. Whether the Braze SDK should automatically handle push deep links. | | `androidPushNotificationHtmlRenderingEnabled` | boolean | Android only. Sets whether the text content in a push notification should be interpreted and rendered as HTML using `android.text.Html.fromHtml`. | | `androidNotificationAccentColor` | string | Android only. Sets the Android notification accent color. | | `androidNotificationLargeIcon` | string | Android only. Sets the Android notification large icon. | | `androidNotificationSmallIcon` | string | Android only. Sets the Android notification small icon. | | `iosRequestPushPermissionsAutomatically` | boolean | iOS only. Whether the user should automatically be prompted for push permissions on app launch. | | `enableBrazeIosRichPush` | boolean | iOS only. Whether to enable rich push features for iOS. | | `enableBrazeIosPushStories` | boolean | iOS only. Whether to enable Braze Push Stories for iOS. | | `iosPushStoryAppGroup` | string | iOS only. The app group used for iOS Push Stories. | | `iosUseUUIDAsDeviceId` | boolean | iOS only. Whether the device ID uses a randomly generated UUID. | | `iosForwardUniversalLinks` | boolean | iOS only. Specifies if the SDK should automatically recognize and forward universal links to the system methods (default: `false`). | {: .reset-td-br-1 .reset-td-br-2 .reset-td-br-3 aria-label="2.2 Add the plugin to your app.json" } The following code snippet shows an example `app.json` configuration: ```json { "expo": { "plugins": [ [ "@braze/expo-plugin", { "sessionTimeout": 60, "enableGeofence": false, "enableBrazeIosPush": false, "enableFirebaseCloudMessaging": false, "firebaseCloudMessagingSenderId": "YOUR-FCM-SENDER-ID", "androidHandlePushDeepLinksAutomatically": true, "enableSdkAuthentication": false, "logLevel": 0, "minimumTriggerIntervalInSeconds": 0, "enableAutomaticLocationCollection": false, "enableAutomaticGeofenceRequests": false, "dismissModalOnOutsideTap": true, "androidPushNotificationHtmlRenderingEnabled": true, "androidNotificationAccentColor": "#ff3344", "androidNotificationLargeIcon": "@drawable/custom_app_large_icon", "androidNotificationSmallIcon": "@drawable/custom_app_small_icon", "iosRequestPushPermissionsAutomatically": false, "enableBrazeIosPushStories": true, "iosPushStoryAppGroup": "group.com.example.myapp.PushStories", "iosForwardUniversalLinks": false } ] ] } } ``` ###### Configuring Android push notification icons {#android-push-icons} When using `androidNotificationLargeIcon` and `androidNotificationSmallIcon`, follow these best practices for proper icon display: **Icon placement and format** To use custom push notification icons with the Braze Expo plugin: 1. Create your icon files following the Icon requirements listed below. 2. Place them in your project's Android native directories at `android/app/src/main/res/drawable-/`. For example, use `android/app/src/main/res/drawable-mdpi/` and `android/app/src/main/res/drawable-hdpi/`. 3. Alternatively, if you're managing assets in your React Native directory, you can use Expo's [app.json icon configuration](https://docs.expo.dev/versions/latest/config/app/#icon) or create an [Expo config plugin](https://docs.expo.dev/config-plugins/introduction/) to copy the icons to the Android drawable folders during prebuild. The Braze Expo plugin references these icons using Android's drawable resource system. **Icon requirements** - **Small icon:** Must be a white silhouette on a transparent background (this is an Android platform requirement) - **Large icon:** Can be a full-color image. - **Format:** PNG format is recommended. - **Naming:** Use lowercase letters, numbers, and underscores only (for example, `my_large_icon.png`) **Configuration in app.json** The following code snippet shows how to reference Android notification icons in `app.json` using the `@drawable/` prefix: ```json { "expo": { "plugins": [ [ "@braze/expo-plugin", { "androidNotificationLargeIcon": "@drawable/large_icon", "androidNotificationSmallIcon": "@drawable/small_icon" } ] ] } } ``` **Important:** Do not use relative file paths (such as `src/assets/images/icon.png`) or include the file extension when referencing icons. The Expo plugin requires the `@drawable/` prefix to properly locate the icons in the Android native folders after the prebuild process. **How it works** The Braze Expo plugin references your icon files from the Android `drawable` directories. When you run `npx expo prebuild`, Expo generates the native Android project structure. Your icons must be present in the Android `drawable` folders (either placed manually or copied through a config plugin) before the build process. The plugin then configures the Braze SDK to use these drawable resources by their names (without path or extension), which is why the `@drawable/` prefix is required in your configuration. For more information on Android notification icons, see [Android's notification icon guidelines](https://developer.android.com/develop/ui/views/notifications#icon). ##### 2.3 Build and run your application Prebuilding your application generates the native files necessary for the Braze Expo plugin to work. The following code snippet shows the command to prebuild your application: ```bash npx expo prebuild ``` Run your application as specified in the [Expo docs](https://docs.expo.dev/workflow/customizing/). If you make changes to the configuration options, prebuild and run the application again. #### Method 2: Using React Native CLI {#reactnative-using-react-native-cli} ##### Set up Android **2.1 Add the Kotlin Gradle plugin** The following code snippet shows how to add the Kotlin Gradle plugin in your top-level project `build.gradle` under `buildscript` > `dependencies`: ```groovy buildscript { dependencies { ... // Choose your Kotlin version classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.8.10") } } ``` This adds Kotlin to your project. **2.2 Configure the Braze SDK** Create a `braze.xml` file in your project's `res/values` folder. The API key and endpoint are provided at runtime from JavaScript, so they are not required in this file. The following code snippet shows how to enable delayed initialization with `com_braze_enable_delayed_initialization`: ```xml true ``` **Note:** You can still add other native configuration values to `braze.xml` (such as push, session timeout, and logging settings). These are applied automatically when `Braze.initialize()` is called from JavaScript. The following code snippet shows the required permissions for your `AndroidManifest.xml` file: ```xml ``` **Tip:** On Braze Android SDK version 12.2.0 or later, you can automatically pull in the android-sdk-location library by setting `importBrazeLocationLibrary=true` in your `gradle.properties` file. **2.3 Implement user session tracking** The calls to `openSession()` and `closeSession()` are handled automatically. The following code snippet shows what to add to the `onCreate()` method of your `MainApplication` class: ```java import com.braze.BrazeActivityLifecycleCallbackListener; @Override public void onCreate() { super.onCreate(); ... registerActivityLifecycleCallbacks(new BrazeActivityLifecycleCallbackListener()); } ``` ```kotlin import com.braze.BrazeActivityLifecycleCallbackListener override fun onCreate() { super.onCreate() ... registerActivityLifecycleCallbacks(BrazeActivityLifecycleCallbackListener()) } ``` **2.4 Handle intent updates** If your MainActivity has `android:launchMode` set to `singleTask`, the following code snippet shows what to add to your `MainActivity` class: ```java @Override public void onNewIntent(Intent intent) { super.onNewIntent(intent); setIntent(intent); } ``` ```kotlin override fun onNewIntent(intent: Intent) { super.onNewIntent(intent) setIntent(intent) } ``` ##### Set up iOS **2.5 (Optional) Configure Podfile for dynamic XCFrameworks** To import certain Braze libraries, such as BrazeUI, into an Objective-C++ file, you must use the `#import` syntax. Starting in version `7.4.0` of the Braze Swift SDK, binaries have an [optional distribution channel as dynamic XCFrameworks](https://github.com/braze-inc/braze-swift-sdk-prebuilt-dynamic), which are compatible with this syntax. If you'd like to use this distribution channel, manually override the CocoaPods source locations in your Podfile. Reference the sample below and replace `{your-version}` with the relevant version you wish to import: ```ruby pod 'BrazeKit', :podspec => 'https://raw.githubusercontent.com/braze-inc/braze-swift-sdk-prebuilt-dynamic/{your-version}/BrazeKit.podspec' pod 'BrazeUI', :podspec => 'https://raw.githubusercontent.com/braze-inc/braze-swift-sdk-prebuilt-dynamic/{your-version}/BrazeUI.podspec' pod 'BrazeLocation', :podspec => 'https://raw.githubusercontent.com/braze-inc/braze-swift-sdk-prebuilt-dynamic/{your-version}/BrazeLocation.podspec' ``` **2.6 Install pods** Since React Native automatically links the libraries to the native platform, you can install the SDK with the help of CocoaPods. The following code snippet shows how to install pods from the root folder of the project: ```bash # To install using the React Native New Architecture cd ios && pod install # To install using the React Native legacy architecture cd ios && RCT_NEW_ARCH_ENABLED=0 pod install ``` **2.7 Configure the Braze SDK** Use `BrazeReactInitializer.configure` in your `AppDelegate` to register native configuration. The closures you provide are stored and applied later when `Braze.initialize(apiKey, endpoint)` is called from JavaScript. The following code snippet shows how to import the Braze SDK at the top of the `AppDelegate.swift` file: ```swift import BrazeKit import braze_react_native_sdk ``` In the `application(_:didFinishLaunchingWithOptions:)` method, register your native configuration using `BrazeReactInitializer.configure`. Do not set the API key or endpoint here. They are provided from JavaScript through `Braze.initialize()`. - **`configure` closure**: Receives a `Braze.Configuration` and lets you set native configuration properties (logging, push, sessions, and more). - **`postInitialization` closure** _(optional)_: Receives the live `Braze` instance after creation, for setup that requires the instance (for example, storing a reference or setting delegates). The following code snippet shows an example `AppDelegate.swift` implementation that uses `BrazeReactInitializer.configure`: ```swift @main class AppDelegate: UIResponder, UIApplicationDelegate { static var braze: Braze? = nil func application( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil ) -> Bool { BrazeReactInitializer.configure { configuration in configuration.logger.level = .info configuration.push.automation = true } postInitialization: { braze in AppDelegate.braze = braze } // ... React Native setup return true } } ``` The following code snippet shows how to import the Braze SDK at the top of the `AppDelegate.m` file: ```objc @import BrazeKit; @import braze_react_native_sdk; ``` In the `application:didFinishLaunchingWithOptions:` method, register your native configuration using `BrazeReactInitializer`. Do not set the API key or endpoint here. They are provided from JavaScript through `Braze.initialize()`. The following code snippet shows an example `AppDelegate.m` implementation that uses `BrazeReactInitializer`: ```objc - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { [BrazeReactInitializer configure:^(BRZConfiguration *configuration) { configuration.logger.level = BRZLoggerLevelInfo; configuration.push.automation = [[BRZConfigurationPushAutomation alloc] initWithAutomationEnabled:YES]; } postInitialization:^(Braze *braze) { // Store the Braze instance for later use. }]; /* Other configuration */ return YES; } ``` **Important:** `BrazeReactInitializer.configure()` only stores your configuration. No Braze instance exists until `Braze.initialize()` is called from JavaScript, so do not call any Braze SDK methods in the AppDelegate after `configure()`. When you call `Braze.initialize()` again, the same `configure` and `postInitialization` blocks are applied to the new Braze instance. #### Method 1: Using the Expo plugin ##### Step 2.1: Install the Braze Expo plugin Ensure that your version of the Braze React Native SDK is at least 1.37.0. For the full list of supported versions, see the [Braze React Native repository](https://github.com/braze-inc/braze-react-native-sdk?tab=readme-ov-file#version-support). The following code snippet shows the command to install the Braze Expo plugin: ```bash npx expo install @braze/expo-plugin ``` ##### Step 2.2: Add the plugin to your app.json In your `app.json`, add the Braze Expo plugin. You can provide the following configuration options: | Method | Type | Description | | --------------------------------------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | | `androidApiKey` | string | Required. The [API key](https://www.braze.com/docs/ko/ko/api/identifier_types/) for your Android application, located in your Braze dashboard under **Manage Settings**. | | `iosApiKey` | string | Required. The [API key](https://www.braze.com/docs/ko/ko/api/identifier_types/) for your iOS application, located in your Braze dashboard under **Manage Settings**. | | `baseUrl` | string | Required. The [SDK endpoint](https://www.braze.com/docs/ko/ko/api/basics/#endpoints) for your application, located in your Braze dashboard under **Manage Settings**. | | `enableBrazeIosPush` | boolean | iOS only. Whether to use Braze to handle push notifications on iOS. Introduced in React Native SDK v1.38.0 and Expo Plugin v0.4.0. | | `enableFirebaseCloudMessaging` | boolean | Android only. Whether to use Firebase Cloud Messaging for push notifications. Introduced in React Native SDK v1.38.0 and Expo Plugin v0.4.0. | | `firebaseCloudMessagingSenderId` | string | Android only. Your Firebase Cloud Messaging sender ID. Introduced in React Native SDK v1.38.0 and Expo Plugin v0.4.0. | | `sessionTimeout` | integer | The Braze session timeout for your application in seconds. | | `enableSdkAuthentication` | boolean | Whether to enable the [SDK Authentication](https://www.braze.com/docs/ko/ko/developer_guide/platform_wide/sdk_authentication#sdk-authentication) feature. | | `logLevel` | integer | The log level for your application. The default log level is 8 and minimally logs info. To enable verbose logging for debugging, use log level 0. | | `minimumTriggerIntervalInSeconds` | integer | The minimum time interval in seconds between triggers. Defaults to 30 seconds. | | `enableAutomaticLocationCollection` | boolean | Whether automatic location collection is enabled (if the user permits). | | `enableGeofence` | boolean | Whether geofences are enabled. | | `enableAutomaticGeofenceRequests` | boolean | Whether geofence requests should be made automatically. | | `dismissModalOnOutsideTap` | boolean | iOS only. Whether a modal in-app message is dismissed when the user clicks outside of the in-app message. | | `androidHandlePushDeepLinksAutomatically` | boolean | Android only. Whether the Braze SDK should automatically handle push deep links. | | `androidPushNotificationHtmlRenderingEnabled` | boolean | Android only. Sets whether the text content in a push notification should be interpreted and rendered as HTML using `android.text.Html.fromHtml`. | | `androidNotificationAccentColor` | string | Android only. Sets the Android notification accent color. | | `androidNotificationLargeIcon` | string | Android only. Sets the Android notification large icon. | | `androidNotificationSmallIcon` | string | Android only. Sets the Android notification small icon. | | `iosRequestPushPermissionsAutomatically` | boolean | iOS only. Whether the user should automatically be prompted for push permissions on app launch. | | `enableBrazeIosRichPush` | boolean | iOS only. Whether to enable rich push features for iOS. | | `enableBrazeIosPushStories` | boolean | iOS only. Whether to enable Braze Push Stories for iOS. | | `iosPushStoryAppGroup` | string | iOS only. The app group used for iOS Push Stories. | | `iosUseUUIDAsDeviceId` | boolean | iOS only. Whether the device ID will use a randomly generated UUID. | | `iosForwardUniversalLinks` | boolean | iOS only. Specifies if the SDK should automatically recognize and forward universal links to the system methods (default: `false`). When enabled, the SDK will automatically forward universal links to the system methods defined in [Supporting universal links in your app](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/configuration-swift.class/forwarduniversallinks/). Introduced in React Native SDK v11.1.0 and Expo Plugin v3.2.0. | {: .reset-td-br-1 .reset-td-br-2 .reset-td-br-3 aria-label="Step 2.2: Add the plugin to your app.json" } The following code snippet shows an example `app.json` configuration: ```json { "expo": { "plugins": [ [ "@braze/expo-plugin", { "androidApiKey": "YOUR-ANDROID-API-KEY", "iosApiKey": "YOUR-IOS-API-KEY", "baseUrl": "YOUR-SDK-ENDPOINT", "sessionTimeout": 60, "enableGeofence": false, "enableBrazeIosPush": false, "enableFirebaseCloudMessaging": false, "firebaseCloudMessagingSenderId": "YOUR-FCM-SENDER-ID", "androidHandlePushDeepLinksAutomatically": true, "enableSdkAuthentication": false, "logLevel": 0, "minimumTriggerIntervalInSeconds": 0, "enableAutomaticLocationCollection": false, "enableAutomaticGeofenceRequests": false, "dismissModalOnOutsideTap": true, "androidPushNotificationHtmlRenderingEnabled": true, "androidNotificationAccentColor": "#ff3344", "androidNotificationLargeIcon": "@drawable/custom_app_large_icon", "androidNotificationSmallIcon": "@drawable/custom_app_small_icon", "iosRequestPushPermissionsAutomatically": false, "enableBrazeIosPushStories": true, "iosPushStoryAppGroup": "group.com.example.myapp.PushStories", "iosForwardUniversalLinks": false } ], ] } } ``` ###### Configuring Android push notification icons When using `androidNotificationLargeIcon` and `androidNotificationSmallIcon`, follow these best practices for proper icon display: **Icon placement and format** To use custom push notification icons with the Braze Expo plugin: 1. Create your icon files following the Icon requirements listed below. 2. Place them in your project's Android native directories at `android/app/src/main/res/drawable-/` (for example, `android/app/src/main/res/drawable-mdpi/`, `drawable-hdpi/`, or similar.) 3. Alternatively, if you're managing assets in your React Native directory, you can use Expo's [app.json icon configuration](https://docs.expo.dev/versions/latest/config/app/#icon) or create an [Expo config plugin](https://docs.expo.dev/config-plugins/introduction/) to copy the icons to the Android drawable folders during prebuild. The Braze Expo plugin references these icons using Android's drawable resource system. **Icon requirements** - **Small icon:** Must be a white silhouette on a transparent background (this is an Android platform requirement) - **Large icon:** Can be a full-color image. - **Format:** PNG format is recommended. - **Naming:** Use lowercase letters, numbers, and underscores only (for example, `my_large_icon.png`) **Configuration in app.json** The following code snippet shows how to reference Android notification icons in `app.json` using the `@drawable/` prefix: ```json { "expo": { "plugins": [ [ "@braze/expo-plugin", { "androidNotificationLargeIcon": "@drawable/large_icon", "androidNotificationSmallIcon": "@drawable/small_icon" } ] ] } } ``` **Important:** Do not use relative file paths (such as `src/assets/images/icon.png`) or include the file extension when referencing icons. The Expo plugin requires the `@drawable/` prefix to properly locate the icons in the Android native folders after the prebuild process. **How it works** The Braze Expo plugin references your icon files from the Android `drawable` directories. When you run `npx expo prebuild`, Expo generates the native Android project structure. Your icons must be present in the Android `drawable` folders (either placed manually or copied through a config plugin) before the build process. The plugin then configures the Braze SDK to use these drawable resources by their names (without path or extension), which is why the `@drawable/` prefix is required in your configuration. For more information on Android notification icons, see [Android's notification icon guidelines](https://developer.android.com/develop/ui/views/notifications#icon). ##### Step 2.3: Build and run your application Prebuilding your application generates the native files necessary for the Braze Expo plugin to work. The following code snippet shows the command to prebuild your application: ```bash npx expo prebuild ``` Run your application as specified in the [Expo docs](https://docs.expo.dev/workflow/customizing/). Keep in mind, if you make any changes to the configuration options, you'll be required to prebuild and run the application again. #### Method 2: Using React Native CLI ##### Set up Android **Step 2.1: Add the Kotlin Gradle plugin** The following code snippet shows how to add the Kotlin Gradle plugin in your top-level project `build.gradle` under `buildscript` > `dependencies`: ```groovy buildscript { dependencies { ... // Choose your Kotlin version classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.8.10") } } ``` This adds Kotlin to your project. **Step 2.2: Configure the Braze SDK** To connect to Braze servers, create a `braze.xml` file in your project's `res/values` folder. The following code snippet shows an example `braze.xml` configuration. Replace the API [key](https://www.braze.com/docs/ko/ko/api/identifier_types/) and [endpoint](https://www.braze.com/docs/ko/ko/api/basics/#endpoints) with your values: ```xml YOU_APP_IDENTIFIER_API_KEY YOUR_CUSTOM_ENDPOINT_OR_CLUSTER ``` The following code snippet shows the required permissions for your `AndroidManifest.xml` file: ```xml ``` **Tip:** On Braze Android SDK version 12.2.0 or later, you can automatically pull in the android-sdk-location library by setting `importBrazeLocationLibrary=true` in your `gradle.properties` file. **Step 2.3: Implement user session tracking** The calls to `openSession()` and `closeSession()` are handled automatically. The following code snippet shows what to add to the `onCreate()` method of your `MainApplication` class: ```java import com.braze.BrazeActivityLifecycleCallbackListener; @Override public void onCreate() { super.onCreate(); ... registerActivityLifecycleCallbacks(new BrazeActivityLifecycleCallbackListener()); } ``` ```kotlin import com.braze.BrazeActivityLifecycleCallbackListener override fun onCreate() { super.onCreate() ... registerActivityLifecycleCallbacks(BrazeActivityLifecycleCallbackListener()) } ``` **Step 2.4: Handle intent updates** If your MainActivity has `android:launchMode` set to `singleTask`, the following code snippet shows what to add to your `MainActivity` class: ```java @Override public void onNewIntent(Intent intent) { super.onNewIntent(intent); setIntent(intent); } ``` ```kotlin override fun onNewIntent(intent: Intent) { super.onNewIntent(intent) setIntent(intent) } ``` ##### Set up iOS **Step 2.5: (Optional) Configure Podfile for dynamic XCFrameworks** To import certain Braze libraries, such as BrazeUI, into an Objective-C++ file, you must use the `#import` syntax. Starting in version `7.4.0` of the Braze Swift SDK, binaries have an [optional distribution channel as dynamic XCFrameworks](https://github.com/braze-inc/braze-swift-sdk-prebuilt-dynamic), which are compatible with this syntax. If you'd like to use this distribution channel, manually override the CocoaPods source locations in your Podfile. The following code snippet shows a sample override. Replace `{your-version}` with the relevant version you wish to import: ```ruby pod 'BrazeKit', :podspec => 'https://raw.githubusercontent.com/braze-inc/braze-swift-sdk-prebuilt-dynamic/{your-version}/BrazeKit.podspec' pod 'BrazeUI', :podspec => 'https://raw.githubusercontent.com/braze-inc/braze-swift-sdk-prebuilt-dynamic/{your-version}/BrazeUI.podspec' pod 'BrazeLocation', :podspec => 'https://raw.githubusercontent.com/braze-inc/braze-swift-sdk-prebuilt-dynamic/{your-version}/BrazeLocation.podspec' ``` **Step 2.6: Install pods** Since React Native automatically links the libraries to the native platform, you can install the SDK with the help of CocoaPods. The following code snippet shows how to install pods from the root folder of the project: ```bash # To install using the React Native New Architecture cd ios && pod install # To install using the React Native legacy architecture cd ios && RCT_NEW_ARCH_ENABLED=0 pod install ``` **Step 2.7: Configure the Braze SDK** The following code snippet shows how to import the Braze SDK at the top of the `AppDelegate.swift` file: ```swift import BrazeKit import braze_react_native_sdk ``` In the `application(_:didFinishLaunchingWithOptions:)` method, replace the API [key](https://www.braze.com/docs/ko/ko/api/identifier_types/) and [endpoint](https://www.braze.com/docs/ko/ko/api/basics/#endpoints) with your app's values. Then, create the Braze instance using the configuration, and create a static property on the `AppDelegate` for easy access. **Note:** Our example assumes an implementation of [RCTAppDelegate](https://github.com/facebook/react-native/blob/e64756ae5bb5c0607a4d97a134620fafcb132b3b/packages/react-native/Libraries/AppDelegate/RCTAppDelegate.h), which provides a number of abstractions in the React Native setup. If you are using a different setup for your app, be sure to adjust your implementation as needed. The following code snippet shows an example `AppDelegate.swift` setup: ```swift func application( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil ) -> Bool { // Setup Braze let configuration = Braze.Configuration( apiKey: "{BRAZE_API_KEY}", endpoint: "{BRAZE_ENDPOINT}") // Enable logging and customize the configuration here. configuration.logger.level = .info let braze = BrazeReactBridge.perform( #selector(BrazeReactBridge.initBraze(_:)), with: configuration ).takeUnretainedValue() as! Braze AppDelegate.braze = braze /* Other configuration */ return true } // MARK: - AppDelegate.braze static var braze: Braze? = nil ``` The following code snippet shows how to import the Braze SDK at the top of the `AppDelegate.m` file: ```objc #import #import "BrazeReactBridge.h" ``` In the `application:didFinishLaunchingWithOptions:` method, replace the API [key](https://www.braze.com/docs/ko/ko/api/identifier_types/) and [endpoint](https://www.braze.com/docs/ko/ko/api/basics/#endpoints) with your app's values. Then, create the Braze instance using the configuration, and create a static property on the `AppDelegate` for easy access. **Note:** Our example assumes an implementation of [RCTAppDelegate](https://github.com/facebook/react-native/blob/e64756ae5bb5c0607a4d97a134620fafcb132b3b/packages/react-native/Libraries/AppDelegate/RCTAppDelegate.h), which provides a number of abstractions in the React Native setup. If you are using a different setup for your app, be sure to adjust your implementation as needed. The following code snippet shows an example `AppDelegate.m` setup: ```objc - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { // Setup Braze BRZConfiguration *configuration = [[BRZConfiguration alloc] initWithApiKey:@"{BRAZE_API_KEY}" endpoint:@"{BRAZE_ENDPOINT}"]; // Enable logging and customize the configuration here. configuration.logger.level = BRZLoggerLevelInfo; Braze *braze = [BrazeReactBridge initBraze:configuration]; AppDelegate.braze = braze; /* Other configuration */ return YES; } #pragma mark - AppDelegate.braze static Braze *_braze = nil; + (Braze *)braze { return _braze; } + (void)setBraze:(Braze *)braze { _braze = braze; } ``` ### Step 3: Initialize the SDK The following code snippet shows how to import the library in your React Native code: ```javascript import Braze from "@braze/react-native-sdk"; ``` Then call `Braze.initialize()` with your app identifier API key and SDK endpoint to create the Braze instance. See the options below for where to call this method in your app. #### Standard initialization The following code snippet shows how to initialize the SDK when your app starts by calling `Braze.initialize()` in a `useEffect`: ```javascript import React, { useEffect } from "react"; import Braze from "@braze/react-native-sdk"; const App = () => { useEffect(() => { Braze.initialize("YOUR-API-KEY", "YOUR-SDK-ENDPOINT"); }, []); return ( // Your app components ); }; ``` #### Delayed initialization The following code snippet shows how to defer SDK initialization until later in the session. For example, after the user grants consent or completes login: ```javascript function onUserConsent() { Braze.initialize("YOUR-API-KEY", "YOUR-SDK-ENDPOINT"); } ``` **Warning:** On iOS, push notifications received before `Braze.initialize()` are queued and processed after initialization. On Android, deep links from push notifications do not resolve while the SDK is waiting to be initialized. If your app relies on immediate deep link handling at launch, use [standard initialization](#standard-initialization) instead. #### Platform-specific API keys The following code snippet shows how to use platform detection when your Android and iOS apps use different API keys: ```javascript import { Platform } from "react-native"; import Braze from "@braze/react-native-sdk"; const apiKey = Platform.select({ android: "YOUR-ANDROID-API-KEY", ios: "YOUR-IOS-API-KEY", }) ?? ""; Braze.initialize(apiKey, "YOUR-SDK-ENDPOINT"); ``` #### Re-initialization You can call `Braze.initialize()` multiple times to re-initialize the SDK with a different API key and endpoint mid-session. Each call tears down the previous Braze instance and creates a new one. **Important:** All SDK method calls made before `Braze.initialize()` are ignored on iOS, so call `Braze.initialize()` before using any other Braze methods. For React Native SDK 19.1.0 and earlier, native initialization happens in Step 2. Import the library in your React Native code to call Braze methods. For more details, check out our [sample project](https://github.com/braze-inc/braze-react-native-sdk/tree/master/BrazeProject). ```javascript import Braze from "@braze/react-native-sdk"; ``` ### Step 4: Test the integration (optional) You can verify that the SDK is integrated by checking session statistics in the dashboard. If you run your application on either platform, you should see a new session in the dashboard (in the **Overview** section). The following code snippet shows how to open a session for a particular user in your app: ```javascript import Braze from "@braze/react-native-sdk"; Braze.initialize("YOUR-API-KEY", "YOUR-SDK-ENDPOINT"); Braze.changeUser("{some-user-id}"); ``` Search for the user with `{some-user-id}` in the dashboard under **Audience** > **Search Users**. There, you can verify that session and device data have been logged. To test your SDK integration, the following code snippet shows how to start a new session on either platform for a user. ```javascript Braze.changeUser("userId"); ``` The following code snippet shows an example of assigning the user ID at app startup: ```javascript import React, { useEffect } from "react"; import Braze from "@braze/react-native-sdk"; const App = () => { useEffect(() => { Braze.changeUser("some-user-id"); }, []); return (
...
) ``` In the Braze dashboard, go to [User Search](https://www.braze.com/docs/ko/ko/user_guide/engagement_tools/segments/using_user_search#using-user-search) and look for the user with the ID matching `some-user-id`. Here, you can verify that session and device data were logged. ## Next steps After integrating the Braze SDK, you can start implementing common messaging features: - [Push Notifications](https://www.braze.com/docs/ko/ko/developer_guide/push_notifications/): Set up and send push notifications to your users. - [In-App Messages](https://www.braze.com/docs/ko/ko/developer_guide/in_app_messages/): Display contextual messages within your app. - [Banners](https://www.braze.com/docs/ko/ko/developer_guide/banners/): Show persistent banners in your app interface. ## Integrating the Roku SDK ### Step 1: Add files Braze SDK files can be found in the `sdk_files` directory in the [Braze Roku SDK repository](https://github.com/braze-inc/braze-roku-sdk). 1. Add `BrazeSDK.brs` to your app in the `source` directory. 2. Add `BrazeTask.brs` and `BrazeTask.xml` to your app in the `components` directory. ### Step 2: Add references Add a reference to `BrazeSDK.brs` in your main scene using the following `script` element: ``` ``` 4. Under **Triggering**, select the trigger you created in step 2. 5. Save and publish your container. To include event properties, pass them as the second argument: ```html ``` ## Google's EU User Consent Policy **Important:** Google is updating their [EU User Consent Policy](https://www.google.com/about/company/user-consent-policy/) in response to changes to the [Digital Markets Act (DMA)](https://ads-developers.googleblog.com/2023/10/updates-to-customer-match-conversion.html), which is in effect as of March 6, 2024. This new change requires advertisers to disclose certain information to their EEA and UK end users, as well as obtain necessary consents from them. Review the following documentation to learn more. As part of Google's EU User Consent Policy, the following boolean custom attributes need to be logged to user profiles: - `$google_ad_user_data` - `$google_ad_personalization` If setting these via the GTM integration, custom attributes require creating a custom HTML tag. The following is an example of how to log these values as boolean data types (not as strings): ```js ``` For more information, refer to [Audience Sync to Google](https://www.braze.com/docs/ko/ko/partners/canvas_audience_sync/google_audience_sync/). ## Prerequisites Before you can use this feature, you'll need to [integrate the Android Braze SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=android). ## Using Google Tag Manager for Android In the following example, a music streaming app wants to log different events as users listen to songs. Using Google Tag Manager for Android, they can control which of the Braze third-party vendors receive this event, and create tags specific to Braze. ### Step 1: Create a trigger for custom events Custom events are logged with `actionType` set to `logEvent`. The Braze custom tag provider in this example is expecting the custom event name to be set using `eventName`. To get started, create a trigger that looks for an "Event Name" that equals `played song` ![A custom trigger in Google Tag Manager set to trigger for some events when "event name" equals "played song".](https://www.braze.com/docs/ko/ko/assets/img/android_google_tag_manager/gtm_android_trigger.png?ce7d5cd1e1ab6a285076d8429ac796bd) Next, create a new tag (also known as a "Function Call") and enter the class path of your [custom tag provider](#adding-android-google-tag-provider) described later in this article. This tag will be triggered when you log the `played song` event. In the tag's custom parameters (also known as the key-value pairs), set `eventName` to `played song`. This will be the custom event name logged to Braze. ![A tag in Google Tag Manager with classpath and key-value pair fields. This tag is set to trigger with the previously created "played song" trigger.](https://www.braze.com/docs/ko/ko/assets/img/android_google_tag_manager/gtm_android_function_call_tag.png?40fad5b2a61b7d2183f635a10e290252) **Important:** When sending a custom event, be sure to set `actionType` to `logEvent`, and set a value for `eventName` so Braze receives the correct event name and action to take. You can also include additional key-value pair arguments to the tag, which will be sent as custom event properties to Braze. `eventName` and `actionType` will not be ignored for custom event properties. In the following example tag, `genre` is passed and defined using a tag variable in Google Tag Manager, which is sourced from the custom event logged in the app. Because Google Tag Manager for Android uses Firebase as the data layer, the `genre` event property is sent to Google Tag Manager as a "Firebase - Event Parameter" variable. ![A variable in Google Tag Manager where "genre" is added as an event parameter for the "Braze - Played Song Event" tag.](https://www.braze.com/docs/ko/ko/assets/img/android_google_tag_manager/gtm_android_eventname_variable.png?abff82f38b65ae64ad0ae3842d2ea439) When a user plays a song in the app, an event will be logged through Firebase and Google Tag Manager using the Firebase analytics event name that matches the tag's trigger name, `played song`: ```java Bundle params = new Bundle(); params.putString("genre", "pop"); params.putInt("number of times listened", 42); mFirebaseAnalytics.logEvent("played song", params); ``` ```kotlin val params = Bundle() params.putString("genre", "pop") params.putInt("number of times listened", 42); mFirebaseAnalytics.logEvent("played song", params) ``` ### Step 2: Log custom attributes Custom attributes are set via an `actionType` set to `customAttribute`. The Braze custom tag provider is expecting the custom attribute key-value to be set via `customAttributeKey` and `customAttributeValue`: ```java Bundle params = new Bundle(); params.putString("customAttributeKey", "favorite song"); params.putString("customAttributeValue", "Private Eyes"); mFirebaseAnalytics.logEvent("customAttribute", params); ``` ```kotlin val params = Bundle() params.putString("customAttributeKey", "favorite song") params.putString("customAttributeValue", "Private Eyes") mFirebaseAnalytics.logEvent("customAttribute", params) ``` ### Step 3: Call `changeUser()` Calls to `changeUser()` are made via an `actionType` set to `changeUser`. The Braze custom tag provider is expecting the Braze user ID to be set via an `externalUserId` key-value pair within your tag: ```java Bundle params = new Bundle(); params.putString("externalUserId", userId); mFirebaseAnalytics.logEvent("changeUser", params); ``` ```kotlin val params = Bundle() params.putString("externalUserId", userId) mFirebaseAnalytics.logEvent("changeUser", params) ``` ### Step 4: Add a custom tag provider {#adding-android-google-tag-provider} With the tags and triggers set up, you will also need to implement Google Tag Manager in your Android app which can be found in Google's [documentation](https://developers.google.com/tag-manager/android/v5/). After the Google Tag Manager is installed in your app, add a custom tag provider to call Braze SDK methods based on the tags you've configured within Google Tag Manager. Be sure to note the "Class Path" to the file - this is what you'll enter when setting up a Tag in the [Google Tag Manager](https://tagmanager.google.com/) console. This example highlights one of many ways you can structure your custom tag provider. Specifically, it shows how to determine which Braze SDK method to call based on the `actionType` key-value pair sent from the GTM Tag. The `actionType` shown in this example are `logEvent`, `customAttribute`, and `changeUser`, but you may prefer to change how your tag provider handles data from Google Tag Manager. ```java public class BrazeGtmTagProvider implements CustomTagProvider { private static final String TAG = BrazeLogger.getBrazeLogTag(BrazeGtmTagProvider.class); private static final String ACTION_TYPE_KEY = "actionType"; // Custom Events private static final String LOG_EVENT_ACTION_TYPE = "logEvent"; private static final String EVENT_NAME_VARIABLE = "eventName"; // Custom Attributes private static final String CUSTOM_ATTRIBUTE_ACTION_TYPE = "customAttribute"; private static final String CUSTOM_ATTRIBUTE_KEY = "customAttributeKey"; private static final String CUSTOM_ATTRIBUTE_VALUE_KEY = "customAttributeValue"; // Change User private static final String CHANGE_USER_ACTION_TYPE = "changeUser"; private static final String CHANGE_USER_ID_VARIABLE = "externalUserId"; private static Context sApplicationContext; /** * Must be set before calling any of the following methods * so that the proper application context is available when needed. * * Recommended to be called in your {@link Application#onCreate()}. */ public static void setApplicationContext(Context applicationContext) { if (applicationContext != null) { sApplicationContext = applicationContext.getApplicationContext(); } } @Override public void execute(Map map) { BrazeLogger.i(TAG, "Got google tag manager parameters map: " + map); if (sApplicationContext == null) { BrazeLogger.w(TAG, "No application context provided to this tag provider."); return; } if (!map.containsKey(ACTION_TYPE_KEY)) { BrazeLogger.w(TAG, "Map does not contain the Braze action type key: " + ACTION_TYPE_KEY); return; } String actionType = String.valueOf(map.remove(ACTION_TYPE_KEY)); switch (actionType) { case LOG_EVENT_ACTION_TYPE: logEvent(map); break; case CUSTOM_ATTRIBUTE_ACTION_TYPE: setCustomAttribute(map); break; case CHANGE_USER_ACTION_TYPE: changeUser(map); break; default: BrazeLogger.w(TAG, "Got unknown action type: " + actionType); break; } } private void logEvent(Map tagParameterMap) { String eventName = String.valueOf(tagParameterMap.remove(EVENT_NAME_VARIABLE)); Braze.getInstance(sApplicationContext).logCustomEvent(eventName, parseMapIntoProperties(tagParameterMap)); } private BrazeProperties parseMapIntoProperties(Map map) { BrazeProperties brazeProperties = new BrazeProperties(); for (Map.Entry entry : map.entrySet()) { final Object value = entry.getValue(); final String key = entry.getKey(); if (value instanceof Boolean) { brazeProperties.addProperty(key, (Boolean) value); } else if (value instanceof Integer) { brazeProperties.addProperty(key, (Integer) value); } else if (value instanceof Date) { brazeProperties.addProperty(key, (Date) value); } else if (value instanceof Long) { brazeProperties.addProperty(key, (Long) value); } else if (value instanceof String) { brazeProperties.addProperty(key, (String) value); } else if (value instanceof Double) { brazeProperties.addProperty(key, (Double) value); } else { BrazeLogger.w(TAG, "Failed to parse value into an BrazeProperties " + "accepted type. Key: '" + key + "' Value: '" + value + "'"); } } return brazeProperties; } private void setCustomAttribute(Map tagParameterMap) { String key = String.valueOf(tagParameterMap.get(CUSTOM_ATTRIBUTE_KEY)); Object value = tagParameterMap.get(CUSTOM_ATTRIBUTE_VALUE_KEY); Braze.getInstance(sApplicationContext).getCurrentUser(new IValueCallback() { @Override public void onSuccess(BrazeUser brazeUser) { if (value instanceof Boolean) { brazeUser.setCustomUserAttribute(key, (Boolean) value); } else if (value instanceof Integer) { brazeUser.setCustomUserAttribute(key, (Integer) value); } else if (value instanceof Long) { brazeUser.setCustomUserAttribute(key, (Long) value); } else if (value instanceof String) { brazeUser.setCustomUserAttribute(key, (String) value); } else if (value instanceof Double) { brazeUser.setCustomUserAttribute(key, (Double) value); } else if (value instanceof Float) { brazeUser.setCustomUserAttribute(key, (Float) value); } else { BrazeLogger.w(TAG, "Failed to parse value into a custom " + "attribute accepted type. Key: '" + key + "' Value: '" + value + "'"); } } }); } private void changeUser(Map tagParameterMap) { String userId = String.valueOf(tagParameterMap.get(CHANGE_USER_ID_VARIABLE)); Braze.getInstance(sApplicationContext).changeUser(userId); } } ``` ```kotlin class BrazeGtmTagProvider : CustomTagProvider { override fun execute(map: MutableMap) { BrazeLogger.i(TAG, "Got google tag manager parameters map: $map") if (sApplicationContext == null) { BrazeLogger.w(TAG, "No application context provided to this tag provider.") return } if (!map.containsKey(ACTION_TYPE_KEY)) { BrazeLogger.w(TAG, "Map does not contain the Braze action type key: $ACTION_TYPE_KEY") return } val actionType = map.remove(ACTION_TYPE_KEY).toString() when (actionType) { LOG_EVENT_ACTION_TYPE -> logEvent(map) CUSTOM_ATTRIBUTE_ACTION_TYPE -> setCustomAttribute(map) CHANGE_USER_ACTION_TYPE -> changeUser(map) else -> BrazeLogger.w(TAG, "Got unknown action type: $actionType") } } private fun logEvent(tagParameterMap: MutableMap) { val eventName = tagParameterMap.remove(EVENT_NAME_VARIABLE).toString() Braze.getInstance(sApplicationContext).logCustomEvent(eventName, parseMapIntoProperties(tagParameterMap)) } private fun parseMapIntoProperties(map: Map): BrazeProperties { val brazeProperties = BrazeProperties() map.forEach { param -> val key = param.key val value = param.value when (value) { is Boolean -> brazeProperties.addProperty(key, value) is Int -> brazeProperties.addProperty(key, value) is Date -> brazeProperties.addProperty(key, value) is Long -> brazeProperties.addProperty(key, value) is String -> brazeProperties.addProperty(key, value) is Double -> brazeProperties.addProperty(key, value) else -> BrazeLogger.w(TAG, "Failed to parse value into an BrazeProperties " + "accepted type. Key: '" + key + "' Value: '" + value + "'") } } return brazeProperties } private fun setCustomAttribute(tagParameterMap: Map) { val key = tagParameterMap[CUSTOM_ATTRIBUTE_KEY].toString() val value = tagParameterMap[CUSTOM_ATTRIBUTE_VALUE_KEY] Braze.getInstance(sApplicationContext).getCurrentUser { brazeUser -> when (value) { is Boolean -> brazeUser.setCustomUserAttribute(key, value) is Int -> brazeUser.setCustomUserAttribute(key, value) is Long -> brazeUser.setCustomUserAttribute(key, value) is String -> brazeUser.setCustomUserAttribute(key, value) is Double -> brazeUser.setCustomUserAttribute(key, value) is Float -> brazeUser.setCustomUserAttribute(key, value) else -> BrazeLogger.w( TAG, "Failed to parse value into a custom " + "attribute accepted type. Key: '" + key + "' Value: '" + value + "'" ) } } } private fun changeUser(tagParameterMap: Map) { val userId = tagParameterMap[CHANGE_USER_ID_VARIABLE].toString() Braze.getInstance(sApplicationContext).changeUser(userId) } companion object { private val TAG = BrazeLogger.getBrazeLogTag(BrazeGtmTagProvider::class.java) private val ACTION_TYPE_KEY = "actionType" // Custom Events private val LOG_EVENT_ACTION_TYPE = "logEvent" private val EVENT_NAME_VARIABLE = "eventName" // Custom Attributes private val CUSTOM_ATTRIBUTE_ACTION_TYPE = "customAttribute" private val CUSTOM_ATTRIBUTE_KEY = "customAttributeKey" private val CUSTOM_ATTRIBUTE_VALUE_KEY = "customAttributeValue" // Change User private val CHANGE_USER_ACTION_TYPE = "changeUser" private val CHANGE_USER_ID_VARIABLE = "externalUserId" private var sApplicationContext: Context? = null /** * Must be set before calling any of the following methods so * that the proper application context is available when needed. * * Recommended to be called in your [Application.onCreate]. */ fun setApplicationContext(applicationContext: Context?) { if (applicationContext != null) { sApplicationContext = applicationContext.applicationContext } } } } ``` In your `Application.onCreate()` be sure to add the following initialization for the previous snippet: ```java BrazeGtmTagProvider.setApplicationContext(this.getApplicationContext()); ``` ```kotlin BrazeGtmTagProvider.setApplicationContext(this.applicationContext) ``` ## Prerequisites Before you can use this feature, you'll need to [integrate the Swift Braze SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=swift). ## Using Google Tag Manager for Swift In the following example, a music streaming app wants to log different events as users listen to songs. Using Google Tag Manager for iOS, they can control which of the Braze third-party vendors receive this event and create tags specific to Braze. ### Step 1: Create a trigger for custom events Custom events are logged with `actionType` set to `logEvent`. In this example, the Braze custom tag provider is expecting the custom event name to be set using `eventName`. First, create a trigger that looks for an `eventName` that equals `played song`. ![A custom trigger in Google Tag Manager set to trigger for some events when "eventName" equals "played song".](https://www.braze.com/docs/ko/ko/assets/img/android_google_tag_manager/gtm_android_trigger.png?ce7d5cd1e1ab6a285076d8429ac796bd) Next, create a new Tag (also known as a "Function Call") and enter the class path of your [custom tag provider](#adding-ios-google-tag-provider) described later in this article. This tag will be triggered when you log the `played song` event. Because `eventName` is set to `played song` it will be used as custom event name that's logged to Braze. **Important:** When sending a custom event, set `actionType` to `logEvent`, and set a value for `eventName` so Braze receives the correct event name and action to take. ![A tag in Google Tag Manager with classpath and key-value pair fields. This tag is set to trigger with the previously created "played song" trigger.](https://www.braze.com/docs/ko/ko/assets/img/android_google_tag_manager/gtm_android_function_call_tag.png?40fad5b2a61b7d2183f635a10e290252) You can also include additional key-value pair arguments to the tag, which will be sent as custom event properties to Braze. `eventName` and `actionType` will not be ignored for custom event properties. In the following example tag, pass in `genre`, which was defined using a tag variable in Google Tag Manager and sourced from the custom event logged in the app. The `genre` event property is sent to Google Tag Manager as a "Firebase - Event Parameter" variable since Google Tag Manager for iOS uses Firebase as the data layer. ![A variable in Google Tag Manager where "genre" is added as an event parameter for the "Braze - Played Song Event" tag.](https://www.braze.com/docs/ko/ko/assets/img/android_google_tag_manager/gtm_android_eventname_variable.png?abff82f38b65ae64ad0ae3842d2ea439) When a user plays a song in the app, log an event through Firebase and Google Tag Manager using the Firebase analytics event name that matches the tag's trigger name, `played song`: ```swift let parameters: [String: Any] = ["genre": "pop", "number of times listened": 42] Analytics.logEvent("played song", parameters: parameters) ``` ```obj-c NSDictionary *parameters = @{@"genre" : @"pop", @"number of times listened" : @42}; [FIRAnalytics logEventWithName:@"played song" parameters:parameters]; ``` ### Step 2: Log custom attributes Custom attributes are set via an `actionType` set to `customAttribute`. The Braze custom tag provider is expecting the custom attribute key-value to be set via `customAttributeKey` and `customAttributeValue`: ```swift let parameters: [String: Any] = ["customAttributeKey": "favoriteSong", "customAttributeValue": "Private Eyes"] FIRAnalytics.logEvent(withName:"customAttribute", parameters: parameters) ``` ```obj-c NSDictionary *parameters = @{@"customAttributeKey" : @"favoriteSong", @"customAttributeValue" : @"Private Eyes"}; [FIRAnalytics logEventWithName:@"customAttribute" parameters:parameters]; ``` ### Step 3: Call `changeUser()` Calls to `changeUser()` are made via an `actionType` set to `changeUser`. The Braze custom tag provider is expecting the Braze user ID to be set via an `externalUserId` key-value pair within your tag: ```swift let parameters: [String: Any] = ["externalUserId": "favorite userId"] Analytics.logEvent(withName:"changeUser", parameters: parameters) ``` ```obj-c NSDictionary *parameters = @{@"externalUserId" : userId}; [FIRAnalytics logEventWithName:@"changeUser" parameters:parameters]; ``` ### Step 4: Add a custom tag provider {#adding-ios-google-tag-provider} With the tags and triggers set up, you will also need to implement Google Tag Manager in your iOS app which can be found in Google's [documentation](https://developers.google.com/tag-manager/ios/v5/). After Google Tag Manager is installed in your app, add a custom tag provider to call Braze SDK methods based on the tags you've configured within Google Tag Manager. Be sure to note the "Class Path" to the file - this is what you'll enter when setting up a tag in the [Google Tag Manager](https://tagmanager.google.com/) console. This example highlights one of many ways you can structure your custom tag provider. Specifically, it shows how to determine which Braze SDK method to call based on the `actionType` key-value pair sent from the GTM Tag. This example assumes you've assigned the Braze instance as a variable in the AppDelegate. The `actionType` supported in this example are `logEvent`, `customAttribute`, and `changeUser`, but you may prefer to change how your tag provider handles data from Google Tag Manager. Add the following code to your `BrazeGTMTagManager.swift` file. ```swift import FirebaseAnalytics import GoogleTagManager import BrazeKit let ActionTypeKey: String = "actionType" // Custom Events let LogEventAction: String = "logEvent" let LogEventName: String = "eventName" // Custom Attributes let CustomAttributeAction: String = "customAttribute" let CustomAttributeKey: String = "customAttributeKey" let CustomAttributeValueKey: String = "customAttributeValue" // Change User let ChangeUserAction: String = "changeUser" let ChangeUserExternalUserId: String = "externalUserId" @objc(BrazeGTMTagManager) final class BrazeGTMTagManager : NSObject, TAGCustomFunction { @objc func execute(withParameters parameters: [AnyHashable : Any]!) -> NSObject! { var parameters: [String : Any] = parameters as! [String : Any] guard let actionType: String = parameters[ActionTypeKey] as? String else { print("There is no Braze action type key in this call. Doing nothing.") return nil } parameters.removeValue(forKey: ActionTypeKey) if actionType == LogEventAction { logEvent(parameters: parameters) } else if actionType == CustomAttributeAction { logCustomAttribute(parameters: parameters) } else if actionType == ChangeUserAction { changeUser(parameters: parameters) } return nil } func logEvent(parameters: [String : Any]) { var parameters: [String : Any] = parameters guard let eventName: String = parameters[LogEventName] as? String else { return } parameters.removeValue(forKey: LogEventName) AppDelegate.braze?.logCustomEvent(name: eventName, properties: parameters) } func logCustomAttribute(parameters: [String: Any]) { guard let customAttributeKey = parameters[CustomAttributeKey] as? String else { return } let customAttributeValue = parameters[CustomAttributeValueKey] if let customAttributeValue = customAttributeValue as? String { AppDelegate.braze?.user.setCustomAttribute(key: customAttributeKey, value: customAttributeValue) } else if let customAttributeValue = customAttributeValue as? Date { AppDelegate.braze?.user.setCustomAttribute(key: customAttributeKey, value: customAttributeValue) } else if let customAttributeValue = customAttributeValue as? Double { AppDelegate.braze?.user.setCustomAttribute(key: customAttributeKey, value: customAttributeValue) } else if let customAttributeValue = customAttributeValue as? Bool { AppDelegate.braze?.user.setCustomAttribute(key: customAttributeKey, value: customAttributeValue) } else if let customAttributeValue = customAttributeValue as? Int { AppDelegate.braze?.user.setCustomAttribute(key: customAttributeKey, value: customAttributeValue) } else if let customAttibuteValue = customAttributeValue as? [String] { AppDelegate.braze?.user.setCustomAttributeArray(key: customAttributeKey, array: customAttibuteValue) } } func changeUser(parameters: [String: Any]) { guard let userId = parameters[ChangeUserExternalUserId] as? String else { return } AppDelegate.braze?.changeUser(userId: userId) } } ``` Add the following code to your `BrazeGTMTagManager.h` file: ```obj-c @import Firebase; @import GoogleTagManager; @interface BrazeGTMTagManager : NSObject @end ``` And add the following code to your `BrazeGTMTagManager.m` file: ```obj-c #import #import "BrazeGTMTagManager.h" #import "BrazeKit" #import "AppDelegate.h" static NSString *const ActionTypeKey = @"actionType"; // Custom Events static NSString *const LogEventAction = @"logEvent"; static NSString *const LogEventEventName = @"eventName"; // Custom Attributes static NSString *const CustomAttributeAction = @"customAttribute"; static NSString *const CustomAttributeKey = @"customAttributeKey"; static NSString *const CustomAttributeValueKey = @"customAttributeValue"; // Change User static NSString *const ChangeUserAction = @"changeUser"; static NSString *const ChangeUserExternalUserId = @"externalUserId"; @implementation BrazeGTMTagManager - (NSObject *)executeWithParameters:(NSDictionary *)parameters { NSMutableDictionary *mutableParameters = [parameters mutableCopy]; NSString *actionType = mutableParameters[ActionTypeKey]; if (!actionType) { NSLog(@"There is no Braze action type key in this call. Doing nothing.", nil); return nil; } [mutableParameters removeObjectForKey:ActionTypeKey]; if ([actionType isEqualToString:LogEventAction]) { [self logEvent:mutableParameters]; } else if ([actionType isEqualToString:CustomAttributeAction]) { [self logCustomAttribute:mutableParameters]; } else if ([actionType isEqualToString:ChangeUserAction]) { [self changeUser:mutableParameters]; } else { NSLog(@"Invalid action type. Doing nothing."); } return nil; } - (void)logEvent:(NSMutableDictionary *)parameters { NSString *eventName = parameters[LogEventEventName]; [parameters removeObjectForKey:LogEventEventName]; [AppDelegate.braze logCustomEvent:eventName properties:parameters]; } - (void)logCustomAttribute:(NSMutableDictionary *)parameters { NSString *customAttributeKey = parameters[CustomAttributeKey]; id customAttributeValue = parameters[CustomAttributeValueKey]; if ([customAttributeValue isKindOfClass:[NSString class]]) { [AppDelegate.braze logCustomEvent:customAttributeKey properties:parameters]; } else if ([customAttributeValue isKindOfClass:[NSDate class]]) { [AppDelegate.braze.user setCustomAttributeWithKey:customAttributeKey dateValue:customAttributeValue]; } else if ([customAttributeValue isKindOfClass:[NSNumber class]]) { if (strcmp([customAttributeValue objCType], [@(YES) objCType]) == 0) { [AppDelegate.braze.user setCustomAttributeWithKey:customAttributeKey boolValue:[(NSNumber *)customAttributeValue boolValue]]; } else if (strcmp([customAttributeValue objCType], @encode(short)) == 0 || strcmp([customAttributeValue objCType], @encode(int)) == 0 || strcmp([customAttributeValue objCType], @encode(long)) == 0) { [AppDelegate.braze.user setCustomAttributeWithKey:customAttributeKey intValue:[(NSNumber *)customAttributeValue integerValue]]; } else if (strcmp([customAttributeValue objCType], @encode(float)) == 0 || strcmp([customAttributeValue objCType], @encode(double)) == 0) { [AppDelegate.braze.user setCustomAttributeWithKey:customAttributeKey doubleValue:[(NSNumber *)customAttributeValue doubleValue]]; } else { NSLog(@"Could not map NSNumber value to Braze custom attribute:%@", customAttributeValue); } } else if ([customAttributeValue isKindOfClass:[NSArray class]]) { [AppDelegate.braze.user setCustomAttributeArrayWithKey:customAttributeKey array:customAttributeValue]; } } - (void)changeUser:(NSMutableDictionary *)parameters { NSString *userId = parameters[ChangeUserExternalUserId]; [AppDelegate.braze changeUser:userId]; } @end ``` ## 문제 해결 {#troubleshooting} Braze가 초기화되지 않거나 이벤트가 예상대로 표시되지 않는 경우, GTM 컨테이너가 게시되었는지, 트리거 및 태그 실행 순서가 SDK [라이프사이클 및 초기화 전략](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/)과 일치하는지, 테스트 기기가 Braze 엔드포인트를 차단하고 있지 않은지 확인하세요. 초기화 실패의 경우, Braze 태그 또는 커스텀 태그 제공업체가 예상되는 `actionType` 및 파라미터를 수신하는지 확인하세요(이 페이지의 Android, Swift, 웹 탭 참조). GTM에서 실행된 이벤트를 검증하는 동안 상세 로깅을 활성화하려면 해당 탭에서 링크된 플랫폼 통합 가이드에 설명된 대로 플랫폼의 SDK 디버그 로깅을 활성화하세요. # Braze SDK 인증 설정 Source: /docs/ko/developer_guide/sdk_integration/authentication/index.md # SDK 인증 설정 {#set-up-sdk-authentication} > SDK 인증을 통해 로그인한 사용자를 대신하여 SDK 요청에 암호화 증명(서버 측에서 생성됨)을 제공할 수 있습니다. ## 작동 방식 {#how-it-works} 앱에서 이 기능을 활성화한 후에는 유효하지 않거나 누락된 JSON 웹 토큰(JWT)이 포함된 모든 요청을 거부하도록 Braze 대시보드를 구성할 수 있습니다. 여기에는 다음이 포함됩니다: - 커스텀 이벤트, 속성, 구매 및 세션 데이터 전송 - Braze 워크스페이스에서 새 사용자 만들기 - 표준 고객 프로필 속성 업데이트 - 메시지 수신 또는 트리거 이제 인증되지 않은 로그인 사용자가 앱의 SDK API 키를 사용하여 다른 사용자를 사칭하는 등의 악의적인 행동을 수행하는 것을 방지할 수 있습니다. ## 인증 설정 {#setting-up-authentication} ### 1단계: 서버 설정 {#server-side-integration} #### 1.1단계: 공개/비공개 키 쌍 생성 {#generate-keys} RSA256 공개/비공개 키 쌍을 생성합니다. 공개 키는 최종적으로 Braze 대시보드에 추가되며, 비공개 키는 서버에 안전하게 저장해야 합니다. RS256 JWT 알고리즘과 함께 사용할 2048비트의 RSA 키를 권장합니다. **Warning:** 비공개 키는 반드시 _비공개_로 유지해야 합니다. 절대 비공개 키를 앱이나 웹사이트에 노출하거나 하드코딩하지 마십시오. 비공개 키를 아는 사람은 누구나 애플리케이션을 대신하여 사용자를 사칭하거나 생성할 수 있습니다. #### 1.2단계: 현재 사용자를 위한 JSON 웹 토큰 생성 {#create-jwt} 비공개 키를 확보한 후, 서버 측 애플리케이션은 현재 로그인한 사용자에게 앱 또는 웹사이트로 JWT를 반환해야 합니다. 일반적으로 이 로직은 앱이 현재 고객 프로필을 요청하는 곳 어디에서나 사용할 수 있습니다. 예를 들어 로그인 엔드포인트나 앱이 현재 고객 프로필을 새로고침하는 곳이 될 수 있습니다. JWT를 생성할 때 다음 필드가 필요합니다: **JWT 헤더** | 필드 | 필수 | 설명 | | ----- | -------- | ----------------------------------- | | `alg` | 예 | 지원되는 알고리즘은 `RS256`입니다. | | `typ` | 예 | 유형은 `JWT`와 같아야 합니다. | {: .reset-td-br-1 .reset-td-br-2 .reset-td-br-3 aria-label="1.2단계: 현재 사용자를 위한 JSON 웹 토큰 생성" } {: .reset-td-br-1 .reset-td-br-2 .reset-td-br-3 aria-label="1.2단계: 현재 사용자를 위한 JSON 웹 토큰 생성 #create-jwt" } **JWT 페이로드** | 필드 | 필수 | 설명 | | ----- | -------- | -------------------------------------------------------------------------------------- | | `sub` | 예 | "subject"는 `changeUser`를 호출할 때 Braze SDK에 제공하는 사용자 ID와 같아야 합니다 | | `exp` | 예 | "expiration"은 이 토큰이 만료되는 시점으로, Unix 타임스탬프(초 단위)입니다(예: 2030년 1월 1일의 경우 `1893456000`). | {: .reset-td-br-1 .reset-td-br-2 .reset-td-br-3 aria-label="1.2단계: 현재 사용자를 위한 JSON 웹 토큰 생성" } {: .reset-td-br-1 .reset-td-br-2 .reset-td-br-3 aria-label="1.2단계: 현재 사용자를 위한 JSON 웹 토큰 생성 #create-jwt" } **Tip:** JSON 웹 토큰에 대해 더 알아보거나 이 서명 프로세스를 단순화하는 많은 오픈 소스 라이브러리를 둘러보려면 [https://jwt.io](https://jwt.io)를 확인하세요. ### 2단계: SDK 구성 {#sdk-integration} 이 기능은 다음 [SDK 버전](https://www.braze.com/docs/ko/ko/user_guide/engagement_tools/campaigns/ideas_and_strategies/new_features/#filtering-by-most-recent-app-versions)부터 사용할 수 있습니다: **Note:** iOS 통합의 경우, 이 페이지에서는 Braze Swift SDK에 대한 단계를 설명합니다. 레거시 AppboyKit iOS SDK에서의 샘플 사용법은 [이 파일](https://github.com/Appboy/appboy-ios-sdk/blob/master/Example/Stopwatch/Sources/AppDelegate.m) 및 [이 파일](https://github.com/Appboy/appboy-ios-sdk/blob/master/Example/Stopwatch/Sources/Utils/SdkAuthDelegate.m)을 참조하십시오. #### 2.1단계: Braze SDK에서 인증 활성화 {#step-21-enable-authentication-in-the-braze-sdk} 이 기능이 활성화되면 Braze SDK는 현재 사용자의 마지막으로 알려진 JWT를 Braze 서버에 대한 네트워크 요청에 추가합니다. **Note:** 걱정하지 마세요. 이 옵션만으로 초기화하는 것은 데이터 수집에 전혀 영향을 미치지 않으며, Braze 대시보드 내에서 [인증을 적용](#braze-dashboard)하기 전까지는 영향이 없습니다. `initialize` 호출 시 선택적 `enableSdkAuthentication` 속성을 `true`로 설정합니다. ```javascript import * as braze from "@braze/web-sdk"; braze.initialize("YOUR-API-KEY-HERE", { baseUrl: "YOUR-SDK-ENDPOINT-HERE", enableSdkAuthentication: true, }); ``` 네이티브 SDK 초기화 시 SDK 인증을 반드시 활성화해야 합니다. 다음 구성을 네이티브 iOS 및 Android 코드에 추가하십시오: **iOS (AppDelegate.swift)** ```swift import BrazeKit import braze_react_native_sdk let configuration = Braze.Configuration( apiKey: "{YOUR-BRAZE-API-KEY}", endpoint: "{YOUR-BRAZE-ENDPOINT}" ) configuration.api.sdkAuthentication = true let braze = BrazeReactBridge.perform( #selector(BrazeReactBridge.initBraze(_:)), with: configuration ).takeUnretainedValue() as! Braze ``` **Android (braze.xml)** ```xml true ``` 네이티브 레이어에서 SDK 인증을 활성화한 후, 다음 단계에 표시된 React Native JavaScript 메서드를 사용할 수 있습니다. Braze 인스턴스를 구성할 때 `setIsSdkAuthenticationEnabled`를 `true`로 호출합니다. ```java BrazeConfig.Builder brazeConfigBuilder = new BrazeConfig.Builder() .setIsSdkAuthenticationEnabled(true); Braze.configure(this, brazeConfigBuilder.build()); ``` 또는 `true`을 braze.xml에 추가할 수 있습니다. Braze 인스턴스를 구성할 때 `setIsSdkAuthenticationEnabled`를 `true`로 호출합니다. ```kotlin BrazeConfig.Builder brazeConfigBuilder = BrazeConfig.Builder() .setIsSdkAuthenticationEnabled(true) Braze.configure(this, brazeConfigBuilder.build()) ``` 또는 `true`을 braze.xml에 추가할 수 있습니다. SDK 인증을 활성화하려면 `BRZConfiguration` 오브젝트의 `configuration.api.sdkAuthentication` 속성을 Braze 인스턴스를 초기화하기 전에 `YES`로 설정합니다: ```objc BRZConfiguration *configuration = [[BRZConfiguration alloc] initWithApiKey:@"{BRAZE_API_KEY}" endpoint:@"{BRAZE_ENDPOINT}"]; configuration.api.sdkAuthentication = YES; Braze *braze = [[Braze alloc] initWithConfiguration:configuration]; AppDelegate.braze = braze; ``` SDK 인증을 활성화하려면 SDK를 초기화할 때 `Braze.Configuration` 오브젝트의 `configuration.api.sdkAuthentication` 속성을 `true`로 설정합니다: ```swift let configuration = Braze.Configuration(apiKey: "{YOUR-BRAZE-API-KEY}", endpoint: "{YOUR-BRAZE-ENDPOINT}") configuration.api.sdkAuthentication = true let braze = Braze(configuration: configuration) AppDelegate.braze = braze ``` 현재 SDK 인증은 네이티브 iOS 및 Android 코드에서 SDK를 초기화하는 과정의 일부로 활성화해야 합니다. Flutter SDK에서 SDK 인증을 활성화하려면 다른 탭의 iOS 및 Android 통합을 따르세요. SDK 인증이 활성화된 후 나머지 기능은 Dart에서 통합할 수 있습니다. SDK 인증은 네이티브 iOS 및 Android 코드에서 SDK 초기화의 일부로 반드시 활성화해야 합니다. 네이티브 레이어에서 활성화된 경우 Flutter SDK 메서드를 사용하여 JWT 서명을 전달할 수 있습니다. **iOS** SDK 인증을 활성화하려면 네이티브 iOS 코드에서 `configuration.api.sdkAuthentication` 속성을 `true`로 설정하십시오: ```swift let configuration = Braze.Configuration(apiKey: "{YOUR-BRAZE-API-KEY}", endpoint: "{YOUR-BRAZE-ENDPOINT}") configuration.api.sdkAuthentication = true let braze = Braze(configuration: configuration) ``` **Android (braze.xml)** ```xml true ``` 네이티브 레이어에서 SDK 인증을 활성화한 후, 다음 단계에 표시된 Flutter SDK 메서드를 사용할 수 있습니다. 네이티브 SDK 초기화 시 SDK 인증을 반드시 활성화해야 합니다. 다음 구성을 네이티브 iOS 및 Android 코드에 추가하십시오: **iOS** 구성 파일에서 `SDKAuthenticationEnabled` 속성을 `true`로 설정하십시오: ```xml SDKAuthenticationEnabled ``` **Android (braze.xml)** ```xml true ``` 네이티브 레이어에서 SDK 인증을 활성화한 후, 다음 단계에 표시된 Unity C# 메서드를 사용할 수 있습니다. 네이티브 SDK 초기화 시 SDK 인증을 반드시 활성화해야 합니다. 다음 구성을 네이티브 iOS 및 Android 코드에 추가하십시오: **iOS** SDK 인증을 활성화하려면 `config.xml`에서 `enableSDKAuthentication` 속성을 `true`로 설정하십시오: ```xml ``` **Android (braze.xml)** ```xml true ``` 네이티브 레이어에서 SDK 인증을 활성화한 후, 다음 단계에 표시된 Cordova JavaScript 메서드를 사용할 수 있습니다. 네이티브 SDK 초기화 시 SDK 인증을 반드시 활성화해야 합니다. iOS와 Android용 SDK 인증을 별도로 구성하십시오: **iOS** SDK 인증을 활성화하려면 SDK 초기화 시 `configuration.Api.SdkAuthentication` 속성을 `true`로 설정하십시오: ```csharp var configuration = new BRZConfiguration("YOUR-API-KEY", "YOUR-ENDPOINT"); configuration.Api.SdkAuthentication = true; var braze = new Braze(configuration); ``` **Android (braze.xml)** ```xml true ``` SDK 인증을 활성화한 후에는 다음 단계에 표시된 .NET MAUI 메서드를 사용할 수 있습니다. Braze Expo 플러그인을 사용할 때는 앱 구성에서 `enableSdkAuthentication` 속성을 `true`로 설정하십시오. 이렇게 하면 수동으로 네이티브 코드를 변경하지 않아도 네이티브 iOS 및 Android 레이어에서 SDK 인증이 자동으로 구성됩니다. **app.json 또는 app.config.js** ```json { "expo": { "plugins": [ [ "@braze/expo-plugin", { "enableSdkAuthentication": true } ] ] } } ``` 앱 구성에서 SDK 인증을 활성화한 후, React Native 탭에 표시된 React Native JavaScript 메서드를 사용하여 다음 단계를 수행할 수 있습니다. **Note:** 완전한 구현 예제는 GitHub의 [Braze Expo 플러그인 샘플 앱](https://github.com/braze-inc/braze-expo-plugin/blob/main/example/components/Braze.tsx)을 참조하십시오. #### 2.2단계: 현재 사용자의 JWT 설정 {#step-22-set-the-current-users-jwt} 앱에서 Braze `changeUser` 메서드를 호출할 때마다 [서버 측에서 생성된](#braze-dashboard) JWT도 함께 제공하세요. 현재 사용자의 세션 중간에 토큰을 새로고침하도록 구성할 수도 있습니다. **Note:** `changeUser`는 사용자 ID가 _실제로 변경_된 경우에만 호출해야 합니다. 사용자 ID가 변경되지 않은 경우 이 메서드를 인증 토큰(JWT)을 업데이트하는 용도로 사용해서는 안 됩니다. [`changeUser`](https://js.appboycdn.com/web-sdk/latest/doc/modules/braze.html#changeuser) 호출 시 JWT를 제공하세요: ```javascript import * as braze from "@braze/web-sdk"; braze.changeUser("NEW-USER-ID", "JWT-FROM-SERVER"); ``` 또는 세션 중간에 사용자의 토큰을 새로고침한 경우: ```javascript import * as braze from "@braze/web-sdk"; braze.setSdkAuthenticationSignature("NEW-JWT-FROM-SERVER"); ``` [`changeUser`](https://braze-inc.github.io/braze-react-native-sdk/classes/Braze.Braze-1.html#changeUser) 호출 시 JWT를 제공하세요: ```typescript import Braze from '@braze/react-native-sdk'; Braze.changeUser("NEW-USER-ID", "JWT-FROM-SERVER"); ``` 또는 세션 중간에 사용자의 토큰을 새로고침한 경우: ```typescript import Braze from '@braze/react-native-sdk'; Braze.setSdkAuthenticationSignature("NEW-JWT-FROM-SERVER"); ``` [`changeUser`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze/-i-braze/change-user.html) 호출 시 JWT를 제공하세요: ```java Braze.getInstance(this).changeUser("NEW-USER-ID", "JWT-FROM-SERVER"); ``` 또는 세션 중간에 사용자의 토큰을 새로고침한 경우: ```java Braze.getInstance(this).setSdkAuthenticationSignature("NEW-JWT-FROM-SERVER"); ``` [`changeUser`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze/-i-braze/change-user.html) 호출 시 JWT를 제공하세요: ```kotlin Braze.getInstance(this).changeUser("NEW-USER-ID", "JWT-FROM-SERVER") ``` 또는 세션 중간에 사용자의 토큰을 새로고침한 경우: ```kotlin Braze.getInstance(this).setSdkAuthenticationSignature("NEW-JWT-FROM-SERVER") ``` [`changeUser`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/changeuser(userid:sdkauthsignature:fileid:line:)) 호출 시 JWT를 제공하세요: ```objc [AppDelegate.braze changeUser:@"userId" sdkAuthSignature:@"JWT-FROM-SERVER"]; ``` 또는 세션 중간에 사용자의 토큰을 새로고침한 경우: ```objc [AppDelegate.braze setSDKAuthenticationSignature:@"NEW-JWT-FROM-SERVER"]; ``` [`changeUser`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/changeuser(userid:sdkauthsignature:fileid:line:)) 호출 시 JWT를 제공하세요: ```swift AppDelegate.braze?.changeUser(userId: "userId", sdkAuthSignature: "JWT-FROM-SERVER") ``` 또는 세션 중간에 사용자의 토큰을 새로고침한 경우: ```swift AppDelegate.braze?.set(sdkAuthenticationSignature: "NEW-JWT-FROM-SERVER") ``` [`changeUser`](https://js.appboycdn.com/web-sdk/latest/doc/modules/braze.html#changeuser) 호출 시 JWT를 제공하세요: ```dart braze.changeUser("userId", sdkAuthSignature: "JWT-FROM-SERVER") ``` 또는 세션 중간에 사용자의 토큰을 새로고침한 경우: ```dart braze.setSdkAuthenticationSignature("NEW-JWT-FROM-SERVER") ``` `changeUser` 호출 시 JWT를 제공하십시오: ```dart import 'package:braze_plugin/braze_plugin.dart'; BrazePlugin braze = BrazePlugin(); braze.changeUser("NEW-USER-ID", sdkAuthSignature: "JWT-FROM-SERVER"); ``` 또는 세션 중간에 사용자의 토큰을 새로고침한 경우: ```dart import 'package:braze_plugin/braze_plugin.dart'; BrazePlugin braze = BrazePlugin(); braze.setSdkAuthenticationSignature("NEW-JWT-FROM-SERVER"); ``` `ChangeUser` 호출 시 JWT를 제공하십시오: ```csharp BrazeBinding.ChangeUser("NEW-USER-ID", "JWT-FROM-SERVER"); ``` 또는 세션 중간에 사용자의 토큰을 새로고침한 경우: ```csharp BrazeBinding.SetSdkAuthenticationSignature("NEW-JWT-FROM-SERVER"); ``` `changeUser` 호출 시 JWT를 제공하십시오: ```javascript BrazePlugin.changeUser("NEW-USER-ID", "JWT-FROM-SERVER"); ``` 또는 세션 중간에 사용자의 토큰을 새로고침한 경우: ```javascript BrazePlugin.setSdkAuthenticationSignature("NEW-JWT-FROM-SERVER"); ``` `ChangeUser` 호출 시 JWT를 제공하십시오: **iOS** ```csharp Braze.SharedInstance?.ChangeUser("NEW-USER-ID", "JWT-FROM-SERVER"); ``` 또는 세션 중간에 사용자의 토큰을 새로고침한 경우: ```csharp Braze.SharedInstance?.SetSDKAuthenticationSignature("NEW-JWT-FROM-SERVER"); ``` **Android** ```csharp Braze.GetInstance(this).ChangeUser("NEW-USER-ID", "JWT-FROM-SERVER"); ``` 또는 세션 중간에 사용자의 토큰을 새로고침한 경우: ```csharp Braze.GetInstance(this).SetSdkAuthenticationSignature("NEW-JWT-FROM-SERVER"); ``` Braze Expo 플러그인을 사용할 때는 동일한 React Native SDK 메서드를 사용하십시오. `changeUser` 호출 시 JWT를 제공하십시오: ```typescript import Braze from '@braze/react-native-sdk'; Braze.changeUser("NEW-USER-ID", "JWT-FROM-SERVER"); ``` 또는 세션 중간에 사용자의 토큰을 새로고침한 경우: ```typescript import Braze from '@braze/react-native-sdk'; Braze.setSdkAuthenticationSignature("NEW-JWT-FROM-SERVER"); ``` #### 2.3단계: 잘못된 토큰에 대한 콜백 함수 등록 {#sdk-callback} 이 기능이 [필수](#enforcement-options)로 설정된 경우 다음 시나리오에서는 SDK 요청이 Braze에 의해 거부됩니다: - JWT가 Braze API에 도착했을 때 이미 만료된 경우 - JWT가 비어 있거나 누락된 경우 - JWT가 Braze 대시보드에 업로드한 공개 키에 대해 검증에 실패한 경우 `subscribeToSdkAuthenticationFailures`를 사용하여 SDK 요청이 이러한 이유 중 하나로 실패할 때 알림을 받도록 구독할 수 있습니다. 콜백 함수는 관련 [`errorCode`](#error-codes), 오류 `reason`, 요청의 `userId`(사용자는 익명일 수 없음), 그리고 오류를 유발한 인증 토큰(JWT)을 포함하는 오브젝트를 전달합니다. 실패한 요청은 앱이 새로운 유효한 JWT를 제공할 때까지 주기적으로 재시도됩니다. 해당 사용자가 여전히 로그인되어 있는 경우, 이 콜백을 사용하여 서버에서 새 JWT를 요청하고 Braze SDK에 이 새 유효 토큰을 제공할 수 있습니다. 인증 오류가 발생하면 오류의 `userId`가 현재 로그인한 사용자와 일치하는지 확인한 후, 서버에서 새 서명을 가져와 Braze SDK에 제공하십시오. 이러한 오류를 모니터링 또는 오류 보고 서비스에 기록할 수도 있습니다. **Tip:** 이 콜백 메서드는 Braze 요청이 얼마나 자주 거부되는지 추적하기 위해 자체 모니터링 또는 오류 로깅 서비스를 추가하기에 좋은 위치입니다. ```javascript import * as braze from "@braze/web-sdk"; braze.subscribeToSdkAuthenticationFailures((error) => { console.error("SDK authentication failed:", error); console.log("Error code:", error.errorCode); console.log("User ID:", error.userId); // Note: Do not log error.signature as it contains sensitive authentication credentials // Verify the error.userId matches the currently logged-in user // Fetch a new token from your server and set it fetchNewSignature(error.userId).then((newSignature) => { braze.setSdkAuthenticationSignature(newSignature); }); }); ``` ```typescript import Braze from '@braze/react-native-sdk'; const sdkAuthErrorSubscription = Braze.addListener( Braze.Events.SDK_AUTHENTICATION_ERROR, (error) => { console.log(`SDK Authentication for ${error.userId} failed with error code ${error.errorCode}.`); const updated_jwt = getNewTokenSomehow(error); Braze.setSdkAuthenticationSignature(updated_jwt); } ); // Don't forget to remove the listener when done // sdkAuthErrorSubscription.remove(); ``` ```java Braze.getInstance(this).subscribeToSdkAuthenticationFailures(error -> { String newToken = getNewTokenSomehow(error); Braze.getInstance(getContext()).setSdkAuthenticationSignature(newToken); }); ``` ```kotlin Braze.getInstance(this).subscribeToSdkAuthenticationFailures({ error: BrazeSdkAuthenticationErrorEvent -> val newToken: String = getNewTokenSomehow(error) Braze.getInstance(getContext()).setSdkAuthenticationSignature(newToken) }) ``` ```objc Braze *braze = [[Braze alloc] initWithConfiguration:configuration]; braze.sdkAuthDelegate = delegate; AppDelegate.braze = braze; // Method to implement in delegate - (void)braze:(Braze *)braze sdkAuthenticationFailedWithError:(BRZSDKAuthenticationError *)error { NSLog(@"Invalid SDK Authentication Token."); NSString *newSignature = getNewTokenSomehow(error); [AppDelegate.braze setSDKAuthenticationSignature:newSignature]; } ``` ```swift let braze = Braze(configuration: configuration) braze.sdkAuthDelegate = delegate AppDelegate.braze = braze // Method to implement in delegate func braze(_ braze: Braze, sdkAuthenticationFailedWithError error: Braze.SDKAuthenticationError) { print("Invalid SDK Authentication Token.") let newSignature = getNewTokenSomehow(error) AppDelegate.braze?.set(sdkAuthenticationSignature: newSignature) } ``` ```dart braze.setBrazeSdkAuthenticationErrorCallback((BrazeSdkAuthenticationError error) async { print("Invalid SDK Authentication Token."); final newSignature = getNewTokenSomehow(error); braze.setSdkAuthenticationSignature(newSignature); }); ``` ```dart import 'package:braze_plugin/braze_plugin.dart'; BrazePlugin braze = BrazePlugin(); braze.setBrazeSdkAuthenticationErrorCallback((BrazeSdkAuthenticationError error) async { print("SDK Authentication for ${error.userId} failed with error code ${error.errorCode}."); String newSignature = getNewTokenSomehow(error); braze.setSdkAuthenticationSignature(newSignature); }); ``` **iOS** 네이티브 iOS 구현에서 SDK 인증 델리게이트를 설정하십시오: ```csharp public class SdkAuthDelegate : BRZSdkAuthDelegate { public void Braze(Braze braze, BRZSDKAuthenticationError error) { Debug.Log("Invalid SDK Authentication Token."); string newSignature = GetNewTokenSomehow(error); BrazeBinding.SetSdkAuthenticationSignature(newSignature); } } ``` **Android** ```csharp Braze.GetInstance(this).SubscribeToSdkAuthenticationFailures((error) => { string newToken = GetNewTokenSomehow(error); Braze.GetInstance(this).SetSdkAuthenticationSignature(newToken); }); ``` ```javascript BrazePlugin.subscribeToSdkAuthenticationFailures((error) => { console.log(`SDK Authentication for ${error.user_id} failed with error code ${error.error_code}.`); const newSignature = getNewTokenSomehow(error); BrazePlugin.setSdkAuthenticationSignature(newSignature); }); ``` **iOS** SDK 인증 델리게이트를 `Braze` 인스턴스에 설정하세요: ```csharp public class SdkAuthDelegate : BRZSdkAuthDelegate { public override void Braze(Braze braze, BRZSDKAuthenticationError error) { Console.WriteLine("Invalid SDK Authentication Token."); string newSignature = GetNewTokenSomehow(error); Braze.SharedInstance?.SetSDKAuthenticationSignature(newSignature); } } // Set the delegate during initialization var configuration = new BRZConfiguration("YOUR-API-KEY", "YOUR-ENDPOINT"); configuration.Api.SdkAuthentication = true; var braze = new Braze(configuration); braze.SdkAuthDelegate = new SdkAuthDelegate(); ``` **Android** ```csharp Braze.GetInstance(this).SubscribeToSdkAuthenticationFailures((error) => { string newToken = GetNewTokenSomehow(error); Braze.GetInstance(this).SetSdkAuthenticationSignature(newToken); }); ``` Braze Expo 플러그인을 사용할 때는 동일한 React Native SDK 메서드를 사용하십시오: ```typescript import Braze from '@braze/react-native-sdk'; const sdkAuthErrorSubscription = Braze.addListener( Braze.Events.SDK_AUTHENTICATION_ERROR, (error) => { console.log(`SDK Authentication for ${error.userId} failed with error code ${error.errorCode}.`); const updated_jwt = getNewTokenSomehow(error); Braze.setSdkAuthenticationSignature(updated_jwt); } ); // Don't forget to remove the listener when done // sdkAuthErrorSubscription.remove(); ``` ### 3단계: 대시보드에서 인증 활성화 {#braze-dashboard} 다음으로, 이전에 설정한 앱에 대해 Braze 대시보드에서 인증을 활성화할 수 있습니다. Braze 대시보드에서 앱의 SDK 인증 설정이 **필수**로 설정되지 않는 한, SDK 요청은 인증 없이 평소처럼 계속 진행됩니다. 통합에 문제가 발생한 경우(예: 앱이 SDK에 토큰을 잘못 전달하거나 서버가 잘못된 토큰을 생성하는 경우), Braze 대시보드에서 이 기능을 비활성화하면 데이터가 검증 없이 평소처럼 다시 흐르기 시작합니다. #### 적용 옵션 {#enforcement-options} 대시보드 **설정 관리** 페이지에서 각 앱에는 Braze가 요청을 검증하는 방식을 제어하는 세 가지 SDK 인증 상태가 있습니다. | 설정 | 설명 | | ------ | ---------- | | **비활성화됨** | Braze는 사용자에게 제공된 JWT를 확인하지 않습니다. (기본 설정)| | **선택 사항** | Braze는 로그인한 사용자의 요청을 확인하지만, 유효하지 않은 요청을 거부하지 않습니다. | | **필수** | Braze는 로그인한 사용자의 요청을 확인하고 유효하지 않은 JWT를 거부합니다.| {: .reset-td-br-1 .reset-td-br-2 aria-label="적용 옵션" } ![](https://www.braze.com/docs/ko/ko/assets/img/sdk-auth-settings.png?57ca6f4602ca213f2fc891c8c459c6f2) **선택 사항** 설정은 이 기능이 앱의 SDK 트래픽에 미칠 잠재적 영향을 모니터링하는 유용한 방법입니다. 잘못된 JWT는 **선택 사항** 및 **필수** 상태 모두에서 보고되지만, **필수** 상태에서만 SDK 요청이 거부되어 앱이 재시도하고 새 JWT를 요청하게 됩니다. ## 공개 키 관리 {#key-management} ### 공개 키 추가 {#adding-a-public-key} 앱당 최대 세 개의 공개 키(기본, 보조 및 3차)를 추가할 수 있습니다. 필요한 경우 동일한 키를 둘 이상의 앱에 추가할 수도 있습니다. 공개 키를 추가하려면: 1. Braze 대시보드로 이동하여 **설정** > **앱 설정**을 선택합니다. 2. 사용 가능한 앱 목록에서 앱을 선택합니다. 3. **SDK Authentication**에서 **Add Public Key**를 선택합니다. 4. 선택적 설명을 입력하고, 공개 키를 붙여넣은 다음, **Add Public Key**를 선택합니다. ### 새 기본 키 할당 {#assign-a-new-primary-key} 보조 키 또는 3차 키를 새 기본 키로 할당하려면: 1. Braze 대시보드로 이동하여 **설정** > **앱 설정**을 선택합니다. 2. 사용 가능한 앱 목록에서 앱을 선택합니다. 3. **SDK Authentication**에서 키를 선택하고 **Manage** > **Make Primary Key**를 선택합니다. ### 키 삭제 {#deleting-a-key} 기본 키를 삭제하려면 먼저 [새 기본 키를 할당](#assign-a-new-primary-key)한 다음, 키를 삭제합니다. 기본 키가 아닌 키를 삭제하려면: 1. Braze 대시보드로 이동하여 **설정** > **앱 설정**을 선택합니다. 2. 사용 가능한 앱 목록에서 앱을 선택합니다. 3. **SDK Authentication**에서 기본 키가 아닌 키를 선택하고 **Manage** > **Delete Public Key**를 선택합니다. ## 분석 {#analytics} 각 앱은 이 기능이 **선택 사항** 및 **필수** 상태에 있는 동안 수집된 SDK 인증 오류의 분석을 보여줍니다. 데이터는 실시간으로 제공되며, 차트의 포인트 위로 마우스를 가져가면 특정 날짜의 오류 내역을 볼 수 있습니다. ![인증 오류 발생 횟수를 보여주는 차트. 총 오류 수, 오류 유형 및 조정 가능한 날짜 범위도 표시됩니다.](https://www.braze.com/docs/ko/ko/assets/img/sdk-auth-analytics.png?c6dc9b578383a57c21f77f032808930e){: style="max-width:80%"} ## 오류 코드 {#error-codes} | 오류 코드 | 오류 사유 | 설명 | 해결 방법 | | -------- | ------------ | --------- | --------- | | 10 | `EXPIRATION_REQUIRED` | 만료는 Braze 사용을 위한 필수 필드입니다. | JWT 생성 로직에 `exp` 또는 만료 필드를 추가하십시오. | | 20 | `DECODING_ERROR` | 일치하지 않는 공개 키 또는 일반적인 포착되지 않은 오류. | JWT를 JWT 테스트 도구에 복사하여 JWT가 유효하지 않은 형식인 이유를 진단하십시오. | | 21 | `SUBJECT_MISMATCH` | 예상 subject와 실제 subject가 동일하지 않습니다. | `sub` 필드는 `changeUser` SDK 메서드에 전달된 것과 동일한 사용자 ID여야 합니다. | | 22 | `EXPIRED` | 제공된 토큰이 만료되었습니다. | 만료 기간을 연장하거나 만료 전에 주기적으로 토큰을 새로고침하십시오. | | 23 | `INVALID_PAYLOAD` | 토큰 페이로드가 유효하지 않습니다. | JWT를 JWT 테스트 도구에 복사하여 JWT가 유효하지 않은 형식인 이유를 진단하십시오. | | 24 | `INCORRECT_ALGORITHM` | 토큰의 알고리즘이 지원되지 않습니다. | JWT를 `RS256` 암호화 방식으로 변경하십시오. 다른 유형은 지원되지 않습니다. | | 25 | `PUBLIC_KEY_ERROR` | 공개 키를 올바른 형식으로 변환할 수 없습니다. | JWT를 JWT 테스트 도구에 복사하여 JWT가 유효하지 않은 형식인 이유를 진단하십시오. | | 26 | `MISSING_TOKEN` | 요청에 토큰이 제공되지 않았습니다. | `changeUser(id, token)` 호출 시 토큰을 반드시 전달하고, 토큰이 비어 있지 않은지 확인하십시오.| | 27 | `NO_MATCHING_PUBLIC_KEYS` | 제공된 토큰과 일치하는 공개 키가 없습니다. | JWT에 사용된 비공개 키가 앱에 구성된 공개 키와 일치하지 않습니다. 이 API 키와 일치하는 워크스페이스의 올바른 앱에 공개 키를 추가했는지 확인하십시오.| | 28 | `PAYLOAD_USER_ID_MISMATCH` | 요청 페이로드 내 모든 사용자 ID가 요구되는 대로 일치하지 않습니다. | 이는 예상치 못한 상황이며, 잘못된 형식의 페이로드가 발생할 수 있습니다. 고객지원 티켓을 열어 도움을 받으세요. | {: .reset-td-br-1 .reset-td-br-2 .reset-td-br-3 .reset-td-br-4 aria-label="오류 코드" } ## 자주 묻는 질문(FAQ) {#faq} #### 이 기능을 모든 앱에서 동시에 활성화해야 하나요? {#faq-app-by-app} 아니요. 이 기능은 특정 앱에 대해 활성화할 수 있으며 모든 앱에서 한 번에 사용할 필요는 없습니다. #### 내 앱의 이전 버전을 사용하는 사용자에게는 어떤 일이 발생하나요? {#faq-sdk-backward-compatibility} 이 기능을 적용하기 시작하면, 이전 앱 버전에서 보낸 요청은 Braze에 의해 거부되고 SDK에 의해 재시도됩니다. 사용자가 앱을 지원되는 버전으로 업그레이드하면 대기줄에 추가된 요청이 다시 수락되기 시작합니다. 가능하다면 다른 필수 업그레이드와 마찬가지로 사용자가 업그레이드하도록 푸시해야 합니다. 또는 허용 가능한 비율의 사용자가 업그레이드할 때까지 기능을 [선택 사항](#enforcement-options)으로 유지할 수 있습니다. #### JWT를 생성할 때 어떤 만료 기간을 사용해야 하나요? {#faq-expiration} 평균 세션 지속 시간, 세션 쿠키/토큰 만료 또는 애플리케이션이 현재 고객 프로필을 새로고침하는 빈도 중 더 높은 값을 사용하는 것이 좋습니다. #### JWT가 사용자의 세션 중간에 만료되면 어떻게 되나요? {#faq-jwt-expiration} 사용자의 토큰이 세션 도중에 만료되는 경우, SDK에는 [콜백 함수](#sdk-callback)가 있어 앱에 Braze로 데이터를 계속 전송하려면 새 JWT가 필요하다는 것을 알려줍니다. #### 서버 측 통합이 중단되어 더 이상 JWT를 생성할 수 없으면 어떻게 되나요? {#faq-server-downtime} 서버에서 JWT를 제공할 수 없거나 통합 문제가 발견되는 경우 언제든지 Braze 대시보드에서 해당 기능을 비활성화할 수 있습니다. 비활성화되면 보류 중인 실패한 모든 SDK 요청이 결국 SDK에 의해 재시도되고 Braze에 의해 수락됩니다. #### 왜 이 기능은 공유 비밀 대신 공개/비공개 키를 사용하나요? {#faq-shared-secrets} 공유 비밀을 사용할 때 Braze 대시보드 페이지와 같은 해당 공유 비밀에 접근할 수 있는 사람은 누구나 토큰을 생성하고 최종 사용자를 사칭할 수 있습니다. 대신 공개/비공개 키를 사용하므로, Braze 직원은 물론이고 귀사의 사용자도 비공개 키에 접근할 수 없습니다. #### 거부된 요청은 어떻게 재시도되나요? {#faq-retry-logic} 인증 오류로 인해 요청이 거부되면 SDK는 사용자의 JWT를 새로고침하는 데 사용되는 콜백을 호출합니다. 요청은 지수 백오프 접근 방식을 사용하여 주기적으로 재시도됩니다. 50번 연속으로 시도에 실패하면 다음 세션이 시작될 때까지 재시도가 일시 중지됩니다. 각 SDK에는 데이터 플러시를 수동으로 요청하는 메서드도 있습니다. #### 익명 사용자에게 SDK 인증을 사용할 수 있나요? {#faq-anonymous-users} 아니요. SDK 인증은 웹사이트가 누군가의 신원을 확인하는 방식으로 작동하므로, 식별된 사용자에게만 적용됩니다. 익명 사용자의 경우 확인할 신원이 없습니다. 적용은 `changeUser`가 호출된 후에 시작됩니다. 사용자가 식별되기 전(예: 가입 전에 익명으로 탐색하는 동안)에는 SDK가 JWT 없이도 Braze에 데이터를 전송할 수 있습니다. `changeUser`가 호출된 후에는 해당 식별된 프로필에 대한 요청에 유효한 JWT가 필요합니다. 이는 일반적인 사용자 여정이 다음과 같을 수 있음을 의미합니다: 1. 사용자가 익명으로 사이트를 방문하거나 앱을 엽니다. Braze는 JWT 없이 이 활동을 수집합니다. 2. 사용자가 가입하거나 로그인하면, 앱이 `external_id`와 함께 `changeUser`를 호출합니다. 3. Braze는 해당 사용자의 활동을 계속 수집하며, 해당 식별된 프로필에 대한 요청에 SDK 인증이 적용됩니다. #### SDK 인증은 사용자 별칭과 함께 작동하나요? {#faq-aliases} 아니요. SDK 인증에는 `external_id`가 필요합니다. `braze_id` 또는 `alias_id`만 사용 가능한 경우에는 설정할 수 없으므로, 별칭 전용 프로필은 SDK 인증을 사용할 수 없습니다. #### SDK 인증을 활성화하면 인증되지 않은 활동 수집이 차단되나요? {#faq-unauthenticated-collection} 아니요. SDK 인증은 정당한 익명 활동 수집을 차단하지 않습니다. `changeUser`로 프로필이 식별된 후에만 적용됩니다. # Braze SDK 디버깅하기 Source: /docs/ko/developer_guide/sdk_integration/debugging/index.md # Braze SDK 디버깅하기 {#debugging-the-braze-sdk} > 앱에서 상세 로깅을 활성화하지 않고도 SDK 기반 채널의 문제를 해결할 수 있도록 Braze SDK에 내장된 디버거를 사용하는 방법을 알아보세요. **Tip:** 더 심층적인 조사가 필요한 경우, [상세 로깅을 활성화](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/verbose_logging/)하여 자세한 SDK 출력을 캡처하고, 특정 채널에 대한 [상세 로그를 읽는 방법](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/reading_verbose_logs/)도 확인할 수 있습니다. ## 필수 조건 {#prerequisites} Braze SDK 디버거를 사용하려면 "PII 보기" 및 "고객 프로필 보기(PII 삭제됨)" 권한이 필요합니다. 디버깅 세션 로그를 다운로드하려면 "사용자 데이터 내보내기" 권한도 필요합니다. 또한 Braze SDK는 다음 최소 버전 이상이어야 합니다: `Braze.configuration.logger.level`이 `.disabled`일 때 디버거 로그를 수집하려면 Swift SDK 11.9.0 이상을 사용하세요. 자세한 내용은 [Swift 체인지로그](https://www.braze.com/docs/ko/ko/developer_guide/changelogs/#swift_fixed-12)를 참조하세요. ## Braze SDK 디버깅하기 **Tip:** Braze 웹 SDK의 디버깅을 활성화하려면 [URL 매개변수를 사용](https://www.braze.com/docs/ko/ko/developer_guide/platform_integration_guides/web/initial_sdk_setup/#logging)하면 됩니다. ### 1단계: 앱 닫기 {#step-1-close-your-app} 디버깅 세션을 시작하기 전에 현재 문제가 발생하고 있는 앱을 닫으세요. 세션 시작 시 앱을 다시 실행할 수 있습니다. ### 2단계: 디버깅 세션 생성 {#step-2-create-a-debugging-session} Braze에서 **설정**으로 이동한 다음 **설정 및 테스트**에서 **SDK 디버거**를 선택합니다. !["SDK 디버거"가 강조 표시된 "설정 및 테스트" 섹션.](https://www.braze.com/docs/ko/ko/assets/img/sdk_debugger/select_sdk_debugger.png?2387a3520a04597abdb85a9a100baa87) **Create debugging session**을 선택합니다. !["SDK 디버거" 페이지.](https://www.braze.com/docs/ko/ko/assets/img/sdk_debugger/select_create_debugging_session.png?b8edf96c40967f310493fcb8de32a9de) ### 3단계: 사용자 선택 {#step-3-select-a-user} 이메일 주소, `external_id`, 사용자 별칭 또는 푸시 토큰을 사용하여 사용자를 검색합니다. 세션을 시작할 준비가 되면 **Select User**를 선택합니다. ![선택한 사용자의 디버깅 페이지.](https://www.braze.com/docs/ko/ko/assets/img/sdk_debugger/search_and_select_user.png?bab3a4f1e879e4148badff07a76a1385){: style="max-width:85%;"} ### 4단계: 앱 다시 실행 {#step-4-relaunch-the-app} 먼저 앱을 실행하고 기기가 페어링되었는지 확인합니다. 페어링에 성공하면 앱을 다시 실행하세요—이렇게 하면 앱의 초기화 로그가 완전히 캡처됩니다. ### 5단계: 재현 단계 완료 {#step-5-complete-the-reproduction-steps} 앱을 다시 실행한 후 오류를 재현하는 단계를 따라 진행합니다. **Tip:** 오류를 재현할 때는 재현 단계를 가능한 한 정확히 따라 [양질의 로그](#step-6-export-your-session-logs-optional)를 생성할 수 있도록 하세요. ### 6단계: 세션 종료 {#step-6-end-your-session} 재현 단계를 완료했으면 **End Session** > **Close**를 선택합니다. !["End Session" 버튼이 표시된 디버깅 세션.](https://www.braze.com/docs/ko/ko/assets/img/sdk_debugger/close_debugging_session.png?1acb6a88b82a111e7d69f7d7ccbd18b5){: style="max-width:85%;"} **Note:** 세션 길이와 네트워크 연결 상태에 따라 로그를 생성하는 데 몇 분 정도 걸릴 수 있습니다. ### 7단계: 세션 공유 또는 내보내기(선택 사항) {#step-7-share-or-export-your-session-optional} 세션이 끝나면 세션 로그를 CSV 파일로 내보낼 수 있습니다. 또한 다른 사람이 **Session ID**를 사용하여 디버그 세션을 검색할 수 있으므로 로그를 직접 보낼 필요가 없습니다. ![세션 종료 후 "Export Logs" 및 "Copy Session ID"가 표시된 디버깅 페이지.](https://www.braze.com/docs/ko/ko/assets/img/sdk_debugger/copy_id_and_export_logs.png?6e9b1911ea1e119ed20a667f37ab535a) # 상세 로깅 Source: /docs/ko/developer_guide/sdk_integration/verbose_logging/index.md # 상세 로깅 > 상세 로깅은 Braze SDK에서 낮은 수준의 자세한 정보를 출력하여 SDK가 초기화되고, 서버와 통신하며, 푸시, 인앱 메시지 및 콘텐츠 카드와 같은 메시징 채널을 처리하는 방식을 볼 수 있게 해줍니다. 푸시 알림이 도착하지 않거나, 인앱 메시지가 표시되지 않거나, 사용자 데이터가 동기화되지 않는 등 예상대로 작동하지 않을 때, 상세 로그는 문제의 근본 원인을 파악하는 데 도움이 됩니다. 추측하는 대신, 각 단계에서 SDK가 무엇을 하고 있는지 정확히 볼 수 있습니다. **Tip:** 상세 로깅을 수동으로 활성화하지 않고 디버깅하려면 [SDK 디버거](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/debugging)를 사용하여 Braze 대시보드에서 직접 디버깅 세션을 생성할 수 있습니다. ## 상세 로깅을 사용할 때 다음이 필요할 때 상세 로깅을 켜세요: - **SDK 초기화 확인**: SDK가 올바른 API 키와 엔드포인트로 올바르게 시작되는지 확인하세요. - **메시지 전달 문제 해결**: 푸시 토큰이 등록되었는지, 인앱 메시지가 트리거되었는지, 콘텐츠 카드가 동기화되었는지 확인하세요. - **딥 링크 디버깅**: SDK가 푸시, 인앱 메시지 또는 콘텐츠 카드에서 딥 링크를 수신하고 여는지 확인하세요. - **세션 추적 검증**: 세션이 예상대로 시작되고 종료되는지 확인하세요. - **연결 문제 진단**: SDK와 Braze 서버 간의 네트워크 요청 및 응답을 검사하세요. ## 상세 로깅 활성화 **Important:** 상세 로그는 개발 및 테스트 환경에서만 사용하도록 설계되었습니다. 앱을 프로덕션에 배포하기 전에 자세한 로깅을 비활성화하여 민감한 정보가 노출되는 것을 방지하십시오. 가장 완전한 출력을 캡처하기 위해 `Application.onCreate()` 메서드에서 다른 SDK 호출 전에 자세한 로깅을 활성화하십시오. **코드:** ```java BrazeLogger.setLogLevel(Log.VERBOSE); ``` ```kotlin BrazeLogger.logLevel = Log.VERBOSE ``` **`braze.xml`:** ```xml 2 ``` 자세한 로깅이 활성화되었는지 확인하려면 Logcat 출력에서 `V/Braze`을 검색하십시오. For example: ``` 2077-11-19 16:22:49.591 ? V/Braze v9.0.01 .bo.app.d3: Request started ``` 자세한 내용은 [Android SDK 로깅](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration#android_enabling-logs)을 참조하십시오. 초기화 중에 `Braze.Configuration` 객체에서 로그 수준을 `.debug`으로 설정하십시오. ```swift let configuration = Braze.Configuration( apiKey: "", endpoint: "" ) configuration.logger.level = .debug let braze = Braze(configuration: configuration) ``` ```objc BRZConfiguration *configuration = [[BRZConfiguration alloc] initWithApiKey:@"" endpoint:@""]; [configuration.logger setLevel:BRZLoggerLevelDebug]; Braze *braze = [[Braze alloc] initWithConfiguration:configuration]; ``` `.debug` 수준은 가장 자세하며 문제 해결을 위해 권장됩니다. 자세한 내용은 [Swift SDK 로깅](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration#swift_log-levels)을 참조하십시오. URL 매개변수로 `?brazeLogging=true`을 추가하거나 SDK 초기화 중에 로깅을 활성화하십시오: ```javascript braze.initialize('YOUR-API-KEY', { baseUrl: 'YOUR-SDK-ENDPOINT', enableLogging: true }); ``` 초기화 후에도 로깅을 토글할 수 있습니다: ```javascript braze.toggleLogging(); ``` 로그는 브라우저의 개발자 도구의 **콘솔** 탭에 나타납니다. 자세한 내용은 [Web SDK 로깅](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration#web_logging)을 참조하십시오. 1. **Braze** > **Braze 구성**으로 이동하여 Braze 구성 설정을 엽니다. 2. **Braze Android 설정 표시** 드롭다운을 선택하십시오. 3. **SDK 로그 수준** 필드에 `0`를 입력하십시오. SDK 구성 중에 로그 수준을 설정하십시오: ```javascript const configuration = new Braze.BrazeConfiguration('YOUR-API-KEY', 'YOUR-SDK-ENDPOINT'); configuration.logLevel = Braze.LogLevel.Verbose; ``` ## 로그 수집 자세한 로깅을 활성화한 후, 문제를 재현하고 플랫폼의 콘솔 또는 디버깅 툴에서 로그를 수집하십시오. Android Studio에서 **Logcat**을 사용하여 로그를 캡처하십시오: 1. 기기를 연결하거나 에뮬레이터를 시작하십시오. 2. Android 스튜디오에서 하단 패널의 **Logcat**을 엽니다. 3. Braze SDK 출력을 분리하기 위해 `V/Braze` 또는 `D/Braze`로 필터링합니다. 4. 문제를 재현합니다. 5. 관련 로그를 복사하여 텍스트 파일에 저장합니다. macOS에서 **Console** 앱을 사용하여 로그를 캡처합니다: 1. 상세 로깅이 활성화된 상태로 기기에 앱을 설치합니다. 2. 기기를 Mac에 연결합니다. 3. **Console** 앱을 열고 **Devices** 사이드바에서 기기를 선택합니다. 4. 검색창에서 `Braze` 또는 `BrazeKit`로 로그를 필터링합니다. 5. 문제를 재현합니다. 6. 관련 로그를 복사하여 텍스트 파일에 저장합니다. 브라우저의 개발자 도구를 사용합니다: 1. 브라우저의 개발자 도구를 엽니다(보통 **F12** 또는 **Cmd+Option+I**입니다). 2. **Console** 탭으로 이동합니다. 3. 문제를 재현합니다. 4. 콘솔 출력을 복사하여 텍스트 파일에 저장합니다. **Tip:** Braze 지원을 위한 로그를 수집할 때, 앱을 실행하기 전에 로깅을 시작하고 문제가 발생한 후에도 계속합니다. 이것은 사건의 전체 순서를 캡처하는 데 도움이 됩니다. ## 상세 로그 읽기 상세 로그는 SDK가 수행하는 작업을 추적하는 데 도움이 되는 일관된 구조를 따릅니다. 특정 채널에 대한 로그 출력을 해석하는 방법, 어떤 주요 항목을 찾아야 하는지 및 일반적인 문제 해결 패턴에 대해 알아보려면 [상세 로그 읽기](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/reading_verbose_logs)를 참조하세요. ## Braze 지원과 로그 공유하기 SDK 문제로 Braze 지원에 연락할 때 다음을 포함하세요: 1. **자세한 로그 파일**: 앱 실행 전부터 문제 발생까지의 전체 로그 캡처입니다. 2. **재현 단계**: 문제를 유발하는 행동에 대한 명확한 설명입니다. 3. **예상 vs. 실제 동작**: 당신이 기대했던 일과 실제로 일어난 일입니다. 4. **SDK 버전**: 당신이 사용하고 있는 Braze SDK의 버전입니다. 5. **플랫폼 및 OS 버전**: 예: iOS 18.0, Android 14, 또는 Chrome 120. # 상세 로그 읽기 Source: /docs/ko/developer_guide/sdk_integration/reading_verbose_logs/index.md # 상세 로그 읽기 {#reading-verbose-logs} > 이 페이지에서는 Braze SDK의 상세 로그 출력을 해석하는 방법을 설명합니다. 각 메시징 채널에 대해 찾아야 할 주요 로그 항목, 그 의미, 주의해야 할 일반적인 문제를 확인할 수 있습니다. 시작하기 전에 [상세 로깅을 활성화](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/verbose_logging/)했는지, 그리고 플랫폼에서 로그를 수집하는 방법을 알고 있는지 확인하세요. ## 세션 {#sessions} 세션은 Braze 분석 및 메시지 전달의 기초입니다. 인앱 메시지 및 Content Cards를 포함한 많은 메시징 기능은 유효한 세션이 시작되어야 작동할 수 있습니다. 세션이 올바르게 기록되지 않으면 먼저 이를 조사하세요. 세션 추적 활성화에 대한 자세한 내용은 [5단계: 사용자 세션 추적 활성화](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=android#android_step-5-enable-user-session-tracking)를 참조하세요. ### 주요 로그 항목 {#key-log-entries} **세션 시작:** ``` Started user session (id: ) ``` **세션 종료:** ``` Ended user session (id: , duration: s) Logged event: - userId: - sessionId: - data: sessionEnd(duration: ) ``` **세션 시작:** 다음 항목을 찾으세요: ``` New session created with ID: Session start event for new session received Completed the openSession call Opened session with activity: ``` 구성된 Braze 엔드포인트(예: sdk.iad-01.braze.com)에 대한 네트워크 요청을 필터링하여 세션 시작(`ss`) 이벤트를 확인하세요. **세션 종료:** ``` Closed session with activity: Closed session with session ID: Requesting data flush on internal session close flush timer. ``` ### 확인할 사항 {#what-to-check} - 앱이 시작될 때 세션 시작 로그가 나타나는지 확인하세요. - 세션 시작이 보이지 않으면 SDK가 올바르게 초기화되었는지, `openSession`(Android)이 호출되고 있는지 확인하세요. - Android에서 Braze 엔드포인트에 대한 네트워크 요청이 이루어지고 있는지 확인하세요. 보이지 않으면 API 키와 엔드포인트 구성을 확인하세요. ## 푸시 알림 {#push-notifications} 푸시 알림 로그는 기기 토큰이 등록되었는지, 알림이 전달되었는지, 클릭 이벤트가 추적되었는지 확인하는 데 도움이 됩니다. ### 토큰 등록 {#token-registration} 세션이 시작되면 SDK는 기기의 푸시 토큰을 Braze에 등록합니다. ``` Updated push notification authorization: - authorization: authorized Received remote notifications device token: ``` 구성된 Braze 엔드포인트(예: sdk.iad-01.braze.com)에 대한 요청을 필터링하고 요청 본문 속성에서 `push_token`을 찾으세요: ``` "attributes": [ { "push_token": "", "user_id": "" } ] ``` 기기 정보에 다음이 포함되어 있는지도 확인하세요: ``` "device": { "ios_push_auth": "authorized", "remote_notification_enabled": 1 } ``` FCM 등록 로그를 찾으세요: ``` Registering for Firebase Cloud Messaging token using sender id: ``` 다음을 확인하세요: - `com_braze_firebase_cloud_messaging_registration_enabled`이 `true`인지 확인합니다. - FCM 발신자 ID가 Firebase 프로젝트와 일치하는지 확인합니다. 일반적인 오류는 `SENDER_ID_MISMATCH`이며, 이는 구성된 발신자 ID가 Firebase 프로젝트와 일치하지 않음을 의미합니다. ### 확인할 사항 - 요청 본문에서 `push_token`이 누락된 경우 토큰이 캡처되지 않은 것입니다. 앱 구성에서 푸시 설정을 확인하세요. - `ios_push_auth`가 `denied` 또는 `provisional`을 표시하면 사용자가 전체 푸시 권한을 부여하지 않은 것입니다. - Android에서 `SENDER_ID_MISMATCH`가 보이면 FCM 발신자 ID를 Firebase 프로젝트와 일치하도록 업데이트하세요. ### 푸시 전달 및 클릭 {#push-delivery-and-click} 푸시 알림을 탭하면 SDK가 처리 및 클릭 이벤트를 기록합니다. ``` Processing push notification: - date: - silent: false - userInfo: { "ab": { ... }, "ab_uri": "", "aps": { "alert": { "body": "", "title": "" } } } ``` 이어서 클릭 이벤트가 기록됩니다: ``` Logged event: - userId: - sessionId: - data: pushClick(campaignId: ...) ``` 푸시에 딥링크가 포함되어 있으면 다음도 볼 수 있습니다: ``` Opening '': - channel: notification - useWebView: false - isUniversalLink: false ``` ``` BrazeFirebaseMessagingService: Got Remote Message from FCM ``` 이어서 푸시 페이로드 및 표시 로그가 나타납니다. 딥링크의 경우 딥링크 대리자 또는 `UriAction` 항목을 찾으세요. ### 확인할 사항 - 푸시 페이로드에 예상되는 `title`, `body` 및 딥링크(`ab_uri`)가 포함되어 있는지 확인하세요. - 탭한 후 `pushClick` 이벤트가 기록되었는지 확인하세요. - 클릭 이벤트가 누락된 경우 앱 대리자 또는 알림 핸들러가 푸시 이벤트를 Braze SDK에 올바르게 전달하고 있는지 확인하세요. ## 인앱 메시지 {#in-app-messages} 인앱 메시지 로그는 서버에서의 전달, 이벤트 기반 트리거, 표시, 노출 기록 및 클릭 추적의 전체 생애 주기를 보여줍니다. ### 메시지 전달 {#message-delivery} 사용자가 세션을 시작하고 인앱 메시지 수신 자격이 있을 때 SDK는 서버로부터 메시지 페이로드를 수신합니다. 구성된 Braze 엔드포인트(예: sdk.iad-01.braze.com)에서 인앱 메시지 데이터를 포함하는 응답을 필터링하세요. 응답 본문에는 다음을 포함한 메시지 페이로드가 포함되어 있습니다: ``` "templated_message": { "data": { "message": "...", "type": "HTML", "message_close": "SWIPE", "trigger_id": "" }, "type": "inapp" } ``` 일치하는 트리거 이벤트 로그를 찾으세요: ``` Triggering action: ``` 이는 인앱 메시지가 트리거 이벤트와 일치했음을 확인합니다. ### 메시지 표시 및 노출 {#message-display-and-impression} ``` In-app message ready for display: - triggerId: (campaignId: , ...) - extras: { ... } ``` 이어서 노출 로그가 나타납니다: ``` Logged event: - userId: - sessionId: - data: inAppMessageImpression(triggerIds: [...]) ``` ``` handleExistingInAppMessagesInStackWithDelegate:: Displaying in-app message ``` ### 클릭 및 버튼 이벤트 {#click-and-button-events} 사용자가 버튼을 탭하거나 메시지를 닫을 때: ``` Logged event: - userId: - sessionId: - data: inAppMessageButtonClick(triggerIds: [...], buttonId: "") ``` 추가로 트리거된 메시지가 일치하지 않으면 다음도 볼 수 있습니다: ``` No matching trigger for event. ``` 이것은 해당 이벤트에 대해 추가 인앱 메시지가 구성되지 않았을 때 예상되는 동작입니다. 구성된 Braze 엔드포인트(예: sdk.iad-01.braze.com)에 대한 요청을 필터링하고 요청 본문에서 `sbc`(버튼 클릭) 또는 `si`(노출)라는 이름의 이벤트를 찾으세요. ### 확인할 사항 - 인앱 메시지가 표시되지 않으면 먼저 세션 시작이 기록되었는지 확인하세요. - 구성된 Braze 엔드포인트에서 응답을 필터링하여 메시지 페이로드가 전달되었는지 확인하세요. - 노출 횟수가 기록되지 않으면 로깅을 억제하는 커스텀 `inAppMessageDisplay` 대리자를 구현하지 않았는지 확인하세요. - "No matching trigger for event"가 나타나면 이는 정상이며, 해당 이벤트에 대해 추가 인앱 메시지가 구성되지 않았음을 나타냅니다. ## Content Cards Content Cards 로그는 카드가 기기에 동기화되고, 사용자에게 표시되며, 상호작용(노출 횟수, 클릭, 해제)이 추적되고 있는지 확인하는 데 도움이 됩니다. ### 카드 동기화 {#card-sync} Content Cards는 세션 시작 시 및 수동 새로고침 요청 시 동기화됩니다. 세션이 기록되지 않으면 Content Cards가 표시되지 않습니다. 구성된 Braze 엔드포인트(예: sdk.iad-01.braze.com)에서 카드 데이터를 포함하는 응답을 필터링하세요. 응답 본문에는 다음을 포함한 카드 데이터가 포함되어 있습니다: ``` "cards": [ { "id": "", "tt": "", "ds": "", "tp": "short_news", "v": 0, "cl": 0, "p": 1 } ] ``` 주요 필드: - `v` (조회됨): `0` = 조회되지 않음, `1` = 조회됨 - `cl` (클릭됨): `0` = 클릭되지 않음, `1` = 클릭됨 - `p` (고정됨): `0` = 고정되지 않음, `1` = 고정됨 - `tp` (유형): `short_news`, `captioned_image`, `classic` 등 ``` Requesting content cards sync. ``` 이어서 사용자 및 기기 정보를 포함하는 구성된 Braze 엔드포인트(예: sdk.iad-01.braze.com)에 대한 POST 요청이 나타납니다. ### 노출 횟수, 클릭 및 해제 {#impressions-clicks-and-dismissals} **노출 횟수:** ``` Logged event: - userId: - sessionId: - data: contentCardImpression(cardIds: [...]) ``` **클릭:** ``` Logged event: - userId: - sessionId: - data: contentCardClick(cardIds: [...]) ``` 카드에 URL이 있는 경우 다음도 볼 수 있습니다: ``` Opening '': - channel: contentCard - useWebView: true ``` **해제:** ``` Logged event: - userId: - sessionId: - data: contentCardDismissed(cardIds: [...]) ``` 구성된 Braze 엔드포인트(예: sdk.iad-01.braze.com)에 대한 요청을 필터링하고 요청 본문에서 이벤트 이름을 찾으세요: - `cci` — Content Cards 노출 횟수 - `ccc` — Content Cards 클릭 - `ccd` — Content Cards 해제됨 ### 확인할 사항 - **카드가 표시되지 않음**: 세션 시작이 기록되었는지 확인하세요. Content Cards는 동기화를 위해 활성 세션이 필요합니다. - **새 사용자에게 카드가 누락됨**: 첫 세션의 새 사용자는 다음 세션까지 Content Cards를 보지 못할 수 있습니다. 이는 예상되는 동작입니다. - **카드가 크기 제한을 초과함**: 2 KB를 초과하는 Content Cards는 표시되지 않으며 메시지가 중단됩니다. - **Campaign 중지 후 카드가 지속됨**: Campaign이 중지된 후 동기화가 완료되었는지 확인하세요. Content Cards는 성공적인 동기화 후 기기에서 제거됩니다. Campaign을 중지할 때 사용자 피드에서 활성 카드를 제거하는 옵션이 선택되었는지 확인하세요. ## 딥링크 {#deep-links} 딥링크 로그는 푸시 알림, 인앱 메시지, Content Cards에 걸쳐 나타납니다. 로그 구조는 소스 채널에 관계없이 일관됩니다. SDK가 딥링크를 처리할 때: ``` Opening '': - channel: - useWebView: false - isUniversalLink: false - extras: { ... } ``` 여기서 ``은 `notification`, `inAppMessage` 또는 `contentCard` 중 하나입니다. 딥링크의 경우 Logcat에서 **Deep Link Delegate** 또는 **UriAction** 항목을 찾으세요. 딥링크 해석을 독립적으로 테스트하려면 다음 명령을 실행하세요: ```bash adb shell am start -W -a android.intent.action.VIEW -d "" "" ``` 이를 통해 딥링크가 Braze SDK 외부에서 올바르게 해석되는지 확인할 수 있습니다. ### 확인할 사항 - 딥링크 URL이 Campaign에서 구성한 내용과 일치하는지 확인하세요. - 딥링크가 한 채널(예: 푸시)에서는 작동하지만 다른 채널(예: Content Cards)에서는 작동하지 않는 경우, 딥링크 처리 구현이 모든 채널을 지원하는지 확인하세요. - iOS에서는 유니버설 링크에 추가 처리가 필요합니다. Braze 채널에서 유니버설 링크가 작동하지 않는 경우 앱이 URL 처리를 위해 `BrazeDelegate` 프로토콜을 구현했는지 확인하세요. - Android에서는 커스텀 핸들러를 사용하는 경우 자동 딥링크 처리가 비활성화되어 있는지 확인하세요. 그렇지 않으면 기본 핸들러가 구현과 충돌할 수 있습니다. ## 사용자 식별 {#user-identification} 사용자가 `external_id`로 식별되면 SDK는 사용자 변경 이벤트를 기록합니다. ``` changeUser called with: ``` 알아야 할 주요 사항: - 사용자가 로그인하는 즉시 `changeUser`를 호출하세요. 빠를수록 좋습니다. - 사용자가 로그아웃하면 `changeUser`를 호출하여 익명 사용자로 되돌릴 수 있는 방법이 없습니다. - 익명 사용자를 원하지 않는 경우 세션 시작 또는 앱 시작 시 `changeUser`를 호출하세요. 구성된 Braze 엔드포인트(예: sdk.iad-01.braze.com)에 대한 요청을 필터링하고 요청 본문에서 사용자 식별을 찾으세요: ``` "user_id": "" ``` ## 네트워크 요청 {#network-requests} 상세 로그에는 Braze 서버와의 SDK 통신을 위한 전체 HTTP 요청 및 응답 세부 정보가 포함됩니다. 이는 연결 문제를 진단하는 데 유용합니다. ### 요청 구조 {#request-structure} 구성된 Braze 엔드포인트(예: sdk.iad-01.braze.com)에 대한 요청을 필터링하세요. 요청 구조에는 다음이 포함됩니다: ``` [http] request POST: - Headers: - Content-Type: application/json - X-Braze-Api-Key: - X-Braze-Req-Attempt: 1 - X-Braze-Req-Tokens-Remaining: - Body: { ... } ``` ``` Making request(id = ) to ``` ### 확인할 사항 - **API 키**: `X-Braze-Api-Key`가 워크스페이스의 API 키와 일치하는지 확인하세요. - **엔드포인트**: 요청 URL이 구성된 SDK 엔드포인트와 일치하는지 확인하세요. - **재시도 횟수**: `X-Braze-Req-Attempt`가 1보다 크면 SDK가 실패한 요청을 재시도하고 있음을 나타내며, 이는 연결 문제를 의미할 수 있습니다. - **사용량 제한**: `X-Braze-Req-Tokens-Remaining`은 남은 요청 토큰을 보여줍니다. 낮은 수치는 SDK가 사용량 제한에 접근하고 있음을 나타낼 수 있습니다. - **누락된 요청**: Android에서 세션 시작 후 Braze 엔드포인트에 대한 요청이 보이지 않으면 API 키 및 엔드포인트 구성을 확인하세요. ## 일반 이벤트 약어 {#common-event-abbreviations} 상세 로그 페이로드에서 Braze는 약어 이벤트 이름을 사용합니다. 다음은 참조 목록입니다: | 약어 | 이벤트 | |---|---| | `ss` | 세션 시작 | | `se` | 세션 종료 | | `si` | 인앱 메시지 노출 횟수 | | `sbc` | 인앱 메시지 버튼 클릭 | | `cci` | Content Cards 노출 횟수 | | `ccc` | Content Cards 클릭 | | `ccd` | Content Cards 해제됨 | | `lr` | 위치 기록됨 | {: .reset-td-br-1 .reset-td-br-2 aria-label="일반 이벤트 약어" } ## 문제 해결 {#troubleshooting} ### 사용자 프로필에 세션이 0으로 기록되는 경우는 언제인가요? {#when-might-a-user-have-0-sessions-recorded-against-their-profile} REST API([`/users/track`](https://www.braze.com/docs/ko/ko/api/endpoints/user_data/post_user_track/)) 또는 CSV 가져오기를 통해 **첫 번째 세션** 또는 **마지막 세션** 필드 없이 사용자를 가져오면 사용자 프로필에 세션이 0으로 표시될 수 있습니다. 세션은 사용자가 SDK를 통해 앱과 상호작용할 때 기록됩니다. 자세한 내용은 [사용자 프로필에 세션이 0인 경우](https://www.braze.com/docs/ko/ko/developer_guide/analytics/tracking_sessions/#user-profile-has-0-sessions)를 참조하세요. ### SDK와 REST API를 함께 사용할 때 사용자 데이터 불일치 {#user-data-discrepancies-when-using-the-sdk-and-rest-api-together} SDK와 REST API를 동시에 사용하면 경합 조건으로 인해 데이터 불일치가 발생할 수 있습니다. `changeUser()`를 호출한 후 중요한 REST API 호출을 하기 전에 SDK가 대기 중인 데이터를 플러시할 수 있도록 하고, 시간에 민감한 업데이트를 일괄 처리하지 않으며, SDK와 API 요청 사이에 짧은 지연을 추가하는 것을 고려하세요. `changeUser()` 동작에 대한 자세한 내용은 [changeUser() 작동 방식](https://www.braze.com/docs/ko/ko/developer_guide/analytics/setting_user_ids/#how-changeuser-works)을 참조하세요. ### 데이터가 Braze에 도달하지 않는 경우 {#data-not-reaching-braze} 데이터가 Braze에 도달하지 않는 경우 방화벽이 Braze API 엔드포인트 및 CDN 공급자에 대한 아웃바운드 트래픽을 허용하는지 확인하세요. 문제가 발생하는 동안 MTR 테스트를 실행하고 [Fastly Debug](https://www.fastly-debug.com/)를 사용하세요. 허용 목록 및 연결 문제 해결에 대한 자세한 내용은 [API 네트워크 연결 문제](https://www.braze.com/docs/ko/ko/api/network_connectivity_issues/)를 참조하세요. # Braze SDK 사용량 제한 Source: /docs/ko/developer_guide/sdk_integration/rate_limits/index.md # Braze SDK 사용량 제한 {#braze-sdk-rate-limits} > Braze SDK의 지능형 클라이언트 측 사용량 제한 기능에 대해 알아보세요. 이 기능은 배터리 수명을 최적화하고, 대역폭 사용량을 줄이며, 안정적인 데이터 전달을 보장합니다. ## SDK 사용량 제한 이해하기 {#understanding-sdk-rate-limits} Braze SDK 사용량 제한은 성능 최적화, 배터리 소모 최소화, 데이터 사용량 감소 및 안정적인 데이터 전달을 보장하기 위해 다음과 같은 기능을 사용합니다: ### 비동기 처리 {#asynchronous-processing} Braze SDK는 사용량 제한을 위해 토큰 버킷 알고리즘을 사용합니다. 이 접근법은 장기적인 속도 제어를 유지하면서 활동의 급증을 허용합니다. 엄격한 대기줄에서 요청을 처리하는 대신, 토큰 버킷은 비동기적으로 작동합니다: - **토큰 생성**: 토큰은 버킷에 일정한 속도로 보충됩니다. - **요청 처리**: 토큰이 사용 가능한 상태에서 도착한 모든 SDK 호출은 다른 호출의 도착 시점과 무관하게 즉시 진행됩니다. - **엄격한 순서 없음**: 요청은 줄을 서서 기다리지 않습니다. 여러 호출이 다음 사용 가능한 토큰을 놓고 경쟁할 수 있습니다. - **버스트 처리**: 요청 시점에 충분한 토큰이 사용 가능한 경우, 짧은 활동 폭발이 허용됩니다. - **속도 제어**: 장기적인 처리량은 꾸준한 토큰 보충 속도에 의해 제한됩니다. 이 비동기적 흐름은 SDK가 예측 가능한 전체 트래픽 수준을 유지하면서 사용 가능한 네트워크 용량에 신속하게 대응할 수 있도록 도와줍니다. ### 적응형 사용량 제한 {#adaptive-rate-limiting} Braze SDK는 네트워크 인프라를 보호하고 최적의 성능을 유지하기 위해 실시간으로 사용량 제한을 조정할 수 있습니다. 이 접근법은: - **과부하 방지**: 네트워크 혼잡을 방지하기 위해 제한을 조정합니다. - **성능 최적화**: 다양한 조건에서도 SDK의 원활한 작동을 유지합니다. - **조건 대응**: 현재 네트워크 및 사용 패턴에 따라 적응합니다. **Note:** 제한은 실시간으로 조정되므로 정확한 버킷 크기와 정적 값은 제공되지 않습니다. 네트워크 상태 및 사용량에 따라 변경될 수 있습니다. ### 네트워킹 최적화 {#networking-optimizations} Braze SDK에는 효율성 향상, 배터리 사용량 감소, 다양한 네트워크 환경 처리를 위한 여러 내장 동작이 포함되어 있습니다: - **자동 배치**: 이벤트를 대기줄에 넣고 효율적인 배치 단위로 전송합니다. - **네트워크 인식 동작**: 연결 품질에 따라 플러시 속도를 조정합니다. - **배터리 최적화**: 라디오 웨이크업 및 네트워크 호출을 최소화합니다. - **점진적 성능 저하**: 네트워크 상태가 좋지 않은 환경에서도 기능을 유지합니다. - **백그라운드/포그라운드 인식**: 앱의 라이프사이클 변화에 따라 동작을 최적화합니다. ## 모범 사례 {#best-practices} 다음 모범 사례를 따라 사용량 제한 문제를 방지하세요: | 이렇게 하세요 | 이렇게 하지 마세요 | | --- | --- | | 의미 있는 사용자 동작과 마일스톤을 추적하세요 | 모든 사소한 상호작용이나 UI 이벤트를 추적하지 마세요 | | 필요한 경우에만 콘텐츠를 새로고침하세요 | 사용자 동작(스크롤 이벤트 등)마다 콘텐츠를 새로고침하지 마세요 | | SDK가 자동으로 배치 처리를 수행하도록 하세요 | 절대적으로 필요한 경우가 아니면 즉시 데이터 전송을 강제하지 마세요 | | 분석에 가치를 더하는 이벤트에 집중하세요 | 빈도를 고려하지 않고 SDK 메서드를 빠르게 연속 호출하지 마세요 | {: .reset-td-br-1 .reset-td-br-2 aria-label="모범 사례" } ## 도움 받기 {#getting-help} SDK 사용량 제한 문제를 겪고 있다면 다음 네트워킹 메서드를 검토하세요: - `requestImmediateDataFlush()` - `requestContentCardsRefresh()` - `refreshFeatureFlags()` - `logCustomEvent()` - `logPurchase()` [Braze 고객지원](https://www.braze.com/docs/ko/ko/user_guide/administer/personal/braze_support/)에 문의할 때는 사용 중인 각 네트워킹 SDK 메서드에 대해 다음 세부 정보를 포함해 주세요: ```plaintext Method name: Frequency: [Describe how often this is called, e.g., at every app launch, once per session] Trigger/context: [Describe what causes it to be called, e.g., button click, scroll event] Code snippet: [Paste the exact code where this method is called, one snippet for each time it is called] Patterns in user flow that may cause bursts or excessive calls: [Describe here] ``` # Braze를 ChatGPT 앱과 통합하기 Source: /docs/ko/developer_guide/sdk_integration/chatgpt_apps/index.md # Braze를 ChatGPT 앱과 통합하기 {#integrate-braze-with-chatgpt-apps} > 이 가이드는 Braze를 ChatGPT 앱과 통합하여 AI 기반 애플리케이션 내에서 분석 및 이벤트 로깅을 활성화하는 방법을 다룹니다. ![ChatGPT 앱에 통합된 콘텐츠 카드.](https://www.braze.com/docs/ko/ko/assets/img/chatgpt_app_integration.png?78e00c2a0ba475aaf2cae479a9a5fa0b){: style="float:right;max-width:30%;border:none;" } ## 개요 {#overview} ChatGPT 앱은 AI 대화형 애플리케이션을 구축하기 위한 강력한 플랫폼을 제공합니다. Braze를 ChatGPT 앱과 통합하면, AI 시대에도 다음과 같은 방법을 포함하여 퍼스트파티 데이터 통제권을 지속적으로 유지할 수 있습니다: - ChatGPT 앱 내에서 사용자 참여도와 행동을 추적합니다(예: 고객이 어떤 질문이나 채팅 기능을 사용하는지 파악). - AI 상호작용 패턴을 기반으로 Braze Campaign을 세그먼트하고 리타겟팅합니다(예: 주당 3회 이상 채팅을 이용한 사용자에게 이메일 발송). ### 주요 이점 {#key-benefits} - **고객 여정을 주도하세요:** 사용자가 ChatGPT를 통해 귀사의 브랜드와 상호작용하는 동안, 귀사는 그들의 행동, 선호도 및 참여 패턴에 대한 가시성을 유지합니다. 이 데이터는 AI 플랫폼의 분석에만 머무르지 않고 Braze 고객 프로필에 직접 반영됩니다. - **크로스 플랫폼 리타겟팅:** ChatGPT 앱 내 사용자 상호작용을 추적하고, AI 사용 패턴을 기반으로 한 개인화된 Campaign을 통해 자사 채널(이메일, SMS, 푸시 알림, 인앱 메시징) 전반에 걸쳐 리타겟팅하세요. - **1:1 프로모션 콘텐츠를 ChatGPT 대화로 전달:** 팀이 앱을 위해 구축한 커스텀 대화형 UI 구성요소를 활용하여 ChatGPT 경험 내에서 Braze [인앱 메시지](https://www.braze.com/docs/ko/ko/user_guide/channels/in_app_messages/), [Content Cards](https://www.braze.com/docs/ko/ko/user_guide/channels/content_cards/) 등을 직접 전달하세요. - **매출 기여도:** ChatGPT 앱 상호작용에서 비롯된 구매 및 전환을 추적합니다. ## 필수 조건 {#prerequisites} Braze를 ChatGPT 앱과 통합하기 전에 다음을 준비해야 합니다: - Braze 워크스페이스에 새로운 웹 앱 및 API 키 - OpenAI 플랫폼에서 생성된 [ChatGPT 앱](https://openai.com/index/introducing-apps-in-chatgpt/) ([OpenAI 샘플 앱](https://github.com/openai/openai-apps-sdk-examples)) # ChatGPT app integration ## Setup ### Step 1: Get the Braze integration file Copy the `braze.js` file from our [ChatGPT apps integration repository](https://github.com/braze-inc/chatgpt-apps-braze-integration/blob/main/src/braze/braze.ts) to your project. This file contains all the necessary Braze SDK configuration and helper functions. ### Step 2: Install dependencies Install our Web SDK for Braze's most up-to-date set of features: **For client-side integration:** ```bash npm install @braze/web-sdk ``` ## Implementation There are two ways to integrate Braze with your ChatGPT app depending on your use case: ### Client-side integration (custom widgets) **Tip:** **Recommended Approach:** This method enables rich messaging experiences and real-time user interaction tracking within your ChatGPT app widgets. For displaying Braze messaging and tracking user interactions within your custom ChatGPT app widgets, use the Web SDK integration. A full messaging example can be found in our sample repository [here](https://github.com/braze-inc/chatgpt-apps-braze-integration/tree/main/src/inbox). #### Configure widget metadata Add the following metadata to your MCP server file to allow Braze domains, ensuring to update the CDN domain based on [your region](https://www.braze.com/docs/ko/ko/developer_guide/platforms/web/content_security_policy): ```javascript "openai/widgetCSP": { connect_domains: ["https://YOUR-SDK-ENDPOINT"], resource_domains: [ "https://appboy-images.com", "https://braze-images.com", "https://cdn.braze.eu", "https://use.fontawesome.com" ], } ``` Replace `YOUR-SDK-ENDPOINT` with your actual Braze SDK endpoint. #### Set up the useBraze hook ```javascript import { useBraze } from "./utils/braze"; function YourWidget() { const braze = useBraze({ apiKey: "your-braze-api-key", baseUrl: "your-braze-endpoint.braze.com", }); useEffect(() => { if (!braze.isInitialized) { return; } // Set user identity braze.changeUser("user-id-123"); // Log widget interactions braze.logCustomEvent("viewed_pizzaz_list"); }, [braze.isInitialized]); return ( // Your widget JSX ); } ``` #### Display Braze Content Cards ```javascript const [cards, setCards] = useState([]); useEffect(() => { // Get cached content cards setCards(braze.getCachedContentCards()?.cards ?? []); // Subscribe to content card updates braze.subscribeToContentCardsUpdates((contentCards) => { setCards(contentCards.cards); }); // Open session braze.openSession(); return () => { braze.removeAllSubscriptions(); } }, []); ``` #### Track widget events ```javascript // Track user interactions within your widget const handleButtonClick = () => { braze.logCustomEvent("widget_button_clicked", { button_type: "save_list", widget_name: "pizza_list" }); }; const handleItemInteraction = (itemId) => { braze.logCustomEvent("item_interacted", { item_id: itemId, interaction_type: "view_details" }); }; ``` ### Server-side integration (MCP server) If you also need a server-side integration for messaging functionality on your MCP server, contact `mcp-product@braze.com`. For tracking events and purchases from your MCP server, use our [REST API](https://www.braze.com/docs/ko/ko/api/home). # Braze SDK 버전 관리 정보 Source: /docs/ko/developer_guide/sdk_integration/version_management/index.md # 버전 관리 정보 {#about-version-management} > 앱이 최신 기능과 품질 개선 사항을 반영하여 최신 상태를 유지할 수 있도록 Braze SDK의 버전 관리에 대해 알아보세요. 이전 버전의 SDK는 최신 패치, 버그 수정 또는 고객지원을 받지 못할 수 있으므로, 지속적인 개발 라이프사이클의 일부로 항상 최신 상태를 유지하는 것이 좋습니다. ## 버전 관리 권장 사항 {#versioning-recommendations} 모든 Braze SDK는 [시맨틱 버전 관리 사양(SemVer)](https://semver.org/)을 준수하므로, 버전 번호 `MAJOR.MINOR.PATCH`가 주어지면 다음을 권장합니다: | 버전 | 이 버전 정보 | 권장 사항 | |-------|------------------|--------------| | `PATCH` | 업데이트는 항상 하위 호환성을 유지하며, 중요한 버그 수정이 포함됩니다. 항상 안전합니다. | 현재 사용 중인 메이저 및 마이너 버전의 최신 패치 버전으로 항상 즉시 업데이트해야 합니다. | | `MINOR` | 업데이트는 항상 하위 호환성을 유지하며, 새로운 기능을 포함합니다. 애플리케이션 코드를 변경할 필요가 없습니다. | 즉시 수행할 필요는 없지만, 현재 사용 중인 메이저 버전의 최신 마이너 버전으로 가능한 한 빨리 업데이트해야 합니다. | `MAJOR` | 업데이트는 호환성을 깨뜨리는 변경 사항이며, 애플리케이션 코드를 변경해야 할 수 있습니다. | 코드 변경이 필요할 수 있으므로, 팀에 가장 적합한 일정에 맞춰 최신 메이저 버전으로 업데이트하세요. | {: .reset-td-br-1 .reset-td-br-2 .reset-td-br-3 aria-label="Versioning recommendations" } **Note:** 때때로 새로운 Android 또는 Apple OS 업데이트로 인해 Braze SDK를 변경해야 하는 경우가 있습니다. 앱이 최신 기기와 호환되도록 하려면 SDK를 최신 상태로 유지하는 것이 중요합니다. ## 새 릴리스 알림 받기 {#getting-notified-of-new-releases} 새 SDK 버전이 릴리스될 때 자동으로 알림을 받으려면 Braze SDK의 GitHub 리포지토리를 구독하면 됩니다: 1. SDK의 GitHub 리포지토리로 이동합니다(예: [braze-android-sdk](https://github.com/braze-inc/braze-android-sdk), [braze-swift-sdk](https://github.com/braze-inc/braze-swift-sdk) 또는 [braze-web-sdk](https://github.com/braze-inc/braze-web-sdk)). 2. 오른쪽 상단의 **Watch**를 클릭합니다. 3. **Custom**을 클릭한 다음 **Releases**를 선택하고 **Apply**를 클릭합니다. 새 릴리스가 게시될 때마다 GitHub 알림(및 [알림 설정](https://github.com/settings/notifications)에 따라 이메일)을 받게 됩니다. SDK 리포지토리의 전체 목록은 [참조, 리포지토리 및 샘플 앱](https://www.braze.com/docs/ko/ko/developer_guide/references/)을 확인하세요. ## 알려진 문제 정보 {#about-known-issues} 변경 사항으로 인해 빌드 파이프라인이 손상되지 않도록 하기 위해, 특정 릴리스에 알려진 문제가 있더라도 **배포 시스템에 게시된 후에는 릴리스를 변경하거나 제거하지 않습니다**—. 이러한 경우 [Braze SDK 체인지로그](https://www.braze.com/docs/ko/ko/developer_guide/changelogs/)에 해당 문제를 문서화한 다음, 영향을 받는 메이저 또는 마이너 버전에 대한 새 패치를 최대한 빨리 릴리스합니다. # Android 13 업그레이드 가이드 Source: /docs/ko/developer_guide/platforms/android/android_13/index.md # Upgrading to Android 13 > This guide describes relevant changes introduced in Android 13 (2022) and the required upgrade steps for your Braze Android SDK integration. Refer to the [Android 13 developer documentation](https://developer.android.com/about/versions/13) for a full migration guide. ## Android 13 Braze SDK To prepare for Android 13, please upgrade your Braze SDK to the [latest version (v21.0.0+)](https://github.com/braze-inc/braze-android-sdk/blob/master/CHANGELOG.md#2300). Doing so will give you access to our new ["no-code" push primer feature](https://www.braze.com/docs/ko/ko/user_guide/message_building_by_channel/push/best_practices/push_primer_messages/). ## Changes in Android 13 ### Push permission {#push-permission} Android 13 introduces a [major change](https://developer.android.com/about/versions/13/changes/notification-permission) in how users manage apps that send push notifications. In Android 13, apps are required to obtain permission before push notifications can be shown. ![An Android push message asking "Allow Kitchenerie to send you notifications?" with two buttons "Allow" and "Don't allow" at the bottom of the message.](https://www.braze.com/docs/ko/ko/assets/img/android/android-13-push-prompt.png?aa824eb39eb4947698a29f41affb97dc){: style="float:right;max-width:430px;width:50%;margin-left:15px;border:0"} This new permission follows a similar pattern to iOS and Web push, where you only have one attempt to obtain permission. If a user chooses `Don't Allow` or dismisses the prompt, your app cannot ask for permission again. Note that apps are granted an [exemption](https://developer.android.com/about/versions/13/changes/notification-permission#eligibility) for users who previously had push notifications enabled prior to updating to Android 13. These users [will remain eligible](https://developer.android.com/about/versions/13/changes/notification-permission#existing-apps) to receive push when they update to Android 13 without having to request permission. #### Permission prompt timing {#push-permission-timing} **Targeting Android 13** Apps targeting Android 13 can control when to request permission and show the native push prompt. If your user upgrades from Android 12 to 13, your app was previously installed, and you were already sending push, the system automatically pre-grants the new notification permission to all eligible apps. In other words, these apps can continue to send notifications to users, and users don't see a runtime permission prompt. For more details on this see Android's Developer Documentation for [effects on updates to existing apps](https://developer.android.com/about/versions/13/changes/notification-permission#existing-apps). **Targeting Android 12 or earlier** If your app does not yet target Android 13, then a new user on Android 13 installs your app, they will automatically see a push permission prompt when your app creates its first notification channel (via `notificationManager.createNotificationChannel`). Users who already have your app installed and then upgrade to Android 13 are never shown a prompt and are automatically granted push permission. **Note:** Braze SDK v23.0.0 automatically creates a default notification channel if one does not already exist when a push notification is received. If you don't target Android 13, this will cause the push permission prompt to be shown, which is required to show the notification. ## Preparing for Android 13 {#next-steps} It is strongly recommended that your app targets Android 13 in order to control when users are prompted for push permission. This will allow you to optimize your [push opt-in rates](https://www.braze.com/resources/articles/android-13-developer-preview-push-opt-ins-arrive-for-android-apps) by prompting users at more appropriate times and will lead to a better user experience in how and when your app asks for push permission. To start using our new ["no-code" push primer feature](https://www.braze.com/docs/ko/ko/user_guide/message_building_by_channel/push/best_practices/push_primer_messages/), upgrade your Android SDK to the [latest version (v23.0.0+)](https://github.com/braze-inc/braze-android-sdk/blob/master/CHANGELOG.md#2300). # iOS 18로 업그레이드 Source: /docs/ko/developer_guide/platforms/swift/ios_18/index.md # iOS 18로 업그레이드 {#upgrading-to-ios-18} > Braze가 곧 출시될 iOS 버전을 어떻게 준비하고 있는지 궁금하신가요? 이 문서에서는 여러분과 사용자에게 원활한 경험을 제공하는 데 도움이 되는 iOS 18 릴리스에 대한 인사이트를 요약합니다. Apple의 [WWDC](https://developer.apple.com/wwdc24/)는 2024년 6월 9일부터 11일까지 진행되었습니다. [블로그 게시물](https://www.braze.com/resources/articles/wwdc-announcements-bring-apple-intelligence-rcs-and-more-to-ios-18)에서 발표 내용을 자세히 알아보거나, Braze에서 iOS 18을 활용하는 방법을 읽어보세요. ## iOS 18의 변경 사항 {#changes-in-ios-18} ### Apple Watch의 라이브 활동 {#live-activities-on-apple-watch} [라이브 활동](https://www.braze.com/docs/ko/ko/developer_guide/push_notifications/live_notifications/?sdktab=swift)은 watchOS 11에서 지원됩니다. 추가 설정은 필요하지 않습니다. 다만 Apple은 시계 인터페이스를 커스텀할 수 있는 옵션을 제공합니다. ### Apple Vision Pro 현재 중국, 일본, 싱가포르, 호주, 캐나다, 프랑스, 독일, 영국에서 Vision Pro를 사용할 수 있습니다. [Braze가 visionOS를 지원하는 방법](https://www.braze.com/resources/articles/building-braze-a-new-era-of-customer-engagement-braze-announces-visionos-support)은 블로그에서 확인하세요. ### macOS의 iPhone 알림 {#iphone-notifications-on-macos} Apple의 새로운 [iPhone 미러링](https://www.apple.com/newsroom/2024/06/macos-sequoia-takes-productivity-and-intelligence-on-mac-to-new-heights/) 기능을 사용하면 macOS 기기에서 iPhone 알림을 받을 수 있습니다. Push Story 이미지 및 GIF와 같은 일부 미디어 유형은 macOS 알림으로 렌더링할 수 없으므로 지원되지 않는다는 점에 유의하세요. ### Apple Intelligence [Apple Intelligence](https://developer.apple.com/documentation/Updates/Apple-Intelligence)는 이제 iOS 18.1 이상을 실행하는 기기에서 사용할 수 있습니다. Braze 사용자로서 가장 중요하게 알아야 할 새로운 기능은 [알림 요약](https://support.apple.com/en-us/108781)입니다. 이 기능은 온디바이스 처리를 사용하여 단일 앱에서 전송된 관련 푸시 알림을 자동으로 그룹화하고 텍스트 요약을 생성합니다. 최종 사용자는 요약을 탭하여 확장하고 원래 전송된 각 푸시 알림을 볼 수 있습니다. 이러한 요약이 생성되는 방식 때문에 특정 동작이나 생성된 텍스트를 제어할 수는 없습니다. 그러나 이는 푸시 클릭 추적과 같은 분석 또는 보고 기능에 영향을 미치지 않습니다. ![푸시 알림 미리보기 요약의 샘플 스크린샷.](https://www.braze.com/docs/ko/ko/assets/img/apple/apple_intelligence/notification_preview_summary.png?1b8357db05ac28fe35dad65c78525987) # visionOS 지원 Source: /docs/ko/developer_guide/platforms/swift/visionos/index.md # visionOS 지원 > [Braze Swift SDK 8.0.0](https://github.com/braze-inc/braze-swift-sdk/blob/main/CHANGELOG.md#800)부터 Apple Viwion Pro용 공간 컴퓨팅 플랫폼인 [visonOS](https://developer.apple.com/visionos/)에서 Braze를 활용할 수 있습니다. Braze를 사용하는 visonOS 앱 샘플은 [샘플 앱](https://www.braze.com/docs/ko/ko/developer_guide/references/?tab=swift)을 참조하세요. ## 완벽하게 지원되는 기능 iOS에서 사용할 수 있는 대부분의 기능은 visonOS에서도 사용할 수 있습니다. - 애널리틱스(세션, 사용자 지정 이벤트, 구매 등) - 인앱 메시징(데이터 모델 및 UI) - 콘텐츠 카드(데이터 모델 및 UI) - 푸시 알림(사용자가 볼 수 있는 동작 버튼 및 무음 알림) - 피처 플래그 - 위치 분석 ## 부분적으로 지원되는 기능 일부 기능이 visionOS에서 부분적으로만 지원되지만, 앞으로 Apple에서 이러한 문제를 해결할 가능성이 큽니다. - 풍부한 푸시 알림 - 이미지가 지원됩니다. - GIF 및 동영상은 미리보기 이미지로 표시되지만 재생할 수는 없습니다. - 오디오 재생은 지원되지 않습니다. - 푸시 스토리 - 푸시 스토리 페이지 스크롤 및 선택이 지원됩니다. - **다음을** 사용하여 푸시 스토리 페이지 간에 이동하는 기능은 지원되지 않습니다. ## 지원되지 않는 기능 - 지오펜스 모니터링은 지원되지 않습니다. Apple은 visionOS에서 지역 모니터링을 위한 핵심 위치 API를 제공하지 않습니다. - 라이브 활동은 지원되지 않습니다. 현재 ActivityKit는 iOS 및 iPadOS에서만 사용할 수 있습니다. # iOS 14 SDK 업그레이드 가이드 Source: /docs/ko/developer_guide/platforms/swift/_archived_updates/ios_14/index.md # iOS 14 SDK 업그레이드 가이드 {#ios-14-sdk-upgrade-guide} > 이 가이드는 iOS 14에서 도입된 Braze 관련 변경 사항과 Braze iOS SDK 통합을 위한 필수 업그레이드 단계를 설명합니다. 새로운 iOS 14 업데이트의 전체 목록은 Apple의 [iOS 14 페이지](https://www.apple.com/ios/ios-14/)를 참조하세요. **Tip:** iOS 14.5부터 **IDFA** 수집 및 [특정 데이터 공유](https://developer.apple.com/app-store/user-privacy-and-data-use/#permission-to-track)에는 새로운 [AppTrackingTransparency](https://developer.apple.com/documentation/apptrackingtransparency) 프레임워크 권한 프롬프트가 필요합니다([자세히 알아보기](#idfa)). #### iOS 14 주요 변경 사항 요약 {#summary-of-ios-14-breaking-changes} - iOS 14/Xcode 12를 대상으로 하는 앱은 [공식 iOS 14 릴리스](https://github.com/Appboy/appboy-ios-sdk/releases/tag/3.27.0)를 사용해야 합니다. - 새로운 _대략적인 위치_ 권한을 선택한 사용자에 대해 지오펜스는 [더 이상 iOS에서 지원되지 않습니다](https://developer.apple.com/documentation/corelocation/cllocationmanager/3600215-accuracyauthorization). - "마지막으로 알려진 위치" 타겟팅 기능을 사용하려면 _대략적인 위치_ 권한과의 호환성을 위해 Braze iOS SDK v3.26.1 이상으로 업그레이드해야 합니다. Xcode 12를 사용하는 경우 v3.27.0 이상으로 업그레이드해야 합니다. - iOS 14.5부터 IDFA 수집 및 [특정 데이터 공유](https://developer.apple.com/app-store/user-privacy-and-data-use/#permission-to-track)에는 새로운 [AppTrackingTransparency](https://developer.apple.com/documentation/apptrackingtransparency) 프레임워크 권한 프롬프트가 필요합니다. - Campaign 타겟팅 또는 분석에 "광고 추적 활성화됨" 필드를 사용하는 경우, 사용자의 옵트인 상태를 보고하려면 Xcode 12로 업그레이드하고 새로운 AppTrackingTransparency 프레임워크를 사용해야 합니다. ## 업그레이드 요약 {#upgrade-summary} | 앱에서 사용하는 항목: | 업그레이드 권장 사항 | 설명 | |------|--------|---| | Xcode 12 | **iOS SDK v3.27 이상으로 업그레이드** | Xcode 12를 사용하는 고객은 호환성을 위해 v3.27.0 이상을 사용해야 합니다. iOS 14 호환성과 관련된 문제나 질문이 있는 경우 새 [GitHub 이슈](https://github.com/Appboy/appboy-ios-sdk/issues)를 개설하세요. | | 가장 최근 위치 | **iOS SDK v3.26.1 이상으로 업그레이드** | 최근 위치 타겟팅 기능을 사용하고 Xcode 11을 계속 사용하는 경우 새로운 _대략적인 위치_ 기능을 지원하는 iOS SDK v3.26.1 이상으로 업그레이드해야 합니다. 사용자가 iOS 14로 업그레이드하고 _또한_ 대략적인 위치를 선택하면 이전 SDK는 위치를 안정적으로 수집할 수 없습니다.

앱이 iOS 14를 대상으로 하지 않더라도 사용자가 iOS 14로 업그레이드하면 새로운 위치 정확도 옵션을 사용할 수 있습니다. iOS SDK v3.26.1 이상으로 업그레이드하지 않은 앱은 사용자가 iOS 14 기기에서 _대략적인 위치_를 제공할 때 위치 속성을 안정적으로 수집할 수 없습니다. | | IDFA 광고 추적 ID | **Xcode 12 및 iOS SDK v3.27로 업그레이드해야 할 수 있음** | 2021년에 Apple은 IDFA 수집을 위한 권한 프롬프트를 요구하기 시작할 예정이었습니다. 해당 시점에 IDFA를 계속 수집하려면 앱을 Xcode 12로 업그레이드하고 새로운 `AppTrackingTransparency` 프레임워크를 사용해야 합니다. IDFA를 Braze SDK에 전달하는 경우에도 v3.27.0 이상으로 업그레이드해야 합니다.

새로운 iOS 14 API를 사용하지 않는 앱은 IDFA를 수집할 수 없으며, 2021년 Apple이 이 변경 사항을 시행하기 시작한 후에는 대신 빈 ID(`00000000-0000-0000-0000-000000000000`)를 수집하게 됩니다. 앱에 이 변경 사항이 적용되는지 여부에 대한 자세한 내용은 [IDFA 세부 정보](#idfa)를 참조하세요. | {: .reset-td-br-1 .reset-td-br-2 .reset-td-br-3 aria-label="Upgrade summary" } ## iOS 14 동작 변경 사항 {#ios-14-behavior-changes} ### 대략적인 위치 권한 {#approximate-location-permission} ![Precise Location](https://www.braze.com/docs/ko/ko/assets/img/ios/ios14-approximate-location.png?762985d2d9f36f73a39b9b49bb1c4884){: style="float:right;max-width:45%;margin-left:15px;"} #### 개요 {#overview} 위치 권한을 요청할 때 사용자는 _정확한 위치_(이전 동작)를 제공하거나 새로운 _대략적인 위치_를 제공할 수 있습니다. 대략적인 위치는 정확한 좌표 대신 사용자가 위치한 더 큰 반경을 반환합니다. #### 지오펜스 {#geofences} 새로운 _대략적인 위치_ 권한을 선택한 사용자에 대해 지오펜스는 [더 이상 iOS에서 지원되지 않습니다](https://developer.apple.com/documentation/corelocation/cllocationmanager/3600215-accuracyauthorization). Braze SDK 통합에는 업데이트가 필요하지 않지만, 지오펜스에 의존하는 Campaign의 경우 [위치 기반 마케팅 전략](https://www.braze.com/blog/geofencing-geo-targeting-beaconing-when-to-use/)을 조정해야 할 수 있습니다. #### 위치 타겟팅 {#location-tracking} _대략적인 위치_가 부여된 상태에서 사용자의 _마지막으로 알려진 위치_를 계속 수집하려면 앱을 Braze iOS SDK v3.26.1 이상으로 업그레이드해야 합니다. 위치는 정확도가 떨어질 수 있으며, 테스트 결과 12,000미터(7마일 이상) 이상에서 측정되었다는 점에 유의하세요. Braze 대시보드에서 _마지막으로 알려진 위치_ 타겟팅 옵션을 사용할 때는 새로운 _대략적인 위치_를 고려할 수 있도록 위치의 반경을 늘려야 합니다(반경 1마일/1.6km 이상을 권장합니다). Braze iOS SDK를 v3.26.1 이상으로 업그레이드하지 않은 앱은 iOS 14 기기에서 _대략적인 위치_가 부여될 때 더 이상 위치 추적을 사용할 수 없습니다. 이미 위치 액세스 권한을 부여한 사용자는 업그레이드 후에도 계속해서 _정확한 위치_를 제공합니다. Xcode 12를 사용하는 경우 v3.27.0 이상으로 업그레이드해야 합니다. 대략적인 위치에 대한 자세한 내용은 Apple의 [What's New In Location](https://developer.apple.com/videos/play/wwdc2020/10660/) WWDC 비디오를 참조하세요. ### IDFA 및 앱 추적 투명성 {#idfa} #### 개요 IDFA(광고주 식별자)는 광고 및 기여도 파트너가 교차 기기 추적을 위해 사용할 수 있도록 Apple에서 제공하는 식별자로, 개인의 Apple ID에 연결됩니다. iOS 14.5부터 IDFA에 대한 명시적인 사용자 동의를 수집하기 위해 새로운 권한 프롬프트(새로운 `AppTrackingTransparency` 프레임워크에서 실행됨)가 표시되어야 합니다. "다른 회사가 소유한 앱과 웹사이트에서 사용자를 추적"하기 위한 이 권한 프롬프트는 사용자에게 위치를 요청하는 것과 유사한 방식으로 요청됩니다. 사용자가 프롬프트에 동의하지 않거나 Xcode 12의 `AppTrackingTransparency` 프레임워크로 업그레이드하지 않으면 빈 IDFA 값(`00000000-0000-0000-0000-000000000000`)이 반환되고 앱은 사용자에게 다시 프롬프트를 표시할 수 없습니다. **Important:** 이러한 IDFA 업데이트는 최종 사용자가 기기를 iOS 14.5로 업그레이드한 후에 적용됩니다. IDFA를 수집하려는 경우 앱이 Xcode 12에서 새로운 `AppTransparencyFramework`를 사용해야 합니다. #### Braze IDFA 수집 변경 사항 {#changes-to-braze-idfa-collection} ![IDFA](https://www.braze.com/docs/ko/ko/assets/img/ios/ios14-idfa.png?d088f91fda2277b22f9b32cdec6cbb5a){: style="float:right;max-width:25%;margin-left:15px;border:0"} 1. Braze를 통해 앱은 계속해서 사용자의 IDFA 값을 Braze SDK_에_ 제공할 수 있습니다. 2. 선택적 자동 IDFA 수집에서 조건부로 컴파일되는 `ABK_ENABLE_IDFA_COLLECTION` 컴파일 매크로는 iOS 14에서 더 이상 작동하지 않으며 3.27.0에서 제거되었습니다. 3. Campaign 타겟팅 또는 분석에 "광고 추적 활성화됨" 필드를 사용하는 경우, 사용자의 옵트인 상태를 보고하려면 Xcode 12로 업그레이드하고 새로운 AppTrackingTransparency 프레임워크를 사용해야 합니다. 이러한 변경의 이유는 iOS 14에서 이전 [`advertisingTrackingEnabled`](https://developer.apple.com/documentation/adsupport/asidentifiermanager/1614148-advertisingtrackingenabled) 필드가 항상 No를 반환하기 때문입니다. 4. 앱에서 IDFA 또는 IDFV를 Braze 외부 ID로 사용했다면 이러한 식별자 대신 UUID로 마이그레이션할 것을 적극 권장합니다. 외부 ID 마이그레이션에 대한 자세한 내용은 [외부 ID 마이그레이션 API 엔드포인트](https://www.braze.com/docs/ko/ko/api/endpoints/user_data/external_id_migration/)를 참조하세요. Apple의 [개인정보 보호 업데이트](https://developer.apple.com/app-store/user-privacy-and-data-use/) 및 새로운 [앱 추적 투명성 프레임워크](https://developer.apple.com/documentation/apptrackingtransparency)에 대해 자세히 알아보세요. ### 푸시 권한 부여 {#push-provisional-auth} **Important:** iOS 14에는 임시 푸시 권한 부여에 대한 변경 사항이 포함되어 있지 않습니다. iOS 14의 이전 베타 버전에서 Apple은 변경 사항을 도입했지만 이후에 이전 동작으로 되돌렸습니다. ## iOS 14의 새로운 기능 {#ios-14-new-features} ### 앱 개인정보 보호 및 데이터 수집 개요 {#app-privacy} 2020년 12월 8일부터 App Store에 제출하는 모든 앱은 [Apple의 새로운 앱 개인정보 보호 표준](https://developer.apple.com/app-store/app-privacy-details/)을 준수하기 위해 추가 단계를 거쳐야 합니다. #### Apple 개발자 포털 설문지 {#apple-developer-portal-questionnaire} _Apple 개발자 포털_에서: * 앱 또는 서드파티 파트너가 데이터를 수집하는 방법을 설명하기 위해 설문지를 작성하라는 메시지가 표시됩니다. * 설문지는 항상 App Store의 최근 릴리스에 따라 최신 상태로 유지되어야 합니다. * 설문지는 새로운 앱을 제출하지 않아도 업데이트될 수 있습니다. * 앱의 개인정보처리방침 URL 링크를 붙여넣어야 합니다. 설문지를 작성할 때 법무팀에 문의하여 다음 항목에 대한 Braze의 사용이 공개 요건에 어떤 영향을 미칠 수 있는지 고려하세요. #### Braze 기본 데이터 수집 {#braze-default-data-collection} **식별자** - 익명의 기기 식별자는 항상 Braze SDK에 의해 수집됩니다. 현재 기기 IDFV(벤더 식별자)로 설정되어 있습니다. **사용 데이터** - 여기에는 제품 상호작용을 측정하는 데 사용하는 이벤트 또는 속성 수집뿐만 아니라 Braze 세션 데이터도 포함될 수 있습니다. #### 선택적 데이터 수집 {#optional-data-collection} Braze 사용을 통해 선택적으로 수집할 수 있는 데이터: **위치** - 대략적인 위치와 정확한 위치는 모두 Braze SDK에서 선택적으로 수집할 수 있습니다. 이러한 기능은 기본적으로 비활성화되어 있습니다. **연락처 정보** - 여기에는 사용자의 신원과 관련된 이벤트 및 속성이 포함될 수 있습니다. **구매** - 여기에는 사용자를 대신하여 기록된 이벤트 및 구매가 포함될 수 있습니다. **Important:** 이 목록은 전체 목록이 아닙니다. 앱 개인정보 보호 설문지의 다른 카테고리에 해당하는 사용자에 대한 다른 정보를 Braze에서 수동으로 수집하는 경우, 해당 정보도 공개해야 합니다. 이 기능에 대해 자세히 알아보려면 [Apple의 개인정보 보호 및 데이터 사용](https://developer.apple.com/app-store/user-privacy-and-data-use/)을 참조하세요. # iOS 15 SDK 업그레이드 가이드 Source: /docs/ko/developer_guide/platforms/swift/_archived_updates/ios_15/index.md # iOS 15 SDK 업그레이드 가이드 {#ios-15-sdk-upgrade-guide} > 이 가이드에서는 iOS 15(WWDC21)에 도입된 변경 사항과 Braze iOS SDK 통합에 필요한 업그레이드 단계를 간략하게 설명합니다. 새로운 iOS 15 업데이트의 전체 목록은 Apple의 [iOS 15 릴리스 정보](https://developer.apple.com/documentation/ios-ipados-release-notes/ios-ipados-15-release-notes)를 참조하세요. ## UI 내비게이션의 투명성 변경 사항 {#transparency-changes-to-ui-navigations} 매년 iOS 베타를 테스트하는 과정에서 특정 UI 내비게이션 바가 불투명하지 않고 투명하게 표시되는 Apple의 변경 사항을 확인했습니다. 이 현상은 Content Cards에 Braze 기본 UI를 사용하거나 웹 딥링크가 별도의 브라우저 앱이 아닌 앱 내에서 열릴 때 iOS 15에서 나타납니다. iOS 15에서 이러한 시각적 변경을 방지하려면 사용자가 새로운 iOS 15 운영체제로 업그레이드하기 전에 가능한 한 빨리 [Braze iOS SDK v4.3.2](https://github.com/Appboy/appboy-ios-sdk/releases/tag/4.3.2)로 업그레이드할 것을 적극 권장합니다. ## 새로운 알림 설정 {#notification-settings} iOS 15에는 사용자가 하루 종일 집중하고 자주 방해받지 않도록 도와주는 새로운 알림 기능이 도입되었습니다. 이러한 새로운 기능에 대한 지원을 제공하게 되어 기쁩니다. 이 기능은 추가적인 SDK 업그레이드가 필요하지 않으며 iOS 15 기기를 사용하는 사용자에게만 적용됩니다. ### 집중 모드 {#focus-mode} 이제 iOS 15 사용자는 "집중 모드"를 생성할 수 있습니다. 이는 어떤 알림이 집중 상태를 뚫고 눈에 띄게 표시될지 결정하는 데 사용되는 커스텀 프로필입니다. ![](https://www.braze.com/docs/ko/ko/assets/img/ios/ios15-notification-settings.png?821058a3051c6ec90473df1553398091){: style="float:right;max-width:25%;margin-left:15px;border:0"} ### 방해 수준 {#interruption-levels} iOS 15에서는 푸시 알림을 네 가지 방해 수준 중 하나로 보낼 수 있습니다. * **수동**(신규) - 소리 없음, 진동 없음, 화면 깨우기 없음, 집중 설정 해제 없음. * **활성**(기본값) - 소리, 진동, 화면 깨우기 허용, 집중 설정 해제 없음. * **시간 긴급**(신규) - 소리, 진동, 화면 깨우기 허용, 허용된 경우 시스템 제어를 해제할 수 있음. * **중요** - 소리, 진동, 화면 깨우기 허용, 시스템 제어를 해제할 수 있으며 벨소리 스위치를 우회할 수 있음. iOS 푸시에서 이 옵션을 설정하는 방법에 대해 자세히 알아보려면 [iOS 알림 옵션](https://www.braze.com/docs/ko/ko/user_guide/message_building_by_channel/push/ios/notification_options/#interruption-level)을 참조하세요. ### 알림 요약 {#notification-summary} ![](https://www.braze.com/docs/ko/ko/assets/img/ios/ios15-notification-summary.png?2fa3879f3f2600c850c6911da84c13a1){: style="float:right;max-width:25%;margin-left:15px;border:0"} iOS 15에서는 사용자가 선택적으로 하루 중 특정 시간을 선택하여 알림 요약을 받을 수 있습니다. 즉각적인 주의가 필요하지 않은 알림(예: "수동"으로 전송되거나 사용자가 집중 모드에 있을 때 전송되는 알림)은 하루 종일 계속 방해받지 않도록 그룹화됩니다. 보내는 각 알림에 대해 곧 "관련성 점수"를 지정하여 요약 상단에 표시할 알림을 제어할 수 있습니다. 알림의 "관련성 점수"를 설정하는 방법에 대해 자세히 알아보려면 [iOS 알림 옵션](https://www.braze.com/docs/ko/ko/user_guide/message_building_by_channel/push/ios/notification_options/#relevance-score)을 참조하세요. ## 위치 버튼 {#location-buttons} iOS 15에서는 사용자가 앱 내에서 일시적으로 위치 액세스 권한을 부여할 수 있는 편리한 새로운 방법이 도입되었습니다. 새로운 위치 버튼은 동일한 세션에서 여러 번 클릭하는 사용자에게 반복적으로 메시지를 표시하지 않고 기존의 "한 번만 허용" 권한을 기반으로 합니다. 자세한 내용은 올해 세계 개발자 컨퍼런스(WWDC)에서 Apple의 [위치 버튼 만나기](https://developer.apple.com/videos/play/wwdc2021/10102/) 동영상을 시청하세요. **Tip:** 이 기능을 사용하면 사용자에게 권한을 요청할 수 있는 추가 기회를 얻을 수 있습니다! iOS 15 이전에 위치 권한을 거부한 적이 있는 사용자가 위치 버튼을 클릭하면 마지막으로 거부된 상태에서 권한을 재설정할 수 있는 프롬프트가 표시됩니다. ### Braze에서 위치 버튼 사용하기 {#using-location-buttons-with-braze} Braze에서 위치 버튼을 사용할 때는 추가 통합이 필요하지 않습니다. 사용자가 권한을 부여한 후에는 앱이 평소와 같이 사용자 위치를 계속 전달해야 합니다. Apple에 따르면, 이미 백그라운드 위치 액세스를 공유한 사용자의 경우 "앱 사용 중" 옵션은 iOS 15로 업그레이드한 후에도 해당 수준의 권한을 계속 부여합니다. ## Apple 메일 {#mail} 올해 Apple은 이메일 추적 및 개인정보 보호에 대한 많은 업데이트를 발표했습니다. 자세한 내용은 [블로그 게시물](https://www.braze.com/resources/articles/9-ways-email-marketers-can-respond-to-apples-mail-privacy-protection-feature)을 참조하세요. ## Safari IP 주소 위치 {#safari-ip-address-location} iOS 15에서는 사용자가 자신의 IP 주소에서 확인된 위치를 익명화하거나 일반화하도록 Safari를 구성할 수 있습니다. 위치 기반 타겟팅 또는 세분화를 사용할 때는 이 점을 염두에 두세요. # iOS 16 업그레이드 가이드 Source: /docs/ko/developer_guide/platforms/swift/_archived_updates/ios_16/index.md # iOS 16 SDK 업그레이드 가이드 {#ios-16-sdk-upgrade-guide} > 이 가이드에서는 iOS 16(2022)에 도입된 관련 변경 사항과 Braze iOS SDK 통합에 미치는 영향을 설명합니다. 전체 마이그레이션 가이드는 [iOS 16 릴리스 노트](https://developer.apple.com/documentation/ios-ipados-release-notes/ios-ipados-16-release-notes)를 참조하세요. ## iOS 16의 변경 사항 {#changes-in-ios-16} ### Safari 웹 푸시 {#safari-web-push} Apple은 웹 푸시 기능에 대한 두 가지 변경 사항을 발표했습니다. #### 데스크탑 웹 푸시(MacOS) {#macos-push} 이전에 Apple은 자체 Safari 푸시 API를 사용하여 macOS(데스크탑)에서 푸시 알림을 지원했습니다. macOS Ventura(2022년 10월 24일 출시)부터 [Safari는 웹 푸시 API에 대한 지원을 추가](https://webkit.org/blog/12824/news-from-wwdc-webkit-features-in-safari-16-beta/#web-push-for-macos)했습니다(Safari 푸시 외). 이는 다른 인기 브라우저에서 사용되는 기존의 크로스 브라우저 API 표준입니다. 이미 Braze를 통해 Safari용 웹 푸시를 발송하고 있다면 변경할 필요가 없습니다. #### 모바일 웹 푸시(iOS 및 iPadOS) {#ios-push} 이전에는 iPhone 및 iPad의 Safari에서 푸시 알림 수신을 지원하지 않았습니다. 2023년에 Apple은 Safari를 통해 iPhone 및 iPad 기기에서 웹 푸시 지원을 추가할 예정입니다. Braze는 추가 변경이나 업그레이드 없이 이 새로운 iOS 및 iPadOS 웹 푸시를 지원합니다. ## iOS 16 준비 {#next-steps} iOS 16에서 Braze iOS SDK를 업그레이드할 필요는 없지만, 두 가지 흥미로운 업데이트가 있습니다. 1. Braze에서 [새로운 Swift SDK](https://github.com/braze-inc/braze-swift-sdk)를 출시했습니다. 향상된 성능, 새로운 기능 및 다양한 개선 사항을 제공합니다. 2. Braze Swift SDK는 새로운 ["노코드" 푸시 프라이머 기능](https://www.braze.com/docs/ko/ko/user_guide/channels/push/best_practices/push_primer_messages/)을 지원합니다! # iOS 17 업그레이드 가이드 Source: /docs/ko/developer_guide/platforms/swift/_archived_updates/ios_17/index.md # iOS 17 업그레이드 가이드 > Braze가 곧 출시될 iOS 버전을 어떻게 준비하고 있는지 궁금하신가요? 이 문서에는 사용자와 사용자의 고객에게 원활한 경험을 제공하는 데 도움이 되는는 iOS 17 릴리스에 대한 인사이트를 요약하여 제공합니다. ## iOS 17 및 Xcode 15 호환성 Braze Swift SDK와 Objective-C SDK는 모두 Xcode 14 및 Xcode 15와 역호환되며 iOS 17 기기와도 호환됩니다. ## iOS 17의 변경 사항 ### 링크 추적 및 UTM 매개변수 제거 iOS 17의 중요한 변경 사항 중 하나는 Safari에서 UTM 매개변수를 차단하는 것입니다. UTM 매개변수는 URL에 추가되는 코드 조각으로, 마케팅 캠페인에서 이메일, SMS 및 기타 메시징 채널의 효과를 측정하는 데 자주 사용됩니다. 이 변경 사항은 Braze 이메일 클릭 추적 및 SMS 링크 단축 전송에는 영향을 미치지 않습니다. ### 앱 추적 투명성 Apple은 사용자가 앱이 다른 회사의 앱과 웹사이트에서 자신의 활동에 액세스할 수 있는지 여부를 제어할 수 있는 [광고 추적 투명성(ATT)](https://support.apple.com/en-us/HT212025)의 범위를 확대하겠다고 발표했습니다. iOS 17 릴리스에는 개인정보 보호 매니페스트 및 코드 서명이라는 두 가지 주요 ATT 기능이 포함되어 있습니다. #### 개인정보 보호 매니페스트 이제 Apple은 앱 및 서드파티 SDK가 데이터를 수집하는 이유와 데이터 수집 방법을 설명하는 개인정보 보호 매니페스트 파일을 요구합니다. iOS 17.2부터 Apple은 최종 사용자가 ATT 프롬프트를 수락할 때까지 앱에서 선언된 모든 추적 엔드포인트를 차단합니다. Braze는 신고된 추적 기술 데이터를 전용 `-tracking` 엔드포인트로 자동 리라우팅하는 유연한 새 API와 함께 자체 개인정보처리방침을 공개했습니다. 자세한 내용은 [Braze 개인정보취급방침](https://www.braze.com/docs/ko/ko/developer_guide/analytics/managing_data_collection/?sdktab=swift#swift_privacy-manifest)을 참조하세요. #### 코드 서명 코드 서명을 사용하면 애플리케이션에서 서드파티 SDK를 사용하는 개발자가 Xcode에서 이전 버전과 동일한 개발자가 서명했는지 확인할 수 있습니다. ### Braze SDK 및 개인정보 보호 또한 Apple은 2023년 말에 '개인정보 보호에 영향을 미치는' 것으로 간주되는 서드파티 SDK 목록을 공개할 것이라고 발표했습니다. 이러한 SDK는 특히 Apple의 사용자 개인정보 보호에 큰 영향을 미칠 것으로 예상됩니다. 여러 웹사이트와 애플리케이션에서 사용자를 모니터링하도록 설계된 기존 추적 SDK와 달리 Braze SDK는 퍼스트파티 데이터 메시징과 사용자 경험에 중점을 둡니다. Braze SDK가 이 목록에 포함될 것으로 예상하지는 않지만, 상황을 면밀히 모니터링하여 필요한 업데이트가 있으면 발표할 계획입니다. # 웹용 브라우저 확장 프로그램 통합 Source: /docs/ko/developer_guide/platforms/web/browser_extensions/index.md # 브라우저 확장 프로그램 {#browser-extension} > 이 문서에서는 브라우저 확장 프로그램(Google Chrome, Firefox)에서 Braze 웹 SDK를 사용하는 방법을 설명합니다. 브라우저 확장 프로그램에 Braze 웹 SDK를 통합하여 분석을 수집하고 사용자에게 리치 메시징을 표시할 수 있습니다. 여기에는 **Google Chrome 확장 프로그램** 및 **Firefox 추가 기능**이 모두 포함됩니다. ## 지원되는 항목 {#whats-supported} 일반적으로 확장 프로그램은 HTML과 JavaScript에 기반하므로 다음과 같은 용도로 Braze를 사용할 수 있습니다. * **분석**: 커스텀 이벤트와 속성을 캡처하고 확장 프로그램 내에서 반복 사용자도 식별합니다. 이러한 프로필 특성을 사용하여 크로스채널 메시징을 강화하세요. * **인앱 메시지**: 기본 또는 커스텀 HTML 메시징을 사용하여 사용자가 확장 프로그램 내에서 동작을 수행할 때 인앱 메시지를 트리거합니다. * **Content Cards**: 온보딩 또는 프로모션 콘텐츠를 위해 확장 프로그램에 기본 카드 피드를 추가하세요. * **웹 푸시**: 웹 페이지가 현재 열려 있지 않을 때에도 적시에 알림을 보냅니다. ## 지원되지 않는 항목 {#whats-not-supported} * 서비스 워커는 Braze 웹 SDK에서 지원되지 않지만, 향후 검토를 위한 로드맵에 포함되어 있습니다. ## 확장 프로그램 유형 {#extension-types} Braze는 확장 프로그램의 다음 영역에 포함될 수 있습니다. | 영역 | 세부 정보 | 지원되는 항목 | |--------|-------|------| | 팝업 페이지 | [팝업](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/user_interface/Popups) 페이지는 브라우저 툴바에서 확장 프로그램의 아이콘을 클릭하면 사용자에게 표시되는 대화상자입니다. | 분석, 인앱 메시지 및 Content Cards | | 백그라운드 스크립트 | [백그라운드 스크립트](https://developer.chrome.com/extensions/background_pages)(매니페스트 v2 전용)를 사용하면 확장 프로그램에서 사용자 탐색을 검사하고 상호 작용하거나 웹 페이지를 수정할 수 있습니다(예: 광고 차단 프로그램이 페이지의 콘텐츠를 감지하고 변경하는 방법). | 분석, 인앱 메시지 및 Content Cards.

백그라운드 스크립트는 사용자에게 표시되지 않으므로, 메시징의 경우 메시지를 표시할 때 브라우저 탭이나 팝업 페이지와 통신해야 합니다. | | 옵션 페이지 | [옵션 페이지](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/user_interface/Options_pages)를 통해 사용자가 확장 프로그램 내 설정을 토글할 수 있습니다. 새 탭이 열리는 독립형 HTML 페이지입니다. | 분석, 인앱 메시지 및 Content Cards | {: .reset-td-br-1 .reset-td-br-2, .reset-td-br-3 role="presentation" } ## 권한 {#permissions} Braze SDK(`braze.min.js`)를 확장 프로그램과 함께 번들로 제공되는 로컬 파일로 통합할 때 `manifest.json`에 추가 권한이 필요하지 않습니다. 그러나 [Google Tag Manager](https://www.braze.com/docs/ko/ko/developer_guide/platform_integration_guides/web/google_tag_manager/)를 사용하거나 외부 URL에서 Braze SDK를 참조하거나 확장 프로그램에 대해 엄격한 콘텐츠 보안 정책을 설정한 경우 `manifest.json`에서 [`content_security_policy`](https://developer.chrome.com/extensions/contentSecurityPolicy) 설정을 조정하여 원격 스크립트 소스를 허용해야 합니다. ## 시작하기 {#getting-started} **Tip:** 시작하기 전에 웹 SDK의 [초기 SDK 설정 가이드](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=web)를 읽고 JavaScript 통합에 대해 전반적으로 자세히 알아보세요.

다양한 SDK 메서드 및 구성 옵션에 대한 자세한 내용은 [JavaScript SDK 참조](https://js.appboycdn.com/web-sdk/latest/doc/modules/braze.html)를 북마크에 추가해 두는 것도 좋습니다. Braze 웹 SDK를 통합하려면 먼저 최신 JavaScript 라이브러리 사본을 다운로드해야 합니다. NPM을 사용하거나 [Braze CDN](https://js.appboycdn.com/web-sdk/latest/braze.min.js)에서 직접 다운로드할 수 있습니다. 또는 [Google Tag Manager](https://www.braze.com/docs/ko/ko/developer_guide/platform_integration_guides/web/google_tag_manager/)를 사용하거나 외부에서 호스팅된 Braze SDK 사본을 사용하는 경우, 외부 리소스를 로드하려면 `manifest.json`에서 [`content_security_policy`](https://developer.chrome.com/extensions/contentSecurityPolicy) 설정을 조정해야 합니다. 다운로드가 완료되면 `braze.min.js` 파일을 확장 프로그램의 디렉토리에 복사합니다. ### 확장 프로그램 팝업 {#popup} 확장 프로그램 팝업에 Braze를 추가하려면 일반 웹사이트에서와 마찬가지로 `popup.html`에서 로컬 JavaScript 파일을 참조합니다. Google Tag Manager를 사용하는 경우, 대신 [Google Tag Manager 템플릿](https://www.braze.com/docs/ko/ko/developer_guide/platform_integration_guides/web/google_tag_manager/)을 사용하여 Braze를 추가할 수 있습니다. ```html popup.html ``` ### 백그라운드 스크립트(매니페스트 v2만 해당) {#background-script} 확장 프로그램의 백그라운드 스크립트 내에서 Braze를 사용하려면 `manifest.json`의 `background.scripts` 배열에 Braze 라이브러리를 추가합니다. 이렇게 하면 백그라운드 스크립트 컨텍스트에서 글로벌 `braze` 변수를 사용할 수 있습니다. ```json { "manifest_version": 2, "background": { "scripts": [ "relative/path/to/braze.min.js", "background.js" ] } } ``` ### 옵션 페이지 {#options-page} 옵션 페이지(`options` 또는 `options_ui` 매니페스트 속성 사용)를 사용하는 경우, [`popup.html` 지침](#popup)에서와 같이 Braze를 포함할 수 있습니다. ## 초기화 {#initialization} SDK가 포함되면 평소처럼 라이브러리를 초기화할 수 있습니다. 브라우저 확장 프로그램에서는 쿠키가 지원되지 않으므로 `noCookies: true`로 초기화하여 쿠키를 비활성화할 수 있습니다. ```javascript braze.initialize("YOUR-API-KEY-HERE", { baseUrl: "YOUR-API-ENDPOINT", enableLogging: true, noCookies: true }); ``` 지원되는 초기화 옵션에 대한 자세한 내용은 [웹 SDK 참조](https://js.appboycdn.com/web-sdk/latest/doc/modules/braze.html#initialize)를 확인하세요. ## 푸시 {#push} 확장 프로그램 팝업 대화상자에서는 푸시 프롬프트가 허용되지 않습니다(탐색에 URL 표시줄이 없음). 따라서 확장 프로그램의 팝업 대화상자에서 푸시 권한을 등록하고 요청하려면 [대체 푸시 도메인](https://www.braze.com/docs/ko/ko/developer_guide/platform_integration_guides/web/push_notifications/alternate_push_domain)에서 설명한 대로 대체 도메인 해결 방법을 사용해야 합니다. # 웹을 위한 콘텐츠 보안 정책 헤더 Source: /docs/ko/developer_guide/platforms/web/content_security_policy/index.md # 콘텐츠 보안 정책 헤더 {#content-security-policy-headers} > Content-Security-Policy는 콘텐츠가 웹사이트에 로드되는 방식과 위치를 제한하여 추가 보안을 제공합니다. 이 참조 문서에서는 웹 SDK에 필요한 콘텐츠 보안 정책 헤더를 다룹니다. **Important:** 이 문서는 CSP 규칙을 적용하고 Braze와 통합하는 웹사이트에서 작업하는 개발자를 대상으로 합니다. 보안 접근 방식에 대한 조언을 제공하기 위한 것이 아닙니다. **Note:** This guide uses code samples from the Braze Web SDK 4.0.0+. To upgrade to the latest Web SDK version, see [SDK Upgrade Guide](https://github.com/braze-inc/braze-web-sdk/blob/master/UPGRADE_GUIDE.md). ## Nonce 속성 {#nonce} `nonce` 값을 `script-src` 또는 `style-src` 지시문에서 사용하는 경우, 해당 값을 `contentSecurityNonce` 초기화 옵션에 전달하여 SDK에서 새로 생성하는 스크립트 및 스타일에 전파합니다. ```javascript import * as braze from "@braze/web-sdk"; braze.initialize(apiKey, { baseUrl: baseUrl, contentSecurityNonce: "YOUR-NONCE-HERE", // assumes a "nonce-YOUR-NONCE-HERE" CSP value }); ``` ## 지시문 {#directives} ### `connect-src` {#connect-src} **Warning:** URL은 선택한 `baseUrl` 초기화 옵션의 [API SDK 엔드포인트](https://www.braze.com/docs/ko/ko/user_guide/administer/personal/sdk_endpoints/)와 일치해야 합니다. | URL | 정보 | |---|-----------| | `connect-src https://sdk.iad-01.braze.com` | SDK가 Braze API와 통신할 수 있도록 허용합니다. 선택한 `baseUrl` 초기화 옵션의 [API SDK 엔드포인트](https://www.braze.com/docs/ko/ko/user_guide/administer/personal/sdk_endpoints/)와 일치하도록 이 URL을 변경하세요. | {: .reset-td-br-1 .reset-td-br-2 aria-label="connect-src #connect-src" } ### `script-src` {#script-src} | URL | 정보 | |---|-----------| | `script-src https://js.appboycdn.com` | CDN 호스팅 통합을 사용할 때 필요합니다. | | `script-src 'unsafe-eval'` | `appboyQueue`에 대한 참조가 포함된 통합 스니펫을 사용할 때 필요합니다. 이 지시문을 사용하지 않으려면 대신 [NPM을 사용하여 SDK를 통합](https://www.braze.com/docs/ko/ko/developer_guide/platform_integration_guides/web/initial_sdk_setup/?tab=package%20manager)하세요. | | `script-src 'nonce-...'`
또는
`script-src 'unsafe-inline'` | 커스텀 HTML과 같은 특정 인앱 메시지에 필요합니다. | {: .reset-td-br-1 .reset-td-br-2 aria-label="script-src #script-src" } ### `img-src` {#img-src} | URL | 정보 | |---|-----------| | `img-src: appboy-images.com braze-images.com cdn.braze.eu` | Braze CDN 호스팅 이미지를 사용할 때 필요합니다. 호스트 이름은 대시보드 클러스터에 따라 다를 수 있습니다.

**중요:** 커스텀 글꼴을 사용하는 경우 `font-src`도 포함해야 합니다. | {: .reset-td-br-1 .reset-td-br-2 aria-label="img-src #img-src" } ## Font Awesome {#font-awesome} Font Awesome의 자동 포함을 비활성화하려면 `doNotLoadFontAwesome` 초기화 옵션을 사용하세요. ```javascript import * as braze from "@braze/web-sdk"; braze.initialize(apiKey, { baseUrl: baseUrl, doNotLoadFontAwesome: true, }); ``` Font Awesome을 사용하기로 선택한 경우 다음 CSP 지시문이 필요합니다. - `font-src https://use.fontawesome.com` - `style-src https://use.fontawesome.com` - `style-src 'nonce-...'` 또는 `style-src 'unsafe-inline'` # 접근성 Source: /docs/ko/developer_guide/platforms/web/accessibility/index.md # 접근성 {#accessibility} > 이 문서에서는 Braze가 통합 내에서 접근성을 지원하는 방법에 대한 개요를 제공합니다. Braze Web SDK는 [웹 콘텐츠 접근성 지침(WCAG 2.1)](https://www.w3.org/TR/WCAG21/)에서 제공하는 표준을 지원합니다. 접근성 표준을 유지하기 위해 모든 새로운 빌드에서 Content Cards 및 인앱 메시지에 대해 [100/100 라이트하우스 점수](https://developer.chrome.com/docs/lighthouse/accessibility/scoring)를 유지하고 있습니다. ## 필수 조건 {#prerequisites} WCAG 2.1을 만족하는 최소 SDK 버전은 v3.4.0에 가깝습니다. 그러나 주요 이미지 태그 수정을 위해 최소 v6.0.0으로 업그레이드할 것을 권장합니다. ### 주요 접근성 수정 사항 {#notable-accessibility-fixes} | 버전 | 유형 | 주요 변경 사항 | |---------|------|-------------| | **6.0.0** | **주요** | 이미지를 `` 태그로 변경, `imageAltText` 또는 `language` 필드, 일반 UI 접근성 개선 | | **3.5.0** | 부분 | 스크롤 가능한 텍스트 접근성 개선 | | **3.4.0** | 수정 | Content Cards `article` 역할 수정 | | **3.2.0** | 부분 | 버튼에 대한 최소 45x45px 터치 타겟 | | **3.1.2** | 부분 | 이미지에 대한 기본 대체 텍스트 | | **2.4.1** | **주요** | 시맨틱 HTML(`h1` 또는 `button`), ARIA 속성, 키보드 탐색, 포커스 관리 | | **2.0.5** | 부분 | 포커스 관리, 키보드 탐색, 레이블 | {: .reset-td-br-1, .reset-td-br-2 aria-label="Notable accessibility fixes" } ## 지원되는 접근성 기능 {#supported-accessibility-features} Content Cards 및 인앱 메시지에 대해 다음 기능을 지원합니다: - ARIA 역할 및 레이블 - 키보드 탐색 지원 - 포커스 관리 - 스크린 리더 알림 - 이미지에 대한 대체 텍스트 지원 ## SDK 통합을 위한 접근성 가이드라인 {#accessibility-guidelines-for-sdk-integrations} 일반 접근성 가이드라인에 대해서는 [Braze에서 접근성 높은 메시지 구축하기](https://www.braze.com/docs/ko/ko/user_guide/messaging/messaging_fundamentals/accessibility/)를 참조하세요. 이 가이드는 Braze Web SDK를 웹 애플리케이션에 통합할 때 최대 접근성을 위한 팁과 모범 사례를 제공합니다. ### Content Cards #### 최대 높이 설정 {#setting-a-maximum-height} Content Cards가 너무 많은 수직 공간을 차지하지 않도록 하고 접근성을 개선하기 위해, 다음 예시와 같이 피드 컨테이너에 최대 높이를 설정할 수 있습니다: ```css /* Limit the height of the Content Cards feed */ .ab-feed { max-height: 600px; /* Adjust to your needs */ overflow-y: auto; } /* For inline feeds (non-sidebar), you may want to limit individual cards */ .ab-card { max-height: 400px; /* Optional: limit individual card height */ overflow: hidden; } ``` #### 뷰포트 고려사항 {#viewport-considerations} 인라인으로 표시되는 Content Cards의 경우, 다음 예시와 같이 뷰포트 제약을 고려하세요. ```css /* Limit feed height on mobile to prevent covering too much screen */ @media (max-width: 768px) { body > .ab-feed { max-height: 80vh; /* Leave space for other content */ } } ``` ### 인앱 메시지 {#in-app-messages} **Warning:** 중요한 정보를 슬라이드 업 인앱 메시지 내에 두지 마세요. 스크린 리더에서 접근할 수 없습니다. ### 모바일 고려사항 {#mobile-considerations} #### 반응형 디자인 {#responsive-design} SDK에는 반응형 중단점이 포함되어 있습니다. 다음 예시와 같이 커스텀 설정이 다양한 화면 크기에서 올바르게 작동하는지 확인하세요: ```css /* Mobile-specific accessibility considerations */ @media (max-width: 768px) { /* Ensure readable font sizes */ .ab-feed { font-size: 14px; /* Minimum 14px for mobile readability */ } /* Ensure sufficient touch targets */ .ab-card { padding: 16px; /* Adequate padding for touch */ } } ``` ### 접근성 테스트 {#testing-accessibility} #### 수동 테스트 체크리스트 {#manual-test-checklist} 다음 작업을 완료하여 접근성을 수동으로 테스트하세요: - 키보드만 사용하여 Content Cards 및 인앱 메시지를 탐색합니다(Tab, Enter, Space) - 스크린 리더로 테스트합니다(NVDA, JAWS, VoiceOver) - 모든 이미지에 대체 텍스트가 있는지 확인합니다 - 색상 대비 비율을 확인합니다(WebAIM Contrast Checker와 같은 도구 사용) - 터치 기능이 있는 모바일 기기에서 테스트합니다 - 포커스 표시기가 보이는지 확인합니다 - 모달 메시지 포커스 트래핑을 테스트합니다 - 모든 상호작용 요소가 키보드로 접근 가능한지 확인합니다 ### 일반적인 접근성 문제 {#common-accessibility-issues} 일반적인 접근성 문제를 피하려면 다음을 수행하세요: 1. **포커스 스타일 유지:** SDK의 포커스 표시기는 키보드 사용자에게 필수적입니다. 2. **비상호작용 요소에만 `display: none` 사용:** 상호작용 요소를 숨기려면 `visibility: hidden` 또는 `opacity: 0`를 사용하세요. 3. **ARIA 속성을 재정의하지 마세요:** SDK는 적절한 ARIA 역할과 레이블을 설정합니다. 4. **`tabindex` 속성을 사용하세요:** 이 속성은 키보드 탐색 순서를 제어합니다. 5. **`overflow: hidden`을 설정하면 스크롤을 제공하세요:** 스크롤 가능한 콘텐츠가 접근 가능하게 유지되는지 확인하세요. 6. **내장된 키보드 핸들러에 간섭하지 마세요:** 기존 키보드 탐색이 작동하는지 확인하세요. # 웹브레이즈 SDK를 위한 스마트 TV 지원 Source: /docs/ko/developer_guide/platforms/web/smart_tvs/index.md # 스마트 TV 지원 > Braze 웹 SDK를 사용하면 분석을 수집하고 리치 서식의 인앱 메시지와 콘텐츠 카드 메시지를 [삼성 Tizen TV](https://developer.samsung.com/smarttv/develop/specifications/tv-model-groups.html) 및 [LG TV(webOS)](https://webostv.developer.lge.com/discover)를 포함한 스마트 TV 사용자에게 표시할 수 있습니다. 이 문서에서는 Braze 웹 SDK를 사용하여 스마트 TV와 통합하는 방법을 다룹니다. **Tip:** 전체 기술 참조는 [JavaScript 설명서](https://js.appboycdn.com/web-sdk/latest/doc/modules/braze.html)를 참조하세요. 또는 TV에서 실행되는 웹 SDK를 확인하려면 [샘플 앱](https://github.com/Appboy/smart-tv-sample-apps)을 참조하세요. ## Prerequisites Before you can use this feature, you'll need to [integrate the Web Braze SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=web). ## 웹브레이즈 SDK 구성 스마트 TV와 통합할 때는 두 가지 변경 사항이 필요합니다: 1. 웹 SDK를 다운로드하거나 가져올 때는 반드시 '코어' 번들(`https://js.appboycdn.com/web-sdk/x.y/braze.core.min.js`에서 사용 가능, 여기서 `x.y`는 원하는 버전)을 사용해야 합니다. NPM 버전은 기본 ES 모듈로 작성되는 반면 CDN 버전은 ES5로 변환되므로 웹 SDK의 CDN 버전을 사용하는 것이 좋습니다. [NPM 버전을](https://www.npmjs.com/package/@braze/web-sdk) 사용하려면 사용하지 않는 코드를 제거하는 웹팩과 같은 번들러를 사용하고 있는지, 코드가 ES5로 트랜스파일되었는지 확인하세요. 2. 웹 SDK를 초기화할 때 `disablePushTokenMaintenance` 및 `manageServiceWorkerExternally` 초기화 옵션을 `true`로 설정해야 합니다. ## 분석 분석을 위한 모든 동일한 웹 SDK 메서드는 스마트 TV에서도 사용할 수 있습니다. 커스텀 이벤트, 커스텀 속성 등을 추적하는 전체 안내는 [분석](https://www.braze.com/docs/ko/ko/developer_guide/analytics/tracking_sessions/?tab=web)을(를) 참조하세요. ## 인앱 메시지 및 콘텐츠 카드 Braze 웹 SDK는 스마트 TV에서 [인앱 메시지](https://www.braze.com/docs/ko/ko/developer_guide/in_app_messages/?sdktab=web) 및 [콘텐츠 카드](https://www.braze.com/docs/ko/ko/developer_guide/content_cards/?sdktab=web)를 모두 지원합니다. 인앱 메시지 및 콘텐츠 카드 렌더링은 표준 UI 표시로는 지원되지 않아 대신 앱에서 TV 앱 경험에 맞게 사용자 지정해야 하므로 ['코어' 웹 SDK](https://www.npmjs.com/package/@braze/web-sdk)를 사용해야 합니다. 스마트 TV 앱에서 인앱 메시지를 수신하고 표시하는 방법에 대한 자세한 내용은 [메시지 트리거하기를](https://www.braze.com/docs/ko/ko/developer_guide/in_app_messages/triggering_messages/?tab=web) 참조하세요. # Braze를 위한 TV 및 OTT 통합 Source: /docs/ko/developer_guide/platforms/tv_and_ott/index.md # TV 및 OTT 통합 {#tv-and-ott-integrations} > 기술이 새로운 플랫폼과 기기로 발전함에 따라 Braze를 사용한 메시징도 함께 발전할 수 있습니다! Braze는 다양한 TV 운영체제 및 OTT(Over-the-Top) 콘텐츠 전달 방법을 위한 여러 참여 채널을 제공합니다. ## 플랫폼 및 기능 {#platforms-and-features} 다음은 현재 지원되는 기능 및 메시징 채널 목록입니다.
플랫폼 및 기능
기기 유형 데이터 및 분석 인앱 메시지 Content Cards 푸시 알림 Canvas 기능 플래그 배너
Amazon Fire TV
Kindle Fire
Android TV
LG TV (webOS) N/A
Samsung Tizen TV N/A
Roku N/A
Apple TV OS
Apple Vision Pro
- = 지원됨 - = 부분 지원 - = Braze에서 지원되지 않음 - N/A = OTT 플랫폼에서 지원되지 않음 ## 통합 가이드 {#integration-guides} ### Amazon Fire TV {#fire-tv} Braze Fire OS SDK를 사용하여 Amazon Fire TV 기기와 통합하세요. 지원되는 기능은 다음과 같습니다. - 크로스채널 참여를 위한 데이터 및 분석 수집 - 푸시 알림(["헤드업 알림"](https://developer.amazon.com/docs/fire-tv/notifications.html#headsup)이라고도 함) - 이 알림이 표시되려면 우선순위를 "HIGH"로 설정해야 합니다. 모든 알림은 Fire TV 설정 메뉴에 표시됩니다. - Content Cards - 기능 플래그 - 인앱 메시지 - TV와 같은 비터치 환경에서 HTML 메시지를 표시하려면 `com.braze.configuration.BrazeConfig.Builder.setIsTouchModeRequiredForHtmlInAppMessages`를 `false`로 설정하세요([Android SDK v23.1.0](https://github.com/braze-inc/braze-android-sdk/blob/master/CHANGELOG.md#2310)부터 사용 가능) - 배너 - [배너 배치](https://www.braze.com/docs/ko/ko/developer_guide/banners/placements/)를 사용하여 Fire TV 앱에 메시지를 직접 삽입하세요. 자세한 내용은 [Fire OS 통합 가이드](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=android)를 참조하세요. ### Kindle Fire {#kindle-fire} Braze Fire OS SDK를 사용하여 Amazon Kindle Fire 기기와 통합하세요. 지원되는 기능은 다음과 같습니다. - 크로스채널 참여를 위한 데이터 및 분석 수집 - 푸시 알림 - Content Cards - 기능 플래그 - 인앱 메시지 - 배너 - [배너 배치](https://www.braze.com/docs/ko/ko/developer_guide/banners/placements/)를 사용하여 Kindle Fire에 메시지를 직접 삽입하세요. 자세한 내용은 [Fire OS 통합 가이드](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=android)를 참조하세요. ### Android TV {#android-tv} Braze Android SDK를 사용하여 Android TV 기기와 통합하세요. 지원되는 기능은 다음과 같습니다. - 크로스채널 참여를 위한 데이터 및 분석 수집 - Content Cards - 기능 플래그 - 인앱 메시지 - TV와 같은 비터치 환경에서 HTML 메시지를 표시하려면 `com.braze.configuration.BrazeConfig.Builder.setIsTouchModeRequiredForHtmlInAppMessages`를 `false`로 설정하세요([Android SDK v23.1.0](https://github.com/braze-inc/braze-android-sdk/blob/master/CHANGELOG.md#2310)부터 사용 가능) - * 푸시 알림(수동 통합 필요) - Android TV에서는 푸시 알림이 기본적으로 지원되지 않습니다. 그 이유를 알아보려면 Google의 [디자인 가이드라인](https://designguidelines.withgoogle.com/android-tv/patterns/notifications.html)을 참조하세요. 그러나 **푸시 알림 UI를 수동으로 통합하여 이를 구현**할 수 있습니다. 설정 방법은 [설명서](https://www.braze.com/docs/ko/ko/developer_guide/push_notifications/?sdktab=android%20tv)를 참조하세요. - 배너 - [배너 배치](https://www.braze.com/docs/ko/ko/developer_guide/banners/placements/)를 사용하여 Android TV 앱에 메시지를 직접 삽입하세요. 자세한 내용은 [Android SDK 통합 가이드](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=android)를 참조하세요. **Note:** Android OTT 통합을 위해 대시보드에서 새 Android 앱을 생성해야 합니다. ### LG webOS {#lg-webos} Braze 웹 SDK를 사용하여 [LG webOS TV](https://webostv.developer.lge.com/discover)와 통합하세요. 지원되는 기능은 다음과 같습니다. - 크로스채널 참여를 위한 데이터 및 분석 수집 - Content Cards([헤드리스 UI](#custom-ui) 사용) - 기능 플래그 - 인앱 메시지([헤드리스 UI](#custom-ui) 사용) - 배너 - [배너 배치](https://www.braze.com/docs/ko/ko/developer_guide/banners/placements/)를 사용하여 webOS 앱에 메시지를 직접 삽입하세요. 자세한 내용은 [웹 스마트 TV 통합 가이드](https://www.braze.com/docs/ko/ko/developer_guide/platforms/web/smart_tvs/)를 참조하세요. ### Samsung Tizen {#tizen} Braze 웹 SDK를 사용하여 [Samsung Tizen TV](https://developer.samsung.com/smarttv/develop/specifications/tv-model-groups.html)와 통합하세요. 지원되는 기능은 다음과 같습니다. - 크로스채널 참여를 위한 데이터 및 분석 수집 - Content Cards([헤드리스 UI](#custom-ui) 사용) - 기능 플래그 - 인앱 메시지([헤드리스 UI](#custom-ui) 사용) - 배너 - [배너 배치](https://www.braze.com/docs/ko/ko/developer_guide/banners/placements/)를 사용하여 Tizen 앱에 메시지를 직접 삽입하세요. 자세한 내용은 [웹 스마트 TV 통합 가이드](https://www.braze.com/docs/ko/ko/developer_guide/platforms/web/smart_tvs/)를 참조하세요. ### Roku {#roku} Braze Roku SDK를 사용하여 [Roku TV](https://developer.roku.com/docs/developer-program/getting-started/roku-dev-prog.md)와 통합하세요. 지원되는 기능은 다음과 같습니다. - 크로스채널 참여를 위한 데이터 및 분석 수집 - 인앱 메시지([헤드리스 UI](#custom-ui) 사용) - Roku 플랫폼에서는 웹뷰가 지원되지 않으므로 HTML 인앱 메시지도 지원되지 않습니다. - 기능 플래그 자세한 내용은 [Roku 통합 가이드](https://www.braze.com/docs/ko/ko/developer_guide/in_app_messages/?sdktab=roku)를 참조하세요. ### Apple TV OS {#tvos} Braze Swift SDK를 사용하여 tvOS와 통합하세요. Swift SDK에는 tvOS용 기본 UI나 뷰가 포함되어 있지 않으므로 직접 구현해야 합니다. 지원되는 기능은 다음과 같습니다. - 크로스채널 참여를 위한 데이터 및 분석 수집 - Content Cards([헤드리스 UI](#custom-ui) 사용) - 기능 플래그 - 인앱 메시지([헤드리스 UI](#custom-ui) 사용) - tvOS 플랫폼에서는 웹뷰가 지원되지 않으므로 HTML 인앱 메시지도 지원되지 않습니다. - tvOS에서 커스텀 메시징을 위해 헤드리스 UI를 사용하는 방법에 대해 자세히 알아보려면 [샘플 앱](https://github.com/braze-inc/braze-swift-sdk/tree/main/Examples#inappmessages-custom-ui)을 참조하세요. - 무음 푸시 알림 및 배지 업데이트 - 배너 - [배너 배치](https://www.braze.com/docs/ko/ko/developer_guide/banners/placements/)를 사용하여 tvOS 앱에 메시지를 직접 삽입하세요. 자세한 내용은 [iOS Swift SDK 통합 가이드](https://github.com/braze-inc/braze-swift-sdk)를 참조하세요. **Note:** TV 사용자에게 모바일 인앱 메시지가 표시되지 않도록 하려면 [앱 타겟팅](#app-targeting)을 설정하거나 키-값 페어를 사용하여 메시지를 필터링하세요. 예를 들어, 특별한 `tv = true` 키-값 페어가 포함된 경우에만 tvOS 메시지를 표시할 수 있습니다. ### Apple Vision Pro {#vision-pro} Braze Swift SDK를 사용하여 visionOS와 통합하세요. iOS에서 사용할 수 있는 대부분의 기능은 visionOS에서도 사용할 수 있으며, 다음이 포함됩니다. - 분석(세션, 커스텀 이벤트, 구매 등) - 인앱 메시징(데이터 모델 및 UI) - Content Cards(데이터 모델 및 UI) - 푸시 알림(실행 버튼이 있는 사용자 표시 알림 및 무음 알림) - 기능 플래그 - 위치 분석 - 배너 - [배너 배치](https://www.braze.com/docs/ko/ko/developer_guide/banners/placements/)를 사용하여 visionOS 앱에 메시지를 직접 삽입하세요. 자세한 내용은 [iOS Swift SDK 통합 가이드](https://github.com/braze-inc/braze-swift-sdk)를 참조하세요. **Important:** 일부 iOS 기능은 부분적으로 지원되거나 지원되지 않습니다. 전체 목록은 [visionOS 지원](https://www.braze.com/docs/ko/ko/developer_guide/platform_integration_guides/swift/visionos/)을 참조하세요. ## 앱 타겟팅 {#app-targeting} 메시징을 위해 OTT 앱을 타겟팅하려면 OTT 앱 전용 세그먼트를 생성하는 것이 좋습니다. ![Android OTT 앱을 사용하여 생성한 세그먼트](https://www.braze.com/docs/ko/ko/assets/img/android_ott.png?5835ec23ecf7848155d909dcfa1009c7) ## 헤드리스 UI {#custom-ui} **Important:** 헤드리스 UI를 통해 인앱 메시지 또는 Content Cards를 지원하는 플랫폼에는 기본 UI나 뷰가 포함되어 있지 **않습니다**. 커스텀 UI(예: 인앱 메시지용)를 직접 구축한 다음 SDK에서 제공하는 데이터 모델을 사용하여 해당 UI를 채우세요. 헤드리스 UI를 사용하면 Braze는 앱이 제어하는 UI 내에서 앱이 읽고 사용할 수 있는 JSON과 같은 데이터 모델을 전달합니다. 이 데이터에는 대시보드에서 구성한 필드(제목, 본문, 버튼 텍스트, 색상 등)가 포함되며, 앱이 이를 읽고 적절히 표시할 수 있습니다. 커스텀 메시지 처리에 대한 자세한 내용은 다음을 참조하세요. **Android SDK** - [인앱 메시지 커스터마이징](https://www.braze.com/docs/ko/ko/developer_guide/in_app_messages/customization/?sdktab=android#android_setting-custom-manager-listeners) - [Content Cards 커스터마이징](https://www.braze.com/docs/ko/ko/developer_guide/content_cards/customizing_cards/style/) **Swift SDK** - [인앱 메시지 커스터마이징](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/brazeinappmessagepresenter/) - [헤드리스 UI 샘플 앱](https://github.com/braze-inc/braze-swift-sdk/tree/main/Examples#inappmessages-custom-ui) - [Content Cards 커스터마이징](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/contentcards-swift.class/) **웹 SDK** - [인앱 메시지 커스터마이징](https://www.braze.com/docs/ko/ko/developer_guide/in_app_messages/triggering_messages/?tab=web) - [Content Cards 커스터마이징](https://www.braze.com/docs/ko/ko/developer_guide/content_cards/customizing_cards/style/) # iOS용 통합 개요 Source: /docs/ko/developer_guide/platforms/legacy_sdks/ios/initial_sdk_setup/overview/index.md

**Warning:** [AppboyKit](https://github.com/Appboy/appboy-ios-sdk) (also known as the Objective-C SDK) is no longer supported and has been replaced by the [Swift SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=swift). It will no longer receive new features, bug fixes, security updates, or technical support—however, messaging and analytics will continue to function as normal. To learn more, see [Introducing the New Braze Swift SDK](https://www.braze.com/resources/articles/introducing-the-new-braze-swift-sdk). Braze iOS SDK를 설치하면 기본 분석 기능(세션 처리) 및 기본 인앱 메시지가 제공됩니다. 추가 채널 및 기능을 위해 통합을 추가로 사용자 정의해야 합니다.

Braze iOS SDK는 CocoaPods, Carthage, 스위프트 패키지 매니저 또는 수동 통합을 사용하여 설치하거나 업데이트할 수 있습니다.

또한, Braze iOS SDK는 RubyMotion 앱을 완벽하게 지원합니다. **Important:** iOS SDK는 앱 IPA 파일에 1MB에서 2MB를 추가하고, APP 파일 외에도 프레임워크에 30MB를 추가합니다. 나열된 옵션 중 하나를 사용하여 통합을 완료하고 [통합 완료 단계](https://www.braze.com/docs/ko/ko/developer_guide/platforms/legacy_sdks/ios/initial_sdk_setup/completing_integration/)를 따른 후 다른 SDK 사용자 지정(선택 사항)을 활성화하고 향후 캠페인의 요구 사항에 맞게 추가 채널 및 기능을 통합, 활성화 및 사용자 지정하는 단계를 진행합니다.
# iOS용 Carthage 통합 Source: /docs/ko/developer_guide/platforms/legacy_sdks/ios/initial_sdk_setup/installation_methods/carthage_integration/index.md
**Warning:** [AppboyKit](https://github.com/Appboy/appboy-ios-sdk) (also known as the Objective-C SDK) is no longer supported and has been replaced by the [Swift SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=swift). It will no longer receive new features, bug fixes, security updates, or technical support—however, messaging and analytics will continue to function as normal. To learn more, see [Introducing the New Braze Swift SDK](https://www.braze.com/resources/articles/introducing-the-new-braze-swift-sdk). # 카르타고 통합 ## SDK 가져오기 버전 `4.4.0`부터 Carthage를 통해 통합할 때 Braze SDK는 XCFrameworks를 지원합니다. 전체 SDK를 가져오려면 `Cartfile`에 다음 줄을 포함합니다. ``` binary "https://raw.githubusercontent.com/Appboy/appboy-ios-sdk/master/appboy_ios_sdk.json" github "SDWebImage/SDWebImage" ``` SDK 가져오기에 대한 자세한 지침은 [Carthage 빠른 시작 가이드](https://github.com/Carthage/Carthage#quick-start)를 참조하세요. `4.4.0` 이전 버전에서 마이그레이션하는 경우 [XCFrameworks용 Carthage 마이그레이션 가이드](https://github.com/Carthage/Carthage#migrating-a-project-from-framework-bundles-to-xcframeworks)를 따르세요. **Note:** `Cartfile`의 구문 또는 버전 고정과 같은 기능에 대한 자세한 내용은 [Carthage 설명서](https://github.com/Carthage/Carthage/blob/master/Documentation/Artifacts.md#cartfile)를 참조하세요. 플랫폼별 Carthage 사용법은 해당 [사용 설명서](https://github.com/Carthage/Carthage#if-youre-building-for-ios-tvos-or-watchos)를 참조하세요. ### 이전 버전 `3.24.0`~`4.3.4` 버전의 경우 `Cartfile`에 다음을 포함합니다. ``` binary "https://raw.githubusercontent.com/Appboy/appboy-ios-sdk/master/appboy_ios_sdk_full.json" ``` `3.24.0` 이전 버전을 가져오려면 `Cartfile`에 다음을 포함합니다. ``` github "Appboy/Appboy-iOS-SDK" "" ``` ``을 'x.y.z' 형식의 [적절한 버전](https://github.com/Appboy/appboy-ios-sdk/releases)에 해당하는 Braze iOS SDK로 바꾸어야 합니다. ## 다음 단계 [통합을 완료하려면](https://www.braze.com/docs/ko/ko/developer_guide/platforms/legacy_sdks/ios/initial_sdk_setup/completing_integration/) 지침을 따르세요. ## 코어 전용 통합 UI 구성요소나 종속성 없이 코어 SDK를 사용하려면 `Cartfile`에 다음 줄을 포함하여 Braze Carthage 프레임워크의 코어 버전을 설치합니다. ``` binary "https://raw.githubusercontent.com/Appboy/appboy-ios-sdk/master/appboy_ios_sdk_core.json" ``` # iOS용 CocoaPods 통합 Source: /docs/ko/developer_guide/platforms/legacy_sdks/ios/initial_sdk_setup/installation_methods/cocoapods/index.md
**Warning:** [AppboyKit](https://github.com/Appboy/appboy-ios-sdk) (also known as the Objective-C SDK) is no longer supported and has been replaced by the [Swift SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=swift). It will no longer receive new features, bug fixes, security updates, or technical support—however, messaging and analytics will continue to function as normal. To learn more, see [Introducing the New Braze Swift SDK](https://www.braze.com/resources/articles/introducing-the-new-braze-swift-sdk). # CocoaPods 통합 {#cocoapods-integration} ## 1단계: CocoaPods 설치 {#step-1-install-cocoapods} [CocoaPods](http://cocoapods.org/)를 통해 iOS SDK를 설치하면 대부분의 설치 과정이 자동으로 수행됩니다. 이 프로세스를 시작하기 전에 [Ruby 버전 2.0.0](https://www.ruby-lang.org/en/installation/) 이상을 사용해야 합니다. Ruby 구문에 대한 지식이 없어도 이 SDK를 설치할 수 있으니 걱정하지 마세요. 시작하려면 다음 명령을 실행하세요: ```bash $ sudo gem install cocoapods ``` CocoaPods와 관련된 문제가 있는 경우 CocoaPods [문제 해결 가이드](http://guides.cocoapods.org/using/troubleshooting.html)를 참조하세요. **Note:** `rake` 실행 파일을 덮어쓰라는 메시지가 표시되면 자세한 내용은 CocoaPods.org의 [시작하기](http://guides.cocoapods.org/using/getting-started.html) 안내를 참조하세요. ## 2단계: Podfile 구성 {#step-2-constructing-the-podfile} CocoaPods Ruby Gem을 설치했으므로 Xcode 프로젝트 디렉토리에 `Podfile`이라는 파일을 만들어야 합니다. Podfile에 다음 줄을 추가하세요: ``` target 'YourAppTarget' do pod 'Appboy-iOS-SDK' end ``` 포드 업데이트에서 마이너 버전 업데이트보다 작은 내용을 자동으로 가져올 수 있도록 Braze 버전을 설정하는 것이 좋습니다. `pod 'Appboy-iOS-SDK' ~> Major.Minor.Build`와 같은 형태입니다. 주요 변경 사항이 있더라도 최신 Braze SDK 버전을 자동으로 통합하려면 Podfile에서 `pod 'Appboy-iOS-SDK'`를 사용하면 됩니다. #### 서브스펙 {#subspecs} 전체 SDK를 가져오는 것을 권장합니다. 그러나 특정 Braze 기능만 통합하려는 경우 전체 SDK 대신 원하는 UI 서브스펙만 가져올 수 있습니다. | 서브스펙 | 세부 정보 | | ------- | ------- | | `pod 'Appboy-iOS-SDK/InAppMessage'` | `InAppMessage` 서브스펙에는 Braze 인앱 메시지 UI와 Core SDK가 포함되어 있습니다. | | `pod 'Appboy-iOS-SDK/ContentCards'` | `ContentCards` 서브스펙에는 Braze 콘텐츠 카드 UI와 Core SDK가 포함되어 있습니다. | | `pod 'Appboy-iOS-SDK/NewsFeed'` | `NewsFeed` 서브스펙에는 Braze Core SDK가 포함되어 있습니다. | | `pod 'Appboy-iOS-SDK/Core'` | `Core` 서브스펙에는 커스텀 이벤트 및 속성과 같은 분석 지원이 포함되어 있습니다. | {: .ws-td-nw-1 aria-label="Subspecs" } ## 3단계: Braze SDK 설치 {#step-3-installing-the-braze-sdk} Braze SDK CocoaPods를 설치하려면 터미널에서 Xcode 앱 프로젝트의 디렉토리로 이동하여 다음 명령을 실행하세요: ``` pod install ``` 이제 CocoaPods에서 생성한 새 Xcode 프로젝트 워크스페이스를 열 수 있습니다. Xcode 프로젝트 대신 이 Xcode 워크스페이스를 사용해야 합니다. ![Appboy 예제 폴더가 확장되어 새로운 `AppbpyExample.workspace`가 표시됩니다.](https://www.braze.com/docs/ko/ko/assets/img_archive/podsworkspace.png?96819fcb60bb61e9a9b991e15b4ef6d6) ## 다음 단계 {#next-steps} [통합 완료하기](https://www.braze.com/docs/ko/ko/developer_guide/platforms/legacy_sdks/ios/initial_sdk_setup/completing_integration/) 안내를 따르세요. ## CocoaPods를 통해 Braze SDK 업데이트하기 {#updating-the-braze-sdk-via-cocoapods} CocoaPod를 업데이트하려면 프로젝트 디렉토리에서 다음 명령을 실행하면 됩니다: ``` pod update ``` # iOS용 Swift 패키지 관리자 통합 Source: /docs/ko/developer_guide/platforms/legacy_sdks/ios/initial_sdk_setup/installation_methods/swift_package_manager/index.md
**Warning:** [AppboyKit](https://github.com/Appboy/appboy-ios-sdk) (also known as the Objective-C SDK) is no longer supported and has been replaced by the [Swift SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=swift). It will no longer receive new features, bug fixes, security updates, or technical support—however, messaging and analytics will continue to function as normal. To learn more, see [Introducing the New Braze Swift SDK](https://www.braze.com/resources/articles/introducing-the-new-braze-swift-sdk). # 스위프트 패키지 매니저 통합 [Swift 패키지 관리자](https://swift.org/package-manager/) (SPM)를 통해 iOS SDK를 설치하면 대부분의 설치 프로세스가 자동화됩니다. 이 프로세스를 시작하기 전에 Xcode 12 이상을 사용해야 합니다. **Note:** tvOS는 현재 Swift 패키지 관리자를 통해 사용할 수 없습니다. ## 1단계: 프로젝트에 종속성 추가하기 ### SDK 버전 가져오기 프로젝트를 열고 프로젝트 설정으로 이동합니다. **Swift 패키지** 탭을 선택하고 패키지 목록 아래에 있는 추가 버튼을 클릭합니다. ![](https://www.braze.com/docs/ko/ko/assets/img/ios/spm/swiftpackages.png?398868edc113b05736013ff65fe4371c) SDK 버전 `3.33.1` 이상을 가져올 때 텍스트 필드에 iOS SDK 리포지토리의 URL(`https://github.com/braze-inc/braze-ios-sdk`)을 입력하고 **다음**을 클릭합니다. 버전 `3.29.0`~`3.32.0` 의 경우 URL `https://github.com/Appboy/Appboy-ios-sdk`를 사용합니다. ![](https://www.braze.com/docs/ko/ko/assets/img/ios/spm/importsdk_example.png?10d54b223901ee4a41f1925772a9d042) 다음 화면에서 SDK 버전을 선택하고 **다음**을 클릭합니다. 버전 `3.29.0` 이상은 스위프트 패키지 매니저와 호환됩니다. ![](https://www.braze.com/docs/ko/ko/assets/img/ios/spm/select_version.png?80ad4717f88ae2a3be660fc15eb50670) ### 패키지 선택 요구 사항에 가장 적합한 패키지를 선택하고 **마침**을 클릭합니다. `AppboyKit` 또는 `AppboyUI` 중 하나를 선택해야 합니다. 두 패키지를 모두 포함하면 원치 않는 동작이 발생할 수 있습니다: - `AppboyUI` - Braze에서 제공하는 UI 컴포넌트를 사용하려는 경우에 가장 적합합니다. - 자동으로 `AppboyKit`를 포함합니다. - `AppboyKit` - Braze에서 제공하는 UI 구성요소(예: 콘텐츠 카드, 인앱 메시지 등)를 사용할 필요가 없는 경우에 가장 적합합니다. - `AppboyPushStory` - 앱에 푸시 스토리를 통합한 경우 이 패키지를 포함하세요. 버전 `3.31.0`부터 지원됩니다. - `Add to Target` 아래의 드롭다운에서 기본 앱의 대상 대신 `ContentExtension` 대상을 선택합니다. ![](https://www.braze.com/docs/ko/ko/assets/img/ios/spm/add_package.png?740b02bd5b888a29365d9f2882fea061) ## 2단계: 프로젝트 구성 그런 다음, 프로젝트 **빌드 설정**으로로 이동고 **기타 링커 플래그** 설정에 `-ObjC` 플래그를 추가합니다. SDK를 추가로 통합하려면 이 플래그를 추가하고 [오류](https://developer.apple.com/library/archive/qa/qa1490/_index.html)를 해결해야 합니다. ![](https://www.braze.com/docs/ko/ko/assets/img/ios/spm/buildsettings.png?311ae2fec7fc6770507aaf37e6e3dc03) **Note:** `-ObjC` 플래그를 추가하지 않으면 API의 일부가 누락되고 동작이 정의되지 않을 수 있습니다. '클래스로 전송된 선택기가 인식되지 않음', 애플리케이션 충돌 및 기타 문제와 같은 예기치 않은 오류가 발생할 수 있습니다. ## 3단계: 대상의 스키마 편집 **Important:** Xcode 12.5 이상을 사용하는 경우 이 단계를 건너뜁니다. Xcode 12.4 이하 버전을 사용하는 경우 Appboy 패키지를 포함한 대상의 스키마를 편집합니다**(제품 > 스키마 > 스키마 편집** 메뉴 항목). 1. **빌드** 메뉴를 확장하고 **포스트 액션을** 선택합니다. 더하기(+) 버튼을 누르고 **새 스크립트 실행 작업**을 선택합니다. 2. **빌드 설정 제공 위치** 드롭다운에서 앱의 대상을 선택합니다. 3. 이 스크립트를 열린 필드에 복사합니다: ```sh # iOS bash "$BUILT_PRODUCTS_DIR/Appboy_iOS_SDK_AppboyKit.bundle/Appboy.bundle/appboy-spm-cleanup.sh" # macOS (if applicable) bash "$BUILT_PRODUCTS_DIR/Appboy_iOS_SDK_AppboyKit.bundle/Contents/Resources/Appboy.bundle/appboy-spm-cleanup.sh" ``` ![](https://www.braze.com/docs/ko/ko/assets/img/ios/spm/swiftmanager_buildmenu.png?22987f6735d16f8dbe868f2a7173f960) ## 다음 단계 [통합을 완료하려면](https://www.braze.com/docs/ko/ko/developer_guide/platforms/legacy_sdks/ios/initial_sdk_setup/completing_integration/) 지침을 따르세요. # 수동 통합 옵션 iOS용 Source: /docs/ko/developer_guide/platforms/legacy_sdks/ios/initial_sdk_setup/installation_methods/manual_integration_options/index.md
**Warning:** [AppboyKit](https://github.com/Appboy/appboy-ios-sdk) (also known as the Objective-C SDK) is no longer supported and has been replaced by the [Swift SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=swift). It will no longer receive new features, bug fixes, security updates, or technical support—however, messaging and analytics will continue to function as normal. To learn more, see [Introducing the New Braze Swift SDK](https://www.braze.com/resources/articles/introducing-the-new-braze-swift-sdk). # 수동 통합 **Tip:** SDK를 [스위프트 패키지 매니저](../swift_package_manager/), [CocoaPods](../cocoapods/), 또는 [Carthage](../carthage_integration/)와 같은 패키지 매니저를 통해 구현할 것을 강력히 권장합니다. 그러면 많은 시간을 절감하고 프로세스의 많은 부분을 자동화할 수 있습니다. 그러나 그렇게 할 수 없는 경우 지침을 따라 통합을 수동으로 완료할 수 있습니다. ## 1단계: Braze SDK 다운로드 중 ### 옵션 1: 동적 XCFramework 1. `Appboy_iOS_SDK.xcframework.zip`을(를) [릴리스 페이지](https://github.com/appboy/appboy-ios-sdk/releases)에서 다운로드하여 파일을 추출하십시오. 2. Xcode에서 이 `.xcframework`을(를) 프로젝트에 드래그 앤 드롭하세요. 3. 프로젝트의 **일반** 탭에서 **임베드 & 서명**을 선택하십시오 `Appboy_iOS_SDK.xcframework`. ### Option 2: 정적 통합을 위한 정적 XCFramework 1. `Appboy_iOS_SDK.zip`을(를) [릴리스 페이지](https://github.com/appboy/appboy-ios-sdk/releases)에서 다운로드하십시오.

2. Xcode의 프로젝트 탐색기에서 Braze에 대한 대상 프로젝트 또는 그룹을 선택합니다

3. **파일 > 파일 추가 > Project_Name**로 이동하십시오.

4. `AppboyKit` 및 `AppboyUI` 폴더를 그룹으로 프로젝트에 추가하십시오. - **대상 그룹의 폴더에 항목 복사** 옵션이 처음 통합하는 경우 선택되어 있는지 확인합니다. 파일 선택기에서 **옵션**을 확장하여 **필요한 경우 항목 복사** 및 **그룹 생성**을 선택합니다. - `AppboyKit/include` 및 `AppboyUI/include` 디렉터리를 삭제하십시오.

5. (선택 사항) 다음 중 하나가 적용되는 경우: - SDK의 핵심 분석 기능만 원하고 UI 기능(예: 인앱 메시지 또는 콘텐츠 카드)은 사용하지 않으려고 합니다. - 귀하는 Braze UI 기능에 대한 커스텀 UI를 가지고 있으며 이미지 다운로드를 직접 처리합니다.

SDK의 핵심 버전은 `ABKSDWebImageProxy.m` 및 `Appboy.bundle` 파일을 제거하여 사용할 수 있습니다. 그러면 `SDWebImage` 프레임워크 종속성과 모든 UI 관련 리소스(예: Nib 파일, 이미지, 현지화 파일)가 SDK에서 제거됩니다. **Warning:** SDK의 핵심 버전을 Braze UI 기능 없이 사용하려고 하면, 인앱 메시지가 표시되지 않습니다. 핵심 버전에서 Braze 콘텐츠 카드 UI를 표시하려고 하면 예측할 수 없는 동작이 발생합니다. ## 2단계: 필요한 iOS 라이브러리 추가 1. 왼쪽 탐색을 사용하여 프로젝트의 대상을 클릭하고 **구축 단계** 탭을 선택합니다.

2. 클릭 버튼 under **Link Binary With Libraries**.

3. 메뉴에서 `SystemConfiguration.framework`을 선택하세요.

4. `SystemConfiguration.framework` 옆의 풀다운 메뉴를 사용하여 이 라이브러리를 필수로 표시하십시오.

5. 다음 필수 프레임워크를 프로젝트에 추가하고 각 프레임워크를 '필수'로 표시합니다. - `QuartzCore.framework` - `libz.tbd` - `CoreImage.framework` - `CoreText.framework` - `WebKit.framework`

6. 다음 프레임워크를 추가하고 선택 사항으로 표시하십시오: - `CoreTelephony.framework`

7. **구축 설정** 탭을 선택하십시오. **링킹** 섹션에서 **기타 링커 플래그** 설정을 찾아 `-ObjC` 플래그를 추가합니다.

8. `SDWebImage` 프레임워크는 콘텐츠 카드 및 인앱 메시징이 제대로 작동하는 데 필요합니다. `SDWebImage`는 GIF를 포함하여 이미지를 다운로드 및 표시하는 데 사용됩니다. 콘텐츠 카드 또는 인앱 메시지를 사용하려면 SDWebImage 통합 단계를 수행합니다. ### SDWebImage 통합 `SDWebImage`를 설치하려면 [지침](https://github.com/SDWebImage/SDWebImage/wiki/Installation-Guide#build-sdwebimage-as-xcframework)을 따르고 결과로 생성된 `XCFramework`를 프로젝트에 끌어 놓습니다. ### 선택적 위치 추적 1. `CoreLocation.framework`을(를) 추가하여 위치 추적을 활성화합니다. 2. 사용자의 앱에서 `CLLocationManager`을(를) 사용하여 위치를 승인해야 합니다. ## 3단계: Objective-C 브리징 헤더 **Note:** 프로젝트에서 Objective-C만 사용하는 경우 이 단계를 건너뛰십시오. 프로젝트에서 Swift를 사용하는 경우 브리징 헤더 파일이 필요합니다. 브리징 헤더 파일이 없는 경우 **File > New > File > (iOS or OS X) > Source > Header File**을 선택하여 `your-product-module-name-Bridging-Header.h`이라는 이름으로 새로 만드십시오. 그런 다음, 다음 코드 줄을 브리징 헤더 파일의 맨 위에 추가합니다. ``` #import "AppboyKit.h" ``` 프로젝트의 **구축 설정**에서 헤더 파일의 상대 경로를 `Objective-C Bridging Header` 구축 설정의 `Swift Compiler - Code Generation` 아래에 추가합니다. ## 다음 단계 [통합을 완료하려면](https://www.braze.com/docs/ko/ko/developer_guide/platforms/legacy_sdks/ios/initial_sdk_setup/completing_integration/) 지침을 따르세요. # iOS SDK 통합 완료 Source: /docs/ko/developer_guide/platforms/legacy_sdks/ios/initial_sdk_setup/completing_integration/index.md
**Warning:** [AppboyKit](https://github.com/Appboy/appboy-ios-sdk) (also known as the Objective-C SDK) is no longer supported and has been replaced by the [Swift SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=swift). It will no longer receive new features, bug fixes, security updates, or technical support—however, messaging and analytics will continue to function as normal. To learn more, see [Introducing the New Braze Swift SDK](https://www.braze.com/resources/articles/introducing-the-new-braze-swift-sdk). # 통합 완료 {#complete-the-integration} 이 단계를 수행하기 전에 [Carthage](https://www.braze.com/docs/ko/ko/developer_guide/platforms/legacy_sdks/ios/initial_sdk_setup/installation_methods/carthage_integration/), [CocoaPods](https://www.braze.com/docs/ko/ko/developer_guide/platforms/legacy_sdks/ios/initial_sdk_setup/installation_methods/cocoapods/), [스위프트 패키지 매니저](https://www.braze.com/docs/ko/ko/developer_guide/platforms/legacy_sdks/ios/initial_sdk_setup/installation_methods/swift_package_manager/) 또는 [수동](https://www.braze.com/docs/ko/ko/developer_guide/platforms/legacy_sdks/ios/initial_sdk_setup/installation_methods/manual_integration_options/) 통합을 사용하여 SDK를 통합했는지 확인하세요. ## 1단계: 앱 델리게이트 업데이트 {#step-1-update-your-app-delegate} Braze SDK를 CocoaPods, Carthage와 통합하거나 [동적 수동 통합](https://www.braze.com/docs/ko/ko/developer_guide/platforms/legacy_sdks/ios/initial_sdk_setup/installation_methods/manual_integration_options/)을 사용하는 경우 `AppDelegate.m` 파일에 다음 코드 줄을 추가합니다: ```objc #import "Appboy-iOS-SDK/AppboyKit.h" ``` 스위프트 패키지 매니저와 통합하거나 [정적 수동 통합](https://www.braze.com/docs/ko/ko/developer_guide/platforms/legacy_sdks/ios/initial_sdk_setup/installation_methods/manual_integration_options/)을 사용하는 경우 대신 이 줄을 사용합니다: ```objc #import "AppboyKit.h" ``` 그런 다음, `AppDelegate.m` 파일 내 `application:didFinishLaunchingWithOptions:` 메서드에 다음 스니펫을 추가합니다: ```objc [Appboy startWithApiKey:@"YOUR-APP-IDENTIFIER-API-KEY" inApplication:application withLaunchOptions:launchOptions]; ``` **설정 관리** 페이지에서 `YOUR-APP-IDENTIFIER-API-KEY`를 올바른 값으로 업데이트합니다. 앱 식별자 API 키를 찾을 수 있는 위치에 대한 자세한 내용은 [API 설명서](https://www.braze.com/docs/ko/ko/api/api_key/#the-app-identifier-api-key)를 참조하세요. Braze SDK를 CocoaPods, Carthage와 통합하거나 [동적 수동 통합](https://www.braze.com/docs/ko/ko/developer_guide/platforms/legacy_sdks/ios/initial_sdk_setup/installation_methods/manual_integration_options/)을 사용하는 경우 `AppDelegate.swift` 파일에 다음 코드 줄을 추가합니다: ```swift import Appboy_iOS_SDK ``` 스위프트 패키지 매니저와 통합하거나 [정적 수동 통합](https://www.braze.com/docs/ko/ko/developer_guide/platforms/legacy_sdks/ios/initial_sdk_setup/installation_methods/manual_integration_options/)을 사용하는 경우 대신 이 줄을 사용합니다: ```swift import AppboyKit ``` Swift 프로젝트에서 Objective-C 코드를 사용하는 방법에 대한 자세한 내용은 [Apple 개발자 설명서](https://developer.apple.com/library/ios/documentation/swift/conceptual/buildingcocoaapps/MixandMatch.html)를 참조하세요. 다음으로, `AppDelegate.swift`에서 `application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool`에 다음 스니펫을 추가합니다: ```swift Appboy.start(withApiKey: "YOUR-APP-IDENTIFIER-API-KEY", in:application, withLaunchOptions:launchOptions) ``` **설정 관리** 페이지에서 `YOUR-APP-IDENTIFIER-API-KEY`를 올바른 값으로 업데이트합니다. 앱 식별자 API 키를 찾을 수 있는 위치에 대한 자세한 내용은 [API 설명서](https://www.braze.com/docs/ko/ko/api/api_key/#the-app-identifier-api-key)를 참조하세요. **Note:** `sharedInstance` 싱글톤은 `startWithApiKey:` 호출 전에는 nil 상태입니다. 이는 모든 Braze 기능을 사용하기 위한 전제 조건이기 때문입니다. **Warning:** 애플리케이션의 메인 스레드에서 Braze를 초기화해야 합니다. 비동기적으로 초기화하면 기능이 손상될 수 있습니다. ## 2단계: 데이터 클러스터 지정 {#step-2-specify-your-data-cluster} **Note:** 2019년 12월부터 커스텀 엔드포인트는 더 이상 제공되지 않습니다. 기존 커스텀 엔드포인트가 있는 경우 계속 사용할 수 있습니다. 자세한 내용은 사용 가능한 엔드포인트 목록 을 참조하세요. ### 컴파일 타임 엔드포인트 구성(권장) {#compile-time-endpoint-configuration-recommended} 기존 커스텀 엔드포인트가 있는 경우: - Braze iOS SDK v3.0.2부터 `Info.plist` 파일을 사용하여 커스텀 엔드포인트를 설정할 수 있습니다. `Info.plist` 파일에 `Braze` 사전을 추가합니다. `Braze` 사전 내에서 `Endpoint` 문자열 하위 항목을 추가하고 값을 커스텀 엔드포인트 URL의 권한으로 설정합니다(예: `https://sdk.iad-01.braze.com`이 아닌 `sdk.iad-01.braze.com`). Braze iOS SDK v4.0.2 이전 버전에서는 `Braze` 대신 `Appboy` 사전 키를 사용해야 합니다. Braze 담당자가 이미 [올바른 엔드포인트](https://www.braze.com/docs/ko/ko/user_guide/administer/personal/sdk_endpoints/)를 알려드렸을 것입니다. ### 런타임 엔드포인트 구성 {#runtime-endpoint-configuration} 기존 커스텀 엔드포인트가 있는 경우: - Braze iOS SDK v3.17.0 이상부터 `startWithApiKey:inApplication:withLaunchOptions:withAppboyOptions:`에 전달된 `appboyOptions` 매개변수 내 `ABKEndpointKey`를 통해 엔드포인트 설정을 재정의할 수 있습니다. 값을 커스텀 엔드포인트 URL의 권한으로 설정합니다(예: `https://sdk.iad-01.braze.com`이 아닌 `sdk.iad-01.braze.com`). ## SDK 통합 완료 {#sdk-integration-complete} 이제 Braze가 애플리케이션에서 데이터를 수집하며 기본 통합이 완료되었습니다. [커스텀 이벤트 추적](https://www.braze.com/docs/ko/ko/developer_guide/analytics/logging_events/?tab=swift), [푸시 메시징](https://www.braze.com/docs/ko/ko/developer_guide/platforms/legacy_sdks/ios/push_notifications/integration/) 및 전체 Braze 기능 모음을 활성화하려면 다음 문서를 참조하세요. ## 시작 시 Braze 커스터마이징 {#customizing-braze-on-startup} 시작 시 Braze를 커스터마이징하려면 대신 Braze 초기화 메서드 `startWithApiKey:inApplication:withLaunchOptions:withAppboyOptions:`를 사용하고 선택 사항인 Braze 시작 키의 `NSDictionary`를 전달할 수 있습니다. `AppDelegate.m` 파일의 `application:didFinishLaunchingWithOptions:` 메서드에서 다음 Braze 메서드를 추가합니다: ```objc [Appboy startWithApiKey:@"YOUR-APP-IDENTIFIER-API-KEY" inApplication:application withLaunchOptions:launchOptions withAppboyOptions:appboyOptions]; ``` 이 메서드는 `startWithApiKey:inApplication:withLaunchOptions:` 초기화 메서드를 대체합니다. `AppDelegate.swift`의 `application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool` 메서드에 다음 Braze 메서드를 추가합니다. 여기서 `appboyOptions`는 시작 구성 값의 `Dictionary`입니다: ```swift Appboy.start(withApiKey: "YOUR-APP-IDENTIFIER-API-KEY", in:application, withLaunchOptions:launchOptions, withAppboyOptions:appboyOptions) ``` 이 메서드는 `startWithApiKey:inApplication:withLaunchOptions:` 초기화 메서드를 대체합니다. 이 메서드는 다음 매개변수와 함께 호출됩니다: - `YOUR-APP-IDENTIFIER-API-KEY` – Braze 대시보드의 [앱 식별자](https://www.braze.com/docs/ko/ko/api/api_key/#the-app-identifier-api-key) API 키입니다. - `application` – 현재 앱입니다. - `launchOptions` – `application:didFinishLaunchingWithOptions:`에서 가져오는 옵션 `NSDictionary`입니다. - `appboyOptions` – Braze의 시작 구성 값이 포함된 선택적 `NSDictionary`입니다. Braze 시작 키 목록은 [Appboy.h](https://github.com/braze-inc/braze-ios-sdk/blob/master/AppboyKit/include/Appboy.h)를 참조하세요. ## Appboy.sharedInstance() 및 Swift 널 허용 {#appboysharedinstance-and-swift-nullability} 일반적인 관행과는 다소 다르게 `Appboy.sharedInstance()` 싱글톤은 옵셔널입니다. `startWithApiKey:` 호출 전에는 `sharedInstance`가 `nil`이고, 표준은 아니지만 지연된 초기화를 사용할 수 있는 유효한 구현이 일부 있기 때문입니다. Appboy의 `sharedInstance`(표준 구현)에 액세스하기 전에 `didFinishLaunchingWithOptions:` 델리게이트에서 `startWithApiKey:`를 호출하면 `Appboy.sharedInstance()?.changeUser("testUser")`와 같은 옵셔널 체이닝을 사용하여 번거로운 확인 작업을 피할 수 있습니다. 이는 null이 아닌 `sharedInstance`를 가정하는 Objective-C 구현과 동등합니다. ## 추가 리소스 {#additional-resources} 모든 SDK 메서드에 대한 추가 지침은 전체 [iOS 클래스 설명서](http://appboy.github.io/appboy-ios-sdk/docs/annotated.html)에서 확인할 수 있습니다. # iOS용 기타 SDK 커스터마이징 Source: /docs/ko/developer_guide/platforms/legacy_sdks/ios/initial_sdk_setup/other_sdk_customizations/index.md
**Warning:** [AppboyKit](https://github.com/Appboy/appboy-ios-sdk) (also known as the Objective-C SDK) is no longer supported and has been replaced by the [Swift SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=swift). It will no longer receive new features, bug fixes, security updates, or technical support—however, messaging and analytics will continue to function as normal. To learn more, see [Introducing the New Braze Swift SDK](https://www.braze.com/resources/articles/introducing-the-new-braze-swift-sdk). # 기타 SDK 커스터마이징 {#other-sdk-customizations} ## Braze 로그 수준 {#braze-log-level} Braze iOS SDK의 기본 로그 수준은 최소, 즉 다음 차트에서 `8`입니다. 이 수준은 대부분의 로깅을 억제하여 프로덕션 출시 애플리케이션에서 민감한 정보가 기록되지 않도록 합니다. 사용 가능한 로그 수준은 다음 목록을 참조하세요: ### 로그 수준 {#log-levels} | 수준 | 설명 | |----------|-------------| | 0 | 상세. 모든 로그 정보가 iOS 콘솔에 기록됩니다. | | 1 | 디버그. 디버그 및 상위 로그 정보가 iOS 콘솔에 기록됩니다. | | 2 | 경고. 경고 및 상위 로그 정보가 iOS 콘솔에 기록됩니다. | | 4 | 오류. 오류 및 상위 로그 정보가 iOS 콘솔에 기록됩니다. | | 8 | 최소. 최소한의 정보가 iOS 콘솔에 기록됩니다. SDK의 기본 설정입니다. | {: .reset-td-br-1 .reset-td-br-2 aria-label="Log levels" } ### 상세 로깅 {#verbose-logging} 로그 수준을 사용 가능한 값으로 구성할 수 있습니다. 그러나 로그 수준을 상세 또는 `0`으로 설정하면 통합 문제를 디버깅하는 데 매우 유용할 수 있습니다. 이 수준은 개발 환경에서만 사용하도록 되어 있으며 출시된 애플리케이션에서는 설정하지 않아야 합니다. 상세 로깅은 Braze에 추가 또는 새로운 사용자 정보를 전송하지 않습니다. ### 로그 수준 설정 {#setting-log-level} 로그 수준은 컴파일 시 또는 런타임 시에 할당할 수 있습니다: `Info.plist` 파일에 `Braze`라는 사전을 추가합니다. `Braze` 사전 내에서 `LogLevel` 문자열 하위 항목을 추가하고 값을 `0`으로 설정합니다. **Note:** Braze iOS SDK v4.0.2 이전 버전에서는 `Braze` 대신 `Appboy` 사전 키를 사용해야 합니다. `Info.plist` 콘텐츠 예시: ``` Braze LogLevel 0 ``` `startWithApiKey:inApplication:withLaunchOptions:withAppboyOptions:`에 전달되는 `appboyOptions` 매개변수 내에 `ABKLogLevelKey`를 추가합니다. 값을 정수 `0`으로 설정합니다. ```objc NSMutableDictionary *appboyOptions = [NSMutableDictionary dictionary]; appboyOptions[ABKLogLevelKey] = @(0); [Appboy startWithApiKey:@"YOUR-API-KEY" inApplication:application withLaunchOptions:launchOptions withAppboyOptions:appboyOptions]; ``` ```swift let appboyOptions: [AnyHashable: Any] = [ ABKLogLevelKey : 0 ] Appboy.start(withApiKey: "YOUR-API-KEY", in:application, withLaunchOptions:launchOptions, withAppboyOptions:appboyOptions) ``` **Note:** 로그 수준은 Braze iOS SDK v4.4.0 이상에서만 런타임에 설정할 수 있습니다. 이전 버전의 SDK를 사용하는 경우 컴파일 시점에 로그 수준을 설정하세요. ## 선택적 IDFV 수집 - Swift {#optional-idfv-collection-swift} 이전 버전의 Braze iOS Swift SDK에서는 IDFV(공급업체 식별자) 필드가 사용자의 기기 ID로 자동 수집되었습니다. Swift SDK v5.7.0부터 IDFV 필드를 선택적으로 비활성화할 수 있으며, 대신 Braze가 임의의 UUID를 기기 ID로 설정합니다. 자세한 내용은 [IDFV 수집](https://www.braze.com/docs/ko/ko/developer_guide/analytics/managing_data_collection/?sdktab=swift)을 참조하세요. ## 선택적 IDFA 수집 {#optional-idfa-collection} IDFA 수집은 Braze SDK 내에서 선택 사항이며 기본적으로 비활성화되어 있습니다. IDFA 수집은 Braze 내에서 [설치 경로 통합](https://www.braze.com/docs/ko/ko/partners/message_orchestration/attribution/adjust/)을 사용하려는 경우에만 필요합니다. IDFA를 저장하기로 선택하면 무료로 저장해 드리므로, 추가 개발 작업 없이 출시 즉시 이러한 옵션을 활용할 수 있습니다. 따라서 다음 기준 중 하나라도 충족하는 경우 IDFA를 계속 수집하는 것이 좋습니다: - 이전에 게재된 광고에 앱 설치를 기여하는 경우 - 이전에 게재된 광고에 애플리케이션 내 동작을 기여하는 경우 ### iOS 14.5 AppTrackingTransparency Apple은 IDFA를 수집하기 위해 사용자가 권한 프롬프트를 통해 옵트인하도록 요구합니다. IDFA를 수집하려면 `ABKIDFADelegate` 프로토콜을 구현하는 것 외에도, 앱 추적 투명성 프레임워크에서 Apple의 `ATTrackingManager`를 사용하여 사용자에게 승인을 요청해야 합니다. 자세한 내용은 Apple의 [사용자 개인정보 보호 문서](https://developer.apple.com/app-store/user-privacy-and-data-use/)를 참조하세요. 앱 추적 투명성 승인 프롬프트에는 식별자 사용을 설명하기 위한 `Info.plist` 항목이 필요합니다: ``` NSUserTrackingUsageDescription To retarget ads and build a global profile to better serve you things you would like. ``` ### IDFA 수집 구현 {#implementing-idfa-collection} 다음 단계를 따라 IDFA 수집을 구현합니다: ##### 1단계: ABKIDFADelegate 구현 {#step-1-implement-abkidfadelegate} [`ABKIDFADelegate`](https://github.com/Appboy/appboy-ios-sdk/blob/master/AppboyKit/include/ABKIDFADelegate.h) 프로토콜을 준수하는 클래스를 생성합니다: ```objc #import "IDFADelegate.h" #import #import @implementation IDFADelegate - (NSString *)advertisingIdentifierString { return [[[ASIdentifierManager sharedManager] advertisingIdentifier] UUIDString]; } - (BOOL)isAdvertisingTrackingEnabledOrATTAuthorized { if (@available(iOS 14, *)) { return [ATTrackingManager trackingAuthorizationStatus] == ATTrackingManagerAuthorizationStatusAuthorized; } return [[ASIdentifierManager sharedManager] isAdvertisingTrackingEnabled]; } @end ``` ```swift import Appboy_iOS_SDK import AdSupport import AppTrackingTransparency class IDFADelegate: NSObject, ABKIDFADelegate { func advertisingIdentifierString() -> String { return ASIdentifierManager.shared().advertisingIdentifier.uuidString } func isAdvertisingTrackingEnabledOrATTAuthorized() -> Bool { if #available(iOS 14, *) { return ATTrackingManager.trackingAuthorizationStatus == ATTrackingManager.AuthorizationStatus.authorized } return ASIdentifierManager.shared().isAdvertisingTrackingEnabled } } ``` ##### 2단계: Braze 초기화 중 델리게이트 설정 {#step-2-set-the-delegate-during-braze-initialization} `startWithApiKey:inApplication:withAppboyOptions:`에 전달되는 `appboyOptions` 사전에서 `ABKIDFADelegateKey` 키를 `ABKIDFADelegate`를 준수하는 클래스의 인스턴스로 설정합니다. ## 대략적인 iOS SDK 크기 {#ios-sdk-size} 대략적인 iOS SDK 프레임워크 파일 크기는 30 MB이며, 대략적인 .ipa(앱 파일에 추가) 크기는 1 MB에서 2 MB 사이입니다. Braze는 Apple의 [앱 크기에 대한 권장 사항](https://developer.apple.com/library/content/qa/qa1795/_index.html)에 따라 `.ipa` 크기에 대한 SDK의 영향을 관찰하여 iOS SDK의 크기를 측정합니다. 애플리케이션에 대한 iOS SDK 크기 추가를 계산하는 경우, Braze iOS SDK를 통합하기 전후의 `.ipa` 크기 차이를 비교하기 위해 [앱 크기 보고서 가져오기](https://developer.apple.com/library/content/qa/qa1795/_index.html)를 따르는 것이 좋습니다. 앱 씨닝 크기 보고서에서 크기를 비교할 때, 씨닝된 `.ipa` 파일의 앱 크기도 확인하는 것이 좋습니다. 유니버설 `.ipa` 파일은 App Store에서 다운로드하여 사용자 기기에 설치되는 바이너리보다 크기가 크기 때문입니다. **Note:** `use_frameworks!`와 함께 CocoaPods를 통해 통합하는 경우, 정확한 크기 측정을 위해 타겟의 빌드 설정에서 `Enable Bitcode = NO`를 설정하세요. # iOS용 Braze SDK 통합 가이드(선택 사항) Source: /docs/ko/developer_guide/platforms/legacy_sdks/ios/initial_sdk_setup/ios_sdk_integration/index.md
**Warning:** [AppboyKit](https://github.com/Appboy/appboy-ios-sdk) (also known as the Objective-C SDK) is no longer supported and has been replaced by the [Swift SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=swift). It will no longer receive new features, bug fixes, security updates, or technical support—however, messaging and analytics will continue to function as normal. To learn more, see [Introducing the New Braze Swift SDK](https://www.braze.com/resources/articles/introducing-the-new-braze-swift-sdk). # Braze iOS SDK 통합 가이드 > 이 iOS 통합 SDK 가이드(선택 사항)는 iOS SDK와 핵심 구성요소를 애플리케이션에 처음 통합할 때 설정 모범 사례에 대한 단계별 여정을 안내합니다. 이 가이드는 나머지 프로덕션 코드에서 Braze iOS SDK에 대한 모든 종속성을 분리하는 `BrazeManager.swift` 헬퍼 파일을 빌드하여 전체 애플리케이션에서 하나의 `import AppboyUI` 헬퍼 파일을 생성하는 데 도움이 됩니다. 이 접근 방식은 과도한 SDK 가져오기로 인해 발생하는 문제를 제한하여 코드를 쉽게 추적, 디버그 및 변경할 수 있도록 합니다. **Important:** 이 가이드에서는 이미 Xcode 프로젝트에 [SDK를 추가](https://www.braze.com/docs/ko/ko/developer_guide/platforms/legacy_sdks/ios/initial_sdk_setup/overview/)했다고 가정합니다. ## 통합 개요 다음 단계는 프로덕션 코드가 호출되는 `BrazeManager` 헬퍼 파일을 빌드하는 데 도움이 됩니다. 이 헬퍼 파일은 나열된 다음 통합 주제에 대한 다양한 확장을 추가하여 모든 Braze 관련 종속성을 처리합니다. 각 주제에는 Swift 및 Objective-C 모두에 가로 탭 단계와 코드 스니펫이 포함됩니다. 애플리케이션에서 이러한 채널을 사용할 계획이 없는 경우 콘텐츠 카드 및 인앱 메시지 단계는 통합에 필요하지 않습니다. - [BrazeManager.swift 생성](#create-brazemanagerswift) - [SDK 초기화](#initialize-the-sdk) - [푸시 알림](#push-notifications) - [사용자 변수 및 메서드 액세스](#access-user-variables-and-methods) - [로그 분석](#log-analytics) - [인앱 메시지(선택 사항)](#in-app-messages) - [콘텐츠 카드(선택 사항)](#content-cards) - [다음 단계](#next-steps) ### BrazeManager.swift 생성 ##### BrazeManager.swift 생성 `BrazeManager.swift` 파일을 빌드하려면 _BrazeManager_라는 이름의 Swift 파일을 새로 생성하여 원하는 위치의 프로젝트에 추가합니다. 그런 다음, `import Foundation`을 `import AppboyUI`(SPM의 경우) 또는 `import Appboy_iOS_SDK`(CocoaPods의 경우)로 바꾸고, 모든 Braze 관련 메서드와 변수를 호스팅하는 데 사용할 `BrazeManager` 클래스를 생성합니다. `Appboy_iOS_SDK` **Note:** - `BrazeManager`는 구조가 아닌 `NSObject` 클래스이므로 `ABKInAppMessageUIDelegate`와 같은 ABK 위임을 준수할 수 있습니다. - `BrazeManager`는 싱글톤 클래스이므로 이 클래스의 인스턴스만 사용하도록 설계되었습니다. 이는 개체에 대한 통합 액세스 지점을 제공하기 위해 수행됩니다. 1. `BrazeManager` 클래스를 초기화하는 _shared라는_ 정적 변수를 추가합니다. 이는 한 번만 느린 시작을 보장합니다. 2. 그런 다음, _apiKey_라는 비공개 상수 변수를 추가하고 Braze 대시보드의 워크스페이스에서 API 키 값으로 설정합니다. 3. SDK에 대한 구성 값을 저장할 _appboyOptions_라는 비공개 계산 변수를 추가합니다. 지금은 비어 있습니다. ```swift class BrazeManager: NSObject { // 1 static let shared = BrazeManager() // 2 private let apikey = "YOUR-API-KEY" // 3 private var appboyOptions: [String:Any] { return [:] } } ``` ```objc @implementation BrazeManager // 1 + (instancetype)shared { static BrazeManager *shared = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ shared = [[BrazeManager alloc] init]; // Do any other initialisation stuff here }); return shared; } // 2 - (NSString *)apiKey { return @"YOUR-API-KEY"; } // 3 - (NSDictionary *)appboyOptions { return [NSDictionary dictionary]; } ``` ### SDK 초기화 ##### BrazeManager.swift에서 SDK 초기화 다음으로 SDK를 초기화해야 합니다. 이 가이드에서는 이미 Xcode 프로젝트에 [SDK를 추가](https://www.braze.com/docs/ko/ko/developer_guide/platforms/legacy_sdks/ios/initial_sdk_setup/overview/)했다고 가정합니다. 또한 [워크스페이스 SDK 엔드포인트](https://www.braze.com/docs/ko/ko/developer_guide/platform_integration_guides/ios/initial_sdk_setup/completing_integration/#step-2-specify-your-data-cluster)가 있어야 하고 `Info.plist` 파일 또는 `appboyOptions`에서 [`LogLevel`](https://www.braze.com/docs/ko/ko/developer_guide/platform_integration_guides/ios/initial_sdk_setup/other_sdk_customizations/#braze-log-level)이 설정되어 있어야 합니다. `BrazeManager.swift` 파일에서 반환 유형 없이 `AppDelegate.swift` 파일에서 `didFinishLaunchingWithOptions` 메서드를 추가합니다. `BrazeManager.swift` 파일에 비슷한 방법을 생성하면 `AppDelegate.swift` 파일에 `import AppboyUI` 문이 생기지 않습니다. 그런 다음, 새로 선언한 `apiKey` 및 `appboyOptions` 변수를 사용하여 SDK를 초기화합니다. **Important:** 초기화는 메인 스레드에서 수행해야 합니다. ```swift func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) { Appboy.start(withApiKey: apikey, in: application, withLaunchOptions: launchOptions, withAppboyOptions: appboyOptions) } ``` ```objc - (void)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { [Appboy startWithApiKey:[self apiKey] inApplication:application withLaunchOptions:launchOptions withAppboyOptions:[self appboyOptions]]; } ``` ##### AppDelegate.swift에서 Appboy 초기화 처리 그런 다음, `AppDelegate.swift` 파일로 돌아가 AppDelegate의 `didFinishLaunchingWithOptions` 메서드에 다음 코드 스니펫을 추가하여 `BrazeManager.swift` 헬퍼 파일에서 Appboy 초기화를 처리합니다. `AppDelegate.swift` 에 `import AppboyUI` 문을 추가할 필요가 없다는 점을 기억하세요. ```swift func application( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? ) -> Bool { // Override point for customization after application launch BrazeManager.shared.application(application, didFinishLaunchingWithOptions: launchOptions) return true } ``` ```objc - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { // Override point for customization after application launch [[BrazeManager shared] application:application didFinishLaunchingWithOptions:launchOptions]; return YES; } ``` **Checkpoint:** 코드를 컴파일하고 애플리케이션을 실행합니다.

이 시점에서 SDK가 가동 및 실행 중이어야 합니다. 대시보드에서 더 진행하기 전에 세션이 기록되는지 관찰합니다. ### 푸시 알림 ##### 푸시 인증서 추가 Braze 대시보드에서 기존 작업 공간으로 이동합니다. **푸시 알림 설정에서** 푸시 인증서 파일을 Braze 대시보드에 업로드하고 저장합니다. ![](https://www.braze.com/docs/ko/ko/assets/img/ios_sdk/ios_sdk2.png?6e92324225dd71128662df17dbf54d2b){: style="max-width:60%;"} **Important:** 이 단계의 마지막에 있는 전용 체크포인트를 놓치지 마세요! ##### 푸시 알림 등록하기 다음으로 푸시 알림을 등록합니다. 이 가이드에서는는 Apple 개발자 포털 및 Xcode 프로젝트에서 [푸시 자격 증명을 올바르게 설정](https://www.braze.com/docs/ko/ko/developer_guide/platforms/legacy_sdks/ios/push_notifications/integration/)했다고 가정합니다. 푸시 알림을 등록하는 코드는 `BrazeManager.swift` 파일의 `didFinishLaunching...` 메소드에 추가됩니다. 초기화 코드는 다음과 같이 완성되어야 합니다: 1. 사용자와 상호 작용하기 위한 권한 요청 콘텐츠를 구성합니다. 이러한 옵션은 예시로 나열되어 있습니다. 2. 사용자에게 푸시 알림을 보낼 수 있는 권한을 요청하세요. 푸시 알림을 허용하거나 거부하는 사용자의 응답은 `granted` 변수에서 추적됩니다. 3. 사용자가 알림 프롬프트와 상호 작용한 후 푸시 인증 결과를 Braze에 전달합니다. 4. APN으로 등록 프로세스를 시작합니다. 이 작업은 기본 스레드에서 수행해야 합니다. 등록이 성공하면 앱이 `AppDelegate` 객체의 `didRegisterForRemoteNotificationsWithDeviceToken` 메서드를 호출합니다. ```swift func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions:[UIApplication.LaunchOptionsKey:Any]?) { Appboy.start(withAPIKey: apikey, in: application, withLaunchOptions: launchOptions, withAppboyOptions: appboyOptions) // 1 let options: UNAuthorizationOptions = [.alert, .sound, .badge] // 2 UNUserNotificationCenter.current().requestAuthorization(option: options) { (granted, error) in // 3 Appboy.sharedInstance()?.pushAuthorization(fromUserNotificationCenter: granted) } // 4 UIApplications.shared.registerForRemoteNotificiations() } ``` ```objc - (void)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { [Appboy startWithApiKey:[self apiKey] inApplication:application withLaunchOptions:launchOptions withAppboyOptions:[self appboyOptions]]; // 1 UNAuthorizationOptions options = (UNAuthorizationOptionSound | UNAuthorizationOptionAlert | UNAuthorizationOptionBadge); // 2 [[UNUserNotificationCenter currentNotificationCenter] requestAuthorizationWithOptions:options completionHandler:^(BOOL granted, NSError * _Nullable error) { // 3 [[Appboy sharedInstance] pushAuthorizationFromUserNotificationCenter:granted]; }]; // 4 [[UIApplication sharedApplication] registerForRemoteNotifications]; } ``` **Checkpoint:** 코드를 컴파일하고 애플리케이션을 실행합니다. - 더 진행하기 전에 앱에서 푸시 알림 메시지가 표시되는지 확인하세요. - 프롬프트가 표시되지 않으면 앱을 삭제한 후 다시 설치하여 이전에 푸시 알림 프롬프트가 표시되지 않았는지 확인합니다. 더 진행하기 전에 푸시 알림 메시지가 표시되는지 확인합니다. ##### 전달 푸시 알림 방법 그런 다음, Braze iOS SDK에서 처리하도록 시스템 푸시 알림 메서드를 `AppDelegate.swift`에서 `BrazeManager.swift`로 전달합니다. ###### 1단계: 푸시 알림 코드 확장 만들기 `BrazeManager.swift` 파일에 푸시 알림 코드의 확장을 생성하여 다음과 같이 헬퍼 파일에서 어떤 용도를 지원하는지 보다 체계적으로 파악할 수 있도록 합니다. 1. `AppDelegate`에 `import AppboyUI` 문을 포함하지 않는 패턴에 따라 `BrazeManager.swift` 파일에서 푸시 알림 방법을 처리합니다. 사용자의 디바이스 토큰은 `didRegisterForRemote...` 메소드를 통해 Braze에 전달되어야 합니다. 이 방법은 무음 푸시 알림을 구현하는 데 필요합니다. 그런 다음, `BrazeManager` 클래스의 `AppDelegate`에서 동일한 메서드를 추가합니다. 2. 메서드 안에 다음 줄을 추가하여 디바이스 토큰을 Braze에 등록합니다. 이는 Braze가 토큰을 현재 장치와 연결하기 위해 필요합니다. ```swift // MARK - Push Notifications extension BrazeManager { // 1 func application( _ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data ) { // 2 Appboy.sharedInstance().?registerDeviceToken(deviceToken) } } ``` ```objc // MARK - Push Notifications // 1 - (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken { // 2 [[Appboy sharedInstance] registerDeviceToken:deviceToken]; } ``` ###### 2단계: 원격 알림 지원 **서명 & 기능** 탭에서 **백그라운드 모드** 지원을 추가하고 **원격 알림**을 선택하여 Braze에서 발생하는 원격 푸시 알림 지원을 시작합니다.

![서명 & 기능](https://www.braze.com/docs/ko/ko/assets/img/ios_sdk/ios_sdk3.png?8064706b42f135ed57efdfbd6102a9a0) ###### 3단계: 원격 알림 처리 Braze SDK는 Braze에서 발생하는 원격 푸시 알림을 처리할 수 있습니다. 원격 알림을 Braze로 전달합니다. 그러면 SDK는 Braze에서 발생하지 않은 푸시 알림을 자동으로 무시합니다. 푸시 알림 확장의 `BrazeManager.swift` 파일에 다음 메서드를 추가합니다. ```swift func application( _ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void ) { Appboy.sharedInstance()?.register( application, didReceiveRemoteNotification: userInfo, fetchCompletionHandler: completionHandler ) } ``` ```objc - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler { [[Appboy sharedInstance] registerApplication:application didReceiveRemoteNotification:userInfo fetchCompletionHandler:completionHandler]; } ``` ###### 4단계: 알림 응답 전달 Braze SDK는 Braze에서 발생하는 푸시 알림의 응답을 처리할 수 있습니다. 알림의 응답을 Braze로 전달합니다. 그러면 SDK는 Braze에서 발생하지 않은 푸시 알림의 응답을 자동으로 무시합니다. `BrazeManager.swift` 파일에 다음 메서드를 추가합니다: ```swift func userNotificationCenter( _ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void ) { Appboy.sharedInstance()?.userNotificationCenter( center, didReceive: response, withCompletionHandler: completionHandler ) } ``` ```objc - (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)(void))completionHandler { [[Appboy sharedInstance] userNotificationCenter:center didReceiveNotificationResponse:response withCompletionHandler:completionHandler]; } ``` **Checkpoint:** 코드를 컴파일하고 애플리케이션을 실행합니다.

더 진행하기 전에 Braze 대시보드에서 푸시 알림을 직접 보내고 푸시 알림에서 분석이 기록되는지 관찰합니다. ### 사용자 변수 및 메서드 액세스 ##### 사용자 변수 및 메서드 생성 다음으로 `ABKUser` 변수와 메서드에 쉽게 액세스할 수 있어야 합니다. `BrazeManager.swift` 파일에 사용자 코드의 확장을 생성하여 다음과 같이 헬퍼 파일에서 어떤 용도를 지원하는지 보다 체계적으로 파악할 수 있도록 합니다. 1. `ABKUser` 개체는 iOS 애플리케이션에서 알려진 사용자 또는 익명 사용자를 나타냅니다. `ABKUser`를 검색하도록 컴퓨팅 변수를 추가합니다. 이 변수는 사용자에 대한 변수를 검색하는 데 재사용됩니다. 2. 사용자 변수를 쿼리하여 `userId` 에 쉽게 액세스할 수 있습니다. 다른 변수 중에서도 `ABKUser` 오브젝트는 `firstName`, `lastName`, `phone`, `homeCity` 등을 담당합니다. 3. 해당 `userId`로 `changeUser()`를 호출하여 사용자를 설정합니다. ```swift // MARK: - User extension BrazeManager { // 1 var user: ABKUser? { return Appboy.sharedInstance()?.user } // 2 var userId: String? { return user?.userID } // 3 func changeUser(_ userId: String) { Appboy.sharedInstance()?.changeUser(userId) } } ``` ```objc // MARK: - User // 1 - (ABKUser *)user { return [[Appboy sharedInstance] user]; } // 2 - (NSString *)userId { return [self user].userID; } // 3 - (void)changeUser:(NSString *)userId { [[Appboy sharedInstance] changeUser:userId]; } ``` **Checkpoint:** 코드를 컴파일하고 애플리케이션을 실행합니다.

로그인/가입에 성공한 사용자를 식별해 보세요. 적절한 사용자 식별자와 부적절한 사용자 식별자를 확실히 이해해야 합니다.

대시보드에서 더 진행하기 전에 사용자 식별자가 기록되는지 관찰합니다. ### 로그 분석 ##### 로그 사용자 지정 이벤트 메서드 생성 다음 Braze SDK `logCustomEvent` 메소드를 기반으로 일치하는 메소드를 생성합니다. **Braze `logCustomEvent` 참조 메서드**
`BrazeManager.swift` 파일만 Braze iOS SDK 메서드에 직접 액세스할 수 있기 때문에 의도된 것입니다. 따라서 일치하는 메서드를 생성하면 결과는 동일하며 프로덕션 코드에서 Braze iOS SDK에 직접 종속될 필요 없이 수행됩니다. ``` open func logCustomEvent(_ eventName: String, withProperties properties: [AnyHashable : Any]?) ``` **매칭 방법**
`Appboy` 오브젝트에서 Braze로 커스텀 이벤트를 기록합니다. `Properties`는 기본값이 nil인 선택적 매개변수입니다. 사용자 지정 이벤트에는 속성이 필요하지 않지만 이름이 있어야 합니다. ```swift func logCustomEvent(_ eventName: String, withProperties properties: [AnyHashable: Any]? = nil) { Appboy.sharedInstance()?.logCustomEvent(eventName, withProperties: properties) } ``` ```objc - (void)logCustomEvent:(NSString *)eventName withProperties:(nullable NSDictionary *)properties { [[Appboy sharedInstance] logCustomEvent:eventName withProperties:properties]; } ``` ##### 로그 사용자 지정 속성 만들기 메서드 SDK는 다양한 유형을 사용자 정의 속성으로 기록할 수 있습니다. 설정할 수 있는 각 값 유형에 대해 헬퍼 메서드를 만들 필요가 없습니다. 대신 적절한 값으로 필터링할 수 있는 하나의 메서드만 노출하세요. ``` - (BOOL)setCustomAttributeWithKey:(NSString *)key andBOOLValue:(BOOL)value; - (BOOL)setCustomAttributeWithKey:(NSString *)key andIntegerValue:(NSIntenger)value; - (BOOL)setCustomAttributeWithKey:(NSString *)key andDoubleValue:(double)value; - (BOOL)setCustomAttributeWithKey:(NSString *)key andStringValue:(NSString *)value; - (BOOL)setCustomAttributeWithKey:(NSString *)key andDateValue:(NSDate *)value; ``` 사용자 지정 속성은 `ABKUser` 객체에서 로깅됩니다. 속성에 설정할 수 있는 모든 유형을 포괄하는 **하나의 메서드**를 생성합니다. 분석 확장의 `BrazeManager.swift` 파일에 이 메서드를 추가합니다. 유효한 커스텀 속성 유형을 필터링하고 일치하는 유형과 연결된 메서드를 호출하면 됩니다. - `value` 매개변수는 `Equatable` 프로토콜을 따르는 일반 유형입니다. 이 작업은 명시적으로 수행되므로 유형이 Braze iOS SDK가 예상하는 것과 다르면 컴파일 시 오류가 발생합니다. - `key` 및 `value` 매개변수는 메서드에서 조건부로 래핑 해제되는 선택적 매개변수입니다. nil이 아닌 값이 Braze iOS SDK에 전달되도록 보장하는 한 가지 방법일 뿐입니다. ```swift func setCustomAttributeWithKey(_ key: String?, andValue value: T?) { guard let key = key, let value = value else { return } switch value.self { case let value as Date: user?.setCustomAttributeWithKey(key, andDateValue: value) case let value as Bool: user?.setCustomAttributeWithKey(key, andBOOLValue: value) case let value as String: user?.setCustomAttributeWithKey(key, andStringValue: value) case let value as Double: user?.setCustomAttributeWithKey(key, andDoubleValue: value) case let value as Int: user?.setCustomAttributeWithKey(key, andIntegerValue: value) default: return } } ``` ```objc - (void)setCustomAttributeWith:(NSString *)key andValue:(id)value { if ([value isKindOfClass:[NSDate class]]) { [[self user] setCustomAttributeWithKey:key andDateValue:value]; } else if ([value isKindOfClass:[NSString class]]) { [[self user] setCustomAttributeWithKey:key andStringValue:value]; } else if ([value isKindOfClass:[NSNumber class]]) { if (strcmp([value objCType], @encode(double)) == 0) { [[self user] setCustomAttributeWithKey:key andDoubleValue:[value doubleValue]]; } else if (strcmp([value objCType], @encode(int)) == 0) { [[self user] setCustomAttributeWithKey:key andIntegerValue:[value integerValue]]; } else if ([value boolValue]) { [[self user] setCustomAttributeWithKey:key andBOOLValue:[value boolValue]]; } } } ``` ##### 로그 구매 방법 만들기 다음으로, 다음 Braze SDK `logPurchase` 메서드를 기반으로 일치하는 메서드를 생성합니다. **Braze `logPurchase` 참조 메서드**
`BrazeManager.swift` 파일만 Braze iOS SDK 메서드에 직접 액세스할 수 있기 때문에 의도된 것입니다. 따라서 일치하는 메서드를 생성하면 결과는 동일하며 프로덕션 코드에서 Braze iOS SDK에 직접 종속될 필요 없이 수행됩니다. ``` open func logPurchase(_ productIdentifier: String, inCurrency currency: String, atPrice price: NSDecimalNumber, withoutQuantity quantity: UInt) ``` **매칭 방법**
`Appboy` 객체에서 구매를 Braze에 기록합니다. SDK에는 구매를 기록하는 여러 가지 방법이 있으며, 이는 한 가지 예일 뿐입니다. 이 메서드는 `NSDecimal` 및 `UInt` 객체 생성도 처리합니다. 이 부분을 어떻게 처리할 것인지는 여러분이 결정할 수 있으며, 이는 하나의 예시일 뿐입니다. ```swift func logPurchase(_ productIdentifier: String, inCurrency currency: String, atPrice price: String, withQuantity quantity: Int) { Appboy.sharedInstance()?.logPurchase(productIdentifier, inCurrency: currency, atPrice: NSDecimalNumber(string: price), withQuantity: UInt(quantity)) } ``` ```objc - (void)logPurchase:(NSString *)productIdentifier inCurrency:(nonnull NSString *)currencyCode atPrice:(nonnull NSDecimalNumber *)price withQuantity:(NSUInteger)quantity { [[Appboy sharedInstance] logPurchase:productIdentifier inCurrency:currencyCode atPrice:price withQuantity:quantity]; } ``` **Checkpoint:** 코드를 컴파일하고 애플리케이션을 실행합니다.

사용자 지정 이벤트를 로깅해 보세요.

대시보드에서 더 진행하기 전에 사용자 지정 이벤트가 기록되는지 확인합니다. ### 인앱 메시지 **Important:** 애플리케이션에서 이 채널을 사용할 계획이 없는 경우 다음 인앱 메시지 섹션은 통합에 필요하지 않습니다. ##### ABKInAppMessageUIDelegate 준수 그런 다음, `ABKInAppMessageUIDelegate`를 준수하도록 `BrazeManager.swift` 파일 코드를 활성화하여 관련 메서드를 직접 처리합니다. 위임을 준수하기 위한 코드는 `BrazeManager.swift` 파일의 `didFinishLaunching...` 메서드에 추가됩니다. 초기화 코드는 다음과 같이 완성됩니다: ```swift func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) { Appboy.start(withApiKey: apiKey, in: application, withLaunchOptions: launchOptions, withAppboyOptions: appboyOptions) let options: UNAuthorizationOptions = [.alert, .sound, .badge] UNUserNotificationCenter.current().requestAuthorization(options: options) { (granted, error) in Appboy.sharedInstance()?.pushAuthorization(fromUserNotificationCenter: granted) } UIApplication.shared.registerForRemoteNotifications() Appboy.sharedInstance()?.inAppMessageController.inAppMessageUIController?.setInAppMessageUIDelegate?(self) } ``` ```objc - (void)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { [Appboy startWithApiKey:[self apiKey] inApplication:application withLaunchOptions:launchOptions withAppboyOptions:[self appboyOptions]]; UNAuthorizationOptions options = (UNAuthorizationOptionSound | UNAuthorizationOptionAlert | UNAuthorizationOptionBadge); [[UNUserNotificationCenter currentNotificationCenter] requestAuthorizationWithOptions:options completionHandler:^(BOOL granted, NSError * _Nullable error) { [[Appboy sharedInstance] pushAuthorizationFromUserNotificationCenter:granted]; }]; [[UIApplication sharedApplication] registerForRemoteNotifications]; [[Appboy sharedInstance].inAppMessageController.inAppMessageUIController setInAppMessageUIDelegate:self]; } ``` ##### 델리게이트 메서드 추가 그런 다음 `ABKInAppMessageUIDelegate` 을 준수하는 확장자를 만듭니다. 분석 섹션에 다음 코드 스니펫을 추가합니다. `BrazeManager.swift` 오브젝트가 위임으로 설정되어 있으며, 여기에서 `BrazeManager.swift` 파일이 모든 `ABKInAppMessageUIDelegate` 메서드를 처리합니다. **Important:** `ABKInAppMessageUIDelegate`에는 필수 메서드가 제공되지 않지만 다음은 한 가지 예입니다. ```swift // MARK: - ABKInAppMessage UI Delegate extension AppboyManager: ABKInAppMessageUIDelegate{ func inAppMessageViewControllerWith(_ inAppMessage: ABKInAppMessage) -> ABKInAppMessageViewController { switch inAppMessage { case is ABKInAppMessageSlideup: return ABKInAppMessageSlideupViewController(inAppMessage: inAppMessage) case is ABKInAppMessageModal: return ABKInAppMessageModalViewController(inAppMessage: inAppMessage) case is ABKInAppMessageFull: return ABKInAppMessageFullViewController(inAppMessage: inAppMessage) case is ABKInAppMessageHTML: return ABKInAppMessageHTMLViewController(inAppMessage: inAppMessage) default: return ABKInAppMessageViewController(inAppMessage: inAppMessage) } ``` ```objc // MARK: - ABKInAppMessage UI Delegate - (ABKInAppMessageViewController *)inAppMessageViewControllerWithInAppMessage:(ABKInAppMessage *)inAppMessage { if ([inAppMessage isKindOfClass:[ABKInAppMessageSlideup class]]) { return [[ABKInAppMessageSlideupViewController alloc] initWithInAppMessage:inAppMessage]; } else if ([inAppMessage isKindOfClass:[ABKInAppMessageModal class]]) { return [[ABKInAppMessageModalViewController alloc] initWithInAppMessage:inAppMessage]; } else if ([inAppMessage isKindOfClass:[ABKInAppMessageFull class]]) { return [[ABKInAppMessageFullViewController alloc] initWithInAppMessage:inAppMessage]; } else if ([inAppMessage isKindOfClass:[ABKInAppMessageHTML class]]) { return [[ABKInAppMessageHTMLViewController alloc] initWithInAppMessage:inAppMessage]; } return nil; } ``` **Checkpoint:** 코드를 컴파일하고 애플리케이션을 실행합니다.

인앱 메시지를 직접 보내 보세요.

`BrazeManager.swift` 파일에서 예제 `ABKInAppMessageUIDelegate` 메서드의 항목에 중단점을 설정합니다. 더 진행하기 전에 인앱 메시지를 직접 보내고 중단점에 도달했는지 확인합니다. ### 콘텐츠 카드 **Important:** 애플리케이션에서 이 채널을 사용할 계획이 없는 경우 다음 콘텐츠 카드 섹션은 통합에 필요하지 않습니다. ##### 콘텐츠 카드 변수 및 메서드 만들기 프로덕션 코드에서 불필요한 `import AppboyUI` 문 없이도 콘텐츠 카드 보기 컨트롤러를 표시할 수 있습니다. `BrazeManager.swift` 파일에 콘텐츠 카드 코드의 확장을 생성하여 다음과 같이 헬퍼 파일에서 어떤 용도를 지원하는지 보다 체계적으로 파악할 수 있도록 합니다. 1. `ABKContentCardsTableViewController` 을 표시합니다. `navigationController` 옵션은 보기 컨트롤러를 표시하거나 푸시하는 데 필요한 유일한 매개변수입니다. 2. `ABKContentCardsTableViewController` 개체를 초기화하고 선택적으로 제목을 변경합니다. 또한 초기화된 보기 컨트롤러를 탐색 스택에 추가해야 합니다. ```swift // MARK: - Content Cards extension BrazeManager { // 1 func displayContentCards(navigationController: UINavigationController?) { // 2 let contentCardsVc = ABKContentCardsTableViewController() contentCardsVc.title = "Content Cards" navigationController?.pushViewController(contentCardsVc, animated: true) } } ``` ```objc // MARK: - Content Cards // 1 - (void)displayContentCards:(UINavigationController *)navigationController { // 2 ABKContentCardsTableViewController *contentCardsVc = [[ABKContentCardsTableViewController alloc] init]; contentCardsVc.title = @"Content Cards"; [navigationController pushViewController:contentCardsVc animated:YES]; } ``` **Checkpoint:** 코드를 컴파일하고 애플리케이션을 실행합니다.

더 진행하기 전에 애플리케이션에 `ABKContentCardsTableViewController`를 표시해 보세요. ## 다음 단계 축하합니다! 이 모범 사례 통합 가이드를 완료하셨습니다! 예제 `BrazeManager` 헬퍼 파일은 [GitHub](https://github.com/braze-inc/braze-growth-shares-ios-demo-app/blob/master/Braze-Demo/BrazeManager.swift)에서 찾을 수 있습니다. 나머지 프로덕션 코드에서 Braze iOS SDK에 대한 종속성을 분리했으므로 다음 고급 구현 가이드(선택 사항)를 확인하세요. - [고급 푸시 알림 구현 가이드](https://www.braze.com/docs/ko/ko/developer_guide/platforms/legacy_sdks/ios/push_notifications/implementation_guide/) - [고급 인앱 메시지 구현 가이드](https://www.braze.com/docs/ko/ko/developer_guide/platforms/legacy_sdks/ios/in-app_messaging/implementation_guide/) - [고급 콘텐츠 카드 구현 가이드](https://www.braze.com/docs/ko/ko/developer_guide/platforms/legacy_sdks/ios/content_cards/implementation_guide/) # iOS용 푸시 통합 Source: /docs/ko/developer_guide/platforms/legacy_sdks/ios/push_notifications/integration/index.md
**Warning:** [AppboyKit](https://github.com/Appboy/appboy-ios-sdk) (also known as the Objective-C SDK) is no longer supported and has been replaced by the [Swift SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=swift). It will no longer receive new features, bug fixes, security updates, or technical support—however, messaging and analytics will continue to function as normal. To learn more, see [Introducing the New Braze Swift SDK](https://www.braze.com/resources/articles/introducing-the-new-braze-swift-sdk). # 푸시 통합 {#push-integration} ## 1단계: APNs 토큰 업로드 {#step-1-upload-your-apns-token} Before you can send an iOS push notification using Braze, you need to upload your `.p8` push notification file, as described in [Apple's developer documentation](https://developer.apple.com/documentation/usernotifications/establishing-a-token-based-connection-to-apns): 1. In your Apple developer account, go to [**Certificates, Identifiers & Profiles**](https://developer.apple.com/account/ios/certificate). 2. Under **Keys**, select **All** and click the add button (+) in the upper-right corner. 3. Under **Key Description**, enter a unique name for the signing key. 4. Under **Key Services**, select the **Apple Push Notification service (APNs)** checkbox, then click **Continue**. Click **Confirm**. 5. Note the key ID. Click **Download** to generate and download the key. Make sure to save the downloaded file in a secure place, as you cannot download this more than once. 6. In Braze, go to **Settings** > **App Settings** and upload the `.p8` file under **Apple Push Certificate**. You can upload either your development or production push certificate. To test push notifications after your app is live in the App Store, its recommended to set up a separate workspace for the development version of your app. 7. When prompted, enter your app's [bundle ID](https://developer.apple.com/documentation/foundation/nsbundle/1418023-bundleidentifier), [key ID](https://developer.apple.com/help/account/manage-keys/get-a-key-identifier/), and [team ID](https://developer.apple.com/help/account/manage-your-team/locate-your-team-id). You'll also need to specify whether to send notifications to your app's development or production environment, which is defined by its provisioning profile. 8. When you're finished, select **Save**. ## 2단계: 푸시 기능 활성화 {#step-2-enable-push-capabilities} 프로젝트 설정의 **Capabilities** 탭에서 **Push Notifications** 기능이 켜져 있는지 확인합니다. ![](https://www.braze.com/docs/ko/ko/assets/img_archive/Enable_push_capabilities.png?8a3957eea917ba442294b7dbbe60732f) 개발 및 프로덕션 푸시 인증서가 따로 있는 경우 **General** 탭에서 **Automatically manage signing** 확인란을 선택 취소해야 합니다. Xcode의 자동 코드 서명 기능은 개발 서명만 수행하므로 각 빌드 구성에 대해 서로 다른 프로비저닝 프로필을 선택할 수 있습니다. !["General" 탭이 표시된 Xcode 프로젝트 설정. 이 탭에서는 "Automatically manage signing" 옵션이 선택 취소되어 있습니다.](https://www.braze.com/docs/ko/ko/assets/img_archive/xcode8_auto_signing.png?b00c3b1c9fb39c8f9977bc42343af466) ## 3단계: 푸시 알림 등록하기 {#step-3-register-for-push-notifications} 앱의 `application:didFinishLaunchingWithOptions:` 델리게이트 메서드에 적절한 코드 샘플을 포함해야 사용자 기기가 APNs에 등록할 수 있습니다. 애플리케이션의 메인 스레드에서 모든 푸시 통합 코드를 호출해야 합니다. Braze는 푸시 실행 버튼 지원을 위한 기본 푸시 카테고리도 제공하며, 이 카테고리는 푸시 등록 코드에 수동으로 추가해야 합니다. 추가 통합 단계는 [푸시 실행 버튼](https://www.braze.com/docs/ko/ko/developer_guide/platforms/legacy_sdks/ios/push_notifications/customization/action_buttons/)을 참조하세요. **Warning:** [푸시 모범 사례](https://www.braze.com/docs/ko/ko/developer_guide/platforms/legacy_sdks/ios/push_notifications/troubleshooting/)에서 설명한 대로 커스텀 푸시 프롬프트를 구현한 경우 앱에 푸시 권한을 부여한 후 **앱을 실행할 때마다** 다음 코드를 호출하고 있는지 확인합니다. **[기기 토큰은 임의로 변경](https://developer.apple.com/library/ios/documentation/iPhone/Conceptual/iPhoneOSProgrammingGuide/BackgroundExecution/BackgroundExecution.html)될 수 있으므로 앱을 APNs에 다시 등록해야 합니다.** ### UserNotification 프레임워크 사용(iOS 10 이상) {#using-usernotification-framework-ios-10} iOS 10에 도입된 `UserNotifications` 프레임워크(권장)를 사용하는 경우 앱 델리게이트의 `application:didFinishLaunchingWithOptions:` 메서드에 다음 코드를 추가합니다. **Important:** 다음 코드 샘플에는 임시 푸시 인증(5번째 줄 및 6번째 줄)을 위한 통합이 포함되어 있습니다. 앱에서 임시 권한 부여를 사용하지 않으려면 `requestAuthorization` 옵션에 `UNAuthorizationOptionProvisional`을 추가하는 코드 줄을 제거할 수 있습니다.
푸시 임시 인증에 대한 자세한 내용은 [iOS 알림 옵션](https://www.braze.com/docs/ko/ko/user_guide/channels/push/platform_specific_resources/ios/notification_options/)을 참조하세요. ```objc if (floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_9_x_Max) { UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter]; center.delegate = self; UNAuthorizationOptions options = UNAuthorizationOptionAlert | UNAuthorizationOptionSound | UNAuthorizationOptionBadge; if (@available(iOS 12.0, *)) { options = options | UNAuthorizationOptionProvisional; } [center requestAuthorizationWithOptions:options completionHandler:^(BOOL granted, NSError * _Nullable error) { [[Appboy sharedInstance] pushAuthorizationFromUserNotificationCenter:granted]; }]; [[UIApplication sharedApplication] registerForRemoteNotifications]; } else { UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:(UIUserNotificationTypeBadge | UIUserNotificationTypeAlert | UIUserNotificationTypeSound) categories:nil]; [[UIApplication sharedApplication] registerForRemoteNotifications]; [[UIApplication sharedApplication] registerUserNotificationSettings:settings]; } ``` ```swift if #available(iOS 10, *) { let center = UNUserNotificationCenter.current() center.delegate = self as? UNUserNotificationCenterDelegate var options: UNAuthorizationOptions = [.alert, .sound, .badge] if #available(iOS 12.0, *) { options = UNAuthorizationOptions(rawValue: options.rawValue | UNAuthorizationOptions.provisional.rawValue) } center.requestAuthorization(options: options) { (granted, error) in Appboy.sharedInstance()?.pushAuthorization(fromUserNotificationCenter: granted) } UIApplication.shared.registerForRemoteNotifications() } else { let types : UIUserNotificationType = [.alert, .badge, .sound] let setting : UIUserNotificationSettings = UIUserNotificationSettings(types:types, categories:nil) UIApplication.shared.registerUserNotificationSettings(setting) UIApplication.shared.registerForRemoteNotifications() } ``` **Warning:** 앱 실행이 완료되기 전에 `center.delegate = self`를 사용하여 델리게이트 오브젝트를 동기적으로 할당해야 합니다(가급적이면 `application:didFinishLaunchingWithOptions:`에서 할당). 그렇게 하지 않으면 앱에서 수신 푸시 알림을 놓칠 수 있습니다. 자세한 내용은 Apple의 [`UNUserNotificationCenterDelegate`](https://developer.apple.com/documentation/usernotifications/unusernotificationcenterdelegate) 설명서를 참조하세요. ### UserNotifications 프레임워크 없이 사용 {#without-usernotifications-framework} `UserNotifications` 프레임워크를 사용하지 않는 경우 앱 델리게이트의 `application:didFinishLaunchingWithOptions:` 메서드에 다음 코드를 추가하세요: ```objc UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:(UIUserNotificationTypeBadge | UIUserNotificationTypeAlert | UIUserNotificationTypeSound) categories:nil]; [[UIApplication sharedApplication] registerForRemoteNotifications]; [[UIApplication sharedApplication] registerUserNotificationSettings:settings]; ``` ```swift let types : UIUserNotificationType = UIUserNotificationType.Badge | UIUserNotificationType.Sound | UIUserNotificationType.Alert var setting : UIUserNotificationSettings = UIUserNotificationSettings(forTypes: types, categories: nil) UIApplication.shared.registerUserNotificationSettings(setting) UIApplication.shared.registerForRemoteNotifications() ``` ## 4단계: Braze에 푸시 토큰 등록 {#step-4-register-push-tokens-with-braze} APNs 등록이 완료되면 사용자가 푸시 알림을 받을 수 있도록 결과 `deviceToken`을 Braze에 전달하기 위해 다음 메서드를 변경해야 합니다: `application:didRegisterForRemoteNotificationsWithDeviceToken:` 메서드에 다음 코드를 추가합니다: ```objc [[Appboy sharedInstance] registerDeviceToken:deviceToken]; ``` 앱의 `application(_:didRegisterForRemoteNotificationsWithDeviceToken:)` 메서드에 다음 코드를 추가합니다: ```swift Appboy.sharedInstance()?.registerDeviceToken(deviceToken) ``` **Important:** `application:didRegisterForRemoteNotificationsWithDeviceToken:` 델리게이트 메서드는 `[[UIApplication sharedApplication] registerForRemoteNotifications]` 호출 후 항상 호출됩니다. 다른 푸시 서비스에서 Braze로 마이그레이션하고 사용자 기기가 이미 APNs에 등록되어 있는 경우, 이 메서드는 다음에 호출될 때 기존 등록에서 토큰을 수집하며, 사용자는 푸시에 다시 옵트인하지 않아도 됩니다. ## 5단계: 푸시 처리 활성화 {#step-5-enable-push-handling} 다음 코드는 수신된 푸시 알림을 Braze에 전달하며, 푸시 분석 및 링크 처리를 로깅하는 데 필요합니다. 애플리케이션의 메인 스레드에서 모든 푸시 통합 코드를 호출해야 합니다. ### iOS 10+ iOS 10 이상을 대상으로 빌드할 때는 `UserNotifications` 프레임워크를 통합하고 다음을 수행하는 것이 좋습니다: 애플리케이션의 `application:didReceiveRemoteNotification:fetchCompletionHandler:` 메서드에 다음 코드를 추가합니다: ```objc [[Appboy sharedInstance] registerApplication:application didReceiveRemoteNotification:userInfo fetchCompletionHandler:completionHandler]; ``` 다음으로 앱의 `(void)userNotificationCenter:didReceiveNotificationResponse:withCompletionHandler:` 메서드에 다음 코드를 추가합니다: ```objc [[Appboy sharedInstance] userNotificationCenter:center didReceiveNotificationResponse:response withCompletionHandler:completionHandler]; ``` **포그라운드 푸시 처리** 앱이 포그라운드에 있는 동안 푸시 알림을 표시하려면 `userNotificationCenter:willPresentNotification:withCompletionHandler:`를 구현합니다: ```objc - (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler { if (@available(iOS 14.0, *)) { completionHandler(UNNotificationPresentationOptionList | UNNotificationPresentationOptionBanner); } else { completionHandler(UNNotificationPresentationOptionAlert); } } ``` 포그라운드 알림을 클릭하면 iOS 10 푸시 델리게이트(`userNotificationCenter:didReceiveNotificationResponse:withCompletionHandler:`)가 호출되고 Braze는 푸시 클릭 이벤트를 기록합니다. 앱의 `application(_:didReceiveRemoteNotification:fetchCompletionHandler:)` 메서드에 다음 코드를 추가합니다: ```swift Appboy.sharedInstance()?.register(application, didReceiveRemoteNotification: userInfo, fetchCompletionHandler: completionHandler) ``` 다음으로 앱의 `userNotificationCenter(_:didReceive:withCompletionHandler:)` 메서드에 다음 코드를 추가합니다: ```swift Appboy.sharedInstance()?.userNotificationCenter(center, didReceive: response, withCompletionHandler: completionHandler) ``` **포그라운드 푸시 처리** 앱이 포그라운드에 있는 동안 푸시 알림을 표시하려면 `userNotificationCenter(_:willPresent:withCompletionHandler:)`를 구현합니다: ```swift func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) { if #available(iOS 14.0, *) { completionHandler([.list, .banner]); } else { completionHandler([.alert]); } } ``` 포그라운드 알림을 클릭하면 iOS 10 푸시 델리게이트(`userNotificationCenter(_:didReceive:withCompletionHandler:)`)가 호출되고 Braze는 푸시 클릭 이벤트를 기록합니다. ### iOS 10 이전 버전 {#pre-ios-10} iOS 10에서는 푸시를 클릭해도 더 이상 `application:didReceiveRemoteNotification:fetchCompletionHandler:`가 호출되지 않도록 동작이 변경되었습니다. 따라서 iOS 10 이상 대상 빌드로 업데이트하지 않고 `UserNotifications` 프레임워크를 사용하지 않는 경우, 이전 통합과 달리 기존 스타일의 델리게이트 양쪽 모두에서 Braze를 호출해야 합니다. SDK < iOS 10을 대상으로 빌드하는 앱의 경우 다음 지침을 따르세요: 푸시 알림에서 열람 추적을 활성화하려면 앱의 `application:didReceiveRemoteNotification:fetchCompletionHandler:` 메서드에 다음 코드를 추가합니다: ```objc [[Appboy sharedInstance] registerApplication:application didReceiveRemoteNotification:userInfo fetchCompletionHandler:completionHandler]; ``` iOS 10에서 푸시 분석을 지원하려면 앱의 `application:didReceiveRemoteNotification:` 델리게이트 메서드에 다음 코드도 추가해야 합니다: ```objc [[Appboy sharedInstance] registerApplication:application didReceiveRemoteNotification:userInfo]; ``` 푸시 알림에서 열람 추적을 활성화하려면 앱의 `application(_:didReceiveRemoteNotification:fetchCompletionHandler:)` 메서드에 다음 코드를 추가합니다: ```swift Appboy.sharedInstance()?.register(application, didReceiveRemoteNotification: userInfo, fetchCompletionHandler: completionHandler) ``` iOS 10에서 푸시 분석을 지원하려면 앱의 `application(_:didReceiveRemoteNotification:)` 델리게이트 메서드에 다음 코드도 추가해야 합니다: ```swift Appboy.sharedInstance()?.register(application, didReceiveRemoteNotification: userInfo) ``` ## 6단계: 딥링킹 {#step-6-deep-linking} 푸시에서 앱으로의 딥링킹은 표준 푸시 통합 설명서를 통해 자동으로 처리됩니다. 앱의 특정 위치에 딥링크를 추가하는 방법에 대해 자세히 알아보려면 [고급 사용 사례](https://www.braze.com/docs/ko/ko/developer_guide/platform_integration_guides/ios/advanced_use_cases/linking/#linking-implementation)를 참조하세요. ## 7단계: 단위 테스트(선택 사항) {#step-7-unit-tests-optional} 방금 수행한 통합 단계에 대한 테스트 커버리지를 추가하려면 [푸시 단위 테스트](https://www.braze.com/docs/ko/ko/developer_guide/platforms/legacy_sdks/ios/push_notifications/unit_tests/)를 구현하세요. # iOS 푸시 사용자 지정 Source: /docs/ko/developer_guide/platforms/legacy_sdks/ios/push_notifications/customization/index.md

# iOS용 푸시 액션 버튼 Source: /docs/ko/developer_guide/platforms/legacy_sdks/ios/push_notifications/customization/action_buttons/index.md
**Warning:** [AppboyKit](https://github.com/Appboy/appboy-ios-sdk) (also known as the Objective-C SDK) is no longer supported and has been replaced by the [Swift SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=swift). It will no longer receive new features, bug fixes, security updates, or technical support—however, messaging and analytics will continue to function as normal. To learn more, see [Introducing the New Braze Swift SDK](https://www.braze.com/resources/articles/introducing-the-new-braze-swift-sdk). # 실행 버튼 {#push-action-buttons-integration} Braze iOS SDK는 각 푸시 실행 버튼에 대한 URL 처리 지원을 포함하여 기본 푸시 카테고리를 지원합니다. 현재 기본 카테고리에는 네 가지 푸시 액션 버튼 세트가 있습니다: `Accept`/`Decline`, `Yes`/`No`, `Confirm`/`Cancel`, `More` 입니다. ![푸시 메시지를 아래로 당겨서 두 개의 사용자 지정 가능한 작업 버튼을 표시하는 GIF입니다.](https://www.braze.com/docs/ko/ko/assets/img_archive/iOS8Action.gif?d3553a68ae1aa0c3a58da9e65174f404) 기본 푸시 카테고리를 등록하려면 통합 지침을 따릅니다. ## 1단계: Braze 기본 푸시 카테고리 추가하기 [푸시를 등록할](https://www.braze.com/docs/ko/ko/developer_guide/platform_integration_guides/ios/push_notifications/integration/#step-4-register-push-tokens-with-braze) 때 다음 코드를 사용하여 기본 푸시 카테고리에 등록하세요: ```objc // For UserNotification.framework (iOS 10+ only) NSSet *appboyCategories = [ABKPushUtils getAppboyUNNotificationCategorySet]; [[UNUserNotificationCenter currentNotificationCenter] setNotificationCategories:appboyCategories]; // For UIUserNotificationSettings (before iOS 10) NSSet *appboyCategories = [ABKPushUtils getAppboyUIUserNotificationCategorySet]; UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeBadge categories:appboyCategories]; [[UIApplication sharedApplication] registerUserNotificationSettings:settings]; ``` ```swift // For UserNotification.framework (iOS 10+ only) let appboyCategories = ABKPushUtils.getAppboyUNNotificationCategorySet() UNUserNotificationCenter.current().setNotificationCategories(appboyCategories) // For UIUserNotificationSettings (before iOS 10) let appboyCategories = ABKPushUtils.getAppboyUIUserNotificationCategorySet() let settings = UIUserNotificationSettings.init(types: .badge, categories: appboyCategories) UIApplication.shared.registerUserNotificationSettings(settings) ``` 백그라운드 활성화 모드에서 푸시 동작 버튼을 클릭하면 알림만 해제되고 앱은 열리지 않습니다. 다음에 사용자가 앱을 열면 이 작업에 대한 버튼 클릭 분석이 서버로 플러시됩니다. 나만의 사용자 지정 알림 카테고리를 만들려면 [작업 버튼 사용자 지정을](https://www.braze.com/docs/ko/ko/developer_guide/platform_integration_guides/ios/push_notifications/customization/action_buttons/#push-category-customization) 참조하세요. ## 2단계: 대화형 푸시 처리 사용 `UNNotification` 프레임워크를 사용하고 Braze [델리게이트를](https://www.braze.com/docs/ko/ko/developer_guide/platform_integration_guides/ios/push_notifications/integration/#step-5-enable-push-handling) 구현한 경우 이 메서드가 이미 통합되어 있을 것입니다. 클릭 분석 및 URL 라우팅을 포함한 푸시 액션 버튼 처리를 활성화하려면 앱의 `(void)userNotificationCenter:didReceiveNotificationResponse:withCompletionHandler:` 델리게이트 메서드에 다음 코드를 추가하세요: ```objc [[Appboy sharedInstance] userNotificationCenter:center didReceiveNotificationResponse:response withCompletionHandler:completionHandler]; ``` ```swift Appboy.sharedInstance()?.userNotificationCenter(center, didReceive: response, withCompletionHandler: completionHandler) ``` UNNotification 프레임워크를 사용하지 않는 경우, 앱의 `application:handleActionWithIdentifier:forRemoteNotification:completionHandler:`에 다음 코드를 추가하여 푸시 실행 버튼 처리를 활성화해야 합니다. ```objc [[Appboy sharedInstance] getActionWithIdentifier:identifier forRemoteNotification:userInfo completionHandler:completionHandler]; ``` ```swift Appboy.sharedInstance()?.getActionWithIdentifier(identifier, forRemoteNotification: userInfo,, completionHandler: completionHandler) ``` **Important:** `handleActionWithIdentifier`를 사용하시는 사용자는 `UNNotification` 프레임워크를 사용하기 시작하도록 강력히 권장합니다. [`handleActionWithIdentifier`](https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1623068-application?language=objc)의 사용 중단으로 인해 권장합니다. ## 푸시 카테고리 사용자 지정 Braze는 [기본 푸시 카테고리](https://www.braze.com/docs/ko/ko/developer_guide/platforms/legacy_sdks/ios/push_notifications/customization/action_buttons/) 세트를 제공하는 것 외에도 사용자 지정 알림 카테고리 및 작업을 지원합니다. 애플리케이션에 카테고리를 등록한 후 Braze 대시보드를 사용하여 사용자에게 알림 카테고리를 보낼 수 있습니다. `UserNotifications` 프레임워크를 사용하지 않는 경우 [대체 카테고리](https://developer.apple.com/documentation/usernotifications/unnotificationcategory) 문서를 참조하세요. 그런 다음 대시보드를 통해 이러한 카테고리를 푸시 알림에 할당하여 디자인의 액션 버튼 구성을 트리거할 수 있습니다. 다음은 기기에 표시되는 `LIKE_CATEGORY`를 활용하는 예제입니다. !['싫어요' 및 '좋아요' 푸시 동작 버튼 두 개를 표시하는 푸시 메시지입니다.](https://www.braze.com/docs/ko/ko/assets/img_archive/push_example_category.png?342eb7b8bc6d24142ee32606e22f8eee) # iOS용 사용자 지정 푸시 알림 소리 Source: /docs/ko/developer_guide/platforms/legacy_sdks/ios/push_notifications/customization/custom_sounds/index.md
**Warning:** [AppboyKit](https://github.com/Appboy/appboy-ios-sdk) (also known as the Objective-C SDK) is no longer supported and has been replaced by the [Swift SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=swift). It will no longer receive new features, bug fixes, security updates, or technical support—however, messaging and analytics will continue to function as normal. To learn more, see [Introducing the New Braze Swift SDK](https://www.braze.com/resources/articles/introducing-the-new-braze-swift-sdk). # 사용자 지정 사운드 ## 1단계: 앱에서 사운드 호스팅하기 사용자 지정 푸시 알림 사운드는 클라이언트 애플리케이션의 기본 번들 내에서 로컬로 호스팅해야 합니다. 허용되는 오디오 데이터 형식은 다음과 같습니다: - 선형 PCM - MA4 - µLaw - aLaw 오디오 데이터를 AIFF, WAV 또는 CAF 파일로 패키징할 수 있습니다. Xcode에서 사운드 파일을 프로젝트에 애플리케이션 번들의 지역화되지 않은 리소스로 추가합니다. afconvert 툴을 사용하여 사운드를 변환할 수 있습니다. 예를 들어 16비트 선형 PCM 시스템 사운드 Submarine.aiff를 CAF 파일에서 IMA4 오디오로 변환하려면 터미널에서 다음 명령을 사용합니다. ```bash afconvert /System/Library/Sounds/Submarine.aiff ~/Desktop/sub.caf -d ima4 -f caff -v ``` QuickTime Player에서 사운드를 열고 **동영상** 메뉴에서 **동영상 검사기 표시**를 선택해 사운드를 검사하여 데이터 형식을 확인할 수 있습니다. 사용자 지정 사운드는 재생 시 30초 미만이어야 합니다. 커스텀 사운드가 이 제한을 초과하면 기본 시스템 사운드가 대신 재생됩니다. ## 2단계: 대시보드에 사운드에 대한 프로토콜 URL 제공 사운드는 앱 내에서 로컬로 호스팅되어야 합니다. 푸시 작성기의 **사운드** 필드 내에서 앱의 사운드 파일 위치로 연결되는 프로토콜 URL을 지정해야 합니다. 이 필드에서 '기본값'을 지정하면 기기에서 기본 알림 사운드가 재생됩니다. 이는 다음 스크린샷과 같이 [메시징 API](https://www.braze.com/docs/ko/ko/api/endpoints/messaging/) 또는 푸시 작성기의 **설정** 아래 대시보드를 통해 지정할 수 있습니다: ![](https://www.braze.com/docs/ko/ko/assets/img_archive/sound_push_ios.png?c035b34ffb6c0f720f6d2c08ca1ba2b2) 지정한 사운드 파일이 존재하지 않거나 'default' 키워드가 입력된 경우 Braze는 기기의 기본 알림 사운드를 사용합니다. 대시보드 외에도 [메시징 API를](https://www.braze.com/docs/ko/ko/api/endpoints/messaging/) 통해 사운드를 구성할 수도 있습니다. 자세한 내용은 [사용자 지정 경고음 준비에](https://developer.apple.com/library/content/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/SupportingNotificationsinYourApp.html) 관한 Apple 개발자 문서를 참조하세요. # iOS용 리치 푸시 알림 Source: /docs/ko/developer_guide/platforms/legacy_sdks/ios/push_notifications/customization/rich_notifications/index.md
**Warning:** [AppboyKit](https://github.com/Appboy/appboy-ios-sdk) (also known as the Objective-C SDK) is no longer supported and has been replaced by the [Swift SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=swift). It will no longer receive new features, bug fixes, security updates, or technical support—however, messaging and analytics will continue to function as normal. To learn more, see [Introducing the New Braze Swift SDK](https://www.braze.com/resources/articles/introducing-the-new-braze-swift-sdk). # iOS 10 리치 알림 iOS 10에서는 이미지, GIF 및 비디오를 포함하는 푸시 알림을 보낼 수 있는 기능이 도입되었습니다. 이 기능을 활성화하려면 클라이언트는 푸시 페이로드를 표시하기 전에 이를 수정할 수 있는 새로운 유형의 확장인 `Service Extension`을 생성해야 합니다. ## 서비스 확장 만들기 [`Notification Service Extension`](https://developer.apple.com/reference/usernotifications/unnotificationserviceextension)을 생성하려면 Xcode에서 **파일 > 새로 만들기 > 대상**으로 이동하여 **알림 서비스 확장**을 선택합니다. ![](https://www.braze.com/docs/ko/ko/assets/img_archive/ios10_se_at.png?ad077697c9a4c7c7bc3ca07a6405c05d){: style="max-width:90%"} **애플리케이션에 임베드**가 애플리케이션에 확장을 임베드하도록 설정되었는지 확인합니다. ## 서비스 확장 설정 `Notification Service Extension`은 앱과 함께 번들로 제공되는 자체 바이너리입니다. [Apple 개발자 포털에서](https://developer.apple.com) 자체 앱 ID 및 프로비저닝 프로필을 사용하여 설정해야 합니다. `Notification Service Extension`의 번들 ID는 기본 앱 대상의 번들 ID와 구별되어야 합니다. 예를 들어 앱의 번들 ID가 `com.company.appname`인 경우 서비스 확장에 `com.company.appname.AppNameServiceExtension`을 사용할 수 있습니다. ### Braze와 함께 작동하도록 서비스 확장 구성하기 Braze는 `ab` 키 아래 APN 페이로드에 리치 콘텐츠를 구성, 다운로드 및 표시하는 데 사용하는 첨부 파일 페이로드를 전송합니다. 예를 들어, 다음과 같습니다. ```json { "ab" : { ... "att" : { "url" : "http://mysite.com/myimage.jpg", "type" : "jpg" } }, "aps" : { ... } } ``` 관련 페이로드 값은 다음과 같습니다: ```objc // The Braze dictionary key static NSString *const AppboyAPNSDictionaryKey = @"ab"; // The attachment dictionary static NSString *const AppboyAPNSDictionaryAttachmentKey = @"att"; // The attachment URL static NSString *const AppboyAPNSDictionaryAttachmentURLKey = @"url"; // The type of the attachment - a suffix for the file you save static NSString *const AppboyAPNSDictionaryAttachmentTypeKey = @"type"; ``` Braze 페이로드가 포함된 푸시를 수동으로 표시하려면 `AppboyAPNSDictionaryAttachmentURLKey` 아래의 값에서 콘텐츠를 다운로드하고 `AppboyAPNSDictionaryAttachmentTypeKey` 키 아래에 저장된 파일 형식으로 파일로 저장한 다음, 알림 첨부 파일에 추가합니다. ### 코드 예제 서비스 확장은 Objective-C 또는 Swift로 작성할 수 있습니다. Objective-C 샘플 코드를 사용하려면 `Notification Service Extension` 대상에서 자동 생성된 `NotificationService.m`의 콘텐츠를 Appboy [`NotificationService.m`](https://github.com/Appboy/appboy-ios-sdk/blob/master/Example/StopwatchNotificationService/NotificationService.m)으로 바꿉니다. Swift 샘플 코드를 사용하려면 `Notification Service Extension` 대상에서 자동 생성된 `NotificationService.swift`의 콘텐츠를 Appboy [`NotificationService.swift`](https://github.com/Appboy/appboy-ios-sdk/blob/master/HelloSwift/HelloSwiftNotificationExtension/NotificationService.swift)로 바꿉니다. ## 대시보드에서 리치 알림 만들기 Braze 대시보드에서 리치 알림을 만들려면 iOS 푸시를 만들고, 이미지 또는 GIF를 첨부하거나, 이미지, GIF 또는 동영상을 호스팅하는 URL을 입력합니다. 푸시 알림을 받으면 자산이 다운로드되므로 콘텐츠를 호스팅하는 경우 요청이 동시에 급증하는 상황에 대비해야 합니다. 지원되는 파일 형식 및 크기 목록은 [`unnotificationattachment`](https://developer.apple.com/reference/usernotifications/unnotificationattachment) 에서 지원되는 파일 유형 및 크기 목록을 확인하세요. # iOS용 푸시 알림 배지 수 Source: /docs/ko/developer_guide/platforms/legacy_sdks/ios/push_notifications/customization/badges/index.md
**Warning:** [AppboyKit](https://github.com/Appboy/appboy-ios-sdk) (also known as the Objective-C SDK) is no longer supported and has been replaced by the [Swift SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=swift). It will no longer receive new features, bug fixes, security updates, or technical support—however, messaging and analytics will continue to function as normal. To learn more, see [Introducing the New Braze Swift SDK](https://www.braze.com/resources/articles/introducing-the-new-braze-swift-sdk). # Badges Braze 대시보드를 통해 푸시 알림을 작성할 때 원하는 배지 수를 지정할 수 있습니다. 애플리케이션의 [`applicationIconBadgeNumber`](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIApplication_Class/index.html#//apple_ref/occ/instp/UIApplication/applicationIconBadgeNumber) 속성정보 또는 [원격 알림 페이로드](https://developer.apple.com/library/content/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/CreatingtheNotificationPayload.html#//apple_ref/doc/uid/TP40008194-CH10-SW1)를 통해 배지 수를 수동으로 업데이트할 수도 있습니다. Braze는 앱이 포그라운드에 있는 상태에서 Braze 알림을 받으면 배지 수도 지웁니다. 정상적인 앱 작동의 일부로 배지를 지우거나 배지를 지우는 푸시를 전송하여 배지를 지우려는 계획이 없는 경우 앱의 `applicationDidBecomeActive:` 위임 메서드에 다음 코드를 추가하여 앱이 활성화되면 배지를 지워야 합니다. ```objc // For iOS 16.0+ UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter]; [center setBadgeCount:0 withCompletionHandler:^(NSError * _Nullable error) { if (error != nil) { // Handle errors } }]; // Prior to iOS 16. Deprecated in iOS 17+. [UIApplication sharedApplication].applicationIconBadgeNumber = 0; ``` ```swift // For iOS 16.0+ let center = UNUserNotificationCenter.current() do { try await center.setBadgeCount(0) } catch { // Handle errors } // Prior to iOS 16. Deprecated in iOS 17+. UIApplication.shared.applicationIconBadgeNumber = 0 ``` 배지 번호를 0으로 설정하면 알림 센터의 알림도 지워집니다. 따라서 푸시 페이로드에서 배지 번호를 설정하지 않더라도 계속 배지 번호를 0으로 설정하여 사용자가 푸시를 클릭한 후 알림 센터에서 푸시 알림을 제거할 수 있습니다. # iOS용 Braze 내부 푸시 알림 무시하기 Source: /docs/ko/developer_guide/platforms/legacy_sdks/ios/push_notifications/customization/ignoring_internal_push/index.md
**Warning:** [AppboyKit](https://github.com/Appboy/appboy-ios-sdk) (also known as the Objective-C SDK) is no longer supported and has been replaced by the [Swift SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=swift). It will no longer receive new features, bug fixes, security updates, or technical support—however, messaging and analytics will continue to function as normal. To learn more, see [Introducing the New Braze Swift SDK](https://www.braze.com/resources/articles/introducing-the-new-braze-swift-sdk). # Braze 내부 푸시 알림 무시하기 Braze는 특정 고급 기능의 내부 구현을 위해 무음 푸시 알림을 사용합니다. 대부분의 통합에서는 앱 측에서 변경하지 않아도 됩니다. 그러나 내부 푸시 알림(예: 제거 추적 또는 지오펜스)에 의존하는 Braze 기능을 통합하는 경우 앱을 업데이트하여 내부 푸시를 무시할 수 있습니다. 앱이 애플리케이션 실행 또는 백그라운드 푸시에서 자동 작업을 수행하는 경우 해당 활동이 내부 푸시 알림에 의해 트리거되지 않도록 활동의 게이팅을 고려해야 합니다. 예를 들어, 백그라운드 푸시 또는 애플리케이션 실행 시 새 콘텐츠를 위해 서버를 호출하는 로직이 있는 경우 불필요한 네트워크 트래픽을 유발할 수 있으므로 내부 푸시 트리거를 원치 않을 수 있습니다. 또한 Braze는 특정 종류의 내부 푸시를 거의 동시에 모든 사용자에게 전송하기 때문에 내부 푸시에서 실행 시 네트워크 호출을 게이팅하지 않으면 상당한 서버 부하가 발생할 수 있습니다. ## 앱에서 자동 동작 확인 다음 위치에서 애플리케이션의 자동 동작을 확인하고 내부 푸시를 무시하도록 코드를 업데이트해야 합니다. 1. **푸시 수신기.** 백그라운드 푸시 알림은 `UIApplicationDelegate`에서 `application:didReceiveRemoteNotification:fetchCompletionHandler:`를 호출합니다. 2. **애플리케이션 위임.** 백그라운드 푸시는 [일시 중단](https://developer.apple.com/library/ios/documentation/iPhone/Conceptual/iPhoneOSProgrammingGuide/TheAppLifeCycle/TheAppLifeCycle.html#//apple_ref/doc/uid/TP40007072-CH2-SW3)된 앱을 백그라운드로 실행하여 `UIApplicationDelegate`에서 `application:willFinishLaunchingWithOptions:` 및 `application:didFinishLaunchingWithOptions:` 메서드를 트리거할 수 있습니다. 백그라운드 푸시에서 애플리케이션이 실행되었는지 확인하려면 이러한 메서드의 `launchOptions`를 확인할 수 있습니다. ## Braze 내부 푸시 유틸리티 방법 사용 `ABKPushUtils`에서 유틸리티 메서드를 사용하여 앱이 Braze 내부 푸시를 수신했는지 또는 앱이 실행되었는지 확인할 수 있습니다. `isAppboyInternalRemoteNotification:`은 모든 Braze 내부 푸시 알림에서 `YES`를 반환하고 `isUninstallTrackingRemoteNotification:` 및 `isGeofencesSyncRemoteNotification:`는 각각 제거 추적 및 지오펜스 동기화에 대해 `YES`를 반환합니다. 메서드 선언은 [`ABKPushUtils.h`](https://github.com/Appboy/appboy-ios-sdk/blob/master/AppboyKit/include/ABKPushUtils.h)를 참조하세요. ## 구현 예시 {#internal-push-implementation-example} ```objc - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { NSDictionary *pushDictionary = launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey]; BOOL launchedFromAppboyInternalPush = pushDictionary && [ABKPushUtils isAppboyInternalRemoteNotification:pushDictionary]; if (!launchedFromAppboyInternalPush) { // ... Gated logic here (such as pinging your server to download content) ... } } ``` ```objc - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult result))completionHandler { if (![ABKPushUtils isAppboyInternalRemoteNotification:userInfo]) { // ... Gated logic here (such as pinging server for content) ... } } ``` ```swift func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey : Any]? = nil) -> Bool { let pushDictionary = launchOptions?[UIApplicationLaunchOptionsKey.remoteNotification] as? NSDictionary as? [AnyHashable : Any] ?? [:] let launchedFromAppboyInternalPush = ABKPushUtils.isAppboyInternalRemoteNotification(pushDictionary) if (!launchedFromAppboyInternalPush) { // ... Gated logic here (such as pinging your server to download content) ... } } ``` ```swift func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) { if (!ABKPushUtils.isAppboyInternalRemoteNotification(userInfo)) { // ... Gated logic here (such as pinging server for content) ... } } ``` # 고급 푸시 설정 Source: /docs/ko/developer_guide/platforms/legacy_sdks/ios/push_notifications/customization/advanced_settings/index.md
**Warning:** [AppboyKit](https://github.com/Appboy/appboy-ios-sdk) (also known as the Objective-C SDK) is no longer supported and has been replaced by the [Swift SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=swift). It will no longer receive new features, bug fixes, security updates, or technical support—however, messaging and analytics will continue to function as normal. To learn more, see [Introducing the New Braze Swift SDK](https://www.braze.com/resources/articles/introducing-the-new-braze-swift-sdk). # 고급 설정 {#advanced-settings} 푸시 캠페인을 생성할 때 작성 단계에서 **Settings**을 선택하여 사용 가능한 고급 설정을 확인합니다. ![](https://www.braze.com/docs/ko/ko/assets/img_archive/ios_advanced_settings.png?16f142abe70d854830708b0cb21d9465) ## 푸시 키-값 페어에서 데이터 추출하기 {#extracting-data-from-push-key-value-pairs} Braze에서는 커스텀 정의 문자열 키-값 페어(`extras`)를 푸시 알림과 함께 애플리케이션에 전송할 수 있습니다. 추가 항목은 대시보드 또는 API를 통해 정의할 수 있으며, 푸시 위임 구현에 전달되는 `notification` 사전 내에서 키-값 페어로 사용할 수 있습니다. ## 알림 옵션 {#alert-options} **Alert Options** 확인란을 선택하여 알림이 기기에 표시되는 방식을 조정할 수 있는 키-값 드롭다운을 확인합니다. ## 콘텐츠 가용 플래그 추가 {#adding-content-available-flag} **Add Content-Available Flag** 확인란을 선택하여 기기가 백그라운드에서 새 콘텐츠를 다운로드하도록 지시합니다. 가장 일반적으로 [무음 알림](https://www.braze.com/docs/ko/ko/developer_guide/platforms/legacy_sdks/ios/push_notifications/silent_push_notifications/)을 보내고 싶은 경우 이 옵션을 선택할 수 있습니다. ## 변경 가능한 콘텐츠 플래그 추가 {#adding-mutable-content-flag} iOS 10 이상 기기에서 고급 수신기 커스터마이징을 활성화하려면 **Add Mutable-Content Flag** 확인란을 선택합니다. 이 플래그는 이 확인란의 값에 관계없이 [리치 알림](https://www.braze.com/docs/ko/ko/developer_guide/platforms/legacy_sdks/ios/push_notifications/customization/rich_notifications/)을 작성할 때 자동으로 전송됩니다. ## 앱 배지 수 업데이트 {#update-app-badge-count} 배지 수를 업데이트하려는 숫자를 입력하거나 Liquid 구문을 활용하여 커스텀 조건을 설정하세요. 애플리케이션의 `applicationIconBadgeNumber` 속성정보 또는 푸시 알림 페이로드를 통해 배지 수를 수동으로 업데이트할 수도 있습니다. 자세한 내용은 [배지 수](https://www.braze.com/docs/ko/ko/developer_guide/platforms/legacy_sdks/ios/push_notifications/customization/badges/) 관련 문서를 참조하세요. ## 소리 {#sounds} 여기에서 앱 번들에 있는 사운드 파일의 경로를 입력하여 푸시 메시지를 수신할 때 재생할 사운드를 지정할 수 있습니다. 지정한 사운드 파일이 존재하지 않거나 "default" 키워드가 입력된 경우 Braze는 기기의 기본 알림 사운드를 사용합니다. 커스터마이징에 대한 자세한 내용은 [커스텀 사운드](https://www.braze.com/docs/ko/ko/developer_guide/platforms/legacy_sdks/ios/push_notifications/customization/custom_sounds/) 전용 문서를 참조하세요. ## 축소 ID {#collapse-id} 축소 ID를 지정하여 유사한 알림을 결합합니다. 동일한 축소 ID로 여러 알림을 발송한 경우, 기기에는 가장 최근 수신된 알림만 표시됩니다. Apple의 [통합 알림](https://developer.apple.com/library/content/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/APNSOverview.html#//apple_ref/doc/uid/TP40008194-CH8-SW1) 관련 문서를 참조하세요. ## 만료 {#expiry} **Expiry** 확인란을 선택하면 메시지의 만료 시간을 설정할 수 있습니다. 사용자 기기의 연결이 끊어지면 Braze는 지정된 시간까지 계속해서 메시지 발송을 시도합니다. 이 옵션을 설정하지 않으면 플랫폼은 기본적으로 30일 만료로 설정됩니다. 전달 전에 만료되는 푸시 알림은 실패로 간주되지 않으며 반송으로 기록되지 않습니다. # iOS용 무음 푸시 알림 Source: /docs/ko/developer_guide/platforms/legacy_sdks/ios/push_notifications/silent_push_notifications/index.md
**Warning:** [AppboyKit](https://github.com/Appboy/appboy-ios-sdk) (also known as the Objective-C SDK) is no longer supported and has been replaced by the [Swift SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=swift). It will no longer receive new features, bug fixes, security updates, or technical support—however, messaging and analytics will continue to function as normal. To learn more, see [Introducing the New Braze Swift SDK](https://www.braze.com/resources/articles/introducing-the-new-braze-swift-sdk). # 무음 푸시 알림 {#silent-push-notifications} 푸시 알림을 사용하면 중요한 이벤트가 발생할 때 앱에 알림을 보낼 수 있습니다. 전달할 새 인스턴트 메시지, 송출할 뉴스 속보 알림 또는 오프라인으로 시청할 수 있도록 다운로드할 준비가 된 사용자가 좋아하는 TV 프로그램의 최신 에피소드가 있을 때 푸시 알림을 전송할 수 있습니다. 푸시 알림은 경고 메시지나 소리가 없는 무음 알림일 수도 있으며, 앱의 인터페이스를 업데이트하거나 백그라운드 작업을 트리거하는 데만 사용할 수 있습니다. 푸시 알림은 백그라운드 가져오기 사이의 지연이 허용되지 않을 수 있는 산발적이지만 즉시 중요한 콘텐츠에 적합합니다. 필요할 때만 애플리케이션이 실행되므로 푸시 알림은 백그라운드 가져오기보다 훨씬 효율적일 수도 있습니다. 푸시 알림은 전송 속도가 제한되므로 애플리케이션에 필요한 만큼 많이 보내도 괜찮습니다. iOS와 APNs 서버가 알림 전송 빈도를 제어하므로 너무 많이 보내도 문제가 발생하지 않습니다. 푸시 알림이 제한되는 경우, 기기가 다음 번에 연결 유지 패킷을 보내거나 다른 알림을 받을 때까지 지연될 수 있습니다. ## 무음 푸시 알림 보내기 {#sending-silent-push-notifications} 무음 푸시 알림을 보내려면 푸시 알림 페이로드에서 `content-available` 플래그를 `1`로 설정합니다. 무음 푸시 알림을 보낼 때 애플리케이션에서 이벤트를 참조할 수 있도록 알림 페이로드에 일부 데이터를 포함할 수도 있습니다. 그러면 몇 개의 네트워킹 요청을 절약하고 앱의 응답성을 높일 수 있습니다. **Warning:** 제목과 본문에 모두 `content-available=1`을 첨부하는 것은 정의되지 않은 동작을 유발할 수 있으므로 권장되지 않습니다. 알림이 실제로 무음인지 확인하려면 `content-available` 플래그를 `1`로 설정할 때 제목과 본문을 모두 제외하세요. 자세한 내용은 공식 [백그라운드 업데이트에 대한 Apple 설명서](https://developer.apple.com/documentation/usernotifications/setting_up_a_remote_notification_server/pushing_background_updates_to_your_app)를 참조하세요. `content-available` 플래그는 Braze 대시보드와 [메시징 API](https://www.braze.com/docs/ko/ko/api/endpoints/messaging/)의 [Apple 푸시 오브젝트](https://www.braze.com/docs/ko/ko/api/objects_filters/messaging/apple_object/) 내에서 설정할 수 있습니다. ![푸시 작성기의 '설정' 탭에 있는 'content-available' 확인란을 보여주는 Braze 대시보드.](https://www.braze.com/docs/ko/ko/assets/img_archive/remote_notification.png?7c9ef06cb8e9c148d37019f5e01d0ce6 "content available") ## 무음 푸시 알림을 사용하여 백그라운드 작업 트리거하기 {#use-silent-push-notifications-to-trigger-background-work} 무음 푸시 알림은 사용자에게 알리지 않고 콘텐츠를 업데이트하거나 특정 작업을 실행하기 위해 앱을 '일시 중단' 또는 '실행 중이 아님' 상태에서 깨울 수 있습니다. 무음 푸시 알림을 사용하여 백그라운드 작업을 트리거하려면 이전 지침에 따라 메시지나 소리 없이 `content-available` 플래그를 설정합니다. 프로젝트 설정의 **Capabilities** 탭에서 `remote notifications`을 활성화하도록 앱의 백그라운드 모드를 설정합니다. 원격 알림은 `content-available` 플래그가 설정된 일반 푸시 알림일 뿐입니다. !["capabilities" 아래에 "remote notifications" 모드 확인란을 보여주는 Xcode.](https://www.braze.com/docs/ko/ko/assets/img_archive/background_mode.png?15bb65e9a98f4b01af0c73c3917d6950 "background mode enabled") [제거 추적](https://www.braze.com/docs/ko/ko/developer_guide/analytics/tracking_uninstalls/?sdktab=swift)을 위해서는 원격 알림에 백그라운드 모드를 활성화해야 합니다. 원격 알림 백그라운드 모드가 활성화되어 있어도 사용자가 애플리케이션을 강제 종료한 경우 시스템은 백그라운드로 앱을 실행하지 않습니다. 사용자가 애플리케이션을 명시적으로 실행하거나 기기를 재부팅해야 시스템에서 백그라운드로 앱을 자동으로 실행할 수 있습니다. 자세한 내용은 [백그라운드 업데이트 푸시하기](https://developer.apple.com/documentation/usernotifications/setting_up_a_remote_notification_server/pushing_background_updates_to_your_app?language=objc) 및 [`application:didReceiveRemoteNotification:fetchCompletionHandler:`](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIApplicationDelegate_Protocol/index.html#//apple_ref/occ/intfm/UIApplicationDelegate/application:didReceiveRemoteNotification:fetchCompletionHandler:)를 참조하세요. ## iOS 무음 알림 제한 사항 {#ios-silent-notifications-limitations} iOS 운영체제에서는 일부 기능에 대한 알림을 차단할 수 있습니다. 이러한 기능에 문제가 있는 경우 iOS의 무음 알림 게이트가 원인일 수 있습니다. Braze에는 iOS 무음 푸시 알림을 사용하는 여러 기능이 있습니다: | 기능 | 사용자 경험 | |---|---| | 제거 추적 | 사용자는 매일 밤 무음 제거 추적 푸시를 수신합니다. | | 지오펜스 | 서버에서 기기로 지오펜스를 자동 동기화합니다. | {: .reset-td-br-1 .reset-td-br-2 role="presentation" } 자세한 내용은 Apple의 [인스턴스 메서드](https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1623013-application) 및 [미수신 알림](https://developer.apple.com/library/content/technotes/tn2265/_index.html#//apple_ref/doc/uid/DTS40010376-CH1-TNTAG23) 문서를 참조하세요. [8]:https://developer.apple.com/library/content/technotes/tn2265/_index.html#//apple_ref/doc/uid/DTS40010376-CH1-TNTAG23 # iOS용 푸시 프라이머 Source: /docs/ko/developer_guide/platforms/legacy_sdks/ios/push_notifications/push_primer/index.md
**Warning:** [AppboyKit](https://github.com/Appboy/appboy-ios-sdk) (also known as the Objective-C SDK) is no longer supported and has been replaced by the [Swift SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=swift). It will no longer receive new features, bug fixes, security updates, or technical support—however, messaging and analytics will continue to function as normal. To learn more, see [Introducing the New Braze Swift SDK](https://www.braze.com/resources/articles/introducing-the-new-braze-swift-sdk). # 푸시 프라이머 통합 푸시 프라이머 캠페인은 사용자가 기기에서 앱에 대한 푸시를 활성화할 것을 권장합니다. 사용자로부터 사용자의 기기로 직접 메시지를 보낼 수 있는 권한을 부여받는 작업은 복잡할 수 있지만, 본 가이드가 도움이 될 수 있습니다! 이 가이드에서는 개발자가 푸시 프라이밍을 통합하기 위해 수행해야 하는 단계를 보여줍니다. ## 1단계: AppDelegate.m 파일에 스니펫 추가 표준 통합 대신 `AppDelegate.m` 파일에 다음 코드 줄을 추가합니다: ```objc - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { ... if (@available(iOS 10.0, *)) { UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter]; [center getNotificationSettingsWithCompletionHandler:^(UNNotificationSettings * _Nonnull settings) { if (settings.authorizationStatus != UNAuthorizationStatusNotDetermined) { // authorization has already been requested, need to follow usual steps [center requestAuthorizationWithOptions:(UNAuthorizationOptionAlert | UNAuthorizationOptionSound | UNAuthorizationOptionBadge) completionHandler:^(BOOL granted, NSError * _Nullable error) { [[Appboy sharedInstance] pushAuthorizationFromUserNotificationCenter:granted]; }]; center.delegate = self; [center setNotificationCategories:[ABKPushUtils getAppboyUNNotificationCategorySet]]; [[UIApplication sharedApplication] registerForRemoteNotifications]; } }]; } else { UIApplication *sharedApplication = [UIApplication sharedApplication]; UIUserNotificationSettings *notificationSettings = [sharedApplication currentUserNotificationSettings]; if (notificationSettings.types) { UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:(UIUserNotificationTypeBadge | UIUserNotificationTypeAlert | UIUserNotificationTypeSound) categories:[ABKPushUtils getAppboyUIUserNotificationCategorySet]]; [sharedApplication registerUserNotificationSettings:settings]; [sharedApplication registerForRemoteNotifications]; } } ``` ```swift if #available(iOS 10, *) { let center = UNUserNotificationCenter.current() center.getNotificationSettings(completionHandler: { (settings) in if settings.authorizationStatus != .notDetermined { // authorization has already been requested, need to follow usual steps center.requestAuthorization(options: [.alert, .sound, .badge]) { (granted, error) in Appboy.sharedInstance()?.pushAuthorization(fromUserNotificationCenter: granted) } center.delegate = self as? UNUserNotificationCenterDelegate center.setNotificationCategories(ABKPushUtils.getAppboyUNNotificationCategorySet()) UIApplication.shared.registerForRemoteNotifications() } }) } else { let notificationSettiings = UIApplication.shared.currentUserNotificationSettings if notificationSettiings?.types != nil { let setting = UIUserNotificationSettings(types: [.alert, .badge, .sound], categories:nil) UIApplication.shared.registerUserNotificationSettings(setting) UIApplication.shared.registerForRemoteNotifications() } } ``` ## 2단계: AppDelegate.m 파일에 사용자 지정 이벤트 검사기 추가 다음 코드 스니펫은 사용자 지정 이벤트가 실행되어야 하는지 여부를 확인합니다. `AppDelegate.m` 에 다음 코드 줄을 추가합니다. ```objc if (@available(iOS 10.0, *)) { UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter]; [center getNotificationSettingsWithCompletionHandler:^(UNNotificationSettings * _Nonnull settings) { if (settings.authorizationStatus == UNAuthorizationStatusNotDetermined) { // ... // fire custom event // ... } }]; } else { UIUserNotificationSettings *notificationSettings = [[UIApplication sharedApplication] currentUserNotificationSettings]; if (!notificationSettings.types) { // … // fire custom event // ... } } ``` ```swift if #available(iOS 10, *) { let center = UNUserNotificationCenter.current() center.getNotificationSettings(completionHandler: { (settings) in if settings.authorizationStatus == .notDetermined { // ... // fire custom event // ... } }) } else { let notificationSettiings = UIApplication.shared.currentUserNotificationSettings if notificationSettiings?.types != nil { // ... // fire custom event // ... } } ``` ## 3단계: 딥링크 핸들러 설정 딥링크 처리 코드 안에 다음 코드 스니펫을 배치하세요. 푸시 프라이머 인앱 메시지에 대해서만 이 딥링킹 코드를 실행해야 합니다. 딥링크에 대한 자세한 내용은 [링크 처리 사용자 지정을](https://www.braze.com/docs/ko/ko/developer_guide/platform_integration_guides/ios/advanced_use_cases/linking/#linking-handling-customization) 참조하세요. ```objc // ... // check that this deep link relates to the push prompt // ... if (@available(iOS 10.0, *)) { UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter]; [center requestAuthorizationWithOptions:(UNAuthorizationOptionAlert | UNAuthorizationOptionSound | UNAuthorizationOptionBadge) completionHandler:^(BOOL granted, NSError * _Nullable error) { [[Appboy sharedInstance] pushAuthorizationFromUserNotificationCenter:granted]; }]; center.delegate = self; [center setNotificationCategories:[ABKPushUtils getAppboyUNNotificationCategorySet]]; [[UIApplication sharedApplication] registerForRemoteNotifications]; } else { UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:(UIUserNotificationTypeBadge | UIUserNotificationTypeAlert | UIUserNotificationTypeSound) categories:[ABKPushUtils getAppboyUIUserNotificationCategorySet]]; UIApplication *sharedApplication = [UIApplication sharedApplication]; [sharedApplication registerUserNotificationSettings:settings]; [sharedApplication registerForRemoteNotifications]; } ``` ```swift // ... // check that this deep link relates to the push prompt // ... if #available(iOS 10, *) { let center = UNUserNotificationCenter.current() center.delegate = self as? UNUserNotificationCenterDelegate center.requestAuthorization(options: [.alert, .sound, .badge]) { (granted, error) in Appboy.sharedInstance()?.pushAuthorization(fromUserNotificationCenter: granted) } UIApplication.shared.registerForRemoteNotifications() } else { let setting = UIUserNotificationSettings(types: [.alert, .badge, .sound], categories:nil) UIApplication.shared.registerUserNotificationSettings(setting) UIApplication.shared.registerForRemoteNotifications() } ``` # iOS용 푸시 스토리 Source: /docs/ko/developer_guide/platforms/legacy_sdks/ios/push_notifications/push_story/index.md
**Warning:** [AppboyKit](https://github.com/Appboy/appboy-ios-sdk) (also known as the Objective-C SDK) is no longer supported and has been replaced by the [Swift SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=swift). It will no longer receive new features, bug fixes, security updates, or technical support—however, messaging and analytics will continue to function as normal. To learn more, see [Introducing the New Braze Swift SDK](https://www.braze.com/resources/articles/introducing-the-new-braze-swift-sdk). # 푸시 스토리 설정 푸시 스토리 기능을 사용하려면 `UNNotification` 프레임워크와 iOS 10이 필요합니다. 이 기능은 iOS SDK 버전 3.2.1부터 사용할 수 있습니다. ## 1단계: 앱에서 푸시 활성화 [푸시 알림 통합](https://www.braze.com/docs/ko/ko/developer_guide/platforms/legacy_sdks/ios/push_notifications/integration/)에 따라 앱에서 푸시를 활성화합니다. ## 2단계: 알림 콘텐츠 확장 대상 추가하기 앱 프로젝트에서 **파일 > 새로 만들기 > 대상...** 메뉴로 이동하여 새 `Notification Content Extension` 대상을 추가하고 활성화합니다. ![](https://www.braze.com/docs/ko/ko/assets/img/ios/push_story/add_content_extension.png?ad9e5d8cc83d88d9e26dbd2c4c8dba67) Xcode가 새 대상을 자동으로 생성하고 다음을 포함하여 파일을 자동으로 생성합니다. - `NotificationViewController.h` - `NotificationViewController.m` - `MainInterface.storyboard` - `NotificationViewController.swift` - `MainInterface.storyboard` ## 3단계: 기능 활성화 푸시 스토리 기능을 사용하려면 기본 앱 대상의 **기능** 섹션에서 백그라운드 모드가 필요합니다. 백그라운드 모드를 켠 후 **백그라운드 가져오기** 및 **원격 알림**을 선택합니다. ![](https://www.braze.com/docs/ko/ko/assets/img/ios/push_story/enable_background_mode.png?37d0c9c4c59fb04aa930729a5539ed59) ### 앱 그룹 추가 또한 `Capability App Groups` 을 추가해야 합니다. 앱에 앱 그룹이 없으면 기본 앱 대상의 **기능**으로 이동하여 `App Groups`을 켜고 **+** 버튼을 클릭합니다. 앱의 번들 ID를 사용하여 앱 그룹을 생성합니다. 예를 들어 앱의 번들 ID가 `com.company.appname`인 경우 앱 그룹 이름을 `group.com.company.appname.xyz`로 지정할 수 있습니다. 기본 앱과 콘텐츠 확장 대상 모두에 대해 `App Groups`을 켜야 합니다. **Important:** 이 컨텍스트에서 `App Groups`은 Braze 워크스페이스(이전 앱 그룹) ID가 아닌 Apple의 [앱 그룹 권한](https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_security_application-groups)을 의미합니다. 앱을 앱 그룹에 추가하지 않으면 앱이 푸시 페이로드의 특정 필드를 채우지 못하고 예상대로 완전히 작동하지 않을 수 있습니다. ## 4단계: 앱에 푸시 스토리 프레임워크 추가하기 [스위프트 패키지 매니저 통합 가이드](https://www.braze.com/docs/ko/ko/developer_guide/platforms/legacy_sdks/ios/initial_sdk_setup/installation_methods/swift_package_manager/)를 수행한 후 `Notification Content Extension`에 `AppboyPushStory`를 추가합니다. ![Xcode에서 프레임워크 및 라이브러리 아래에 있는 "+" 아이콘을 선택하여 프레임워크를 추가합니다.](https://www.braze.com/docs/ko/ko/assets/img/ios/push_story/spm1.png?e0309c244812bf68280a061a2de6f24e) ![](https://www.braze.com/docs/ko/ko/assets/img/ios/push_story/spm2.png?b44d800ab07db2b950dd6275937465c0) 포드파일에 다음 줄을 추가합니다: ```ruby target 'YourContentExtensionTarget' do pod 'Appboy-Push-Story' end ``` Podfile을 업데이트한 후 터미널에서 Xcode 앱 프로젝트의 디렉토리로 이동하고 `pod install`을 실행합니다. [GitHub 릴리스 페이지](https://github.com/Appboy/appboy-ios-sdk/releases)에서 최신 `AppboyPushStory.zip`을 다운로드하여 압축을 푼 후 프로젝트의 `Notification Content Extension`에 다음 파일을 추가합니다. - `Resources/ABKPageView.nib` - `AppboyPushStory.xcframework` ![](https://www.braze.com/docs/ko/ko/assets/img/ios/push_story/manual1.png?fb802f18809806513f0295486afb90dd) **Important:** **Do Not Embed**이(가) **Embed** 열의 **AppboyPushStory.xcframework** 아래에 선택되어 있는지 확인하십시오. **빌드 설정 > 기타 링커 플래그**에서 프로젝트의 `Notification Content Extension`에 `-ObjC` 플래그를 추가합니다. ## 5단계: 알림 보기 컨트롤러 업데이트하기 `NotificationViewController.h` 에 다음 줄을 추가하여 새 속성을 추가하고 헤더 파일을 가져옵니다: ```objc #import ``` ```objc @property (nonatomic) IBOutlet ABKStoriesView *storiesView; @property (nonatomic) ABKStoriesViewDataSource *dataSource; ``` `NotificationViewController.m` 에서 기본 구현을 제거하고 다음 코드를 추가합니다: ```objc @implementation NotificationViewController - (void)didReceiveNotification:(UNNotification *)notification { self.dataSource = [[ABKStoriesViewDataSource alloc] initWithNotification:notification storiesView:self.storiesView appGroup:@"YOUR-APP-GROUP-IDENTIFIER"]; } - (void)didReceiveNotificationResponse:(UNNotificationResponse *)response completionHandler:(void (^)(UNNotificationContentExtensionResponseOption option))completion { UNNotificationContentExtensionResponseOption option = [self.dataSource didReceiveNotificationResponse:response]; completion(option); } - (void)viewWillDisappear:(BOOL)animated { [self.dataSource viewWillDisappear]; [super viewWillDisappear:animated]; } @end ``` `NotificationViewController.swift` 에 다음 줄을 추가하여 헤더 파일을 가져옵니다: ```swift import AppboyPushStory ``` 그런 다음 기본 구현을 제거하고 다음 코드를 추가합니다: ```swift class NotificationViewController: UIViewController, UNNotificationContentExtension { @IBOutlet weak var storiesView: ABKStoriesView! var dataSource: ABKStoriesViewDataSource? func didReceive(_ notification: UNNotification) { dataSource = ABKStoriesViewDataSource(notification: notification, storiesView: storiesView, appGroup: "YOUR-APP-GROUP-IDENTIFIER") } func didReceive(_ response: UNNotificationResponse, completionHandler completion: @escaping (UNNotificationContentExtensionResponseOption) -> Void) { if dataSource != nil { let option: UNNotificationContentExtensionResponseOption = dataSource!.didReceive(response) completion(option) } } override func viewWillDisappear(_ animated: Bool) { dataSource?.viewWillDisappear() super.viewWillDisappear(animated) } } ``` ## 6단계: 알림 콘텐츠 확장 스토리보드 설정 `Notification Content Extension` 스토리보드를 열고 알림 보기 컨트롤러에 새 `UIView` 을 배치합니다. 클래스 이름을 `ABKStoriesView` 로 변경합니다. 알림 보기 컨트롤러의 기본 보기 프레임에 맞게 보기 너비와 높이를 자동 크기 조정할 수 있도록 설정합니다. ![](https://www.braze.com/docs/ko/ko/assets/img/ios/push_story/abkstoriesview_class.png?ee2f2e08fdd56df7e4f5bb7661559767) ![](https://www.braze.com/docs/ko/ko/assets/img/ios/push_story/abkstoriesview_size.png?7edb36e8900896e9ad64bf49ea100715) 그런 다음, 알림 보기 컨트롤러의 `storiesView` IBOutlet을 추가된 `ABKStoriesView`에 연결합니다. ![](https://www.braze.com/docs/ko/ko/assets/img/ios/push_story/abkstoriesview_outlet.png?495dd440247e01d31851cf1b878563f1) ## 7단계: 알림 콘텐츠 확장 목록 설정 `Notification Content Extension`의 `Info.plist` 파일을 열고 `NSExtension \ NSExtensionAttributes` 아래에서 다음 키를 추가 및 변경합니다. `UNNotificationExtensionCategory` = `ab_cat_push_story_v2` (`String` 유형) `UNNotificationExtensionDefaultContentHidden` = `YES` (`Boolean` 유형) `UNNotificationExtensionInitialContentSizeRatio` = `0.65` (`Number` 유형) ![](https://www.braze.com/docs/ko/ko/assets/img/ios/push_story/notificationcontentextension_plist.png?6c58d280881fdc3384127cad54a4eb4c) ## 8단계: 기본 앱에서 Braze 연동 업데이트하기 ##### 옵션 1: 런타임 Braze 인스턴스를 구성하는 데 사용되는 `appboyOptions` 사전에서 `ABKPushStoryAppGroupKey` 항목을 추가하고 값을 워크스페이스 API 식별자로 설정합니다. ```objc NSMutableDictionary *appboyOptions = [NSMutableDictionary dictionary]; appboyOptions[ABKPushStoryAppGroupKey] = @"YOUR-APP-GROUP-IDENTIFIER"; [Appboy startWithApiKey:@"YOUR-API-KEY" inApplication:application withLaunchOptions:launchOptions withAppboyOptions:appboyOptions]; ``` ```swift let appboyOptions: [AnyHashable: Any] = [ ABKPushStoryAppGroupKey : "YOUR-APP-GROUP-IDENTIFIER" ] Appboy.start(withApiKey: "YOUR-API-KEY", in:application, withLaunchOptions:launchOptions, withAppboyOptions:appboyOptions) ``` ##### 옵션 2: Info.plist 또는 `Info.plist` 파일에서 푸시 스토리 작업 영역을 구성하려면 `Info.plist` 파일에 `Braze` 이라는 사전을 추가합니다. `Braze` 사전 내에서 문자열 형식의 `PushStoryAppGroup` 하위 항목을 추가하고 값을 워크스페이스 식별자로 설정합니다. Braze iOS SDK v4.0.2 이전 버전에서는 `Appboy` 대신 `Braze`의 사전 키를 사용해야 합니다. ## 다음 단계 다음으로 푸시 스토리 메시지에 버튼이 표시되는 데 필요한 [작업 버튼](https://www.braze.com/docs/ko/ko/developer_guide/platforms/legacy_sdks/ios/push_notifications/customization/action_buttons/) 통합 단계를 참조하세요. # iOS용 고급 푸시 알림 구현(선택 사항) Source: /docs/ko/developer_guide/platforms/legacy_sdks/ios/push_notifications/implementation_guide/index.md
**Warning:** [AppboyKit](https://github.com/Appboy/appboy-ios-sdk) (also known as the Objective-C SDK) is no longer supported and has been replaced by the [Swift SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=swift). It will no longer receive new features, bug fixes, security updates, or technical support—however, messaging and analytics will continue to function as normal. To learn more, see [Introducing the New Braze Swift SDK](https://www.braze.com/resources/articles/introducing-the-new-braze-swift-sdk).
**Important:** 기본 푸시 알림 개발자 통합 가이드를 찾고 계신가요? [여기](https://www.braze.com/docs/ko/ko/developer_guide/platforms/legacy_sdks/ios/push_notifications/integration/)에서 확인하세요. # 푸시 알림 구현 가이드 {#push-notification-implementation-guide} > 이 고급 구현 가이드(선택 사항)에서는 푸시 알림 콘텐츠 앱 확장을 활용하여 푸시 메시지를 최대한 활용하는 방법을 다룹니다. 저희 팀이 구축한 세 가지 커스텀 사용 사례와 함께 코드 스니펫 및 분석 로깅에 대한 지침도 포함되어 있습니다. [여기](https://github.com/braze-inc/braze-growth-shares-ios-demo-app)에서 Braze 데모 리포지토리를 방문하세요! 이 구현 가이드는 Swift 구현을 중심으로 하지만 관심 있는 분을 위해 Objective-C 스니펫도 제공됩니다. ## 알림 콘텐츠 앱 확장 {#notification-content-app-extensions} ![두 개의 푸시 메시지가 나란히 표시됩니다. 왼쪽의 메시지는 기본 UI에서 푸시가 어떻게 표시되는지 보여줍니다. 오른쪽의 메시지는 커스텀 푸시 UI를 구현하여 만든 커피 펀치 카드 푸시를 보여줍니다.](https://www.braze.com/docs/ko/ko/assets/img/push_implementation_guide/push1.png?d04035fb11637f7db51f24a1afab9e8f){: style="max-width:65%;border:0;margin-top:10px"} 푸시 알림은 여러 플랫폼에서 표준처럼 보이지만, 기본 UI에서 일반적으로 구현되는 것 이상의 방대한 커스터마이징 옵션을 제공합니다. 푸시 알림이 확장되면 콘텐츠 알림 확장을 통해 확장된 푸시 알림의 커스텀 보기를 활성화할 수 있습니다. 푸시 알림은 세 가지 방법으로 확장할 수 있습니다.
- 푸시 배너를 길게 누르기
- 푸시 배너를 아래로 스와이프
- 배너를 왼쪽으로 스와이프하고 "보기"를 선택 이러한 커스텀 보기는 대화형 알림, 사용자 데이터로 채워진 알림, 전화번호와 이메일 등의 정보를 캡처할 수 있는 푸시 메시지 등 다양한 유형의 콘텐츠를 표시하여 고객을 참여시킬 수 있는 스마트한 방법을 제공합니다. 이러한 방식으로 푸시를 구현하는 것이 생소할 수도 있지만, Braze의 잘 알려진 기능 중 하나인 [Push Stories](https://www.braze.com/docs/ko/ko/user_guide/channels/push/create_a_push_message/push_stories/)는 알림 콘텐츠 앱 확장을 위한 커스텀 보기가 어떤 모습일 수 있는지 보여주는 대표적인 예입니다! #### 요구 사항 {#requirements} ![](https://www.braze.com/docs/ko/ko/assets/img/push_implementation_guide/push15.png?64059ffe5c16313ee6377e0a79405812){: style="float:right;max-width:50%;margin-left:10px; border:0;margin-top:10px"} - 앱에 성공적으로 통합된 [푸시 알림](https://www.braze.com/docs/ko/ko/developer_guide/platforms/legacy_sdks/ios/push_notifications/integration/) - iOS 10 이상 - 코딩 언어에 따라 Xcode에서 생성되는 파일은 다음과 같습니다. Swift
- `NotificationViewController.swift`
- `MainInterface.storyboard`

Objective-C
- `NotificationViewController.h`
- `NotificationViewController.m`
- `MainInterface.storyboard` ### 커스텀 카테고리 구성 {#custom-category-configuration} 대시보드에서 커스텀 보기를 설정하려면 알림 버튼을 토글하여 켜고 커스텀 카테고리를 입력해야 합니다. 그런 다음 제공한 사전 등록된 커스텀 iOS 카테고리를 알림 콘텐츠 확장 타겟의 `.plist`에서 `UNNotificationExtensionCategory`와 비교하여 확인합니다. 여기에 입력한 값은 Braze 대시보드에 설정된 값과 일치해야 합니다. ![푸시 메시지 작성기 설정에 있는 알림 버튼 옵션입니다.](https://www.braze.com/docs/ko/ko/assets/img/push_implementation_guide/push16.png?be40aad198215645c3ef4ac2553267f4){: style="max-width:75%;border:0;margin-top:10px"} ![](https://www.braze.com/docs/ko/ko/assets/img/push_implementation_guide/push17.png?42f5ff77b6402aade26b936b5c78dbc7){: style="max-width:75%;border:0;margin-top:10px"} **Tip:** 콘텐츠 확장이 포함된 푸시가 항상 눈에 띄는 것은 아니므로, 사용자가 푸시 알림을 확장하도록 유도하는 클릭 유도 문안을 포함하는 것이 좋습니다. ## 사용 사례 및 구현 워크스루 {#use-case-and-implementation-walkthrough} 푸시 알림 콘텐츠 앱 확장 유형은 세 가지가 제공됩니다. 각 유형에는 개념 안내, 잠재적 사용 사례, 그리고 Braze 대시보드에서 푸시 알림 변수를 표시 및 사용하는 방법에 대한 설명이 포함됩니다. - [대화형 푸시 알림](#interactive-push-notification) - [개인화된 푸시 알림](#personalized-push-notifications) - [정보 캡처 푸시 알림](#information-capture-push-notification) ### 대화형 푸시 알림 {#interactive-push-notification} 푸시 알림은 콘텐츠 확장 내에서 사용자 동작에 응답할 수 있습니다. iOS 12 이상을 실행하는 사용자의 경우, 푸시 메시지를 완전한 대화형 푸시 알림으로 전환할 수 있습니다! 이 대화형 기능은 사용자가 알림에 참여하도록 유도하는 다양한 기회를 제공합니다. 다음 예시는 사용자가 확장된 알림 내에서 매치 게임을 플레이할 수 있는 푸시를 보여줍니다. ![대화형 푸시 알림의 단계가 어떤 모습일 수 있는지 보여주는 다이어그램입니다. 이미지는 사용자가 대화형 매칭 게임을 표시하는 푸시 알림을 누르는 모습을 보여줍니다.](https://www.braze.com/docs/ko/ko/assets/img/push_implementation_guide/push12.png?e32579b6de7f5aec62265828724d6657){: style="border:0"} #### 대시보드 구성 {#dashboard-configuration} 대시보드에서 커스텀 보기를 설정하려면 알림 버튼 설정에서 표시하려는 특정 카테고리를 입력합니다. 다음으로, 알림 콘텐츠 확장의 `.plist`에서 커스텀 카테고리를 `UNNotificationExtensionCategory` 속성으로 설정해야 합니다. 여기에 입력한 값은 Braze 대시보드에 설정된 값과 일치해야 합니다. 마지막으로 푸시 알림에서 사용자 상호작용을 활성화하려면 `UNNotificationExtensionInteractionEnabled` 키를 true로 설정합니다. ![](https://www.braze.com/docs/ko/ko/assets/img/push_implementation_guide/push3.png?1b1b1f110b2fd607874b5210e8533620){: style="float:right;max-width:45%;"} ![푸시 메시지 작성기 설정에 있는 알림 버튼 옵션입니다.](https://www.braze.com/docs/ko/ko/assets/img/push_implementation_guide/push14.png?0015bbc93063d506eabdce38248f8664){: style="max-width:50%;"} #### 기타 사용 사례 {#other-use-cases} 푸시 콘텐츠 확장은 프로모션과 애플리케이션에 인터랙티브한 기능을 도입할 수 있는 흥미로운 옵션입니다. 예를 들어 사용자가 플레이할 수 있는 게임, 할인을 위한 돌림판, 목록이나 노래를 저장하는 "좋아요" 버튼 등이 있습니다. ##### 분석을 기록할 준비가 되셨나요? {#ready-to-log-analytics} 데이터 흐름의 진행 방식을 더 잘 이해하려면 [다음 섹션](#logging-analytics)을 참조하세요. ### 개인화된 푸시 알림 {#personalized-push-notifications} ![두 대의 iPhone이 나란히 표시됩니다. 첫 번째 iPhone은 푸시 메시지의 확장되지 않은 보기를 표시합니다. 두 번째 iPhone에는 코스의 "진행" 화면, 다음 세션, 다음 세션의 마감 시점을 표시하는 확장된 버전의 푸시 메시지가 표시됩니다.](https://www.braze.com/docs/ko/ko/assets/img/push_implementation_guide/push6.png?438d9acc8285244397d14467a8a63d3a){: style="float:right;max-width:40%;margin-left:15px;border:0"} 푸시 알림은 콘텐츠 확장 내에서 사용자별 정보를 표시할 수 있습니다. 오른쪽의 예시는 사용자가 특정 작업(Braze 학습 과정)을 완료한 후의 푸시 알림을 보여주며, 이제 이 알림을 확장하여 진행 상황을 확인하도록 권장합니다. 여기에 제공된 정보는 사용자에 따라 다르며, API 트리거를 활용하여 세션이 완료되거나 특정 사용자 동작이 수행될 때 발송될 수 있습니다. #### 대시보드 구성 {#dashboard-configuration} 대시보드에서 개인화된 푸시를 설정하려면 표시하려는 특정 카테고리를 등록한 다음, 표준 Liquid를 사용하여 키-값 페어 내에서 메시지에 표시할 적절한 사용자 속성을 설정해야 합니다. 이러한 보기는 특정 고객 프로필의 특정 사용자 속성을 기반으로 개인화될 수 있습니다. ![키-값 페어의 네 세트. 여기서 "next_session_name"과 "next_session_complete_date"는 Liquid를 사용하여 API 트리거 속성으로 설정되고, "completed_session count"와 "total_session_count"는 Liquid를 사용하여 커스텀 사용자 속성으로 설정됩니다.](https://www.braze.com/docs/ko/ko/assets/img/push_implementation_guide/push5.png?199277e2adf2d1ded48e5dba4c2d7b4a){: style="max-width:60%;"} #### 키-값 페어 처리하기 {#handling-key-value-pairs} 콘텐츠 확장이 알림을 받으면 다음 메서드 `didReceive`가 호출되며, `NotificationViewController`에서 찾을 수 있습니다. 대시보드에 제공된 키-값 페어는 `userInfo` 사전을 사용하여 코드에 표시됩니다. **푸시 알림에서 키-값 페어 구문 분석하기**
``` swift func didReceive(_ notification: UNNotification) { let userInfo = notification.request.content.userInfo guard let value = userInfo["YOUR-KEY-VALUE-PAIR"] as? String, let otherValue = userInfo["YOUR-OTHER-KEY-VALUE-PAIR"] as? String, else { fatalError("Key-Value Pairs are incorrect.")} ... } ``` ```objc - (void)didReceiveNotification:(nonnull UNNotification *)notification { NSDictionary *userInfo = notification.request.content.userInfo; if (userInfo[@"YOUR-KEY-VALUE-PAIR"] && userInfo[@"YOUR-OTHER-KEY-VALUE-PAIR"]) { ... } else { [NSException raise:NSGenericException format:@"Key-Value Pairs are incorrect"]; } } ``` #### 기타 사용 사례 {#other-use-cases} 진행 상황 기반 및 사용자 중심의 푸시 콘텐츠 확장에 대한 아이디어는 무궁무진합니다. 몇 가지 예로, 여러 플랫폼에서 진행 상황을 공유하는 옵션 추가, 잠금 해제된 업적, 펀치 카드 또는 온보딩 체크리스트가 있습니다. ##### 분석을 기록할 준비가 되셨나요? {#ready-to-log-analytics} 데이터 흐름의 진행 방식을 더 잘 이해하려면 [다음 섹션](#logging-analytics)을 참조하세요. ### 정보 캡처 푸시 알림 {#information-capture-push-notification} 푸시 알림은 콘텐츠 확장 내에서 사용자 정보를 캡처할 수 있으므로 푸시로 가능한 작업의 한계를 뛰어넘을 수 있습니다. 다음에 표시된 흐름을 살펴보면 보기가 상태 변경에 응답할 수 있습니다. 이러한 상태 변경 구성요소가 각 이미지에 표시됩니다. 1. 사용자가 푸시 알림을 받습니다. 2. 푸시가 열리고 사용자에게 정보를 입력하라는 메시지가 표시됩니다. 3. 정보가 제공되고 유효한 경우 등록 버튼이 표시됩니다. 3. 확인 보기가 표시되고 푸시가 해제됩니다. 여기서 요청하는 정보는 SMS 번호 캡처와 같은 광범위한 정보일 수 있으며 이메일로 한정되지 않아도 됩니다. #### 대시보드 구성 {#dashboard-configuration} 대시보드에서 정보 캡처가 가능한 푸시를 설정하려면 커스텀 카테고리를 등록 및 설정하고 필요한 키-값 페어를 제공해야 합니다. 예시에서 볼 수 있듯이 푸시에 이미지를 포함할 수도 있습니다. 이렇게 하려면 [리치 알림](https://www.braze.com/docs/ko/ko/developer_guide/platforms/legacy_sdks/ios/push_notifications/customization/rich_notifications/)을 통합하고, Campaign에서 알림 스타일을 리치 알림으로 설정한 다음, 리치 푸시 이미지를 포함해야 합니다. ![세 세트의 키-값 페어가 포함된 푸시 메시지입니다. 1. "Braze_id"는 Braze ID를 검색하기 위한 Liquid 호출로 설정됩니다. 2. "cert_title"은 "Braze Marketer Certification"으로 설정됩니다. 3. "Cert_description"은 "Certified Braze marketers drive..."로 설정됩니다.](https://www.braze.com/docs/ko/ko/assets/img/push_implementation_guide/push9.png?4f1d1fc129e7f564d006e649dc0ef582) #### 버튼 동작 처리하기 {#handling-button-actions} 각 실행 버튼은 고유하게 식별됩니다. 이 코드는 응답 식별자가 `actionIdentifier`와 같은지 확인하고, 같다면 사용자가 실행 버튼을 클릭했음을 알 수 있습니다. **푸시 알림 실행 버튼 응답 처리하기**
``` swift func didReceive(_ response: UNNotificationResponse, completionHandler completion: @escaping (UNNotificationContentExtensionResponseOption) -> Void) { if response.actionIdentifier == "YOUR-REGISTER-IDENTIFIER" { // do something } else { // do something else } } ``` ```objc - (void)didReceiveNotificationResponse:(UNNotificationResponse *)response completionHandler:(void (^)(UNNotificationContentExtensionResponseOption))completion { if ([response.actionIdentifier isEqualToString:@"YOUR-REGISTER-IDENTIFIER"]) { completion(UNNotificationContentExtensionResponseOptionDismiss); } else { completion(UNNotificationContentExtensionResponseOptionDoNotDismiss); } } ``` ##### 푸시 해제하기 {#dismissing-pushes} 푸시 알림은 실행 버튼을 누르면 자동으로 해제할 수 있습니다. 권장되는 세 가지 사전 빌드된 푸시 해제 옵션이 있습니다. 1. `completion(.dismiss)` - 알림을 해제합니다. 2. `completion(.doNotDismiss)` - 알림이 계속 열려 있습니다. 3. `completion(.dismissAndForward)` - 푸시가 해제되고 사용자가 애플리케이션으로 이동합니다. #### 기타 사용 사례 {#other-use-cases} 푸시 알림을 통해 사용자 입력을 요청하는 것은 많은 기업이 활용하지 않는 흥미로운 기회입니다. 이러한 푸시 메시지에서는 이름, 이메일 또는 번호와 같은 기본 정보를 요청할 수 있을 뿐만 아니라, 고객 프로필을 완성하지 않은 경우 완성하도록 유도하거나 피드백을 제출하라는 프롬프트도 표시할 수 있습니다. ##### 분석을 기록할 준비가 되셨나요? {#ready-to-log-analytics} 데이터 흐름의 진행 방식을 더 잘 이해하려면 [다음 섹션](#logging-analytics)을 참조하세요. ## 분석 로깅 {#logging-analytics} ### Braze API를 사용한 로깅(권장) {#logging-with-the-braze-api-recommended} 분석 로깅은 고객의 서버가 [`/users/track` 엔드포인트](https://www.braze.com/docs/ko/ko/api/endpoints/user_data/post_user_track/)에 접속하는 방식으로 실시간으로만 수행할 수 있습니다. 분석을 기록하려면 다음 스크린샷과 같이 키-값 페어 필드에 `braze_id` 값을 보내 업데이트할 고객 프로필을 식별합니다. ![세 세트의 키-값 페어가 포함된 푸시 메시지입니다. 1. "Braze_id"는 Braze ID를 검색하기 위한 Liquid 호출로 설정됩니다. 2. "cert_title"은 "Braze Marketer Certification"으로 설정됩니다. 3. "Cert_description"은 "Certified Braze marketers drive..."로 설정됩니다.](https://www.braze.com/docs/ko/ko/assets/img/push_implementation_guide/push18.png?ae37ef2a75d3afb0525cc480263728d7){: style="max-width:80%;"} ### 수동으로 로깅하기 {#logging-manually} 수동으로 로깅하려면 먼저 Xcode 내에서 앱 그룹을 구성한 다음, 분석을 생성, 저장 및 검색해야 합니다. 이를 위해서는 사용자 측에서 커스텀 개발자 작업이 필요합니다. 다음에 표시된 코드 스니펫은 이 문제를 해결하는 데 도움이 됩니다. 또한 모바일 애플리케이션이 후속으로 실행될 때까지 분석 데이터가 Braze로 전송되지 않는다는 점도 중요합니다. 즉, 해제 설정에 따라 푸시 알림이 해제되고 모바일 앱이 실행되어 분석이 검색되기까지 확정되지 않은 시간이 존재하기도 합니다. 이 시간 버퍼가 모든 사용 사례에 영향을 미치는 것은 아니지만, 사용자는 이 영향을 고려해야 하며, 필요한 경우 이 문제를 해결하기 위해 애플리케이션을 여는 것을 포함하여 사용자 여정을 조정해야 합니다. ![Braze에서 분석이 처리되는 방식을 설명하는 그래픽입니다. 1. 분석 데이터가 생성됩니다. 2. 분석 데이터가 저장됩니다. 3. 푸시 알림이 해제됩니다. 4. 푸시 알림 해제와 모바일 앱 실행 사이에 확정되지 않은 시간이 존재합니다. 5. 모바일 앱이 실행됩니다. 6. 분석 데이터가 수신됩니다. 7. 분석 데이터가 Braze로 전송됩니다.](https://www.braze.com/docs/ko/ko/assets/img/push_implementation_guide/push13.png?817f7603e474002aae9a3b25bccd81bb) #### 1단계: Xcode 내에서 앱 그룹 구성 {#step-1-configure-app-groups-within-xcode} `App Groups` 기능을 추가합니다. 앱에 앱 그룹이 없으면 기본 앱 타겟의 기능으로 이동하여 `App Groups`를 켜고 "+" 버튼을 클릭합니다. 앱의 번들 ID를 사용하여 앱 그룹을 생성합니다. 예를 들어 앱의 번들 ID가 `com.company.appname`인 경우 앱 그룹 이름을 `group.com.company.appname.xyz`로 지정할 수 있습니다. 기본 앱 타겟과 콘텐츠 확장 타겟 모두에 대해 `App Groups`가 켜져 있는지 확인합니다. ![](https://www.braze.com/docs/ko/ko/assets/img/ios/push_story/add_app_groups.png?44e3d92af533e6323db33236364b99e1) #### 2단계: 코드 스니펫 통합 {#step-2-integrate-code-snippets} 다음 코드 스니펫은 커스텀 이벤트, 커스텀 속성 및 사용자 속성을 저장하고 전송하는 방법에 대한 유용한 참고 자료입니다. 이 가이드에서는 UserDefaults의 관점에서 설명하지만 코드는 헬퍼 파일 `RemoteStorage`의 형태로 표현됩니다. 사용자 속성을 전송하고 저장할 때 사용되는 추가 헬퍼 파일 `UserAttributes` 및 `EventName Dictionary`도 있습니다. 모든 헬퍼 파일은 이 가이드의 마지막 부분에서 찾을 수 있습니다. ##### 커스텀 이벤트 저장 {#saving-custom-events} 커스텀 이벤트를 저장하려면 분석을 처음부터 새로 만들어야 합니다. 이는 사전을 만들고 메타데이터로 채운 다음 헬퍼 파일을 사용하여 데이터를 저장하는 방식으로 이루어집니다. 1. 이벤트 메타데이터로 사전 초기화 2. `userDefaults`를 초기화하여 이벤트 데이터 검색 및 저장 3. 기존 배열이 있는 경우 기존 배열에 새 데이터를 추가하고 저장 4. 기존 배열이 없는 경우 `userDefaults`에 새 배열 저장 ``` swift func saveCustomEvent(with properties: [String: Any]? = nil) { // 1 let customEventDictionary = Dictionary(eventName: "YOUR-EVENT-NAME", properties: properties) // 2 let remoteStorage = RemoteStorage(storageType: .suite) // 3 if var pendingEvents = remoteStorage.retrieve(forKey: .pendingCustomEvents) as? [[String: Any]] { pendingEvents.append(contentsOf: [customEventDictionary]) remoteStorage.store(pendingEvents, forKey: .pendingCustomEvents) } else { // 4 remoteStorage.store([customEventDictionary], forKey: .pendingCustomEvents) } } ``` ```objc - (void)saveCustomEvent:(NSDictionary *)properties { // 1 NSDictionary *customEventDictionary = [[NSDictionary alloc] initWithEventName:@"YOUR-EVENT-NAME" properties:properties]; // 2 RemoteStorage *remoteStorage = [[RemoteStorage alloc] initWithStorageType:StorageTypeSuite]; NSMutableArray *pendingEvents = [[remoteStorage retrieveForKey:RemoteStorageKeyPendingCustomEvents] mutableCopy]; // 3 if (pendingEvents) { [pendingEvents addObject:customEventDictionary]; [remoteStorage store:pendingEvents forKey:RemoteStorageKeyPendingCustomAttributes]; } else { // 4 [remoteStorage store:@[ customEventDictionary ] forKey:RemoteStorageKeyPendingCustomAttributes]; } } ``` ##### Braze에 커스텀 이벤트 보내기 {#sending-custom-events-to-braze} SDK가 초기화된 후가 알림 콘텐츠 앱 확장에 저장된 모든 분석을 기록하기에 가장 좋은 시기입니다. 보류 중인 이벤트를 반복하고 "이벤트 이름" 키를 확인하며 Braze에서 적절한 값을 설정하고, 다음에 이 기능이 필요할 때를 대비해 스토리지를 지워 이 작업을 수행할 수 있습니다. 1. 보류 중인 이벤트 배열 반복 2. `pendingEvents` 사전의 각 키-값 페어를 반복 3. "이벤트 이름"에 대한 키를 명시적으로 확인하여 적절히 값 설정 4. 다른 모든 키-값은 `properties` 사전에 추가 5. 개별 커스텀 이벤트 기록 6. 스토리지에서 보류 중인 모든 이벤트 제거 ``` swift func logPendingCustomEventsIfNecessary() { let remoteStorage = RemoteStorage(storageType: .suite) guard let pendingEvents = remoteStorage.retrieve(forKey: .pendingCustomEvents) as? [[String: Any]] else { return } // 1 for event in pendingEvents { var eventName: String? var properties: [AnyHashable: Any] = [:] // 2 for (key, value) in event { if key == PushNotificationKey.eventName.rawValue { // 3 if let eventNameValue = value as? String { eventName = eventNameValue } else { print("Invalid type for event_name key") } } else { // 4 properties[key] = value } } // 5 if let eventName = eventName { logCustomEvent(eventName, withProperties: properties) } } // 6 remoteStorage.removeObject(forKey: .pendingCustomEvents) } ``` ```objc - (void)logPendingEventsIfNecessary { RemoteStorage *remoteStorage = [[RemoteStorage alloc] initWithStorageType:StorageTypeSuite]; NSArray *pendingEvents = [remoteStorage retrieveForKey:RemoteStorageKeyPendingCustomEvents]; // 1 for (NSDictionary *event in pendingEvents) { NSString *eventName = nil; NSMutableDictionary *properties = [NSMutableDictionary dictionary]; // 2 for (NSString* key in event) { if ([key isEqualToString:@"event_name"]) { // 3 if ([[event objectForKey:key] isKindOfClass:[NSString class]]) { eventName = [event objectForKey:key]; } else { NSLog(@"Invalid type for event_name key"); } } else { // 4 properties[key] = event[key]; } } // 5 if (eventName != nil) { [[Appboy sharednstance] logCustomEvent:eventName withProperties:properties]; } } // 6 [remoteStorage removeObjectForKey:RemoteStorageKeyPendingCustomEvents]; } ``` ##### 커스텀 속성 저장 {#saving-custom-attributes} 커스텀 속성을 저장하려면 분석을 처음부터 새로 만들어야 합니다. 이는 사전을 만들고 메타데이터로 채운 다음 헬퍼 파일을 사용하여 데이터를 저장하는 방식으로 이루어집니다. 1. 속성 메타데이터로 사전 초기화 2. `userDefaults`를 초기화하여 속성 데이터 검색 및 저장 3. 기존 배열이 있는 경우 기존 배열에 새 데이터를 추가하고 저장 4. 기존 배열이 없는 경우 `userDefaults`에 새 배열 저장 ``` swift func saveCustomAttribute() { // 1 let customAttributeDictionary: [String: Any] = ["YOUR-CUSTOM-ATTRIBUTE-KEY": "YOUR-CUSTOM-ATTRIBUTE-VALUE"] // 2 let remoteStorage = RemoteStorage(storageType: .suite) // 3 if var pendingAttributes = remoteStorage.retrieve(forKey: .pendingCustomAttributes) as? [[String: Any]] { pendingAttributes.append(contentsOf: [customAttributeDictionary]) remoteStorage.store(pendingAttributes, forKey: .pendingCustomAttributes) } else { // 4 remoteStorage.store([customAttributeDictionary], forKey: .pendingCustomAttributes) } } ``` ``` objc - (void)saveCustomAttribute { // 1 NSDictionary *customAttributeDictionary = @{ @"YOUR-CUSTOM-ATTRIBUTE-KEY": @"YOUR-CUSTOM-ATTRIBUTE-VALUE" }; // 2 RemoteStorage *remoteStorage = [[RemoteStorage alloc] initWithStorageType:StorageTypeSuite]; NSMutableArray *pendingAttributes = [[remoteStorage retrieveForKey:RemoteStorageKeyPendingCustomAttributes] mutableCopy]; // 3 if (pendingAttributes) { [pendingAttributes addObject:customAttributeDictionary]; [remoteStorage store:pendingAttributes forKey:RemoteStorageKeyPendingCustomAttributes]; } else { // 4 [remoteStorage store:@[ customAttributeDictionary ] forKey:RemoteStorageKeyPendingCustomAttributes]; } } ``` ##### Braze에 커스텀 속성 보내기 {#sending-custom-attributes-to-braze} SDK가 초기화된 후가 알림 콘텐츠 앱 확장에 저장된 모든 분석을 기록하기에 가장 좋은 시기입니다. 보류 중인 속성을 반복하고 Braze에서 적절한 커스텀 속성을 설정하며, 다음에 이 기능이 필요할 때를 대비해 스토리지를 지워 이 작업을 수행할 수 있습니다. 1. 보류 중인 속성 배열 반복 2. `pendingAttributes` 사전의 각 키-값 페어를 반복 3. 해당 키와 값으로 개별 커스텀 속성을 기록 4. 스토리지에서 보류 중인 모든 속성 제거 ``` swift func logPendingCustomAttributesIfNecessary() { let remoteStorage = RemoteStorage(storageType: .suite) guard let pendingAttributes = remoteStorage.retrieve(forKey: .pendingCustomAttributes) as? [[String: Any]] else { return } // 1 pendingAttributes.forEach { setCustomAttributesWith(keysAndValues: $0) } // 4 remoteStorage.removeObject(forKey: .pendingCustomAttributes) } func setCustomAttributesWith(keysAndValues: [String: Any]) { // 2 for (key, value) in keysAndValues { // 3 if let value = value as? [String] { setCustomAttributeArrayWithKey(key, andValue: value) } else { setCustomAttributeWithKey(key, andValue: value) } } } ``` ```objc - (void)logPendingCustomAttributesIfNecessary { RemoteStorage *remoteStorage = [[RemoteStorage alloc] initWithStorageType:StorageTypeSuite]; NSArray *pendingAttributes = [remoteStorage retrieveForKey:RemoteStorageKeyPendingCustomAttributes]; // 1 for (NSDictionary *attribute in pendingAttributes) { [self setCustomAttributeWith:attribute]; } // 4 [remoteStorage removeObjectForKey:RemoteStorageKeyPendingCustomAttributes]; } - (void)setCustomAttributeWith:(NSDictionary *)keysAndValues { // 2 for (NSString *key in keysAndValues) { // 3 [self setCustomAttributeWith:key andValue:[keysAndValues objectForKey:key]]; } } ``` ##### 사용자 속성 저장 {#saving-user-attributes} 사용자 속성을 저장할 때는 커스텀 오브젝트를 만들어 업데이트되는 속성 유형(`email`, `first_name`, `phone_number` 등)을 해독하는 것이 좋습니다. 오브젝트는 `UserDefaults`에서 저장/검색하는 것과 호환되어야 합니다. 이를 수행하는 방법에 대한 한 가지 예시는 `UserAttribute` 헬퍼 파일을 참조하세요. 1. 인코딩된 `UserAttribute` 오브젝트를 해당 유형으로 초기화 2. `userDefaults`를 초기화하여 이벤트 데이터 검색 및 저장 3. 기존 배열이 있는 경우 기존 배열에 새 데이터를 추가하고 저장 4. 기존 배열이 없는 경우 `userDefaults`에 새 배열 저장 ``` swift func saveUserAttribute() { // 1 guard let data = try? PropertyListEncoder().encode(UserAttribute.userAttributeType("USER-ATTRIBUTE-VALUE")) else { return } // 2 let remoteStorage = RemoteStorage(storageType: .suite) // 3 if var pendingAttributes = remoteStorage.retrieve(forKey: .pendingUserAttributes) as? [Data] { pendingAttributes.append(contentsOf: [data]) remoteStorage.store(pendingAttributes, forKey: .pendingUserAttributes) } else { // 4 remoteStorage.store([data], forKey: .pendingUserAttributes) } } ``` ```objc - (void)saveUserAttribute { // 1 UserAttribute *userAttribute = [[UserAttribute alloc] initWithUserField:@"USER-ATTRIBUTE-VALUE" attributeType:UserAttributeTypeEmail]; NSError *error; NSData *data = [NSKeyedArchiver archivedDataWithRootObject:userAttribute requiringSecureCoding:YES error:&error]; if (error != nil) { // log error } // 2 RemoteStorage *remoteStorage = [[RemoteStorage alloc] initWithStorageType:StorageTypeSuite]; NSMutableArray *pendingAttributes = [[remoteStorage retrieveForKey:RemoteStorageKeyPendingUserAttributes] mutableCopy]; // 3 if (pendingAttributes) { [pendingAttributes addObject:data]; [remoteStorage store:pendingAttributes forKey:RemoteStorageKeyPendingUserAttributes]; } else { // 4 [remoteStorage store:@[data] forKey:RemoteStorageKeyPendingUserAttributes]; } } ``` ##### Braze에 사용자 속성 보내기 {#sending-user-attributes-to-braze} SDK가 초기화된 후가 알림 콘텐츠 앱 확장에 저장된 모든 분석을 기록하기에 가장 좋은 시기입니다. 보류 중인 속성을 반복하고 Braze에서 적절한 커스텀 속성을 설정하며, 다음에 이 기능이 필요할 때를 대비해 스토리지를 지워 이 작업을 수행할 수 있습니다. 1. `pendingAttributes` 데이터 배열 반복 2. 속성 데이터에서 인코딩된 `UserAttribute` 오브젝트를 초기화 3. 사용자 속성 유형(이메일)에 따라 특정 사용자 필드를 설정 4. 스토리지에서 보류 중인 모든 사용자 속성 제거 ``` swift func logPendingUserAttributesIfNecessary() { let remoteStorage = RemoteStorage(storageType: .suite) guard let pendingAttributes = remoteStorage.retrieve(forKey: .pendingUserAttributes) as? [Data] else { return } // 1 for attributeData in pendingAttributes { // 2 guard let userAttribute = try? PropertyListDecoder().decode(UserAttribute.self, from: attributeData) else { continue } // 3 switch userAttribute { case .email(let email): user?.email = email } } // 4 remoteStorage.removeObject(forKey: .pendingUserAttributes) } ``` ```objc - (void)logPendingUserAttributesIfNecessary { RemoteStorage *remoteStorage = [[RemoteStorage alloc] initWithStorageType:StorageTypeSuite]; NSArray *pendingAttributes = [remoteStorage retrieveForKey:RemoteStorageKeyPendingUserAttributes]; // 1 for (NSData *attributeData in pendingAttributes) { NSError *error; // 2 UserAttribute *userAttribute = [NSKeyedUnarchiver unarchivedObjectOfClass:[UserAttribute class] fromData:attributeData error:&error]; if (error != nil) { // log error } // 3 if (userAttribute) { switch (userAttribute.attributeType) { case UserAttributeTypeEmail: [self user].email = userAttribute.userField; break; } } } // 4 [remoteStorage removeObjectForKey:RemoteStorageKeyPendingUserAttributes]; } ``` ##### 헬퍼 파일 {#helper-files} **RemoteStorage 헬퍼 파일** ```swift enum RemoteStorageKey: String, CaseIterable { // MARK: - Notification Content Extension Analytics case pendingCustomEvents = "pending_custom_events" case pendingCustomAttributes = "pending_custom_attributes" case pendingUserAttributes = "pending_user_attributes" } enum RemoteStorageType { case standard case suite } class RemoteStorage: NSObject { private var storageType: RemoteStorageType = .standard private lazy var defaults: UserDefaults = { switch storageType { case .standard: return .standard case .suite: return UserDefaults(suiteName: "YOUR-DOMAIN-IDENTIFIER")! } }() init(storageType: RemoteStorageType = .standard) { self.storageType = storageType } func store(_ value: Any, forKey key: RemoteStorageKey) { defaults.set(value, forKey: key.rawValue) } func retrieve(forKey key: RemoteStorageKey) -> Any? { return defaults.object(forKey: key.rawValue) } func removeObject(forKey key: RemoteStorageKey) { defaults.removeObject(forKey: key.rawValue) } func resetStorageKeys() { for key in RemoteStorageKey.allCases { defaults.removeObject(forKey: key.rawValue) } } } ``` ```objc @interface RemoteStorage () @property (nonatomic) StorageType storageType; @property (nonatomic, strong) NSUserDefaults *defaults; @end @implementation RemoteStorage - (id)initWithStorageType:(StorageType)storageType { if (self = [super init]) { self.storageType = storageType; } return self; } - (void)store:(id)value forKey:(RemoteStorageKey)key { [[self defaults] setValue:value forKey:[self rawValueForKey:key]]; } - (id)retrieveForKey:(RemoteStorageKey)key { return [[self defaults] objectForKey:[self rawValueForKey:key]]; } - (void)removeObjectForKey:(RemoteStorageKey)key { [[self defaults] removeObjectForKey:[self rawValueForKey:key]]; } - (void)resetStorageKeys { [[self defaults] removeObjectForKey:[self rawValueForKey:RemoteStorageKeyPendingCustomEvents]]; [[self defaults] removeObjectForKey:[self rawValueForKey:RemoteStorageKeyPendingCustomAttributes]]; [[self defaults] removeObjectForKey:[self rawValueForKey:RemoteStorageKeyPendingUserAttributes]]; } - (NSUserDefaults *)defaults { if (!self.defaults) { switch (self.storageType) { case StorageTypeStandard: return [NSUserDefaults standardUserDefaults]; break; case StorageTypeSuite: return [[NSUserDefaults alloc] initWithSuiteName:@"YOUR-DOMAIN-IDENTIFIER"]; } } else { return self.defaults; } } - (NSString*)rawValueForKey:(RemoteStorageKey)remoteStorageKey { switch(remoteStorageKey) { case RemoteStorageKeyPendingCustomEvents: return @"pending_custom_events"; case RemoteStorageKeyPendingCustomAttributes: return @"pending_custom_attributes"; case RemoteStorageKeyPendingUserAttributes: return @"pending_user_attributes"; default: [NSException raise:NSGenericException format:@"Unexpected FormatType."]; } } ``` **UserAttribute 헬퍼 파일** ```swift enum UserAttribute: Hashable { case email(String?) } // MARK: - Codable extension UserAttribute: Codable { private enum CodingKeys: String, CodingKey { case email } func encode(to encoder: Encoder) throws { var values = encoder.container(keyedBy: CodingKeys.self) switch self { case .email(let email): try values.encode(email, forKey: .email) } } init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) let email = try values.decode(String.self, forKey: .email) self = .email(email) } } ``` ```objc @implementation UserAttribute - (id)initWithUserField:(NSString *)userField attributeType:(UserAttributeType)attributeType { if (self = [super init]) { self.userField = userField; self.attributeType = attributeType; } return self; } - (void)encodeWithCoder:(NSCoder *)encoder { [encoder encodeObject:self.userField forKey:@"userField"]; [encoder encodeInteger:self.attributeType forKey:@"attributeType"]; } - (id)initWithCoder:(NSCoder *)decoder { if (self = [super init]) { self.userField = [decoder decodeObjectForKey:@"userField"]; NSInteger attributeRawValue = [decoder decodeIntegerForKey:@"attributeType"]; self.attributeType = (UserAttributeType) attributeRawValue; } return self; } @end ``` **EventName Dictionary 헬퍼 파일** ```swift extension Dictionary where Key == String, Value == Any { init(eventName: String, properties: [String: Any]? = nil) { self.init() self[PushNotificationKey.eventName.rawValue] = eventName if let properties = properties { for (key, value) in properties { self[key] = value } } } } ``` ```objc @implementation NSDictionary (Helper) - (id)initWithEventName:(NSString *)eventName properties:(NSDictionary *)properties { self = [self init]; if (self) { dict[@"event_name"] = eventName; for(id key in properties) { dict[key] = properties[key]; } } return self; } @end ```
# 푸시 알림 테스트 iOS용 Source: /docs/ko/developer_guide/platforms/legacy_sdks/ios/push_notifications/testing/index.md
**Warning:** [AppboyKit](https://github.com/Appboy/appboy-ios-sdk) (also known as the Objective-C SDK) is no longer supported and has been replaced by the [Swift SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=swift). It will no longer receive new features, bug fixes, security updates, or technical support—however, messaging and analytics will continue to function as normal. To learn more, see [Introducing the New Braze Swift SDK](https://www.braze.com/resources/articles/introducing-the-new-braze-swift-sdk). # 테스트 {#push-testing} 명령줄을 통해 인앱 및 푸시 알림을 테스트하려면 터미널을 통해 CURL 및 [메시징 API](https://www.braze.com/docs/ko/ko/api/endpoints/messaging/)를 통해 단일 알림을 보낼 수 있습니다. 다음 필드를 테스트 케이스에 맞는 올바른 값으로 바꿔야 합니다: 필수 필드: - `YOUR-API-KEY-HERE` - **설정** > **API 키**에서 사용할 수 있습니다. `/messages/send` REST API 엔드포인트를 통해 메시지를 발송할 수 있도록 키가 승인되었는지 확인합니다. - `EXTERNAL_USER_ID` - **사용자 검색** 페이지에서 사용할 수 있습니다. - `REST_API_ENDPOINT_URL` - Braze [인스턴스](https://www.braze.com/docs/ko/ko/api/basics/#엔드포인트)에 나열되어 있습니다. 엔드포인트를 사용하는 것이 워크스페이스가 있는 Braze 인스턴스에 해당하는지 확인하세요. 선택적 필드: - `YOUR_KEY1` (선택 사항) - `YOUR_VALUE1` (선택 사항) ```bash curl -X POST -H "Content-Type: application/json" -H "Authorization: Bearer YOUR-API-KEY-HERE" -d '{ "external_user_ids":["EXTERNAL_USER_ID"], "messages": { "apple_push": { "alert":"Test push", "extra": { "YOUR_KEY1":"YOUR_VALUE1" } } } }' https://{REST_API_ENDPOINT_URL}/messages/send ``` # iOS용 푸시 알림 단위 테스트 Source: /docs/ko/developer_guide/platforms/legacy_sdks/ios/push_notifications/unit_tests/index.md
**Warning:** [AppboyKit](https://github.com/Appboy/appboy-ios-sdk) (also known as the Objective-C SDK) is no longer supported and has been replaced by the [Swift SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=swift). It will no longer receive new features, bug fixes, security updates, or technical support—however, messaging and analytics will continue to function as normal. To learn more, see [Introducing the New Braze Swift SDK](https://www.braze.com/resources/articles/introducing-the-new-braze-swift-sdk). # 단위 테스트 {#unit-tests} 이 선택적 가이드는 앱 대리자가 [푸시 통합 지침](https://www.braze.com/docs/ko/ko/developer_guide/platforms/legacy_sdks/ios/push_notifications/integration/)에 설명된 단계를 올바르게 따르는지 확인하는 몇 가지 단위 테스트를 구현하는 방법을 설명합니다. 모든 테스트가 통과되면 일반적으로 코드 기반 푸시 설정 부분이 작동한다는 의미입니다. 테스트가 실패하면 단계를 잘못 따랐거나 유효한 사용자 지정이 기본 지침과 정확히 일치하지 않기 때문일 수 있습니다. 어느 쪽이든 통합 단계를 따랐는지 확인하고 회귀를 모니터링하는 데 도움이 되는 유용한 접근 방식이 될 수 있습니다. ## 1단계: 단위 테스트 대상 생성 Xcode의 앱 프로젝트에 단위 테스트 번들이 이미 포함되어 있는 경우 이 단계를 건너뜁니다. 앱 프로젝트에서 메뉴 **파일 > 새로 만들기 > 대상**으로 이동하여 새로운 '단위 테스트 번들'을 추가합니다. 이 번들은 Objective-C 또는 Swift를 사용할 수 있으며 어떤 이름도 지정할 수 있습니다. '테스트할 대상'을 기본 앱 대상으로 설정합니다. ## 2단계: 단위 테스트에 Braze 소프트웨어 개발 키트를 추가하십시오 처음에 [Braze SDK를 설치](https://www.braze.com/docs/ko/ko/developer_guide/platforms/legacy_sdks/ios/initial_sdk_setup/overview/)하는 데 사용한 동일한 방법을 사용하여 동일한 SDK 설치가 단위 테스트 대상으로 사용 가능한지 확인합니다. 예를 들어, CocoaPods를 사용하는 경우: ``` target 'YourAppTarget' do pod 'Appboy-iOS-SDK' target 'YourAppTargetTests' do inherit! :search_paths end end ``` ## 3단계: 단위 테스트에 OCMock 추가 CocoaPods, Carthage 또는 정적 라이브러리를 통해 [OCMock](https://ocmock.org/)을 테스트 대상에 추가하십시오. 예를 들어, CocoaPods를 사용하는 경우: ``` target 'YourAppTarget' do pod 'Appboy-iOS-SDK' target 'YourAppTargetTests' do inherit! :search_paths pod 'OCMock' end end ``` ## 4단계: 추가된 라이브러리 설치 완료 Braze SDK 및 OCMock 설치를 완료합니다. 예를 들어 CocoaPods를 통해 터미널에서 Xcode 앱 프로젝트의 디렉토리로 이동하여 다음 명령을 실행합니다. ``` pod install ``` 이 시점에서 CocoaPods가 생성한 Xcode 프로젝트 작업 공간을 열 수 있어야 합니다. ## 5단계: 푸시 테스트 추가 단위 테스트 대상에 새 Objective-C 파일을 만드십시오. 단위 테스트 대상이 Swift에 있는 경우 Xcode에서 'Objective-C 브리징 헤더를 구성하시겠어요?'라고 물을 수 있습니다. 브리징 헤더는 선택 사항이므로 **생성하지 않음**을 클릭하고 여전히 이러한 단위 테스트를 성공적으로 실행할 수 있습니다. 새 파일에 HelloSwift 샘플 앱의 [`AppboyPushUnitTests.m`](https://github.com/Appboy/appboy-ios-sdk/blob/master/HelloSwift/HelloSwiftTests/AppboyPushUnitTests.m) 내용을 추가합니다. ## 6단계: 테스트 스위트 실행 앱의 유닛 테스트를 실행하세요. 일회성 검증 단계일 수 있습니다. 또는 테스트 스위트에 무기한으로 이를 포함시켜 회귀를 포착할 수 있습니다. # iOS용 푸시 알림 문제 해결 Source: /docs/ko/developer_guide/platforms/legacy_sdks/ios/push_notifications/troubleshooting/index.md
**Warning:** [AppboyKit](https://github.com/Appboy/appboy-ios-sdk) (also known as the Objective-C SDK) is no longer supported and has been replaced by the [Swift SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=swift). It will no longer receive new features, bug fixes, security updates, or technical support—however, messaging and analytics will continue to function as normal. To learn more, see [Introducing the New Braze Swift SDK](https://www.braze.com/resources/articles/introducing-the-new-braze-swift-sdk). # 문제 해결 {#push-troubleshooting} ## Braze/APNs 워크플로 이해하기 {#understanding-the-brazeapns-workflow} Apple 푸시 알림 서비스(APNs)는 iOS 및 OS X 애플리케이션에 푸시 알림을 전송하기 위한 Apple의 인프라입니다. 다음은 사용자의 기기에서 푸시 알림을 활성화하는 방법과 Braze가 푸시 알림을 보내는 방법에 대한 간단한 구조입니다: 1. 푸시 인증서 및 프로비저닝 프로필을 구성합니다. 2. 기기가 APNs에 등록하고 Braze에 푸시 토큰을 제공합니다. 3. Braze 푸시 Campaign을 시작합니다. 4. Braze는 유효하지 않은 토큰을 제거합니다. #### 1단계: 푸시 인증서 및 프로비저닝 프로필 구성하기 {#step-1-configuring-the-push-certificate-and-provisioning-profile} 앱을 개발할 때 푸시 알림을 활성화하려면 SSL 인증서를 생성해야 합니다. 이 인증서는 앱이 빌드된 프로비저닝 프로필에 포함되며, Braze 대시보드에도 업로드되어야 합니다. 이 인증서를 통해 Braze는 사용자를 대신하여 푸시 알림을 전송할 수 있음을 APNs에 알릴 수 있습니다. [프로비저닝 프로필](https://developer.apple.com/library/content/documentation/IDEs/Conceptual/AppDistributionGuide/MaintainingProfiles/MaintainingProfiles.html) 및 인증서에는 개발 및 배포의 두 가지 유형이 있습니다. 혼동을 피하기 위해 배포 프로필과 인증서만 사용하는 것을 권장합니다. 개발 및 배포에 다른 프로필과 인증서를 사용하기로 선택한 경우 대시보드에 업로드한 인증서가 현재 사용 중인 프로비저닝 프로필과 일치하는지 확인하세요. **Warning:** 푸시 인증서 환경을 변경하지 마세요(개발 대 프로덕션). 푸시 인증서를 잘못된 환경으로 변경하면 푸시 토큰이 실수로 제거되어 푸시가 사용자에게 전달되지 못할 수 있습니다. #### 2단계: 기기가 APNs에 등록하고 Braze에 푸시 토큰을 제공합니다 {#step-2-devices-register-for-apns-and-provide-braze-with-push-tokens} 사용자가 앱을 열면 푸시 알림을 수락할지 묻는 프롬프트가 표시됩니다. 이 프롬프트를 수락하면 APNs가 해당 특정 기기에 대한 푸시 토큰을 생성합니다. iOS SDK는 기본 [자동 플러시 정책](https://www.braze.com/docs/ko/ko/developer_guide/platform_integration_guides/ios/advanced_use_cases/fine_network_traffic_control/#automatic-request-processing)을 사용하는 앱에 대해 푸시 토큰을 즉시 비동기적으로 전송합니다. 푸시 토큰이 사용자와 연결되면 사용자 프로필의 **참여** 탭에 있는 대시보드에 "푸시 등록됨"으로 표시되며, Braze Campaign에서 푸시 알림을 받을 수 있게 됩니다. **Note:** Xcode 14부터는 iOS 시뮬레이터에서 원격 푸시 알림을 테스트할 수 있습니다. #### 3단계: Braze 푸시 Campaign 시작 {#step-3-launching-a-braze-push-campaign} 푸시 Campaign이 시작되면 Braze는 APNs에 메시지 전달을 요청합니다. Braze는 대시보드에 업로드된 SSL 푸시 인증서를 사용하여 제공된 푸시 토큰으로 푸시 알림을 전송할 수 있는지 인증하고 확인합니다. 기기가 온라인 상태인 경우 Campaign이 전송된 직후에 알림이 수신되어야 합니다. Braze는 알림의 기본 APNs [만료일](https://developer.apple.com/documentation/usernotifications/setting_up_a_remote_notification_server/sending_notification_requests_to_apns#2947607)을 30일로 설정합니다. #### 4단계: 유효하지 않은 토큰 제거하기 {#step-4-removing-invalid-tokens} 메시지를 보내려고 했던 푸시 토큰이 유효하지 않다고 [APNs](https://developer.apple.com/library/content/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/APNSOverview.html#//apple_ref/doc/uid/TP40008194-CH8-SW1)가 알려주면 해당 토큰이 연결된 사용자 프로필에서 해당 토큰을 제거합니다. ## 푸시 오류 로그 활용하기 {#utilizing-the-push-error-logs} Braze는 **메시지 활동 로그** 내에 푸시 알림 오류 로그를 제공합니다. 이 오류 로그는 Campaign이 예상대로 작동하지 않는 이유를 파악하는 데 매우 유용한 다양한 경고를 제공합니다. 오류 메시지를 클릭하면 특정 인시던트 문제를 해결하는 데 도움이 되는 관련 설명서로 리디렉션됩니다. ![오류 발생 시간, 앱 이름, 채널, 오류 유형 및 오류 메시지를 표시하는 푸시 오류 로그.](https://www.braze.com/docs/ko/ko/assets/img_archive/message_activity_log.png?6577302323ab3f2df3196a973320b8d3) 여기에서 볼 수 있는 일반적인 오류로는 ["푸시 토큰에 등록되지 않은 전송 수신"](#received-unregistered-sending) 등의 사용자별 알림이 있습니다. 또한 Braze는 **참여** 탭의 사용자 프로필에서 푸시 변경 로그를 제공합니다. 이 체인지로그는 토큰 무효화, 푸시 등록 오류, 새 사용자에게 이동되는 토큰 등의 푸시 등록 동작에 대한 인사이트를 제공합니다. ![](https://www.braze.com/docs/ko/ko/assets/img_archive/push_changelog.gif?36d10186d33121a195e943385dd0d02a){: style="max-width:50%;" } ## 푸시 등록 문제 {#push-registration-issues} 애플리케이션의 푸시 등록 로직에 대한 검증을 추가하려면 [푸시 단위 테스트](https://www.braze.com/docs/ko/ko/developer_guide/platforms/legacy_sdks/ios/push_notifications/unit_tests/)를 구현하세요. #### 푸시 등록 프롬프트 없음 {#no-push-registration-prompt} 애플리케이션에서 사용자에게 푸시 알림을 등록하라는 프롬프트를 표시하지 않으면 푸시 등록 통합에 문제가 있을 수 있습니다. [설명서](https://www.braze.com/docs/ko/ko/developer_guide/platforms/legacy_sdks/ios/push_notifications/integration/)를 준수하고 푸시 등록을 올바르게 통합했는지 확인하세요. 코드에 중단점을 설정하여 푸시 등록 코드가 실행 중인지 확인할 수도 있습니다. #### 대시보드에 "푸시 등록" 사용자가 표시되지 않음 {#no-push-registered-users-showing-in-the-dashboard} - 앱에서 푸시 알림을 허용하라는 프롬프트를 표시하는지 확인합니다. 일반적으로 이 프롬프트는 앱을 처음 열 때 표시되지만 다른 경우에 표시하도록 프로그래밍할 수 있습니다. 표시해야 할 위치에 표시되지 않는다면 앱의 푸시 기능 기본 구성에 문제가 있는 것일 수 있습니다. - [푸시 통합](https://www.braze.com/docs/ko/ko/developer_guide/platforms/legacy_sdks/ios/push_notifications/integration/)을 위한 단계가 성공적으로 완료되었는지 확인합니다. - 앱이 빌드된 프로비저닝 프로필에 푸시 권한이 포함되어 있는지 확인합니다. Apple 개발자 계정에서 사용 가능한 모든 프로비저닝 프로필을 가져오고 있는지 확인합니다. 이를 확인하려면 다음 단계를 수행하세요: 1. Xcode에서 **Preferences > Accounts**로 이동합니다(또는 키보드 단축키 Command+,를 사용합니다). 2. 개발자 계정에 사용하는 Apple ID를 선택하고 **View Details**를 클릭합니다. 3. 다음 페이지에서 ** Refresh**를 클릭하고 사용 가능한 모든 프로비저닝 프로필을 가져오는지 확인합니다. - 앱에서 [푸시 기능을 제대로 활성화했는지](https://www.braze.com/docs/ko/ko/developer_guide/platform_integration_guides/ios/push_notifications/integration/#step-2-enable-push-capabilities) 확인하세요. - 푸시 프로비저닝 프로필이 테스트를 수행하는 환경과 일치하는지 확인합니다. 유니버설 인증서는 개발 또는 프로덕션 APNs 환경으로 보내도록 Braze 대시보드에서 구성할 수 있습니다. 프로덕션 앱에 개발 인증서를 사용하거나 개발 앱에 프로덕션 인증서를 사용하는 경우 작동하지 않습니다. - 코드에 중단점을 설정하여 `registerPushToken` 메서드를 호출하고 있는지 확인하세요. - 기기를 사용 중이고(시뮬레이터에서는 푸시가 작동하지 않음) 네트워크 연결 상태가 양호한지 확인합니다. ## 푸시 알림을 받지 못하는 기기 {#devices-not-receiving-push-notifications} #### 푸시 알림을 보낸 후 사용자가 더 이상 "푸시 등록" 상태가 아님 {#users-no-longer-push-registered-after-sending-a-push-notification} 사용자에게 유효하지 않은 푸시 토큰이 있음을 나타낼 수 있습니다. 이는 여러 가지 이유로 발생할 수 있습니다: ##### 대시보드와 앱 인증서 불일치 {#dashboard-and-app-certificate-mismatch} 대시보드에 업로드한 푸시 인증서가 앱이 빌드된 프로비저닝 프로필의 푸시 인증서와 같지 않은 경우 APNs는 토큰을 거부합니다. 다른 테스트 알림을 시도하기 전에 올바른 인증서를 업로드하고 앱에서 다른 세션을 완료했는지 확인합니다. ##### 앱 삭제 {#uninstalls} 사용자가 애플리케이션을 삭제한 경우, 해당 푸시 토큰은 유효하지 않으며 다음 전송 시 제거됩니다. ##### 프로비저닝 프로필 다시 생성 {#regenerating-your-provisioning-profile} 마지막 수단으로, 처음부터 완전히 새로운 프로비저닝 프로필을 만들면 여러 환경, 프로필 및 앱에서 동시에 작업할 때 발생하는 구성 오류를 해결할 수 있습니다. iOS 앱용 푸시 알림을 설정할 때 "변동 부분"이 많으므로 때로는 처음부터 다시 시도하는 것이 가장 좋습니다. 또한 문제 해결을 계속해야 하는 경우 문제를 격리하는 데 도움이 됩니다. #### 푸시 알림을 보낸 후 사용자가 여전히 "푸시 등록" 상태임 {#users-still-push-registered-after-sending-a-push-notification} ##### 앱이 포그라운드에 있음 {#app-is-foregrounded} `UserNotifications` 프레임워크를 통해 푸시를 통합하지 않는 iOS 버전에서는 푸시 메시지를 수신할 때 앱이 포그라운드에 있는 경우 푸시 메시지가 표시되지 않습니다. 테스트 메시지를 보내기 전에 테스트 기기에서 앱을 백그라운드로 실행해야 합니다. ##### 잘못 예약된 테스트 알림 {#test-notification-scheduled-incorrectly} 테스트 메시지에 대해 설정한 스케줄을 확인하세요. 현지 시간대 전달 또는 [Intelligent Timing](https://www.braze.com/docs/ko/ko/user_guide/brazeai/intelligence_suite/intelligent_timing/)으로 설정되어 있는 경우 메시지를 아직 받지 못했거나 메시지를 받았을 때 앱이 포그라운드에 있었을 수 있습니다. #### 테스트 중인 앱에서 "푸시 등록"되지 않은 사용자 {#user-not-push-registered-for-the-app-being-tested} 테스트 메시지를 보내려는 사용자의 고객 프로필을 확인합니다. **참여** 탭 아래에 "푸시 가능한 앱" 목록이 있어야 합니다. 테스트 메시지를 보내려는 앱이 이 목록에 있는지 확인합니다. 사용자가 워크스페이스의 앱에 대한 푸시 토큰을 보유한 경우 "푸시 등록"으로 표시되므로 이는 오탐일 수 있습니다. 다음은 푸시 등록에 문제가 있거나 사용자의 토큰이 푸시된 후 APNs에 의해 유효하지 않은 것으로 Braze에 반환되었음을 나타냅니다: ![사용자의 연락처 설정을 표시하는 고객 프로필입니다. 여기에서 푸시가 등록된 앱을 확인할 수 있습니다.](https://www.braze.com/docs/ko/ko/assets/img_archive/registration_problem.png?b01abd4f8f8ddd58425f6ecc82c256ea){: style="max-width:50%"} ## 푸시 메시지 미발송 {#push-messages-not-sending} 전송되지 않는 푸시 알림 문제를 해결하려면 [푸시 문제 해결](https://www.braze.com/docs/ko/ko/user_guide/channels/push/troubleshooting/)을 참조하세요. ## 메시지 활동 로그 오류 {#message-activity-log-errors} #### 푸시 토큰에 등록되지 않은 전송 수신됨 {#received-unregistered-sending} - `[[Appboy sharedInstance] registerPushToken:]` 메서드에서 Braze로 전송되는 푸시 토큰이 유효한지 확인합니다. **메시지 활동 로그**에서 푸시 토큰을 확인할 수 있습니다. 문자와 숫자 조합이 포함된 긴 문자열(예: `6e407a9be8d07f0cdeb9e724733a89445f57a89ec890d63867c482a483506fa6`)이어야 합니다. 푸시 토큰이 다르게 보인다면 Braze에 푸시 토큰을 전송하는 [코드](https://www.braze.com/docs/ko/ko/developer_guide/platform_integration_guides/ios/push_notifications/integration/#step-4-register-push-tokens-with-braze)를 확인하세요. - 푸시 프로비저닝 프로필이 테스트 중인 환경과 일치하는지 확인합니다. 유니버설 인증서는 개발 또는 프로덕션 APNs 환경으로 보내도록 Braze 대시보드에서 구성할 수 있습니다. 프로덕션 앱에 개발 인증서를 사용하거나 개발 앱에 프로덕션 인증서를 사용하는 경우 작동하지 않습니다. - Braze에 업로드한 푸시 토큰이 푸시 토큰을 보낸 앱을 빌드하는 데 사용한 프로비저닝 프로필과 일치하는지 확인합니다. #### 주제용이 아닌 기기 토큰 {#device-token-not-for-topic} 이 오류는 앱의 푸시 인증서와 번들 ID가 일치하지 않음을 나타냅니다. Braze에 업로드한 푸시 인증서가 푸시 토큰을 보낸 앱을 빌드하는 데 사용한 프로비저닝 프로필과 일치하는지 확인합니다. #### 푸시 토큰으로 전송하는 BadDeviceToken {#baddevicetoken-sending-to-push-token} `BadDeviceToken`은 APNs 오류 코드이며, Braze에서 생성된 오류가 아닙니다. 이 응답이 반환되는 데에는 다음과 같은 여러 가지 이유가 있을 수 있습니다: - 앱이 대시보드에 업로드된 자격 증명에 대해 유효하지 않은 푸시 토큰을 수신했습니다. - 이 워크스페이스에 대해 푸시가 비활성화되었습니다. - 사용자가 푸시 수신을 거부했습니다. - 앱이 삭제되었습니다. - Apple이 푸시 토큰을 새로 고침하여 기존 토큰이 무효화되었습니다. - 앱은 프로덕션 환경용으로 빌드되었지만 Braze에 업로드된 푸시 자격 증명은 개발 환경용으로 설정되어 있습니다(또는 반대의 경우도 마찬가지). ## 푸시 전송 후 문제 {#issues-after-push-delivery} 애플리케이션의 푸시 처리에 대한 검증을 추가하려면 [푸시 단위 테스트](https://www.braze.com/docs/ko/ko/developer_guide/platforms/legacy_sdks/ios/push_notifications/unit_tests/)를 구현하세요. #### 푸시 클릭이 기록되지 않음 {#push-clicks-not-logged} - 이 문제가 iOS 10에서만 발생하는 경우 [iOS 10](https://www.braze.com/docs/ko/ko/developer_guide/platform_integration_guides/ios/push_notifications/integration/#step-5-enable-push-handling)에 대한 푸시 통합 단계를 따랐는지 확인하세요. - Braze는 포그라운드에서 무음 수신된 푸시 알림을 처리하지 않습니다(예: `UserNotifications` 프레임워크 이전의 기본 포그라운드 푸시 동작). 즉, 링크가 열리지 않고 푸시 클릭이 기록되지 않습니다. 애플리케이션이 아직 `UserNotifications` 프레임워크를 통합하지 않은 경우, 애플리케이션 상태가 `UIApplicationStateActive`이면 Braze는 푸시 알림을 처리하지 않습니다. 앱에서 [푸시 처리 메서드](https://www.braze.com/docs/ko/ko/developer_guide/platform_integration_guides/ios/push_notifications/integration/#step-5-enable-push-handling) 호출이 지연되지 않도록 해야 하며, 그렇지 않으면 iOS SDK에서 푸시 알림을 무음 포그라운드 푸시 이벤트로 처리하여 전달하지 않을 수 있습니다. #### 푸시 클릭으로 인한 웹 링크가 열리지 않음 {#web-links-from-push-clicks-not-opening} iOS 9 이상에서는 웹 보기에서 링크를 열려면 ATS와 호환되는 링크가 필요합니다. 웹 링크가 HTTPS를 사용하는지 확인합니다. 자세한 내용은 [ATS 규정 준수](https://www.braze.com/docs/ko/ko/developer_guide/platform_integration_guides/ios/advanced_use_cases/linking/#app-transport-security-ats) 문서를 참조하세요. #### 푸시 클릭으로 인한 딥링크가 열리지 않음 {#deep-links-from-push-clicks-not-opening} 딥링크를 처리하는 대부분의 코드는 푸시 오픈도 처리합니다. 먼저 푸시 오픈이 기록되고 있는지 확인합니다. 그렇지 않은 경우 [해당 문제를 수정하세요](#push-clicks-not-logged)(수정하면 링크 처리가 수정되는 경우가 많으므로). 열람이 기록되는 경우 일반적인 딥링크 문제인지 또는 딥링크 푸시 클릭 처리에 문제가 있는지 확인합니다. 이를 위해 인앱 메시지 클릭의 딥링크가 작동하는지 테스트합니다. #### 직접 열람 수가 거의 또는 전혀 없음 {#few-or-no-direct-opens} 한 명 이상의 사용자가 iOS 푸시 알림을 열었지만 Braze에 _직접 열람 수_가 거의 또는 전혀 기록되지 않는다면 [SDK 통합](https://www.braze.com/docs/ko/ko/developer_guide/platforms/legacy_sdks/ios/initial_sdk_setup/overview/)에 문제가 있는 것일 수 있습니다. 테스트 전송 또는 무음 푸시 알림의 경우 _직접 열람 수_는 기록되지 않습니다. - 메시지가 [무음 푸시 알림](https://www.braze.com/docs/ko/ko/developer_guide/platform_integration_guides/swift/push_notifications/silent_push_notifications/#sending-silent-push-notifications)으로 전송되고 있지 않은지 확인하세요. 메시지가 무음으로 간주되지 않으려면 제목이나 본문에 텍스트가 있어야 합니다. - [푸시 통합 가이드](https://www.braze.com/docs/ko/ko/developer_guide/platforms/legacy_sdks/ios/push_notifications/integration/)에서 다음 단계를 다시 확인하세요: - [푸시 등록하기](https://www.braze.com/docs/ko/ko/developer_guide/platform_integration_guides/swift/push_notifications/integration/#step-1-register-for-push-notifications-with-apns): 앱을 실행할 때마다, 가급적이면 `application:didFinishLaunchingWithOptions:` 내에서 3단계의 코드가 실행되어야 합니다. `UNUserNotificationCenter.current()`의 델리게이트 속성은 `UNUserNotificationCenterDelegate`를 구현하고 `(void)userNotificationCenter:didReceiveNotificationResponse:withCompletionHandler:` 메서드를 포함하는 오브젝트에 할당해야 합니다. - [푸시 처리 활성화](https://www.braze.com/docs/ko/ko/developer_guide/platform_integration_guides/legacy_sdks/ios/push_notifications/integration/#step-5-enable-push-handling): `(void)userNotificationCenter:didReceiveNotificationResponse:withCompletionHandler:` 메서드가 구현되었는지 확인합니다. # iOS용 인앱 메시지 개요 Source: /docs/ko/developer_guide/platforms/legacy_sdks/ios/in-app_messaging/overview/index.md
**Warning:** [AppboyKit](https://github.com/Appboy/appboy-ios-sdk) (also known as the Objective-C SDK) is no longer supported and has been replaced by the [Swift SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=swift). It will no longer receive new features, bug fixes, security updates, or technical support—however, messaging and analytics will continue to function as normal. To learn more, see [Introducing the New Braze Swift SDK](https://www.braze.com/resources/articles/introducing-the-new-braze-swift-sdk). # 인앱 메시지 {#in-app-messages} [인앱 메시지](https://www.braze.com/docs/ko/ko/user_guide/channels/in_app_messages/)는 푸시 알림으로 사용자의 일과를 방해하지 않고 콘텐츠를 전달할 수 있도록 도와줍니다. 커스텀화되고 맞춤 조정된 인앱 메시지는 사용자 경험을 향상시키고 오디언스가 앱에서 최대한의 가치를 얻을 수 있도록 도와줍니다. 다양한 레이아웃과 커스텀 도구를 선택할 수 있는 인앱 메시지는 그 어느 때보다 사용자의 참여를 유도합니다. 인앱 메시지의 예제를 보려면 [사례 연구](https://www.braze.com/customers)를 확인하세요. ## 인앱 메시지 유형 {#in-app-message-types} Braze는 현재 다음과 같은 기본 인앱 메시지 유형을 제공합니다: - `Slideup` - `Modal` - `Full` - `HTML Full` 각 인앱 메시지 유형은 콘텐츠, 이미지, 아이콘, 클릭 동작, 분석, 표시 및 전달 전반에 걸쳐 고도로 커스텀 가능합니다. 모든 인앱 메시지는 모든 인앱 메시지의 기본 동작과 특성을 정의하는 `ABKInAppMessage`의 서브클래스입니다. 인앱 메시지 클래스 구조는 다음과 같습니다: ![ABKInAppMessage 클래스가 ABKInAppMessageSlideup, ABKInAppMessageImmersive, ABKInAppMessageHTML의 루트 클래스임을 보여주는 그래픽. ABKInAppMessage에는 메시지, 추가 항목, 기간, 클릭 동작, URI, 해제 동작, 아이콘 방향 및 텍스트 정렬과 같은 커스텀 가능한 속성정보가 포함되어 있습니다. ABKInAppMessageSlideup에는 갈매기형 및 슬라이드업 앵커와 같은 커스텀 가능한 속성정보가 포함되어 있습니다. ABKInAppMessageImmersive에는 헤더, 닫기 버튼, 프레임 및 인앱 메시지 버튼과 같은 커스텀 가능한 속성정보가 포함되어 있습니다. ABKInAppMessageHTML을 사용하면 HTML 인앱 메시지 버튼 클릭을 수동으로 기록할 수 있습니다.](https://www.braze.com/docs/ko/ko/assets/img_archive/ABKInAppMessage-models.png?b0b1c31bde206c1dc8d5f14a07cc82a0) **Important:** 기본적으로 인앱 메시지는 GIF 지원을 포함하여 표준 SDK 통합을 완료한 후에 활성화됩니다.

iOS 인앱 메시지 또는 Content Cards에서 이미지를 표시하기 위해 Braze UI를 사용하려는 경우 `SDWebImage`의 통합이 필요합니다. ### 메시지 유형별 예상 동작 {#expected-behaviors-by-message-types} 다음은 사용자가 기본 인앱 메시지 유형 중 하나를 여는 모습입니다. [`Slideup`](https://appboy.github.io/appboy-ios-sdk/docs/interface_a_b_k_in_app_message_slideup.html) 인앱 메시지는 화면 상단 또는 하단에서 '슬라이드 업' 또는 '슬라이드 다운'되기 때문에 그렇게 이름이 붙여졌습니다. 화면의 작은 부분을 차지하며 효과적이고 방해가 되지 않는 메시징 기능을 제공합니다. ![휴대폰 화면 하단에서 슬라이드되는 인앱 메시지로 "Humans are complicated. Custom engagement shouldn't be."라고 표시됩니다. 배경에는 웹 페이지 하단 모서리에 표시되는 동일한 인앱 메시지가 보입니다.](https://www.braze.com/docs/ko/ko/assets/img/slideup-behavior.gif?7239589ee8c964f354440e07ca4b9db1){: style="border:0px;"} [`Modal`](https://appboy.github.io/appboy-ios-sdk/docs/interface_a_b_k_in_app_message_modal.html) 인앱 메시지는 화면 중앙에 표시되며 반투명 패널로 둘러싸여 있습니다. 보다 중요한 메시징에 유용하며, 최대 두 개의 클릭 동작 및 분석 지원 버튼을 제공할 수 있습니다. ![휴대폰 화면 가운데에 표시되는 모달 인앱 메시지로 "Humans are complicated. Custom engagement shouldn't be."라고 표시됩니다. 배경에는 웹 페이지 중앙에 표시되는 동일한 인앱 메시지가 보입니다.](https://www.braze.com/docs/ko/ko/assets/img/modal-behavior.gif?00fa4f83404c611c82cb0816f682e3f3){: style="border:0px;"} [`Full`](https://appboy.github.io/appboy-ios-sdk/docs/interface_a_b_k_in_app_message_full.html) 인앱 메시지는 사용자 커뮤니케이션의 콘텐츠와 효과를 극대화하는 데 유용합니다. `full` 인앱 메시지의 상단에는 이미지가, 하단에는 텍스트와 최대 2개의 클릭 동작 및 분석 지원 버튼이 표시됩니다. ![휴대폰 화면 전체에 표시되는 전체화면 인앱 메시지로 "Humans are complicated. Custom engagement shouldn't be."라고 표시됩니다. 배경에는 웹 페이지 중앙에 크게 표시되는 동일한 인앱 메시지가 보입니다.](https://www.braze.com/docs/ko/ko/assets/img/full-screen-behavior.gif?b47edcbdd910efce932489d1fa592bd0){: style="border:0px;"} [`HTML Full`](https://appboy.github.io/appboy-ios-sdk/docs/interface_a_b_k_in_app_message_h_t_m_l_full.html) 인앱 메시지는 완전히 커스텀화된 사용자 콘텐츠를 만드는 데 유용합니다. 사용자 정의 HTML Full 인앱 메시지 콘텐츠는 `WKWebView`에 표시되며, 선택적으로 이미지 및 글꼴과 같은 다양한 리치 콘텐츠를 포함할 수 있으므로 메시지 모양과 기능을 완벽하게 제어할 수 있습니다.

iOS 인앱 메시지는 HTML 내에서 Braze 웹 SDK의 메서드를 호출하기 위해 JavaScript `brazeBridge` 인터페이스를 지원합니다. 자세한 내용은 [모범 사례](https://www.braze.com/docs/ko/ko/user_guide/channels/in_app_messages/best_practices/)를 참조하세요. 다음 예는 페이지가 지정된 HTML Full 인앱 메시지를 보여줍니다: ![콘텐츠 캐러셀과 인터랙티브 버튼이 포함된 HTML 인앱 메시지.](https://www.braze.com/docs/ko/ko/assets/img_archive/ios-html-full-iam.gif?4c6c9603065d4c430d406677e8cb6045) 전체 인앱 메시지 콘텐츠는 `WKWebView`에 표시되며, 선택적으로 이미지 및 글꼴과 같은 다양한 리치 콘텐츠를 포함할 수 있으므로 메시지 모양과 기능을 완벽하게 제어할 수 있습니다. 현재 iOS 및 Android 플랫폼에서는 iFrame에 커스텀 HTML 인앱 메시지를 표시하는 기능을 지원하지 않습니다. **Note:** iOS SDK 버전 3.19.0부터 `alert`, `confirm`, `prompt`와 같은 JavaScript 메서드는 HTML 인앱 메시지에서 no-ops입니다. # iOS 인앱 메시지 사용자 지정 Source: /docs/ko/developer_guide/platforms/legacy_sdks/ios/in-app_messaging/customization/index.md

# iOS용 인앱 메시지 위임자 설정 Source: /docs/ko/developer_guide/platforms/legacy_sdks/ios/in-app_messaging/customization/setting_delegates/index.md
**Warning:** [AppboyKit](https://github.com/Appboy/appboy-ios-sdk) (also known as the Objective-C SDK) is no longer supported and has been replaced by the [Swift SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=swift). It will no longer receive new features, bug fixes, security updates, or technical support—however, messaging and analytics will continue to function as normal. To learn more, see [Introducing the New Braze Swift SDK](https://www.braze.com/resources/articles/introducing-the-new-braze-swift-sdk). # 위임자 설정 인앱 메시지 표시 및 전달을 위임(선택 사항)을 설정하여 코드에서 사용자 지정할 수 있습니다. ## 인앱 메시지 위임 [`ABKInAppMessageUIDelegate`](https://github.com/Appboy/appboy-ios-sdk/blob/master/AppboyUI/ABKInAppMessage/ABKInAppMessageUIDelegate.h) 위임은 추가 처리를 위해 트리거된 인앱 메시지 페이로드를 수신하고 표시 생애주기 이벤트를 수신하며 표시 타이밍을 제어하는 데 사용할 수 있습니다. 다음을 호출하여 `ABKInAppMessageUIDelegate` Braze 인스턴스에서 위임 오브젝트를 설정합니다. ```objc [[Appboy sharedInstance].inAppMessageController.inAppMessageUIController setInAppMessageUIDelegate:self]; ``` ```swift Appboy.sharedInstance()?.inAppMessageController.inAppMessageUIController?.setInAppMessageUIDelegate?(self) ``` 구현 예제는 인앱 메시지 [샘플 앱](https://github.com/Appboy/appboy-ios-sdk/blob/master/Samples/InAppMessage/BrazeInAppMessageSample/BrazeInAppMessageSample/ViewController.m)을 확인하세요. 프로젝트에 Braze UI 라이브러리를 포함하지 않은 경우(흔하지 않음) 이 위임은 사용할 수 없습니다. ## 핵심 인앱 메시지 위임 프로젝트에 Braze UI 라이브러리를 포함하지 않고 앱에서 커스텀 표시 또는 추가 처리를 위해 트리거된 인앱 메시지 페이로드를 수신하려는 경우 [`ABKInAppMessageControllerDelegate`](https://www.braze.com/docs/ko/ko/developer_guide/platforms/legacy_sdks/ios/in-app_messaging/customization/setting_delegates/) 프로토콜을 구현합니다. 다음을 호출하여 `ABKInAppMessageControllerDelegate` Braze 인스턴스에서 위임 오브젝트를 설정합니다. ```objc [Appboy sharedInstance].inAppMessageController.delegate = self; ``` ```swift Appboy.sharedInstance()?.inAppMessageController.delegate = self ``` 또는 `ABKInAppMessageControllerDelegateKey` 키를 사용하여 `appboyOptions`를 통해 초기화할 때 핵심 인앱 메시지 위임을 설정할 수도 있습니다. ```objc [Appboy startWithApiKey:@"YOUR-API_KEY" inApplication:application withLaunchOptions:options withAppboyOptions:@{ ABKInAppMessageControllerDelegateKey : self }]; ``` ```swift Appboy.start(withApiKey: "YOUR-API-KEY", in:application, withLaunchOptions:launchOptions, withAppboyOptions:[ ABKInAppMessageControllerDelegateKey : self ]) ``` ## 메서드 선언 추가 정보는 다음 헤더 파일을 참조하십시오: - [`ABKInAppMessage.h`](https://github.com/Appboy/appboy-ios-sdk/blob/master/AppboyKit/include/ABKInAppMessage.h) - [`ABKInAppMessageControllerDelegate.h`](https://github.com/Appboy/appboy-ios-sdk/blob/master/AppboyKit/include/ABKInAppMessageControllerDelegate.h) ## 구현 샘플 인앱 메시지 샘플 앱에서 [`ViewController.m`](https://github.com/Appboy/appboy-ios-sdk/blob/master/Samples/InAppMessage/BrazeInAppMessageSample/BrazeInAppMessageSample/ViewController.m)을 참조하세요. # iOS용 인앱 메시지 방향 사용자 정의 Source: /docs/ko/developer_guide/platforms/legacy_sdks/ios/in-app_messaging/customization/customizing_orientation/index.md
**Warning:** [AppboyKit](https://github.com/Appboy/appboy-ios-sdk) (also known as the Objective-C SDK) is no longer supported and has been replaced by the [Swift SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=swift). It will no longer receive new features, bug fixes, security updates, or technical support—however, messaging and analytics will continue to function as normal. To learn more, see [Introducing the New Braze Swift SDK](https://www.braze.com/resources/articles/introducing-the-new-braze-swift-sdk). # 방향 사용자 정의 ## 모든 인앱 메시지의 방향 설정하기 모든 인앱 메시지에 고정 방향을 설정하려면 `ABKInAppMessageUIController`에서 `supportedOrientationMask` 속성정보를 설정하면 됩니다. 앱 호출 후 다음 코드를 `startWithApiKey:inApplication:withLaunchOptions:` 에 추가합니다: ```objc // Set fixed in-app message orientation to portrait. // Use UIInterfaceOrientationMaskLandscape to display in-app messages in landscape id inAppMessageUIController = [Appboy sharedInstance].inAppMessageController.inAppMessageUIController; ((ABKInAppMessageUIController *)inAppMessageUIController).supportedOrientationMask = UIInterfaceOrientationMaskPortrait; ``` ```swift // Set fixed in-app message orientation to portrait // Use .landscape to display in-app messages in landscape if let controller = Appboy.sharedInstance()?.inAppMessageController.inAppMessageUIController as? ABKInAppMessageUIController { controller.supportedOrientationMask = .portrait } ``` 이렇게 하면 기기 방향에 관계없이 모든 인앱 메시지가 지원되는 방향으로 표시됩니다. 기기의 방향이 인앱 메시지의 `orientation` 속성정보에서도 지원해야 메시지가 표시됩니다. ## 인앱 메시지별 방향 설정 또는 메시지별로 방향을 설정할 수도 있습니다. 이렇게 하려면 [인앱 메시지 위임자를](https://www.braze.com/docs/ko/ko/developer_guide/platforms/legacy_sdks/ios/in-app_messaging/customization/setting_delegates/) 설정하세요. 그런 다음, `beforeInAppMessageDisplayed:` 위임 메서드의 `ABKInAppMessage`에서 `orientation` 속성정보를 설정합니다. ```objc // Set inAppMessage orientation to portrait inAppMessage.orientation = ABKInAppMessageOrientationPortrait; // Set inAppMessage orientation to landscape inAppMessage.orientation = ABKInAppMessageOrientationLandscape; ``` ```swift // Set inAppMessage orientation to portrait inAppMessage.orientation = ABKInAppMessageOrientation.portrait // Set inAppMessage orientation to landscape inAppMessage.orientation = ABKInAppMessageOrientation.landscape ``` 기기 방향이 인앱 메시지의 `orientation` 속성정보와 일치하지 않으면 인앱 메시지가 표시되지 않습니다. **Note:** iPads의 경우 인앱 메시지는 실제 화면 방향과 관계없이 사용자가 선호하는 방향 스타일로 표시됩니다. ## 메서드 선언 자세한 내용은 다음 헤더 파일을 참조하세요: - [`ABKInAppMessage.h`](https://github.com/Appboy/appboy-ios-sdk/blob/master/AppboyKit/include/ABKInAppMessage.h) # iOS용 인앱 메시지 디스플레이 처리 사용자 정의 Source: /docs/ko/developer_guide/platforms/legacy_sdks/ios/in-app_messaging/customization/handling_in_app_display/index.md
**Warning:** [AppboyKit](https://github.com/Appboy/appboy-ios-sdk) (also known as the Objective-C SDK) is no longer supported and has been replaced by the [Swift SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=swift). It will no longer receive new features, bug fixes, security updates, or technical support—however, messaging and analytics will continue to function as normal. To learn more, see [Introducing the New Braze Swift SDK](https://www.braze.com/resources/articles/introducing-the-new-braze-swift-sdk). # 커스텀 처리 인앱 메시지 표시 {#custom-handling-in-app-message-display} [`ABKInAppMessageControllerDelegate`](https://github.com/Appboy/appboy-ios-sdk/blob/master/AppboyKit/include/ABKInAppMessageControllerDelegate.h)가 설정되면, 인앱 메시지가 표시되기 전에 다음 델리게이트 메서드가 호출됩니다: ```objc - (ABKInAppMessageDisplayChoice) beforeInAppMessageDisplayed:(ABKInAppMessage *)inAppMessage; ``` ```swift func beforeInAppMessageDisplayed(inAppMessage: ABKInAppMessage!) -> ABKInAppMessageDisplayChoice ``` [`ABKInAppMessageUIDelegate`](https://github.com/Appboy/appboy-ios-sdk/blob/master/AppboyUI/ABKInAppMessage/ABKInAppMessageUIDelegate.h)만 구현한 경우 다음 UI 델리게이트 메서드가 대신 호출됩니다: ```objc - (ABKInAppMessageDisplayChoice) beforeInAppMessageDisplayed:(ABKInAppMessage *)inAppMessage withKeyboardIsUp:(BOOL)keyboardIsUp; ``` ```swift func beforeInAppMessageDisplayed(inAppMessage: ABKInAppMessage!, withKeyboardIsUp keyboardIsUp: Bool) -> ABKInAppMessageDisplayChoice ``` 이 델리게이트 메서드를 구현하고 `ABKInAppMessageDisplayChoice`에 대해 다음 값 중 하나를 반환하여 인앱 메시지 처리를 커스터마이즈할 수 있습니다: | `ABKInAppMessageDisplayChoice` | 동작 | | -------------------------- | -------- | | Objective-C: `ABKDisplayInAppMessageNow`
Swift: `displayInAppMessageNow` | 메시지가 즉시 표시됩니다. | | Objective-C: `ABKDisplayInAppMessageLater`
Swift: `displayInAppMessageLater` | 메시지가 표시되지 않고 스택의 맨 위로 다시 배치됩니다. | | Objective-C: `ABKDiscardInAppMessage`
Swift: `discardInAppMessage`| 메시지가 폐기되며 표시되지 않습니다. | {: .reset-td-br-1 .reset-td-br-2 aria-label="Custom handling in-app message display" } `beforeInAppMessageDisplayed:` 델리게이트 메서드를 사용하여 인앱 메시지 표시 로직을 추가하거나, Braze가 표시하기 전에 인앱 메시지를 커스터마이즈하거나, Braze 인앱 메시지 표시 로직 및 UI를 완전히 옵트아웃할 수 있습니다. 구현 예제는 [샘플 애플리케이션](https://github.com/Appboy/appboy-ios-sdk/blob/master/Samples/InAppMessage/BrazeInAppMessageSample/BrazeInAppMessageSample/AppDelegate.m)을 확인하세요. ## 표시 전에 인앱 메시지 재정의 {#overriding-in-app-messages-before-display} 인앱 메시지의 표시 동작을 변경하려면 필요한 표시 로직을 `beforeInAppMessageDisplayed:` 델리게이트 메서드에 추가해야 합니다. 예를 들어, 키보드가 현재 표시 중인 경우 화면 상단에서 인앱 메시지를 표시하거나, 인앱 메시지 데이터 모델을 가져와 직접 인앱 메시지를 표시할 수 있습니다. 세션이 시작될 때 인앱 메시지 캠페인이 표시되지 않는 경우, 필요한 표시 로직이 `beforeInAppMessageDisplayed:` 델리게이트 메서드에 추가되었는지 확인하세요. 이렇게 하면 키보드가 표시 중이더라도 화면 상단에서 인앱 메시지 캠페인을 표시할 수 있습니다. ## 다크 모드 비활성화 {#disabling-dark-mode} 사용자 기기에 다크 모드가 활성화되어 있을 때 인앱 메시지에 다크 모드 스타일이 적용되지 않도록 하려면 [`ABKInAppMessage.enableDarkTheme`](https://appboy.github.io/appboy-ios-sdk/docs/interface_a_b_k_in_app_message.html#ae89df6090bed623099ab0ecc0a74ad5d) 속성을 사용합니다. `ABKInAppMessageControllerDelegate.beforeInAppMessageDisplayed:` 또는 `ABKInAppMessageUIDelegate.beforeInAppMessageDisplayed:` 메서드 내에서 메서드의 `inAppMessage` 매개변수의 `enableDarkTheme` 속성을 `NO`로 설정합니다. ```objc // ABKInAppMessageControllerDelegate - (ABKInAppMessageDisplayChoice)beforeInAppMessageDisplayed:(ABKInAppMessage *)inAppMessage { ... inAppMessage.enableDarkTheme = NO; ... return ABKDisplayInAppMessageNow; } // ABKInAppMessageUIDelegate - (ABKInAppMessageDisplayChoice)beforeInAppMesssageDisplayed:(ABKInAppMessage *)inAppMessage withKeyboardIsUp:(BOOL)keyboardIsUp { ... inAppMessage.enableDarkTheme = NO; ... return ABKDisplayInAppMessageNow; } ``` ```swift // ABKInAppMessageControllerDelegate func before(inAppMessageDisplayed inAppMessage: ABKInAppMessage) -> ABKInAppMessageDisplayChoice { ... inAppMessage.enableDarkTheme = false ... return ABKInAppMessageDisplayChoice.displayInAppMessageNow } // ABKInAppMessageUIDelegate func before(inAppMessageDisplayed inAppMessage: ABKInAppMessage, withKeyboardIsUp keyboardIsUp: Bool) -> ABKInAppMessageDisplayChoice { ... inAppMessage.enableDarkTheme = false ... return ABKInAppMessageDisplayChoice.displayInAppMessageNow } ``` ## 표시 중 상태 표시줄 숨기기 {#hiding-the-status-bar-during-display} `Full` 및 `HTML` 인앱 메시지의 경우, SDK는 기본적으로 메시지를 상태 표시줄 위에 배치하려고 시도합니다. 그러나 일부 경우 상태 표시줄이 인앱 메시지 위에 여전히 나타날 수 있습니다. iOS SDK [3.21.1](https://github.com/Appboy/appboy-ios-sdk/blob/master/CHANGELOG.md#3211) 버전부터 `startWithApiKey:`에 전달된 `appboyOptions` 내에서 `ABKInAppMessageHideStatusBarKey`를 `YES`로 설정하여 `Full` 및 `HTML` 인앱 메시지를 표시할 때 상태 표시줄을 강제로 숨길 수 있습니다. ## 노출 횟수 및 클릭 수 기록 {#logging-impressions-and-clicks} 완전히 커스텀 처리를 구현하는 경우(예: `beforeInAppMessageDisplayed:`에서 `ABKDiscardInAppMessage`를 반환하여 Braze 인앱 메시지 표시를 우회하는 경우) 인앱 메시지 노출 횟수 및 클릭 수 기록은 자동으로 수행되지 않습니다. 인앱 메시지 모델을 사용하여 자체 UI를 구현하기로 선택한 경우, `ABKInAppMessage` 클래스에서 다음 메서드를 사용하여 분석을 기록해야 합니다: ```objc // Registers that a user has viewed an in-app message with the Braze server. - (void) logInAppMessageImpression; // Registers that a user has clicked on an in-app message with the Braze server. - (void) logInAppMessageClicked; ``` ```swift // Registers that a user has viewed an in-app message with the Braze server. func logInAppMessageImpression() // Registers that a user has clicked on an in-app message with the Braze server. func logInAppMessageClicked() ``` 또한 `ABKInAppMessageImmersive`의 서브클래스(즉, `Modal` 및 `Full` 인앱 메시지)에서 버튼 클릭을 기록해야 합니다: ```objc // Logs button click analytics - (void)logInAppMessageClickedWithButtonID:(NSInteger)buttonID; ``` ```swift // Logs button click analytics func logInAppMessageClickedWithButtonID(buttonId: NSInteger) ``` ## 메서드 선언 {#method-declarations} 추가 정보는 다음 헤더 파일을 참조하세요: - [`ABKInAppMessage.h`](https://github.com/Appboy/appboy-ios-sdk/blob/master/AppboyKit/include/ABKInAppMessage.h) - [`ABKInAppMessageControllerDelegate.h`](https://github.com/Appboy/appboy-ios-sdk/blob/master/AppboyKit/include/ABKInAppMessageControllerDelegate.h) ## 구현 샘플 {#implementation-samples} [`AppDelegate.m`](https://github.com/Appboy/appboy-ios-sdk/blob/master/Samples/InAppMessage/BrazeInAppMessageSample/BrazeInAppMessageSample/AppDelegate.m) 인앱 메시지 샘플 앱을 참조하세요. # iOS용 인앱 메시지 클릭 시 동작 사용자 지정 Source: /docs/ko/developer_guide/platforms/legacy_sdks/ios/in-app_messaging/customization/behavior_on_click/index.md
**Warning:** [AppboyKit](https://github.com/Appboy/appboy-ios-sdk) (also known as the Objective-C SDK) is no longer supported and has been replaced by the [Swift SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=swift). It will no longer receive new features, bug fixes, security updates, or technical support—however, messaging and analytics will continue to function as normal. To learn more, see [Introducing the New Braze Swift SDK](https://www.braze.com/resources/articles/introducing-the-new-braze-swift-sdk). # 클릭 시 인앱 메시지 동작 사용자 지정 {#customize-in-app-message-behavior-on-click} `ABKInAppMessage`의 `inAppMessageClickActionType` 속성정보는 인앱 메시지를 클릭한 후의 동작을 정의합니다. 이 속성은 읽기 전용입니다. 인앱 메시지의 클릭 동작을 변경하려면 `ABKInAppMessage`에서 다음 메서드를 호출할 수 있습니다. ```objc [inAppMessage setInAppMessageClickAction:clickActionType withURI:uri]; ``` ```swift inAppMessage.setInAppMessageClickAction(clickActionType: clickActionType, withURI: uri) ``` `inAppMessageClickActionType`은 다음 값 중 하나로 설정할 수 있습니다. | `ABKInAppMessageClickActionType` | 클릭 시 동작 | | -------------------------- | -------- | | `ABKInAppMessageRedirectToURI` | 메시지를 클릭하면 지정된 URI가 표시되고 메시지가 해제됩니다. `uri` 매개변수는 nil일 수 없습니다. | | `ABKInAppMessageNoneClickAction` | 클릭하면 메시지가 해제됩니다. `uri` 매개변수는 무시되고 `ABKInAppMessage`의 `uri` 속성정보는 nil로 설정됩니다. | {: .reset-td-br-1 .reset-td-br-2 aria-label="Customize in-app message behavior on click" } **Important:** 버튼이 포함된 인앱 메시지의 경우, 버튼 텍스트를 추가하기 전에 클릭 동작이 추가되면 `clickAction` 메시지도 최종 페이로드에 포함됩니다. ## 인앱 메시지 본문 클릭 사용자 지정 {#customizing-in-app-message-body-clicks} 다음 [`ABKInAppMessageUIDelegate`](https://github.com/Appboy/appboy-ios-sdk/blob/master/AppboyUI/ABKInAppMessage/ABKInAppMessageUIDelegate.h) 델리게이트 메서드는 인앱 메시지를 클릭할 때 호출됩니다. ```objc - (BOOL) onInAppMessageClicked:(ABKInAppMessage *)inAppMessage; ``` ```swift func onInAppMessageClicked(inAppMessage: ABKInAppMessage!) -> Bool ``` ## 인앱 메시지 버튼 클릭 사용자 지정 {#customizing-in-app-message-button-clicks} 인앱 메시지 버튼 및 HTML 인앱 메시지 버튼(예: 링크) 클릭의 경우, [`ABKInAppMessageUIDelegate`](https://github.com/Appboy/appboy-ios-sdk/blob/master/AppboyUI/ABKInAppMessage/ABKInAppMessageUIDelegate.h)에는 다음과 같은 델리게이트 메서드가 포함됩니다. ```objc - (BOOL)onInAppMessageButtonClicked:(ABKInAppMessageImmersive *)inAppMessage button:(ABKInAppMessageButton *)button; - (BOOL)onInAppMessageHTMLButtonClicked:(ABKInAppMessageHTML *)inAppMessage clickedURL:(nullable NSURL *)clickedURL buttonID:(NSString *)buttonID; ``` ```swift func onInAppMessageButtonClicked(inAppMessage: ABKInAppMessageImmersive!, button: ABKInAppMessageButton) -> Bool func onInAppMessageHTMLButtonClicked(inAppMessage: ABKInAppMessageHTML!, clickedURL: URL, buttonID: String) -> Bool ``` 각 메서드는 Braze에서 클릭 동작을 계속 실행할지 여부를 나타내는 `BOOL` 값을 반환합니다. 델리게이트 메서드에서 버튼의 클릭 동작 유형에 액세스하려면 다음 코드를 사용할 수 있습니다. ```objc if ([inAppMessage isKindOfClass:[ABKInAppMessageImmersive class]]) { ABKInAppMessageImmersive *immersiveIAM = (ABKInAppMessageImmersive *)inAppMessage; NSArray *buttons = immersiveIAM.buttons; for (ABKInAppMessageButton *button in buttons) { // Button action type is accessible via button.buttonClickActionType } } ``` ```swift if inAppMessage is ABKInAppMessageImmersive { let immersiveIAM = inAppMessage as! ABKInAppMessageImmersive; for button in inAppMessage.buttons as! [ABKInAppMessageButton]{ // Button action type is accessible via button.buttonClickActionType } } ``` 인앱 메시지에 버튼이 있는 경우, 실행되는 클릭 동작은 `ABKInAppMessageButton` 모델에 있는 것만 해당됩니다. `ABKInAppMessage` 모델에 기본 클릭 동작이 할당되어 있어도 인앱 메시지 본문은 클릭할 수 없습니다. ## 메서드 선언 {#method-declarations} 추가 정보는 다음 헤더 파일을 참조하세요. - [`ABKInAppMessage.h`](https://github.com/Appboy/appboy-ios-sdk/blob/master/AppboyKit/include/ABKInAppMessage.h) # iOS용 인앱 메시지 트리거 사용자 정의 Source: /docs/ko/developer_guide/platforms/legacy_sdks/ios/in-app_messaging/customization/custom_triggering/index.md
**Warning:** [AppboyKit](https://github.com/Appboy/appboy-ios-sdk) (also known as the Objective-C SDK) is no longer supported and has been replaced by the [Swift SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=swift). It will no longer receive new features, bug fixes, security updates, or technical support—however, messaging and analytics will continue to function as normal. To learn more, see [Introducing the New Braze Swift SDK](https://www.braze.com/resources/articles/introducing-the-new-braze-swift-sdk). # 커스텀 인앱 메시지 트리거 기본적으로, 인앱 메시지는 SDK에 의해 기록된 이벤트 유형에 의해 트리거됩니다. 서버에서 보낸 이벤트로 인앱 메시지를 트리거하려는 경우에도 이를 수행할 수 있습니다. 이 기능을 활성화하기 위해 무음 푸시가 기기로 전송되어 기기가 SDK 기반 이벤트를 기록할 수 있습니다. 이 SDK 이벤트는 이후에 사용자에게 표시되는 인앱 메시지를 트리거할 수 있습니다. ## 1단계: 무음 푸시 및 키-값 페어 처리 `application(_:didReceiveRemoteNotification:fetchCompletionHandler:)` 메소드에 다음 코드를 추가합니다: ```objc - (void)handleExtrasFromPush:(NSDictionary *)userInfo { NSLog(@"A push was received."); if (userInfo !=nil && userInfo[@"IS_SERVER_EVENT"] !=nil && userInfo[@"CAMPAIGN_NAME"]!=nil) { [[Appboy sharedInstance] logCustomEvent:@"IAM Trigger" withProperties:@{@"campaign_name": userInfo[@"CAMPAIGN_NAME"]}]; } }; ``` ```swift func handleExtras(userInfo: [AnyHashable : Any]) { NSLog("A push was received"); if userInfo != nil && (userInfo["IS_SERVER_EVENT"] as? String) != nil && (userInfo["CAMPAIGN_NAME"] as? String) != nil { Appboy.sharedInstance()?.logCustomEvent("IAM Trigger", withProperties: ["campaign_name": userInfo["CAMPAIGN_NAME"]]) } } ``` 푸시가 수신되면 SDK에서 기록한 이벤트 '인앱 메시지 트리거'가 고객 프로필에 기록됩니다. 이 인앱 메시지는 애플리케이션이 포그라운드에 있을 때 무음 푸시를 수신해야만 트리거됩니다. ## 2단계: 푸시 캠페인 만들기 서버 전송 이벤트를 통해 트리거되는 무음 푸시 캠페인을 만듭니다. 무음 푸시 캠페인을 생성하는 방법에 대한 자세한 내용은 [무음 푸시 알림](https://www.braze.com/docs/ko/ko/developer_guide/platforms/legacy_sdks/ios/push_notifications/silent_push_notifications/)을 참조하세요. ![사용자 정의 이벤트 "server_event".](https://www.braze.com/docs/ko/ko/assets/img_archive/iosServerSentPush.png?f2398c5efce1eef517dc7eabe0b5801b)를 수행하는 사용자에게 전달될 실행 기반 전달 인앱 메시지 캠페인입니다. 푸시 캠페인에는 이 푸시 캠페인이 SDK 커스텀 이벤트를 기록하기 위해 전송되었음을 나타내는 키-값 페어 추가 항목이 포함되어야 합니다. 이 이벤트는 인앱 메시지를 트리거하는 데 사용됩니다: ![두 개의 키-값 쌍이 있는 실행 기반 전달 인앱 메시지 캠페인입니다. "CAMPAIGN_NAME"는 "인앱 메시지 이름 예시"로 설정되고, "IS_SERVER_EVENT"은 "true"로 설정됩니다.](https://www.braze.com/docs/ko/ko/assets/img_archive/iOSServerPush.png?e84dc261f2b58bc43d35748e9c7db7f7) `application(_:didReceiveRemoteNotification:fetchCompletionHandler:)` 메서드 내 코드가 `IS_SERVER_EVENT` 키에 있는지 확인하고, 이 키가 존재하면 SDK 커스텀 이벤트를 기록합니다. 푸시 페이로드의 키-값 페어 추가 항목 내에서 원하는 값을 보내 이벤트 이름이나 이벤트 속성정보를 변경할 수 있습니다. 사용자 지정 이벤트를 로깅할 때 이러한 추가 정보는 이벤트 이름의 매개변수 또는 이벤트 속성으로 사용할 수 있습니다. ## 3단계: 인앱 메시지 캠페인 만들기 Braze 대시보드 내에서 사용자가 볼 수 있는 인앱 메시지 캠페인을 생성하세요. 이 캠페인에는 액션 기반 전달이 있어야 하며 `application(_:didReceiveRemoteNotification:fetchCompletionHandler:)` 메서드 내에서 로깅된 사용자 지정 이벤트에서 트리거되어야 합니다. 다음 예제에서는 초기 무음 푸시의 일부로 이벤트 속성정보를 전송하여 트리거할 특정 인앱 메시지를 구성합니다. ![사용자 정의 이벤트 "인앱 메시지 트리거"를 수행하는 사용자에게 전달될 실행 기반 전달 인앱 메시지 캠페인입니다. 여기서 "campaign_name"는 "인앱 메시지 이름 예시"와 같습니다.](https://www.braze.com/docs/ko/ko/assets/img_archive/iosIAMeventTrigger.png?2f425e73fa63c23e0270be6007c72cbe) 푸시 메시지는 SDK에서 기록한 커스텀 이벤트에 사용되기 때문에, Braze는 이 솔루션을 활성화하기 위해 각 사용자의 푸시 토큰을 저장해야 합니다. iOS 및 Android 모두에서 Braze는 사용자에게 OS의 푸시 프롬프트가 제공된 시점부터 토큰을 저장합니다. 이전에는 푸시를 사용하여 사용자에게 접근할 수 없으며, 이전 솔루션은 불가능합니다. # iOS용 커스텀 보기 컨트롤러의 인앱 메시지 Source: /docs/ko/developer_guide/platforms/legacy_sdks/ios/in-app_messaging/customization/custom_view_controller/index.md
**Warning:** [AppboyKit](https://github.com/Appboy/appboy-ios-sdk) (also known as the Objective-C SDK) is no longer supported and has been replaced by the [Swift SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=swift). It will no longer receive new features, bug fixes, security updates, or technical support—however, messaging and analytics will continue to function as normal. To learn more, see [Introducing the New Braze Swift SDK](https://www.braze.com/resources/articles/introducing-the-new-braze-swift-sdk). # 사용자 지정 보기 컨트롤러에 인앱 메시지 표시 인앱 메시지는 커스텀 보기 컨트롤러 내에 표시할 수도 있으며, 이를 Braze에 전달할 수도 있습니다. Braze는 사용자 지정된 인앱 메시지를 애니메이션으로 표시하거나 해제하고 인앱 메시지 분석을 처리합니다. 뷰 컨트롤러는 다음 요구 사항을 충족해야 합니다: - `ABKInAppMessageViewController` 의 서브클래스 또는 인스턴스여야 합니다. - 반환된 보기 컨트롤러의 보기는 `ABKInAppMessageView`의 인스턴스 또는 해당 서브클래스여야 합니다. 다음 UI 위임 메서드는 인앱 메시지를 `ABKInAppMessageViewController`로 제공할 때마다 호출되어 앱이 인앱 메시지 표시를 위해 커스텀 보기 컨트롤러를 Braze에 전달할 수 있도록 합니다. ```objc - (ABKInAppMessageViewController *)inAppMessageViewControllerWithInAppMessage:(ABKInAppMessage *)inAppMessage; ``` ```swift func inAppMessageViewControllerWithInAppMessage(inAppMessage: ABKInAppMessage!) -> ABKInAppMessageViewController! ``` [인앱 메시지 보기 컨트롤러는](https://github.com/Appboy/appboy-ios-sdk/tree/master/AppboyUI/ABKInAppMessage/ViewControllers) 사용자 지정할 수 있습니다. 하위 클래스 또는 카테고리를 사용하여 인앱 메시지의 표시 또는 동작을 사용자 지정할 수 있습니다. ## 메서드 선언 추가 정보는 다음 헤더 파일을 참조하십시오: - [`ABKInAppMessage.h`](https://github.com/Appboy/appboy-ios-sdk/blob/master/AppboyKit/include/ABKInAppMessage.h) ## 구현 샘플 인앱 메시지 샘플 앱에서 [`ViewController.m`](https://github.com/Appboy/appboy-ios-sdk/blob/master/Samples/InAppMessage/BrazeInAppMessageSample/BrazeInAppMessageSample/ViewController.m) 및 [`CustomInAppMessageViewController.m`](https://github.com/Appboy/appboy-ios-sdk/blob/master/Samples/InAppMessage/BrazeInAppMessageSample/BrazeInAppMessageSample/)을 참조하세요. # iOS용 인앱 메시지 모달 해제 Source: /docs/ko/developer_guide/platforms/legacy_sdks/ios/in-app_messaging/customization/modal_dismissal/index.md
**Warning:** [AppboyKit](https://github.com/Appboy/appboy-ios-sdk) (also known as the Objective-C SDK) is no longer supported and has been replaced by the [Swift SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=swift). It will no longer receive new features, bug fixes, security updates, or technical support—however, messaging and analytics will continue to function as normal. To learn more, see [Introducing the New Braze Swift SDK](https://www.braze.com/resources/articles/introducing-the-new-braze-swift-sdk). # 바깥쪽 탭으로 모달 닫기 {#dismiss-modal-on-outside-tap} 기본값은 `NO`입니다. 사용자가 인앱 메시지 외부를 탭할 때 모달 인앱 메시지를 해제할지 여부를 결정합니다. 외부 탭 해제를 활성화하려면 `Info.plist` 파일에 `Braze`라는 사전을 추가합니다. 다음 코드 스니펫과 같이 `Braze` 사전 내에 `DismissModalOnOutsideTap` 부울 하위 항목을 추가하고 값을 `YES`로 설정합니다. Braze iOS SDK v4.0.2 이전 버전에서는 `Braze` 대신 `Appboy` 사전 키를 사용해야 합니다. ``` Braze DismissModalOnOutsideTap YES ``` 런타임에 `appboyOptions`에서 `ABKEnableDismissModalOnOutsideTapKey`를 `YES`로 설정하여 이 기능을 활성화할 수도 있습니다. | `DismissModalOnOutsideTap` | 설명 | |----------|-------------| | `YES` | 외부를 탭하면 모달 인앱 메시지가 해제됩니다. | | `NO` | 기본값으로, 외부를 탭해도 모달 인앱 메시지는 해제되지 않습니다. | {: .reset-td-br-1 .reset-td-br-2 aria-label="Dismiss modal on outside tap" } # iOS용 인앱 메시지 키-값 쌍 Source: /docs/ko/developer_guide/platforms/legacy_sdks/ios/in-app_messaging/customization/key_value_pairs/index.md
**Warning:** [AppboyKit](https://github.com/Appboy/appboy-ios-sdk) (also known as the Objective-C SDK) is no longer supported and has been replaced by the [Swift SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=swift). It will no longer receive new features, bug fixes, security updates, or technical support—however, messaging and analytics will continue to function as normal. To learn more, see [Introducing the New Braze Swift SDK](https://www.braze.com/resources/articles/introducing-the-new-braze-swift-sdk). # 키-값 쌍 추가 정보 `ABKInAppMessage` 오브젝트는 키-값 페어를 `extras`로 전달할 수 있습니다. 캠페인 생성 시 대시보드에서 지정할 수 있습니다. 키-값 페어를 사용하여 앱에서 추가 처리를 위해 인앱 메시지와 함께 데이터를 전송할 수 있습니다. # iOS용 인앱 메시지 전달 Source: /docs/ko/developer_guide/platforms/legacy_sdks/ios/in-app_messaging/in-app_message_delivery/index.md
**Warning:** [AppboyKit](https://github.com/Appboy/appboy-ios-sdk) (also known as the Objective-C SDK) is no longer supported and has been replaced by the [Swift SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=swift). It will no longer receive new features, bug fixes, security updates, or technical support—however, messaging and analytics will continue to function as normal. To learn more, see [Introducing the New Braze Swift SDK](https://www.braze.com/resources/articles/introducing-the-new-braze-swift-sdk). # 인앱 메시지 전달 ## 트리거 유형 인앱 메시지 제품을 사용하면 여러 가지 이벤트 유형(`Any Purchase`, `Specific Purchase`, `Session Start`, `Custom Event`, `Push Click`)의 결과로 인앱 메시지 표시를 트리거할 수 있습니다. 게다가, `Specific Purchase` 및 `Custom Event` 트리거에는 강력한 속성정보 필터가 포함되어 있습니다. **Note:** 트리거된 인앱 메시지는 Braze SDK를 통해 기록된 커스텀 이벤트에서만 작동합니다. 인앱 메시지는 API 또는 API 이벤트(예: 구매 이벤트)를 통해 트리거할 수 없습니다. iOS를 사용하는 경우 자세한 내용은 [커스텀 이벤트 추적](https://www.braze.com/docs/ko/ko/developer_guide/analytics/logging_events/?tab=swift) 문서를 참조하세요. ## 전달 의미 체계 사용자가 받을 수 있는 모든 인앱 메시지는 세션 시작 시 사용자의 기기로 전달됩니다. 하나의 이벤트에 의해 두 개의 인앱 메시지가 트리거되는 경우 우선순위가 더 높은 인앱 메시지가 표시됩니다. SDK의 세션 시작 의미 체계에 대한 자세한 내용은 [세션 생애주기](https://www.braze.com/docs/ko/ko/developer_guide/platform_integration_guides/ios/analytics/tracking_sessions/#session-lifecycle)를 참조하세요. 제공되면 SDK가 트리거 시점에 즉시 사용할 수 있도록 자산을 미리 가져와 표시 지연 시간을 최소화합니다. 트리거 이벤트에 적격한 인앱 메시지가 두 개 이상 연결된 경우 우선순위가 가장 높은 인앱 메시지만 전달됩니다. 전달 즉시 표시되는 인앱 메시지(세션 시작, 푸시 클릭)의 경우 자산을 미리 가져오지 않아 약간의 지연 시간이 발생할 수 있습니다. ## 트리거 사이의 최소 시간 간격 기본적으로 양질의 사용자 경험을 제공하기 위해 인앱 메시지 전송을 30초에 한 번으로 제한합니다. `startWithApiKey:inApplication:withLaunchOptions:withAppboyOptions:`에 전달된 `appboyOptions` 매개변수 내 `ABKMinimumTriggerTimeIntervalKey`를 통해 이 값을 재정의할 수 있습니다. `ABKMinimumTriggerTimeIntervalKey`을 인앱 메시지 간 최소 시간(초)에 대해 원하는 정수 값으로 설정합니다. ```objc // Sets the minimum trigger time interval to 5 seconds [Appboy startWithApiKey:@"YOUR-API-KEY" inApplication:application withLaunchOptions:options withAppboyOptions:@{ ABKMinimumTriggerTimeIntervalKey : @(5) }]; ``` ```swift Appboy.start(withApiKey: "YOUR-API-KEY", in:application, withLaunchOptions:launchOptions, withAppboyOptions:[ABKMinimumTriggerTimeIntervalKey : 5]) ``` ## 일치하는 트리거를 찾지 못함 특정 이벤트에 대해 일치하는 트리거를 찾지 못하면 Braze는 [`ABKInAppMessageControllerDelegate`](https://appboy.github.io/appboy-ios-sdk/docs/protocol_a_b_k_in_app_message_controller_delegate-p.html)의 [noMatchingTriggerForEvent:name:](https://appboy.github.io/appboy-ios-sdk/docs/protocol_a_b_k_in_app_message_controller_delegate-p.html#ab4d57b13c51545d487227945a37d4ab8) 메서드를 호출합니다. 이 시나리오를 처리하기 위해 델리게이트 프로토콜을 채택한 클래스에서 이 메서드를 구현하세요. ## 로컬 인앱 메시지 전달 ### 인앱 메시지 스택 #### 인앱 메시지 표시 사용자에게 인앱 메시지를 수신할 수 있는 자격이 있으면 인앱 메시지 스택에서 최신 인앱 메시지를 `ABKInAppMessageController`에 전송합니다. 스택은 메모리에 저장된 인앱 메시지만 유지하며 일시 중단 모드에서 앱을 실행할 때마다 지워집니다. **Important:** 이 상황에서는 렌더링이 정의되지 않으므로 키보드가 화면에 표시될 때 인앱 메시지를 표시하지 않습니다. #### 스택에 인앱 메시지 추가하기 사용자는 다음 상황에서 인앱 메시지를 받을 자격이 있습니다: - 인앱 메시지 트리거 이벤트가 발생합니다. - 세션 시작 이벤트 - 푸시 알림에서 앱이 열립니다. 트리거된 인앱 메시지는 트리거 이벤트가 실행될 때 스택에 배치됩니다. 여러 개의 인앱 메시지가 스택에 있고 표시 대기 중인 경우, Braze는 최근에 수신한 인앱 메시지를 먼저 표시합니다(후입후출). #### 인앱 메시지를 스택으로 반환하기 트리거된 인앱 메시지는 다음 상황에서 스택으로 반환될 수 있습니다: - 인앱 메시지는 앱이 백그라운드에 있을 때 트리거됩니다. - 현재 다른 인앱 메시지가 표시되고 있습니다. - 더 이상 사용되지 않는 `beforeInAppMessageDisplayed:withKeyboardIsUp:` [UI 델리게이트 메서드는](https://www.braze.com/docs/ko/ko/developer_guide/platform_integration_guides/ios/in-app_messaging/customization/setting_delegates/#in-app-message-delegate) 구현되지 않았으며 현재 키보드가 표시되고 있습니다. - `beforeInAppMessageDisplayed:` [위임 메서드](https://www.braze.com/docs/ko/ko/developer_guide/platform_integration_guides/ios/in-app_messaging/customization/setting_delegates/#core-in-app-message-delegate) 또는 더 이상 사용되지 않는 `beforeInAppMessageDisplayed:withKeyboardIsUp:` [UI 위임 메서드](https://www.braze.com/docs/ko/ko/developer_guide/platform_integration_guides/ios/in-app_messaging/customization/setting_delegates/#in-app-message-delegate)에서 `ABKDisplayInAppMessageLater`를 반환합니다. #### 인앱 메시지 삭제하기 다음과 같은 상황에서는 트리거된 인앱 메시지가 삭제됩니다: - `beforeInAppMessageDisplayed:` [위임 메서드](https://www.braze.com/docs/ko/ko/developer_guide/platform_integration_guides/ios/in-app_messaging/customization/setting_delegates/#core-in-app-message-delegate) 또는 더 이상 사용되지 않는 `beforeInAppMessageDisplayed:withKeyboardIsUp:` [UI 위임 메서드](https://www.braze.com/docs/ko/ko/developer_guide/platform_integration_guides/ios/in-app_messaging/customization/setting_delegates/#in-app-message-delegate)에서 `ABKDiscardInAppMessage`를 반환합니다. - 인앱 메시지의 에셋(이미지 또는 ZIP 파일)을 다운로드하지 못했습니다. - 인앱 메시지가 표시될 준비가 되었지만 제한 시간이 초과되었습니다. - 디바이스 방향이 트리거된 인앱 메시지의 방향과 일치하지 않습니다. - 인앱 메시지는 전체 인앱 메시지이지만 이미지가 없습니다. - 인앱 메시지는 이미지가 없는 이미지 전용 Modal 인앱 메시지입니다. #### 수동으로 인앱 메시지 표시를 대기열에 추가 앱 내에서 다른 시간에 인앱 메시지를 표시하려면 다음 메서드를 호출하여 스택에서 맨 위에 있는 인앱 메시지를 수동으로 표시할 수 있습니다. ```objc [[Appboy sharedInstance].inAppMessageController displayNextInAppMessage]; ``` ```swift Appboy.sharedInstance()!.inAppMessageController.displayNextInAppMessage() ``` ### 실시간 인앱 메시지 생성 및 표시 인앱 메시지는 앱 내에서 로컬로 생성하여 Braze를 통해 표시할 수도 있습니다. 이 기능은 앱 내에서 트리거하려는 메시지를 실시간으로 표시할 때 특히 유용합니다. Braze는 로컬에서 생성된 인앱 메시지에 대한 분석을 지원하지 않습니다. ```objc ABKInAppMessageSlideup *customInAppMessage = [[ABKInAppMessageSlideup alloc] init]; customInAppMessage.message = @"YOUR_CUSTOM_SLIDEUP_MESSAGE"; customInAppMessage.duration = 2.5; customInAppMessage.extras = @{@"key" : @"value"}; [[Appboy sharedInstance].inAppMessageController addInAppMessage:customInAppMessage]; ``` ```swift let customInAppMessage = ABKInAppMessageSlideup.init() customInAppMessage.message = "YOUR_CUSTOM_SLIDEUP_MESSAGE" customInAppMessage.duration = 2.5 customInAppMessage.extras = ["key": "value"] Appboy.sharedInstance()!.inAppMessageController.add(customInAppMessage) ``` # 커스텀 App Store 리뷰 프롬프트 Source: /docs/ko/developer_guide/platforms/legacy_sdks/ios/in-app_messaging/custom_app_store_review_prompt/index.md
**Warning:** [AppboyKit](https://github.com/Appboy/appboy-ios-sdk) (also known as the Objective-C SDK) is no longer supported and has been replaced by the [Swift SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=swift). It will no longer receive new features, bug fixes, security updates, or technical support—however, messaging and analytics will continue to function as normal. To learn more, see [Introducing the New Braze Swift SDK](https://www.braze.com/resources/articles/introducing-the-new-braze-swift-sdk). # 커스텀 App Store 리뷰 프롬프트 {#custom-app-store-review-prompt} **Note:** 이 프롬프트를 구현하면 Braze는 자동으로 노출 횟수 추적을 중지하며, 직접 [분석](https://www.braze.com/docs/ko/ko/developer_guide/platform_integration_guides/ios/in-app_messaging/customization/handing_in_app_display/#logging-impressions-and-clicks)을 기록해야 합니다. 사용자에게 App Store 리뷰를 요청하는 Campaign을 생성하는 것은 인앱 메시지의 인기 있는 활용 방법입니다. 먼저 앱에서 [인앱 메시지 델리게이트](#in-app-message-controller-delegate)를 설정합니다. 그런 다음, 기본 App Store 리뷰 메시지를 비활성화하기 위해 다음 델리게이트 메서드를 구현합니다. ```objc - (ABKInAppMessageDisplayChoice)beforeInAppMessageDisplayed:(ABKInAppMessage *)inAppMessage { if (inAppMessage.extras != nil && inAppMessage.extras[@"Appstore Review"] != nil) { [[UIApplication sharedApplication] openURL:inAppMessage.uri options:@{} completionHandler:nil]; return ABKDiscardInAppMessage; } else { return ABKDisplayInAppMessageNow; } } ``` ```swift func before(inAppMessageDisplayed inAppMessage: ABKInAppMessage) -> ABKInAppMessageDisplayChoice { if inAppMessage.extras?["Appstore Review"] != nil && inAppMessage.uri != nil { UIApplication.shared.open(inAppMessage.uri!, options: [:], completionHandler: nil) return ABKInAppMessageDisplayChoice.discardInAppMessage } else { return ABKInAppMessageDisplayChoice.displayInAppMessageNow } } ``` 딥링크 처리 코드에서 `{YOUR-APP-SCHEME}:appstore-review` 딥링크를 처리하기 위해 다음 코드를 추가합니다. `SKStoreReviewController`를 사용하려면 `StoreKit`를 가져와야 합니다. ```objc - (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary *)options { NSString *urlString = url.absoluteString.stringByRemovingPercentEncoding; if ([urlString isEqualToString:@"{YOUR-APP-SCHEME}:appstore-review"]) { [SKStoreReviewController requestReview]; return YES; } // Other deep link handling code… } ``` ```swift func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool { let urlString = url.absoluteString.removingPercentEncoding if (urlString == "{YOUR-APP-SCHEME}:appstore-review") { SKStoreReviewController.requestReview() return true; } // Other deep link handling code… } ``` 다음으로 아래 내용을 포함하여 인앱 메시징 Campaign을 생성합니다. - 키-값 페어 `"Appstore Review" : "true"` - 클릭 시 동작을 "앱으로 딥링크"로 설정하고, 딥링크 `{YOUR-APP-SCHEME}:appstore-review`를 사용합니다. **Tip:** Apple은 사용자당 연간 최대 3회로 App Store 리뷰 프롬프트를 제한하므로, Campaign도 사용자당 연간 3회로 [빈도 제한](https://www.braze.com/docs/ko/ko/user_guide/messaging/messaging_fundamentals/frequency_capping/)을 설정해야 합니다.

사용자는 App Store 리뷰 프롬프트를 끌 수 있습니다. 따라서 커스텀 리뷰 프롬프트는 네이티브 App Store 리뷰 프롬프트가 표시될 것이라고 약속하거나 직접적으로 리뷰를 요청해서는 안 됩니다. # iOS용 인앱 메시지 구현 가이드(선택 사항) Source: /docs/ko/developer_guide/platforms/legacy_sdks/ios/in-app_messaging/implementation_guide/index.md
**Warning:** [AppboyKit](https://github.com/Appboy/appboy-ios-sdk) (also known as the Objective-C SDK) is no longer supported and has been replaced by the [Swift SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=swift). It will no longer receive new features, bug fixes, security updates, or technical support—however, messaging and analytics will continue to function as normal. To learn more, see [Introducing the New Braze Swift SDK](https://www.braze.com/resources/articles/introducing-the-new-braze-swift-sdk).
**Important:** 기본 인앱 메시지 개발자 통합 가이드를 찾고 계신가요? 찾기 [here](https://www.braze.com/docs/ko/ko/developer_guide/platforms/legacy_sdks/ios/in-app_messaging/overview/). # 인앱 메시징 구현 가이드 > 이 고급 구현 가이드(선택 사항)에서는 인앱 메시지 코드 고려사항, 저희 팀이 구축한 세 가지 커스텀 사용 사례 및 관련 코드 스니펫을 다룹니다. Braze Demo 저장소 [여기](https://github.com/braze-inc/braze-growth-shares-ios-demo-app)를 방문하세요! 이 구현 가이드는 Swift 구현을 중심으로 하지만 관심 있는 사람을 위해 Objective-C 스니펫도 제공됩니다. HTML 구현을 찾고 계신가요? [HTML 템플릿 리포지토리를](https://github.com/braze-inc/in-app-message-templates) 살펴보세요! ## 코드 고려 사항 다음 가이드에서는 기본 인앱 메시지 외에 사용할 수 있는 선택적 커스텀 개발자 통합도 제공합니다. 커스텀 보기 컨트롤러는 각 사용 사례에 포함되어 기능을 확장하고 인앱 메시지의 모양과 느낌을 기본적으로 사용자 지정하는 예제를 제공합니다. ### ABKInAppMessage 하위 클래스 다음 코드 스니펫은 인앱 메시지를 채우려는 서브클래스 보기를 결정하는 Braze SDK의 UI 위임 메서드입니다. 이 가이드에서는 기본 구현을 다루고 전체, 슬라이드업 및 Modal 서브클래스를 매력적인 방식으로 구현하는 방법을 보여줍니다. 커스텀 보기 컨트롤러를 설정하려면 모든 다른 인앱 메시지 서브클래스를 설정해야 합니다. 서브클래싱의 개념에 대해 확실히 이해한 후, [사용 사례](#sample-use-cases)를 확인하여 인앱 메시징 서브클래스를 구현하기 시작합니다. **ABKInAppMessage 하위 클래스**
```swift extension AppboyManager: ABKInAppMessageUIDelegate { func inAppMessageViewControllerWith(_ inAppMessage: ABKInAppMessage) -> ABKInAppMessageViewController { switch inAppMessage { case is ABKInAppMessageSlideup: return slideupViewController(inAppMessage: inAppMessage) //Custom Method case is ABKInAppMessageModal: return modalViewController(inAppMessage: inAppMessage) //Custom Method case is ABKInAppMessageFull: return fullViewController(inAppMessage: inAppMessage) //Custom Method case is ABKInAppMessageHTML: return ABKInAppMessageHTMLViewController(inAppMessage: inAppMessage) default: return ABKInAppMessageViewController(inAppMessage: inAppMessage) } } } ``` **ABKInAppMessage 하위 클래스**
```objc - (ABKInAppMessageViewController *)inAppMessageViewControllerWithInAppMessage:(ABKInAppMessage *)inAppMessage { if ([inAppMessage isKindOfClass:[ABKInAppMessageSlideup class]]) { return [self slideupViewControllerWithInAppMessage:inAppMessage]; //Custom Method } else if ([inAppMessage isKindOfClass:[ABKInAppMessageModal class]]) { return [self modalViewControllerWithInAppMessage:inAppMessage]; //Custom Method } else if ([inAppMessage isKindOfClass:[ABKInAppMessageFull class]]) { return [self fullViewControllerWithInAppMessage:inAppMessage]; //Custom Method } else if ([inAppMessage isKindOfClass:[ABKInAppMessageHTML class]]) { return [[ABKInAppMessageHTMLViewController alloc] initWithInAppMessage:inAppMessage]; } else { return [[ABKInAppMessageViewController alloc] initWithInAppMessage:inAppMessage]; } } ``` ## 사용 사례 우리는 아래에 세 가지 사용 사례를 제공했습니다. 각 사용 사례는 자세한 설명, 관련 코드 스니펫 및 Braze 대시보드에서 인앱 메시지의 모양과 느낌에 대한 인상을 제공합니다. - [커스텀 슬라이드업 인앱 메시지](#custom-slide-up-in-app-message) - [커스텀 모달 인앱 메시지](#custom-modal-in-app-message) - [커스텀 전체 인앱 메시지](#custom-full-in-app-message) ### 커스텀 슬라이드업 인앱 메시지 ![나란히 놓인 두 개의 iPhone. 첫 번째 iPhone은 전화기 화면 하단을 터치하여 슬라이드업 메시지를 표시합니다. 두 번째 iPhone에서는 슬라이드업 메시지가 화면에서 더 높이 있어서 표시된 앱 탐색 버튼을 볼 수 있습니다.](https://www.braze.com/docs/ko/ko/assets/img/iam_implementation/slideup.png?9e02cf3a30829eac685b141146429fdb){: style="float:right;max-width:45%;margin-left:15px;border:0;"} 슬라이드업 인앱 메시지를 작성하는 동안 기본 방법을 사용하여 메시지의 위치를 수정할 수 없다는 점을 알아차렸습니다. 메시지 위치를 수정하려면 `ABKInAppMessageSlideupViewController`를 서브클래스로 만들고 `offset` 변수를 자체 커스텀 변수로 재정의해야 합니다. 오른쪽의 이미지는 슬라이드업 인앱 메시지를 조정하는 방법의 예를 보여줍니다. [`SlideFromBottomViewController`](https://github.com/braze-inc/braze-growth-shares-ios-demo-app/blob/master/Braze-Demo/ViewController/In-App-Messages/SlideFromBottomViewController.swift)에 방문하여 시작하십시오. #### 기본 UI에 추가 동작 추가

**`offset` 변수 업데이트**
`offset` 변수를 업데이트하고 자체 오프셋을 필요에 맞게 설정합니다. ```swift func setSlideConstraint() { offset = 0 } ``` ```swift override var offset: CGFloat { get { return super.offset } set { super.offset = newValue + adjustedOffset } } ``` **Version 3.34.0 or earlier** **`slideConstraint` 변수 업데이트**
`slideConstraint` 공용 변수는 슈퍼클래스 `ABKInAppMessageSlideupViewController`에서 나옵니다. ```swift func setSlideConstraint() { slideConstraint?.constant = bottomSpacing } ``` ```swift private var bottomSpacing: CGFloat { return AppboyManager.shared.activeApplicationViewController.topMostViewController().view.safeAreaInsets.bottom } ``` Braze 데모 저장소를 방문하여 [`topMostViewController()`](https://github.com/braze-inc/braze-growth-shares-ios-demo-app/blob/master/Braze-Demo/Utils/UIViewController_Util.swift#L17) 기능을 확인하세요. **`offset` 변수 업데이트**
`offset` 변수를 업데이트하고 자체 오프셋을 필요에 맞게 설정합니다. ```objc - (void)setOffset { self.offset = 0; } ``` ```objc - (CGFloat)offset { return [super offset]; } - (void)setOffset:(CGFloat)offset { [super setOffset:offset + [self adjustedOffset]]; } ``` **Version 3.34.0 or earlier** **`slideConstraint` 변수 업데이트**
`slideConstraint` 공용 변수는 슈퍼클래스 `ABKInAppMessageSlideupViewController`에서 나옵니다. ```objc - (void)self.setSlideConstraint:(NSLayoutConstraint *)slideConstraint { slideConstraint.constant = bottomSpacing; } ``` ```objc - (CGFloat)bottomSpacing { return [AppboyManager shared].activeApplicationViewController.topMostViewController.view.safeAreaInsets.bottom; } ``` **재정의 및 커스텀 제약 조건 설정**
`beforeMoveInAppMessageViewOnScreen()`을 재정의하고 필요에 맞게 커스텀 제약 조건 값을 설정합니다. 원래 값은 슈퍼클래스에 설정됩니다. ```swift override func beforeMoveInAppMessageViewOnScreen() { super.beforeMoveInAppMessageViewOnScreen() setOffset() } ``` **Version 3.34.0 or earlier** ```swift override func beforeMoveInAppMessageViewOnScreen() { setSlideConstraint() } ``` **재정의 및 커스텀 제약 조건 설정**
`beforeMoveInAppMessageViewOnScreen()`을 재정의하고 필요에 맞게 커스텀 제약 조건 값을 설정합니다. 원래 값은 슈퍼클래스에 설정됩니다. ```objc - (void)beforeMoveInAppMessageViewOnScreen { [super beforeMoveInAppMessageViewOnScreen]; [self setOffset]; } ``` **Version 3.34.0 or earlier** ```objc - (void)beforeMoveInAppMessageViewOnScreen { [self setSlideConstraint:self.slideConstraint]; } ``` **기기 방향에 대한 제약 조건 조정**
`viewWillTransition()`에서 각각의 값을 조정합니다. 서브클래스는 레이아웃 변경 중 제약 조건을 동기화해야 한다고 가정하기 때문입니다. ### 커스텀 모달 인앱 메시지 ![Modal 인앱 메시지를 표시하는 iPhone으로, 여기서 스포츠 팀 목록을 살펴보고 좋아하는 팀을 선택할 수 있습니다. 이 인앱 메시지의 하단에는 큰 파란색 제출 버튼이 있습니다.](https://www.braze.com/docs/ko/ko/assets/img/iam_implementation/modal.png?519e4b01528bc44cbbe0e83274f32c41){: style="float:right;max-width:23%;margin-left:15px;border:0;"} 소중한 사용자 속성을 수집하는 매력적인 방법을 제공하기 위해 `ABKInAppMessageModalViewController`는 `UIPickerView`를 활용하여 서브클래스로 만들 수 있습니다. 커스텀 Modal 인앱 메시지를 사용하면 연결된 콘텐츠 또는 사용 가능한 목록을 통해 항목의 동적 목록에서 속성을 표시하고 캡처할 수 있습니다. 자체 보기를 서브클래스로 만든 인앱 메시지에 삽입할 수 있습니다. 이 예제에서는 `UIPickerView`를 사용하여 `ABKModalInAppMessageViewController`의 기능을 확장하는 방법을 보여줍니다. [ModalPickerViewController](https://github.com/braze-inc/braze-growth-shares-ios-demo-app/blob/master/Braze-Demo/ViewController/In-App-Messages/ModalPickerViewController/ModalPickerViewController.swift)에 방문하여 시작하십시오. #### 대시보드 구성 대시보드에서 모달 인앱 메시지를 설정하려면 쉼표로 구분된 문자열로 형식화된 항목 목록을 제공해야 합니다. 이 예제에는, 연결된 콘텐츠를 사용하여 팀 이름의 JSON 목록을 가져오고 적절하게 형식을 지정합니다. ![인앱 메시지 작성기는 인앱 메시지의 모양에 대한 미리보기를 보여주지만 대신 Braze에 제공한 항목 목록을 표시합니다. Braze UI는 커스텀 인앱 메시지 UI를 휴대폰으로 전송하지 않으면 표시하지 않으므로, 미리보기는 메시지의 실제 모양을 나타내지 않습니다. 따라서 전송하기 전에 테스트하는 것을 좋습니다.](https://www.braze.com/docs/ko/ko/assets/img/iam_implementation/dashboard1.png?7c867625250df852a14d3f201138e219) 키-값 페어에서 `attribute_key`를 제공합니다. 이 키는 사용자가 선택한 값과 함께 고객 프로필에 커스텀 속성으로 저장됩니다. 귀하의 커스텀 보기 로직은 Braze로 전송된 사용자 속성을 처리해야 합니다. `ABKInAppMessage` 오브젝트의 `extras` 사전을 통해 올바른 보기를 표시하도록 알리는 `view_type` 키(있는 경우)를 쿼리할 수 있습니다. 인앱 메시지는 메시지별로 구성되므로 커스텀 및 기본값 Modal 보기가 조화를 이룰 수 있습니다. ![메시지 작성기에 두 개의 키-값 페어가 있습니다. 첫 번째 키-값 페어는 "attribute_key"를 "즐겨찾는 팀"으로 설정하고, 두 번째는 "view_type"을 "픽커"로 설정합니다.](https://www.braze.com/docs/ko/ko/assets/img/iam_implementation/dashboard2.png?f6f9998e902ef22d05a1c3571c0872f7){: style="max-width:65%;"} **UI 표시 동작에 `view_type` 사용**
원하는 서브클래스 보기 컨트롤러를 로드하도록 `view_type`에 대해 `extras` 사전을 쿼리합니다. ```swift func modalViewController(inAppMessage: ABKInAppMessage) -> ABKInAppMessageModalViewController { switch inAppMessage.extras?[InAppMessageKey.viewType.rawValue] as? String { case InAppMessageViewType.picker.rawValue: return ModalPickerViewController(inAppMessage: inAppMessage) default: return ABKInAppMessageModalViewController(inAppMessage: inAppMessage) } } ``` **UI 표시 동작에 `view_type` 사용**
원하는 서브클래스 보기 컨트롤러를 로드하도록 `view_type`에 대해 `extras` 사전을 쿼리합니다. ```objc - (ABKInAppMessageModalViewController *)modalViewControllerWithInAppMessage:(ABKInAppMessage *)inAppMessage { InAppMessageData *inAppMessageData = [[InAppMessageData alloc] init]; NSString *key = [inAppMessageData rawValueForInAppMessageKey:InAppMessageKeyViewType]; NSString *viewType = [inAppMessageData rawValueForInAppMessageViewType:InAppMessageViewTypePicker]; if ([inAppMessage.extras objectForKey:key] && [inAppMessage.extras[key] isEqualToString:viewType]) { return [[ModalViewController alloc] initWithInAppMessage:inAppMessage]; } else { return [[ABKInAppMessageModalViewController alloc] initWithInAppMessage:inAppMessage]; } } ``` **사용자 지정 보기 재정의 및 제공**
`loadView()`를 재정의하고 필요에 맞게 커스텀 제약 조건 값을 설정합니다. ```swift override var nibname: String{ return "ModalPickerViewController" } override func loadView() { Bundle.main.loadNibNamed(nibName, owner: self, options: nil) } ``` **사용자 지정 보기 재정의 및 제공**
`loadView()`를 재정의하고 필요에 맞게 커스텀 제약 조건 값을 설정합니다. ```objc - (void)loadView { NSString *nibName = @"ModalPickerViewController"; [[NSBundle mainBundle] loadNibNamed:nibName owner:self options:nil]; } ``` **동적 목록을 위한 변수 형식**
`UIPickerView` 구성 요소를 다시 로드하기 전에 `inAppMessage` 메시지 변수가 _문자열_로 출력됩니다. 이 메시지는 올바르게 표시되도록 항목 배열로 형식화되어야 합니다. 예를 들어, 이것은 [`components(separatedBy: ", ")`](https://developer.apple.com/documentation/foundation/nsstring/1413214-components)을 사용하여 달성할 수 있습니다. ```swift override func viewDidLoad() { super.viewDidLoad() items = inAppMessage.message.separatedByCommaSpaceValue pickerView.reloadAllComponents() } ``` **PickerView에 대한 변수 형식 지정**
`UIPickerView` 구성 요소를 다시 로드하기 전에 `inAppMessage` 메시지 변수가 _문자열_로 출력됩니다. 이 메시지는 올바르게 표시되도록 항목 배열로 형식화되어야 합니다. 예를 들어, 이것은 [`componentsSeparatedByString`](https://developer.apple.com/documentation/foundation/nsstring/1413214-componentsseparatedbystring?language=objc)을 사용하여 달성할 수 있습니다. ```objc - (void)viewDidLoad { [super viewDidLoad]; self.items = [[NSArray alloc] initWithArray:[self.inAppMessage.message componentsSeparatedByString:@", "]]; [self.pickerView reloadAllComponents]; } ``` **커스텀 속성 할당**
서브클래스를 사용하여 사용자가 제출을 누른 후 속성과 이에 대응하는 선택된 값을 Braze에 전달합니다. ```swift @IBAction func primaryButtonTapped(_ sender: Any) { guard let item = selectedItem, !item.isEmpty, let attributeKey = inAppMessage.extras?[InAppMessageKey.attributeKey.rawValue] as? String else { return } AppboyManager.shared.setCustomAttributeWithKey(attributeKey, andStringValue: item) } ``` **커스텀 속성 할당**
서브클래스를 사용하여 사용자가 제출을 누른 후 속성과 이에 대응하는 선택된 값을 Braze에 전달합니다. ```objc - (IBAction)primaryButtonTapped:(id)sender { InAppMessageData *inAppMessageData = [[InAppMessageData alloc] init]; NSString *key = [inAppMessageData rawValueForInAppMessageKey:InAppMessageKeyAttributeKey]; if (self.selectedItem.length > 0 && [self.inAppMessage.extras objectForKey:key]) { [[AppboyManager shared] setCustomAttributeWithKey:self.inAppMessage.extras[key] andStringValue:self.selectedItem]; } } ``` **Tip:** FaceTime을 통해 비디오를 공유하기 위해 커스텀 Modal 인앱 메시지를 활용하는 데 관심이 있으신가요? SharePlay 인앱 메시지 [구현 가이드](https://www.braze.com/docs/ko/ko/developer_guide/platforms/legacy_sdks/ios/in-app_messaging/implementation_guide/shareplay/)를 확인하세요. ### 커스텀 전체 인앱 메시지 ![구성 옵션 목록을 표시하고 각 옵션 옆에 토글이 있는 인앱 메시지. 메시지 하단에 큰 파란색 제출 버튼이 있습니다.](https://www.braze.com/docs/ko/ko/assets/img/iam_implementation/fullscreen.png?efd1d7a82e515ea563a083b96ff24d4a){: style="float:right;max-width:23%;margin-left:15px;border:0;"} 커스텀 전체 인앱 메시지를 사용하여 상호작용하고 사용자 친화적인 프롬프트를 만들어 귀중한 고객 데이터를 수집하세요. 오른쪽 예제는 알림 환경설정을 포함한 대화형 푸시 프라이머로 재구성된 커스텀 전체 인앱 메시지의 구현을 보여줍니다. [`FullListViewController`](https://github.com/braze-inc/braze-growth-shares-ios-demo-app/blob/master/Braze-Demo/ViewController/In-App-Messages/FullListViewController/FullListViewController.swift)에 방문하여 시작하십시오. #### 대시보드 구성 대시보드에서 커스텀 전체 인앱 메시지를 설정하려면 쉼표로 구분된 문자열로 형식화된 태그 목록을 제공해야 합니다. 키-값 페어에서 `attribute_key`를 제공합니다. 이 키는 사용자가 선택한 값과 함께 고객 프로필에 커스텀 속성으로 저장됩니다. 귀하의 커스텀 보기 로직은 Braze로 전송된 사용자 속성을 처리해야 합니다. ![메시지 작성기에 세 개의 키-값 페어가 있습니다. 첫 번째 키-값 페어 "attribute_key"는 "푸시 태그"로 설정되고, 두 번째 "subtitle_text"은 "알림 활성화는 또한..."으로 설정되며, 세 번째 "view_type"는 "table_list".](https://www.braze.com/docs/ko/ko/assets/img/iam_implementation/dashboard3.png?87fc332d5e758f31bf85971ba1f9345f){: style="max-width:65%;"} #### 인앱 메시지 터치 가로채기 ![설정 및 토글 행을 표시하는 Apple 기기. 커스텀 보기는 버튼을 처리하며, 버튼 컨트롤 외부의 모든 터치는 인앱 메시지에 의해 처리되고 메시지를 해제합니다.](https://www.braze.com/docs/ko/ko/assets/img/iam_implementation_guide.png?962cfedd9859a5291b9f693d6e402213){: style="float:right;max-width:30%;margin-left:10px;border:0"} 인앱 메시지 터치를 가로채는 작업은 커스텀 전체 인앱 메시지 버튼이 올바르게 작동하도록 하는 데 매우 중요합니다. 기본적으로, `ABKInAppMessageImmersive`에서는 메시지에 탭 제스처 인식기를 추가하여 사용자가 버튼 없이 메시지를 해제할 수 있습니다. `UISwitch` 또는 버튼을 `UITableViewCell` 보기 계층 구조에 추가하면 이제 터치는 이제 커스텀 보기에 의해 처리됩니다. iOS 6부터 버튼 및 기타 컨트롤이 제스처 인식기보다 우선하므로, 커스텀 전체 인앱 메시지가 정상적으로 작동합니다. # SharePlay 인앱 메시지 구현 가이드 Source: /docs/ko/developer_guide/platforms/legacy_sdks/ios/in-app_messaging/implementation_guide/shareplay/index.md
**Warning:** [AppboyKit](https://github.com/Appboy/appboy-ios-sdk) (also known as the Objective-C SDK) is no longer supported and has been replaced by the [Swift SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=swift). It will no longer receive new features, bug fixes, security updates, or technical support—however, messaging and analytics will continue to function as normal. To learn more, see [Introducing the New Braze Swift SDK](https://www.braze.com/resources/articles/introducing-the-new-braze-swift-sdk). # SharePlay 인앱 메시지 구현 가이드 {#shareplay-in-app-message-implementation-guide} > SharePlay는 iOS 15 FaceTime 사용자가 여러 기기에서 미디어를 공유할 수 있는 새로 출시된 기능으로, 실시간 오디오 및 비디오 동기화를 제공합니다. SharePlay는 사용자가 친구 및 가족과 함께 콘텐츠를 경험할 수 있는 좋은 방법으로, Braze 고객에게 동영상 콘텐츠를 위한 추가적인 경로와 신규 사용자를 애플리케이션에 소개할 수 있는 기회를 제공합니다. ![SharePlay](https://www.braze.com/docs/ko/ko/assets/img/shareplay/shareplay6.png?0ffbb203ed26aa0f503eb8f83645216a){: style="border:0;margin-top:10px;"} ## 개요 {#overview} Apple이 iOS 15 업데이트의 일환으로 출시한 새로운 `GroupActivities` 프레임워크를 사용하면 Braze 인앱 메시지의 도움을 받아 SharePlay를 애플리케이션에 통합하여 FaceTime을 활용할 수 있습니다. ![SharePlay](https://www.braze.com/docs/ko/ko/assets/img/shareplay/shareplay3.png?4b2fdfd489a89e3c3778a1cd3b2e4ab0){: style="float:right;max-width:30%;margin-left:15px;margin-top:10px;"} 사용자가 FaceTime 통화에서 SharePlay 비디오를 시작하면 모든 사람의 화면 상단에 "열기" 버튼이 나타납니다. 열면 오디오와 비디오가 호환되는 모든 기기에서 동기화되어 사용자가 실시간으로 함께 비디오를 시청할 수 있습니다. 앱을 다운로드하지 않은 사용자는 App Store로 리디렉션됩니다. **동기화된 미디어 재생**
동기화된 미디어 재생을 사용하면 한 사람이 SharePlay 비디오를 일시 중지할 경우 모든 기기에서 해당 비디오가 일시 중지됩니다.

![SharePlay](https://www.braze.com/docs/ko/ko/assets/img/shareplay/shareplay7.png?bcd102ca4418004d04bec6dbcec4fc66){: style="border:0"} ## 통합 {#integration} 이 통합에 사용되는 인앱 메시지는 서브클래스된 모달 인앱 메시지 뷰 컨트롤러입니다. 설정 가이드는 iOS 인앱 메시지 고급 사용 사례 [구현 가이드](https://www.braze.com/docs/ko/ko/developer_guide/platforms/legacy_sdks/ios/in-app_messaging/implementation_guide/)에서 확인할 수 있습니다. 통합하기 전에 Xcode 프로젝트에 `GroupActivities` 엔타이틀먼트를 추가하세요. **Important:** 통합을 완료하려면 이 가이드와 함께 [Apple SharePlay 설명서](https://developer.apple.com/documentation/avfoundation/media_playback_and_selection/supporting_coordinated_media_playback)를 나란히 열어보는 것이 좋습니다. ### 1단계: XIB 재정의 및 로드 {#step-1-overriding-and-loading-xib} ```swift override var nibName: String { return "ModalVideoViewController" } /// Overriding loadView() from ABKInAppMessageModalViewController to provide our own view for the in-app message override func loadView() { Bundle.main.loadNibNamed(nibName, owner: self, options: nil) } ``` ### 2단계: 인앱 메시지용 AVPlayer 구성 {#step-2-configure-avplayer-for-in-app-messages} 인앱 메시지는 약간의 간단한 개발자 작업으로 기본적으로 비디오를 재생할 수 있습니다. 이렇게 하면 SharePlay와 같은 모든 `AVPlayerVideoController` 기능에 액세스할 수 있습니다. 이 예제에 사용된 인앱 메시지는 네이티브 비디오 플레이어를 포함하기 위한 커스텀 뷰가 있는 서브클래스된 `ABKInAppMessageModalViewController`입니다. ```swift func configureVideoPlayer() { guard let urlString = inAppMessage.extras?["video_url"] as? String, let url = URL(string: urlString) else { return } let videoTitle = inAppMessage.extras?["video_title"] as? String mediaItem = MediaItem(title: videoTitle ?? "Video Content", url: url) let asset = AVAsset(url: url) let playerItem = AVPlayerItem(asset: asset) player.replaceCurrentItem(with: playerItem) playerViewController.player = player addChild(playerViewController) videoPlayerContainer.addSubview(playerViewController.view) playerViewController.didMove(toParent: self) } ``` #### 대시보드 구성 {#dashboard-configuration} **키-값 페어**: 비디오 파일은 인앱 메시지의 키-값 페어로 설정해야 하며 미디어 항목 자체에 첨부할 수 없습니다. 콘텐츠를 표시하기 전에 `beforeInAppMessageDisplayed`에서 URL 유효성 검사를 가드레일로 추가할 수도 있습니다. **트리거**: 인앱 메시지는 재적격성이 활성화된 모든 사용자에게 적용 가능해야 합니다. 이를 위해 두 개의 트리거를 설정합니다. 하나는 메시지를 실행하는 기본 트리거이고, 다른 하나는 SharePlay에서 시작될 때 메시지를 실행하는 트리거입니다. iOS 15를 사용하지 않는 사용자는 로컬에서만 메시지를 볼 수 있습니다. **Important:** 세션 시작 시 트리거되는 다른 인앱 메시지가 서로 충돌할 수 있으므로 주의하세요. ### 3단계: 그룹 시청 활동 만들기 {#step-3-create-group-watching-activity} `GroupActivity` 프로토콜을 준수하는 오브젝트를 만듭니다. 이 오브젝트는 SharePlay 수명 주기 동안 공유되는 `GroupSession`의 메타데이터가 됩니다. ```swift struct MediaItem: Hashable, Codable { let title: String let url: URL } @available(iOS 15, *) struct MediaItemActivity: GroupActivity { static let activityIdentifier = "com.book-demo.GroupWatching" let mediaItem: MediaItem var metadata: GroupActivityMetadata { var metadata = GroupActivityMetadata() metadata.type = .watchTogether metadata.title = mediaItem.title metadata.fallbackURL = mediaItem.url return metadata } } ``` #### 재생 준비 {#prepare-to-play} 미디어 항목 재생을 준비할 때 각 그룹 활동에는 세 가지 `prepareForActivation()` 상태가 있습니다. - `.activationDisabled` - 개별적으로 보기 - `.activationPreferred` - 함께 보기 - `.cancelled` - 무시하고 적절히 처리 상태가 `activationPreferred`로 돌아오면 나머지 그룹 활동 수명 주기를 활성화하라는 신호입니다. ![SharePlay](https://www.braze.com/docs/ko/ko/assets/img/shareplay/shareplay.png?dcfb560fc9e9e3fc546253ab35042273){: style="border:0;"} ### 4단계: SharePlay API에서 인앱 메시지 실행 {#step-4-launch-in-app-message-from-shareplay-api} `GroupActivities` API는 비디오가 있는지 여부를 확인합니다. 비디오가 있는 경우 커스텀 이벤트를 트리거하여 SharePlay 지원 인앱 메시지를 실행해야 합니다. `CoordinationManager`는 사용자가 통화를 종료하거나 참여하는 경우와 같은 SharePlay의 상태 변경을 담당합니다. ```swift private var subscriptions = Set() private var selectedMediaItem: MediaItem? { didSet { // Ensure the UI selection always represents the currently playing media. guard let _ = selectedMediaItem else { return } if !BrazeManager.shared.inAppMessageCurrentlyVisible { BrazeManager.shared.logCustomEvent("SharePlay Event") } } } private func launchVideoPlayerIfNecessary() { CoordinationManager.shared.$enqueuedMediaItem .receive(on: DispatchQueue.main) .compactMap { $0 } .assign(to: \.selectedMediaItem, on: self) .store(in: &subscriptions) } ``` ### 5단계: 인앱 메시지 해제 시 그룹 세션에서 나가기 {#step-5-leaving-a-group-session-on-in-app-message-dismissal} 인앱 메시지가 해제될 때 SharePlay 세션을 종료하고 세션 오브젝트를 삭제하는 것이 적절합니다. ```swift override func viewDidDisappear(_ animated: Bool) { super.viewDidDisappear(animated) groupSession?.leave() CoordinationManager.shared.leave() } class CoordinationManager() { ... // Published values that the player, and other UI items, observe. @Published var enqueuedMediaItem: MediaItem? @Published var groupSession: GroupSession? // Clear activity when the user leaves func leave() { groupSession = nil enqueuedMediaItem = nil } ... } ``` ### SharePlay 버튼 표시 여부 구성 {#configure-shareplay-button-visibility} SharePlay 표시기를 동적으로 숨기거나 표시하는 것이 모범 사례입니다. `isEligibleForGroupSession` 변수를 사용하여 사용자가 현재 FaceTime 통화 중인지 여부를 관찰합니다. FaceTime 통화 중인 경우, 채팅에서 호환되는 기기 간에 비디오를 공유할 수 있는 버튼이 표시되어야 합니다. 사용자가 SharePlay를 처음 시작하면 원래 기기에서 옵션을 선택하라는 프롬프트가 나타납니다. 이후 공유 대상 사용자의 기기에서 콘텐츠에 참여하라는 후속 프롬프트가 나타납니다. ```swift private var isEligibleForSharePlay: Bool = false { didSet { sharePlayButton.isHidden = !isEligibleForSharePlay } } override func viewDidLoad() { super.viewDidLoad() // SharePlay button eligibility groupStateObserver.$isEligibleForGroupSession .receive(on: DispatchQueue.main) .assign(to: \.isEligibleForSharePlay, on: self) .store(in: &subscriptions) } ``` # iOS용 인앱 메시징 문제 해결 Source: /docs/ko/developer_guide/platforms/legacy_sdks/ios/in-app_messaging/troubleshooting/index.md
**Warning:** [AppboyKit](https://github.com/Appboy/appboy-ios-sdk) (also known as the Objective-C SDK) is no longer supported and has been replaced by the [Swift SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=swift). It will no longer receive new features, bug fixes, security updates, or technical support—however, messaging and analytics will continue to function as normal. To learn more, see [Introducing the New Braze Swift SDK](https://www.braze.com/resources/articles/introducing-the-new-braze-swift-sdk). # 인앱 메시지 문제 해결 {#troubleshoot-in-app-messages} ## 노출 횟수 {#impressions} #### 노출 또는 클릭 분석이 기록되지 않습니다 {#impression-or-click-analytics-arent-being-logged} 메시지 표시 또는 클릭 동작을 수동으로 처리하도록 인앱 메시지 델리게이트를 설정한 경우, 인앱 메시지에서 클릭 및 노출 횟수를 수동으로 기록해야 합니다. #### 예상보다 적은 노출 횟수 {#impressions-are-lower-than-expected} 트리거는 세션 시작 시 기기와 동기화하는 데 시간이 걸리므로 사용자가 세션 시작 직후 이벤트 또는 구매를 기록하면 경합 조건이 발생할 수 있습니다. 한 가지 가능한 해결 방법은 세션 시작 시 트리거하도록 캠페인을 변경한 다음, 의도한 이벤트 또는 구매를 기준으로 세분화하는 것입니다. 이렇게 하면 이벤트가 발생한 후 다음 세션이 시작될 때 인앱 메시지가 전달됩니다. ## 예상 인앱 메시지가 표시되지 않았습니다 {#expected-in-app-message-did-not-display} 대부분의 인앱 메시지 문제는 전달과 표시라는 두 가지 주요 카테고리로 분류할 수 있습니다. 예상 인앱 메시지가 기기에 표시되지 않는 문제를 해결하려면 먼저 [인앱 메시지가 기기에 전달되었는지](#troubleshooting-in-app-message-delivery) 확인한 다음 [메시지 표시 문제를](#troubleshooting-in-app-message-display) 해결해야 합니다. ### 인앱 메시지 전달 {#troubleshooting-in-app-message-delivery} SDK는 세션 시작 시 Braze 서버에 인앱 메시지를 요청합니다. 인앱 메시지가 기기에 전달되고 있는지 확인하려면 인앱 메시지가 SDK에 의해 요청되고 Braze 서버에 의해 반환되는지 확인해야 합니다. #### 메시지 요청 및 반환 여부 확인 {#check-if-messages-are-requested-and-returned} 1. 대시보드에서 [테스트 사용자](https://www.braze.com/docs/ko/ko/user_guide/administrative/app_settings/developer_console/internal_groups_tab/#adding-test-users)로 자신을 추가하세요. 2. 사용자를 대상으로 인앱 메시지 캠페인을 설정합니다. 3. 애플리케이션에서 새 세션이 발생하는지 확인합니다. 4. [이벤트 사용자 로그](https://www.braze.com/docs/ko/ko/user_guide/administrative/app_settings/developer_console/event_user_log_tab/#event-user-log-tab)를 사용하여 기기가 세션 시작 시 인앱 메시지를 요청하고 있는지 확인하세요. 테스트 사용자의 세션 시작 이벤트와 연결된 SDK 요청을 찾습니다. - 앱에서 트리거된 인앱 메시지를 요청하는 경우 **Response Data** 아래 **Requested Responses** 필드에 `trigger`가 표시되어야 합니다. - 앱에서 원본 인앱 메시지를 요청하는 경우 **Response Data** 아래 **Requested Responses** 필드에 `in_app`이 표시되어야 합니다. 5. [이벤트 사용자 로그](https://www.braze.com/docs/ko/ko/user_guide/administrative/app_settings/developer_console/event_user_log_tab/#event-user-log-tab)를 사용하여 응답 데이터에서 올바른 인앱 메시지가 반환되고 있는지 확인하세요.
![](https://www.braze.com/docs/ko/ko/assets/img_archive/event_user_log_iams.png?fd8f7c0f05a549b6a529b92744f37f96) #### 요청되지 않는 메시지 문제 해결 {#troubleshoot-messages-not-being-requested} 인앱 메시지가 요청되지 않는 경우 앱이 세션을 올바르게 추적하지 않을 수 있습니다. 인앱 메시지는 세션 시작 시 새로고침되기 때문입니다. 또한 앱의 세션 타임아웃 시맨틱에 따라 앱이 실제로 세션을 시작하고 있는지 확인해야 합니다. ![이벤트 사용자 로그에서 성공적인 세션 시작 이벤트를 표시하는 SDK 요청.](https://www.braze.com/docs/ko/ko/assets/img_archive/event_user_log_session_start.png?972201c9c20f018bc85d97167638f04e) ### 반환되지 않는 메시지 문제 해결 {#troubleshoot-messages-not-being-returned} 인앱 메시지가 반환되지 않는다면 캠페인 타겟팅에 문제가 있는 것일 수 있습니다: - Segment에 사용자가 포함되어 있지 않습니다. - 사용자의 [**참여**](https://www.braze.com/docs/ko/ko/user_guide/audience/manage_audience/user_profiles/#engagement-tab) 탭을 확인하여 **Segments** 아래에 올바른 Segment가 나타나는지 확인하세요. - 사용자가 이전에 인앱 메시지를 받은 적이 있으며 다시 받을 자격이 없습니다. - **Campaign Composer**의 **Delivery** 단계에서 [Campaign 재자격 설정](https://www.braze.com/docs/ko/ko/user_guide/engagement_tools/campaigns/building_campaigns/delivery_types/reeligibility/)을 확인하고 재자격 설정이 테스트 설정과 일치하는지 확인하세요. - 사용자가 Campaign의 최대 게재빈도 한도에 도달했습니다. - Campaign [최대 게재빈도 설정](https://www.braze.com/docs/ko/ko/user_guide/engagement_tools/campaigns/building_campaigns/rate-limiting/#frequency-capping)을 확인하고 테스트 설정과 일치하는지 확인하세요. - Campaign에 대조군이 있는 경우 사용자가 대조군에 속했을 수 있습니다. - Campaign 배리언트가 **Control**로 설정된 수신된 Campaign 배리언트 필터로 Segment를 생성하고 사용자가 해당 Segment에 속하는지 확인하여 이 문제가 발생했는지 확인할 수 있습니다. - 통합 테스트 목적으로 Campaign을 생성할 때는 대조군 추가를 옵트아웃해야 합니다. ### 인앱 메시지 표시 {#troubleshooting-in-app-message-display} 앱에서 인앱 메시지를 성공적으로 요청 및 수신하고 있지만 표시되지 않는 경우 일부 기기 측 로직으로 인해 표시가 방해되고 있을 수 있습니다: - 트리거된 인앱 메시지는 [트리거 사이의 최소 시간 간격](https://www.braze.com/docs/ko/ko/developer_guide/platform_integration_guides/ios/in-app_messaging/in-app_message_delivery/#minimum-time-interval-between-triggers)을 기준으로 속도 제한이 적용되며, 기본값은 30초입니다. - 인앱 메시지 처리를 커스텀하기 위해 델리게이트를 설정한 경우, 델리게이트가 인앱 메시지 표시에 영향을 주지 않는지 확인하세요. - 이미지 다운로드에 실패하면 이미지가 포함된 인앱 메시지가 표시되지 않습니다. `SDWebImage` 프레임워크가 제대로 통합되지 않은 경우 이미지 다운로드는 항상 실패합니다. 기기 로그를 확인하여 이미지 다운로드가 실패하지 않았는지 확인하세요. - 기기 방향이 인앱 메시지에 지정된 방향과 일치하지 않으면 인앱 메시지가 표시되지 않습니다. 기기의 방향이 올바른지 확인하세요. # iOS용 콘텐츠 카드 뷰 컨트롤러 통합 Source: /docs/ko/developer_guide/platforms/legacy_sdks/ios/content_cards/integration/index.md
**Warning:** [AppboyKit](https://github.com/Appboy/appboy-ios-sdk) (also known as the Objective-C SDK) is no longer supported and has been replaced by the [Swift SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=swift). It will no longer receive new features, bug fixes, security updates, or technical support—however, messaging and analytics will continue to function as normal. To learn more, see [Introducing the New Braze Swift SDK](https://www.braze.com/resources/articles/introducing-the-new-braze-swift-sdk). # Content Cards 통합 {#content-card-integration} ## Content Cards 데이터 모델 {#content-cards-data-model} Content Cards 데이터 모델은 iOS SDK에서 사용할 수 있습니다. ### 데이터 가져오기 {#getting-the-data} Content Cards 데이터 모델에 액세스하려면 Content Cards 업데이트 이벤트를 구독합니다. ```objc // Subscribe to Content Cards updates // Note: you should remove the observer where appropriate [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(contentCardsUpdated:) name:ABKContentCardsProcessedNotification object:nil]; ``` ```objc // Called when Content Cards are refreshed (via `requestContentCardsRefresh`) - (void)contentCardsUpdated:(NSNotification *)notification { BOOL updateIsSuccessful = [notification.userInfo[ABKContentCardsProcessedIsSuccessfulKey] boolValue]; if (updateIsSuccessful) { // get the cards using [[Appboy sharedInstance].contentCardsController getContentCards]; } } ``` ```swift // Subscribe to content card updates // Note: you should remove the observer where appropriate NotificationCenter.default.addObserver(self, selector: #selector(contentCardsUpdated), name:NSNotification.Name.ABKContentCardsProcessed, object: nil) ``` ```swift // Called when the Content Cards are refreshed (via `requestContentCardsRefresh`) @objc private func contentCardsUpdated(_ notification: Notification) { if let updateIsSuccessful = notification.userInfo?[ABKContentCardsProcessedIsSuccessfulKey] as? Bool { if (updateIsSuccessful) { // get the cards using Appboy.sharedInstance()?.contentCardsController.contentCards } } } ``` Braze에서 카드 데이터를 보낸 후에 변경하려면 카드 데이터의 딥 카피를 로컬에 저장하고 데이터를 업데이트한 후 직접 표시하는 것이 좋습니다. 카드는 [`ABKContentCardsController`](https://appboy.github.io/appboy-ios-sdk/docs/interface_a_b_k_content_cards_controller.html)를 통해 접근할 수 있습니다. ## 콘텐츠 카드 모델 {#content-card-model} Braze는 배너, 캡션 이미지, 클래식의 세 가지 Content Cards 유형을 제공합니다. 각 유형은 기본 `ABKContentCard` 클래스에서 공통 속성정보를 상속받으며 다음과 같은 추가 속성정보가 있습니다. ### 기본 콘텐츠 카드 모델 속성정보 - ABKContentCard {#base-content-card-model-properties-abkcontentcard} | 등록정보 | 설명 | |---|---| | `idString` | (읽기 전용) Braze에서 설정한 카드의 ID. | | `viewed` | 이 속성정보는 사용자가 카드를 조회했는지 여부를 반영합니다. | | `created` | (읽기 전용) 이 속성정보는 Braze에서 카드 생성 시간의 Unix 타임스탬프입니다. | | `expiresAt` | (읽기 전용) 이 속성정보는 카드의 만료 시간에 대한 Unix 타임스탬프입니다. | | `dismissible` | 이 속성정보는 사용자가 카드를 해제할 수 있는지 여부를 반영합니다. | | `pinned` | 이 속성정보는 대시보드에서 카드가 "고정됨"으로 설정되었는지 여부를 반영합니다. | | `dismissed` | 이 속성정보는 사용자가 카드를 닫았는지 여부를 반영합니다. | | `url` | 카드를 클릭한 후 열리는 URL입니다. HTTP(s) URL 또는 프로토콜 URL일 수 있습니다. | | `openURLInWebView` | 이 속성정보는 URL이 앱 내에서 열릴지, 외부 웹 브라우저에서 열릴지를 결정합니다. | | `extras` | 선택적 `NSDictionary`(값: `NSString`). | {: .reset-td-br-1 .reset-td-br-2 aria-label="Base Content Card model properties - ABKContentCard" } ### 배너 콘텐츠 카드 속성정보 - ABKBannerContentCard {#banner-content-card-properties-abkbannercontentcard} | 등록정보 | 설명 | |---|---| | `image` | 이 속성정보는 카드 이미지의 URL입니다. | | `imageAspectRatio` | 이 속성정보는 카드 이미지의 종횡비이며 이미지 로드가 완료되기 전에 힌트로 사용됩니다. 특정 상황에서는 속성정보가 제공되지 않을 수 있습니다. | {: .reset-td-br-1 .reset-td-br-2 aria-label="Banner Content Card properties - ABKBannerContentCard" } ### 캡션 이미지 콘텐츠 카드 속성정보 - ABKCaptionedImageCard {#captioned-image-content-card-properties-abkcaptionedimagecard} | 등록정보 | 설명 | |---|---| | `image` | 이 속성정보는 카드 이미지의 URL입니다. | | `imageAspectRatio` | 이 속성정보는 카드 이미지의 종횡비입니다. | | `title` | 카드의 제목 텍스트입니다. | | `cardDescription` | 카드의 본문 텍스트입니다. | | `domain` | 속성정보 URL의 링크 텍스트, 예를 들어 @"blog.braze.com". 카드의 UI에 표시되어 카드를 클릭할 때의 동작 및 방향을 나타낼 수 있습니다. | {: .reset-td-br-1 .reset-td-br-2 aria-label="Captioned image Content Card properties - ABKCaptionedImageCard" } ### 클래식 콘텐츠 카드 속성정보 - ABKClassicContentCard {#classic-content-card-properties-abkclassiccontentcard} | 등록정보 | 설명 | |---|---| | `image` | (선택 사항) 이 속성정보는 카드 이미지의 URL입니다. | | `title` | 카드의 제목 텍스트입니다. | | `cardDescription` | 카드의 본문 텍스트입니다. | | `domain` | 속성정보 URL의 링크 텍스트, 예를 들어 @"blog.braze.com". 카드의 UI에 표시되어 카드를 클릭할 때의 동작 및 방향을 나타낼 수 있습니다. | {: .reset-td-br-1 .reset-td-br-2 aria-label="Classic Content Card properties - ABKClassicContentCard" } ## 카드 메서드 {#card-methods} | 메서드 | 설명 | |---|---| | `logContentCardImpression` | 특정 카드에 대해 Braze에 노출 횟수를 수동으로 기록합니다. | | `logContentCardClicked` | 특정 카드에 대해 Braze에 클릭을 수동으로 기록합니다. SDK는 카드에 유효한 값이 있는 `url` 속성정보가 있을 때만 카드 클릭을 기록합니다. | | `logContentCardDismissed` | 특정 카드에 대해 Braze에 카드 방출을 수동으로 기록합니다. SDK는 카드의 `dismissed` 속성정보가 이미 `true`로 설정되지 않은 경우에만 카드 방출을 기록합니다. | | `isControlCard` | 카드가 A/B 테스트의 제어 카드인지 확인합니다. | {: .reset-td-br-1 .reset-td-br-2 aria-label="Card methods" } 자세한 내용은 [클래스 참조 설명서](https://appboy.github.io/appboy-ios-sdk/docs/interface_a_b_k_content_card.html)를 참조하세요. ## Content Cards 뷰 컨트롤러 통합 {#content-cards-view-controller-integration} Content Cards는 내비게이션 또는 모달의 두 가지 뷰 컨트롤러 컨텍스트와 통합할 수 있습니다. ### 내비게이션 컨텍스트 {#navigation-context} 내비게이션 컨트롤러에 `ABKContentCardsTableViewController` 인스턴스를 푸시하는 예: ```objc ABKContentCardsTableViewController *contentCards = [[ABKContentCardsTableViewController alloc] init]; contentCards.title = @"Content Cards Title"; contentCards.disableUnreadIndicator = YES; [self.navigationController pushViewController:contentCards animated:YES]; ``` ```swift let contentCards = ABKContentCardsTableViewController() contentCards.title = "Content Cards Title" contentCards.disableUnreadIndicator = true navigationController?.pushViewController(contentCards, animated: true) ``` **Note:** 내비게이션 바의 제목을 커스터마이즈하려면 `ABKContentCardsTableViewController` 인스턴스의 `navigationItem`에서 제목 속성정보를 설정합니다. ### 모달 컨텍스트 {#modal-context} 이 모달은 모달 뷰에서 뷰 컨트롤러를 표시하는 데 사용되며, 상단에 내비게이션 바가 있고 바 측면에 **Done** 버튼이 있습니다. ```objc ABKContentCardsViewController *contentCards = [[ABKContentCardsViewController alloc] init]; contentCards.contentCardsViewController.title = @"Content Cards Title"; contentCards.contentCardsViewController.disableUnreadIndicator = YES; [self.navigationController presentViewController:contentCards animated:YES completion:nil]; ``` ```swift let contentCards = ABKContentCardsViewController() contentCards.contentCardsViewController.title = "Content Cards Title" contentCards.contentCardsViewController.disableUnreadIndicator = true self.present(contentCards, animated: true, completion: nil) ``` 뷰 컨트롤러 예제를 보려면 [Content Cards 샘플 앱](https://github.com/Appboy/appboy-ios-sdk/tree/master/Samples/ContentCards/BrazeContentCardsSampleApp)을 확인하세요. **Note:** 헤더를 커스터마이즈하려면 부모 `ABKContentCardsViewController` 인스턴스에 포함된 `ABKContentCardsTableViewController` 인스턴스에 속하는 `navigationItem`의 제목 속성정보를 설정합니다. # iOS 콘텐츠 카드 사용자 지정 Source: /docs/ko/developer_guide/platforms/legacy_sdks/ios/content_cards/customization/index.md

# iOS용 사용자 지정 콘텐츠 카드 스타일링 Source: /docs/ko/developer_guide/platforms/legacy_sdks/ios/content_cards/customization/custom_styling/index.md
**Warning:** [AppboyKit](https://github.com/Appboy/appboy-ios-sdk) (also known as the Objective-C SDK) is no longer supported and has been replaced by the [Swift SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=swift). It will no longer receive new features, bug fixes, security updates, or technical support—however, messaging and analytics will continue to function as normal. To learn more, see [Introducing the New Braze Swift SDK](https://www.braze.com/resources/articles/introducing-the-new-braze-swift-sdk). # 사용자 지정 스타일 ## 기본 이미지 재정의 **Important:** iOS 인앱 메시지 또는 콘텐츠 카드에서 이미지를 표시하기 위해 Braze UI를 사용하려는 경우 `SDWebImage`의 통합이 필요합니다. Braze를 사용하면 고객이 기존의 기본 이미지를 자신만의 사용자 지정 이미지로 대체할 수 있습니다. 이를 위해 커스텀 이미지를 사용하여 새 `png` 파일을 만들고 앱의 이미지 번들에 추가합니다. 그런 다음, 라이브러리의 기본 이미지를 덮어쓰도록 파일 이름을 이미지 이름으로 변경합니다. 또한 다양한 휴대폰 크기에 맞추기 위해 `@2x` 및 `@3x` 버전의 이미지를 업로드해야 합니다. 다음은 콘텐츠 카드에서 재정의할 수 있는 이미지입니다. - 플레이스홀더 이미지: `appboy_cc_noimage_lrg` - 고정된 아이콘 이미지: `appboy_cc_icon_pinned` 대시보드에 입력하는 콘텐츠(메시지 텍스트, 이미지 URL, 링크 및 모든 키-값 페어 포함)의 콘텐츠 카드 크기는 최대 2KB이므로 보내기 전에 크기를 확인합니다. 이 크기를 초과하면 카드가 전송되지 않습니다. **Important:** 기본값 이미지를 재정의하는 것은 현재 우리의 .NET MAUI iOS 통합에서 지원되지 않습니다. ## 다크 모드 비활성화하기 사용자 기기에서 다크 모드가 활성화된 경우 콘텐츠 카드 UI에 다크 테마 스타일이 적용되지 않도록 하려면 `ABKContentCardsTableViewController.enableDarkTheme` 속성정보를 설정합니다. `ABKContentCardsTableViewController` 인스턴스에서 직접 `enableDarkTheme` 속성정보에 액세스하거나 자체 UI에 가장 적합하도록 `ABKContentCardsViewController.contentCardsViewController` 속성정보를 통해 액세스할 수 있습니다. ```objc // Accessing enableDarkTheme via ABKContentCardsViewController.contentCardsViewController. - (IBAction)presentModalContentCards:(id)sender { ABKContentCardsViewController *contentCardsVC = [ABKContentCardsViewController new]; contentCardsVC.contentCardsViewController.enableDarkTheme = NO; ... [self.navigationController presentViewController:contentCardsVC animated:YES completion:nil]; } // Accessing enableDarkTheme directly. - (IBAction)presentNavigationContentCards:(id)sender { ABKContentCardsTableViewController *contentCardsTableVC = [[ABKContentCardsTableViewController alloc] init]; contentCardsTableVC.enableDarkTheme = NO; ... [self.navigationController pushViewController:contentCardsTableVC animated:YES]; } ``` ```swift // Accessing enableDarkTheme via ABKContentCardsViewController.contentCardsViewController. @IBAction func presentModalContentCards(_ sender: Any) { let contentCardsVC = ABKContentCardsViewController() contentCardsVC.contentCardsViewController.enableDarkTheme = false ... self.navigationController?.present(contentCardsVC, animated: true, completion: nil) } // Accessing enableDarkTheme directly. @IBAction func presentNavigationContentCards(_ sender: Any) { let contentCardsTableVC = ABKContentCardsTableViewController() contentCardsTableVC.enableDarkTheme = false ... self.navigationController?.present(contentCardsTableVC, animated: true, completion: nil) } ``` # iOS용 콘텐츠 카드 피드 사용자 지정 Source: /docs/ko/developer_guide/platforms/legacy_sdks/ios/content_cards/customization/customizing_feed/index.md
**Warning:** [AppboyKit](https://github.com/Appboy/appboy-ios-sdk) (also known as the Objective-C SDK) is no longer supported and has been replaced by the [Swift SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=swift). It will no longer receive new features, bug fixes, security updates, or technical support—however, messaging and analytics will continue to function as normal. To learn more, see [Introducing the New Braze Swift SDK](https://www.braze.com/resources/articles/introducing-the-new-braze-swift-sdk). # 콘텐츠 카드 피드를 사용자 정의 `ABKContentCardsTableViewController`를 확장하여 모든 UI 요소와 콘텐츠 카드 동작을 커스텀하여 나만의 콘텐츠 카드 인터페이스를 만들 수 있습니다. 콘텐츠 카드 셀을 하위 클래스로 분류한 다음 프로그래밍 방식으로 사용하거나 새 클래스를 등록하는 사용자 지정 스토리보드를 도입하여 사용할 수도 있습니다. 전체 예제는 콘텐츠 카드 [샘플 앱](https://github.com/Appboy/appboy-ios-sdk/tree/master/Samples/ContentCards/BrazeContentCardsSampleApp)을 확인하세요. 또한 서브클래스 전략을 사용할지 아니면 완전히 커스텀 보기 컨트롤러를 사용하고 [데이터 업데이트에 가입](https://www.braze.com/docs/ko/ko/developer_guide/platforms/legacy_sdks/ios/content_cards/integration/)할지 고려하는 것도 중요합니다. 예를 들어 `ABKContentCardsTableViewController`를 서브클래스로 설정하는 경우 [`populateContentCards` 메서드](#overriding-populated-content-cards)를 사용하여 카드를 필터링 및 정렬(권장)할 수 있습니다). 그러나 전체 보기 컨트롤러 사용자 지정을 사용하면 캐러셀에 표시하거나 대화형 요소를 추가하는 등 카드 동작을 더 많이 제어할 수 있지만 정렬 및 필터링 로직을 구현하려면 관찰자에 의존해야 합니다. 또한 노출 횟수, 해제 이벤트 및 클릭을 올바르게 기록하려면 각 분석 메서드를 구현해야 합니다. ## UI 사용자 지정 다음 코드 스니펫은 SDK에서 제공하는 메서드를 사용하여 UI 요구 사항에 맞게 콘텐츠 카드의 스타일을 지정하고 변경하는 방법을 보여줍니다. 이러한 방법을 사용하면 사용자 지정 글꼴, 사용자 지정 색상 구성 요소, 사용자 지정 텍스트 등을 포함하여 콘텐츠 카드 UI의 모든 측면을 사용자 지정할 수 있습니다. 콘텐츠 카드 UI를 사용자 지정하는 두 가지 방법이 있습니다. - 동적 방법: 카드별로 카드 UI 업데이트 - 정적 방법: 모든 카드의 UI 업데이트 ### 동적 UI 콘텐츠 카드 `applyCard` 메서드는 카드 오브젝트를 참조하여 UI를 업데이트하는 데 사용할 키-값 페어를 전달할 수 있습니다. ```objc - (void)applyCard:(ABKCaptionedImageContentCard *)captionedImageCard { [super applyCard:captionedImageCard]; if ([card.extras objectForKey:ContentCardKeyBackgroundColorValue]) { NSString *backgroundColor = [card.extras objectForKey:ContentCardKeyBackgroundColor]; if ([backgroundColor colorValue]) { self.rootView.backgroundColor = [backgroundColor colorValue]; } else { self.rootView.backgroundColor = [UIColor lightGray]; } } else { self.rootView.backgroundColor = [UIColor lightGray]; } } ``` ```swift override func apply(_ captionedImageCard: ABKCaptionedImageContentCard!) { super.apply(captionedImageCard) if let backgroundColor = card.extras?[ContentCardKey.backgroundColor.rawValue] as? String, let backgroundColorValue = backgroundColor.colorValue() { rootView.backgroundColor = backgroundColorValue } else { rootView.backgroundColor = .lightGray } } ``` ### 정적 UI `setUpUI` 메서드는 모든 카드의 정적 콘텐츠 카드 구성요소에 값을 할당할 수 있습니다. ```objc #import "CustomClassicContentCardCell.h" @implementation CustomClassicContentCardCell - (void)setUpUI { [super setUpUI]; self.rootView.backgroundColor = [UIColor lightGrayColor]; self.rootView.layer.borderColor = [UIColor purpleColor].CGColor; self.unviewedLineView.backgroundColor = [UIColor redColor]; self.titleLabel.font = [UIFont italicSystemFontOfSize:20]; } ``` ```swift override func setUpUI() { super.setUpUI() rootView.backgroundColor = .lightGray rootView.layer.borderColor = UIColor.purple.cgColor unviewedLineViewColor = .red titleLabel.font = .italicSystemFont(ofSize: 20) } ``` ## 사용자 지정 인터페이스 제공 원하는 카드 유형별로 사용자 지정 클래스를 등록하여 사용자 지정 인터페이스를 제공할 수 있습니다. ![배너 콘텐츠 카드. 배너 콘텐츠 카드는 배너 오른쪽에 "Braze Demo를 다운로드해 주셔서 감사합니다!"라는 텍스트와 함께 이미지를 보여줍니다.](https://www.braze.com/docs/ko/ko/assets/img/interface1.png?c56c164e8146b5c7652ed27f45facea8){: style="max-width:35%;margin-left:15px;"} ![캡션이 있는 이미지 콘텐츠 카드입니다. 자막 콘텐츠 카드는 Braze 이미지를 보여주며 하단에 "Braze Demo를 다운로드해 주셔서 감사합니다!"라는 자막이 겹쳐져 있습니다. ](https://www.braze.com/docs/ko/ko/assets/img/interface2.png?8ee545c80d29aae4ab7413df02ad504a){: style="max-width:25%;margin-left:15px;"} ![클래식 콘텐츠 카드. 클래식 콘텐츠 카드는 카드 중앙에 이미지를 보여주며 그 아래에 "Braze Demo를 다운로드해 주셔서 감사합니다"라는 문구가 있습니다.](https://www.braze.com/docs/ko/ko/assets/img/interface3.png?00b31c463a9f17c93e748f489d2c7c87){: style="max-width:18%;margin-left:15px;"} Braze는 세 가지 콘텐츠 카드 템플릿(배너, 캡션 이미지, 클래식)을 제공합니다. 또는 자체 커스텀 인터페이스를 제공하려면 다음 코드 스니펫을 참조하세요. ```objc - (void)registerTableViewCellClasses { [super registerTableViewCellClasses]; // Replace the default class registrations with custom classes for these two types of cards [self.tableView registerClass:[CustomCaptionedImageContentCardCell class] forCellReuseIdentifier:@"ABKCaptionedImageContentCardCell"]; [self.tableView registerClass:[CustomClassicContentCardCell class] forCellReuseIdentifier:@"ABKClassicCardCell"]; } ``` ```swift override func registerTableViewCellClasses() { super.registerTableViewCellClasses() // Replace the default class registrations with custom classes tableView.register(CustomCaptionedImageContentCardCell.self, forCellReuseIdentifier: "ABKCaptionedImageContentCardCell") tableView.register(CustomBannerContentCardCell.self, forCellReuseIdentifier: "ABKBannerContentCardCell") tableView.register(CustomClassicImageContentCardCell.self, forCellReuseIdentifier: "ABKClassicImageCardCell") tableView.register(CustomClassicContentCardCell.self, forCellReuseIdentifier: "ABKClassicCardCell") } ``` ## 채워진 콘텐츠 카드 재정의 콘텐츠 카드는 `populateContentCards` 메서드를 사용하여 프로그래밍 방식으로 변경할 수 있습니다. ```objc - (void)populateContentCards { NSMutableArray *cards = [NSMutableArray arrayWithArray:[Appboy.sharedInstance.contentCardsController getContentCards]]; for (ABKContentCard *card in cards) { // Replaces the card description for all Classic Content Cards if ([card isKindOfClass:[ABKClassicContentCard class]]) { ((ABKClassicContentCard *)card).cardDescription = @"Custom Feed Override title [classic cards only]!"; } } super.cards = cards; } ``` ```swift override func populateContentCards() { guard let cards = Appboy.sharedInstance()?.contentCardsController.contentCards else { return } for card in cards { // Replaces the card description for all Classic Content Cards if let classicCard = card as? ABKClassicContentCard { classicCard.cardDescription = "Custom Feed Override title [classic cards only]!" } } super.cards = (cards as NSArray).mutableCopy() as? NSMutableArray } ``` # iOS에서 콘텐츠 카드 클릭을 수동으로 처리하기 Source: /docs/ko/developer_guide/platforms/legacy_sdks/ios/content_cards/customization/handling_clicks_manually/index.md
**Warning:** [AppboyKit](https://github.com/Appboy/appboy-ios-sdk) (also known as the Objective-C SDK) is no longer supported and has been replaced by the [Swift SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=swift). It will no longer receive new features, bug fixes, security updates, or technical support—however, messaging and analytics will continue to function as normal. To learn more, see [Introducing the New Braze Swift SDK](https://www.braze.com/resources/articles/introducing-the-new-braze-swift-sdk). # 클릭을 수동으로 처리하십시오 [`ABKContentCardsTableViewControllerDelegate`](https://appboy.github.io/appboy-ios-sdk/docs/protocol_a_b_k_content_cards_table_view_controller_delegate-p.html) 프로토콜을 구현하고 위임 오브젝트를 `ABKContentCardsTableViewController`의 `delegate` 속성정보로 설정하여 콘텐츠 카드 클릭을 수동으로 처리할 수 있습니다. 예제는 [콘텐츠 카드 샘플 앱을](https://github.com/Appboy/appboy-ios-sdk/tree/master/Samples/ContentCards/BrazeContentCardsSampleApp) 참조하세요. ```objc contentCardsTableViewController.delegate = delegate; // Methods to implement in delegate - (BOOL)contentCardTableViewController:(ABKContentCardsTableViewController *)viewController shouldHandleCardClick:(NSURL *)url { if ([[url.host lowercaseString] isEqualToString:@"my-domain.com"]) { // Custom handle link here NSLog(@"Manually handling Content Card click with URL %@", url.absoluteString); return NO; } // Let the Braze SDK handle the click action return YES; } - (void)contentCardTableViewController:(ABKContentCardsTableViewController *)viewController didHandleCardClick:(NSURL *)url { NSLog(@"Braze SDK handled Content Card click with URL %@", url.absoluteString); } ``` ```swift contentCardsTableViewController.delegate = delegate // Methods to implement in delegate func contentCardTableViewController(_ viewController: ABKContentCardsTableViewController!, shouldHandleCardClick url: URL!) -> Bool { if (url.host?.lowercased() == "my-domain.com") { // Custom handle link here NSLog("Manually handling Content Card click with URL %@", url.absoluteString) return false } // Let the Braze SDK handle the click action return true } func contentCardTableViewController(_ viewController: ABKContentCardsTableViewController!, didHandleCardClick url: URL!) { NSLog("Braze SDK handled Content Card click with URL %@", url.absoluteString) } ``` **Important:** `ABKContentCardsTableViewController`에서 `handleCardClick:` 메서드를 재정의하면 이러한 위임 메서드가 호출되지 않을 수 있습니다. # iOS용 콘텐츠 카드 열람 및 미열람 표시기 Source: /docs/ko/developer_guide/platforms/legacy_sdks/ios/content_cards/customization/read_unread_indicators/index.md
**Warning:** [AppboyKit](https://github.com/Appboy/appboy-ios-sdk) (also known as the Objective-C SDK) is no longer supported and has been replaced by the [Swift SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=swift). It will no longer receive new features, bug fixes, security updates, or technical support—however, messaging and analytics will continue to function as normal. To learn more, see [Introducing the New Braze Swift SDK](https://www.braze.com/resources/articles/introducing-the-new-braze-swift-sdk). # 열람 및 미열람 표시기 ## 미열람 표시기 비활성화 ![두 개의 콘텐츠 카드가 나란히 표시됩니다. 왼쪽 카드 하단에 파란색 선이 있으며, 이는 아직 열람하지 않았음을 나타냅니다. 오른쪽 카드에는 파란색 선이 없으며, 이는 이미 열람했음을 나타냅니다.](https://www.braze.com/docs/ko/ko/assets/img/braze-content-cards-seen-unseen-behavior.png?00ecd6c2252753cde9662110012ab680){: style="max-width:80%"} `ABKContentCardsTableViewController`에서 `disableUnviewedIndicator` 등록정보를 `YES`로 설정하여 카드의 열람 여부를 나타내는 카드 하단의 파란색 선을 비활성화할 수 있습니다. ## 미열람 표시기 커스터마이징 미열람 표시기는 `ABKBaseContentCardCell` 클래스의 `unviewedLineView` 등록정보를 통해 액세스할 수 있습니다. `UITableViewCell` 구현을 사용하는 경우 셀이 그려지기 전에 등록정보에 접근해야 합니다. 예를 들어, 미열람 표시기의 색상을 빨간색으로 설정하려면 다음과 같이 합니다: ```objc ((ABKBaseContentCardCell *)cell).unviewedLineView.backgroundColor = [UIColor redColor]; ``` ```swift (card as? ABKBaseContentCardCell).unviewedLineView.backgroundColor = UIColor.red ``` # iOS용 콘텐츠 카드 배지 Source: /docs/ko/developer_guide/platforms/legacy_sdks/ios/content_cards/customization/badges/index.md
**Warning:** [AppboyKit](https://github.com/Appboy/appboy-ios-sdk) (also known as the Objective-C SDK) is no longer supported and has been replaced by the [Swift SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=swift). It will no longer receive new features, bug fixes, security updates, or technical support—however, messaging and analytics will continue to function as normal. To learn more, see [Introducing the New Braze Swift SDK](https://www.braze.com/resources/articles/introducing-the-new-braze-swift-sdk). # Badges ## 미열람 콘텐츠 카드 수 요청 사용자가 가지고 있는 읽지 않은 콘텐츠 카드의 수를 표시하려면 카드 수를 요청하고 배지로 표시하는 것이 좋습니다. 배지는 콘텐츠 카드에서 사용자를 기다리는 새로운 콘텐츠에 대한 관심을 끌 수 있는 좋은 방법입니다. 콘텐츠 카드에 배지를 추가하려는 경우 Braze SDK에서 다음을 쿼리하는 메서드를 제공합니다. - 현재 사용자가 보지 않은 콘텐츠 카드 - 현재 사용자가 볼 수 있는 총 콘텐츠 카드 [`ABKContentCardsController`](https://appboy.github.io/appboy-ios-sdk/docs/interface_a_b_k_content_cards_controller.html)에서 다음 메서드 선언이 이를 자세히 설명합니다. ```objc - (NSInteger)unviewedContentCardCount; /* This method returns the number of currently active Content Cards that have not been viewed. A "view" happens when a card becomes visible in the Content Cards view. This differentiates between cards that are off-screen in the scrolling view and those which are on-screen; when a card scrolls onto the screen, it's counted as viewed. Cards are counted as viewed only once -- if a card scrolls off the screen and back on, it's not re-counted. Cards are counted only once, even if they appear in multiple Content Cards views or across multiple devices. */ - (NSInteger)contentCardCount; /* This method returns the total number of currently active Content Cards. Cards are counted only once even if they appear in multiple Content Cards views. */ ``` ## 앱 배지 수에 미열람 콘텐츠 카드 수 표시 앱의 푸시 알림에 대한 미리 알림 기능 외에도 배지를 활용하여 사용자의 콘텐츠 카드에서 미열람 항목을 나타낼 수 있습니다. 미열람 콘텐츠 카드 업데이트를 기반으로 배지 수를 업데이트하는 기능은 사용자를 앱으로 다시 끌어들이고 세션을 늘리는 데 유용합니다. 앱이 닫히고 사용자의 세션이 종료된 후 이 메서드는 배지 수를 기록합니다. ```objc (void)applicationDidEnterBackground:(UIApplication *)application ``` 이 메서드 내에서 주어진 세션 동안 사용자가 카드를 볼 때 배지 수를 적극적으로 업데이트하는 다음 코드를 구현합니다. ```objc [UIApplication sharedApplication].applicationIconBadgeNumber = [[Appboy sharedInstance].contentCardsController unviewedContentCardCount]; ``` ```swift func applicationDidEnterBackground(_ application: UIApplication) ``` 이 메서드 내에서 주어진 세션 동안 사용자가 카드를 볼 때 배지 수를 적극적으로 업데이트하는 다음 코드를 구현합니다. ```swift UIApplication.shared.applicationIconBadgeNumber = Appboy.sharedInstance()?.contentCardsController.unviewedContentCardCount() ?? 0 ``` 자세한 내용은 `Appboy.h` [헤더 파일을](https://github.com/Appboy/appboy-ios-sdk/blob/master/AppboyKit/include/Appboy.h) 참조하세요. # iOS용 콘텐츠 카드 캐러셀 보기 Source: /docs/ko/developer_guide/platforms/legacy_sdks/ios/content_cards/customization/use_cases/carousel_view/index.md
**Warning:** [AppboyKit](https://github.com/Appboy/appboy-ios-sdk) (also known as the Objective-C SDK) is no longer supported and has been replaced by the [Swift SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=swift). It will no longer receive new features, bug fixes, security updates, or technical support—however, messaging and analytics will continue to function as normal. To learn more, see [Introducing the New Braze Swift SDK](https://www.braze.com/resources/articles/introducing-the-new-braze-swift-sdk). # Use case: 캐러셀 뷰 ![샘플 뉴스 앱이 기사에서 콘텐츠 카드의 캐러셀을 보여줍니다.](https://www.braze.com/docs/ko/ko/assets/img_archive/cc_politer_carousel.png?69a1741df6c7abe2ac58322d309fe6ad){: style="max-width:35%;float:right;margin-left:15px;border:none;"} 이 섹션에서는 사용자가 수평으로 스와이프하여 추가 추천 카드를 볼 수 있는 다중 카드 캐러셀 피드를 구현하는 방법을 다룹니다. 캐러셀 뷰를 통합하려면 완전히 사용자 지정된 콘텐츠 카드 구현을 사용해야 합니다. [기기, 걷기, 달리기 접근 방식](https://www.braze.com/docs/ko/ko/user_guide/message_building_by_channel/content_cards/customize/#customization-approaches)의 '달리기' 단계에 해당합니다. 이 접근 방식을 사용하면 Braze 뷰와 기본 로직을 사용하지 않고, 대신 Braze 모델의 데이터로 채워진 자체 뷰를 사용하여 콘텐츠 카드를 완전히 커스텀 방식으로 표시하게 됩니다. 개발 노력 수준 측면에서 기본 구현과 캐러셀 구현 간의 주요 차이점은 다음과 같습니다. - 자체 보기 빌드 - 콘텐츠 카드 분석 기록 - 추가 클라이언트 측 로직을 도입하여 캐러셀에 표시할 카드 수와 종류 결정 ## 구현 ### 1단계: 커스텀 보기 컨트롤러 생성 콘텐츠 카드 캐러셀을 생성하려면, 커스텀 보기 컨트롤러(예: `UICollectionViewController`)를 생성하고 [데이터 업데이트에 가입](https://www.braze.com/docs/ko/ko/developer_guide/platform_integration_guides/legacy_sdks/ios/content_cards/integration/#getting-the-data)합니다. 기본 `ABKContentCardTableViewController`를 확장하거나 서브클래스로 만들 수 없습니다. 기본 콘텐츠 카드 유형만 처리할 수 있기 때문입니다. ### 2단계: 분석 구현 완전한 커스텀 보기 컨트롤러를 생성할 때, 콘텐츠 카드 노출 횟수, 클릭 및 해제는 자동으로 기록되지 않습니다. 노출 횟수, 해제 이벤트 및 클릭이 Braze 대시보드 분석에 제대로 기록되도록 각 분석 방법을 구현해야 합니다. 분석 방법에 대한 정보는 [카드 방법](https://www.braze.com/docs/ko/ko/developer_guide/platform_integration_guides/legacy_sdks/ios/content_cards/integration/#card-methods)을 참조하십시오. **Note:** 같은 페이지에서 보기 구현 중에 유용할 수 있는 일반 콘텐츠 카드 모델 클래스에서 상속된 다양한 속성정보도 자세히 설명합니다. ### 3단계: 콘텐츠 카드 옵저버를 생성합니다 [콘텐츠 카드 관찰자](https://www.braze.com/docs/ko/ko/developer_guide/platform_integration_guides/ios/content_cards/multiple_feeds/#step-2-set-up-a-content-card-listener)를 생성하여 콘텐츠 카드의 도착을 처리하고, 캐러셀에서 한 번에 특정 수의 카드를 표시하기 위한 조건 로직을 구현합니다. 기본적으로 콘텐츠 카드는 생성 날짜를 기준으로 정렬되며(최신 날짜부터), 사용자는 적격한 모든 카드를 볼 수 있습니다. 즉, 다양한 방식으로 추가 디스플레이 논리를 주문하고 적용할 수 있습니다. 예를 들어, 배열에서 처음 다섯 개의 콘텐츠 카드 객체를 선택하거나 데이터 모델의 `extras` 속성에 키-값 쌍을 도입하여 조건 로직을 구축할 수 있습니다. 캐러셀을 보조 콘텐츠 카드 피드로 구현하는 경우 [여러 콘텐츠 카드 피드 사용](https://www.braze.com/docs/ko/ko/developer_guide/platforms/legacy_sdks/ios/content_cards/multiple_feeds/)을 참조하여 키-값 페어에 따라 카드를 올바른 피드로 정렬하는 방법을 알아봅니다. **Important:** 마케팅 팀과 개발자 팀이 어떤 키-값 페어를 사용할지 조율하는 것이 중요합니다(예: `feed_type = brand_homepage`). 마케터가 Braze 대시보드에 입력하는 키-값 페어는 개발자가 앱 로직에 빌드하는 키-값 페어와 정확히 일치해야 하기 때문입니다. iOS 특정 개발자 설명서에서 콘텐츠 카드 클래스, 메서드 및 속성은 iOS [`ABKContentCard` 클래스 참조](https://appboy.github.io/appboy-ios-sdk/docs/interface_a_b_k_content_card.html)를 참조하세요. ## 고려사항 - 완전히 커스텀 보기를 사용하면 `ABKContentCardsController`에서 사용되는 메서드를 확장하거나 서브클래스로 작성할 수 없습니다. 대신 데이터 모델 메서드와 속성을 직접 통합해야 합니다. - 캐러셀 뷰의 로직 및 구현은 Braze의 기본 콘텐츠 카드 유형이 아니므로 사용 사례를 달성하기 위한 로직은 개발 팀에서 제공하고 지원해야 합니다. - 캐러셀에 특정 수의 카드를 한 번에 표시하도록 클라이언트 측 로직을 구현해야 합니다. # iOS용 콘텐츠 카드 피드를 새로고침 Source: /docs/ko/developer_guide/platforms/legacy_sdks/ios/content_cards/refreshing_the_feed/index.md
**Warning:** [AppboyKit](https://github.com/Appboy/appboy-ios-sdk) (also known as the Objective-C SDK) is no longer supported and has been replaced by the [Swift SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=swift). It will no longer receive new features, bug fixes, security updates, or technical support—however, messaging and analytics will continue to function as normal. To learn more, see [Introducing the New Braze Swift SDK](https://www.braze.com/resources/articles/introducing-the-new-braze-swift-sdk). # 피드를 새로고침 ## 콘텐츠 카드 새로 고침 `Appboy` 인터페이스에서 `requestContentCardsRefresh:` 방법을 사용하여 사용자의 콘텐츠 카드를 새로 고치도록 Braze에 수동으로 요청할 수 있습니다: ```objc [[Appboy sharedInstance] requestContentCardsRefresh]; ``` ```swift Appboy.sharedInstance()?.requestContentCardsRefresh() ``` 자세한 내용은 `Appboy.h` [헤더 파일을](https://github.com/Appboy/appboy-ios-sdk/blob/master/AppboyKit/include/Appboy.h) 참조하세요. # iOS용 여러 콘텐츠 카드 피드를 사용하세요 Source: /docs/ko/developer_guide/platforms/legacy_sdks/ios/content_cards/multiple_feeds/index.md
**Warning:** [AppboyKit](https://github.com/Appboy/appboy-ios-sdk) (also known as the Objective-C SDK) is no longer supported and has been replaced by the [Swift SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=swift). It will no longer receive new features, bug fixes, security updates, or technical support—however, messaging and analytics will continue to function as normal. To learn more, see [Introducing the New Braze Swift SDK](https://www.braze.com/resources/articles/introducing-the-new-braze-swift-sdk). # 여러 콘텐츠 카드 피드 사용 콘텐츠 카드는 앱에서 특정 카드만 표시하도록 필터링할 수 있으므로 다양한 사용 사례(예: 트랜잭션 피드와 마케팅 피드)별로 여러 콘텐츠 카드 피드를 지원할 수 있습니다. 다음 설명서에서는 특정 통합에 맞게 변경할 수 있는 예제 구현을 보여줍니다. ## 1단계: 카드에 키-값 쌍 설정 콘텐츠 카드 캠페인을 생성할 때 각 카드에 키-값 페어 데이터를 설정할 수 있습니다. 우리의 필터링 로직은 이 키-값 페어 데이터를 사용하여 카드를 분류할 것입니다. 이 예제에서는 `feed_type` 키와 함께 키-값 페어를 설정하여 카드를 표시해야 하는 콘텐츠 카드 피드를 지정합니다. 값은 `Transactional`, `Marketing` 등에서와 같이 커스텀 피드에 따라 달라집니다. ## 2단계: 콘텐츠 카드 리스너 설정 다음 코드 스니펫을 사용하여 콘텐츠 카드 업데이트에 대해 수신 대기할 관찰자를 추가합니다. ```objc [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(contentCardsUpdatedNotificationReceived:) name:ABKContentCardsProcessedNotification object:nil]; ``` ```swift NotificationCenter.default.addObserver(self, selector: #selector(contentCardsUpdated), name:NSNotification.Name.ABKContentCardsProcessed, object: nil) ``` 다음 메서드를 추가하여 관찰자의 업데이트에 응답하고 반환된 카드를 유형별로 필터링합니다. 첫 번째 메서드, `contentCardsUpdatedNotificationReceived:`에서는 관찰자의 업데이트를 처리합니다. 그리고 두 번째 메서드, `getCardsForFeedType:`을 원하는 피드 유형(이 경우 `Transactional`)과 함께 호출합니다. ```objc - (void)contentCardsUpdatedNotificationReceived:(NSNotification *)notification { BOOL updateIsSuccessful = [notification.userInfo[ABKContentCardsProcessedIsSuccessfulKey] boolValue]; if (updateIsSuccessful) { // Get an array containing only cards that have the "Transactional" feed type set in their extras. NSArray *filteredArray = [self getCardsForFeedType:@"Transactional"]; NSLog(@"Got filtered array of length: %lu", [filteredArray count]); // Pass filteredArray to your UI layer for display. } } - (NSArray *)getCardsForFeedType:(NSString *)type { NSArray *cards = [Appboy.sharedInstance.contentCardsController getContentCards]; NSArray *filteredArray = [cards filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(ABKContentCard * card, NSDictionary *bindings) { NSDictionary *extras = [card extras]; if (extras != nil && [extras objectForKey:@"feed_type"] != nil && [[extras objectForKey:@"feed_type"] isEqualToString:type]) { NSLog(@"Got card: %@ ", card.idString); return YES; } return NO; }]]; return filteredArray; } ``` ```swift @objc private func contentCardsUpdatedNotificationReceived(notification: NSNotification) { guard let updateSuccessful = notification.userInfo?[ABKContentCardsProcessedIsSuccessfulKey] as? Bool else { return } if updateSuccessful { // Get an array containing only cards that have the "Transactional" feed type set in their extras. let filteredArray = getCards(forFeedType: "Transactional") NSLog("Got filtered array of length: %@",filteredArray?.count ?? 0) // Pass filteredArray to your UI layer for display. } } func getCards(forFeedType type: String) -> [ABKContentCard]? { guard let allCards = Appboy.sharedInstance()?.contentCardsController.contentCards as? [ABKContentCard] else { return nil } // return filtered cards return allCards.filter { if $0.extras?["feed_type"] as? String == type { NSLog("%@","Got card: \($0.idString)") return true } else { return false } } } ``` # iOS용 Content Cards 구현 가이드(선택 사항) Source: /docs/ko/developer_guide/platforms/legacy_sdks/ios/content_cards/implementation_guide/index.md
**Warning:** [AppboyKit](https://github.com/Appboy/appboy-ios-sdk) (also known as the Objective-C SDK) is no longer supported and has been replaced by the [Swift SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=swift). It will no longer receive new features, bug fixes, security updates, or technical support—however, messaging and analytics will continue to function as normal. To learn more, see [Introducing the New Braze Swift SDK](https://www.braze.com/resources/articles/introducing-the-new-braze-swift-sdk).
**Important:** 기본 Content Cards 개발자 통합 가이드를 찾고 계신가요? [여기](https://www.braze.com/docs/ko/ko/developer_guide/platforms/legacy_sdks/ios/content_cards/integration/)에서 확인하세요. # Content Cards 구현 가이드 {#content-card-implementation-guide} > 이 고급 구현 가이드(선택 사항)에서는 Content Cards 코드 고려사항, 저희 팀이 구축한 세 가지 커스텀 사용 사례, 함께 제공되는 코드 스니펫, 노출 횟수, 클릭 및 해제 로깅에 대한 지침을 다룹니다. [여기에서](https://github.com/braze-inc/braze-growth-shares-ios-demo-app) Braze 데모 리포지토리를 방문하세요! 이 구현 가이드는 Swift 구현을 중심으로 하지만 관심 있는 분을 위해 Objective-C 스니펫도 제공됩니다. ## 코드 고려사항 {#code-considerations} ### 커스텀 오브젝트로서의 Content Cards {#content-cards-as-custom-objects} 로켓에 부스터를 추가하는 것처럼, 커스텀 오브젝트를 Content Cards로 작동하도록 확장할 수 있습니다. 이와 같은 제한된 API 표면은 서로 다른 데이터 백엔드를 상호 호환적으로 함께 사용할 수 있는 유연성을 제공합니다. 다음 코드 스니펫에서 볼 수 있듯이 `ContentCardable` 프로토콜을 준수하고 초기화 기능을 구현함으로써 이를 수행할 수 있으며, `ContentCardData` 구조를 사용하여 `ABKContentCard` 데이터에 접근할 수 있습니다. `ABKContentCard` 페이로드는 `ContentCardData` 구조와 커스텀 오브젝트 자체를 초기화하는 데 사용되며, 모두 프로토콜과 함께 제공되는 초기화 기능을 통해 `Dictionary` 유형에서 가져옵니다. 초기화 기능에는 `ContentCardClassType` 열거형도 포함됩니다. 이 열거형은 초기화할 오브젝트를 결정하는 데 사용됩니다. Braze 대시보드 내에서 키-값 페어를 사용하여 초기화할 오브젝트를 결정하는 데 사용할 명시적 `class_type` 키를 설정할 수 있습니다. Content Cards의 이러한 키-값 페어는 `ABKContentCard`의 `extras` 변수로 전달됩니다. 초기화 기능의 또 다른 핵심 구성요소는 `metaData` 사전 매개변수입니다. `metaData`에는 `ABKContentCard`에서 구문 분석된 모든 내용이 일련의 키와 값으로 포함됩니다. 관련 카드를 구문 분석하고 커스텀 오브젝트로 변환한 후, 앱은 JSON 또는 다른 소스에서 인스턴스화된 것처럼 해당 오브젝트와 함께 작업을 시작할 준비를 마칩니다. 이러한 코드 고려사항을 확실히 이해했다면 [사용 사례](#sample-use-cases)를 참조하여 커스텀 오브젝트 구현을 시작하세요. **ContentCardable 프로토콜**
`ContentCardData` 오브젝트는 `ContentCardClassType` 열거형과 함께 `ABKContentCard` 데이터를 나타냅니다. `ABKContentCard` 메타데이터로 커스텀 오브젝트를 인스턴스화하는 데 사용되는 초기화 기능입니다. ```swift protocol ContentCardable { var contentCardData: ContentCardData? { get } init?(metaData: [ContentCardKey: Any], classType contentCardClassType: ContentCardClassType) } extension ContentCardable { var isContentCard: Bool { return contentCardData != nil } func logContentCardClicked() { BrazeManager.shared.logContentCardClicked(idString: contentCardData?.contentCardId) } func logContentCardDismissed() { BrazeManager.shared.logContentCardDismissed(idString: contentCardData?.contentCardId) } func logContentCardImpression() { BrazeManager.shared.logContentCardImpression(idString: contentCardData?.contentCardId) } } ``` **Content Cards 데이터 구조**
`ContentCardData`는 `ABKContentCard`의 구문 분석된 값을 나타냅니다. ```swift struct ContentCardData: Hashable { let contentCardId: String let contentCardClassType: ContentCardClassType let createdAt: Double let isDismissable: Bool ... // other Content Card properties such as expiresAt, pinned, etc. } extension ContentCardData: Equatable { static func ==(lhs: ContentCardData, rhs: ContentCardData) -> Bool { return lhs.contentCardId == rhs.contentCardId } } ``` **ContentCardable 프로토콜**
`ContentCardData` 오브젝트는 `ContentCardClassType` 열거형과 함께 `ABKContentCard` 데이터를 나타내며, `ABKContentCard` 메타데이터로 커스텀 오브젝트를 인스턴스화하는 데 사용되는 초기화 기능입니다. ```objc @protocol ContentCardable @property (nonatomic, strong) ContentCardData *contentCardData; - (instancetype __nullable)initWithMetaData:(NSDictionary *)metaData classType:(enum ContentCardClassType)classType; - (BOOL)isContentCard; - (void)logContentCardImpression; - (void)logContentCardClicked; - (void)logContentCardDismissed; @end ``` **Content Cards 데이터 구조**
`ContentCardData`는 `ABKContentCard`의 구문 분석된 값을 나타냅니다. ```objc @interface ContentCardData : NSObject + (ContentCardClassType)contentCardClassTypeForString:(NSString *)rawValue; - (instancetype)initWithIdString:(NSString *)idString classType:(ContentCardClassType)classType createdAt:(double)createdAt isDismissible:(BOOL)isDismissible; @property (nonatomic, readonly) NSString *contentCardId; @property (nonatomic) ContentCardClassType classType; @property (nonatomic, readonly) double *createdAt; @property (nonatomic, readonly) BOOL isDismissible; ... // other Content Card properties such as expiresAt, pinned, etc. @end ``` **커스텀 오브젝트 초기화 기능**
`ABKContentCard`의 메타데이터는 오브젝트의 변수를 채우는 데 사용됩니다. Braze 대시보드에 설정된 키-값 페어는 "extras" 사전에 표시됩니다. ```swift extension CustomObject: ContentCardable { init?(metaData: [ContentCardKey: Any], classType contentCardClassType: ContentCardClassType) { guard let idString = metaData[.idString] as? String, let createdAt = metaData[.created] as? Double, let isDismissable = metaData[.dismissable] as? Bool, let extras = metaData[.extras] as? [AnyHashable: Any], else { return nil } let contentCardData = ContentCardData(contentCardId: idString, contentCardClassType: contentCardClassType, createdAt: createdAt, isDismissable: isDismissable) let customObjectProperty = extras["YOUR-CUSTOM-OBJECT-PROPERTY"] as? String self.init(contentCardData: contentCardData, property: customObjectProperty) } } ``` **유형 식별**
`ContentCardClassType` 열거형은 Braze 대시보드의 `class_type` 값을 나타냅니다. 이 값은 필터 식별자로도 사용되어 다양한 위치에서 Content Cards를 표시합니다. ```swift enum ContentCardClassType: Hashable { case yourValue case yourOtherValue ... case none init(rawType: String?) { switch rawType?.lowercased() { case "your_value": // these values much match the value set in the Braze dashboard self = .yourValue case "your_other_value": // these values much match the value set in the Braze dashboard self = .yourOtherValue ... default: self = .none } } } ``` **커스텀 오브젝트 초기화 기능**
`ABKContentCard`의 메타데이터는 오브젝트의 변수를 채우는 데 사용됩니다. Braze 대시보드에 설정된 키-값 페어는 "extras" 사전에 표시됩니다. ```objc - (id _Nullable)initWithMetaData:(nonnull NSDictionary *)metaData classType:(enum ContentCardClassType)classType { self = [super init]; if (self) { if ([metaData objectForKey:ContentCardKeyIdString] && [metaData objectForKey:ContentCardKeyCreated] && [metaData objectForKey:ContentCardKeyDismissible] && [metaData objectForKey:ContentCardKeyExtras]) { NSDictionary *extras = metaData[ContentCardKeyExtras]; NSString *idString = metaData[ContentCardKeyIdString]; double createdAt = [metaData[ContentCardKeyCreated] doubleValue]; BOOL isDismissible = metaData[ContentCardKeyDismissible]; if ([extras objectForKey: @"YOUR-CUSTOM-PROPERTY") _customObjectProperty = extras[@"YOUR-CUSTOM-OBJECT-PROPERTY"]; self.contentCardData = [[ContentCardData alloc] initWithIdString:idString classType:classType createdAt:createdAt isDismissible:isDismissible]; return self; } } return nil; } ``` **유형 식별**
`ContentCardClassType` 열거형은 Braze 대시보드의 `class_type` 값을 나타냅니다. 이 값은 필터 식별자로도 사용되어 다양한 위치에서 Content Cards를 표시합니다. ```objc typedef NS_ENUM(NSInteger, ContentCardClassType) { ContentCardClassTypeNone = 0, ContentCardClassTypeYourValue, ContentCardClassTypeYourOtherValue, ... }; + (NSArray *)contentCardClassTypeArray { return @[ @"", @"your_value", @"your_other_value" ]; } + (ContentCardClassType)contentCardClassTypeForString:(NSString*)rawValue { if ([[self contentCardClassTypeArray] indexOfObject:rawValue] == NSNotFound) { return ContentCardClassTypeNone; } else { NSInteger value = [[self contentCardClassTypeArray] indexOfObject:rawValue]; return (ContentCardClassType) value; } } ``` **Content Cards 요청**
관찰자가 여전히 메모리에 유지되는 한, Braze SDK의 알림 콜백을 수신할 수 있습니다. ```swift func loadContentCards() { BrazeManager.shared.addObserverForContentCards(observer: self, selector: #selector(contentCardsUpdated)) BrazeManager.shared.requestContentCardsRefresh() } ``` **Content Cards SDK 콜백 처리**
알림 콜백을 헬퍼 파일로 전달하여 커스텀 오브젝트에 대한 페이로드 데이터를 구문 분석합니다. ```swift @objc func contentCardsUpdated(_ notification: Notification) { guard let contentCards = BrazeManager.shared.handleContentCardsUpdated(notification, for: [.yourValue]) as? [CustomObject],!contentCards.isEmpty else { return } // do something with your array of custom objects } ``` **Content Cards 작업**
`class_type`은 필터로 전달되어 `class_type`이 일치하는 Content Cards만 반환합니다. ```swift func handleContentCardsUpdated(_ notification: Notification, for classTypes: [ContentCardClassType]) -> [ContentCardable] { guard let updateIsSuccessful = notification.userInfo?[ABKContentCardsProcessedIsSuccessfulKey] as? Bool, updateIsSuccessful, let cards = contentCards else { return [] } return convertContentCards(cards, for: classTypes) } ``` **Content Cards 요청**
관찰자가 여전히 메모리에 유지되는 한, Braze SDK의 알림 콜백을 수신할 수 있습니다. ```objc - (void)loadContentCards { [[BrazeManager shared] addObserverForContentCards:self selector:@selector(contentCardsUpdated:)]; [[BrazeManager shared] requestContentCardsRefresh]; } ``` **Content Cards SDK 콜백 처리**
알림 콜백을 헬퍼 파일로 전달하여 커스텀 오브젝트에 대한 페이로드 데이터를 구문 분석합니다. ```objc - (void)contentCardsUpdated:(NSNotification *)notification { NSArray *classTypes = @[@(ContentCardClassTypeYourValue)]; NSArray *contentCards = [[BrazeManager shared] handleContentCardsUpdated:notification forClassTypes:classTypes]; // do something with your array of custom objects } ``` **Content Cards 작업**
`class_type`은 필터로 전달되어 `class_type`이 일치하는 Content Cards만 반환합니다. ```objc - (NSArray *)handleContentCardsUpdated:(NSNotification *)notification forClassType:(ContentCardClassType)classType { BOOL updateIsSuccessful = [notification.userInfo[ABKContentCardsProcessedIsSuccessfulKey] boolValue]; if (updateIsSuccessful) { return [self convertContentCards:self.contentCards forClassType:classType]; } else { return @[]; } } ``` **페이로드 데이터 작업**
Content Cards 배열을 반복하고 `class_type`이 일치하는 카드만 구문 분석합니다. ABKContentCard의 페이로드는 `Dictionary`로 구문 분석됩니다. ```swift func convertContentCards(_ cards: [ABKContentCard], for classTypes: [ContentCardClassType]) -> [ContentCardable] { var contentCardables: [ContentCardable] = [] for card in cards { let classTypeString = card.extras?[ContentCardKey.classType.rawValue] as? String let classType = ContentCardClassType(rawType: classTypeString) guard classTypes.contains(classType) else { continue } var metaData: [ContentCardKey: Any] = [:] switch card { case let banner as ABKBannerContentCard: metaData[.image] = banner.image case let captioned as ABKCaptionedImageContentCard: metaData[.title] = captioned.title metaData[.cardDescription] = captioned.cardDescription metaData[.image] = captioned.image case let classic as ABKClassicContentCard: metaData[.title] = classic.title metaData[.cardDescription] = classic.cardDescription default: break } metaData[.idString] = card.idString metaData[.created] = card.created metaData[.dismissible] = card.dismissible metaData[.urlString] = card.urlString metaData[.extras] = card.extras ... // other Content Card properties such as expiresAt, pinned, etc. if let contentCardable = contentCardable(with: metaData, for: classType) { contentCardables.append(contentCardable) } } return contentCardables } ``` **Content Cards 페이로드 데이터에서 커스텀 오브젝트 초기화**
`class_type`은 페이로드 데이터에서 초기화할 커스텀 오브젝트를 결정하는 데 사용됩니다. ```swift func contentCardable(with metaData: [ContentCardKey: Any], for classType: ContentCardClassType) -> ContentCardable? { switch classType { case .yourValue: return CustomObject(metaData: metaData, classType: classType) case .yourOtherValue: return OtherCustomObject(metaData: metaData, classType: classType) ... default: return nil } } ``` **페이로드 데이터 작업**
Content Cards 배열을 반복하고 `class_type`이 일치하는 카드만 구문 분석합니다. ABKContentCard의 페이로드는 `Dictionary`로 구문 분석됩니다. ```objc - (NSArray *)convertContentCards:(NSArray *)cards forClassType:(ContentCardClassType)classType { NSMutableArray *contentCardables = [[NSMutableArray alloc] init]; for (ABKContentCard *card in cards) { NSString *classTypeString = [card.extras objectForKey:ContentCardKeyClassType]; ContentCardClassType cardClassType = [ContentCardData contentCardClassTypeForString: classTypeString]; if (cardClassType != classType) { continue; } NSMutableDictionary *metaData = [[NSMutableDictionary alloc] init]; if ([card isKindOfClass:[ABKBannerContentCard class]]) { ABKBannerContentCard *banner = (ABKBannerContentCard *)card; metaData[ContentCardKeyImage] = banner.image; } else if ([card isKindOfClass:[ABKCaptionedImageContentCard class]]) { ABKCaptionedImageContentCard *captioned = (ABKCaptionedImageContentCard *)card; metaData[ContentCardKeyTitle] = captioned.title; metaData[ContentCardKeyCardDescription] = captioned.cardDescription; metaData[ContentCardKeyImage] = captioned.image; } else if ([card isKindOfClass:[ABKClassicContentCard class]]) { ABKClassicContentCard *classic = (ABKClassicContentCard *)card; metaData[ContentCardKeyCardDescription] = classic.title; metaData[ContentCardKeyImage] = classic.image; } metaData[ContentCardKeyIdString] = card.idString; metaData[ContentCardKeyCreated] = [NSNumber numberWithDouble:card.created]; metaData[ContentCardKeyDismissible] = [NSNumber numberWithBool:card.dismissible]; metaData[ContentCardKeyUrlString] = card.urlString; metaData[ContentCardKeyExtras] = card.extras; ... // other Content Card properties such as expiresAt, pinned, etc. id contentCardable = [self contentCardableWithMetaData:metaData forClassType:classType]; if (contentCardable) { [contentCardables addObject:contentCardable]; } } return contentCardables; } ``` **Content Cards 페이로드 데이터에서 커스텀 오브젝트 초기화**
`class_type`은 페이로드 데이터에서 초기화할 커스텀 오브젝트를 결정하는 데 사용됩니다. ```obj-c - (id)contentCardableWithMetaData:(NSDictionary *)metaData forClassType:(ContentCardClassType)classType { switch (classType) { case ContentCardClassTypeYourValue: return [[CustomObject alloc] initWithMetaData:metaData classType:classType]; case ContentCardClassTypeYourOtherValue: return nil; ... default: return nil; } } ``` ## 사용 사례 {#sample-use-cases} 아래에 세 가지 사용 사례를 제공합니다. 각 사용 사례는 자세한 설명, 관련 코드 스니펫, 그리고 Braze 대시보드에서 Content Cards 변수가 어떻게 표시되고 사용되는지에 대한 안내를 포함합니다. - [보조 콘텐츠로서의 Content Cards](#content-cards-as-supplemental-content) - [메시지 센터의 Content Cards](#content-cards-in-a-message-center) - [인터랙티브 Content Cards](#interactive-content-cards) ### 보조 콘텐츠로서의 Content Cards {#content-cards-as-supplemental-content} ![](https://www.braze.com/docs/ko/ko/assets/img/cc_implementation/supplementary.png?04f6645c99fe71ac7abb4cba8a46ccbd){: style="float:right;max-width:25%;margin-left:15px;border:0;"} Content Cards를 기존 피드에 원활하게 혼합하여 여러 피드의 데이터를 동시에 로드할 수 있습니다. 이를 통해 Braze Content Cards와 기존 피드 콘텐츠가 일관되고 조화로운 경험을 만들어냅니다. 오른쪽 예제는 로컬 데이터와 Braze에서 제공하는 Content Cards를 통해 채워지는 하이브리드 항목 목록이 포함된 `UICollectionView`를 보여줍니다. 이를 통해 Content Cards를 기존 콘텐츠와 구분할 수 없게 만들 수 있습니다. #### 대시보드 구성 {#dashboard-configuration} 이 Content Cards는 API 트리거 키-값 페어를 사용하여 API 트리거 Campaign에 의해 전달됩니다. 이것은 카드의 값이 사용자에게 표시할 콘텐츠를 결정하기 위해 외부 요인에 따라 달라지는 Campaign에 이상적입니다. 설정 시 `class_type`을 알아야 합니다. ![보조 Content Cards 사용 사례에 대한 키-값 페어입니다. 이 예제에서는 카드의 다양한 측면이 Liquid를 사용하여 "tile_id", "tile_deeplink", "tile_title"와 같이 설정됩니다.](https://www.braze.com/docs/ko/ko/assets/img/cc_implementation/supplementary_content.png?1b9481939960e31dc1b1e5ef57d51b68){: style="max-width:60%;"} ##### 분석을 기록할 준비가 되셨나요? {#ready-to-log-analytics} 데이터 흐름이 어떻게 진행되는지 더 잘 이해하려면 [다음 섹션](#logging-impressions-clicks-and-dismissals)을 참조하세요. ### 메시지 센터의 Content Cards {#content-cards-in-a-message-center}
Content Cards는 각 메시지가 자체 카드인 메시지 센터 형식으로 사용할 수 있습니다. 메시지 센터의 각 메시지는 Content Cards 페이로드를 통해 채워지며, 각 카드는 클릭 시 UI/UX를 활성화하는 추가 키-값 페어를 포함합니다. 다음 예제에서 하나의 메시지는 임의의 커스텀 보기로 안내하고, 다른 하나는 커스텀 HTML을 표시하는 웹뷰로 열립니다. ![](https://www.braze.com/docs/ko/ko/assets/img/cc_implementation/message_center.png?ec1fb31a68a7f9918c609dac71b1408d){: style="border:0;"}{: style="max-width:80%;border:0"} #### 대시보드 구성 {#dashboard-configuration} 다음 메시지 유형의 경우 키-값 페어 `class_type`을 대시보드 구성에 추가해야 합니다. 여기에 할당된 값은 임의적이지만 클래스 유형 간에 구별할 수 있어야 합니다. 이러한 키-값 페어는 사용자가 요약된 받은편지함 메시지를 클릭할 때 이동 위치를 결정할 때 애플리케이션이 확인하는 핵심 식별자입니다. 이 사용 사례에 대한 키-값 페어는 다음과 같습니다: - `message_header`를 `Full Page`로 설정 - `class_type`을 `message_full_page`로 설정 ![](https://www.braze.com/docs/ko/ko/assets/img/cc_implementation/full_page.png?a9d79abfd4496252706f34aec0a33d17){: style="max-width:60%;"} 이 사용 사례에 대한 키-값 페어는 다음과 같습니다: - `message_header`를 `HTML`로 설정 - `class_type`을 `message_webview`로 설정 - `message_title` 이 메시지는 HTML 키-값 페어도 찾지만, 웹 도메인으로 작업하는 경우 URL 키-값 페어도 유효합니다. ![](https://www.braze.com/docs/ko/ko/assets/img/cc_implementation/html_webview.png?1264dbf1823a4a6168d88108139f1221){: style="max-width:60%;"} #### 추가 설명 {#further-explanation} 메시지 센터 로직은 Braze의 키-값 페어에서 제공하는 `contentCardClassType`에 의해 구동됩니다. `addContentCardToView` 메서드를 사용하면 이러한 클래스 유형을 필터링하고 식별할 수 있습니다. **클릭 시 동작에 `class_type` 사용**
메시지가 클릭되면 `ContentCardClassType`이 다음 화면을 채우는 방법을 처리합니다. ```swift func addContentCardToView(with message: Message) { switch message.contentCardData?.contentCardClassType { case .message(.fullPage): loadContentCardFullPageView(with: message as! FullPageMessage) case .message(.webView): loadContentCardWebView(with: message as! WebViewMessage) default: break } } ``` **클릭 시 동작에 `class_type` 사용**
메시지가 클릭되면 `ContentCardClassType`이 다음 화면을 채우는 방법을 처리합니다. ```objc - (void)addContentCardToView:(Message *)message { switch (message.contentCardData.classType) { case ContentCardClassTypeMessageFullPage: [self loadContentCardFullPageView:(FullPageMessage *)message]; break; case ContentCardClassTypeMessageWebview: [self loadContentCardWebView:(WebViewMessage *)message]; break; default: break; } } ``` ##### 분석을 기록할 준비가 되셨나요? {#ready-to-log-analytics} 데이터 흐름이 어떻게 진행되는지 더 잘 이해하려면 [다음 섹션](#logging-impressions-clicks-and-dismissals)을 참조하세요. ![화면 왼쪽 하단에 50% 프로모션을 표시하는 인터랙티브 Content Cards가 나타납니다. 클릭하면 프로모션이 장바구니에 적용됩니다.](https://www.braze.com/docs/ko/ko/assets/img/cc_implementation/discount2.png?28bacc3995ff935635bb24b7bb547e1b){: style="border:0;"}{: style="float:right;max-width:45%;border:0;margin-left:15px;"} ### 인터랙티브 Content Cards {#interactive-content-cards}
Content Cards를 활용하여 사용자를 위한 역동적이고 인터랙티브한 경험을 만들 수 있습니다. 오른쪽 예제에서는 결제 시 Content Cards 팝업이 표시되어 사용자에게 막바지 프로모션을 제공합니다. 이와 같이 잘 배치된 카드는 특정 사용자 동작을 유도하는 좋은 방법입니다.


#### 대시보드 구성 {#dashboard-configuration} 인터랙티브 Content Cards의 대시보드 구성은 간단합니다. 이 사용 사례의 키-값 페어에는 원하는 할인 금액으로 설정된 `discount_percentage`와 `coupon_code`로 설정된 `class_type`이 포함됩니다. 이러한 키-값 페어에 따라 유형별 Content Cards가 필터링되고 결제 화면에 표시되는 방식이 결정됩니다. ![](https://www.braze.com/docs/ko/ko/assets/img/cc_implementation/discount.png?0f629adf0e5b56e0d2dc52b0b9f3e087){: style="max-width:70%;"} ##### 분석을 기록할 준비가 되셨나요? {#ready-to-log-analytics} 데이터 흐름이 어떻게 진행되는지 더 잘 이해하려면 [다음 섹션](#logging-impressions-clicks-and-dismissals)을 참조하세요. ## 다크 모드 커스터마이징 {#dark-mode-customization} 기본적으로 Content Cards 보기는 테마 색상 세트를 통해 기기의 다크 모드 변경에 자동으로 대응합니다. 이 동작은 [커스텀 스타일 가이드](https://www.braze.com/docs/ko/ko/developer_guide/platform_integration_guides/ios/content_cards/customization/custom_styling/#disabling-dark-mode)에 자세히 설명된 대로 재정의할 수 있습니다. ## 노출 횟수, 클릭, 해제 기록 {#logging-impressions-clicks-and-dismissals} 커스텀 오브젝트를 Content Cards로 작동하도록 확장한 후에는 노출 횟수, 클릭, 해제와 같은 중요한 측정기준을 빠르게 기록할 수 있습니다. 이는 Braze SDK에 의해 기록될 헬퍼 파일에 데이터를 참조하고 제공하는 `ContentCardable` 프로토콜을 사용하여 수행할 수 있습니다. #### 구현 구성요소

{#implementation-components} **분석 로깅**
로깅 메서드는 `ContentCardable` 프로토콜을 준수하는 오브젝트에서 직접 호출할 수 있습니다. ```swift customObject.logContentCardImpression() customObject.logContentCardClicked() customObject.logContentCardDismissed() ``` **`ABKContentCard` 검색**
커스텀 오브젝트에서 전달된 `idString`은 분석을 기록하기 위해 관련 Content Cards를 식별하는 데 사용됩니다. ```swift extension BrazeManager { func logContentCardImpression(idString: String?) { guard let contentCard = getContentCard(forString: idString) else { return } contentCard.logContentCardImpression() } private func getContentCard(forString idString: String?) -> ABKContentCard? { return contentCards?.first(where: { $0.idString == idString }) } } ``` **분석 로깅**
로깅 메서드는 `ContentCardable` 프로토콜을 준수하는 오브젝트에서 직접 호출할 수 있습니다. ```objc [customObject logContentCardImpression]; [customObject logContentCardClicked]; [customObject logContentCardDismissed]; ``` **`ABKContentCard` 검색**
커스텀 오브젝트에서 전달된 `idString`은 분석을 기록하기 위해 관련 Content Cards를 식별하는 데 사용됩니다. ```objc - (void)logContentCardImpression:(NSString *)idString { ABKContentCard *contentCard = [self getContentCard:idString]; [contentCard logContentCardImpression]; } - (ABKContentCard *)getContentCard:(NSString *)idString { NSPredicate *predicate = [NSPredicate predicateWithFormat:@"self.idString == %@", idString]; NSArray *filteredArray = [self.contentCards filteredArrayUsingPredicate:predicate]; return filteredArray.firstObject; } ``` **Important:** 제어 배리언트 Content Cards의 경우 커스텀 오브젝트는 여전히 인스턴스화되어야 하며, UI 로직은 오브젝트의 해당 보기를 숨김으로 설정해야 합니다. 그런 다음 오브젝트는 사용자가 제어 카드를 보았을 때를 분석에 알리기 위해 노출 횟수를 기록할 수 있습니다. ## 헬퍼 파일 {#helper-files} **ContentCardKey 헬퍼 파일** ```swift enum ContentCardKey: String { case idString case created case classType = "class_type" case dismissible case extras ... } ``` ```objc static NSString *const ContentCardKeyIdString = @"idString"; static NSString *const ContentCardKeyCreated = @"created"; static NSString *const ContentCardKeyClassType = @"class_type"; static NSString *const ContentCardKeyDismissible = @"dismissible"; static NSString *const ContentCardKeyExtras = @"extras"; ... ``` # iOS용 트랙 세션 Source: /docs/ko/developer_guide/platforms/legacy_sdks/ios/analytics/tracking_sessions/index.md
**Warning:** [AppboyKit](https://github.com/Appboy/appboy-ios-sdk) (also known as the Objective-C SDK) is no longer supported and has been replaced by the [Swift SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=swift). It will no longer receive new features, bug fixes, security updates, or technical support—however, messaging and analytics will continue to function as normal. To learn more, see [Introducing the New Braze Swift SDK](https://www.braze.com/resources/articles/introducing-the-new-braze-swift-sdk). # iOS용 세션 추적 Braze SDK는 사용자 인게이지먼트를 계산하기 위해 Braze 대시보드에서 사용하는 세션 데이터 및 사용자를 이해하는 데 핵심적인 기타 분석을 보고합니다. SDK는 다음 세션 의미 체계에 따라 Braze 대시보드 내에서 볼 수 있는 세션 길이와 세션 수를 설명하는 '세션 시작' 및 '세션 종료' 데이터 포인트를 생성합니다. ## 세션 수명 주기 `[[Appboy sharedInstance]` `startWithApiKey:inApplication:withLaunchOptions:withAppboyOptions]`를 호출하면 세션이 시작되며, 이후 기본적으로 세션은 `UIApplicationWillEnterForegroundNotification` 알림이 실행될 때(예: 앱이 포그라운드에 표시될 때) 시작되고 앱이 포그라운드를 벗어날 때(예: `UIApplicationDidEnterBackgroundNotification` 알림이 실행되거나 앱이 종료될 때) 종료됩니다. **Note:** 새 세션을 강제로 시작해야 하는 경우 사용자를 변경하면 됩니다. ## 세션 시간 초과 사용자 지정 Braze iOS SDK v3.14.1부터는 Info.plist 파일을 사용하여 세션 제한 시간을 설정할 수 있습니다. `Info.plist` 파일에 `Braze` 사전을 추가합니다. `Braze` 사전에서 `SessionTimeout` 숫자 하위 항목을 추가하고 값을 커스텀 세션 제한 시간으로 설정합니다. Braze iOS SDK v4.0.2 이전 버전에서는 `Braze` 대신 `Appboy`의 사전 키를 사용해야 합니다. 또는 [`startWithApiKey`](https://appboy.github.io/appboy-ios-sdk/docs/interface_appboy.html#afd911d60dfe7e5361afbfb364f5d20f9)에 전달된 `appboyOptions` 오브젝트에서 `ABKSessionTimeoutKey` 키를 원하는 정수 값으로 설정할 수 있습니다. ```objc // Sets the session timeout to 60 seconds [Appboy startWithApiKey:@"YOUR-API_KEY" inApplication:application withLaunchOptions:options withAppboyOptions:@{ ABKSessionTimeoutKey : @(60) }]; ``` ```swift // Sets the session timeout to 60 seconds Appboy.start(withApiKey: "YOUR-API-KEY", in:application, withLaunchOptions:launchOptions, withAppboyOptions:[ ABKSessionTimeoutKey : 60 ]) ``` 세션 시간 제한을 설정한 경우 세션 시맨틱은 모두 해당 사용자 지정 시간 제한으로 확장됩니다. **Note:** `sessionTimeoutInSeconds` 의 최소값은 1초입니다. 기본값은 10초입니다. ## 테스트 세션 추적 사용자를 통해 세션을 감지하려면 대시보드에서 사용자를 찾고 고객 프로필에서 **앱 사용**으로 이동합니다. '세션' 측정기준이 예상했던 시점에 증가하는지 확인하여 세션 추적 기술이 작동함을 확인할 수 있습니다. ![고객 프로필의 앱 사용 섹션으로, 세션 수, 마지막 사용 날짜 및 첫 사용 날짜를 표시합니다.](https://www.braze.com/docs/ko/ko/assets/img_archive/test_session.png?0428888ea3bd01a486d8c674fb973747) # iOS용 사용자 ID 설정 Source: /docs/ko/developer_guide/platforms/legacy_sdks/ios/analytics/setting_user_ids/index.md
**Warning:** [AppboyKit](https://github.com/Appboy/appboy-ios-sdk) (also known as the Objective-C SDK) is no longer supported and has been replaced by the [Swift SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=swift). It will no longer receive new features, bug fixes, security updates, or technical support—however, messaging and analytics will continue to function as normal. To learn more, see [Introducing the New Braze Swift SDK](https://www.braze.com/resources/articles/introducing-the-new-braze-swift-sdk). # iOS 사용자 ID 설정 User IDs should be set for each of your users. These should be unchanging and accessible when a user opens the app. Naming your user IDs correctly from the start is one of the most **crucial** steps when setting up user IDs. We strongly suggest using the Braze standard of UUIDs and GUIDs (detailed below). We also strongly recommend providing this identifier as it will allow you to: - Track your users across devices and platforms, improving the quality of your behavioral and demographic data. - Import data about your users using our [user data API](https://www.braze.com/docs/ko/ko/developer_guide/rest_api/user_data/#user-data). - Target specific users with our [messaging API](https://www.braze.com/docs/ko/ko/api/endpoints/messaging/) for both general and transactional messages. **Note:** If such an identifier is not available, Braze will assign a unique identifier to your users, but you will lack the capabilities listed for user IDs. You should avoid setting user IDs for users for whom you lack a unique identifier that is tied to them as an individual. Passing a device identifier offers no benefit versus the automatic anonymous user tracking Braze offers by default. **Warning:** If you want to include an identifiable value as your user ID, for additional security, we **strongly recommend** adding our [SDK authentication](https://www.braze.com/docs/ko/ko/developer_guide/authentication/) feature to prevent user impersonation. ## 추천 사용자 ID 명명 규칙 At Braze, we **strongly recommend** naming user IDs, also referred to as external IDs, in a [UUIDs and GUIDs](https://en.wikipedia.org/wiki/Universally_unique_identifier) format. UUIDs and GUIDs are universally unique identifiers that consist of a 128-bit number used to identify information in computer systems. This means that these UUIDs are long, random and well distributed. If you choose a different method in which to name your user IDs, they must also be long, random and well distributed. It is also important to note, that user IDs are **case sensitive**. For example, "Abcdef" is a different user from "abcdef". If you find your user IDs include names, email addresses, timestamps, or incrementors, we suggest using a new naming method that is more secure so that your user IDs are not as easy to guess or impersonate. If you choose to include this in your user IDs, we **strongly recommend** adding our [SDK authentication](https://www.braze.com/docs/ko/ko/developer_guide/authentication/) feature to prevent user impersonation. Providing this information to others may allow people outside your organization to glean information on how your user IDs are structured, opening up your organization to potentially malicious updates or removal of information. Choosing the correct naming convention from the start is one of the most important steps in setting up user IDs. However, a migration is possible using our [external ID migration endpoint](https://www.braze.com/docs/ko/ko/api/endpoints/user_data/external_id_migration/). | User ID Naming | | Recommended | Not Recommended | | ------------ | ----------- | | 123e4567-e89b-12d3-a456-836199333115 | JonDoe829525552 | | 8c0b3728-7fa7-4c68-a32e-12de1d3ed2d5 | Anna@email.com | | f0a9b506-3c5b-4d86-b16a-94fc4fc3f7b0 | CompanyName-1-2-19 | | 2d9e96a1-8f15-4eaf-bf7b-eb8c34e25962 | jon-doe-1-2-19 | {: .reset-td-br-1 .reset-td-br-2 aria-label="Table" } ## 사용자 ID 할당 사용자가 식별되는 즉시(일반적으로 로그인 후) 다음 호출을 수행하여 사용자 ID를 설정해야 합니다. ```objc [[Appboy sharedInstance] changeUser:@"YOUR_USER_ID_STRING"]; ``` ```swift Appboy.sharedInstance()?.changeUser("YOUR_USER_ID") ``` **Warning:** **사용자가 로그아웃할 때 `changeUser()`를 호출하지 마세요. `changeUser()`는 사용자가 애플리케이션에 로그인할 때만 호출해야 합니다.** [`changeUser()`](https://appboy.github.io/appboy-ios-sdk/docs/interface_appboy.html#ac8b369b40e15860b0ec18c0f4b46ac69%20%22changeuser%22)를 정적 기본값으로 설정하면 사용자가 다시 로그인할 때까지 모든 사용자 활동이 해당 기본값 '사용자'와 연결됩니다. 애플리케이션의 메인 스레드에서 이 메서드를 호출해야 합니다. 메서드를 비동기적으로 호출하면 정의되지 않은 동작이 발생할 수 있습니다. 또한 사용자가 로그아웃할 때 사용자 ID를 변경하면 이전에 로그인한 사용자를 리인게이지먼트 캠페인으로 타겟팅할 수 없게 되므로 사용자 ID를 변경하지 않는 것이 좋습니다. 여러 사용자가 동일한 기기를 사용할 것으로 예상되지만 앱에서 로그아웃한 상태일 때 사용자 중 한 명만을 타겟팅하려는 경우, 로그아웃 상태에서 타겟팅하려는 사용자 ID를 별도로 추적하고 앱의 로그아웃 프로세스의 일환으로 해당 사용자 ID로 전환하는 방법을 권장합니다. ## 사용자 ID 통합 모범 사례 및 참고 사항 ### Automatic preservation of anonymous user history | Identification Context | Preservation Behavior | | ---------------------- | -------------------------- | | User **has not** been previously identified | Anonymous history **is merged** with user profile upon identification. | | User **has been** previously identified in-app or via API | Anonymous history **is not merged** with user profile upon identification. | {: .reset-td-br-1 .reset-td-br-2 aria-label="Automatic preservation of anonymous user history" } Refer to [Identified user profiles](https://www.braze.com/docs/ko/ko/user_guide/data_and_analytics/user_data_collection/user_profile_lifecycle/#identified-user-profiles) for more information on what occurs when you identify anonymous users. ### Additional notes and best practices Note the following: - If your app is used by multiple people, you can assign each user a unique identifier to track them. - After a user ID has been set, you cannot revert that user to an anonymous profile. - Do not change the user ID when a user logs out as this can separate the device from the user profile. - As a result, you won't be able to target the previously logged out user with re-engagement messages. If you anticipate multiple users on the same device, but only want to target one of them when your app is in a logged-out state, we recommend separately keeping track of the user ID you want to target while logged out and switching back to that user ID as part of your app's logout process. By default, only the last user that was logged in will receive push notifications from your app. - Switching from one identified user to another is a relatively costly operation. - When you request the user switch, the current session for the previous user is automatically closed and a new session is started. Braze will automatically make a data refresh request for in-app messages and other Braze resources for the new user. **Tip:** If you opt to use a hash of a unique identifier as your user ID, be sure that you're normalizing the input to your hashing function. For example, if you're going to use a hash of an email address, confirm that you're stripping leading and trailing whitespace from the input, and taking localization into account. ## 사용자 별칭 지정 A [user alias](https://www.braze.com/docs/ko/ko/user_guide/data_and_analytics/user_data_collection/user_profile_lifecycle/#user-aliases) serves as an alternative unique user identifier. You can use aliases to identify users along different dimensions than your core user ID: * Set a consistent identifier for analytics that will follow a given user both before and after they have logged in to a mobile app or website. * Add the identifiers used by a third-party vendor to your company users in order to more easily reconcile your data externally. Each alias consists of two parts: a name for the identifier itself, and a label indicating the type of alias. Users can have multiple aliases with different labels, but only one name per label. For more information on setting user aliases against a user profile, refer to [User aliases](https://www.braze.com/docs/ko/ko/user_guide/data_and_analytics/user_data_collection/user_profile_lifecycle/#user-aliases). # iOS용 커스텀 이벤트 추적 Source: /docs/ko/developer_guide/platforms/legacy_sdks/ios/analytics/tracking_custom_events/index.md
**Warning:** [AppboyKit](https://github.com/Appboy/appboy-ios-sdk) (also known as the Objective-C SDK) is no longer supported and has been replaced by the [Swift SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=swift). It will no longer receive new features, bug fixes, security updates, or technical support—however, messaging and analytics will continue to function as normal. To learn more, see [Introducing the New Braze Swift SDK](https://www.braze.com/resources/articles/introducing-the-new-braze-swift-sdk). # iOS용 커스텀 이벤트 추적 {#track-custom-events-for-ios} Braze에서 커스텀 이벤트를 기록하여 앱의 사용 패턴에 대해 자세히 알아보고 대시보드에서 사용자의 작업에 따라 사용자를 세분화할 수 있습니다. 구현하기 전에 [이벤트 명명 규칙](https://www.braze.com/docs/ko/ko/user_guide/data/activation/events/event_naming_conventions/)의 참고 사항과 함께 [모범 사례](https://www.braze.com/docs/ko/ko/developer_guide/platform_wide/analytics_overview/#user-data-collection)에서 커스텀 이벤트, 커스텀 속성 및 구매 이벤트가 제공하는 세분화 옵션 예제를 검토하세요. ## 커스텀 이벤트 추가 {#adding-a-custom-event} ```objc [[Appboy sharedInstance] logCustomEvent:@"YOUR_EVENT_NAME"]; ``` ```swift Appboy.sharedInstance()?.logCustomEvent("YOUR_EVENT_NAME") ``` ### 속성정보 추가 {#adding-properties} `NSNumber`, `NSString` 또는 `NSDate` 값으로 채워진 `NSDictionary`를 전달하여 커스텀 이벤트에 대한 메타데이터를 추가할 수 있습니다. ```objc [[Appboy sharedInstance] logCustomEvent:@"YOUR-EVENT-NAME" withProperties:@{ @"you": @"can", @"pass": @(NO), @"orNumbers": @42, @"orDates": [NSDate date], @"or": @[@"any", @"array", @"here"], @"andEven": @{ @"deeply": @[@"nested", @"json"] } }]; ``` ```swift Appboy.sharedInstance()?.logCustomEvent( "YOUR-EVENT-NAME", withProperties: [ "you": "can", "pass": false, "orNumbers": 42, "orDates": Date(), "or": ["any", "array", "here"], "andEven": [ "deeply": ["nested", "json"] ] ] ) ``` 자세한 내용은 [클래스 설명서](http://appboy.github.io/appboy-ios-sdk/docs/interface_appboy.html#a4f0051d73d85cb37f63c232248124c79)를 참조하세요. ### 예약 키 {#event-reserved-keys} 다음 키는 예약되어 있으며 커스텀 이벤트 속성으로 사용할 수 없습니다: - `time` - `event_name` ## 추가 리소스 {#additional-resources} - `Appboy.h` [파일](https://github.com/Appboy/appboy-ios-sdk/blob/master/AppboyKit/include/Appboy.h) 내의 메서드 선언을 참조하세요. - 자세한 내용은 [`logCustomEvent`](http://appboy.github.io/appboy-ios-sdk/docs/interface_appboy.html#ad80c39e8c96482a77562a5b1a1d387aa) 문서를 참조하세요. # iOS용 커스텀 속성 설정 Source: /docs/ko/developer_guide/platforms/legacy_sdks/ios/analytics/setting_custom_attributes/index.md
**Warning:** [AppboyKit](https://github.com/Appboy/appboy-ios-sdk) (also known as the Objective-C SDK) is no longer supported and has been replaced by the [Swift SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=swift). It will no longer receive new features, bug fixes, security updates, or technical support—however, messaging and analytics will continue to function as normal. To learn more, see [Introducing the New Braze Swift SDK](https://www.braze.com/resources/articles/introducing-the-new-braze-swift-sdk). # iOS용 커스텀 속성 설정 {#set-custom-attributes-for-ios} Braze는 사용자에게 속성을 할당하는 방법을 제공합니다. 대시보드에서 이러한 속성에 따라 사용자를 필터링하고 세분화할 수 있습니다. 구현하기 전에 [모범 사례](https://www.braze.com/docs/ko/ko/developer_guide/platform_wide/analytics_overview/#user-data-collection)에서 커스텀 이벤트, 커스텀 속성 및 구매 이벤트가 제공하는 세분화 옵션 예제와 [이벤트 명명 규칙](https://www.braze.com/docs/ko/ko/user_guide/data/activation/events/event_naming_conventions/)에 대한 참고 사항을 검토하세요. ## 기본 사용자 속성 할당하기 {#assigning-default-user-attributes} 사용자 속성을 할당하려면 공유된 `ABKUser` 오브젝트에 적절한 필드를 설정해야 합니다. 다음은 이름 속성 설정 예제입니다: ```objc [Appboy sharedInstance].user.firstName = @"first_name"; ``` ```swift Appboy.sharedInstance()?.user.firstName = "first_name" ``` `ABKUser` 오브젝트에 다음 속성을 설정해야 합니다: - `firstName` - `lastName` - `email` - `dateOfBirth` - `country` - `language` - `homeCity` - `phone` - `userID` - `gender` ## 커스텀 사용자 속성 할당하기 {#assigning-custom-user-attributes} 기본 사용자 속성 외에도 Braze에서는 여러 가지 데이터 유형을 사용하여 커스텀 속성을 정의할 수 있습니다. 이러한 각 속성에서 지원하는 세분화 옵션에 대한 자세한 내용은 [사용자 데이터 수집](https://www.braze.com/docs/ko/ko/developer_guide/analytics/)을 참조하세요. ### 문자열 값이 있는 커스텀 속성 {#custom-attribute-with-a-string-value} ```objc [[Appboy sharedInstance].user setCustomAttributeWithKey:@"your_attribute_key" andStringValue:"your_attribute_value"]; ``` ```swift Appboy.sharedInstance()?.user.setCustomAttributeWithKey("your_attribute_key", andStringValue: "your_attribute_value") ``` ### 정수 값이 있는 커스텀 속성 {#custom-attribute-with-an-integer-value} ```objc [[Appboy sharedInstance].user setCustomAttributeWithKey:@"your_attribute_key" andIntegerValue:yourIntegerValue]; ``` ```swift Appboy.sharedInstance()?.user.setCustomAttributeWithKey("your_attribute_key", andIntegerValue: yourIntegerValue) ``` ### double 값이 있는 커스텀 속성 {#custom-attribute-with-a-double-value} Braze는 데이터베이스 내에서 `float` 및 `double` 값을 동일하게 처리합니다. ```objc [[Appboy sharedInstance].user setCustomAttributeWithKey:@"your_attribute_key" andDoubleValue:yourDoubleValue]; ``` ```swift Appboy.sharedInstance()?.user.setCustomAttributeWithKey("your_attribute_key", andDoubleValue: yourDoubleValue) ``` ### 부울 값이 있는 커스텀 속성 {#custom-attribute-with-a-boolean-value} ```objc [[Appboy sharedInstance].user setCustomAttributeWithKey:@"your_attribute_key" andBOOLValue:yourBOOLValue]; ``` ```swift Appboy.sharedInstance()?.user.setCustomAttributeWithKey("your_attribute_key", andBOOLValue: yourBoolValue) ``` ### 날짜 값이 있는 커스텀 속성 {#custom-attribute-with-a-date-value} 이 메서드를 통해 Braze에 전달되는 날짜는 [ISO 8601](http://en.wikipedia.org/wiki/ISO_8601) 형식(예: `2013-07-16T19:20:30+01:00`) 또는 `yyyy-MM-dd'T'HH:mm:ss:SSSZ` 형식(`2016-12-14T13:32:31.601-0800`)이어야 합니다. ```objc [[Appboy sharedInstance].user setCustomAttributeWithKey:@"your_attribute_key" andDateValue:yourDateValue]; ``` ```swift Appboy.sharedInstance()?.user.setCustomAttributeWithKey("your_attribute_key", andDateValue:yourDateValue) ``` ### 배열 값이 있는 커스텀 속성 {#custom-attribute-with-an-array-value} 배열의 기본 및 최대 요소 개수는 500개입니다. Braze 대시보드의 **데이터 설정** > **커스텀 속성**에서 배열의 최대 요소 개수를 업데이트할 수 있습니다. 최대 요소 개수를 초과하는 배열은 최대 요소 개수만큼 잘립니다. ```objc // Setting a custom attribute with an array value [[Appboy sharedInstance].user setCustomAttributeArrayWithKey:@"array_name" array:@[@"value1", @"value2"]]; // Adding to a custom attribute with an array value [[Appboy sharedInstance].user addToCustomAttributeArrayWithKey:@"array_name" value:@"value3"]; // Removing a value from an array type custom attribute [[Appboy sharedInstance].user removeFromCustomAttributeArrayWithKey:@"array_name" value:@"value2"]; // Removing an entire array and key [[Appboy sharedInstance].user setCustomAttributeArrayWithKey:@"array_name" array:nil]; ``` ```swift // Setting a custom attribute with an array value Appboy.sharedInstance()?.user.setCustomAttributeArrayWithKey("array_name", array: ["value1", "value2"]) // Adding to a custom attribute with an array value Appboy.sharedInstance()?.user.addToCustomAttributeArrayWithKey("array_name", value: "value3") // Removing a value from an array type custom attribute Appboy.sharedInstance()?.user.removeFromCustomAttributeArrayWithKey("array_name", value: "value2") ``` ### 커스텀 속성 설정 해제하기 {#unsetting-a-custom-attribute} 커스텀 속성은 다음 메서드를 사용하여 설정 해제할 수도 있습니다: ```objc [[Appboy sharedInstance].user unsetCustomAttributeWithKey:@"your_attribute_key"]; ``` ```swift Appboy.sharedInstance()?.user.unsetCustomAttributeWithKey("your_attribute_key") ``` ### 커스텀 속성 증가/감소시키기 {#incrementingdecrementing-custom-attributes} 이 코드는 증분 커스텀 속성의 예시입니다. 커스텀 속성의 값을 양의 정수나 음의 정수 또는 long 값만큼 증가시킬 수 있습니다: ```objc [[Appboy sharedInstance].user incrementCustomUserAttribute:@"your_attribute_key" by:incrementIntegerValue]; ``` ```swift Appboy.sharedInstance()?.user.incrementCustomUserAttribute("your_attribute_key", by: incrementIntegerValue) ``` ### REST API를 통해 커스텀 속성 설정하기 {#setting-a-custom-attribute-via-the-rest-api} REST API를 사용하여 사용자 속성을 설정할 수도 있습니다. 자세한 내용은 [사용자 API 설명서](https://www.braze.com/docs/ko/ko/developer_guide/rest_api/user_data/#user-data)를 참조하세요. ### 커스텀 속성 값 제한 {#custom-attribute-value-limits} 커스텀 속성 값의 최대 길이는 255자이며, 이보다 긴 값은 잘립니다. #### 추가 정보 {#additional-information} - 자세한 내용은 [`ABKUser.h` 파일](https://github.com/Appboy/appboy-ios-sdk/blob/master/AppboyKit/include/Appboy.h)에서 확인할 수 있습니다. - 자세한 내용은 [`ABKUser` 설명서](http://appboy.github.io/appboy-ios-sdk/docs/interface_a_b_k_user.html)를 참조하세요. ## 사용자 구독 설정하기 {#setting-up-user-subscriptions} 사용자에 대한 구독(이메일 또는 푸시)을 설정하려면 각각 `setEmailNotificationSubscriptionType` 또는 `setPushNotificationSubscriptionType` 함수를 호출합니다. 이 두 함수 모두 열거형 `ABKNotificationSubscriptionType`을 인수로 받습니다. 이 유형에는 세 가지 상태가 있습니다: | 구독 상태 | 정의 | | ------------------- | ---------- | | `ABKOptedin` | 구독하고 명시적으로 옵트인한 경우 | | `ABKSubscribed` | 구독 중이지만 명시적으로 옵트인하지 않은 경우 | | `ABKUnsubscribed` | 구독 취소 및/또는 명시적으로 수신 거부한 경우 | {: .reset-td-br-1 .reset-td-br-2 aria-label="사용자 구독 설정" } 앱에서 푸시 알림을 보낼 수 있도록 권한을 부여한 사용자의 기본 상태는 `ABKOptedin`입니다. iOS에서는 명시적인 옵트인이 필요하기 때문입니다. 유효한 이메일 주소가 수신되면 자동으로 `ABKSubscribed`로 설정되지만, 명시적인 옵트인 프로세스를 설정하고 사용자의 명시적인 동의를 받은 후 이 값을 `OptedIn`으로 설정하는 것이 좋습니다. 자세한 내용은 [사용자 구독 관리하기](https://www.braze.com/docs/ko/ko/user_guide/channels/email/subscriptions/)를 참조하세요. ### 이메일 구독 설정하기 {#setting-email-subscriptions} ```objc [[Appboy sharedInstance].user setEmailNotificationSubscriptionType: ABKNotificationSubscriptionType] ``` ```swift Appboy.sharedInstance()?.user.setEmailNotificationSubscriptionType(ABKNotificationSubscriptionType) ``` ### 푸시 알림 구독 설정하기 {#setting-push-notification-subscriptions} ```objc [[Appboy sharedInstance].user setPushNotificationSubscriptionType: ABKNotificationSubscriptionType] ``` ```swift Appboy.sharedInstance()?.user.setPushNotificationSubscriptionType(ABKNotificationSubscriptionType) ``` 자세한 내용은 [사용자 구독 관리하기](https://www.braze.com/docs/ko/ko/user_guide/channels/email/subscriptions/)를 참조하세요. # iOS용 구매 기록 Source: /docs/ko/developer_guide/platforms/legacy_sdks/ios/analytics/logging_purchases/index.md
**Warning:** [AppboyKit](https://github.com/Appboy/appboy-ios-sdk) (also known as the Objective-C SDK) is no longer supported and has been replaced by the [Swift SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=swift). It will no longer receive new features, bug fixes, security updates, or technical support—however, messaging and analytics will continue to function as normal. To learn more, see [Introducing the New Braze Swift SDK](https://www.braze.com/resources/articles/introducing-the-new-braze-swift-sdk). # iOS용 구매 기록 {#log-purchases-for-ios} 인앱 구매를 기록하면 여러 매출원에서 시간 경과에 따른 매출을 추적하고 생애주기 가치에 따라 사용자를 세분화할 수 있습니다. Braze는 여러 통화로 구매를 지원합니다. USD가 아닌 다른 통화로 신고한 구매는 신고한 날짜의 환율을 기준으로 대시보드에 USD로 표시됩니다. 구현하기 전에 [모범 사례](https://www.braze.com/docs/ko/ko/developer_guide/platform_wide/analytics_overview/#user-data-collection)에서 커스텀 이벤트, 커스텀 속성 및 구매 이벤트가 제공하는 세분화 옵션 예제를 검토하고, [이벤트 이름 지정 규칙](https://www.braze.com/docs/ko/ko/user_guide/data/activation/events/event_naming_conventions/)도 함께 확인하세요. ## 구매 및 수익 추적 {#tracking-purchases-and-revenue} 이 기능을 사용하려면 앱에서 구매에 성공한 후 이 메서드 호출을 추가합니다. ```objc [[Appboy sharedInstance] logPurchase:@"your product ID" inCurrency:@"USD" atPrice:[[[NSDecimalNumber alloc] initWithString:@"0.99"] autorelease]]; ``` ```swift Appboy.sharedInstance()?.logPurchase("your product ID", inCurrency: "USD", atPrice: NSDecimalNumber(string: "0.99")) ``` - 지원되는 통화 기호는 다음과 같습니다: USD, CAD, EUR, GBP, JPY, AUD, CHF, NOK, MXN, NZD, CNY, RUB, TRY, INR, IDR, ILS, SAR, ZAR, AED, SEK, HKD, SPD, DKK 등. - 제공된 다른 통화 기호를 사용하면 경고가 기록되고, SDK에서 다른 조치를 취하지 않습니다. - 제품 ID는 최대 255자까지 입력할 수 있습니다. - 제품 식별자가 비어 있으면 구매가 Braze에 기록되지 않는다는 점에 유의하세요. ### 속성정보 추가 {#properties-purchases} 구매에 대한 메타데이터는 [이벤트 속성정보 배열](https://www.braze.com/docs/ko/ko/user_guide/data_and_analytics/custom_data/custom_events/#nested-objects)을 전달하거나 `NSNumber`, `NSString` 또는 `NSDate` 값으로 채운 `NSDictionary`를 전달하여 추가할 수 있습니다. 추가 세부 정보는 [iOS 클래스 설명서](http://appboy.github.io/appboy-ios-sdk/docs/interface_appboy.html#aaca4b885a8f61ac9fad3936b091448cc)를 참조하세요. ### 수량 추가 {#adding-quantity} 고객이 한 번의 결제에서 동일한 제품을 여러 번 구매하는 경우 구매에 수량을 추가할 수 있습니다. 수량을 `NSUInteger`로 전달하면 됩니다. * SDK가 구매를 기록하려면 수량 입력이 [0, 100] 범위 내에 있어야 합니다. * 수량 입력이 없는 메서드의 기본 수량 값은 1입니다. * 수량 입력이 있는 메서드에는 기본값이 없으며, SDK가 구매를 기록할 수 있도록 **반드시** 수량 입력을 전달해야 합니다. 자세한 내용은 [iOS 클래스 설명서](http://appboy.github.io/appboy-ios-sdk/docs/interface_appboy.html#ab50403068be47c0acba9943583e259fa)를 참조하세요. ```objc [[Appboy sharedInstance] logPurchase:@"your product ID" inCurrency:@"USD" atPrice:[[[NSDecimalNumber alloc] initWithString:@"0.99"] autorelease] withProperties:@{@"key1":"value1"}]; ``` ```swift Appboy.sharedInstance()?.logPurchase("your product ID", inCurrency: "USD", atPrice: NSDecimalNumber(string: "0.99"), withProperties: ["key1":"value1"]) ``` **Tip:** 10 USD의 가격과 3개의 수량을 전달하면 고객 프로필에 10달러 항목의 3번 구매로 총 30달러가 기록됩니다. ### 주문 수준에서 구매 기록 {#log-purchases-at-the-order-level} 제품 수준 대신 주문 수준에서 구매를 기록하려면 주문 이름 또는 주문 카테고리를 `product_id`로 사용하면 됩니다. 자세한 내용은 [구매 오브젝트 사양](https://www.braze.com/docs/ko/ko/api/objects_filters/purchase_object/#product-id-naming-conventions)을 참조하세요. ### 예약 키 {#reserved-keys} 다음 키는 예약되어 있으며 구매 속성정보로 사용할 수 없습니다: - `time` - `product_id` - `quantity` - `event_name` - `price` - `currency` ### REST API REST API를 사용하여 구매 내역을 기록할 수도 있습니다. 자세한 내용은 [사용자 API 설명서](https://www.braze.com/docs/ko/ko/developer_guide/rest_api/user_data/#user-data)를 참조하세요. # iOS용 위치 추적 Source: /docs/ko/developer_guide/platforms/legacy_sdks/ios/analytics/location_tracking/index.md
**Warning:** [AppboyKit](https://github.com/Appboy/appboy-ios-sdk) (also known as the Objective-C SDK) is no longer supported and has been replaced by the [Swift SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=swift). It will no longer receive new features, bug fixes, security updates, or technical support—however, messaging and analytics will continue to function as normal. To learn more, see [Introducing the New Braze Swift SDK](https://www.braze.com/resources/articles/introducing-the-new-braze-swift-sdk). # iOS용 위치 추적 기본값으로, Braze는 위치 추적을 비활성화합니다. 호스트 애플리케이션이 위치 추적을 옵트인하고 사용자로부터 권한을 얻은 후 위치 추적을 활성화합니다. 사용자가 위치 추적을 옵트인한 경우, Braze는 세션 시작 시 각 사용자에 대해 단일 위치를 기록합니다. **Important:** 대략적인 위치 권한을 부여한 사용자에 대한 위치 추적이 iOS 14에서 안정적으로 작동하려면 SDK 버전을 `3.26.1` 이상으로 업데이트해야 합니다. ## 자동 위치 추적 활성화 Braze iOS SDK `v3.17.0`부터는 위치 추적이 기본적으로 비활성화되어 있습니다. `Info.plist` 파일을 사용하여 자동 위치 추적을 활성화할 수 있습니다. `Info.plist` 파일에 `Braze` 사전을 추가합니다. `Braze` 사전 내에서 `EnableAutomaticLocationCollection` 부울 하위 항목을 추가하고 값을 `YES`로 설정합니다. Braze iOS SDK v4.0.2 이전 버전에서는 `Braze` 대신 `Appboy`의 사전 키를 사용해야 합니다. [`startWithApiKey:inApplication:withLaunchOptions:withAppboyOptions`](https://appboy.github.io/appboy-ios-sdk/docs/interface_appboy.html#aa9f1bd9e4a5c082133dd9cc344108b24) 메서드를 통해 앱 시작 시 자동 위치 추적을 활성화할 수도 있습니다. `appboyOptions` 사전에서 `ABKEnableAutomaticLocationCollectionKey` 을 `YES` 으로 설정합니다. For example: ```objc [Appboy startWithApiKey:@"YOUR-API_KEY" inApplication:application withLaunchOptions:options withAppboyOptions:@{ ABKEnableAutomaticLocationCollectionKey : @(YES) }]; ``` ```swift Appboy.start(withApiKey: "YOUR-API-KEY", in:application, withLaunchOptions:launchOptions, withAppboyOptions:[ ABKEnableAutomaticLocationCollectionKey : true ]) ``` ### 위치 데이터를 Braze에 전달 다음 두 메서드를 사용하여 사용자의 마지막으로 알려진 위치를 수동으로 설정할 수 있습니다. ```objc [[Appboy sharedInstance].user setLastKnownLocationWithLatitude:latitude longitude:longitude horizontalAccuracy:horizontalAccuracy]; ``` ```objc [[Appboy sharedInstance].user setLastKnownLocationWithLatitude:latitude longitude:longitude horizontalAccuracy:horizontalAccuracy altitude:altitude verticalAccuracy:verticalAccuracy]; ``` ```swift Appboy.sharedInstance()?.user.setLastKnownLocationWithLatitude(latitude: latitude, longitude: longitude, horizontalAccuracy: horizontalAccuracy) ``` ```swift Appboy.sharedInstance()?.user.setLastKnownLocationWithLatitude(latitude: latitude, longitude: longitude, horizontalAccuracy: horizontalAccuracy, altitude: altitude, verticalAccuracy: verticalAccuracy) ``` 자세한 내용은 [`ABKUser.h`](https://github.com/Appboy/appboy-ios-sdk/blob/master/AppboyKit/include/ABKUser.h)을 참조하십시오. # iOS용 제거 추적 Source: /docs/ko/developer_guide/platforms/legacy_sdks/ios/analytics/uninstall_tracking/index.md
**Warning:** [AppboyKit](https://github.com/Appboy/appboy-ios-sdk) (also known as the Objective-C SDK) is no longer supported and has been replaced by the [Swift SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=swift). It will no longer receive new features, bug fixes, security updates, or technical support—however, messaging and analytics will continue to function as normal. To learn more, see [Introducing the New Braze Swift SDK](https://www.braze.com/resources/articles/introducing-the-new-braze-swift-sdk). # iOS용 제거 추적 {#uninstall-tracking-for-ios} > 이 문서에서는 iOS 애플리케이션에 대한 제거 추적을 구성하는 방법과 앱이 Braze 제거 추적 푸시를 받을 때 원치 않는 자동 조치를 취하지 않도록 테스트하는 방법을 다룹니다. 제거 추적은 페이로드에 Braze 플래그가 포함된 백그라운드 푸시 알림을 활용합니다. 자세한 내용은 사용자 안내서의 [제거 추적](https://www.braze.com/docs/ko/ko/user_guide/data_and_analytics/tracking/uninstall_tracking/#uninstall-tracking)을 참조하세요. ## 1단계: 백그라운드 푸시 활성화 {#step-1-enabling-background-push} Xcode 프로젝트의 **Capabilities** 탭에 있는 **Background Modes** 섹션에서 **Remote notifications** 옵션을 활성화했는지 확인합니다. 자세한 내용은 [무음 푸시 알림](https://www.braze.com/docs/ko/ko/developer_guide/platforms/legacy_sdks/ios/push_notifications/silent_push_notifications/) 설명서를 참조하세요. ## 2단계: Braze 백그라운드 푸시 확인 {#step-2-checking-for-braze-background-push} Braze는 백그라운드 푸시 알림을 사용하여 제거 추적 분석을 수집합니다. 제거 추적 알림을 받은 애플리케이션이 [원치 않는 동작](https://www.braze.com/docs/ko/ko/developer_guide/platforms/legacy_sdks/ios/push_notifications/customization/ignoring_internal_push/)을 취하지 않는지 확인하세요. ## 3단계: 대시보드에서 테스트 {#step-3-test-from-the-dashboard} 그런 다음, 대시보드에서 테스트 푸시를 직접 전송합니다. 이 테스트 푸시는 고객 프로필을 업데이트하지 않습니다. 1. **Campaigns** 페이지에서 푸시 알림 Campaign을 생성하고 **iOS push**를 플랫폼으로 선택합니다.

2. **Settings** 페이지에서 `appboy_uninstall_tracking` 키를 해당 값 `true`와 함께 추가하고 **Add Content-Available Flag**를 선택합니다.

3. **Preview** 페이지를 사용하여 테스트 제거 추적 푸시를 직접 전송합니다.

4. 앱이 푸시 수신 시 원치 않는 자동 동작을 수행하지 않는지 확인합니다. **Important:** 이 테스트 단계는 Braze에서 제거 추적 푸시를 보내기 위한 프록시입니다. 배지 수를 활성화한 경우 테스트 푸시와 함께 배지 번호가 전송되지만, Braze 제거 추적 푸시에서는 애플리케이션에 배지 번호가 설정되지 않습니다. ## 4단계: 제거 추적 활성화 {#step-4-enable-uninstall-tracking} [제거 추적 활성화](https://www.braze.com/docs/ko/ko/user_guide/data_and_analytics/tracking/uninstall_tracking/#uninstall-tracking) 지침을 따릅니다. # iOS용 SDK 추적 비활성화 Source: /docs/ko/developer_guide/platforms/legacy_sdks/ios/analytics/disabling_tracking/index.md
**Warning:** [AppboyKit](https://github.com/Appboy/appboy-ios-sdk) (also known as the Objective-C SDK) is no longer supported and has been replaced by the [Swift SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=swift). It will no longer receive new features, bug fixes, security updates, or technical support—however, messaging and analytics will continue to function as normal. To learn more, see [Introducing the New Braze Swift SDK](https://www.braze.com/resources/articles/introducing-the-new-braze-swift-sdk). # iOS용 데이터 수집 비활성화 데이터 프라이버시 규정을 준수하기 위해 [`disableSDK`](http://appboy.github.io/appboy-ios-sdk/docs/interface_appboy.html#a8d3b78a98420713d8590ed63c9172733) 메서드를 사용하여 iOS SDK에서 데이터 추적 활동을 완전히 중지할 수 있습니다. 이 방법을 사용하면 모든 네트워크 연결이 취소되고 Braze SDK가 서버로 데이터를 전달하지 않습니다. 나중에 데이터 수집을 재개하려면 향후 [`requestEnableSDKOnNextAppRun`](http://appboy.github.io/appboy-ios-sdk/docs/interface_appboy.html#a781078a40a3db0de64ac82dcae3b595b) 메서드를 사용하여 데이터 수집을 재개할 수 있습니다. 또한 [`wipeDataAndDisableForAppRun`](http://appboy.github.io/appboy-ios-sdk/docs/interface_appboy.html#ac8d580f60ec0608cd91240a8a3aa23a3) 메서드를 사용하여 기기에 저장된 모든 클라이언트 측 데이터를 완전히 지울 수도 있습니다. 사용자가 주어진 기기에서 제공업체의 모든 앱을 제거하지 않는 한, `wipeDataAndDisableForAppRun()` 호출 후 다음에 Braze SDK 및 앱을 실행하면 서버가 기기 식별자(IDFV)를 통해 해당 사용자를 다시 식별하게 됩니다. 모든 사용자 데이터를 완전히 제거하려면 `wipeDataAndDisableForAppRun` 호출을 Braze [REST API](https://www.braze.com/docs/ko/ko/developer_guide/rest_api/user_data/#user-delete-endpoint)를 통해 서버의 데이터를 삭제하는 요청과 결합해야 합니다. ## iOS SDK v5.7.0 이상 iOS SDK v5.7.0 이상을 사용하는 기기의 경우, [IDFV 수집을 비활성화](https://www.braze.com/docs/ko/ko/developer_guide/platform_integration_guides/legacy_sdks/ios/initial_sdk_setup/other_sdk_customizations/#optional-idfv-collection---swift/)할 때 [`wipeData`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/wipedata())를 호출해도 서버가 기기 식별자(IDFV)를 통해 해당 사용자를 다시 식별하지 않습니다. # iOS용 딥링킹 Source: /docs/ko/developer_guide/platforms/legacy_sdks/ios/advanced_use_cases/linking/index.md
**Warning:** [AppboyKit](https://github.com/Appboy/appboy-ios-sdk) (also known as the Objective-C SDK) is no longer supported and has been replaced by the [Swift SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=swift). It will no longer receive new features, bug fixes, security updates, or technical support—however, messaging and analytics will continue to function as normal. To learn more, see [Introducing the New Braze Swift SDK](https://www.braze.com/resources/articles/introducing-the-new-braze-swift-sdk). # iOS용 딥링킹 {#deep-linking-for-ios} 딥링크에 대한 소개 정보는 [사용 설명서 문서](https://www.braze.com/docs/ko/ko/user_guide/messaging/design_and_edit/personalize/actions_and_media_urls/#what-is-deep-linking)를 참조하세요. Braze 앱에서 처음으로 딥링크를 구현하려는 경우, 아래 단계를 따라 시작하세요. ## 1단계: 스키마 등록 {#step-1-register-a-scheme} `Info.plist` 파일에 커스텀 스키마를 명시해야 합니다. 탐색 구조는 사전 배열로 정의됩니다. 이러한 각 사전에는 문자열 배열이 포함되어 있습니다. Xcode를 사용하여 `Info.plist` 파일을 편집합니다: 1. 새 키(`URL types`)를 추가합니다. Xcode는 자동으로 `Item 0`이라는 사전을 포함하는 배열로 만듭니다. 2. `Item 0`에서 `URL identifier` 키를 추가합니다. 값을 커스텀 스키마로 설정합니다. 3. `Item 0`에서 `URL Schemes` 키를 추가합니다. 그러면 자동으로 `Item 0` 문자열을 포함하는 배열이 됩니다. 4. `URL Schemes` >> `Item 0`을 커스텀 스키마로 설정합니다. 또는 `Info.plist` 파일을 직접 편집하려면 다음 사양을 따를 수 있습니다: ```html CFBundleURLTypes CFBundleURLName {YOUR.SCHEME} CFBundleURLSchemes {YOUR.SCHEME} ``` ## 2단계: 커스텀 스키마 허용 목록 추가(iOS 9+) {#step-2-allowlist-the-custom-scheme-ios-9} iOS 9부터 앱에는 앱이 열 수 있는 커스텀 스키마의 허용 목록이 있어야 합니다. 이 목록에 없는 스키마를 호출하려고 하면 시스템이 기기 로그에 오류를 기록하고 딥링크가 열리지 않습니다. 이 오류의 예는 다음과 같습니다: ``` : -canOpenURL: failed for URL: "yourapp://deeplink" – error: "This app is not allowed to query for scheme yourapp" ``` 예를 들어, 인앱 메시지를 탭하여 Facebook 앱을 열어야 하는 경우 앱의 허용 목록에 Facebook 커스텀 스키마(`fb`)가 있어야 합니다. 그렇지 않으면 시스템이 딥링크를 거부합니다. 앱 내부의 페이지나 뷰로 연결되는 딥링크도 앱의 `Info.plist`에 앱의 커스텀 스키마가 나열되어 있어야 합니다. 앱이 딥링킹해야 하는 모든 스키마를 `LSApplicationQueriesSchemes` 키를 사용해 앱의 `Info.plist`에서 허용 목록에 추가해야 합니다. 예를 들어: ```html LSApplicationQueriesSchemes myapp facebook twitter ``` 자세한 내용은 [Apple 설명서](https://developer.apple.com/library/content/documentation/General/Reference/InfoPlistKeyReference/Articles/LaunchServicesKeys.html#//apple_ref/doc/uid/TP40009250-SW14)에서 `LSApplicationQueriesSchemes` 키를 참조하세요. ## 3단계: 핸들러 구현 {#step-3-implement-a-handler} 앱을 활성화한 후 iOS는 [`application:openURL:options:`](https://developer.apple.com/reference/uikit/uiapplicationdelegate/1623112-application?language=objc) 메서드를 호출합니다. 중요한 인수는 [NSURL](https://developer.apple.com/library/ios/DOCUMENTATION/Cocoa/Reference/Foundation/Classes/NSURL_Class/Reference/Reference.html#//apple_ref/doc/c_ref/NSURL) 오브젝트입니다. ```objc - (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary *)options { NSString *path = [url path]; NSString *query = [url query]; // Here you should insert code to take some action based upon the path and query. return YES; } ``` ```swift func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool { let path = url.path let query = url.query // Here you should insert code to take some action based upon the path and query. return true } ``` ![](https://www.braze.com/docs/ko/ko/assets/img_archive/deep_link.png?30080909d43633ac9ca7ac8d115a686a) # 유니버설 링크 {#universal-links} 유니버설 링크를 사용하려면 앱의 기능에 등록된 도메인을 추가하고 `apple-app-site-association` 파일을 업로드했는지 확인합니다. 그런 다음, `AppDelegate`에서 `application:continueUserActivity:restorationHandler:` 메서드를 구현합니다. 예를 들어: ```objc - (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray *restorableObjects))restorationHandler { if ([userActivity.activityType isEqualToString:NSUserActivityTypeBrowsingWeb]) { NSURL *url = userActivity.webpageURL; // Handle url } return YES; } ``` ```swift func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool { if (userActivity.activityType == NSUserActivityTypeBrowsingWeb) { let url = userActivity.webpageURL // Handle url } return true } ``` 자세한 내용은 [Apple](https://developer.apple.com/library/content/documentation/General/Conceptual/AppSearch/UniversalLinks.html)을 참조하세요. **Note:** 기본 유니버설 링크 통합은 Braze 푸시 알림 또는 인앱 메시지와 호환되지 않습니다. 애플리케이션 내에서 유니버설 링크를 처리하려면 [링크 커스터마이징](#linking-handling-customization)을 참조하세요. 또는 푸시 알림 및 인앱 메시지와 함께 [스키마 기반 딥링크](#step-1-registering-a-scheme)를 사용하는 것이 좋습니다. ## 앱 전송 보안(ATS) {#app-transport-security-ats} iOS 9에서는 인앱 메시지에 포함된 웹 URL과 푸시 알림에 영향을 미치는 주요 변경 사항이 도입되었습니다. ### ATS 요구 사항 {#ats-requirements} [Apple의 설명서](https://developer.apple.com/library/prerelease/ios/releasenotes/General/WhatsNewIniOS/Articles/iOS9.html#//apple_ref/doc/uid/TP40016198-SW14)에서 발췌한 내용입니다: "앱 전송 보안은 앱과 웹 서비스 간의 연결 보안을 강화하는 기능입니다. 이 기능은 보안 연결을 위한 모범 사례를 준수하는 기본 연결 요구 사항으로 구성되어 있습니다. 앱은 이 기본 동작을 재정의하고 전송 보안을 해제할 수 있습니다." ATS는 iOS 9 이상에서 기본적으로 적용됩니다. 모든 연결은 HTTPS를 사용해야 하며, 순방향 비밀성을 지원하는 TLS 1.2를 사용하여 암호화해야 합니다. 자세한 내용은 [ATS를 사용하여 연결하기 위한 요구 사항](https://developer.apple.com/library/ios/documentation/General/Reference/InfoPlistKeyReference/Articles/CocoaKeys.html#//apple_ref/doc/uid/TP40009251-SW35)을 참조하세요. Braze가 최종 기기에 제공하는 모든 이미지는 TLS 1.2를 지원하고 ATS와 호환되는 콘텐츠 전송 네트워크("CDN")에서 처리됩니다. 애플리케이션의 `Info.plist`에서 예외로 지정하지 않는 한, 이러한 요구 사항을 따르지 않는 연결은 다음과 같은 오류와 함께 실패합니다: ``` CFNetwork SSLHandshake failed (-9801) Error Domain=NSURLErrorDomain Code=-1200 "An SSL error has occurred, and a secure connection to the server cannot be made." ``` ``` NSURLSession/NSURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9802) ``` ATS 규정 준수는 모바일 앱 내에서 열린 링크(클릭된 링크에 대한 기본 처리)에 대해 적용되며 웹 브라우저를 통해 외부에서 열린 사이트에는 적용되지 않습니다. ### ATS 요구 사항 처리 {#handling-ats-requirements} 다음 세 가지 방법 중 하나로 ATS를 처리할 수 있습니다: #### 모든 링크가 ATS를 준수하는지 확인(권장) {#confirm-all-links-are-ats-compliant-recommended} Braze 통합은 인앱 메시지 및 푸시 Campaign을 통해 사용자를 유도하는 모든 기존 링크가 ATS 요건을 충족하도록 함으로써 ATS 요건을 충족할 수 있습니다. ATS 제한을 우회하는 방법이 있지만, 링크된 모든 URL이 ATS 규정을 준수하는지 확인하는 것이 좋습니다. Apple은 애플리케이션 보안을 점점 더 강조하고 있기 때문에 다음과 같은 ATS 예외 허용 접근 방식은 Apple에서 지원이 보장되지 않습니다. SSL 도구는 웹 서버 보안 문제를 정확히 파악하는 데 도움이 될 수 있습니다. Qualys, Inc.의 이 [SSL 서버 테스트](https://www.ssllabs.com/ssltest/index.html)는 Apple ATS 9 및 iOS 9 규정 준수를 위한 항목을 제공합니다. #### ATS 부분 비활성화 {#partially-disable-ats} 특정 도메인 또는 스키마가 있는 링크의 하위 집합을 ATS 규칙의 예외로 취급하도록 허용할 수 있습니다. Braze 메시징 채널에서 사용하는 모든 링크가 ATS를 준수하거나 예외를 통해 처리되는 경우, Braze 통합은 ATS 요구 사항을 충족합니다. ATS의 예외로 도메인을 추가하려면 앱의 `Info.plist` 파일에 다음을 추가합니다: ```html NSAppTransportSecurity NSAllowsArbitraryLoads NSExceptionDomains example.com NSExceptionAllowsInsecureHTTPLoads NSIncludesSubdomains ``` 자세한 내용은 Apple의 [앱 전송 보안 키](https://developer.apple.com/library/ios/documentation/General/Reference/InfoPlistKeyReference/Articles/CocoaKeys.html#//apple_ref/doc/uid/TP40009251-SW33) 관련 문서를 참조하세요. #### ATS 완전히 비활성화 {#disable-ats-entirely} ATS를 완전히 끌 수 있습니다. 보안 보호 기능이 손실되고 향후 iOS 호환성이 저하될 수 있으므로 권장되지 않는 방법입니다. ATS를 비활성화하려면 앱의 `Info.plist` 파일에 다음을 삽입하세요: ```html NSAppTransportSecurity NSAllowsArbitraryLoads ``` ATS 오류를 디버깅하는 방법에 대한 자세한 내용은 [앱 전송 보안을 사용하여 앱 배포하기](http://timekl.com/blog/2015/08/21/shipping-an-app-with-app-transport-security/?utm_campaign=iOS+Dev+Weekly&utm_medium=email&utm_source=iOS_Dev_Weekly_Issue_213)를 참조하세요. ## URL 인코딩 {#url-encoding} Braze iOS SDK v2.21.0부터 SDK는 링크를 퍼센트 인코딩하여 유효한 `NSURL`을 생성합니다. 유니코드 문자와 같이 올바르게 형성된 URL에서 허용되지 않는 모든 링크 문자는 퍼센트 이스케이프 처리됩니다. 인코딩된 링크를 디코딩하려면 `NSString` 메서드 [`stringByRemovingPercentEncoding`](https://developer.apple.com/library/ios/documentation/Cocoa/Reference/Foundation/Classes/NSString_Class/index.html#//apple_ref/occ/instm/NSString/stringByRemovingPercentEncoding)을 사용합니다. 또한 `ABKURLDelegate`에서 `YES`를 반환해야 하며 앱에서 URL 처리를 트리거하려면 행동 유도 문안이 필요합니다. 예를 들어: ```objc - (BOOL)application:(UIApplication *)application openURL:(NSURL *)url options:(NSDictionary *)options { NSString *urlString = url.absoluteString.stringByRemovingPercentEncoding; // Handle urlString return YES; } ``` ```swift func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool { let urlString = url.absoluteString.removingPercentEncoding // Handle urlString return true } ``` ## 커스터마이징 {#linking-customization} ### 기본 WebView 커스터마이징 {#default-webview-customization} 커스터마이징 가능한 `ABKModalWebViewController` 클래스는 일반적으로 웹 딥링크에 대해 "앱 내에서 웹 URL 열기"를 선택한 경우 SDK에 의해 열린 웹 URL을 표시합니다. `ABKModalWebViewController` 클래스에 대한 카테고리를 선언하거나 직접 수정하여 웹 뷰에 커스터마이징을 적용할 수 있습니다. 자세한 내용은 클래스의 [.h 파일](https://github.com/Appboy/appboy-ios-sdk/blob/master/AppboyKit/include/ABKModalWebViewController.h) 및 [.m 파일](https://github.com/Appboy/appboy-ios-sdk/blob/master/AppboyKit/ABKModalWebViewController.m)을 확인하세요. ### 링크 처리 커스터마이징 {#linking-handling-customization} `ABKURLDelegate` 프로토콜을 사용하여 딥링크, 웹 URL, 유니버설 링크 등의 URL 처리를 커스터마이징할 수 있습니다. Braze 초기화 중에 델리게이트를 설정하려면 [`startWithApiKey:inApplication:withAppboyOptions:`](https://appboy.github.io/appboy-ios-sdk/docs/interface_appboy.html#aa9f1bd9e4a5c082133dd9cc344108b24)의 `appboyOptions`에서 `ABKURLDelegateKey`에 델리게이트 오브젝트를 전달합니다. 그러면 Braze는 URI를 처리하기 전에 델리게이트의 `handleAppboyURL:fromChannel:withExtras:` 구현을 호출합니다. #### 통합 예제: ABKURLDelegate {#integration-example-abkurldelegate} ```objc - (BOOL)handleAppboyURL:(NSURL *)url fromChannel:(ABKChannel)channel withExtras:(NSDictionary *)extras { if ([[url.host lowercaseString] isEqualToString:@"MY-DOMAIN.com"]) { // Custom handle link here return YES; } // Let Braze handle links otherwise return NO; } ``` ```swift func handleAppboyURL(_ url: URL?, from channel: ABKChannel, withExtras extras: [AnyHashable : Any]?) -> Bool { if (url.host == "MY-DOMAIN.com") { // Custom handle link here return true; } // Let Braze handle links otherwise return false; } ``` **Important:** `handleAppboyURL:fromChannel:withExtras:`가 `YES`를 반환하면 Braze는 앱이 URL을 처리하는 것으로 간주하고 URL을 열지 않습니다. 유니버설 링크를 처리하는 경우, `application:continueUserActivity:restorationHandler:`를 직접 호출하는 등 앱의 유니버설 링크 핸들러로 URL을 명시적으로 라우팅해야 합니다. URL을 처리하지 않고 `YES`를 반환하면 인앱 메시지 또는 콘텐츠 카드가 눈에 보이는 동작 없이 닫힙니다. Braze가 기본 동작으로 URL을 처리하도록 하려면 `NO`를 반환하세요. 자세한 내용은 [`ABKURLDelegate.h`](https://github.com/Appboy/appboy-ios-sdk/blob/master/AppboyKit/include/ABKURLDelegate.h)를 참조하세요. ## 자주 사용하는 활용 사례 {#frequent-use-cases} ### 앱 설정으로 딥링킹 {#deep-linking-to-app-settings} iOS는 앱에서 iOS 설정 애플리케이션의 해당 페이지로 사용자를 이동시킬 수 있습니다. `UIApplicationOpenSettingsURLString`을 활용하여 푸시 알림 및 인앱 메시지에서 설정으로 사용자를 딥링킹할 수 있습니다. 1. 먼저, 애플리케이션이 [스키마 기반 딥링크](#deep-links) 또는 [유니버설 링크](#universal-links)를 사용하도록 설정되어 있는지 확인합니다. 2. **설정** 페이지로 딥링킹할 URI를 결정합니다(예: `myapp://settings` 또는 `https://www.braze.com/settings`). 3. 커스텀 스키마 기반 딥링크를 사용하는 경우 `application:openURL:options:` 메서드에 다음 코드를 추가하세요: ```objc - (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary *)options { NSString *path = [url path]; if ([path isEqualToString:@"settings"]) { NSURL *settingsURL = [NSURL URLWithString:UIApplicationOpenSettingsURLString]; [[UIApplication sharedApplication] openURL:settingsURL]; } return YES; } ``` ```swift func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool { let path = url.path if (path == "settings") { UIApplication.shared.openURL(URL(string:UIApplicationOpenSettingsURLString)!) } return true } ``` # iOS용 미세 네트워크 트래픽 제어 Source: /docs/ko/developer_guide/platforms/legacy_sdks/ios/advanced_use_cases/fine_network_traffic_control/index.md
**Warning:** [AppboyKit](https://github.com/Appboy/appboy-ios-sdk) (also known as the Objective-C SDK) is no longer supported and has been replaced by the [Swift SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=swift). It will no longer receive new features, bug fixes, security updates, or technical support—however, messaging and analytics will continue to function as normal. To learn more, see [Introducing the New Braze Swift SDK](https://www.braze.com/resources/articles/introducing-the-new-braze-swift-sdk). # 정밀한 네트워크 트래픽 제어 ## 요청 처리 정책 Braze는 사용자에게 다음 프로토콜을 사용하여 네트워크 트래픽을 제어할 수 있는 옵션을 제공합니다: ### 자동 요청 처리 ***`ABKRequestProcessingPolicy` 열거형 값: `ABKAutomaticRequestProcessing`*** - **기본 요청 정책** 값입니다. - Braze SDK는 다음을 포함한 모든 서버 통신을 자동으로 처리합니다: - 사용자 지정 이벤트 및 속성 데이터를 Braze 서버로 플러시하기 - 콘텐츠 카드 및 지오펜스 업데이트 - 새 인앱 메시지 요청 - 인앱 메시지와 같은 Braze 기능에 사용자 대면 데이터가 필요한 경우 즉각적인 서버 요청이 수행됩니다. - 서버 부하를 최소화하기 위해 Braze는 몇 초마다 새로운 사용자 데이터를 주기적으로 플러시합니다. 다음 방법을 사용하여 언제든지 데이터를 Braze 서버로 수동으로 플러시할 수 있습니다. ```objc [[Appboy sharedInstance] flushDataAndProcessRequestQueue]; ``` ```swift Appboy.sharedInstance()?.flushDataAndProcessRequestQueue() ``` ### 수동 요청 처리 ***`ABKRequestProcessingPolicy` 열거형 값: `ABKManualRequestProcessing`*** - 이 프로토콜은 자동 요청 처리와 동일하지만 다음과 같은 점이 다릅니다: - 커스텀 속성 및 커스텀 이벤트 데이터는 사용자 세션 내내 서버에 자동으로 플러시되지 않습니다. - Braze는 인앱 메시지 요청, 인앱 메시지의 Liquid 템플릿, 지오펜스, 위치 추적 등 내부 기능에 대한 자동 네트워크 요청을 계속 수행합니다. 자세한 내용은 [`Appboy.h`](https://github.com/Appboy/appboy-ios-sdk/blob/master/AppboyKit/include/Appboy.h)에서 `ABKRequestProcessingPolicy` 선언을 참조하세요. 이러한 내부 요청이 수행되면 요청 유형에 따라 로컬에 저장된 커스텀 속성 및 커스텀 이벤트 데이터가 Braze 서버로 플러시될 수 있습니다. 다음 방법을 사용하여 언제든지 데이터를 Braze 서버로 수동으로 플러시할 수 있습니다. ```objc [[Appboy sharedInstance] flushDataAndProcessRequestQueue]; ``` ```swift Appboy.sharedInstance()?.flushDataAndProcessRequestQueue() ``` ## 요청 처리 정책 설정 ### 시작 시 요청 정책 설정 이러한 정책은 앱 시작 시 [`startWithApiKey:inApplication:withLaunchOptions:withAppboyOptions`](https://appboy.github.io/appboy-ios-sdk/docs/interface_appboy.html#aa9f1bd9e4a5c082133dd9cc344108b24) 메서드에서 설정할 수 있습니다. `appboyOptions` 사전에서 다음 코드 스니펫에 표시된 대로 `ABKRequestProcessingPolicyOptionKey` 을 설정합니다: ```objc NSDictionary *appboyOptions = @{ // Other entries ABKRequestProcessingPolicyOptionKey : @(ABKAutomaticRequestProcessing) }; ``` ```swift let appboyOptions: [AnyHashable: Any] = [ // Other entries ABKRequestProcessingPolicyOptionKey: ABKRequestProcessingPolicy.automaticRequestProcessing.rawValue ] ``` ### 런타임에 요청 정책 설정 요청 처리 정책은 `Appboy`의 `requestProcessingPolicy` 속성정보를 통해 런타임 중에 설정할 수도 있습니다. ```objc // Sets the request processing policy to automatic (the default value) [Appboy sharedInstance].requestProcessingPolicy = ABKAutomaticRequestProcessing; ``` ```swift // Sets the request processing policy to automatic (the default value) Appboy.sharedInstance()?.requestProcessingPolicy = ABKRequestProcessingPolicy.automaticRequestProcessing ``` ## 기내 서버 통신 수동 종료 언제라도 '진행 중인' 서버 통신을 중단해야 하는 경우 다음 메서드를 호출해야 합니다. ```objc [[Appboy sharedInstance] shutdownServerCommunication]; ``` ```swift Appboy.sharedInstance()?.shutdownServerCommunication(); ``` 이 메서드를 호출한 후에는 요청 처리 모드를 자동으로 재설정해야 합니다. 따라서 OS에서 백그라운드 작업 또는 이와 유사한 방식으로 강제로 중지하는 경우에만 호출하는 것이 좋습니다. # iOS용 현지화 Source: /docs/ko/developer_guide/platforms/legacy_sdks/ios/advanced_use_cases/localization/index.md
**Warning:** [AppboyKit](https://github.com/Appboy/appboy-ios-sdk) (also known as the Objective-C SDK) is no longer supported and has been replaced by the [Swift SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=swift). It will no longer receive new features, bug fixes, security updates, or technical support—however, messaging and analytics will continue to function as normal. To learn more, see [Introducing the New Braze Swift SDK](https://www.braze.com/resources/articles/introducing-the-new-braze-swift-sdk). # 현지화 {#localization} 현지화는 Braze iOS SDK 내에서 지원됩니다. Braze는 영어 외에도 기본 제공 SDK 메시지에 대해 여러 언어를 지원합니다. 이러한 메시지는 Braze와 통합된 애플리케이션에 표시되는 기본 메시지와 관련이 있으며, 예를 들어 연결 문제가 있을 때 앱에 표시되는 메시지가 해당됩니다(예: "네트워크 연결을 설정할 수 없습니다. 나중에 다시 시도하세요."). 휴대폰 언어가 지원되는 언어 중 하나로 설정되어 있으면, 통합 애플리케이션 내에서 트리거되는 모든 Braze 기본 문자열이 자동으로 해당 언어로 표시됩니다. 프로필에서 사용자에게 속성을 지정할 수 있는 지원되는 언어의 전체 목록을 보려면 [사용자 언어 목록](https://www.braze.com/docs/ko/ko/user_guide/data/unification/user_data/language_codes/)을 참조하세요. ## 지원 언어 {#languages-supported} - 아랍어 - 버마어 - 카탈루냐어 - 중국어 - 체코어 - 덴마크어 - 네덜란드어 - 영어 - 에스페란토어 - 에스토니아어 - 에웨어 - 필리핀어 - 핀란드어 - 프랑스어 - 조지아어 - 독일어 - 그리스어 - 히브리어 - 힌디어 - 헝가리어 - 인도네시아어 - 아일랜드어 - 이탈리아어 - 일본어 - 한국어 - 말레이어 - 노르웨이어 - 뉘노르스크어 - 폴란드어 - 포르투갈어 - 러시아어 - 스페인어 - 스웨덴어 - 태국어 - 우크라이나어 - 베트남어 자세한 내용은 [Apple 현지화](https://developer.apple.com/library/ios/documentation/CoreFoundation/Reference/CFLocaleRef/) 문서 및 [LOC 표준 언어 목록](http://www.loc.gov/standards/iso639-2/php/English_list.php)을 참조하세요. # iOS용 비콘 통합 Source: /docs/ko/developer_guide/platforms/legacy_sdks/ios/advanced_use_cases/beacon_integration/index.md
**Warning:** [AppboyKit](https://github.com/Appboy/appboy-ios-sdk) (also known as the Objective-C SDK) is no longer supported and has been replaced by the [Swift SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=swift). It will no longer receive new features, bug fixes, security updates, or technical support—however, messaging and analytics will continue to function as normal. To learn more, see [Introducing the New Braze Swift SDK](https://www.braze.com/resources/articles/introducing-the-new-braze-swift-sdk). # 비콘 통합 {#beacon-integration} 여기에서는 특정 종류의 비콘을 Braze와 통합하여 세분화 및 메시징을 허용하는 방법을 안내합니다. ## Infillion 비콘 {#infillion-beacons} Infillion 비콘을 설정하고 앱에 통합하면 방문 시작 또는 종료, 비콘 감지 등의 커스텀 이벤트를 기록할 수 있습니다. 장소 이름이나 체류 시간과 같은 이벤트 속성정보도 기록할 수 있습니다. 사용자가 장소에 들어갈 때 커스텀 이벤트를 기록하려면 `didBeginVisit` 메서드에 다음 코드를 입력합니다: ```objc [[Appboy sharedInstance] logCustomEvent:@"Entered %@", visit.place.name]; [[Appboy sharedInstance] flushDataAndProcessRequestQueue]; ``` ```swift Appboy.sharedInstance()?.logCustomEvent("Entered %@", visit.place.name) Appboy.sharedInstance()?.flushDataAndProcessRequestQueue() ``` `flushDataAndProcessRequestQueue`를 사용하면 앱이 백그라운드 상태에 있더라도 이벤트가 기록되며, 장소를 떠나는 경우에도 동일한 프로세스를 구현할 수 있습니다. 이렇게 하면 사용자가 새로 들어가는 장소마다 고유한 커스텀 이벤트가 생성되고 증가한다는 점에 유의하세요. 50개 이상의 장소를 생성할 예정이라면 하나의 일반적인 "Place Entered" 커스텀 이벤트를 만들고 장소 이름을 이벤트 속성정보로 포함하는 것이 좋습니다. # iOS용 위치 및 지오펜스 Source: /docs/ko/developer_guide/platforms/legacy_sdks/ios/advanced_use_cases/locations_and_geofences/index.md
**Warning:** [AppboyKit](https://github.com/Appboy/appboy-ios-sdk) (also known as the Objective-C SDK) is no longer supported and has been replaced by the [Swift SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=swift). It will no longer receive new features, bug fixes, security updates, or technical support—however, messaging and analytics will continue to function as normal. To learn more, see [Introducing the New Braze Swift SDK](https://www.braze.com/resources/articles/introducing-the-new-braze-swift-sdk). # 위치 및 지오펜스 iOS용 지오펜스를 지원하기 위한 조건: 1. 통합은 백그라운드 푸시 알림을 지원해야 합니다. 2. Braze 지오펜스는 SDK를 통해 위치 수집을 활성화하여 암시적으로 또는 지오펜스 수집을 활성화하여 명시적으로 [활성화해야 합니다](https://www.braze.com/docs/ko/ko/developer_guide/platform_integration_guides/swift/analytics/location_tracking/#enabling-automatic-location-tracking). 기본적으로 활성화되어 있지 않습니다. **Important:** iOS 14부터 지오펜스는 대략적인 위치 권한을 제공하는 사용자의 경우 안정적으로 작동하지 않습니다. ## 1단계: 백그라운드 푸시 활성화 지오펜스 동기화 전략을 완전히 활용하려면 표준 푸시 통합을 완료하는 것 외에도 [백그라운드 푸시](https://www.braze.com/docs/ko/ko/developer_guide/platform_integration_guides/ios/push_notifications/silent_push_notifications/#use-silent-remote-notifications-to-trigger-background-work)를 활성화해야 합니다. ## 2단계: 지오펜스 활성화 기본적으로 지오펜스는 자동 위치 수집의 활성화 여부에 따라 활성화됩니다. `Info.plist` 파일을 사용하여 지오펜스를 활성화할 수 있습니다. `Info.plist` 파일에 `Braze` 사전을 추가합니다. `Braze` 사전 내에서 `EnableGeofences` 부울 하위 항목을 추가하고 값을 `YES`로 설정합니다. Braze iOS SDK v4.0.2 이전 버전에서는 `Braze` 대신 `Appboy` 사전 키를 사용해야 합니다. [`startWithApiKey:inApplication:withLaunchOptions:withAppboyOptions`](https://appboy.github.io/appboy-ios-sdk/docs/interface_appboy.html#aa9f1bd9e4a5c082133dd9cc344108b24) 메서드를 사용하여 앱 시작 시 지오펜스를 활성화할 수도 있습니다. `appboyOptions` 사전에서 `ABKEnableGeofencesKey`를 `YES`로 설정합니다. 예를 들어: ```objc [Appboy startWithApiKey:@"YOUR-API_KEY" inApplication:application withLaunchOptions:options withAppboyOptions:@{ ABKEnableGeofencesKey : @(YES) }]; ``` ```swift Appboy.start(withApiKey: "YOUR-API-KEY", in:application, withLaunchOptions:launchOptions, withAppboyOptions:[ ABKEnableGeofencesKey : true ]) ``` ## 3단계: Braze 백그라운드 푸시 확인 Braze는 백그라운드 푸시 알림을 사용하여 지오펜스를 기기와 동기화합니다. 애플리케이션이 Braze 지오펜스 동기화 알림을 수신할 때 원치 않는 동작을 수행하지 않도록 [iOS 커스터마이징](https://www.braze.com/docs/ko/ko/developer_guide/platforms/legacy_sdks/ios/push_notifications/customization/ignoring_internal_push/) 문서를 참조하세요. ## 4단계: Info.plist에 NSLocationAlwaysUsageDescription 추가 `NSLocationAlwaysUsageDescription` 및 `NSLocationAlwaysAndWhenInUseUsageDescription` 키를 `String` 값으로 `info.plist`에 추가합니다. 이 값에는 애플리케이션에서 위치를 추적해야 하는 이유에 대한 설명이 포함되어야 합니다. iOS 11 이상에서는 두 키가 모두 필요합니다. 이 설명은 시스템 위치 프롬프트가 승인을 요청할 때 표시되며, 사용자에게 위치 추적의 이점을 명확하게 설명해야 합니다. ## 5단계: 사용자에게 권한 부여 요청 지오펜스 기능은 `Always` 위치 권한이 부여된 상태에서만 작동합니다. `Always` 위치 권한을 요청하려면 다음 코드를 사용하세요: ```objc CLLocationManager *locationManager = [[CLLocationManager alloc] init]; [locationManager requestAlwaysAuthorization]; ``` ```swift var locationManager = CLLocationManager() locationManager.requestAlwaysAuthorization() ``` ## 6단계: 대시보드에서 지오펜스 활성화 iOS에서는 특정 앱에 대해 최대 20개의 지오펜스만 저장할 수 있습니다. 위치를 사용하면 사용 가능한 지오펜스 슬롯 20개 중 일부가 소진됩니다. 앱의 다른 지오펜스 관련 기능이 실수로 또는 원치 않게 중단되는 것을 방지하려면 대시보드에서 개별 앱에 대해 위치 지오펜스를 활성화해야 합니다. 위치가 올바르게 작동하려면 앱이 사용 가능한 모든 지오펜스 슬롯을 사용하고 있지 않은지도 확인해야 합니다. ### 위치 페이지에서 지오펜스 활성화: ![Braze 위치 페이지의 지오펜스 옵션.](https://www.braze.com/docs/ko/ko/assets/img_archive/enable-geofences-locations-page.png?4bf8451a2e59f1723b529fa8ff43b7f7) ### 설정 페이지에서 지오펜스 활성화: ![Braze 설정 페이지에 있는 지오펜스 확인란.](https://www.braze.com/docs/ko/ko/assets/img_archive/enable-geofences-app-settings-page.png?702b6b77bb33116e03d8ba576f4e62f9) ## 자동 지오펜스 요청 비활성화 iOS SDK 버전 3.21.3부터 지오펜스의 자동 요청을 비활성화할 수 있습니다. `Info.plist` 파일을 사용하여 이 작업을 수행할 수 있습니다. `Info.plist` 파일에 `Braze` 사전을 추가합니다. `Braze` 사전 내에서 `DisableAutomaticGeofenceRequests` 부울 하위 항목을 추가하고 값을 `YES`로 설정합니다. [`startWithApiKey:inApplication:withLaunchOptions:withAppboyOptions`](https://appboy.github.io/appboy-ios-sdk/docs/interface_appboy.html#aa9f1bd9e4a5c082133dd9cc344108b24) 메서드를 통해 앱 시작 시 자동 지오펜스 요청을 비활성화할 수도 있습니다. `appboyOptions` 사전에서 `ABKDisableAutomaticGeofenceRequestsKey`를 `YES`로 설정합니다. 예를 들어: ```objc [Appboy startWithApiKey:@"YOUR-API_KEY" inApplication:application withLaunchOptions:options withAppboyOptions:@{ ABKDisableAutomaticGeofenceRequestsKey : @(YES) }]; ``` ```swift Appboy.start(withApiKey: "YOUR-API-KEY", in:application, withLaunchOptions:launchOptions, withAppboyOptions:[ ABKDisableAutomaticGeofenceRequestsKey : true ]) ``` 이 옵션을 사용하는 경우 기능이 작동하려면 지오펜스를 수동으로 요청해야 합니다. ## 지오펜스 수동 요청 Braze SDK가 백엔드에서 모니터링할 지오펜스를 요청하면 사용자의 현재 위치를 보고하고, 보고된 위치를 기반으로 최적의 관련성이 있는 것으로 판단되는 지오펜스를 수신합니다. 지오펜스 새로고침은 세션당 한 번으로 사용량이 제한됩니다. 가장 관련성이 높은 지오펜스를 수신하기 위해 SDK가 보고하는 위치를 제어하려면, iOS SDK 버전 3.21.3부터 위치의 위도와 경도를 제공하여 지오펜스를 수동으로 요청할 수 있습니다. 이 방법을 사용할 때는 자동 지오펜스 요청을 비활성화하는 것이 좋습니다. 이렇게 하려면 다음 코드를 사용하세요: ```objc [[Appboy sharedInstance] requestGeofencesWithLongitude:longitude latitude:latitude]; ``` ```swift Appboy.sharedInstance()?.requestGeofences(withLongitude: longitude, latitude: latitude) ``` # iOS용 Google Tag Manager Source: /docs/ko/developer_guide/platforms/legacy_sdks/ios/advanced_use_cases/google_tag_manager/index.md
**Warning:** [AppboyKit](https://github.com/Appboy/appboy-ios-sdk) (also known as the Objective-C SDK) is no longer supported and has been replaced by the [Swift SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=swift). It will no longer receive new features, bug fixes, security updates, or technical support—however, messaging and analytics will continue to function as normal. To learn more, see [Introducing the New Braze Swift SDK](https://www.braze.com/resources/articles/introducing-the-new-braze-swift-sdk). # iOS용 Google Tag Manager ## SDK 초기화 {#initializing-ios-google-tag-provider} Braze iOS SDK는 [Google 태그 관리자](https://tagmanager.google.com/) 내에서 구성된 태그를 통해 초기화 및 제어할 수 있습니다. Google Tag Manager를 사용하기 전에 먼저 [초기 SDK 설정](https://www.braze.com/docs/ko/ko/developer_guide/platforms/legacy_sdks/ios/initial_sdk_setup/overview/)을 따르세요. ## Google Tag Manager 구성 {#configuring-ios-google-tag-manager} 이 예에서는 사용자가 노래를 들을 때 다양한 이벤트를 기록하려는 음악 스트리밍 앱이라고 가정해 보겠습니다. iOS용 Google Tag Manager를 사용하면 이 이벤트를 수신할 서드파티 공급업체를 제어하고 Braze 전용 태그를 만들 수 있습니다. ### 사용자 지정 이벤트 커스텀 이벤트는 `actionType`이 `logEvent`로 설정되어 기록됩니다. 예제의 Braze 커스텀 태그 공급자는 `eventName`을 사용하여 커스텀 이벤트 이름을 설정한다고 예상합니다. 시작하려면 `played song`과 동일한 '이벤트 이름'을 찾는 트리거를 만듭니다. ![Google Tag Manager의 커스텀 트리거는 'eventName'이 'played song'과 같을 때 일부 이벤트에 대해 트리거되도록 설정됩니다.](https://www.braze.com/docs/ko/ko/assets/img/android_google_tag_manager/gtm_android_trigger.png?ce7d5cd1e1ab6a285076d8429ac796bd) 그런 다음, 새 태그('Function Call')를 만들고 [커스텀 태그 공급자](#adding-ios-google-tag-provider)(이 문서의 뒷부분에서 설명함)의 클래스 경로를 입력합니다. 이 태그는 방금 만든 `played song` 이벤트를 기록할 때 트리거됩니다. 예제 태그의 커스텀 매개변수(키-값 페어)에서는 `eventName`을 `played song`으로 설정했으며, 이는 Braze에 기록되는 커스텀 이벤트 이름이 됩니다. **Important:** 커스텀 이벤트를 보낼 때는 다음 예제와 같이 `actionType`을 `logEvent`로 설정하고 `eventName`에 대한 값을 설정합니다. 이 예제에서 커스텀 태그 공급자는 이 키를 사용하여 Google Tag Manager에서 데이터를 수신할 때 수행할 작업과 Braze에 전송할 이벤트 이름을 결정합니다. ![클래스 경로 및 키-값 페어 필드에서 Google Tag Manager의 태그. 이 태그는 이전에 생성된 'played song' 트리거로 트리거되도록 설정됩니다.](https://www.braze.com/docs/ko/ko/assets/img/android_google_tag_manager/gtm_android_function_call_tag.png?40fad5b2a61b7d2183f635a10e290252) 태그에 추가 키-값 쌍 인수를 포함할 수도 있으며, 이 인수는 사용자 지정 이벤트 속성으로 Braze에 전송됩니다. `eventName` 및 `actionType` 은 사용자 지정 이벤트 속성에 대해 무시되지 않습니다. 다음 예제 태그에서는 `genre`를 전달합니다. 이 항목은 Google Tag Manager에서 태그 변수를 사용하여 정의되며, 해당 변수는 앱에 기록된 커스텀 이벤트에서 가져옵니다. `genre` 이벤트 속성정보는 iOS용 Google Tag Manager가 데이터 레이어로 Firebase를 사용하기 때문에 'Firebase - 이벤트 매개변수' 변수로 Google Tag Manager에 전송됩니다. ![Google Tag Manager에서 'genre'가 'Braze - Played Song' 이벤트 태그의 이벤트 매개변수로 추가되는 변수.](https://www.braze.com/docs/ko/ko/assets/img/android_google_tag_manager/gtm_android_eventname_variable.png?abff82f38b65ae64ad0ae3842d2ea439) 마지막으로, 사용자가 앱에서 노래를 재생하면 Firebase 및 Google Tag Manager를 통해 태그의 트리거 이름(`played song`)과 일치하는 Firebase 분석 이벤트 이름을 사용하여 이벤트를 기록합니다. ```obj-c NSDictionary *parameters = @{@"genre" : @"pop", @"number of times listened" : @42}; [FIRAnalytics logEventWithName:@"played song" parameters:parameters]; ``` ### 커스텀 속성 로깅 `actionType`을 `customAttribute`로 설정하여 커스텀 속성은 설정됩니다. Braze 커스텀 태그 공급자는 `customAttributeKey` 및 `customAttributeValue`를 통해 커스텀 속성 키-값을 설정한다고 예상합니다. ```obj-c NSDictionary *parameters = @{@"customAttributeKey" : @"favorite song", @"customAttributeValue" : @"Private Eyes"}; [FIRAnalytics logEventWithName:@"customAttribute" parameters:parameters]; ``` ### changeUser 호출 `actionType`을 `changeUser`로 설정하고 `changeUser()`에 대한 호출이 수행됩니다. Braze 커스텀 태그 공급자는 태그 내 `externalUserId` 키-값 페어를 통해 Braze 사용자 ID를 설정한다고 예상합니다. ```obj-c NSDictionary *parameters = @{@"externalUserId" : userId}; [FIRAnalytics logEventWithName:@"changeUser" parameters:parameters]; ``` ## Braze SDK 사용자 지정 태그 공급자 {#adding-ios-google-tag-provider} 태그 및 트리거를 설정한 상태에서 Google Tag Manager를 iOS 앱에서도 구현해야 합니다(Google [설명서](https://developers.google.com/tag-manager/ios/v5/) 참조). 앱에 Google Tag Manager를 설치한 후 Google Tag Manager 내에서 구성한 태그를 기반으로 Braze SDK 메서드를 호출하기 위해 커스텀 태그 제공자를 추가합니다. "클래스 경로"를 파일에 기록해 두십시오. 이는 [Google Tag Manager](https://tagmanager.google.com/) 콘솔에서 태그를 설정할 때 입력할 내용입니다. 이 예제에서는 커스텀 태그 공급자를 구성하는 여러 가지 방법 중 하나를 보여줍니다. 여기서는 Google Tag Manager에서 전송된 `actionType` 키-값 페어에 따라 호출할 Braze SDK 메서드를 결정합니다. 이 예제에서 지원되는 `actionType`은 `logEvent`, `customAttribute`, `changeUser`이지만, 태그 공급자가 Google Tag Manager에서 데이터를 처리하는 방식을 변경할 수도 있습니다. `BrazeGTMTagManager.h` 파일에 다음 코드를 추가합니다: ```obj-c @import Firebase; @import GoogleTagManager; @interface BrazeGTMTagManager : NSObject @end ``` 그리고 `BrazeGTMTagManager.m` 파일에 다음 코드를 추가합니다: ```obj-c #import #import "BrazeGTMTagManager.h" #import "Appboy-iOS-SDK/AppboyKit.h" static NSString *const ActionTypeKey = @"actionType"; // Custom Events static NSString *const LogEventActionType = @"logEvent"; static NSString *const LogEventEventName = @"eventName"; // Custom Attributes static NSString *const CustomAttributeActionType = @"customAttribute"; static NSString *const CustomAttributeKey = @"customAttributeKey"; static NSString *const CustomAttributeValueKey = @"customAttributeValue"; // Change User static NSString *const ChangeUserActionType = @"changeUser"; static NSString *const ChangeUserExternalUserId = @"externalUserId"; @implementation BrazeGTMTagManager - (NSObject *)executeWithParameters:(NSDictionary *)parameters { NSMutableDictionary *mutableParameters = [parameters mutableCopy]; NSString *actionType = mutableParameters[ActionTypeKey]; if (!actionType) { NSLog(@"There is no Braze action type key in this call. Doing nothing.", nil); return nil; } [mutableParameters removeObjectForKey:ActionTypeKey]; if ([actionType isEqualToString:LogEventActionType]) { [self logEvent:mutableParameters]; } else if ([actionType isEqualToString:CustomAttributeActionType]) { [self logCustomAttribute:mutableParameters]; } else if ([actionType isEqualToString:ChangeUserActionType]) { [self changeUser:mutableParameters]; } else { NSLog(@"Invalid action type. Doing nothing."); } return nil; } - (void)logEvent:(NSMutableDictionary *)parameters { NSString *eventName = parameters[LogEventEventName]; [parameters removeObjectForKey:LogEventEventName]; [[Appboy sharedInstance] logCustomEvent:eventName withProperties:parameters]; } - (void)logCustomAttribute:(NSMutableDictionary *)parameters { NSString *customAttributeKey = parameters[CustomAttributeKey]; id customAttributeValue = parameters[CustomAttributeValueKey]; if ([customAttributeValue isKindOfClass:[NSString class]]) { [[Appboy sharedInstance].user setCustomAttributeWithKey:customAttributeKey andStringValue:customAttributeValue]; } else if ([customAttributeValue isKindOfClass:[NSDate class]]) { [[Appboy sharedInstance].user setCustomAttributeWithKey:customAttributeKey andDateValue:customAttributeValue]; } else if ([customAttributeValue isKindOfClass:[NSNumber class]]) { if (strcmp([customAttributeValue objCType], [@(YES) objCType]) == 0) { [[Appboy sharedInstance].user setCustomAttributeWithKey:customAttributeKey andBOOLValue:[(NSNumber *)customAttributeValue boolValue]]; } else if (strcmp([customAttributeValue objCType], @encode(short)) == 0 || strcmp([customAttributeValue objCType], @encode(int)) == 0 || strcmp([customAttributeValue objCType], @encode(long)) == 0) { [[Appboy sharedInstance].user setCustomAttributeWithKey:customAttributeKey andIntegerValue:[(NSNumber *)customAttributeValue integerValue]]; } else if (strcmp([customAttributeValue objCType], @encode(float)) == 0 || strcmp([customAttributeValue objCType], @encode(double)) == 0) { [[Appboy sharedInstance].user setCustomAttributeWithKey:customAttributeKey andDoubleValue:[(NSNumber *)customAttributeValue doubleValue]]; } else { NSLog(@"Could not map NSNumber value to Appboy custom attribute:%@", customAttributeValue); } } else if ([customAttributeValue isKindOfClass:[NSArray class]]) { [[Appboy sharedInstance].user setCustomAttributeArrayWithKey:customAttributeKey array:customAttributeValue]; } } - (void)changeUser:(NSMutableDictionary *)parameters { NSString *userId = parameters[ChangeUserExternalUserId]; [[Appboy sharedInstance] changeUser:userId]; } @end ``` # iOS용 저장소 Source: /docs/ko/developer_guide/platforms/legacy_sdks/ios/storage/index.md
**Warning:** [AppboyKit](https://github.com/Appboy/appboy-ios-sdk) (also known as the Objective-C SDK) is no longer supported and has been replaced by the [Swift SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=swift). It will no longer receive new features, bug fixes, security updates, or technical support—however, messaging and analytics will continue to function as normal. To learn more, see [Introducing the New Braze Swift SDK](https://www.braze.com/resources/articles/introducing-the-new-braze-swift-sdk). # 저장 {#storage} 이 문서에서는 Braze iOS SDK를 사용할 때 캡처되는 다양한 기기 수준의 등록정보를 설명합니다. ## 기기 등록정보 {#device-properties} 기본적으로 Braze는 기기, 언어, 시간대를 기반으로 메시지를 개인화할 수 있도록 다음과 같은 [기기 수준 등록정보](https://github.com/Appboy/appboy-ios-sdk/blob/16e893f2677af7de905b927505d4101c6fb2091d/AppboyKit/headers/AppboyKitLibrary/Appboy.h#L181)를 수집합니다. * 기기 해상도 * 기기 통신사 * 기기 로케일 * 기기 모델 * 기기 OS 버전 * IDFV([iOS SDK v5.7.0 이상](https://github.com/braze-inc/braze-swift-sdk)에서 선택 사항) * 푸시 활성화됨 * 기기 시간대 * 푸시 승인 상태 * 광고 추적 활성화됨 **Note:** Braze SDK는 IDFA를 자동으로 수집하지 않습니다. 앱은 `ABKIDFADelegate` 프로토콜을 구현하여 선택적으로 IDFA를 Braze에 전달할 수 있습니다. 앱은 앱 추적 투명성 프레임워크를 통해 최종 사용자로부터 추적에 대한 명시적인 옵트인을 확보한 후에 IDFA를 Braze에 전달해야 합니다. 구성 가능한 기기 필드는 [`ABKDeviceOptions`](https://github.com/Appboy/appboy-ios-sdk/blob/4390e9eac8401bccdb81b053fa54eb87b1f6fcaa/Appboy-tvOS-SDK/AppboyTVOSKit.framework/Headers/Appboy.h#L179) 열거형에 정의됩니다. 허용 목록에 추가할 기기 필드를 비활성화하거나 지정하려면 `startWithApiKey:inApplication:withAppboyOptions:`의 `appboyOptions`에서 원하는 필드의 비트 단위 `OR`을 [`ABKDeviceAllowlistKey`](https://github.com/Appboy/appboy-ios-sdk/blob/fed071000722673754da288cace15c1ff8aca432/AppboyKit/include/Appboy.h#L148)에 할당합니다. 예를 들어 허용 목록에 추가할 시간대 및 로케일 수집을 지정하려면 다음과 같이 설정합니다. ``` appboyOptions[ABKDeviceAllowlistKey] = @(ABKDeviceOptionTimezone | ABKDeviceOptionLocale); ``` 기본적으로 모든 필드가 활성화되어 있습니다. 일부 등록정보가 없으면 모든 기능이 제대로 작동하지 않을 수 있습니다. 예를 들어, 현지 시간대 전달은 시간대가 없으면 작동하지 않습니다. 자동으로 수집되는 기기 등록정보에 대한 자세한 내용은 [SDK 데이터 수집](https://www.braze.com/docs/ko/ko/user_guide/data/unification/user_data/sdk_data_collection/)을 참조하세요. # iOS용 샘플 앱 Source: /docs/ko/developer_guide/platforms/legacy_sdks/ios/sample_apps/index.md
**Warning:** [AppboyKit](https://github.com/Appboy/appboy-ios-sdk) (also known as the Objective-C SDK) is no longer supported and has been replaced by the [Swift SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=swift). It will no longer receive new features, bug fixes, security updates, or technical support—however, messaging and analytics will continue to function as normal. To learn more, see [Introducing the New Braze Swift SDK](https://www.braze.com/resources/articles/introducing-the-new-braze-swift-sdk). # 샘플 앱 {#sample-apps} Braze SDK에는 각각 사용자의 편의를 위해 리포지토리 내 샘플 애플리케이션이 포함되어 있습니다. 이러한 각 앱은 완전히 빌드할 수 있으므로 자체 애플리케이션 내에서 구현하는 동시에 Braze 기능을 테스트할 수 있습니다. 자체 애플리케이션 내 동작과 샘플 애플리케이션 내 예상 동작 및 코드 경로를 비교하여 테스트하는 것은 발생할 수 있는 문제를 디버깅하는 훌륭한 방법입니다. ## 테스트 애플리케이션 빌드 {#building-test-applications} 여러 테스트 애플리케이션은 [iOS SDK GitHub 리포지토리](https://github.com/appboy/appboy-ios-sdk) 내에서 사용할 수 있습니다. 다음 지침에 따라 테스트 애플리케이션을 빌드하고 실행합니다. 1. 새 [워크스페이스](https://www.braze.com/docs/ko/ko/developer_guide/platform_wide/app_group_configuration/#creating-your-app-group-in-my-apps)를 생성하고 앱 식별자 API 키를 기록합니다. 2. `AppDelegate.m` 파일의 해당 필드에 API 키를 입력합니다. iOS 테스트 애플리케이션의 푸시 알림에는 추가 구성이 필요합니다. 자세한 내용은 [iOS 푸시 통합](https://www.braze.com/docs/ko/ko/developer_guide/platforms/legacy_sdks/ios/push_notifications/integration/)을 참조하세요. # iOS Swift SDK용 변경 로그 Source: /docs/ko/developer_guide/platforms/legacy_sdks/ios/changelog/swift_changelog/index.md
**Warning:** [AppboyKit](https://github.com/Appboy/appboy-ios-sdk) (also known as the Objective-C SDK) is no longer supported and has been replaced by the [Swift SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=swift). It will no longer receive new features, bug fixes, security updates, or technical support—however, messaging and analytics will continue to function as normal. To learn more, see [Introducing the New Braze Swift SDK](https://www.braze.com/resources/articles/introducing-the-new-braze-swift-sdk). # iOS Swift SDK 변경 로그

16.0.0

Breaking
  • Updates to Content Cards behavior and reliability
    • braze.contentCards.cards is now immediately updated after a card is marked as viewed, dismissed, or clicked via its context.
      • Previously, these mutations were only visible in braze.contentCards.cards after the next server sync.
    • Disabling Content Cards via Braze.Configuration now immediately clears braze.contentCards.cards and notifies subscribeToUpdates subscribers with an empty list.
      • Previously, braze.contentCards.cards retained its last value and subscribers were not notified.
Added
  • Adds Braze.FeatureFlags.getAllFeatureFlags(_:) — an asynchronous, callback-based getter that delivers cached feature flags on the main thread without blocking the calling thread.

15.2.0

Added
  • Adds optional subtotalValue, tax, and shipping fields to Braze.Ecommerce.OrderPlacedEvent, all Braze.Ecommerce.CartUpdated variants (Replace / Add / Remove), and Braze.Ecommerce.CheckoutStartedEvent.
    • Available in Objective-C on BRZEcommerceOrderPlacedEvent, BRZEcommerceCartUpdatedEvent, and BRZEcommerceCheckoutStartedEvent.
Fixed
  • Fixes a video player configuration error for embedded YouTube videos in HTML in-app messages.

15.1.0

Added
  • Adds dismiss() to Braze.Banner.Context and dismiss(using:) to Braze.Banner to dismiss a banner when using a custom UI.
    • Recommended to use Braze.Banner.Context.dismiss.
    • Both methods must be called from the main thread.
    • Calling either method fires the onDismiss callback on any registered BrazeBannerPlacement for that placement ID.
    • Available in Objective-C as -[BRZBannerContext dismiss] and -[BRZBanner dismissUsing:].
  • Adds example implementations for building a custom UI with banners.
  • Adds Braze.ContentCards.getCachedContentCards(_:), Braze.ContentCards.getUnviewedCards(_:), and Braze.ContentCards.getLastUpdate(_:) — asynchronous, callback-based getters that deliver on the main thread.
Fixed
  • Fixes a bug in the default Content Cards UI that would prevent image loading if multiple cards contained the same remote image URL. (#176)
    • If multiple cards contained the same image URL, only the first card to finish loading would display the image, whereas all others would indefinitely display the loading spinner.

15.0.1

Fixed
  • Improves the stability of the SDK’s internal state management, resolving a crash that would occur under low memory conditions.
  • getBanner now returns the cached banner immediately even when the SDK is rate limited.

15.0.0

Breaking
  • Banners: onDismiss now receives Braze/BannerDismissalEvent instead of Braze/Banner.
  • Raises the Xcode version to 26.0 (17A324).
  • Raises the minimum Mac Catalyst deployment target from iOS 13 (macOS 10.15 Catalina) to iOS 16 (macOS 13 Ventura).
    • Mac Catalyst users on macOS 12 Monterey or earlier are no longer supported.
  • Removes the ability to control whether the SDK prevents showing in-app messages to different users in certain edge cases.
    • Removes the option to configure through Braze.Configuration.preventInAppMessageDisplayForDifferentUser.
    • The SDK will now always behave as if this configuration option were set to true.
  • Updates the Braze.WebViewBridge.ScriptMessageHandler and Braze.WebViewBridge.SchemeHandler init to have non-optional channel parameter.
Added
  • Logs configuration validation messages when Braze.Configuration.devicePropertyAllowList omits pushEnabled or pushAuthStatus.
    • Both are required for push token registration and for push notifications to behave correctly.
  • Adds support for logging Braze eCommerce recommended events.
    • Creates the following new event types:
      • Braze.Ecommerce.ProductViewedEvent
      • Braze.Ecommerce.CartUpdated.Replace — full cart snapshot
      • Braze.Ecommerce.CartUpdated.Add — incremental add
      • Braze.Ecommerce.CartUpdated.Remove — incremental remove
      • Braze.Ecommerce.CheckoutStartedEvent
      • Braze.Ecommerce.OrderPlacedEvent
    • Adds the following API: Braze.logEcommerceEvent(_:)
    • Adds Objective-C compatible APIs:
      • -[Braze logEcommerceProductViewed:]
      • -[Braze logEcommerceCartUpdated:]
      • -[Braze logEcommerceCheckoutStarted:]
      • -[Braze logEcommerceOrderPlaced:]
Fixed
  • Fixes a rare race condition where the app would become unresponsive when calling changeUser or wipeData while an HTML in-app message or banner was in the middle of displaying.

14.2.1

Fixed
  • Improves the reliability of resuming the SDK’s tracking of Live Activities when there are multiple active activity types.
    • This improves the tracking of Live Activities when relaunching the app after it has been terminated.
  • Fixes a compilation issue introduced in 14.2.0 on Mac Catalyst targets caused by ActivityKit imports.

14.2.0

Added
  • Adds methods to observe all key events and errors in the ActivityKit API, enabling observation of real-time state and error events from the SDK’s Live Activity lifecycle.
Fixed
  • Improves reliability of Live Activity push token updates during app background and foreground transitions, including cold start scenarios where push-to-start activities may not have received token updates.
  • Content cards now filter out invalid cards so users can still view remaining valid cards.
    • Previously, if any of the cards were invalid in the content card sync, the entire sync would be dropped and no cards would be added.
    • This update brings parity with the behavior on Android and Web.

14.1.0

Added
  • Adds support for Banner dismissal events.
  • Improves the robustness of the SDK’s internal state management.
    • This release includes an internal refactor intended to make SDK behavior more consistent. No external API changes.
  • Adds error logging for Banners operations, providing actionable diagnostics for persistence failures and invalid banner states.
Fixed
  • Improves robustness around push notification and deep link handling during delayed SDK initialization.
  • Fixes an issue where Braze.FeatureFlags.subscribeToUpdates would not trigger the update closure upon all refresh completions.
    • All refresh completions, regardless of a success or error result, will now trigger the update closure. This change brings parity with the Android and Web SDKs.
    • Previously, the update closure would not always trigger upon the completion of a refresh request, depending on whether the cached data had previously been reported.

14.0.4

Fixed
  • Fixes an issue where the configuration of push notification automation would be dropped upon every other re-initialization of the Braze instance.

14.0.3

Fixed
  • Push Stories now filter out invalid pages so users can still navigate through remaining valid pages.

14.0.2

Fixed
  • Fixes the SwiftUI implementation of BannerView to update Banner contents in-place whenever a refresh has succeeded.
  • Re-exposes the public initializer of BrazeInAppMessageUI.HtmlView as a designated init instead of a convenience init, which was introduced in version 14.0.0
    • This allows subclasses of HtmlView to access the public initializer.
  • Improves robustness of internal SDK logic around dictionary access to prevent potential crashes.

14.0.1

Fixed
  • Resolves an issue where the handling of universal links defaulted to the UIApplicationDelegate implementation instead of the UISceneDelegate implementation when the app was not in foreground.
    • This would occur even if there was no UIApplicationDelegate implementation, resulting in dropped universal link handling under such scenarios.
  • Fixes a memory leak where base64-encoded tracking IDs in in-app messages would accumulate on background threads.
  • Resolves an issue where in-app messages were not dismissed when the user is changed, resulting in the user seeing incorrect content.
    • This change also adds changeUser dismissal reason for in-app messages.

14.0.0

Breaking
  • Removes News Feed.
    • This fully removes all UI elements, data models, and actions associated with News Feed.
Added
  • Remote configuration now automatically refetches after SDK upgrades, keeping server defaults in sync and improving reliability after version changes.
Fixed
  • Resolves an issue where long text in in-app message buttons would wrap to multiple lines.
    • These messages will now match the dashboard preview behavior of truncating long text.
  • Push Stories now fail gracefully when receiving null/empty deeplink values.
    • Previously, an invalid deeplink would cause the Push Story’s content to appear blank.
    • StoryPage safely trims and percent-encodes deeplink strings, dropping invalid values instead of throwing an error.
    • StoryView only scrolls when pages exist, preventing the “Next” action from crashing when the carousel is empty.
  • HTML in-app messages now reuse cached payloads to mitigate app hangs that occur in rare situations during presentation.
  • Templated in-app messages with delayed presentation will now request templated values only after completion of the delay.
    • This ensures that templated values are most up-to-date with the display of the message.
    • Previously, the request for templated values would occur at trigger time, prior to the delay.

13.3.0

Added
  • Improves reliability when sending the push token and push authorization status to the backend.
    • This change ensures that push authorization status changes will be flushed immediately as soon as they are read.

13.2.1

Fixed
  • Resolves an issue where an accumulation of Banners pending requests could cause the host application to hang at app startup.
    • This fix performs additional cleanup to any existing requests that were accumulated from previous versions, so you do not need to do any manual cleanup.

13.2.0

Added
  • Adds support for compilation with Xcode 26.0 and its corresponding operating system runtimes on all platforms supported by the Braze Swift SDK.

13.1.0

Added
  • Adds support for Banner properties via new public methods on Braze.Banner instances.
    • banner.stringProperty(key:) for accessing String properties.
    • banner.numberProperty(key:) for accessing Double properties.
    • banner.timestampProperty(key:) for accessing Int Unix millisecond timestamp properties.
    • banner.booleanProperty(key:) for accessing Bool properties.
    • banner.imageProperty(key:) for accessing image URL properties as Strings.
    • banner.jsonProperty(key:) for accessing JSON properties as [String:Any] dictionaries.
    • banner.jsonProperty<T: Decodable>(key:type:decoder:) for accessing JSON properties as values of any custom Decodable type.
  • The default client-side rate limiting values for Banners refresh has been increased. For more information on SDK rate limiting, please refer to the Braze Developer Guide
Fixed
  • Improves the behavior of VoiceOver for assets that are missing an imageAltText for Content Card and In-App Message campaigns created via the Traditional editor.
    • These assets will no longer be selectable or narrated by VoiceOver. Previously, the asset would be selectable and VoiceOver would read gibberish.
    • Drag-and-drop campaigns are not affected by this issue.
    • Campaigns created using the Traditional editor should always have the Alt text field populated for accessible users.

13.0.0

Breaking
  • Extends the functionality of BrazeSDKAuthDelegate.braze(_:sdkAuthenticationFailedWithError:) to be triggered for “Optional” authentication errors.
    • The delegate method BrazeSDKAuthDelegate.braze(_:sdkAuthenticationFailedWithError:) will now be triggered for both “Required” and “Optional” authentication errors.
    • If you want to only handle “Required” SDK authentication errors, add a check ensuring that BrazeSDKAuthError.optional is false inside your implementation of this delegate method.
  • Fixes the usage of Braze.Configuration.sdkAuthentication to take effect when enabled.
    • Previously, the value of this configuration was not consumed by the SDK and the token was always attached to requests if it was present.
    • Now, the SDK will only attach the SDK authentication token to outgoing network requests when this configuration is enabled.
  • The setters for all properties of Braze.FeatureFlag and all properties of Braze.Banner have been made private. The properties of these classes are now read-only.
  • Removes the banner.id property, which was deprecated in version 11.4.0.
    • Instead, use banner.trackingId to read a banner’s campaign tracking ID.
Added
  • Adds the boolean field optional to BrazeSDKAuthError to indicate if it is an optional authentication error.

12.1.0

Added
  • Adds optional imageAltText and language fields to UI classes and structs associated with Content Card and In-App Message campaigns for improved accessibility.
    • The imageAltText field contains the image accessibility alt text (if any) for the image or icon in a given campaign. The SDK’s default UI will use this field to inform how VoiceOver narrates the image portion of a campaign
    • The optional language field is a BCP 47 tag. If it is present, VoiceOver will use the corresponding language narrator when reading the campaign. Otherwise, the user system default settings will be used.
    • These are the classes and structs with imageAltText and language:
      • Braze.ContentCard.ClassicImage
      • Braze.ContentCard.ImageOnly
      • Braze.ContentCard.CaptionedImage
      • Braze.ContentCardRaw (BRZContentCardRaw in Objective-C)
      • Braze.InAppMessage.Slideup
      • Braze.InAppMessage.Modal
      • Braze.InAppMessage.ModalImage
      • Braze.InAppMessage.Full
      • Braze.InAppMessage.FullImage
      • Braze.InAppMessageRaw (BRZInAppMessageRaw in Objective-C)
      • Braze.ContentCard.Classic has the language field only
  • Adds provisional support for Xcode 26 Beta via the braze-inc/braze-swift-sdk-xcode-26-preview repository.
    • Full support will be added to the main repository closer to the public release of Xcode 26.
    • For any compatibility issues discovered while using the Xcode 26 Beta, submit a GitHub issue on the main repository.

12.0.3

Fixed
  • Fixes the Banner rendering incompatibility with iOS 18.5+ while maintaining the correct URL redirect behavior.
    • Banners can now successfully render on iOS 18.5+ without compromising click action functionality.
    • See the changelog entries for versions 12.0.1 and 12.0.2 for further details.

12.0.2

⚠️ Important: This version has a known issue preventing Banners from rendering on iOS 18.5+.

Fixed
  • Reverts Banners to the behavior found in versions 12.0.0 and prior.
    • Banners remain unusable on iOS 18.5+. A future release will address this issue.

12.0.1

⚠️ Important: This version has a known issue in Drag-and-Drop in-app messages and Banners, preventing certain URLs from redirecting properly. Update to a newer version if you are using this feature.

Fixed
  • Fixes an issue where setting configuration.forwardUniversalLinks = true would not properly forward universal links to the system APIs in some cases.
    • The SDK now verifies that the system APIs are implemented (either in your UIApplicationDelegate or SceneDelegate) before forwarding the universal link.
    • When multiple implementations are found, the SDK favors the SceneDelegate implementation over the UIApplicationDelegate implementation.
  • Fixes an issue when configuring Braze.Configuration.Push.Automation.authorizationOptions with the .provisional option.
    • Previously, the .provisional option was also applied for push primer in-app messages. This resulted in no push notification permission prompt being shown to the user.
    • With this change, push primer in-app messages will request push notification permissions using only the .alert, .badge, and .sound options, ensuring that the system prompt is presented to the user.
  • Fixes an incompatibility with iOS 18.5 where Banners would not render.
    • Previously, the Banner view would be added to the view hierarchy with a height of 0 but never successfully load the HTML content.
    • Banner views will no longer trigger superfluous about:blank URLs upon initial load.

12.0.0

Breaking
  • The distributed static XCFrameworks now include their resources directly instead of relying on external resources bundles.
    • When manually integrating the static XCFrameworks, you must select the Embed & Sign option for each XCFramework in the Frameworks, Libraries, and Embedded Content section of your target’s General settings.
    • No changes are required for Swift Package Manager or CocoaPods integrations.
Fixed
  • Fixes an App Store validation issue where Braze’s libraries privacy manifests would fail to be detected when integrating the SDK as static XCFrameworks.
  • Fixes BrazeKitCompat ABKContentCard.expiresAt to return the correct expiration date.
    • Previously, ABKContentCard.expiresAt would always return 0.
  • Fixes an issue where the Braze.FeatureFlags.subscribeToUpdates(_:) update closure was being called immediately after calling changeUser(userId:) instead of waiting for the next feature flags sync result.
  • Fixes an issue where Braze.ContentCards.subscribeToUpdates(_:) would not call the update closure whenever a sync occurred without any changes in the Content Cards data.
    • Previously, the update closure would only be called when the sync resulted in a change.
  • Fixes the Braze.User.set(dateOfBirth:) method to report dates using the Gregorian calendar instead of the device’s current calendar setting.
    • Previously, the SDK would override input dates and formats if the device’s calendar settings were non-Gregorian.
    • With this change, you will still need to ensure that dates provided to set(dateOfBirth:) are generated with the Gregorian calendar, but the Braze SDK will no longer override their formats inadvertently.
  • Enhances the ⁠braze.wipeData() function to send a final update to all registered channel subscribers, notifying them of the data wipe.
    • This update ensures that any UI components utilizing the channel’s data are properly dismissed and cleaned up.
    • For instance, if an in-app message is currently displaying as braze.wipeData() is called, the message will be removed from display.
  • Fixes braze.user.id not resetting to nil after calling braze.wipeData().
    • Internally, the user identifier was properly reset, but the public braze.user.id property was not updated to reflect this change.
Added
  • Adds the BrazeInAppMessagePresenter.dismiss(reason:) optional protocol method.
    • This method enables the SDK to inform the in-app message presenter when an in-app message should be dismissed due to an internal SDK state change.
    • Currently, this method is triggered only by calling ⁠braze.wipeData().
    • BrazeInAppMessageUI implements this optional method and dismisses the in-app message when triggered.

11.9.0

Added
Fixed
  • The SDK Debugger tool will now capture logs even when Braze.configuration.logger.level is .disabled and no SDK logging occurs locally.
    • This aligns the Braze Swift SDK Debugger Tool behavior with that of the Debugger Tool on the Braze Android SDK.
  • Sets the default background of BannerUIView to be transparent.
  • Renames the VisibilityTracker.displayLinkTick method to VisibilityTracker.brazeDisplayLinkTick in BrazeUI to avoid potential naming conflicts with private system methods.

11.8.0

Added
  • Network requests made by the SDK to the Braze Live Activities /push_token_tag endpoint will now be retried in the case of a request failure.
  • Expands customizability options of custom endpoints passed when initializing a Braze instance.
    • You can now specify a base path to be used for SDK network requests (i.e. “example.com/mockServer”).
    • http schemes are now supported for use by custom endpoints (i.e. http://example.com). Previously, only https schemes were supported.
Fixed
  • Fixes an issue where in-app messages would not always be triggered when sending Braze requests to the tracking endpoint. This occurred when both of the following conditions are true:
    • The Braze.Configuration.Api.trackingPropertyAllowList did not include the .everything type.
    • All other Braze.Configuration.TrackingProperty types were manually listed in the trackingPropertyAllowList.
  • Improves the rendering behavior of Banner Cards embedded in a scroll view on hybrid development frameworks.
  • Fixes the Banner Card view to prevent drag gestures from exposing the background of the HTML content.
  • Fixes an issue on the Braze web view bridge where numeric values of 1 or 0 would be incorrectly reported as true or false, respectively.

11.7.0

Added
  • Adds the ability for a banner container to resize when the banner content changes height.

11.6.1

Fixed
  • Improves the reliability of collecting Live Activity push-to-start tokens on calling registerPushToStart:
    • Push-to-start tokens will now flush to the server immediately as soon as they are retrieved.
    • Push-to-start tokens will now be read immediately from the pushToStartToken property as soon as registerPushToStart is called, in addition to the existing behavior where an observable is set up to monitor new tokens.
  • Resolves issues with the SDK’s internal state for devices that were previously affected after restoring from another device’s iCloud or iTunes backup.
    • Previously, these devices would incorrectly inherit the device ID from the original device.
    • With this update, the SDK now generates a unique device ID for each restored device, ensuring proper identification and functionality.
    • This update follows up on the 11.6.0 fix, which prevented the issue from occurring on future backups.

11.6.0

Fixed
  • Fixes the behavior in the Braze-provided UI for Banner Cards where content would not automatically be cleared from the UI when changing to a user that was not eligible for that campaign.
  • Changes the behavior of Braze.Banners.subscribeToUpdates(_:) to match behavior of the corresponding API on the Braze Android SDK.
    • Upon calling Braze.Banners.subscribeToUpdates(_:), the update handler closure will only be called if a banners sync has succeeded during the current user session.
      • Previously, calling Braze.Banners.subscribeToUpdates(_:) would always result in the update handler being called one time immediately.
    • Upon successfully completing a banners sync, Braze.Banners.subscribeToUpdates(_:) will call its registered update handler even if the sync result is identical to the last successful sync.
  • Changes the behavior of Braze.Banners.bannersStream to match behavior of the corresponding API on the Braze Android SDK.
    • Braze.Banners.bannersStream will now only emit an update immediately upon access if a banners sync has succeeded during the current user session.
      • Previously, accessing Braze.Banners.bannersStream would always emit one update immediately.
    • Upon successfully completing a banners sync, Braze.Banners.bannersStream will emit an update even if the sync result is identical to the last successful sync.
  • JavaScript bridge methods expecting number arguments now also accept string representations of numbers.
    • This change aligns the behavior of the Swift SDK with the behavior of the Web SDK.
Added
  • Adds an optional method removeBannerContent to the BrazeBannerPlacement protocol.
  • Locally persisted Braze SDK data will no longer transfer during OS backups. This resolves an issue introduced in 6.2.0.

11.5.0

Fixed
  • Braze.banners.getBanner(for:_:) now successfully returns a cached Banner object for the requested placement ID as long as a Banner Cards sync has ever succeeded for the current user.
    • Previously, it would log a warning and pass nil to the completion handler if a Banner Cards sync had not been completed for the current user during the current session specifically.
    • This change aligns behavior with the Android SDK.
  • Fixes an issue where images with the "JPEG" image type would sometimes not display in Push Stories.
  • Fixes an issue where an in-app message in a Braze-provided UI can be displayed for an ineligible user under rare conditions.
    • This may occur if the in-app message was in the process of being displayed in the UI at the same time that the user was changed to a different user.
Added
  • Adds Braze.User.id to access the current user identifier synchronously.
    • Deprecates Braze.User.id() async and Braze.User.id(queue:completion:) in favor of Braze.User.id.
      • These methods will be removed fully in a future update.
  • Adds the optional parameter userIDMatchBehavior to the initializers of Braze.InAppMessageRaw.Context. This determines the behavior in the UI when the current identified user is different from the one that triggered the in-app message.
    • The default for Braze-provided UIs (.enforce) will enforce that the user ID matches the user ID that triggered the in-app message. If there is a mismatch, the in-app message will not be displayed.
    • For custom UIs, the default is .ignore and a mismatch will still display the in-app message.

11.4.0

Fixed
  • Fixes an issue where the SDK could hang during initialization if previous sessions generated a large number of geofence refreshes. This hang could sometimes lead to a crash by blocking the main thread for an extended period.
  • Fixes an issue where the triggering of in-app messages could be delayed in cases where requests for updated in-app message triggers are also delayed due to rate limiting.
  • Adds additional safeguards to ensure that ongoing network requests are dropped when changing users mid-flight.
Added
  • When Content Cards, Feature Flags, or Banner Cards go from enabled to disabled, the stored data is removed from cache.
  • Adds banner.trackingId to distinguish between banner objects.
    • Deprecates banner.id in favor of banner.trackingId.

11.3.0

Fixed
  • Fixes a behavior where calling the logClick bridge method in HTML in-app messages with "" as the button ID would log an error.
    • Instead, this would log an in-app message body click to match other platforms.
Added
  • Adds support for the Braze Banner Cards product.
    • For usage details, refer to our tutorial here.

11.2.0

Fixed
  • Fixes the Objective-C Braze.delegate declaration to be weak like the Swift variant.
Added
  • Braze.prepareForDelayedInitialization now takes an optional parameter analyticsBehavior: PushEnqueueBehavior.
    • Braze uses this value to determine whether any Braze push payloads received before initialization should be processed once initialization is complete.
    • PushEnqueueBehavior.queue will enqueue received push payloads to be processed upon initialization. This option is selected by default.
    • PushEnqueueBehavior.drop will drop received push payloads, ignoring them.
  • Adds configuration properties to customize the lineSpacing, maxLineHeight, minLineHeight, and lineHeightMultiple for the header and message texts in full and modal in-app messages.
  • Updates BrazeContentCardUI.ViewController.Attributes.defaults to be a var to allow directly editing the property for convenience.

11.1.1

Fixed
  • Fixes an issue introduced in 11.0.0 where the push subscription status would be sent to the backend with an inaccurate value at startup, causing an unexpected subscription state. The SDK now sends up the accurate subscription status at each startup.

11.1.0

⚠️ Important: This version has a known issue related to push subscription status. Upgrade to version 11.1.1 instead.

Fixed
  • Fixes an issue introduced in 11.0.0 where the push token status would not always be reported in all circumstances.
  • Fixes a display bug where an in-app message would appear truncated after certain keyboard dismissal scenarios.
  • Fixes a reference cycle in Braze.NewsFeedCard.Context that could prevent the card from being deallocated.
Added
  • Adds a public initializer for Braze.Notifications.Payload.

11.0.1

Fixed
  • Fixes an issue introduced in 11.0.0 where the push subscription status would be sent to the backend with an inaccurate value at startup, causing an unexpected subscription state. The SDK now sends up the accurate subscription status at each startup.

11.0.0

⚠️ Important: This version has a known issue related to push subscription status. Upgrade to version 11.1.1 instead.

Breaking
  • Adds support for Swift 6 strict concurrency checking.
    • Relevant public Braze classes and data types now conform to the Sendable protocol and can be safely used across concurrency contexts.
    • Main thread-only APIs are now marked with the @MainActor attribute.
    • We recommend using Xcode 16.0 or later to take advantage of these features while minimizing the number of warnings generated by the compiler. Previous versions of Xcode may still be used, but some features may generate warnings.
  • When integrating push notification support manually, you may need to update the UNUserNotificationCenterDelegate conformance to use the @preconcurrency attribute to prevent warnings.
    • Applying the @preconcurrency attribute on protocol conformance is only available in Xcode 16.0 or later. Reference our sample integration code here.
    • As of Xcode 16.0, Apple has not yet audited the UNUserNotificationCenterDelegate protocol for Swift concurrency.
      1
      2
      3
      
      extension AppDelegate: @preconcurrency UNUserNotificationCenterDelegate {
      // Your existing implementation
      }
      
  • Updates the SDWebImage dependency in BrazeUICompat and sample apps to 5.19.7+ to support Swift 6 strict concurrency checking.

Fixed

  • Fixes the push authorization status reporting to display the proper push token status on the Dashboard when a user has not explicitly accepted or declined push permissions.

10.3.1

Fixed
  • Improves the reliability of sending updates to Live Activities that were launched via a push-to-start notification to an app in the terminated state.

10.3.0

Fixed
  • Fixes the in-app message orientation validation logic, which prevented certain device classes from displaying messages under certain orientation configurations.
  • Fixes the default behavior on full-screen in-app messages to display as modals only on tablet screen sizes.
    • Previously, full-screen messages would erroneously default to modal presentations on some larger phones.
  • Fixes a crash when dismissing a slideup in-app message before it has finished presenting.
  • Fixes an issue on iOS 18.0+ where the in-app message UI would persist on the screen when attempting to dismiss the message before it has finished presenting.
  • Updates custom attribute value, custom event, and purchase string validation to use a 255 character maximum instead of a 255 byte maximum.
Added

10.2.0

Fixed
  • Updates the content card image background color to be clear.
Added
  • Adds support for an upcoming Braze SDK Debugging tool.

10.1.0

Fixed
  • Fixes an issue affecting the Objective-C variants of BrazeDelegate, BrazeContentCardUIViewControllerDelegate and BrazeInAppMessageUIDelegate.
    • When setting these delegates in Objective-C a second time, the delegate would end up being set to nil.
    • This issue has been resolved and the delegates can now be set multiple times without issue.
Added

10.0.0

Breaking
  • The following changes have been made when subscribing to Push events with Braze.Notifications.subscribeToUpdates(payloadTypes:_:):
    • The update closure will now be triggered by both “Push Opened” and “Push Received” events by default. Previously, it would only be triggered by “Push Opened” events.
      • To continue subscribing only to “Push Opened” events, pass in [.opened] for the parameter payloadTypes. Alternatively, implement your update closure to check that the type from the Braze.Notifications.Payload is .opened.
    • When receiving a push notification with content-available: true, the Braze.Notifications.Payload.type will now be .received instead of .opened.
  • Marks the following deprecated APIs as unavailable:
    • Braze.Configuration.Api.Flavor
    • Braze.Configuration.Api.flavor
    • Braze.Configuration.Api.SdkMetadata
    • Braze.Configuration.Api.addSdkMetadata(_:)
    • Braze.ContentCard.ClickAction.uri(_:useWebview:)
    • Braze.ContentCard.ClickAction.uri
    • Braze.InAppMessage.ClickAction.uri(_:useWebview:)
    • Braze.InAppMessage.ClickAction.uri
    • Braze.InAppMessage.ModalImage.imageUri
    • Braze.InAppMessage.Full.imageUri
    • Braze.InAppMessage.FullImage.imageUri
    • Braze.InAppMessage.Themes.default
    • Braze.deviceId(queue:completion:)
    • Braze._objc_deviceId(completion:)
    • Braze.deviceId()
    • Braze.User.setCustomAttributeArray(key:array:fileID:line:)
    • Braze.User.addToCustomAttributeArray(key:value:fileID:line:)
    • Braze.User.removeFromCustomAttributeArray(key:value:fileID:line:)
    • Braze.User._objc_addToCustomAttributeArray(key:value:)
    • Braze.User._objc_removeFromCustomAttributeArray(key:value:)
    • gifViewProvider
    • GifViewProvider.default
  • Removes the deprecated APIs:
    • Braze.Configuration.DeviceProperty.pushDisplayOptions
    • Braze.InAppMessageRaw.Context.Error.extraProcessClickAction
  • Removes the deprecated BrazeLocation class in favor of BrazeLocationProvider.
Fixed
  • Fixes a crash when handling a scheme-based deep link containing a registered applink domain (e.g. applinks:example.com with a deep link to app://example.com/path).
Added
  • Adds support to subscribe to “Push Received” events via Braze.Notifications.subscribeToUpdates(payloadTypes:_:).
    • The following notifications will trigger this subscription:
      • Notifications received in the foreground
      • Notifications with the field content-available: true received in the foreground or background
    • The following notifications will not trigger this subscription:
      • Notifications received while terminated
      • Notifications received in the background without the field content-available: true
    • The new parameter payloadTypes will allow you to subscribe to “Push Opened” events, “Push Received” events, or both. If the parameter is omitted, it will subscribe to both by default.
    • If you are using manual push integration, you will need to first implement UNUserNotificationCenter.userNotificationCenter(_:willPresent:withCompletionHandler:), and make sure to call Braze.Notifications.handleForegroundNotification(notification:) within your implementation. Then, use subscribeToUpdates as noted above. See our guide on push notification integration for more info.
  • Adds the public property Braze.Notifications.Payload.type.
  • Adds the Braze.WebViewBridge.ScriptMessageHandler.init(braze:) initializer enabling a simpler way to initialize the ScriptMessageHandler for adding it to user-provided web views.

9.3.1

Fixed
  • Fixes an issue where the Braze.FeatureFlag.subscribeToUpdates(_:) callback was not triggered at app launch when the cached feature flags matched the remote feature flags.
  • Fixes an issue in Objective-C projects where the return value of Braze.FeatureFlag.jsonProperty(key:) would incorrectly encode any entry value equal to null under certain conditions.
    • [String: Any] dictionaries returned by the Swift API will now drop null values.
    • NSDictionary objects returned by the Objective-C API will now encode null values as NSNull.

9.3.0

Added
  • Adds Objective-C support for the BrazeInAppMessageUIDelegate.inAppMessage(_:prepareWith:) method.
    • Customization of ViewAttributes via the attributes property is not available in the Objective-C version of PresentationContextRaw.
  • Adds Braze.FeatureFlag.jsonProperty(key:type:decoder:) to decode jsonobject type Feature Flag properties into custom Decodable types.
  • Deprecates the existing Feature Flag APIs, to be removed in a future version:
    • Braze.FeatureFlag.jsonStringProperty(key:) has been deprecated.
    • Braze.FeatureFlag.jsonObjectProperty(key:) has been deprecated in favor of Braze.FeatureFlag.jsonProperty(key:).
Fixed
  • Fixes an issue where the preferredOrientation on the presentation context of an in-app message would not be respected.

9.2.0

Added
  • Adds the openNewWindowLinksInBrowser configuration (default: false) to Braze.ModalContext.
    • Set this value in the braze(_:willPresentModalWithContext:) method of your BrazeDelegate to specify whether to launch the device browser to open web view hyperlinks that normally open a new tab or window.
Fixed
  • Fixes an issue with the automatic push integration feature that could cause the SDK not to send the device token to Braze.
  • Fixes an issue that prevented external links, which open in a new tab, from being activated in presented web views.

9.1.0

Added
  • Adds support for 3 new Feature Flag property types and various APIs for accessing them:
    • Braze.FeatureFlag.timestampProperty(key:) for accessing Int Unix millisecond timestamps.
    • Braze.FeatureFlag.imageProperty(key:) for accessing image URLs as Strings.
    • Braze.FeatureFlag.jsonObjectProperty(key:) for accessing JSONs as [String:Any] dictionaries.
    • Braze.FeatureFlag.jsonStringProperty(key:) for accessing JSONs as Strings.
  • Adds safeguards when reading the device model.
Fixed
  • Fixes the duplicate symbols compilation errors and runtime warnings that may occur under specific conditions when integrating BrazeKit and either BrazeNotificationService or BrazePushStory via CocoaPods.

9.0.0

Breaking
  • Removes the default privacy tracking domains from the BrazeKit privacy manifest.
    • If you are using the Braze data tracking features, you will need to manually add your tracking endpoint to your app-level privacy manifest.
    • Refer to the updated tutorial for integration guidance.
  • Removes the deprecated BrazeDelegate.braze(_:sdkAuthenticationFailedWithError) method in favor of BrazeSDKAuthDelegate.braze(_:sdkAuthenticationFailedWithError).
    • This method was originally deprecated in release 5.14.0.
    • Failing to switch to the new delegate method will not trigger a compiler error; instead, the BrazeDelegate.braze(_:sdkAuthenticationFailedWithError) method you define will simply not be called.
Fixed
  • Adds the missing NSPrivacyCollectedDataTypes key to the BrazePushStory privacy manifest.

8.4.0

Added
  • Expands Geofences behavior in the background while “When In Use” authorization is selected:
    • Adds the Braze.Location.Configuration.allowBackgroundGeofenceUpdates property to toggle whether geofences should be updated in the background.
      • When using this setting, verify that you have enabled the Location updates background mode.
    • Adds the Braze.Location.Configuration.distanceFilter property to configure the minimum distance sensitivity for triggering a location update.
  • Adds support for the message_extras Liquid tag for in-app messages.

8.3.0

Added
  • Adds early access for a third alternative repository which provides all Braze modules as mergeable XCFrameworks. For instructions on how to leverage it, refer to the repository README:
Fixed
  • Adds a missing privacy manifest for BrazePushStory.
  • Fixes an invalid privacy manifest warning in BrazeLocation when submitting to the App Store as a dynamic XCFramework.
  • Fixes an issue where already enqueued in-app messages would not be removed from the stack after subsequent .reenqueue and .discard display actions.
  • Fixes an issue preventing retried requests from using an updated SDK authentication token until a new request was scheduled for processing.
  • Purchases, custom events, and nested custom user attributes can now include properties with values of any type conforming to BinaryInteger (Int64, UInt16, etc).
    • All values will be cast to Int before being logged.
    • This resolves an issue with a bugfix in 7.6.0.

8.2.1

Fixed
  • Fixes App Store validation issues when archiving with Xcode 15.3.

8.2.0

Added
  • Adds support for remotely starting Live Activities via push notifications.
  • Adds return values for existing liveActivities methods:
    • launchActivity(pushTokenTag:activity:) now returns a discardable Task<Void, Never>?.
  • Adds pushToStartTokens as a new tracking property type.

8.1.0

Added
  • Adds the is_test_send boolean value in the in-app message JSON representation.
  • Adds the Braze.subscribeToSessionUpdates(_:) method and Braze.sessionUpdatesStream property to subscribe to the session updates events generated by the SDK.
  • Adds public APIs to access BrazeKit, BrazeLocation and BrazeUI resources bundles:
    • Braze.Resources.bundle
    • BrazeLocationResources.bundle
    • BrazeUIResources.bundle
  • BrazeKit.overrideResourceBundle and BrazeUI.overrideResourceBundle have been deprecated in favor of BrazeKit.overrideResourcesBundle and BrazeUI.overrideResourcesBundle.
  • Re-enables visionOS sample apps requiring SDWebImage in Examples-CocoaPods.xcworkspace. SDWebImage for visionOS is now supported when using CocoaPods.
  • Updated SDWebImage dependency in BrazeUICompat to 5.19.0+.
Fixed
  • Fixes multiple issues on visionOS:

8.0.1

Fixed
  • Fixes the reported SDK version, see 8.0.0.
  • Removes crash data from the BrazeKit privacy manifest. This data type is not collected by Braze.

8.0.0

⚠️ Warning
  • This release reports the SDK version as 7.7.0 instead of 8.0.0.
Breaking
  • Compiles the SDK using Xcode version 15.2 (15C500b).
    • This also raises the minimum deployment targets to iOS 12.0 and tvOS 12.0.
  • The BrazeLocation class is now marked as unavailable. It was previously deprecated in favor of BrazeLocationProvider in 5.8.1.
Added
  • Adds support for visionOS 1.0.
    • ⚠️ Rich push notifications and Push Stories may not display as expected on visionOS 1.0. We are monitoring the latest versions for potential fixes.
    • ⚠️ CocoaPods is not yet supported by SDWebImage for visionOS. visionOS sample apps requiring SDWebImage have been disabled in the Examples-CocoaPods.xcworkspace. Refer to the SwiftPM or manual integration Xcode project instead.

7.7.0

Added
  • Updates the prebuilt release assets to include the privacy manifest for manual integrations of SDWebImage.
  • Enhances support for language localizations.
    • Introduces a localization for Azerbaijani strings.
    • Updates Ukrainian localization strings for accuracy.
Fixed
  • Fixes the default button placement for full in-app message views.
  • Fixes an issue where setting Braze.Configuration.Api.endpoint to a URL with invalid characters could cause a crash.
    • If the SDK is given an invalid endpoint, it will no longer attempt to make network requests and will instead log an error.
  • Fixes an issue preventing BrazeLocation from working correctly when using the dynamic XCFrameworks.

7.6.0

Added
  • Adds the Braze.InAppMessage.Data.isTestSend property, which indicates whether an in-app message was triggered as part of a test send.
  • Adds logic to separate Braze data into tracking and non-tracking requests.
    • Adds the following methods to set and edit the allow list for properties that will be used for tracking:
      • Braze.Configuration.Api.trackingPropertyAllowList: Set the initial allow list before initializing Braze.
      • Braze.updateTrackingAllowList(adding:removing:): Update the existing allow list during runtime.
    • For full usage details on these configurations, refer to our tutorial here.
Fixed
  • Adds safeguards to prevent a rare race condition occuring in the SDK network layer.
  • Prevents in-app message test sends from attempting re-display after being discarded by a custom in-app message UI delegate.
  • Fixes an issue in the default Braze in-app message UI where some messages were not being removed from the stack after display.
  • Fixes the compilation of BrazeKitCompat and BrazeUICompat in Objective-C++ projects.
  • Fixes an issue in BrazeUICompat where the header text in Full or Modal in-app messages would be duplicated in place of the body text under certain conditions.
  • Fixes the encoding of values of types Float, Int8, Int16, Int32, Int64, UInt, UInt8, UInt16, UInt32 and UInt64. Those types were previously not supported in custom events and purchase properties.
  • Fixes an issue preventing purchase events from being logged when the product identifier has a leading dollar sign.
  • Fixes an issue preventing custom attributes from being logged when the attribute key has a leading dollar sign.

7.5.0

Added
  • Adds privacy manifests for BrazeKit and BrazeLocation to describe Braze’s data collection policies. For more details, refer to Apple’s documentation on privacy manifests.
    • More fine-tuned configurations to manage your data collection practices will be made available in a future release.
  • Adds the optInWhenPushAuthorized configuration property to specify whether a user’s notification subscription state should automatically be set to optedIn when updating push permissions to authorized.
  • The WebKit Inspector developer tool is now enabled by default for all instances of BrazeInAppMessagesUI.HtmlView. It can be disabled by setting BrazeInAppMessagesUI.HtmlView.Attributes.allowInspector to false.
Fixed
  • Fixes an issue with the code signatures of XCFrameworks introduced in 7.1.0.
  • Fixes a crash on tvOS devices running versions below 16.0, caused by the absence of the UIApplication.openNotificationSettingsURLString symbol in those OS versions.
  • Fixes an issue where a content card would not display if the value under “Redirect to Web URL” was an empty string.
  • Fixes incorrect behavior in BrazeUI where tapping the body of a Full or Modal in-app message with buttons and an “Image Only” layout would dismiss that message and process the button’s click action.
    • Tapping the body will now be a no-op, bringing parity with other platforms.

7.4.0

Added
  • Adds two alternative repositories to support specialized integration options. For instructions on how to leverage them, refer to their respective READMEs:
  • In-App Message assets from URLs containing the query parameter cache=false will not be prefetched.
Fixed
  • Fixes XCFrameworks headers to use the #import syntax instead of @import for compatibility with Objective-C++ contexts.
  • Fixes the push token tag validation during Live Activity registration, accepting strings up to 256 bytes instead of 255 bytes.
  • Braze.ContentCards.unviewedCards no longer includes Control cards to bring parity with Android and Web.
  • Fixes an Objective-C metaclass crash that occurs when initializing a custom subclass of certain BrazeUI views.

7.3.0

Added
  • Adds support for Expo Notifications event listeners when using the automatic push integration.
Fixed
  • Fixes a rare concurrency issue that might result in duplicated events when logging large amount of events.
  • Fixes an issue where user.set(dateOfBirth:) was not setting the date of birth accurately due to variations in the device’s timezone.

7.2.0

Added
  • Exposes the BrazePushStory.NotificationViewController.didReceive methods for custom handling of push story notification events.
Fixed
  • Resolves an issue for in-app messages with buttons where tapping on the body would incorrectly execute the button’s click action.
    • Now, when you tap on the body of an in-app message with buttons, no event should occur.
  • Resolves a potential deadlock under rare circumstances in BrazeUI’s In-App messages presentation.
  • Fixes the default implementation for the Objective-C representation of BrazeInAppMessageUIDelegate.inAppMessage(_:shouldProcess:url:buttonId:message:view:) to return the proper click action URL.
  • Resolves an issue where the body of the modal in-app message may be displayed stretched on some device models.
  • Resolves an issue where BrazeInAppMessageUI could fail to detect the correct application window for presenting its post-click webview.
    • BrazeInAppMessageUI now prefers using the current key UIWindow instead of the first one in the application’s window stack.
Removed
  • Braze.Configuration.DeviceProperty.pushDisplayOptions has been deprecated. Providing this value no longer has an effect.

7.1.0

Fixed
  • Resolves an issue preventing templated in-app messages from triggering if a previous attempt to display the message failed within the same session.
  • Fixes an issue that prevented custom events and nested custom attributes from logging if had a property with a value that was prefixed with a $.
  • Fixes a bug in the Content Cards feed UI where the empty feed message would not display when the user only had control cards in their feed.
  • Adds additional safeguards when reading the device model.
Added
  • Adds a code signature to all XCFrameworks in the Braze Swift SDK, signed by Braze, Inc..
  • BrazeInAppMessageUI.DisplayChoice.later has been deprecated in favor of BrazeInAppMessageUI.DisplayChoice.reenqueue.

7.0.0

Breaking
  • The useUUIDAsDeviceId configuration is now enabled by default.
  • The Banner Content Card type and corresponding UI elements have been renamed to ImageOnly. All member methods and properties remain the same.
    • Braze.ContentCard.BannerBraze.ContentCard.ImageOnly
    • BrazeContentCardUI.BannerCellBrazeContentCardUI.ImageOnlyCell
  • Refactors some text layout logic in BrazeUI into a new Braze.ModalTextView class.
  • Updates the behavior for Feature Flags methods.
    • FeatureFlags.featureFlag(id:) now returns nil for an ID that does not exist.
    • FeatureFlags.subscribeToUpdates(:) will trigger the callback when any refresh request completes with a success or failure.
      • The callback will also trigger immediately upon initial subscription if previously cached data exists from the current session.
Fixed
  • Fixes compiler warnings about Swift 6 when compiling BrazeUI while using Xcode 15.
  • Exposes public imports for ABKClassicImageContentCardCell.h and ABKControlTableViewCell.h for use in the BrazeUICompat layer.
  • Adds additional safeguards around invalid constraint values for BrazeInAppMessageUI.SlideupView.
  • Resolves a Content Cards feed UI issue displaying a placeholder image in Classic cards without an attached image.
Added
  • Adds the enableDarkTheme property to BrazeContentCardUI.ViewController.Attributes.
    • Set this field to false to prevent the Content Cards feed UI from adopting dark theme styling when the device is in dark mode.
    • This field is true by default.

6.6.2

Fixed
  • Fixes an issue preventing purchase events from being logged when the product identifier has a leading dollar sign ($).
  • Fixes an issue preventing custom attributes from being logged when the attribute key has a leading dollar sign ($).

6.6.1

Fixed
  • Fixes a crash in the geofences feature that could occur when the number of monitored regions exceeded the maximum count.
  • Fixes an issue introduced in 6.3.1 that would always update a user’s push subscription status to optedIn on app launch if push permissions were authorized on the device settings.
    • The SDK now will only send the subscription status at app launch if the device notification settings goes from denied to authorized.
  • Braze.ContentCard.logClick(using braze: Braze) will now log a click regardless of whether the ContentCard has a ClickAction.
    • This behavior differs from the default API Braze.ContentCard.Context.logClick(), which has the safeguard of requiring a ClickAction to log a click.

6.6.0

Fixed
  • Fixes an issue in HTML in-app messages where custom event and purchase properties would always convert values for 1 and 0 to become true and false, respectively.
    • These property values will now respect their original form in the HTML.
  • Prevents the default Braze UI from displaying in-app messages underneath the keyboard when Stage Manager is in use.
Added

6.5.0

Fixed
  • Content card impressions can now be logged any number of times on a single card, bringing parity with Android and Web.
    • This removes the limit introduced in 6.3.1 where a card impression could only be logged once per session.
    • In the Braze-provided Content Cards feed UI, impressions will be logged once per feed instance.
Added
  • Adds a simplified method for integrating push notification support into your application:
    • Automatic push integration can be enabled by setting configuration.push.automation = true on your configuration object.
      • This eliminates the need for the manual push integration outlined in the Implement the push notification handlers manually tutorial section.
      • When enabled, the SDK will automatically implement the necessary system delegate methods for handling push notifications.
      • Compatibility with other push providers, whether first or third party, is maintained. The SDK will automatically handle only Braze push notifications, while original system delegate methods will be executed for all other push notifications.
    • Each automation step can be independently enabled or disabled. For example, configuration.push.automation.requestAuthorizationAtLaunch = false can be used to prevent the automatic request for push permissions at launch.
    • Resources:
  • Adds the Braze.Configuration.forwardUniversalLinks configuration. When enabled, the SDK will redirect universal links from Braze campaigns to the appropriate system methods.
  • Adds the Braze.Notifications.subscribeToUpdates(_:) method to subscribe to the push notifications handled by the SDK.
  • Adds the Braze.Notifications.deviceToken property to access the most recent notification device token received by the SDK.

6.4.0

Fixed
  • Fixes an issue preventing text fields from being selected in HTML IAMs for iOS 15.
  • Fixes an issue where the device model was inaccurately reported as iPad on macOS (Mac Catalyst and Designed for iPad configurations).
  • Fixes an issue where custom event and purchase properties would not accept an entry if its value was an empty string.
  • Fixes a crash that occurred in the default UI for Content Cards when encountering a zero-value aspect ratio.
  • Fixes an issue introduced in 6.0.0 where images in in-app messages would appear smaller than expected when using the compatibility UI (BrazeUICompat).
Added
  • Adds the unviewedCards convenience property to the Braze.ContentCards class to get the unviewed content cards for the current user.

6.3.1

Fixed
  • Fixes an issue where the previous user’s data would not be flushed after calling changeUser(userId:sdkAuthSignature:) when the SDK authentication feature is enabled.
  • A content card impression can now be logged once per session. Previously, the Braze-provided Content Cards UI would limit to a single impression per card at maximum, irrespective of sessions.
  • Fixes an issue that previously caused push notification URLs with percent-encoded characters to fail during decoding.
  • Fixes a behavior to automatically set a user’s push subscription state to optedIn after push permissions have explicitly been authorized via the Settings app.
  • Correctly hides shadows on in-app messages that are configured with a transparent background.
  • Fixes a rare crash occurring when deinitializing the Braze instance.
Added
  • Adds additional logging for network-related decoding errors.

6.3.0

Fixed
  • “Confirm” and “Cancel” notification categories now show the correct titles on the action buttons.
Added

6.2.0

Fixed
  • Fixes a crash introduced in 6.0.0 when displaying an HTML in-app message using the BrazeUICompat module.
  • Removed a system call that is known to be slow on older versions of macOS. This resolves the SDK hanging during initialization on Mac Catalyst when running on affected macOS versions.
Added
  • Adds support for target attributes on anchor tags in HTML in-app messages (e.g. <a href="..." target="_blank"></a>).
    • Adding the target attribute to links will allow them to open in a new webview without dismissing the current in-app message.
    • This behavior can be disabled via the linkTargetSupport property of the BrazeInAppMessageUI.HtmlView.Attributes struct.
    • See our Custom HTML in-app messages documentation page for more details.

6.1.0

Fixed
  • Fixes an issue that led to disproportionately large close buttons on in-app messages when the user has set a large font size in the device settings.
  • Fixes an issue that would lock the screen in a specific orientation after the dismissal of an in-app message customized to be presented in that orientation.
    • This issue only impacted iOS 16.0+ devices.
Added
  • Adds new versions of setCustomAttribute to the User object to support nested custom attributes.
    • Renames User.setCustomAttributeArray(key: String, array: [String]?) to setCustomAttribute(…) to align it with other custom attribute setters, and adds “string” to the addTo and removeFrom attribute array methods to clarify which custom attributes they’re used for.

6.0.0

Breaking
Added

5.14.0

Fixed
  • VoiceOver now correctly focuses on in-app message views when they are presented.
  • Fixes an issue causing in-app messages with re-eligibility disabled to display multiple times under certain conditions.
  • Fixes an issue where modal and full in-app message headers were truncated on devices running iOS versions lower than 16 when displaying non-ASCII characters.
  • The dynamic variant of BrazeUI.framework in the release artifact braze-swift-sdk-prebuilt.zip is now an actual dynamic framework. Previously, this specific framework was mistakenly distributed as a static framework.
Added
  • Adds the BrazeSDKAuthDelegate protocol as a separate protocol from BrazeDelegate, allowing for more flexible integrations.
    • Apps implementing BrazeDelegate.braze(_:sdkAuthenticationFailedWithError:) should migrate to use BrazeSDKAuthDelegate and remove the old implementation. The BrazeDelegate method will be removed in a future major release.

5.13.0

Fixed
  • Fixes an issue where the SDK would automatically track body clicks on non-legacy HTML in-app messages.
Added
  • Adds the synchronous deviceId property on the Braze instance.
    • deviceId(queue:completion:) is now deprecated.
    • deviceId() async is now deprecated.
  • Adds the automaticBodyClicks property to the HTML in-app message view attributes. This property can be used to enable automatic body clicks tracking on non-legacy HTML in-app messages.
    • This property is false by default.
    • This property is ignored for legacy HTML in-app messages.

5.12.0

Starting with this release, this SDK will use Semantic Versioning.

Added
  • Adds json() and decoding(json:) public methods to the Feature Flag model object for JSON encoding/decoding.

5.11.2

Fixed
  • Fixes a crash occurring when the SDK is configured with a flush interval of 0 and network connectivity is poor.

5.11.1

Fixed
  • Fixes an issue preventing the correct calculation of the delay when retrying failed requests. This led to a more aggressive retry schedule than intended.
  • Improves the performance of Live Activity tracking by de-duping push token tag requests.
  • Fixes an issue in logClick(using:) where it would incorrectly open the url field in addition to logging a click for metrics. It now only logs a click for metrics.
    • This applies to the associated APIs for content cards, in-app messages, and news feed cards.
    • It is still recommended to use the associated Context object to log interactions instead of these APIs.
Added

5.11.0

Added
  • Adds support for Live Activities via the liveActivities module on the Braze instance.
    • This feature provides the following new methods for tracking and managing Live Activities with the Braze push server:
      • launchActivity(pushTokenTag:activity:)
      • resumeActivities(ofType:)
    • This feature requires iOS 16.1 and up.
    • To learn how to integrate this feature, refer to the setup tutorial.
  • Adds logic to re-synchronize Content Cards on SDK version changes.
  • Adds provisional support for Xcode 14.3 Beta via the braze-inc/braze-swift-sdk-xcode-14-3-preview repository.

5.10.1

Changed
  • Dynamic versions of the prebuilt xcframeworks are now available in the braze-swift-sdk-prebuilt.zip release artifact.

5.10.0

Fixed
  • Fixes an issue where test content cards were removed before their expiration date.
  • Fixes an issue in BrazeUICompat where the status bar appearance wasn’t restored to its original state after dismissing a full in-app message.
  • Fixes an issue when decoding notification payloads where some valid boolean values weren’t correctly parsed.
Changed
  • In-app modal and full-screen messages are now rendered with UITextView, which better supports large amounts of text and extended UTF code points.

5.9.1

Fixed
  • Fixes an issue preventing local expired content cards from being removed.
  • Fixes a behavior that could lead to background tasks extending beyond the expected time limit with inconsistent network connectivity.
Added
  • Adds logImpression(using:) and logClick(buttonId:using:) to news feed cards.

5.9.0

Breaking
  • Raises the minimum deployment target to iOS 11.0 and tvOS 11.0.
  • Raises the Xcode version to 14.1 (14B47b).
Fixed
  • Fixes an issue where the post-click webview would close automatically in some cases.
  • Fixes a behavior where the current user messaging data would not be directly available after initializing the SDK or calling changeUser(userId:).
  • Fixes an issue preventing News Feed data models from being available offline.
  • Fixes an issue where the release binaries could emit warnings when generating dSYMs.
Changed
  • SwiftPM and CocoaPods now use the same release assets.
Added
  • Adds support for the upcoming Braze Feature Flags product.
  • Adds the braze-swift-sdk-prebuilt.zip archive to the release assets.
    • This archive contains the prebuilt xcframeworks and their associated resource bundles.
    • The content of this archive can be used to manually integrate the SDK.
  • Adds the Examples-Manual.xcodeproj showcasing how to integrate the SDK using the prebuilt release assets.
  • Adds support for Mac Catalyst for example applications, available at Support/Examples/
  • Adds support to convert from Data into an in-app message, content card, or news feed card via decoding(json:).

5.8.1

Fixed
  • Fixes a conflict with the shared instance of ProcessInfo, allowing low power mode notifications to trigger correctly.
Changed
  • Renames the BrazeLocation class to BrazeLocationProvider to avoid shadowing the module of the same name (SR-14195).

5.8.0

To help migrate your app from the Appboy-iOS-SDK to our Swift SDK, this release includes the Appboy-iOS-SDK migration guide:

  • Follow step-by-step instructions to migrate each feature to the new APIs.
  • Includes instructions for a minimal migration scenario via our compatibility libraries.
Added
  • Adds compatibility libraries BrazeKitCompat and BrazeUICompat:
    • Provides all the old APIs from Appboy-iOS-SDK to easily start migrating to the Swift SDK.
    • See the migration guide for more details.
  • Adds support for News Feed data models and analytics.
    • News Feed UI is not supported by the Swift SDK. See the migration guide for instructions on using the compatibility UI.

5.7.0

Fixed
  • Fixes an issue where modal image in-app messages would not respect the aspect ratio of the image when the height of the image is larger than its width.
Changed
  • Changes modal, modal image, full, and full image in-app message view attributes to use the ViewDimension type for their minWidth, maxWidth and maxHeight attributes.
    • The ViewDimension type enables providing different values for regular and large size-classes.
Added
  • Adds a configuration to use a randomly generated UUID instead of IDFV as the device ID: useUUIDAsDeviceId.
    • This configuration defaults to false. To opt in to this feature, set this value to true.
    • Enabling this value will only affect new devices. Existing devices will use the device identifier that was previously registered.

5.6.4

Fixed
  • Fixes an issue preventing the execution of BrazeDelegate methods when setting the delegate using Objective-C.
  • Fixes an issue where triggering an in-app message twice with the same event did not place the message on the in-app message stack under certain conditions.
Added
  • Adds the public id field to Braze.InAppMessage.Data.
  • Adds logImpression(using:) and logClick(buttonId:using:) to both in-app messages and content cards, and adds logDismissed(using:) to content cards.
    • It is recommended to continue using the associated Context to log impressions, clicks, and dismissals for the majority of use cases.
  • Adds Swift concurrency to support async/await versions of the following public methods. These methods can be used as alternatives to their corresponding counterparts that use completion handlers:

5.6.3

Fixed
  • Fixes the InAppMessageRaw to InAppMessage conversion to properly take into account the extras dictionary and the duration.
  • Fixes an issue preventing the execution of the braze(_:sdkAuthenticationFailedWithError:) delegate method in case of an authentication error.
Changed
  • Improves error logging descriptions for HTTP requests and responses.

5.6.2

Changed
  • Corrected the version number from the previous release.

5.6.1

Added
  • Adds the public initializers Braze.ContentCard.Context(card:using:) and Braze.InAppMessage.Context(message:using:).

5.6.0

Fixed
  • The modal webview controller presented after a click now correctly handles non-HTTP(S) URLs (e.g. App Store URLs).
  • Fixes an issue preventing some test HTML in-app messages from displaying images.
Added
  • Learn how to easily customize BrazeUI in-app message and content cards UI components with the following documentation and example code:
  • Adds new attributes to BrazeUI in-app message UI components:
    • cornerCurve to change the cornerCurve
    • buttonsAttributes to change the font, spacing and corner radius of the buttons
    • imageCornerRadius to change the image corner radius for slideups
    • imageCornerCurve to change the image cornerCurve for slideups
    • dismissible to change whether slideups can be interactively dismissed
  • Adds direct accessors to the in-app message view subclass on the BrazeInAppMessageUI.messageView property.
  • Adds direct accessors to the content card title, description and domain when available.
  • Adds Braze.Notifications.isInternalNotification to check if a push notification was sent by Braze for an internal feature.
  • Adds brazeBridge.changeUser() to the HTML in-app messages JavaScript bridge.
Changed
  • The applyAttributes() method for BrazeContentCardUI views now take the attributes explicitly as a parameter.

5.5.1

Fixed
  • Fixes an issue where content cards would not be properly removed when stopping a content card campaign on the dashboard and selecting the option Remove card after the next sync (e.g. session start).

5.5.0

Added
  • Adds support for host apps written in Objective-C.
    • Braze Objective-C types start either with BRZ or Braze, e.g.:
      • Braze
      • BrazeDelegate
      • BRZContentCardRaw
    • See our Objective-C Examples project.
  • Adds BrazeDelegate.braze(_:noMatchingTriggerForEvent:) which is called if no Braze in-app message is triggered for a given event.
Changed
  • In Braze.Configuration.Api:
    • Renamed SdkMetadata to SDKMetadata.
    • Renamed addSdkMetadata(_:) to addSDKMetadata(_:).
  • In Braze.InAppMessage:
    • Renamed Themes.default to Themes.defaults.
    • Renamed ClickAction.uri to ClickAction.url.
    • Renamed ClickAction.uri(_:useWebView:) to ClickAction.url(_:useWebView:).

5.4.0

Fixed
  • Fixes an issue where brazeBridge.logClick(button_id) would incorrectly accept invalid button_id values like "", [], or {}.
Added
  • Adds support for Braze Action Deeplink Click Actions.

5.3.2

Fixed
  • Fixes an issue preventing compilation when importing BrazeUI via SwiftPM in specific cases.
  • Lowers BrazeUI minimum deployment target to iOS 10.0.

5.3.1

Fixed
  • Fixes an HTML in-app message issue where clicking a link in an iFrame would launch a separate webview and close the message, instead of redirecting within the iFrame.
  • Fixes the rounding of In-App Message modal view top corners.
  • Fixes the display of modals and full screen in-app messages on iPads in landscape mode.
Added

5.3.0

Added
  • Adds support for tvOS.
    • See the schemes Analytics-tvOS and Location-tvOS in the Examples project.

5.2.0

Added
Changed
  • Raises BrazeUI minimum deployment target to iOS 11.0 to allow providing SwiftUI compatible Views.

5.1.0

Fixed
  • Fixes an issue where the SDK would be unable to present a webview when the application was already presenting a modal view controller.
  • Fixes an issue preventing a full device data update after changing the identified user.
  • Fixes an issue preventing events and user attributes from being flushed automatically under certain conditions.
  • Fixes an issue delaying updates to push notifications settings.
Added

5.0.1

Fixed
  • Fixes a race condition when retrieving the user’s notification settings.
  • Fixes an issue where duplicate data could be recorded after force quitting the application.

5.0.0 (Early Access)

We are excited to announce the initial release of the Braze Swift SDK!

The Braze Swift SDK is set to replace the current Braze iOS SDK and provides a more modern API, simpler integration, and better performance.

Current limitations

The following features are not supported yet:

  • Objective-C integration
  • tvOS integration
  • News Feed
  • Content Cards
# iOS Objective-C SDK용 변경 로그 Source: /docs/ko/developer_guide/platforms/legacy_sdks/ios/changelog/objc_changelog/index.md
**Warning:** [AppboyKit](https://github.com/Appboy/appboy-ios-sdk) (also known as the Objective-C SDK) is no longer supported and has been replaced by the [Swift SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=swift). It will no longer receive new features, bug fixes, security updates, or technical support—however, messaging and analytics will continue to function as normal. To learn more, see [Introducing the New Braze Swift SDK](https://www.braze.com/resources/articles/introducing-the-new-braze-swift-sdk). # iOS Objective-C SDK 변경 로그

⚠️ The New Braze Swift SDK is now available!

4.7.0

Breaking

  • Updates the minimum required version of SDWebImage from 5.8.2 to 5.18.7.

Added

  • Adds the privacy manifest to describe data usage collected by Braze. For more details, refer to the Apple documentation on privacy manifests.
  • Adds code signatures to all XCFrameworks in the Braze iOS SDK, signed by Braze, Inc..
Fixed
  • Fixes an issue in Full or Modal in-app messages where the header text would be duplicated in place of the body text under certain conditions.

4.6.0

This release requires Xcode 14.x.

Breaking

  • Drops support for iOS 9 and iOS 10.
  • Removes support for the outdated .framework assets when importing via Carthage in favor of the modern .xcframework assets.
    • Use the command carthage update --use-xcframeworks to import the appropriate Braze asset.
    • Removes support for appboy_ios_sdk_full.json in favor of using appboy_ios_sdk.json by including these lines in your Cartfile:
      1
      2
      
      binary "https://raw.githubusercontent.com/Appboy/appboy-ios-sdk/master/appboy_ios_sdk.json"
      github "SDWebImage/SDWebImage"
      
Fixed
  • Improves resilience when triggering in-app messages with date property filters.
Added
  • Adds a new option ABKReenqueueInAppMessage to enum ABKInAppMessageDisplayChoice.
    • Return this option in beforeInAppMessageDisplayed: of an ABKInAppMessageControllerDelegate to ensure that an in-app message is not displayed and becomes eligible to trigger again.
    • This option will reset any trigger times and re-eligibility rules as if it was never triggered. It will not add the message to the in-app message stack.

4.5.4

Fixed
  • Improves reliability of custom event property type validation.
  • Fixes an issue where the status bar would not restore to its original state after a full in-app message was dismissed.

4.5.3

Fixed
  • Fixes a crash that occurs when receiving custom event properties of numeric types under certain conditions.
  • Fixes UI responsiveness warnings when requesting location authorization status.

4.5.2

Fixed
  • Improves reliability when validating trigger properties.
  • Improves the NSURLSessionConfiguration disk and memory cache capacities for file downloads. This change enables larger file downloads to be cached if needed.

4.5.1

Fixed
  • Improves eligibility checks around the minimum trigger timeout for in-app messages by now checking at trigger time in addition to display time.
  • Fixes an issue where purchases would not trigger certain templated in-app messages.
Added
  • Adds the delegate method noMatchingTriggerForEvent:name: to ABKInAppMessageControllerDelegate, which is called if no Braze in-app message was triggered for a given event.

4.5.0

Added
  • Adds support for Content Cards to evaluate Retry-After headers.

4.4.4

Fixed
  • Calling appboyBridge.closeMessage() or brazeBridge.closeMessage() from an HTML in-app message now correctly triggers ABKInAppMessageUIDelegate.onInAppMessageDismissed: when implemented.
  • Fixes an issue in 4.4.3 where the tvOS SDK incorrectly referenced an older SDK version.

4.4.3

Fixed
  • Fixes an issue introduced in 4.4.0 which prevented custom events or purchases with an empty dictionary of properties from being logged.
  • Improves handling of ABKInAppMessageWindow’s dismissal to promptly remove it from the view hierarchy.
  • Fixes the position of the pinned indicator for Captioned Image Content Cards when using the default UI.
  • Fixes an issue introduced in 4.3.2 and limited to users of Appboy-tvOS-SDK, which prevented custom events with properties or purchases with properties from being logged.
Added
  • Adds a padding property to ABKCaptionedImageContentCardCell to support modifying the default value.

4.4.2

Fixed
  • Fixes a bug for HTML in-app messages using the HTML Upload with Preview option to improve the reliability of in-app message display.
  • Fixes a bug preventing integration via Swift Package Manager in specific contexts.
  • Fixes an issue in the default Content Cards UI where the empty feed label was truncated if it was too large for the screen, for example due to accessibility or localization.
  • Fixes an issue where Slideup in-app messages would be automatically dismissed after multiple interaction with the app’s main window.
Changed
  • If changeUser:sdkAuthSignature: is called with the current user’s ID, but with a new and valid SDK Authentication signature, the new signature will be used.
  • Improves push tracking accuracy for apps making use of UISceneDelegate (UIKit) or Scene (SwiftUI).

4.4.1

Fixed
  • Fixes an issue in which input elements with type="date" in HTML in-app messages do not respond to some user interactions on iOS 14 and iOS 15.
  • Fixes ABKSdkMetadata availability when using the dynamic variant of the SDK.
  • Fixes an issue in which the default content cards UI’s empty feed label does not wrap properly when the device is using Larger Accessibility Sizes for its text size.
Changed
  • Changed ABKInAppMessageUIDelegate.inAppMessageViewControllerWithInAppMessage: to accept a nil return value.
Added
  • Adds support for the playsinline attribute on HTML <video> elements within webpages that are opened in the app by Braze.
  • Adds XCFramework support for the Core integration via Carthage. Please follow the Carthage migration guide when transitioning to the new artifact.

4.4.0

Breaking
  • Adds XCFramework support to Carthage. This allows projects integrated via Carthage to support Apple Silicon simulators and Mac Catalyst.
    • When migrating from the original .framework to the new .xcframework, follow the official Carthage migration guide.
    • For those using the Full integration, use the following lines in your Cartfile. Note that it references the file appboy_ios_sdk.json:
      1
      2
      
      binary "https://raw.githubusercontent.com/Appboy/appboy-ios-sdk/master/appboy_ios_sdk.json"
      github "SDWebImage/SDWebImage"
      
      • To continue using the original Full .framework file, include the Cartfile lines above but reference appboy_ios_sdk_full.json. Then, run carthage update.
    • For those using the Thin integration, use the same Cartfile above but exclude the line with SDWebImage.
    • The Core integration does not support XCFrameworks, and you can use the original .framework files as before.
Added
  • Adds a new attachment to the release called Appboy_iOS_SDK.xcframework.zip.
    • This artifact has the all-in-one XCFramework containing the full SDK code including all of the assets.
    • When importing this code manually, drag-and-drop the XCFramework into your project and select Embed & Sign. Then, add -ObjC under Build Settings > Other Linker Flags in your app’s target.
  • Adds localization support for the close button’s accessibility label in modal and full in-app messages.
  • Adds the ability to set the SDK’s log level at runtime by setting ABKLogLevelKey to an integer in appboyOptions. Descriptions of the available log levels can be found here.
  • Adds Appboy.addSdkMetadata: to allow self reporting of SDK Metadata fields via the ABKSdkMetadata enum.

4.3.4

This release requires Xcode 13.

Fixed
  • Fixes an issue in which the pinned indicator for a Banner Content Card would not display in the default Content Cards UI.
  • Fixes an issue which prevented custom events and purchases with properties larger than 50 KB to be properly discarded.

4.3.3

Fixed
  • Fixes a race-condition occasionally preventing HTML in-app messages with assets from being displayed from a test push.
  • Fixes an issue which prevented HTML in-app messages from opening sms:, mailto:, tel:, facetime: and facetime-audio: urls.
    • Previously, those urls would fail to open silently.
  • Fixes an issue where ABKContentCardsTableViewController was not displaying the “no update” label after the last card was deleted from the feed.
Added
  • Adds methods addToSubscriptionGroupWithGroupId: and removeFromSubscriptionGroupWithGroupId: to ABKUser to manage SMS/Email Subscription Groups.
    • Also adds appboyBridge.getUser().addToSubscriptionGroup(groupId) and appboyBridge.getUser().removeFromSubscriptionGroup(groupId) to the javascript interface for HTML in-app messages.

4.3.2

Fixed
  • Iframes embedded in an HTML in-app message are now displayed as part of the same in-app message. Previously, iframes would be loaded in a separate webview.
Added
  • Adds support for navigation bar transparency changes introduced in iOS 15. Apps using Braze default UIs for Content Cards, the News Feed, and the modal WebView should upgrade to this version as soon as possible ahead of iOS 15’s release.

4.3.1

Fixed
  • The sdkAuthenticationDelegate now works as expected when setting the property directly.
  • VoiceOver no longer reads content beneath the displayed in-app message.
Changed
  • The number of unviewed Content Cards in ABKContentCardsController’s unviewedContentCardCount now excludes control cards.
  • The default Content Cards UI now allows swipe-to-refresh gestures when empty.
  • Deprecates ABKInAppMessageController’s method displayNextInAppMessageWithDelegate: in favor of displayNextInAppMessage.
Added
  • Custom events and purchases now support nested properties.
    • In addition to integers, floats, booleans, dates, or strings, a JSON object can be provided containing dictionaries of arrays or nested dictionaries. All properties combined can be up to 50 KB in total length.

4.3.0

Breaking
  • Refined Content Cards UI public api changes introduced in 4.2.0.
Fixed
  • Fixes an issue introduced in 4.2.0 that caused Content Card type ABKClassicImageContentCardCell to crash on display when not using Storyboard.

4.2.0

⚠️ Known Issues
  • This release contains a known issue with the Content Cards default UI on iOS, where showing a “Classic” type card with an image causes a crash. If you are using the default Content Cards UI, do not upgrade to this version.
Breaking
  • Content Cards and News Feed are now more extensible!
Fixed
  • Fixes an issue with Dynamic Type support introduced in 3.34.0 to be compatible with iOS 9.
Added
  • Adds support for new SDK Authentication feature.
  • Exposes window.brazeBridge in HTML in-app messages which replaces window.appboyBridge. appboyBridge is deprecated and will be removed in a future version of the SDK.
Changed
  • Makes in-app message window handling more resilient:
    • The in-app message window tries to display up to 10 times when another window competes for visibility. If the in-app message is not guaranteed visibility, it is dismissed and an error is logged.
  • Improves Appboy’s wipeDataAndDisableForAppRun and disableSDK to handle additional use cases.
  • Deprecates flushDataAndProcessRequestQueue in favor of requestImmediateDataFlush.

4.1.0

Breaking
  • ABKURLDelegate method handleAppboyURL:fromChannel:withExtras: is now invoked for all urls.
    • Previously, this delegate method was not invoked for urls opened in a WebView or the default browser when originating from the News Feed or Content Cards.
  • Removes ABKUIURLUtils method openURLWithSystem:fromChannel:. Use openURLWithSystem: as a replacement.
Fixed
  • Fixes a case where the ABKURLDelegate method handleAppboyURL:fromChannel:withExtras: was being called twice when opening a push notification with an url.
Changed
  • Deprecates ABKUnknownChannel.

4.0.2

Fixed
  • Fixes a double redirection bug in Push Stories when the app is in a terminated state and application:didReceiveRemoteNotification:fetchCompletionHandler: is not implemented.
Changed
  • Improves the Swift Package Manager bundle lookup to be more flexible.
Added
  • Adds support to use a dictionary named Braze instead of Appboy when adding customization in the Info.plist. After adding the Braze dictionary, please remove the previous Appboy dictionary.

4.0.1

Fixed
  • Sets CFBundleSupportedPlatforms in .plist files to the correct non-simulator value.
  • Removes the Dynamic Type support warnings.

4.0.0

Breaking
  • AppboyKit is now distributed as an XCFramework when integrating with Cocoapods. Cocoapods 1.10.0+ is required.
Fixed
  • Fixes the Swift Package Manager cleanup script to remove only the necessary files.
Added
  • Adds Mac Catalyst support for apps integrating with Cocoapods.

3.34.0

Breaking
  • Replaces ABKInAppMessageSlideupViewController’s slideConstraint by offset.
Added
  • Adds a new Github repo to optimize import speeds for applications integrating with Swift Package Manager.
    • To use this repo, follow these steps:
      • Remove the existing package in your application that points to the url: https://github.com/Appboy/Appboy-ios-sdk.
      • Add a new package using the new url: https://github.com/braze-inc/braze-ios-sdk.
      • Follow the rest of the setup instructions here.
  • Adds support for Right-to-Left languages in the News Feed.
  • Adds support for scaling fonts automatically with Dynamic Type for in-app messages and the News Feed.
Changed
  • Improves accessibility handling for modal and full in-app messages.
  • Improves Slideup in-app message animations.

3.33.1

Fixed
  • Fixes Swift Package Manager integration.
    • In Xcode, select File ▸ Swift Packages ▸ Update to Latest Package Versions to update.
  • Fixes Push Story integration via CocoaPods for applications that have use_frameworks! in their Podfile.

3.33.0

Breaking
  • Changed Push Story integration to use XCFrameworks for Cocoapods and manual integration. Applications currently integrating Push Stories via Cocoapods or manual integration must follow these steps when updating:
    • In your Notification Content Extension target:
      • Remove AppboyPushStory.framework from Frameworks and Libraries under the General tab.
    • In your application target:
      • Delete the Copy File build phase copying the AppboyPushStory.framework to the Frameworks destination.
      • Delete the Run Script build phase that starts with:
        1
        2
        3
        4
        
        APP_PATH="${TARGET_BUILD_DIR}/${WRAPPER_NAME}"
        
        find "$APP_PATH" -name 'AppboyPushStory.framework' -type d | while read -r FRAMEWORK
        ...
        
  • Removed ABKSDWebImageProxy’s prefetchURLs: method.
Fixed
  • Fixes a double redirection bug in Push Stories when the app is in a terminated state and the UNUserNotificationCenter delegate is not the AppDelegate.

3.32.0

Added
  • Adds Mac Catalyst support for apps integrating with Swift Package Manager (SPM).
    • Please follow the instructions here to import the SDK with SPM. The SDK does not currently support Mac Catalyst when integrated through Cocoapods or Carthage.
    • To add Mac Catalyst support, update the Run Script Action described in the 3.31.0 section of the Changelog.
      • Replace the existing script with the following:
        1
        2
        3
        4
        
        # iOS
        bash "$BUILT_PRODUCTS_DIR/Appboy_iOS_SDK_AppboyKit.bundle/Appboy.bundle/appboy-spm-cleanup.sh"
        # macOS
        bash "$BUILT_PRODUCTS_DIR/Appboy_iOS_SDK_AppboyKit.bundle/Contents/Resources/Appboy.bundle/appboy-spm-cleanup.sh"
        

3.31.2

Fixed
  • Fixes the formatting of Full and Slideup in-app messages when displaying on iPhone 12 mini.
Changed
  • Improves Push Story click tracking handling.

3.31.1

Breaking
  • Removes the method getSDWebImageProxyClass from ABKUIUtils.
    • You can access the public class ABKSDWebImageProxy directly by importing ABKSDWebImageProxy.h.
Fixed
  • Fixes a bug in the Cocoapods integration that would lead to SDK localizations being embedded for languages not explicitly supported in the app.
  • Fixes a rare crash that would occur when no windows exist at UIWindowLevelNormal while an in-app message is being displayed and UIKit requests UI updates (orientation change, etc.).
  • Fixes a bug in modal in-app messages where some languages (such as Burmese) may have clipped text.

3.31.0

Breaking
  • For apps that have previously integrated through Swift Package Manager, please perform the following steps:
    • In the Xcode menu, click Product > Scheme > Edit Scheme...
      • Click the expand ▶️ next to Build and select Post-actions. Press + and select New Run Script Action.
      • In the dropdown next to Provide build settings from, select your app’s target.
      • Copy this script into the open field:
        1
        
        bash "$BUILT_PRODUCTS_DIR/Appboy_iOS_SDK_AppboyKit.bundle/Appboy.bundle/appboy-spm-cleanup.sh"
        
    • If you are updating from 3.29.0 or 3.29.1, remove the Run Script Action previously specified in the 3.29.0 section of this changelog.
Added
  • Adds Push Stories support for apps integrating with Swift Package Manager.
    • In your app content extension’s target, under Build Settings > Other Linker Flags, add the -ObjC linker flag.
Changed
  • Updates the email validation on the SDK to be more lenient in favor of more accurate validation by the Braze backend. Valid emails with uncommon patterns or international characters that were previously rejected will now be accepted.
  • Deprecates ABKDeviceWhitelistKey in favor of ABKDeviceAllowlistKey.
Fixed
  • Fixes a bug in HTML in-app messages where some native WebKit UI elements could be unresponsive.

3.30.0

Breaking
  • Body click analytics will no longer automatically be collected for HTML in-app messages created using the HTML Upload with Preview option in the platform.
    • To continue to receive body click analytics, you must log body clicks explicitly from your message via Javascript using appboyBridge.logClick().
Fixed
  • Fixes a bug with Full in-app messages where the button positions did not match the preview on the Braze dashboard.
  • Fixes a bug where in-app messages would be displayed below the application window under specific conditions.
    • Apps that set up their window asynchronously at startup could accidentally hide the in-app message window if one was being displayed (e.g. as a result of clicking on a test in-app message notification).
Added
  • Adds support for custom endpoints with a scheme included (https, http, etc.). For example, http://localhost:3001 will no longer result in https://http://localhost:3001 as the resolved endpoint.

3.29.1

Added

  • Adds improved support for in-app message display on iPhone 12 models.

3.29.0

Added
  • Adds initial support for Swift Package Manager. There are 2 new packages that have been added: AppboyKit for the core SDK and AppboyUI for the full SDK (including UI elements), which correspond to the Appboy-iOS-SDK/Core and Appboy-iOS-SDK pods, respectively.
    • Note that tvOS support is not available via Swift Package Manager for this release. Push Stories is only available through a side-by-side integration with Cocoapods.
    • To add the package to your project follow these steps:
      • Select File > Swift Packages > Add Package Dependency.
        • In the search bar, enter https://github.com/Appboy/Appboy-ios-sdk.
        • Select one of AppboyKit or AppboyUI. Note that AppboyUI includes AppboyKit automatically.
      • In your app’s target, under Build Settings > Other Linker Flags, add the -ObjC linker flag.
      • In the Xcode menu, click Product > Scheme > Edit Scheme...
        • Click the expand ▶️ next to Build and select Post-actions. Press + and select New Run Script Action.
        • In the dropdown next to Provide build settings from, select your app’s target.
        • Copy this script into the open field:
          1
          2
          
          rm -rf "${TARGET_BUILD_DIR}/${PRODUCT_NAME}.app/Frameworks/libAppboyKitLibrary.a"
          rm -rf "${TARGET_BUILD_DIR}/${PRODUCT_NAME}.app/Plugins/libAppboyKitLibrary.a"
          

3.28.0

Breaking
  • Removes userNotificationWasSentFromAppboy: and pushNotificationWasSentFromAppboy: on Appboy. Use isAppboyUserNotification: and isAppboyRemoteNotification: in ABKPushUtils instead.
  • Updates ABKURLDelegate’s method signature for handleAppboyURL:fromChannel:withExtras: to include nullability annotations required for proper Swift support.
Fixed
  • Fixes a race condition in Appboy method wipeDataAndDisableForAppRun where certain persisted fields would still be available immediately after calling the method. These fields now are removed synchronously.
Changed
  • Updated SDWebImage to use version 5.9.x.

3.27.0

Breaking
  • Adds support for iOS 14. Requires Xcode 12.
  • Removes the ABK_ENABLE_IDFA_COLLECTION preprocessor macro from the SDK.
  • Updates the ABKIDFADelegate protocol by renaming isAdvertisingTrackingEnabled to isAdvertisingTrackingEnabledOrATTAuthorized to reflect the addition of the AppTrackingTransparency framework in iOS 14.
    • If you use the Ad Tracking Enabled segment filter on the Braze dashboard or are implementing AppTrackingTransparency, you must update your integration to use AppTrackingTransparency to read the correct user status. Please see our sample app for implementation details.
    • If you do not use the Ad Tracking Enabled segment filter and are not implementing AppTrackingTransparency yet, your implementation of isAdvertisingTrackingEnabledOrATTAuthorized may temporarily continue to use isAdvertisingTrackingEnabled. However, the returned value will always be NO in iOS 14, regardless of actual IDFA availability.
    • Note that Apple announced that they will delay the enforcement of upcoming IDFA changes until early 2021. Please reference our iOS 14 upgrade guide for more details.
  • Updates the minimum required version of SDWebImage from 5.0 to 5.8.2.
  • Integrators will now be required to exclude the arm64 simulator slice in their entire project.
    • This is done automatically when integrating via Cocoapods.
    • For other cases:
      • If you are using xcconfig files to build your app, please set:
        • For iOS targets: EXCLUDED_ARCHS[sdk=iphonesimulator*] = arm64
        • For tvOS targets: EXCLUDED_ARCHS[sdk=appletvsimulator*] = arm64
      • If you are using the Xcode Build Settings panel, enable Build Active Architecture Only for the configuration you use to run your app on the simulator. (ONLY_ACTIVE_ARCH = YES)

3.26.1

Changed
  • Deprecates the compilation macro ABK_ENABLE_IDFA_COLLECTION in favor of the ABKIDFADelegate implementation.
    • ABK_ENABLE_IDFA_COLLECTION will not function properly in iOS 14. To continue collecting IDFA on iOS 14 devices, please upgrade to Xcode 12 and implement App Tracking Transparency and Braze’s ABKIDFADelegate (see the iOS 14 upgrade guide for more information).
Added
  • Adds improved support for iOS 14 Approximate Location tracking.

3.26.0

Breaking
  • Removed readonly property overrideApplicationStatusBarHiddenState in ABKInAppMessageViewController.h.
Fixed
  • Fixes an issue with in-app messages not respecting the application’s status bar style when View controller-based status bar appearance (UIViewControllerBasedStatusBarAppearance) is set to YES in the Info.plist.
  • Fixes an issue which can lead to text being cut off in Content Cards for specific iPhone models.
  • Fixes an issue preventing test Content Cards from displaying under specific conditions.
Changed
  • Added Binary Project Specification file for more efficient Carthage integration of the full SDK.
    • Update your Cartfile to use binary "https://raw.githubusercontent.com/Appboy/appboy-ios-sdk/master/appboy_ios_sdk_full.json"
    • Support for this integration method was added starting with version 3.24.0 of the SDK.
Added
  • Adds support for specifying PushStoryAppGroup in the Appboy dictionary in your app’s Info.plist. This Apple App Group will share the Braze Push Story information such as Campaign IDs between applications from a single Apple Developer account.
  • Adds appboyBridge.getUser().addAlias(alias, label) to the javascript interface for HTML in-app messages.
  • Adds the property overrideUserInterfaceStyle to ABKInAppMessage that allows forcing Light or Dark mode in the same way as Apple’s UIViewController.overrideUserInterfaceStyle.
  • Adds the ability to dismiss modal in-app messages when the user clicks outside of the in-app message.
    • This feature is disabled by default.
    • You can enable the feature by adding the Appboy dictionary to your Info.plist file. Inside the Appboy dictionary, add the DismissModalOnOutsideTap boolean subentry and set the value to YES.
    • You can also enable the feature at runtime by setting ABKEnableDismissModalOnOutsideTapKey to YES in appboyOptions.

3.25.0

Breaking
  • Removes the arm64e architecture when building with Cocoapods.
  • Removes the deprecated property appWindow from ABKInAppMessageWindowController.

3.24.2

Fixed
  • Fixes an issue with post-dismissal view hierarchy restoration for in-app messages under specific conditions.
Changed
  • Deprecates ABKInAppMessageWindowController property appWindow.

3.24.1

Fixed
  • Fixes an issue introduced in 3.24.0 breaking the SDK compatibility with Cocoapods.

3.24.0

Important This release is not compatible with Cocoapods. Do not upgrade to this version and upgrade to 3.24.1 and above instead.

Breaking
  • Renames ABKInAppMessageWindow’s catchClicksOutsideInAppMessage to handleAllTouchEvents.
Fixed
  • Fixes an issue where the unread indicator on a Content Card would persist even after being read.
  • Fixes an issue preventing long texts from displaying correctly in Full in-app messages.
  • Fixes an issue where appboyBridge would not work in an Ajax callback within HTML In-App Messages.
Changed
  • Changes the manual integration steps for versions 3.24.0 and newer. Please follow the updated integration steps here.
Added
  • Adds support for JavaScript functions window.alert(), window.confirm() and window.prompt() in HTML in-app messages.
  • Adds the ABKContentCardsTableViewControllerDelegate protocol to more intricately handle Content Card clicks using the methods contentCardTableViewController:shouldHandleCardClick: and contentCardTableViewController:didHandleCardClick:.

3.23.0

Fixed
  • Fixes an issue with regex based event property triggers not working as expected. Previously on iOS they had to match the entire string, now they will search for matches as expected.
  • Improves resiliency when handling multiple background requests.
Added
  • Adds support for upcoming HTML In-App Message templates.
  • Adds support for applications using scenes (UIWindowSceneDelegate). In-app messages are now properly displayed in that context.

3.22.0

Breaking
  • Removes the key ABKInAppMessageHideStatusBarKey from appboyOptions and the property forceHideStatusBar from ABKInAppMessageController. Full screen in-app messages are now always displayed with the status bar hidden.
  • Adds Dark Mode support to Content Cards. This feature is enabled by default and can be disabled by setting enableDarkTheme property to NO on ABKContentCardsTableViewController before the view controller is presented.
Fixed
  • Fixes an issue in HTML in-app messages where button clicks weren’t correctly being attributed for mailto: and tel: links.
  • Fixes an issue in HTML in-app messages where videos would be displayed underneath the in-app message when full screen playback was enabled. The in-app message UIWindow’s windowLevel is now set to UIWindowLevelNormal instead of being above UIWindowLevelStatusBar.
  • Fixes an issue in Content Cards where ABKURLDelegate was not being respected when opening links.
Added
  • Adds appboyBridge.logClick(id), appboyBridge.logClick() and appboyBridge.getUser().setCustomLocationAttribute(key, latitude, longitude) to the javascript interface for HTML in-app messages.
  • Adds Czech and Ukrainian language support for Braze UI elements.
  • Adds the ability to unset the current user’s email attribute by setting the email property of the current ABKUser instance to nil (e.g. [Appboy sharedInstance].user.email = nil;).
  • Adds Dark Mode support to Push Stories.
  • Adds the ability to set maximum width of Content Cards by using the maxContentCardWidth property of ABKContentCardsTableViewController.

3.21.3

Added
  • Adds an option to disable automatic geofence requests.
    • You can do this in the plist by adding the Appboy dictionary to your Info.plist file. Inside the Appboy dictionary, add the DisableAutomaticGeofenceRequests boolean subentry and set the value to YES.
    • You can also disable automatic geofence requests at runtime by setting ABKDisableAutomaticGeofenceRequestsKey to YES in appboyOptions.
  • Adds the method requestGeofencesWithLongitude:latitude: to Appboy.h. This method can be called whenever you explicitly want Braze to send a request for updated Geofences information. This call is rate limited to once per user session.

3.21.2

Fixed
  • Fixes an issue in HTML in-app messages where, during display, the viewport would shift down if the keyboard was opened but not shift back up when the keyboard was closed.
  • Fixes an issue introduced in 3.17.0 where the SDK would give precedence to the endpoint passed in Info.plist if given both an endpoint from the Info.plist and appboyOptions.
Added
  • Adds the ability to set a custom WKWebViewConfiguration for HTML in-app messages. You can set it using the method setCustomWKWebViewConfiguration in ABKInAppMessageUIDelegate.
Changed
  • Removes calls to deprecated APIs statusBarOrientation and statusBarFrame.
  • Un-deprecates the following push utility methods: isUninstallTrackingUserNotification:, isUninstallTrackingRemoteNotification:, isGeofencesSyncUserNotification:, isGeofencesSyncRemoteNotification:, and isPushStoryRemoteNotification: from ABKPushUtils. These APIs were originally deprecated in 3.16.0.

3.21.1

Fixed
  • Fixes an issue for Modal and Full in-app messages where the opacity value of the close X button was not being respected.
Changed
  • ABKContentCard.m will now log a click event when logContentCardClicked is called and no URL field is populated.
Added
  • Adds the ability to force the status bar to hide when a Full or HTML in-app message is being actively displayed. To opt in to this feature, set ABKInAppMessageHideStatusBarKey to YES in appboyOptions.

3.21.0

Breaking
  • Requires XCode 11.
Fixed
  • Fixes an issue in the animate-in behavior of HTML in-app messages that could cause a brief flicker before the message displayed on older devices and simulators.
  • Fixes an issue with Slideup in-app messages where they would cover part of the status bar when animating from the top on non-notched devices.
  • Fixes an issue introduced in 3.14.1 where boolean-typed event properties would be improperly cast to numbers.
Changed
  • Updates the logging format for debug, warn, and error ABKLogger messages to now print their log level.
Added
  • Adds support for the upcoming feature, in-app messages with Dark Mode support.
    • Dark Mode enabled messages must be created from the dashboard. Braze does not dynamically theme in-app messages for Dark Mode.
    • This feature is enabled by default for all new ABKInAppMessage instances. To prevent Braze from automatically applying a Dark Theme when the fields are available on Braze’s servers, set the enableDarkTheme flag on ABKInAppMessage to NO in the beforeInAppMessageDisplayed: method of your ABKInAppMessageControllerDelegate delegate implementation.
  • Adds the ability to reference the Braze iOS SDK API from Swift when using the Appboy-tvOS-SDK pod. Adding import AppboyTVOSKit to the top of your Swift file while using the Appboy-tvOS-SDK pod will give you equivalent behavior to adding import Appboy_iOS_SDK while using the Appboy-iOS-SDK pod.
  • Adds the populateContentCards: method and the cards property to ABKContentCardsTableViewController’s public interface. By setting the cards property from within populateContentCards:, you may manipulate ABKContentCard field data and/or control which ABKContentCard instances are displayed from the context of a custom ABKContentCardsTableViewController subclass.

3.20.4

Fixed
  • Fixed an issue with Content Cards where the header and description text fields would appear to be missing in Dark Mode.
Added
  • Adds a TEALIUM SDK flavor option.

3.20.3

Added
  • If Automatic Braze location collection is enabled, the SDK now submits a session start location request if location hasn’t already been sent up for the session after any affirmative location permission prompt. This also applies to the new “Allow Once” option in iOS 13.

3.20.2

Important If you are on Braze iOS SDK 3.19.0 or below, we recommend upgrading to this version immediately to ensure uninterrupted collection of new push tokens as users upgrade to iOS 13.

  • In application:didRegisterForRemoteNotificationsWithDeviceToken:, replace
    1
    2
    
    [[Appboy sharedInstance] registerPushToken:
                  [NSString stringWithFormat:@"%@", deviceToken]];
    

    with

    1
    
    [[Appboy sharedInstance] registerDeviceToken:deviceToken];
    
  • If you are on Braze iOS SDK 3.19.0 or below and unable to upgrade, you must ensure your [Appboy registerPushToken] implementation does not rely on stringWithFormat or description for parsing the deviceToken passed in from application:didRegisterForRemoteNotificationsWithDeviceToken:. Please reach out to your Customer Success Manager for more information.

  • Important In Braze iOS SDK 3.19.0, we updated our HTML in-app message container from UIWebview to WKWebView, however, the initial releases have known issues displaying HTML in-app messages. If you are currently using 3.19.0, 3.20.0, or 3.20.1, you are strongly encouraged to upgrade if you make use of HTML in-app messages. Please see the following for more important information about the transition to WKWebView:
    • If you are utilizing customization for HTML in-app messages (such as customizing ABKInAppMessageHTMLFullViewController or ABKInAppMessageHTMLViewController), we strongly recommend testing to ensure your in-app messages continue to display correctly and interactions function as intended.
    • The following javascript methods are now no-ops: alert, confirm, prompt.
    • Deep links without schemes are no longer supported. Ensure that your in-app message deep links contain schemes.
Fixed
  • Fixes an issue introduced in 3.19.0 where HTML in-app messages would not register user clicks when the .xib failed to load.
  • Fixes an issue introduced in 3.19.0 where HTML in-app messages with select special characters and an assets zip would cause display irregularities.
Changed
  • Updates the WKWebView which displays HTML in-app messages with the following attributes:
    • suppressesIncrementalRendering is set to true
    • mediaTypesRequiringUserActionForPlayback is set to WKAudiovisualMediaTypeAll
  • Updates the background color of the WKWebView which displays HTML in-app messages from [[UIColor blackColor] colorWithAlphaComponent:.3] to [UIColor clearColor].

3.20.1

Important This release has known issues displaying HTML in-app messages. Do not upgrade to this version and upgrade to 3.20.2 and above instead. If you are using this version, you are strongly encouraged to upgrade to 3.20.2 or above if you make use of HTML in-app messages.

Fixed
  • Fixes an issue introduced in 3.19.0 which changed the background of HTML in-app messages to a non-transparent color.
  • Improves the robustness of push token collection code for iOS 13 introduced in 3.20.0.

3.20.0

Important This release has known issues displaying HTML in-app messages and a known issue with push token collection. Do not upgrade to this version and upgrade to 3.20.2 and above instead. If you are using this version, you are strongly encouraged to upgrade to 3.20.2 or above if you make use of HTML in-app messages.

Breaking
  • Introduced a signature change for push token collection methods:
    1
    2
    
    [[Appboy sharedInstance] registerPushToken:
                  [NSString stringWithFormat:@"%@", deviceToken]];
    

    with

    1
    
    [[Appboy sharedInstance] registerDeviceToken:deviceToken];
    

3.19.0

Important This release has known issues displaying HTML in-app messages. Do not upgrade to this version and upgrade to 3.20.2 and above instead. If you are using this version, you are strongly encouraged to upgrade to 3.20.2 or above if you make use of HTML in-app messages.

Breaking
  • Replaces UIWebView with WKWebView for HTML in-app messages.
    • If you are utilizing customization for HTML in-app messages (such as customizing ABKInAppMessageHTMLFullViewController or ABKInAppMessageHTMLViewController), you must test to ensure your in-app messages continue to display correctly and interactions function as intended.
    • The following javascript methods are now no-ops: alert, confirm, prompt.
    • Deep links without schemes are no longer supported. Please ensure that your in-app message deep links contain schemes.

3.18.0

Breaking
  • Automatic Braze location collection is now disabled by default. If you choose to use our location collection, you must explicitly enable location collection.
    • You can do this in the plist by adding the Appboy dictionary to your Info.plist file. Inside the Appboy dictionary, add the EnableAutomaticLocationCollection boolean subentry and set the value to YES.
    • You can also enable location at runtime by setting ABKEnableAutomaticLocationCollectionKey to YES in appboyOptions.
  • Removes the Feedback feature from the SDK. The Feedback subspec and all Feedback methods on the SDK, including [[Appboy sharedInstance] submitFeedback] and [[Appboy sharedInstance] logFeedbackDisplayed], are removed.
Changed
  • Improves support for in-app messages on “notched” devices (for example, iPhone X, Pixel 3XL). Full-screen messages now expand to fill the entire screen of any phone, while covering the status bar.
Added
  • Adds the ability to enable Braze Geofences without enabling Braze location collection. You can set this in the plist by adding the Appboy dictionary to your Info.plist file. Inside the Appboy dictionary, add the EnableGeofences boolean subentry and set the value to YES to enable Braze Geofences. You can also enable geofences at runtime by setting ABKEnableGeofencesKey to YES in appboyOptions.
    • If this key is not set, it will default to the status of automatic location collection (see breaking change above).
    • Note that Braze Geofences will continue to work on existing integrations if location collection is enabled and this new configuration is not present. This new configuration is intended for integrations that want Braze Geofences, but not location collection enabled as well.

3.17.0

Breaking
  • Removes ABKAppboyEndpointDelegate.
    • You can now set the endpoint at runtime by setting the value of ABKEndpointKey in appboyOptions to your custom endpoint (ex. sdk.api.braze.eu) at initialization.

3.16.0

  • Important: If you are using ABKAppboyEndpointDelegate, you will need to replace dev.appboy.com with sdk.iad-01.braze.com in the getApiEndpoint method.
Breaking
  • Removes the methods: allowRequestWhenInUseLocationPermission and allowRequestAlwaysPermission from ABKLocationManager.
    • To request when in use location permission, use the following code:
      1
      2
      
      CLLocationManager *locationManager = [[CLLocationManager alloc] init];
      [locationManager requestWhenInUseAuthorization];
      
    • To request always location permission, use the following code:
      1
      2
      
      CLLocationManager *locationManager = [[CLLocationManager alloc] init];
      [locationManager requestAlwaysAuthorization];
      
    • The preprocessor macro ABK_DISABLE_LOCATION_SERVICES is no longer needed.
    • Important: Configuring geofences to request always location permissions remotely from the Braze dashboard is no longer supported. If you are using Geofences, you will need to ensure that your app requests always location permission from your users manually.
  • ABKAutomaticRequestProcessingExceptForDataFlush is deprecated. Users using ABKAutomaticRequestProcessingExceptForDataFlush should switch to ABKManualRequestProcessing, as the new behavior of ABKManualRequestProcessing is identical to the previous behavior of ABKAutomaticRequestProcessingExceptForDataFlush
Changed
  • Deprecates the push utility methods: isUninstallTrackingUserNotification:, isUninstallTrackingRemoteNotification:, isGeofencesSyncUserNotification:, isGeofencesSyncRemoteNotification:, and isPushStoryRemoteNotification: from ABKPushUtils. Please use the function isAppboyInternalRemoteNotification:.
  • Minor changes to the logic of ABKManualRequestProcessing. The original ABKManualRequestProcessing had specific exceptions and behaved more like ABKAutomaticRequestProcessingExceptForDataFlush in practice. As a result, the two policies have been merged into ABKManualRequestProcessing. Note that the new definition of ABKManualRequestProcessing is that periodic automatic data flushes are disabled. Other requests important to basic Braze functionality will still occur.

3.15.0

  • Important: If you are using ABKAppboyEndpointDelegate, you will need to replace dev.appboy.com with sdk.iad-01.braze.com in the getApiEndpoint method.
Breaking
  • Adds support for SDWebImage version 5.0.
    • Note that upgrading to SDWebImage 5.0 also removed the FLAnimatedImage transitive dependency from the SDK.

3.14.1

  • Important: If you are using ABKAppboyEndpointDelegate, you will need to replace dev.appboy.com with sdk.iad-01.braze.com in the getApiEndpoint method.
Changed
  • Changed in-app message trigger behavior to not perform trigger events until after any pending trigger sync requests to the server have finished.
Fixed
  • Fixed a serialization issue that could cause improper type conversions for certain decimal values.
  • Fixed a behavior introduced in 3.12.0 which caused in-app messages to not be considered triggered locally if ABKDiscardInAppMessage was returned by the host app in beforeInAppMessageDisplayed:.
Added
  • Added the ability to set the session timeout via the Info.plist.
    • Add the Appboy dictionary to your Info.plist file. Inside the Appboy Dictionary, add the SessionTimeout Number subentry and set the value to your session timeout.
  • Added the ability to disable location tracking via the Info.plist.
    • Add the Appboy dictionary to your Info.plist file. Inside the Appboy Dictionary, add the DisableAutomaticLocation Boolean subentry and set the value to YES.
  • Added dynamic cell resizing for Content Cards cells with templated images in our default Content Cards UI.
  • Added validation to the local filename’s canonical path during zip file extraction.

3.14.0

  • Important: If you are using ABKAppboyEndpointDelegate and plan to upgrade to 3.14.1, you will need to replace dev.appboy.com with sdk.iad-01.braze.com in the getApiEndpoint method.
Added
  • Improves the look and feel of In-App Messages to adhere to the latest UX and UI best practices. Changes affect font sizes, padding, and responsiveness across all message types. Now supports button border styling.

3.13.0

Breaking
  • Upgrades the delivery mechanism of Push Stories to allow delivery even after a user’s app has been force closed..
    • Required: Please change your integration to use ab_cat_push_story_v2 instead of ab_cat_push_story for the UNNotificationExtensionCategory in your content extension. See documentation for more details.
Changed
  • Improves in-app message triggering logic to fall back to lower priority messages when the Braze server aborts templating (e.g. from a Connected Content abort in the message body, or because the user is no longer in the correct Segment for the message).
  • Updates German translations to improve formatting.

3.12.0

Breaking
  • Drops support for iOS 8.
  • Adds support for the arm64e architecture when building with Cocoapods. Requires Xcode 10.1.
Fixed
  • Fixes bitcode support for the Push Story framework when using Xcode 10.
  • Improves triggered in-app message re-eligibility logic to better handle templating failures.
Changed
  • Changes the behavior of News Feed so that only one impression is logged for each card per News Feed open.
Added
  • Adds HTML IAM appboyBridge ready event to know precisely when the appboyBridge has finished loading.
    • Example below:
      1
      2
      3
      4
      5
      6
      
       <script type="text/javascript">
         function logMyCustomEvent() {
           appboyBridge.logCustomEvent('My Custom Event');
         }
         window.addEventListener('ab.BridgeReady', logMyCustomEvent, false);
       </script>
      
Removed
  • Removes Cross-Promotion cards from the News Feed.
    • Cross-Promotion cards have also been removed as a card model and will thus no longer be returned.

3.11.0

Added
  • Adds the ability to set or remove custom location attributes for a specific user from within HTML IAMs.
  • Updates the SDK to report users who disable banner notifications but are still opted-in to push notifications as push enabled. Note this change does not affect provisionally authorized users on iOS 12, who were considered push enabled before this release regardless of their banner notification settings.
  • Adds Carthage Core support which allows for integration with the core Braze SDK without any UI components. To implement the core SDK, add binary "https://raw.githubusercontent.com/Appboy/appboy-ios-sdk/master/appboy_ios_sdk_core.json" to your Cartfile.
Changed
  • Deprecates the Feedback feature.
Fixed
  • Fixes an issue with the JS bridge when trying to set a custom attribute with the character ‘&’.

3.10.0

Added
  • Adds the ability to specify a whitelist for device fields that are collected by the Braze SDK.
    • Configurable device fields are defined in the ABKDeviceOptions enum.
    • To specify whitelisted device fields, assign the bitwise OR of desired fields to ABKDeviceWhitelistKey in the appboyOptions of startWithApiKey:inApplication:withAppboyOptions:.
      • For example, to specify timezone and locale collection to be whitelisted, set appboyOptions[ABKDeviceWhitelistKey] = @(ABKDeviceOptionTimezone | ABKDeviceOptionLocale);.
    • To turn off all fields, set appboyOptions[ABKDeviceWhitelistKey] = @(ABKDeviceOptionNone);.
    • By default, all fields are enabled.
  • Added the clicked property to ABKContentCard. Clicks made through [ABKContentCard logContentCardClicked] are now saved locally on the device.
Breaking
  • Removes ABKSignificantChangeCollectionEnabledOptionKey, ABKSignificantChangeCollectionDistanceFilterOptionKey, and ABKSignificantChangeCollectionTimeFilterOptionKey from the Appboy interface.
Removed
  • Removes the ability to optionally track locations in the background.
Fixed
  • Fixes an issue where Slideup and Full In-App Message content could be obscured by the notch on iPhone XR and iPhone XS Max.

3.9.0

Breaking
  • Adds support for iOS 12. Requires Xcode 10.
Fixed
  • Fixes minor issues with subclassing ABKInAppMessageModalViewController and News Feed request timeouts.
    • Thanks @datkinnguyen for your contribution.

3.8.4

Fixed
  • Fixes a regression introduced in version 3.8.3 that caused background tasks to extend beyond execution time.

3.8.3

Fixed
  • Fixes an issue where ABKContentCardsController unviewedContentCardCount would always return 0.
Changed
  • Updates the Content Cards UI with minor layout improvements.

3.8.2

Fixed
  • Fixes an issue with possible build failure when using Content Cards related to duplicate image names in Content Cards and News Feed pods. Please use this version if integrating Content Cards.
Changed
  • Updates the Content Cards UI with minor layout improvements.

3.8.1

Fixed
  • Important: Fixes an issue with Content Cards syncing. Note: As additional fixes were added in later versions, please use Braze iOS SDK version 3.8.2 or above if integrating Content Cards.

3.8.0

Added
  • In ABKUser class, addLocationCustomAttributeWithKey:latitude:longitude: and removeLocationCustomAttributeWithKey: methods are added to manage location custom attributes.
  • Introduces support for the upcoming Content Cards feature, which will eventually replace the existing News Feed feature and adds significant capability. This feature is currently in closed beta testing; if you’re interested in joining the beta, please reach out to your Customer Success Manager or Account Manager.
Changed
  • Status bar is not obscured when displaying a full screen in-app message.

3.7.1

Changed
  • Improves data handling immediately following a user change to bring behavioral parity with the Android and Web SDKs.

3.7.0

Breaking
  • In ABKInAppMessageUIControlling protocol, getCurrentDisplayChoiceForControlInAppMessage method is added to define whether the control in-app message impression should be logged now, later or discarded.
  • In ABKInAppMessageControllerDelegate protocol, beforeControlMessageImpressionLogged method is added to define whether the control in-app message impression should be logged now, later or discarded.
Added
  • CLLocationManager authorization requests can now be prevented from compiling by setting a Preprocessor flag ABK_DISABLE_LOCATION_SERVICES.
Fixed
  • Fixes an issue where in-app messages triggered on session start could potentially be templated with the old user’s attributes.

3.6.0

Breaking
  • In ABKSDWebImageProxy.h, renames removeImageForKey to removeSDWebImageForKey and clearCache to clearSDWebImageCache to avoid conflicts with internal Apple API. Important: We have received reports of sporadic App Store rejection stemming from Apple’s static checks mistaking our APIs for an invalid usage of the internal Apple API. We recommend new App Store submissions integrating the Braze iOS SDK ship with this version or above to decrease the likelihood of rejection.
Added
  • Exposes handleCardClick on ABKNewsFeedTableViewController.h to enable custom handling via subclassing.
  • Improves News Feed image handling on iPad.

3.5.1

Fixed
  • Fixes an issue with integrating the NewsFeed subspec in Swift projects via Cocoapods.

3.5.0

Breaking
  • Open sources the News Feed UI code and moves it into a new subspec named “NewsFeed”.
    • Manual integrators must now add the AppboyUI folder of this repository to their projects as a group, in addition to AppboyKit.
    • The “NewsFeed” subspec contains the Braze News Feed UI and the Core SDK. It does not include the Feedback or In-App Message UI.
    • The “UI” subspec contains all Braze UI and the Core SDK subpsec.
    • ABKFeedViewControllerDelegate was removed.
    • To integrate a navigation context News Feed, use the following code:
      1
      2
      
      ABKNewsFeedTableViewController *newsFeed = [ABKNewsFeedTableViewController getNavigationFeedViewController];
      [self.navigationController pushViewController:newsFeed animated:YES];
      
    • To integrate a modal context News Feed, use the following code:
      1
      2
      
      ABKNewsFeedViewController *newsFeed = [[ABKNewsFeedViewController alloc] init];
      [self.navigationController presentViewController:newsFeed animated:YES completion:nil];
      
    • See our News Feed Sample app for sample implementations and customizations.
  • Removes NUI support for Feedback, In-App Messages, and the News Feed.
    • All customization can now be done by using categories or by extending our open sourced view controllers.
  • Removes deprecated ABKPushURIDelegate from the SDK. Use ABKURLDelegate instead.

3.4.0

Breaking
  • Adds preferredOrientation to ABKInAppMessageUIController and ABKInAppMessageWindowController.
  • Removes supportedOrientations from ABKInAppMessageUIController and ABKInAppMessageWindowController.
  • Renames supportedOrientationMasks to supportedOrientationMask in ABKInAppMessageUIController and ABKInAppMessageWindowController.
Fixed
  • Fixes an issue that caused GIFs to not animate on SDWebImage versions above or equal to 4.3.0

3.3.4

Added
  • Adds the ability to view verbose logs from the SDK for debugging.
    • To enable verbose logging, add a dictionary named Appboy to your Info.plist file. Inside the Appboy Dictionary, add the LogLevel String subentry and set the value to “0”.

3.3.3

Added
  • Adds wipeDataAndDisableForAppRun: on the Appboy interface to support wiping all customer data created by the Braze SDK.
  • Adds disableSDK: and requestEnableSDKOnNextAppRun: to the Appboy interface to disable and re-enable the Braze SDK.
Fixed
  • Fixes an issue where events setting custom attribute arrays to nil would persist on the SDK beyond their useful life.

3.3.2

Changed
  • Updates the SDK with internal, non-functional improvements.

3.3.1

Added
  • Adds Other, Unknown, Not Applicable, and Prefer not to Say options for user gender.
  • Adds umbrella header files AppboyFeedback.h and AppboyInAppMessage.h for the Feedback and InAppMessage subspecs.
Fixed
  • Fixes an issue where the method beforeInAppMessageDisplayed: in class ABKInAppMessageControllerDelegate is not called when the host app is using the Core subspec.

3.3.0

Breaking
  • Open sources the In-App Message UI code and moves it into a new subspec named “InAppMessage”.
    • Manual integrators must now add the AppboyUI folder of this repository to their projects as a group, in addition to AppboyKit.
    • The “InAppMessage” subspec contains the Braze In-App Message UI and the Core SDK. It does not include Feedback or the News Feed UI.
    • The “UI” subspec contains all Braze UI and the Core SDK subpsec.
    • The open-sourced In-App Message view controllers offer backward compatible NUI support, although we recommend using categories or subclassing the In-App Message view controllers for customization as the NUI library isn’t actively maintained any more. Support for NUI customization will be removed in a future release.
    • Most delegate customization methods are moved from ABKInAppMessageControllerDelegate to ABKInAppMessageUIDelegate.
    • See our In-App Message Sample app for sample implementations and customizations.
  • Removes support for original in-app messages. Moving forward, triggered in-app messages must be used.
  • Removes requestInAppMessageRefresh method from Appboy.
Changed
  • Removes the current behavior of displaying an in-app message from the stack on app open, if the stack is non-empty
Fixed
  • Adds Macros for methods which are only available from iOS 10.
    • Addresses https://github.com/Appboy/appboy-ios-sdk/issues/128.
  • Stops using deprecated openURL: method when in iOS 10 and above.
    • Addresses https://github.com/Appboy/appboy-ios-sdk/issues/132.

3.2.3

Fixed
  • Fixes an issue introduced in version 3.0.0 which caused detailed device model information to not be collected by the SDK.
  • Fixes an issue where Braze’s Carthage framework did not support simulators.

3.2.2

Fixed
  • Fixes an issue where Slideup and Full In-App Message content could be obscured by the notch on iPhone X.

3.2.1

Fixed
  • Fixes an issue where Push Story Framework did not support bitcode.

3.2.0

Added
  • Adds Push Stories, a new push type that uses UNNotificationContentExtension to display multiple images in a single notification.
    • This feature requires iOS 10 and above.
Fixed
  • Fixes an issue where tvOS SDK did not support bitcode.

3.1.1

Added
  • Adds a new property language to ABKUser to allow explicit control over the user’s language in the Braze dashboard. Note that this is separate and independent from the language settings on the user’s device.
  • Adds an Objective-C sample app for the Core subspec of the SDK. See Samples/Core/ObjCSample.
Fixed
  • Fixes a bug introduced in version 2.30 where crashes could occur if the SDK was directed to handle a custom scheme deep link inside a WebView.
    • Addresses https://github.com/Appboy/appboy-ios-sdk/issues/122.
  • Fixes a bug introduced in version 3.0 where new custom attributes were not being flushed if custom attributes had been previously flushed in the same foregrounded session.
  • Fixes a bug introduced in version 3.0 where previously flushed custom attributes were being re-sent.
  • Fixes an issue where slow image fetching blocked image-only modal in-app messages from displaying.
    • Addresses https://github.com/Appboy/appboy-ios-sdk/issues/118.

3.1.0

Breaking
  • Adds support for iOS 11. Requires Xcode 9.

3.0.2

Added
  • Adds the ability to set a custom API endpoint via the Info.plist.
    • Add the Appboy dictionary to your Info.plist file. Inside the Appboy Dictionary, add the Endpoint String subentry and set the value to your custom endpoint (e.g., sdk.api.braze.eu).
Fixed
  • Fixes an issue where changing the IDFA settings through a third party wrapper could cause a crash.

3.0.1

Fixed
  • Fixes an issue where calling incrementCustomUserAttribute: on ABKUser could cause a crash.

3.0.0

Breaking
  • Removes the deprecated foursquareAccessToken property from ABKUser. To associate a Foursquare access token with a user profile, use setCustomAttributeWithKey:andStringValue: instead.
  • Note: Braze iOS SDK version 3.0.0 will only support downgrading to iOS SDK version 2.31.0. Downgrading to versions prior to 2.31.0 may result in app crashes.
Added
  • Adds a major performance upgrade that reduces CPU usage, memory footprint, and network traffic.

2.31.0

Breaking
  • Open sources the Feedback view controllers and moves them into a new subspec “Feedback”.
    • The “Feedback” subspec has the Braze Feedback UI and the Core SDK. It will not include in-app messages or News Feed UI.
    • Removes the popover context for Feedback due to the deprecation of UIPopoverViewController in iOS.
    • Renames the ABKFeedbackViewControllerModalContext and ABKFeedbackViewControllerNavigationContext class to ABKModalFeedbackViewController and ABKNavigationFeedbackViewController.
    • The open-sourced Feedback view controllers offer backward compatible NUI support, although we recommend using categories or subclassing the Feedback view controllers for customization as NUI library isn’t actively maintained any more. See here for customization details.
    • See our Feedback Sample app for sample implementations and customizations.
Added
  • Adds user aliasing capability. Aliases can be used in the API and dashboard to identify users in addition to their ID. See the addAlias:withLabel: method on ABKUser for more information.
Changed
  • Updates the AppboyKit.h to include all the public header files in the SDK.

2.30.0

Breaking
  • Open sources the ABKModalWebViewController class, which is used to display the web URLs from push or in-app message clicks.
    • Drops NUI customization support for the navigation bar and navigation bar button item on ABKModalWebViewController. To customize the UI, create an ABKModalWebViewController category and override the corresponding method(s) exposed.
  • Open sources the ABKNoConnectionLocalization class, which provides Braze’s default localized string for “No Connection” error.
    • You can customize the localization by adding Appboy.no-connection.message as the key in your Localizable.strings files.
  • Removes the Appboy.bundle from the Core subspec of the SDK.
    • If you use the Core subspec, the in-app messages will not display, and trying to display Braze’s News Feed and Feedback UI will lead to unpredictable behavior.

2.29.1

Added
  • Adds a new property buttonTextFont to ABKInAppMessageButton. It allows clients to set customized fonts on in-app message buttons before the in-app message is displayed.
Fixed
  • Makes class ABKInAppMessageWindowController.h public.
    • Addresses https://github.com/Appboy/appboy-ios-sdk/issues/105.
  • Fixes an issue where device information was not flushed for a new user when server requests were queued for two or more users.
Changed
  • Removes the warnings in ABKSDWebImageProxy.

2.29.0

Breaking
  • Drops support for iOS 7.
  • Removes the shouldOpenURIExternally field from ABKInAppMessage.
  • Requires XCode 8.3.
  • Changes the behavior of the onCardClicked:feedViewController: method in ABKFeedViewControllerDelegate to let Braze handle the card click action if the delegate method returns NO.
    • Previously, Braze would handle the card click action if onCardClicked:feedViewController: returned YES.
    • This change standardizes delegate behavior with ABKInAppMessageControllerDelegate and ABKURLDelegate.
Added
  • Adds the property openUrlInWebView to ABKInAppMessage, ABKInAppMessageButton and ABKCard. This property determines if the URL associated with the object will be opened in a UIWebView.
  • Adds a Javascript interface to HTML in-app messages with ability to log custom events, log purchases, set user attributes, navigate users, and close the message.
  • Adds an abDeepLink query field to HTML in-app messages, which defaults to false. To prevent the SDK from opening deep links in a UIWebView, specify abDeepLink=true in your link (e.g., https://www.braze.com?abDeepLink=true).
  • Adds the ABKURLDelegate protocol for customizing URL handling across channels. Set the ABKURLDelegate by passing a delegate object to the ABKURLDelegateKey in the appboyOptions of startWithApiKey:inApplication:withAppboyOptions:. See our Stopwatch sample application for a Universal Link implementation sample.
  • Adds the following utility methods to ABKPushUtils for detecting if a push notification was sent by Braze for internal feature purposes:
    • + (BOOL)isAppboyInternalUserNotification:(UNNotificationResponse *)response;
    • + (BOOL)isAppboyInternalRemoteNotification:(NSDictionary *)userInfo;
    • + (BOOL)isUninstallTrackingUserNotification:(UNNotificationResponse *)response;
    • + (BOOL)isGeofencesSyncUserNotification:(UNNotificationResponse *)response;
    • + (BOOL)isGeofencesSyncRemoteNotification:(NSDictionary *)userInfo;
    • These methods can be used to ensure that your app does not take any undesired or unnecessary actions upon receiving Braze’s internal content-available notifications (e.g., pinging your server for content).
Changed
  • Deprecates ABKPushURIDelegate. If you were previously using ABKPushURIDelegate, use ABKURLDelegate instead.
  • Deprecates userNotificationWasSentFromAppboy: and pushNotificationWasSentFromAppboy: on Appboy. Use isAppboyUserNotification: and isAppboyRemoteNotification: on ABKPushUtils instead.
  • Deprecates shouldFetchTestTriggersFlagContainedInPayload: on ABKPushUtils.

2.28.0

Breaking:
  • Removes support for watchOS 1, including Braze WatchKit SDK and all public APIs for watchOS in Braze iOS SDK.
Added
  • Adds ABKSDWebImageProxy to access the SDWebImage framework. This will prevent the Core subspec of the SDK from calling any SDWebImage methods directly.

2.27.0

Breaking
  • Removes the following deprecated items: the bio field of ABKUser, the setIsSubscribedToEmails: method of ABKUser, and the getResourceEndpoint: method of the ABKAppboyEndpointDelegate protocol.
Added
  • Adds support for registering geofences and messaging on geofence events. Please reach out to success@braze.com for more information about this feature.
  • Adds Braze default push categories which can be fetched from ABKPushUtils.
    • To use the Braze default push categories, you need to manually add the Braze categories when you register for push. You can get the Braze categories from [ABKPushUtils getAppboyUNNotificationCategorySet] or [ABKPushUtils getAppboyUIUserNotificationCategorySet].
    • In this version, we add four sets of push action buttons: accept/decline, yes/no, confirm/cancel, more. These will be available as button sets on the dashboard when creating an iOS push campaign.
    • All Braze push action buttons support localization.
  • Adds support for web link and deep link handling of push action buttons.
Fixed
  • Fixes the issue where the combination of the Core subspec of the SDK and a non-supported version of SDWebImage framework can cause apps to crash.
    • Addresses https://github.com/Appboy/appboy-ios-sdk/issues/104.
Changed
  • HTML in-app messages now log body click analytics on all links that are not appboy://customEvent and do not include the abButtonId query field. Previously, no body click analytics were logged.
Removed
  • Removes deprecated method - (NSString *)getResourceEndpoint:(NSString *)appboyResourceEndpoint from ABKAppboyEndpointDelegate.
  • Removes deprecated property bio and deprecated method - (BOOL)setIsSubscribedToEmails:(BOOL)subscribed from ABKUser.

2.26.0

Breaking
  • Adds support for SDWebImage version 4.0.0 with GIF support. SDWebImage version 3.x will not be supported from this version on. Please make sure you are using the correct version of SDWebImage.framework. Note: SDWebImage 4.0.0 relies on FLAnimatedImage - users integrating in ways besides CocoaPods should ensure they link the FLAnimatedImage framework if they want GIF support.
  • Removes the url property from subclasses of ABKCard. This property has been renamed to urlString and moved onto the ABKCard superclass.
Added
  • Adds Cocoapods subspecs “Core” and “UI”.
    • The “UI” subspsec has the full feature set of the current SDK. This is the default subspec when no subspec is specified in the Podfile.
    • The “Core” subspec removes the SDWebImage framework dependency. This is for apps who do not use any Braze UI that leverages images (News Feed, in-app messages). If you use the “Core” subspec, in-app messages with images will not display, and the News Feed will render with plain white images.
  • Makes ABKThemableFeedNavigationBar.h and ABKNavigationBar.h public.
    • Addresses https://github.com/Appboy/appboy-ios-sdk/issues/68
  • Adds an unsafeInstance method that returns a nonoptional Appboy instance. If used before calling startWithApiKey: an exception will be thrown.
    • Addresses https://github.com/Appboy/appboy-ios-sdk/issues/45.
  • Adds ABKIDFADelegate protocol that can be used to create a delegate to pass Braze the IDFA in startWithApiKey: in the appboyOptions dictionary under the ABKIDFADelegateKey key. Alternative to existing ABKIdentifierForAdvertisingProvider compile flag solution.
Changed
  • Disables the -webkit-touch-callout property on HTML in-app messages. Long presses and 3D Touch on links will no longer display pop-ups with additional link information.

2.25.0

Added
  • Adds the ability to set the ABKInAppMessageControllerDelegate when the SDK starts by passing a delegate object to the ABKInAppMessageControllerDelegateKey in the appboyOptions of startWithApiKey:inApplication:withAppboyOptions:.
    • This is the recommended way to set the ABKInAppMessageControllerDelegate and circumvents a potential race condition where in-app messages can be shown before the delegate has been set.
  • Exposes the ABKFeedback object and adds a new method - (void)submitFeedback:(ABKFeedback *)feedback withCompletionHandler:(nullable void (^)(ABKFeedbackSentResult feedbackSentResult))completionHandler; in Appboy. The new method accepts a completion handler which receives an ABKFeedbackSentResult enum as feedback sending result.
    • The possible feedback sending results are: invalid feedback object(ABKInvalidFeedback), fail to send feedback(ABKNetworkIssue), and feedback sent successfully(ABKFeedbackSentSuccessfully).
  • Adds the utility method - (BOOL)userNotificationWasSentFromAppboy:(UNNotificationResponse *)response; to Appboy. This method is compatible with the UserNotifications framework and returns whether a push notification was sent from Braze’s server.
    • Those using - (BOOL)pushNotificationWasSentFromAppboy:(NSDictionary *)options; who have integrated the UserNotifications framework should use this method instead.
Fixed
  • Changes the ABKInAppMessageButton from a UIButton object to a pure data model class in NSObject.
    • This resolves the issue https://github.com/Appboy/appboy-ios-sdk/issues/97.
Changed
  • Adds more protection around triggered in-app message display.

2.24.5

Fixed
  • Fixes an issue where in-app messages triggered off of push clicks wouldn’t fire when the push click happened before the in-app message configuration was synced to the device.
Changed
  • Updates push registration to flush the token to the server immediately.
  • Improves the accessibility of in-app messages and news feed cards.
    • When in voiceOver mode, the SDK auto-focuses on in-app messages when they appear and resets focus on dismissal.
    • VoiceOver no longer reads Braze internal labels.
    • News feed cards are enhanced to be more accessible.

2.24.4

Added
  • Adds protection around in-app message UI code to avoid displaying in-app messages with corrupted images.
Fixed
  • Fixes the iOS version number in the deprecation warnings in Appboy.h.

2.24.3

Breaking
  • Update REQUIRED for apps using Braze SDK 2.24.0, 2.24.1 or 2.24.2 with UserNotifications.framework
Fixed
  • Fixes an issue where a user’s foreground push enabled status could erroneously be marked as disabled.
    • This issue can occur when opening the app from suspended mode. At that time, the foreground push enabled status was defaulted to disabled until the UserNotifications.framework returned the user’s push authorization status. If the user closed the app within a few seconds, the SDK would not flush the updated push status and the user would mistakenly be marked as “push disabled”.
    • This issue only affected apps using UserNotifications.framework to register for push notifications.
    • The updated code stores the push authorization status on disk to fix the issue.
  • Fixes an issue where triggered in-app messages with event property templating did not respect re-eligibility settings.
Changed
  • Updates the Podspecs for iOS and tvOS SDK.
  • Updates deprecation warnings to specify iOS version.
  • Updates the ABKFeedController with more generic nullability.
  • Disables all data detectors on HTML in-app messages. Phone numbers, web URLs, addresses and calendar events will no longer be automatically converted.
  • Disables scrolling bounces on HTML in-app messages.

2.24.2

Fixed
  • Fixes an issue where HTML in-app messages loaded JavaScript more than once.
  • Fixes the Appboy.inAppMessage.webview.done-button.title string in the French localization file, which was named incorrectly and wasn’t being found.

2.24.1

Added
  • Adds nullability annotation for the completionHandler in userNotificationCenter :didReceiveNotificationResponse:withCompletionHandler.

2.24.0

Breaking
  • Updates the SDK to require XCode 8.
  • iOS 10 changes behavior of application:didReceiveRemoteNotification:fetchCompletionHandler and subsequently breaks open tracking and deep link handling on most existing Braze iOS integrations. If you don’t currently implement application:didReceiveRemoteNotification: you need to modify your integration, and we recommend that all users update.
Added
  • Updates the iOS and tvOS SDKs to support iOS 10.
  • Adds a new method - (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)())completionHandler. This method supports the new delegate method for push notification handling in UserNotification framework.
Changed
  • Deprecates two push delegate methods: - (void)registerApplication:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)notification and - (void)getActionWithIdentifier:(NSString *)identifier forRemoteNotification:(NSDictionary *)userInfo completionHandler:(nullable void (^)())completionHandler.

2.23.0

Added
  • Adds support for upgraded in-app messages including image-only messages, improved image sizing/cropping, text scrolling, text alignment, configurable orientation, and configurable frame color.
  • Adds support for in-app messages triggered on custom event properties, purchase properties, and in-app message clicks.
  • Adds support for templating event properties within in-app messages.
Removed
  • Removes the deprecated method logSocialShare from Appboy class.

2.22.1

Changed
  • Updates tvOS bitcode support, patching an error introduced by an Xcode bug.

2.22.0

Added
  • Adds tvOS support for logging analytics; adds sample applications for tvOS and TVML.
  • Adds Hebrew localization support.

2.21.0

Breaking
  • Drops support for iOS 6.
Added
  • Adds support for deep links with non-URL-encoded characters. The SDK will encode unencoded url strings to create valid deep link NSURLs.
Fixed
  • Fixes a bug where the background of a slideup in-app message remained transparent when configured with 100% opacity.
Changed
  • Updates the podspec SDWebImage dependency to fetch the latest version.
  • Replaces SDK usage of NSURLConnection with NSURLSession.
  • Updates the SDK to always call canOpenURL: before opening a deep link. After this change, the SDK will only direct deep links whose schemes are whitelisted.
  • Updates push registration to immediately, asynchronously send up the push token.

2.20.1

Fixed
  • Fixes an issue where in certain conditions NSUserDefault blocking would cause custom events logged in the main thread to result in UI freezing.
Changed
  • Implements an optimization in push handling to not prefetch the News Feed when a push arrives and the app is in the background.

2.20.0

Added
  • Adds Carthage support.
Fixed
  • Fixes a multithreading issue where logging custom events from different threads would sporadically cause errors.
  • Fixes the issue where a close button’s color on modal and full in-app messages didn’t respect the opacity value.
  • Fixes an issue where failure to download HTML in-app message assets mid-download resulted in display without assets.
Changed
  • Now the onInAppMessageHTMLButtonClicked:clickedURL:buttonID: delegate method will be called every time a URL is clicked. The method used to be only called when there was a button ID in the URL link.
  • Updates the feedback element to reject messages that contain only whitespace.
  • Updates remote push handling to call the completion handler passed in every time (a code path previously existed that would return without calling it).
Removed
  • Removes the delegate method onInAppMessageHTMLButtonClicked:buttonID: from ABKInAppMessageControllerDelegate protocol.

2.19.3

Added
  • Adds a new feature allowing manual control of deep link handling in push notications. To use this, add a ABKPushURIDelegate value for the ABKPushURIDelegate key in the appboyOptions dictionary of startWithApiKey:inApplication:inApplication:withAppboyOptions:. Also updates the ABKPushURIDelegate integration to be initialized through that integration point.
  • Adds guarding against a possible crash caused by a user’s offline state being corrupted and not including an active session when a network request occurred.
Fixed
  • Fixes an issue where duplicate data could be recorded when a force quit or crash occurs after a network request completed successfully, but before any other activity (such as leaving the app, putting it to sleep, updating an attribute or firing some other event or purchase) occurred.

2.19.2

Added
  • Adds warning when messaging doesn’t succeed because SDWebImage is not integrated.
Fixed
  • Fixes a bug where users who went from being eligible for triggered messages to not being eligible for any triggered messages didn’t see their local triggers configuration get updated. This has already been fixed with a server-side update for affected versions; this update fixes the issue client-side.
Changed
  • Updates headers to be compatible with Swift 2.2.

2.19.1

Added
  • Adds sample code for a universal link in Stopwatch.
Fixed
  • Fixes the benign issue that caused the log message *** -[NSKeyedUnarchiver initForReadingWithData:]: data is NULL.
  • Fixes an issue where NULL campaign IDs in push messages (e.g. from a REST API push message without a specified campaign id) resulted in push-clicked triggers for triggered in-app messages not firing.
  • Fixes an issue where calling changeUser between identified users caused the read/unread state of the news feed cards of the old user to be set as the new user’s read/unread states.
  • Fixes an issue where a user attribute value that had been set to multiple different values created a state that would not let you set the original value again. The bug was introduced in version 2.17.1.
Changed
  • Analytics are now logged for in-app messages and in-app message buttons with ‘ABKInAppMessageNoneClickAction’ click actions. ABKInAppMessageNoneClickAction is set when an in-app message on the dashboard has a click action that only closes the in-app message; formerly this did not count as a click.

2.19.0

Added
  • Adds support for action-based, locally triggered in-app messages. In-app messages are now sent to the device at session start with associated trigger events. The SDK will display in-app messages in near real-time when the trigger event associated with a message occurs. Trigger events can be app opens, push opens, purchases, and custom events.
Changed
  • Deprecates the old system of requesting in-app message display, now collectively known as ‘original’ in-app messaging, where messages were limited to displaying at app start.

2.18.4

Fixed
  • Fixes a Cocoapods issue that emerged during the release of 2.8.13.

2.18.3

Changed
  • Makes an internal update to provide functionality for SDKs that embed this library.

2.18.2

Added
  • Adds warning logging if [Appboy sharedInstance] is called while in an uninitialized state.
Changed
  • Deprecates the delegate method getResourceEndpoint: in ABKAppboyEndpointDelegate. The SDK will no longer call this delegate method.

2.18.1

Fixed
  • Fixes the nullability annotation warnings in the public header files.
Changed
  • Updates HelloSwift sample app to adopt swift 2.0.

2.18

Added
  • Adds nullability annotations to all Braze public APIs.
  • Adds a new delegate method to support custom push URI handle. For more detail, please see ABKPushURIDelegate.h;
Changed
  • Updates to auto-dismiss the Braze web view when a user returns to the app after following a link out of the app from an Braze web view.
Removed
  • Removes the deprecated method requestSlideupRefresh from Braze class.

2.17.1

Fixed
  • Fixes a bug where in certain conditions the SDK would resend user attributes that had already synced with the server.

2.17

Added
  • Adds a new button clicked delegate method for HTML in-app message. The new delegate method also passes the URL of the clicked button.
Fixed
  • Fixes the crash caused by inserting a nil object into an NSDictionary when parsing an event object.
Changed
  • Makes the WebView background for HTML in-app messages transparent. Ensure HTML in-app messages you send to the device are created expecting a transparent background.
  • Applies the Braze endpoint delegate methods to in-app messages’ resource(zip and image) fetching.
Removed
  • Removes the Facebook button from Feedback page.

2.16.1

Added
  • Adds the ability to log a custom event from an HTML in-app message. To log a custom event from an HTML in-app message, navigate a user to a url of the form appboy://customEvent?name=customEventName&p1=v2, where the name URL parameter is the name of the event, and the remaining parameters are logged as String properties on the event.
  • Adds the support for customization of the background color of modal in-app messages.
Fixed
  • Fixes an issue where daylight savings changes were not reflected in the user profile timezone.
Changed
  • Enables users to input text into HTML in-app messages by allowing HTML in-app messages to be displayed with a keyboard on screen. For all other in-app messages, the in-app message will be dismissed when a keyboard is displayed.

2.16

Added
  • Adds HTML In-App Message types.
    • HTML In-App Messages consist of HTML and a url of a zipped archive of assets (e.g. images, css) to download locally which the HTML can reference. See InAppMessageUIViewController in our Stopwatch sample app for an example for the callbacks on the actions inside the WebView hosting the HTML In-App Message.
Changed
  • Deprecates the method - (void) logSocialShare:(ABKSocialNetwork)socialNetwork and enum ABKSocialNetwork in the Appboy class. If you use logSocialShare: to track user’s social account sharing, you can use logCustomEvent: instead.
  • Deprecates the property bio in the ABKUser class.

2.15.1

Fixed
  • Fixes the warning “full bitcode bundle could not be generated because XXX was built only with bitcode marker”.

2.15

Changed
  • Updates the SDK to support iOS 9. In iOS9, previous versions of the SDK: 1) did not have bitcode support, 2) had a minor UI issue in in-app messages where the slideup messages were not docked on the bottom of the screen if they had one line of text, 3) failed to localize for zh-HK and zh-TW.

2.14

Breaking
  • Migrates the SDK to ARC. If you are using our Apple Watch Extension and not using ARC, you must apply -fobjc-arc to the extension files.
Added
  • Adds configurable session timeout feature.
  • Adds feedbackViewControllerBeforeFeedbackSent method to the feedback delegate protocols, which can be used to modify the feedback message before it’s sent to Braze.
  • Adds a setAttributionData method to ABKUser that sets an ABKAttributionData object for the user. To be used with attribution provider SDKs when attribution events are fired.

2.13.2

Changed
  • Increases the number of supported currency codes from 22 to 171. All common currency codes are now supported. The full list of supported codes is available at Appboy.h.

2.13.1

Changed
  • Updates the isUninstallTrackingNotification method in ABKPushUtils to return the correct value.

2.13

Added
  • Adds an open-source Watch SDK to support data analytics on watchKit apps. You can use the Appboy-WatchKit SDK by downloading and adding the “Appboy-WatchKit” folder in your watchKit extension target. For more detail, please refer to ABWKUser.h and AppboyWatchKit.h.
  • Adds an opt-in location service that logs background location events; adds ABKLocationManager with methods for allowing Braze to request location permission on your behalf and logging the current location. More information on the background location capabilities will be made available when dashboard support is released.
  • Adds client side blocking of blacklisted attributes and events.
  • Adds ABKPushUtils with method + (BOOL) isUninstallTrackingNotification:(NSDictionary *)userInfo; that can be used to detect if a content-available push is from Braze uninstall tracking (and shouldn’t be acted upon).
  • Adds a new property expiresAt in class ABKCard. The property is the unix timestamp of the card’s expiration time. For more detail, please refer to ABKCard.h.
Changed
  • Stops collecting user’s Twitter data automatically. You can pass a user’s Twitter information to Braze by initialzing a ABKTwitterUser object with the twitter data, and setting it to [Appboy sharedInstance].user.twitterUser. For more information, please refer to ABKUser.h and ABKTwitterUser.h.
  • Stops logging foreground push as a push open as it is not delivered by the system.
Removed
  • Removes the feature of prompting a user to connect his/her social account. You can refer to the method promptUserToConnectTwitterAccountOnDeviceAndFetchAccountData in TwitterViewController.m to continue prompting the user to connect the Twitter account.

2.12.2

Fixed
  • Fixes the slideup in-app message display issue. When the host app sets the launch screen file, slideup in-app message from bottom sometimes didn’t dock at the bottom of the screen on iPhone 6 and iPhone 6 Plus.

2.12.1

Added
  • Adds font and font size customization to all in-app message’s header and message text through NUI. You can customize in-app message’s font by adding ABKInAppMessageSlideupMessageLabel, ABKInAppMessageeModalHeaderLabel,ABKInAppMessageModalMessageLabel, ABKInAppMessageFullHeaderLabel, ABKInAppMessageFullMessageLabel to your NUI nss style sheet.
Fixed
  • Fixes news feed issue where no news feed cards resulted in the loading spinner remaining on screen.
Changed
  • Cleans up the console logging in Class ABKIdentifierForAdvertisingProvider.

2.12.0

Fixed
  • Fixes the incorrect path runtime error for users who integrate our pod as a dynamic framework. For SDK versions before 2.12, when you integrate Braze with use_frameworks! in the Podfile, the library is integrated as a dynamic framework and the Appboy.bundle is stored in a different path.
Changed
  • Changes HelloSwift sample app to integrate Braze SDK as a dynamic framework.
Removed
  • Removes the subspecs from the podspec. This fixes the duplicate symbol error https://github.com/Appboy/appboy-ios-sdk/issues/24. If you are still using subspec like pod 'Appboy-iOS-SDK/AppboyKit' in your podfile, please make sure to change it to pod 'Appboy-iOS-SDK'.

2.11.3

Added
  • Adds the ability to send and retrieve extra key-value pairs via a News Feed card.
  • Adds the ability to define custom key-value properties on a custom event or purchase. Property keys are strings and values may be NSString, NSDate, or NSNumber objects.
  • Added the fix for an edge case when there are extra UIWindows at the time in-app message is going to display, the in-app message would have issue during dismissing.

2.11.2

Changed
  • Updates the serialize and deserialize methods for in-app message classes. This is for use by wrappers such as Braze’s Unity SDK for iOS.

2.11.1

Fixed
  • Fixes a UI issue in modal in-app messages displayed on iPads running iOS 6/7.

2.11

Added
  • Adds support for modal and full screen style in-app messages. Also adds support for including fontawesome icons and images with in-app messages, changing colors on in-app message UI elements, expanded customization options, and message resizing for tablets. Please visit our documentation for more information.
Changed
  • Updates the completionHandler signature in getActionWithIdentifier:forRemoteNotification:completionHandler: to match the comletionHandler passed by the system in method - (void) application:(UIApplication *)application handleActionWithIdentifier:(NSString *)identifier forRemoteNotification:(NSDictionary *)userInfo completionHandler:(void (^)())completionHandler.

2.10.2

Added
  • Adds the fix for an edge case when there are extra UIWindows at the time in-app message is going to display, the in-app message would have issue during dismissing.

2.10.1

Fixed
  • Fixes a bug which would cause the host app to crash when a deep link was launched from a push notification. In versions 2.10.0 and 2.9.4, if the host app used [[Appboy sharedInstance] registerApplication: didReceiveRemoteNotification:]; instead of [[Appboy sharedInstance] registerApplication: didReceiveRemoteNotification: fetchCompletionHandler:];, opening a push with a deep link would crash the host app in some circumstances.

2.10.0

Changed
  • Updates the minimum deployment targets of Braze iOS SDK to iOS 6.0. For apps supporting lower iOS versions, please continue to use 2.9.+ versions of the Braze SDK.
  • Stops collecting user’s Facebook data automatically. You can pass a user’s Facebook information to Braze by initializing a ABKFacebookUser object with the facebook data, and set it to [Appboy sharedInstance].user.facebookUser. For more information, please refer to ABKUser.h and ABKFacebookUser.h.
Removed
  • Removes Facebook SDK dependent builds. Now there is a single library - AppboyKit - and a single Pod without functional subspecs - Appboy-iOS-SDK (note we now have both the subspecs pointing at the same library). Please update your Podfile to pod 'Appboy-iOS-SDK if you are integrating Braze with Cocoapods.
  • Removes the feature of prompting a user to connect his/her Facebook account. You can refer to the method promptUserToConnectFacebookAccountOnDeviceAndFetchAccountData in FacebookViewController.m to continue prompting the user to connect the Facebook account.

2.9.6

Added
  • Adds the fix for an edge case when there are extra UIWindows at the time in-app message is going to display, the in-app message would have issue during dismissing.

2.9.5

Fixed
  • Fixes a bug which would cause the host app to crash when a deep link was launched from a push notification. In versions 2.9.4, if the host app used [[Appboy sharedInstance] registerApplication: didReceiveRemoteNotification:]; instead of [[Appboy sharedInstance] registerApplication: didReceiveRemoteNotification: fetchCompletionHandler:];, opening a push with a deep link would crash the host app in some circumstances.

2.9.4

Added
  • Adds a major performance upgrade that reduces CPU usage, memory footprint, and network traffic.
  • Adds 26 additional languages to localization support for Braze UI elements.
  • Adds support for deep linking from APNS push notification clicks.
  • Adds ability to customize the font of Feedback text using NUI with NUI class name ABKFeedbackTextView.
Fixed
  • Fixes the feedback page UI issues in iOS 8: when the device’s orientation is UIInterfaceOrientationPortraitUpsideDown, the contact info bar was off.
  • Fixes in-app messages to display correctly in landscape mode in iOS 8.
Changed
  • Updates the SDK to adopt the latest SDWebImage protocol methods.
Removed
  • Removes the “required” labels on the feedback page.

2.9.3

Added
  • Adds a new method - (void) registerApplication:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)notification fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler to support push with background fetch. This method should be called in - (void) application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler. For more details, please refer to Appboy.h.
  • Adds a HelloSwift sample app to demo how to use Braze in a swift app.
  • Adds a new NSString property “displayPrice” in ABKCrossPromotionCard to enable server side price localization.
Fixed
  • Fixes a bug of when sessions were being created when the app opened in the background.
  • Fixes a bug where requesting the news feed with a news feed open led to card impressions not updating until the next feed refresh.

2.9.2

Added
  • Adds the ability to turn off Braze’s automatic location collection by setting the ABKDisableAutomaticLocationCollectionKey boolean in AppboyOptions in startWithApiKey:inApplication:inApplication:withAppboyOptions:.
  • Adds the ability to send location tracking events to Braze manually using setLastKnownLocationWithLatitude:longitude:horizontalAccuracy: and setLastKnownLocationWithLatitude:longitude:horizontalAccuracy:altitude:verticalAccuracy: on the ABKUser. this is intended to be used with ABKDisableAutomaticLocationCollectionKey set to true in the AppboyOptions so that locations are only being recorded from a single source.
Fixed
  • Fixes a news feed bug: sometimes the spinner keeps spinning on the screen even after the news feed card image is displayed.
Changed
  • Updates sample app core location fetching code based on the changes in iOS 8.

2.9.1

Fixed
  • Fixes a news feed bug: When a user refreshed the news feed by swiping down, if the total number of cards in the feed was going to be reduced by the refresh, the app would crash.

2.9.0

Fixed
  • Fixes an App Store validation error introduced when the App Store started accepting submissions for iOS8. This was done by changing the packaging of the Braze framework to include a universal binary and a resource bundle (instead of combining them both together in a universal framework). Due to this change, Cocoapod integration is even more highly recommended than before to fully automate integration.

2.8.1

Added
  • Adds a new method - (void) getActionWithIdentifier:(NSString *)identifier forRemoteNotification:(NSDictionary *)userInfo to collect analytics data for push actions in iOS 8. It should be called in the UIApplication delegate method - (void) application:(UIApplication *)application handleActionWithIdentifier:(NSString *)identifier forRemoteNotification:(NSDictionary *)userInfo completionHandler:(void (^)())completionHandler. For more details, please refer to Appboy.h.
  • New Custom Attribute Data Type (Array): Braze now supports custom attributes which contain an array of string elements. In addition, we also provide methods for adding or removing an string element from an array type custom attribute. For more information, please refer to ABKUser.h.
  • Users can now pull down on the Braze Newsfeed to refresh the content on iOS version 6.0 or later.
Changed
  • Restricts product identifier string to 255 characters for method - (void) logPurchase:(NSString *)productIdentifier inCurrency:(NSString *)currencyCode atPrice:(NSDecimalNumber *)price and - (void) logPurchase:(NSString *)productIdentifier inCurrency:(NSString *)currencyCode atPrice:(NSDecimalNumber *)price withQuantity:(NSUInteger)quantity.
  • News feed card now can update the card height and display a full image based on the image ratio. Card image ratio used to be a fix number and images were aspect stretched to fit in the views.
  • The right and left margins in the news feed are now touchable areas for scrolling.
  • Card titles have been improved and will now truncate with “…” when they are more than 2 lines.

2.8

Breaking
  • Renames the class names of news feed cards to match the names on dashboard:
v2.8 v2.7
ABKBannerCard ABKCardBanner
ABKCaptionedImageCard ABKCardCaptionedMessage
ABKCrossPromotionCard ABKCardCrossPromotionSmall
ABKClassicCard ABKCardNews
ABKTextAnnouncementCard ABKCardTextAnnouncement
Added
  • Adds email and push notification subscription types for a user. Subscription types are explicitly opted in, subscribed, and unsubscribed. The previous email boolean subscribe method has been deprecated.
  • Adds custom slideup orientation support. You can now ask the slideup to only support certain orientations. For more details on slideup custom orientation support, please refer to ABKSlideupController.h.
  • Adds quantity parameter as an option when logging purchase. The quanlity should be an unsigned interger greater than 0 and no larger than 100. For more information, please refer to Appboy.h.
  • Adds a class method in ABKCard to deserialize a given dictionary to a card. This is for use by wrappers such as Braze’s Unity SDK for iOS. Please refer to ABKCard.h for more information.

2.7

News Feed Update

  • Exposes raw card data in ABKFeedController
    • Developers can use the raw card data to creat custom user interfaces for the news feed. For more details on the card data, please refer to ABKFeedController.h.
  • Addes support for categories on cards and news feed view controllers.
    • Categories include Announcement, Advertising, Social, News and No Category. You can get cards of certain categories from ABKFeedController, or you can make ABKFeedViewController only display certain categories of cards.
  • Uses SDWebImage to handle images downloading and caching in the news feed, display a spinner while downloading images and show a default image when no image available.
    • Adds support for asynchronous image downloading in the news feed, asynchronous memory and disk image caching with automatic cache expiration handling.
  • Adds news feed view controller delegate to support custom handling of card clicks on news feed.
    • The app can customize click actions from the feed and display any card link in their own user interface.

Slideup Changes

  • Updates ABKSlideupControllerDelegate method onSlideupClicked to return a BOOL value to indicate if Braze should continue to execute the click action.
  • Stops logging slideup click when the slideup click action is ABKSlideupNoneClickAction.

Feedback Changes

  • Updates the ABKFeedbackViewControllerPopoverContext so now it should be used in all cases where the feedback page displayed in a popover, including the case that the feedback is push on a navigation controller in a popover.
  • Fixes the ABKFeedbackVIewControllerModalContext cancel button delegate issue.
  • Fixes the form sheet style ABKFeedbackViewControllerModalContext layout issue.

Other Changes

  • Adds API to request feed and slideup refresh.
  • Adds API to log news feed displayed and feedback displayed.
    • Allows updating analytics data even using customized news feed or feedback user interfaces.
  • Updates badge count policy to only update when app is foreground.
  • Adds clearTwitterDataWhenNoDataOfTwitterIdentifier to ABKUser, allowing developer to clear user data when a user disconnectes their twitter accounts.
  • Updates custom key and string value for custom attributes to automatically trim.

2.6.3

Changed
  • Updates the SDK to authenticate with the Twitter API using SSL.

2.6.2

Fixed
  • Fixes a news feed card click tracking issue.
Changed
  • Updates data flush time interval.

2.6.1

Fixed
  • Fixes a minor display problem that affected news items with no image or link for version 2.6.

2.6

Breaking
  • Braze iOS SDK now supports 64 bit as well. The minimum deployment targets that Braze iOS SDK supports is iOS 5.1.1.
    • The Braze iOS SDK will now allow function with 64-bit apps. This version of the SDK only supports iOS 5.1.1+. Legacy iOS apps should continue to use version 2.5 of the SDK.
    • You can install legacy versions of our SDK via CocoaPods by following changing the podfile to include something like the following example pod 'Appboy-iOS-SDK/AppboyKit', '~> 2.5'.

2.5.1

Fixed
  • Fixes a minor display problem that affected news items with no image or link for version 2.5.

2.5

Localization

Localization is now supported in version 2.5 of the Braze SDK. We have provided .string files for English, Simplified Chinese and Traditional Chinese. You can also optionally override our Braze’s default LocalizedAppboyUIString.strings right within your app’s Localizable.Strings file in much the same way you would do an override in CSS. To do so, copy the key and string pair into your Localizable.Strings file and edit the string as you so desire.

For your convenience our CocoaPod integrates the LocalizedAppboyUIString.strings files for the three aforementioned languages. If you do not wish to use one or more of these languages, you can feel free to delete these files from your project.

Slideup Upgrade

Braze version 2.5 provides a substantial upgrade to the slideup code and reorganization for better flexibility moving forward, but at the expense of a number of breaking changes. We’ve detailed the changes in this changelog and hope that you’ll love the added power, increased flexibility, and improved UI that the new Braze slideup provides. If you have any trouble with these changes, feel free to reach out to success@braze.com for help, but most migrations to the new code structure should be relatively painless.

New Slideup Controller

  • The property slideupController has been added to the Braze object. Please see ABKSlideupController.h for details.
    • The delegate property allows you to specify a delegate for the slideup.
      • This replaces slideupDelegate which has been removed.
    • The displayNextSlideupWithDelegate: method displays the next available slideup with the specified delegate.
      • This replaces provideSlideupToDelegate: which has been removed from Braze.
    • The slideupsRemainingOnStack method returns the number of slideups that are waiting locally to be displayed.
    • The addSlideup: method allows you to display a slideup object with custom content. This is useful in testing or if you want to use the Braze slideup’s UI/UX with another notification system that you are using.
      • Clicks and impressions of slideups added by this method will not be collected by Braze.
    • hideCurrentSlideup: method will remove any slideup currently on screen, with or without animation.

New Slideup Properties and Methods in ABKSlideup.h

The following properties and methods all belong to the ABKSlideup object. Please see ABKSlideup.h for more information.

New Properties
  • The extras property carries additional data within key value pairs that have been defined on the dashboard, just like a push notification. Braze does nothing with the extras property, any additional behavior is at your discretion.
  • The slideupAnchor property defines whether the slideup originates from the top or the bottom of the screen.
  • The slideupDismissType property controls whether the slideup will dismiss automatically after a period of time has lapsed, or if it will wait for interaction with the user before disappearing.
    • The slideup will be dismissed automatically after the number of seconds defined by the newly added duration property if the slideup’s slideupDismissType is ABKSlideupDismissAutomatically.
  • The slideupClickActionType property defines the action behavior after the slideup is clicked: displaying a news feed, redirect to a uri, or nothing but dismissing the slideup. This property is read only. If you want to change the slideup’s click behavior, you can call one of the following method: setSlideupClickActionToNewsFeed, setSlideupClickActionToUri: or setSlideupClickActionToNone.
  • The uri property defines the uri string that the slide up will open when the slideupClickActionType is ABKSlideupRedirectToURI. This is a read only property, you can call setSlideupClickActionToUri: to change it’s value.
New Methods
  • logSlideupImpression and logSlideupClicked have been added to allow you to report user interactions with the slideup in the case that you’ve fully customized the slideup experience and Braze is not handling the interactions.
  • setSlideupClickActionToNewsFeed, setSlideupClickActionToUri: and setSlideupClickActionToNone have been added to allow you to change the slideup’s click action behavior. setSlideupClickActionToUri: accepts a uri string as parameter and required the given uri string is valid.

Delegate Method Changes

All former Braze slideup delegate methods have been depreciated and removed. In their place Braze has added new slideup delegate methods within ABKSlideupControllerDelegate.h.

  • onSlideupReceived: is called when slideup objects are received from the Braze server.
  • beforeSlideupDisplayed:withKeyboardIsUp: is called before slideup objects are displayed, the return value determines whether the slideup will be displayed, queued or discarded.
  • slideupViewControllerWithSlideup: This delegate method allows you to specify custom view controllers in which your slideups will be displayed.
    • The custom view controller should be a subclass of ABKSlideupViewController.
      • Alternatively, it can also be an instance of ABKSlideupViewController.
    • The view of the returned view controller should be an instance of ABKSlideupView or its subclass.
    • For integration examples of a custom slideup view controller, see the CustomSlideupViewController class in Braze’s sample app Stopwatch.
  • onSlideupClicked: is called when a user clicks on a slideup. We recommend that you specify behavior on click via the dashboard, but you can additionally specify behavior on click by defining this delegate method.
  • onSlideupDismissed: is called whenever the slideup is dismissed regardless of whether the dismissal occurs automatically or via swipe. This method is not called if the user clicks on the slideup. If the user clicks or taps on the slideup, onSlideupClicked is called instead.

New Options on the Dashboard

  • Slideup behavior on click can now be set within the dashboard to open a modal news feed, open a URI within a modal, or do nothing.
  • The following properties can be set remotely from the Braze Dashboard:
    • extras
    • slideupAnchor
    • slideupDismissType
    • slideupClickActionType
    • uri

News Feed Changes

  • News feed items are now cached in offline storage, allowing the news feed to render even when no internet connectivity is available. Braze will still automatically try to pull down a new news feed when a session opens, even if an offline feed is available.
  • Each card now has a maximum height of no more than 2009 points to avoid any performance issues as recommended by iOS developer guidelines.
  • The entirety of captioned image cards are now clickable. Formerly, only the link itself was clickable.
  • When the news feed is brought to the foreground, it will now automatically check for new content if the cached version of the feed was received more than 60 seconds ago. — The width of news feed cards as well as the minimum margin between any card and the left & right edges of the view controller can now be customized. These values can be set separately for both iPad and iPhone. This allows for a larger news feed to render on larger screen sizes. All card images will scale proportionally. Please see ABKFeedViewControllerContext.h and ABKFeedViewController.h for more information.

Other Changes

  • Various internal and news feed display optimizations.

2.4

  • IDFA Collection is now optional.
    • By default, IDFA collection is now disabled by the Braze SDK.
      • There will be no loss of continuity on user profiles or loss of functionality whatsoever as a result of this change.
      • If you’re using advertising elsewhere in the app or through our in-app news feed, we recommend continuing to collect the IDFA through Braze. You should be able to do so safely without fear of rejection from the iOS App Store.
      • The future availability of IDFAs will enable functionality like integrating with other third-party systems, including your own servers, and enabling re-targeting of existing users outside of Braze. If you continue to record them we will store IDFAs free of charge so you can take advantage of these options immediately when they are released without additional development work.
    • Necessary Project Changes
      • ABKIdentifierForAdvertisingProvider.m and ABKIdentifierForAdvertisingProvider.h must be added to your project regardless of whether or not you enable collection. This occurs automatically if you integrate/update via the CocoaPod.
    • Enabling Braze IDFA Collection
      • IDFA collection can be enabled via adding the following PreProcessor Macro to the Build Settings of your app:
        • ABK_ENABLE_IDFA_COLLECTION

2.3.1

  • The Braze SDK for iOS now has two versions, one for use with apps which incorporate the official Facebook SDK and one for those which do not. In November of 2013, the App Store Validation Process started generating warnings about the usage of isOpen and setActiveSession in the Braze SDK. These selectors were being sent to instances of classes in the Facebook SDK and are generally able to be used without generating warnings. However because of the way that the classes were initialized in Braze (a result of building a single Braze binary to fully support apps with and without the Facebook SDK), the App Store Validation Process started generating warnings the Facebook SDK methods share a name with private selectors elsewhere in iOS. Although none of our customers have been denied App Store approval yet, to protect against potential validation policy changes by Apple, Braze now provides two versions of its SDK, neither of which generate warnings. Going forward, the appboy-ios-sdk repository will provide both versions of the SDK in the folders ‘AppboySDK’ (as before) and ‘AppboySDKWithoutFacebookSupport’. The ‘AppboySDKWithoutFacebookSupport’ does not require the host app to include the Facebook SDK, but as a result does not include all of the Braze features for Facebook data fetching. More information is available here within the Braze documentation.
  • Fixed a bug that repeatedly updated the push token of some users unnecessarily.
  • The “Reporting an Issue?” box within the UI layout of the Feedback Page has been moved to the left side of the label away from the “Send” button. This change was made to reduce the number of misclicks of the “Send” button. The “Reporting an Issue?” label is now clickable as well.
  • Cross Promotion Cards for apps with long titles will now render appropriately in iOS5. Before the title would render abnormally large on these devices.
  • Fixed a bug where view recycling would cause incorrect card images to appear for newly rendered cards (until the image for that card finished downloading). Card images for newly rendered cards will now remain empty until the correct image is downloaded.
  • Internal changes to enable future support for a 64 bit library release.
  • Improvements to the Braze Sample App.
  • Internal code structure and performance improvements including the move of more offline caching to background tasks.

2.3

  • BREAKING CHANGE: The ABKSlideupControllerDelegate interface has been changed to work with ABKSlideup objects instead of simply the slideup message. This provides you with more control over the click actions and display of slideups and is also being made in anticipation of the augmentation of the ABKSlideup object with more data properties in future releases. To access the message previously sent to shouldDisplaySlideup, simply access the message property on the provided ABKSlideup argument.
  • displayNextAvailableSlideup has been deprecated and will be removed in the next minor release, it has been replaced by provideSlideupToDelegate, see Appboy.h documentation for more information.
  • provideSlideupToDelegate has been added to Braze to allow for more fine grained control over slideup display.
  • Fixes a bug where the slideupDelegate property getter on Braze would always return nil.
  • Changes the slideupDelegate property on Braze to be retained, instead of assigned.

2.2.1

  • Adds a startup option to appboyOptions to control the automatic capture of social network data. See the documentation on ABKSocialAccountAcquisitionPolicy in Appboy.h for more information.
  • Changes a table cell’s default background color to clear, from the white value that became default in iOS7.
  • Adds support for developer to send up image_url for user avatars, allowing for custom images to be included in user profiles on the dashboard.

2.2

  • Adds support for new banner and captioned image card types.
  • Adds support for submitting feedback programmatically through an Appboy.h method without using Braze feedback form. This allows you to create your own feedback form.
  • Fixes an issue where the the news feed’s web view would display “Connection Error” when a user came back into the app after a card had directed him or her to a protocol URL. Now when users come back from a redirected protocol URL, the feed is properly displayed.
  • Fixes an issue where the SDK might have incorrectly sent both read and write Facebook permissions at the same time, instead preferring to request only those read permissions that Braze is interested in and have already been requested by the incorporating app.
  • Fixes a corner case where card impressions could be miscounted when the feed view controller is the master view controller of a split view.
  • Makes cards truncate properly at two lines.

2.1.1

  • URGENT BUGFIX: This fixes an issue which exists in all previous versions of the v2 SDK which is causing crashes on the just release iPhone 5c and iPhone 5s. All users of v2 are recommended to upgrade the Braze SDK to 2.1.1 immediately and re-submit to the app store.

2.1.0

  • Adds support for iOS 7. You will need to use Xcode 5 to use this and future versions of the Braze iOS SDK.
  • Updates internal usage of NUI. If you’re using NUI, please ensure that you are at least using version 0.3.3 (the most up to date as of this writing is 0.3.4).
  • Removes support for iOS 4.3.
  • Optimizes news feed rendering for faster start up times and smoother scrolling of long feeds.
  • Removes the deprecated - (void) logPurchase:(NSString *)productId priceInCents:(NSUInteger)price method in favor of the new multi-currency tracking method. Conversion of old method calls is straightforward. [[Appboy sharedInstance] logPurchase:@"powerups" priceInCents:99]; should turn into [[Appboy sharedInstance] logPurchase:@"powerups" inCurrency:@"USD" atPrice:[[[NSDecimalNumber alloc] initWithFloat:.99f] autorelease]];
  • Any references to the delegate property of ABKFeedbackViewControllerModalContext should be updated to the new property name feedbackDelegate.
  • Following the removal of support for 4.3, removes SBJson parsing and uses built-in parsing added in iOS5 to improve performance and lower the SDK footprint.

2.0.4

  • Adds support for reporting purchases in multiple currencies. Also, changes the price reporting object type to NSDecimalNumber for consistency with StoreKit.
  • Adds additional space savings optimizations to image assets.
  • Minor fix to orientation change handling in the example app code.

2.0.3

  • Adds the ability to assign a Foursquare access token for each user. Doing so will cause the Braze backend to make Foursquare data available in user profiles on the dasbhard.
  • Adds more fine grained control options for Braze’s network activity. See Appboy.h for more information.

2.0.2

  • Fixes a bug where Braze might reopen a Facebook read session when a publish session already exists

2.0.1

  • UI Improvements
    • Fixed a bug when using the nav context feedback in a popover window that would cause the email bar to disappear
    • Updated news feed’s close button when opened from a slide up
    • Added a loading spinner on the feedback page when fetching email address from Facebook
    • Fixed the bug where the modal context feed page’s navigation bar would not adhere to NUI theming
    • Improved the look of the popover content feedback page
    • Enabled resizable webpages when clicking on to a web URL through a card
  • API updates
    • Updated custom user attribute setting methods to return a boolean value indicating if the setting is successful
    • Added methods for incrementing custom user attributes
    • Added support for device push tokens as NSData when registering the token to Braze
    • More detailed error messages logged in console
    • Removed the enable/disable Braze methods from Appboy.h

2.0

  • Initial release
# MacOS용 초기 SDK 설정 Source: /docs/ko/developer_guide/platforms/legacy_sdks/macOS/initial_sdk_setup/index.md
**Warning:** [AppboyKit](https://github.com/Appboy/appboy-ios-sdk) (also known as the Objective-C SDK) is no longer supported and has been replaced by the [Swift SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=swift). It will no longer receive new features, bug fixes, security updates, or technical support—however, messaging and analytics will continue to function as normal. To learn more, see [Introducing the New Braze Swift SDK](https://www.braze.com/resources/articles/introducing-the-new-braze-swift-sdk). # 초기 SDK 설정 {#initial-sdk-setup} > 이 참조 문서에서는 MacOS용 Braze SDK를 설치하는 방법을 설명합니다. 버전 [3.32.0](https://github.com/Appboy/appboy-ios-sdk/releases/tag/3.32.0)부터 Braze SDK는 스위프트 패키지 매니저를 통해 통합할 때 [Mac Catalyst](https://developer.apple.com/mac-catalyst/)를 사용하는 앱에 대해 macOS를 지원합니다. 현재 SDK는 CocoaPods 또는 Carthage를 사용할 때 Mac Catalyst를 지원하지 않습니다. **Note:** Mac Catalyst로 앱을 빌드하려면 Apple 설명서 를 참조하세요. 앱이 Catalyst를 지원하면 [다음 지침에 따라 스위프트 패키지 매니저를 사용](https://www.braze.com/docs/ko/ko/developer_guide/platform_integration_guides/swift/sdk_integration/?tab=swift%20package%20manager/)하여 Braze SDK를 앱으로 가져옵니다. ## 지원되는 기능 {#supported-features} Braze는 Mac Catalyst에서 실행할 때 [푸시 알림](https://www.braze.com/docs/ko/ko/developer_guide/push_notifications/?sdktab=swift), [Content Cards](https://www.braze.com/docs/ko/ko/developer_guide/platforms/swift/content_cards/#content-cards-data-model), [인앱 메시지](https://www.braze.com/docs/ko/ko/developer_guide/analytics/tracking_location/?sdktab=swift), [자동 위치 수집](https://www.braze.com/docs/ko/ko/developer_guide/analytics/tracking_location/?sdktab=swift)을 지원합니다. Push Stories, 리치 푸시 및 지오펜스는 macOS에서 지원되지 않습니다. [1]:https://github.com/Appboy/appboy-ios-sdk/releases/tag/3.32.0 [2]:https://developer.apple.com/mac-catalyst/ # tvOS용 초기 SDK 설정 Source: /docs/ko/developer_guide/platforms/legacy_sdks/tvos/initial_sdk_setup/index.md
**Warning:** [AppboyKit](https://github.com/Appboy/appboy-ios-sdk) (also known as the Objective-C SDK) is no longer supported and has been replaced by the [Swift SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=swift). It will no longer receive new features, bug fixes, security updates, or technical support—however, messaging and analytics will continue to function as normal. To learn more, see [Introducing the New Braze Swift SDK](https://www.braze.com/resources/articles/introducing-the-new-braze-swift-sdk). # 초기 SDK 설정 {#initial-sdk-setup} > 이 참조 문서에서는 tvOS용 Braze SDK를 설치하는 방법을 설명합니다. Braze SDK를 설치하면 기본적인 분석 기능을 사용할 수 있습니다. **Note:** 현재 tvOS SDK는 분석 기능을 지원합니다. 대시보드에 tvOS 앱을 추가하려면 [지원 티켓](https://www.braze.com/docs/ko/ko/braze_support/)을 여세요. tvOS Braze SDK는 Objective-C 및 Swift 프로젝트의 종속성 매니저인 [CocoaPods](http://cocoapods.org/)를 사용하여 설치하거나 업데이트해야 합니다. CocoaPods를 사용하면 통합과 업데이트가 더욱 간편해집니다. ## tvOS SDK CocoaPods 통합 {#tvos-sdk-cocoapods-integration} ### 1단계: CocoaPods 설치 {#step-1-install-cocoapods} tvOS [CocoaPods](http://cocoapods.org/)를 통해 SDK를 설치하면 대부분의 설치 과정이 자동으로 수행됩니다. 이 프로세스를 시작하기 전에 [Ruby 버전 2.0.0](https://www.ruby-lang.org/en/installation/) 이상을 사용 중인지 확인합니다. 시작하려면 다음 명령을 실행하세요: ```bash $ sudo gem install cocoapods ``` - `rake` 실행 파일을 덮어쓰라는 프롬프트가 표시되면 자세한 내용은 CocoaPods.org의 [시작하기](http://guides.cocoapods.org/using/getting-started.html)를 참조하세요. - CocoaPods에 대한 문제가 있는 경우 [CocoaPods 문제 해결 가이드](http://guides.cocoapods.org/using/troubleshooting.html)를 참조하세요. ### 2단계: Podfile 구성 {#step-2-constructing-the-podfile} CocoaPods Ruby Gem을 설치했으므로 Xcode 프로젝트 디렉토리에 `Podfile`이라는 파일을 만들어야 합니다. Podfile에 다음 줄을 추가합니다: ``` target 'YourAppTarget' do pod 'Appboy-tvOS-SDK' end ``` 포드 업데이트에서 마이너 버전 업데이트보다 작은 내용을 자동으로 가져올 수 있도록 Braze 버전을 설정하는 것이 좋습니다. `pod 'Appboy-tvOS-SDK' ~> Major.Minor.Build`와 같은 형태입니다. 주요 변경 사항이 있더라도 최신 Braze SDK 버전을 자동으로 통합하려면 Podfile에서 `pod 'Appboy-tvOS-SDK'`를 사용하면 됩니다. ### 3단계: Braze SDK 설치 {#step-3-installing-the-braze-sdk} Braze SDK CocoaPods를 설치하려면 터미널에서 Xcode 앱 프로젝트의 디렉토리로 이동하여 다음 명령을 실행합니다: ``` pod install ``` 이때 CocoaPods에서 생성한 새 Xcode 프로젝트 워크스페이스를 열 수 있어야 합니다. Xcode 프로젝트 대신 이 Xcode 워크스페이스를 사용해야 합니다. ![](https://www.braze.com/docs/ko/ko/assets/img_archive/podsworkspace.png?96819fcb60bb61e9a9b991e15b4ef6d6) ### 4단계: 앱 델리게이트 업데이트 {#step-4-updating-your-app-delegate} 다음 코드 줄을 `AppDelegate.m` 파일에 추가합니다: ```objc #import ``` `AppDelegate.m` 파일 내에서 `application:didFinishLaunchingWithOptions` 메서드에 다음 스니펫을 추가합니다: ```objc [Appboy startWithApiKey:@"YOUR-API-KEY" inApplication:application withLaunchOptions:launchOptions]; ``` 마지막으로 **설정 관리** 페이지에서 `YOUR-API-KEY`를 올바른 값으로 업데이트합니다. Braze SDK를 CocoaPods 또는 Carthage와 통합하는 경우 `AppDelegate.swift` 파일에 다음 코드 줄을 추가합니다: ```swift import AppboyTVOSKit ``` Swift 프로젝트에서 Objective-C 코드를 사용하는 방법에 대한 자세한 내용은 [Apple 개발자 문서](https://developer.apple.com/library/ios/documentation/swift/conceptual/buildingcocoaapps/MixandMatch.html)를 참조하세요. `AppDelegate.swift`에서 `application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool`에 다음 스니펫을 추가합니다: ```swift Appboy.start(withApiKey: "YOUR-API-KEY", in:application, withLaunchOptions:launchOptions) ``` 그런 다음 **설정 관리** 페이지에서 `YOUR-API-KEY`를 올바른 값으로 업데이트합니다. `sharedInstance` 싱글톤은 `startWithApiKey:`가 호출되기 전에는 nil 상태입니다. 이는 모든 Braze 기능을 사용하기 위한 전제 조건이기 때문입니다. **Warning:** 애플리케이션의 메인 스레드에서 Braze를 초기화해야 합니다. 비동기적으로 초기화하면 기능이 손상될 수 있습니다. ### 5단계: 커스텀 엔드포인트 또는 데이터 클러스터 지정 {#step-5-specify-your-custom-endpoint-or-data-cluster} **Note:** 2019년 12월부터 커스텀 엔드포인트는 더 이상 제공되지 않으며, 기존 커스텀 엔드포인트가 있는 경우 계속 사용할 수 있습니다. 자세한 내용은 사용 가능한 엔드포인트 목록 을 참조하세요. Braze 담당자가 이미 [올바른 엔드포인트](https://www.braze.com/docs/ko/ko/user_guide/administrative/access_braze/sdk_endpoints/)에 대해 안내했을 것입니다. #### 컴파일 타임 엔드포인트 구성(권장) {#compile-time-endpoint-configuration-recommended} 기존 커스텀 엔드포인트가 있는 경우: - Braze iOS SDK v3.0.2부터 `Info.plist` 파일을 사용하여 커스텀 엔드포인트를 설정할 수 있습니다. Info.plist 파일에 `Appboy` 사전을 추가합니다. `Appboy` 사전 내에서 `Endpoint` 문자열 하위 항목을 추가하고 값을 커스텀 엔드포인트 URL의 권한으로 설정합니다(예: `https://sdk.iad-01.braze.com`이 아닌 `sdk.iad-01.braze.com`). #### 런타임 엔드포인트 구성 {#runtime-endpoint-configuration} 기존 커스텀 엔드포인트가 있는 경우: - Braze iOS SDK v3.17.0+부터 `startWithApiKey:inApplication:withLaunchOptions:withAppboyOptions:`에 전달된 `appboyOptions` 매개변수 내 `ABKEndpointKey`를 통해 엔드포인트 설정을 재정의할 수 있습니다. 값을 커스텀 엔드포인트 URL의 권한으로 설정합니다(예: `https://sdk.iad-01.braze.com`이 아닌 `sdk.iad-01.braze.com`). **Note:** `ABKAppboyEndpointDelegate`를 사용하여 런타임에 엔드포인트를 설정하는 지원은 Braze iOS SDK v3.17.0에서 제거되었습니다. 이미 `ABKAppboyEndpointDelegate`를 사용하고 있는 경우, Braze iOS SDK 버전 v3.14.1~v3.16.0에서는 `getApiEndpoint()` 메서드에서 `dev.appboy.com`에 대한 참조를 `sdk.iad-01.braze.com`에 대한 참조로 바꾸어야 합니다. ### SDK 통합 완료 {#sdk-integration-complete} 이제 Braze가 애플리케이션에서 데이터를 수집하며 기본 통합이 완료됩니다. tvOS 앱 및 기타 서드파티 라이브러리를 컴파일할 때는 Bitcode를 활성화해야 합니다. ### CocoaPods를 통해 Braze SDK 업데이트 {#updating-the-braze-sdk-via-cocoapods} CocoaPod를 업데이트하려면 프로젝트 디렉토리에서 다음 명령을 실행하면 됩니다: ``` pod update ``` ## 시작 시 Braze 커스터마이징 {#customizing-braze-on-startup} 시작 시 Braze를 커스터마이징하려면 대신 Braze 초기화 메서드 `startWithApiKey:inApplication:withLaunchOptions:withAppboyOptions`를 사용하고 선택 사항인 Braze 시작 키의 `NSDictionary`를 전달할 수 있습니다. `AppDelegate.m` 파일의 `application:didFinishLaunchingWithOptions` 메서드에서 다음 Braze 메서드를 추가합니다: ```objc [Appboy startWithApiKey:@"YOUR-API-KEY" inApplication:application withLaunchOptions:launchOptions withAppboyOptions:appboyOptions]; ``` `AppDelegate.swift`에서 `application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool` 메서드 내에 다음 Braze 메서드를 추가합니다: ```swift Appboy.start(withApiKey: "YOUR-API-KEY", in:application, withLaunchOptions:launchOptions, withAppboyOptions:appboyOptions) ``` 여기서 `appboyOptions`는 시작 구성 값의 `Dictionary`입니다. 이 메서드는 `startWithApiKey:inApplication:withLaunchOptions:` 초기화 메서드를 대체하며 다음 매개변수와 함께 호출됩니다: - `YOUR-API-KEY`: 애플리케이션의 API 키는 Braze 대시보드의 **설정 관리**에서 찾을 수 있습니다. - `application`: 현재 앱입니다. - `launchOptions`: `application:didFinishLaunchingWithOptions:`에서 가져올 수 있는 옵션 `NSDictionary`. - `appboyOptions`: Braze의 시작 구성 값이 포함된 선택적 `NSDictionary`. Braze 시작 키 목록은 [Appboy.h](https://github.com/Appboy/appboy-ios-sdk/blob/master/AppboyKit/include/Appboy.h)를 참조하세요. ## Appboy.sharedInstance() 및 Swift 널 허용 {#appboysharedinstance-and-swift-nullability} 일반적인 관행과는 다소 다르게 `Appboy.sharedInstance()` 싱글톤은 옵셔널입니다. `startWithApiKey:`가 호출되기 전에 `sharedInstance`가 `nil`이고, 표준은 아니지만 지연된 초기화를 사용할 수 있는 유효한 구현이 일부 있기 때문입니다. Appboy의 `sharedInstance`(표준 구현)에 액세스하기 전에 `didFinishLaunchingWithOptions:` 델리게이트에서 `startWithApiKey:`를 호출하면 `Appboy.sharedInstance()?.changeUser("testUser")`와 같은 옵셔널 체이닝을 사용하여 번거로운 확인 작업을 피할 수 있습니다. 이는 null이 아닌 `sharedInstance`를 가정하는 Objective-C 구현과 동등합니다. ## 수동 통합 옵션 {#manual-integration-options} [퍼블릭 리포지토리](https://github.com/appboy/appboy-ios-sdk)에서 프레임워크를 가져와 이전 섹션에서 설명한 대로 Braze를 초기화하면 tvOS SDK를 수동으로 통합할 수도 있습니다. ## 사용자 식별 및 분석 보고 {#identifying-users-and-reporting-analytics} 사용자 ID 설정, 커스텀 이벤트 기록, 사용자 속성 설정에 대한 자세한 내용은 [iOS 설명서](https://www.braze.com/docs/ko/ko/developer_guide/analytics/setting_user_ids/?tab=swift)를 참조하세요. 또한 [이벤트 이름 지정 규칙](https://www.braze.com/docs/ko/ko/user_guide/data/activation/events/event_naming_conventions/)을 숙지하는 것이 좋습니다. # 배너에 대하여 Source: /docs/ko/developer_guide/banners/index.md # Banners > With Banners, you can create personalized messaging for your users, all while extending the reach of your other channels, such as email or push notifications. You can embed Banners directly in your app or website, which lets you engage with users through an experience that feels natural. ## Prerequisites Banners availability depends on your Braze package. Contact your account manager or customer success manager to get started. Before you start, make sure you have [Banner placements](https://www.braze.com/docs/ko/ko/developer_guide/banners/placements/) created in your app or website. ![An example Banner rendered on a device.](https://www.braze.com/docs/ko/ko/assets/img/banners/sample_banner.png?c7f37292fa4f239707f73a88139a4685) ## Why use Banners? Banners allow marketing and product teams to personalize app or website content dynamically, reflecting real-time user eligibility and behavior. They persistently display messages inline, providing non-intrusive, contextually relevant experiences that can be refreshed at the start of a session or mid-session when your app or website explicitly requests it. After Banners are integrated into an app or website, marketers can design and launch Banners using a simple drag-and-drop editor, eliminating the need for ongoing developer assistance, reducing complexity, and improving efficiency. | Use case | Explanation | | --- | --- | | Announcements | Keep announcements like upcoming events or policy changes at the forefront of your app experience. | | Personalizing offers | Show personalized promotions and incentives based on each user’s browsing history, cart content, subscription tier, and loyalty status. | | Targeting new user engagement | Guide new users through onboarding flows and account setup. | | Sales and promotions | Highlight featured content, trending products, and ongoing brand campaigns persistently and directly on your homepage without disrupting the user experience. | {: .reset-td-br-1 .reset-td-br-2 aria-label="Why use Banners?" } ## Features Features for Banners include: - **Easy content building:** Create and preview your Banner using a visual, drag-and-drop editor with support for images, text, buttons, email capture forms, custom code, and more. - **Flexible placements:** Define multiple locations within your application or website where Banners can appear, enabling precise targeting to specific contexts or user experiences. - **Dynamic personalization:** Banners can only be refreshed at the start of a new session or mid-session if you explicitly request the refresh. Banners don't update automatically on a new session. If you don't request the refresh, the Banner won't update. - **Native prioritization:** Set the display priority for when multiple Banners target the same placement, ensuring the right message reaches users at the right time. - **Custom Code editor block:** Use the Custom Code editor block to add custom HTML for advanced customization or seamless integration with your existing web styles. ## About Banners {#about-banners} ### Placement IDs {#placement-id} Banner placements are specific locations in your app or website [you create with the Braze SDK](https://www.braze.com/docs/ko/ko/developer_guide/banners/placements/) that designate where Banners can appear. Common locations include the top of your homepage, product detail pages, and checkout flows. After placements are created, Banners can be [assigned in your Banner campaign](https://www.braze.com/docs/ko/ko/user_guide/channels/banners/create_a_banner/). There is no fixed limit on the number of placements you can create per workspace, and you can create as many placement IDs as your experience requires. Each placement must be unique within a workspace. A single placement ID can be referenced by up to 25 active messages at the same time. **Important:** Avoid modifying placement IDs after launching a Banner campaign. ### Banner priority {#priority} When multiple Banner messages reference the same placement ID, Banners are displayed in order of priority: high, medium, or low. By default, Banners are set to medium, but you can [manually set the priority](https://www.braze.com/docs/ko/ko/user_guide/channels/banners/create_a_banner/#set-banner-priority-optional) when you create or edit your Banner campaign. If multiple Banners are set to the same priority, the newest Banner that the user is eligible for is displayed first. ### Placement requests {#requests} When you [create placements in your app or website](https://www.braze.com/docs/ko/ko/developer_guide/banners/placements/#requestBannersRefresh), your app sends a request to Braze to fetch Banner messages for each placement. - You can request up to **10 placements per refresh request**. - For each placement, Braze returns the **highest-priority Banner** the user is eligible to receive. - If more than 10 placements are requested in a refresh, only the first 10 are returned; the rest are dropped. For example, an app might request three placements in a refresh request: `homepage_promo`, `cart_abandonment`, and `seasonal_offer`. Each request returns the most relevant Banner for that placement. #### Rate limiting for refresh requests If you're on older SDK versions (before Swift 13.1.0, Android 38.0.0, Web 6.1.0, React Native 17.0.0, and Flutter 15.0.0), only one refresh request is permitted per user session. If you're on newer minimum SDK versions (Swift 13.1.0+, Android 38.0.0+, Web 6.1.0+, React Native 17.0.0+, and Flutter 15.0.0+), refresh requests are controlled by a token bucket algorithm to prevent excessive polling: - Each user session begins with five refresh tokens. - Tokens refill at a rate of one token every 180 seconds (3 minutes). Each explicit call to `requestBannersRefresh` consumes one token. The automatic refresh that occurs at the start of a new session or when `changeUser` is called does not consume a token, as this refresh is a publishing of the last cached Banner for that user. If you attempt a refresh when no tokens are available, the SDK doesn't make the request and logs an error until a token refills. This is important for mid-session and event-triggered updates. To implement dynamic updates (for example, after a user completes an action on the same page), call the refresh method after the custom event is logged, but note the necessary delay for Braze to ingest and process the event before the user qualifies for a different Banner campaign. ### Message delivery Banner messages are delivered to your app or website as HTML content, typically rendered inside an iframe. This ensures that your Banners render consistently across devices, and helps you keep their styles and scripts separate from the rest of your code. Iframes allow for dynamic and personalized content updates that don't require changes to your codebase. Each iframe retrieves and displays the HTML for each user session using campaign targeting and personalization logic. ### Dimensions and sizing Here's what you need to know about Banner dimensions and sizing: - While the composer allows you to preview Banners in different dimensions, that information isn't saved or sent to the SDK. - The HTML takes up the full width of the container it's rendered in. - We recommend making a fixed dimension element and testing those dimensions in composer. ## Limitations Each workspace can support up to 200 active Banner campaigns. If this limit is reached, you'll need to [archive or deactivate](https://www.braze.com/docs/ko/ko/user_guide/messaging/governance/statuses/#changing-the-status) an existing campaign before creating a new one. Additionally, Banner messages do not support the following features: - API-triggered and action-based campaigns - Connected Content - Promotional codes - `catalog_items` using the [`:rerender` tag](https://www.braze.com/docs/ko/ko/user_guide/data/activation/catalogs/using_catalogs/#using-liquid) ## Next steps - [Create Banner placements in your app or website](https://www.braze.com/docs/ko/ko/developer_guide/banners/placements/) - [Create a Banner campaign in Braze](https://www.braze.com/docs/ko/ko/user_guide/channels/banners/create_a_banner/) - [Tutorial: Displaying a Banner by Placement ID](https://www.braze.com/docs/ko/ko/developer_guide/banners/tutorial_displaying_banners) **Tip:** Want to help prioritize what's next? Contact [banners-feedback@braze.com](mailto:banners-feedback@braze.com). # Braze SDK의 배너 배치 관리 Source: /docs/ko/developer_guide/banners/placements/index.md # 배너 배치 관리 {#manage-banner-placements} > Braze SDK에서 배너 배치를 생성하고 관리하는 방법을 알아보세요. 고유 속성에 접근하고 노출을 기록하는 방법도 포함됩니다. 보다 일반적인 정보는 [배너 정보](https://www.braze.com/docs/ko/ko/developer_guide/banners/)를 참조하세요. ## 배치 요청에 대하여 {#requests} When you [create placements in your app or website](https://www.braze.com/docs/ko/ko/developer_guide/banners/placements/#requestBannersRefresh), your app sends a request to Braze to fetch Banner messages for each placement. - You can request up to **10 placements per refresh request**. - For each placement, Braze returns the **highest-priority Banner** the user is eligible to receive. - If more than 10 placements are requested in a refresh, only the first 10 are returned; the rest are dropped. For example, an app might request three placements in a refresh request: `homepage_promo`, `cart_abandonment`, and `seasonal_offer`. Each request returns the most relevant Banner for that placement. #### Rate limiting for refresh requests If you're on older SDK versions (before Swift 13.1.0, Android 38.0.0, Web 6.1.0, React Native 17.0.0, and Flutter 15.0.0), only one refresh request is permitted per user session. If you're on newer minimum SDK versions (Swift 13.1.0+, Android 38.0.0+, Web 6.1.0+, React Native 17.0.0+, and Flutter 15.0.0+), refresh requests are controlled by a token bucket algorithm to prevent excessive polling: - Each user session begins with five refresh tokens. - Tokens refill at a rate of one token every 180 seconds (3 minutes). Each explicit call to `requestBannersRefresh` consumes one token. The automatic refresh that occurs at the start of a new session or when `changeUser` is called does not consume a token, as this refresh is a publishing of the last cached Banner for that user. If you attempt a refresh when no tokens are available, the SDK doesn't make the request and logs an error until a token refills. This is important for mid-session and event-triggered updates. To implement dynamic updates (for example, after a user completes an action on the same page), call the refresh method after the custom event is logged, but note the necessary delay for Braze to ingest and process the event before the user qualifies for a different Banner campaign. ## 배치 생성 {#create-a-placement} ### 필수 조건 {#prerequisites} 배너 배치를 생성하는 데 필요한 최소 SDK 버전은 다음과 같습니다: ### Step 1: Create placements in Braze If you haven't already, you'll need to create Banner placements in Braze that are used to define the locations in your app or site can display Banners. To create a placement, go to **Settings** > **Banners Placements**, then select **Create Placement**. ![Banner Placements section to create placement IDs.](https://www.braze.com/docs/ko/ko/assets/img/banners/create_placement.png?98a42014b57988954fcac2c2d94f82da) Give your placement a name and assign a **Placement ID**. Be sure you consult other teams before assigning an ID, as it'll be used throughout the card's lifecycle and shouldn't be changed later. For more information, see [Placement IDs]. ![Placement details that designate a Banner will display in the left sidebar for spring sale promotion campaigns.](https://www.braze.com/docs/ko/ko/assets/img/banners/placement_details_example.png?e94e5b7365737e3a8d7ae38e01121c6c) ### 2단계: 앱에서 배치 새로고침 {#requestBannersRefresh} 아래에 설명된 새로고침 메서드를 호출하여 배치를 새로고침할 수 있습니다. `subscribeToBannersUpdates`가 활성 상태인 경우, SDK는 새 세션이 시작될 때와 `changeUser`를 호출할 때 캐시된 배치 ID를 자동으로 다시 게시합니다. 이 자동 새로고침은 사용량 제한 토큰을 소비하지 않습니다. **Tip:** 배너 다운로드 또는 표시 지연을 방지하려면 가능한 한 빨리 배치를 새로고침하세요. ```javascript import * as braze from "@braze/web-sdk"; braze.requestBannersRefresh(["global_banner", "navigation_square_banner"]); ``` ```swift AppDelegate.braze?.banners.requestRefresh(placementIds: ["global_banner", "navigation_square_banner"]) ``` ```java ArrayList listOfBanners = new ArrayList<>(); listOfBanners.add("global_banner"); listOfBanners.add("navigation_square_banner"); Braze.getInstance(context).requestBannersRefresh(listOfBanners); ``` ```kotlin Braze.getInstance(context).requestBannersRefresh(listOf("global_banner", "navigation_square_banner")) ``` ```javascript Braze.requestBannersRefresh(["global_banner", "navigation_square_banner"]); ``` ```csharp This feature is not currently supported on Unity. ``` ```javascript This feature is not currently supported on Cordova. ``` ```dart braze.requestBannersRefresh(["global_banner", "navigation_square_banner"]); ``` ```brightscript This feature is not currently supported on Roku. ``` ### 3단계: 업데이트 수신 대기 {#subscribeToBannersUpdates} **Tip:** 이 가이드의 SDK 메서드를 사용하여 배너를 삽입하면 모든 분석 이벤트(노출 및 클릭 등)가 자동으로 처리되며, 배너가 보일 때만 노출이 기록됩니다. Web Braze SDK와 함께 일반 JavaScript를 사용하는 경우 [`subscribeToBannersUpdates`](https://js.appboycdn.com/web-sdk/latest/doc/modules/braze.html#subscribetobannersupdates)를 사용하여 배치 업데이트를 수신한 다음 [`requestBannersRefresh`](https://js.appboycdn.com/web-sdk/latest/doc/modules/braze.html#requestbannersrefresh)를 호출하여 가져옵니다. ```javascript import * as braze from "@braze/web-sdk"; braze.subscribeToBannersUpdates((banners) => { console.log("Banners were updated"); }); // always refresh after your subscriber function has been registered braze.requestBannersRefresh(["global_banner", "navigation_square_banner"]); ``` Web Braze SDK와 함께 React를 사용하는 경우 `useEffect` 훅 내에서 [`subscribeToBannersUpdates`](https://js.appboycdn.com/web-sdk/latest/doc/modules/braze.html#subscribetobannersupdates)를 설정하고 리스너를 등록한 후 [`requestBannersRefresh`](https://js.appboycdn.com/web-sdk/latest/doc/modules/braze.html#requestbannersrefresh)를 호출합니다. ```typescript import * as braze from "@braze/web-sdk"; useEffect(() => { const subscriptionId = braze.subscribeToBannersUpdates((banners) => { console.log("Banners were updated"); }); // always refresh after your subscriber function has been registered braze.requestBannersRefresh(["global_banner", "navigation_square_banner"]); // cleanup listeners return () => { braze.removeSubscription(subscriptionId); } }, []); ``` **Note:** 배너 업데이트 리스너는 SDK의 인메모리 배너 상태를 반영합니다. 단일 업데이트에는 가장 최근 `requestRefresh` 호출의 배치 ID뿐만 아니라 이미 캐시된 배치(예: 이전 새로고침, 다른 화면 또는 자동 SDK 작업에서)도 포함될 수 있습니다. 특정 배치에만 관심이 있는 경우 리스너에서 각 배너의 배치 ID를 확인하고 나머지는 건너뛰세요. 리스너를 등록한 후 Braze에서 동기화하려는 배치에 대해 `requestRefresh`를 호출하세요. ```swift let placementIds = ["global_banner", "navigation_square_banner"] let cancellable = brazeClient.braze()?.banners.subscribeToUpdates { banners in banners.forEach { placementId, banner in print("Received banner: \(banner) with placement ID: \(placementId)") } } // Always refresh after your subscriber is registered brazeClient.braze()?.banners.requestRefresh(placementIds: placementIds) ``` **Note:** 배너 업데이트 리스너는 SDK의 인메모리 배너 상태를 반영합니다. 단일 업데이트에는 가장 최근 `requestBannersRefresh` 호출의 배치 ID뿐만 아니라 이미 캐시된 배치(예: 이전 새로고침, 다른 화면 또는 자동 SDK 작업에서)도 포함될 수 있습니다. 특정 배치에만 관심이 있는 경우 리스너에서 각 배너의 배치 ID를 확인하고 나머지는 건너뛰세요. 리스너를 등록한 후 Braze에서 동기화하려는 배치에 대해 `requestBannersRefresh`를 호출하세요. ```java ArrayList placementIds = new ArrayList<>(); placementIds.add("global_banner"); placementIds.add("navigation_square_banner"); Braze.getInstance(context).subscribeToBannersUpdates(banners -> { for (Banner banner : banners.getBanners()) { Log.d(TAG, "Received banner: " + banner.getPlacementId()); } }); // Always refresh after your subscriber is registered Braze.getInstance(context).requestBannersRefresh(placementIds); ``` ```kotlin val placementIds = listOf("global_banner", "navigation_square_banner") Braze.getInstance(context).subscribeToBannersUpdates { update -> for (banner in update.banners) { Log.d(TAG, "Received banner: " + banner.placementId) } } // Always refresh after your subscriber is registered Braze.getInstance(context).requestBannersRefresh(placementIds) ``` ```javascript const bannerCardsSubscription = Braze.addListener( Braze.Events.BANNER_CARDS_UPDATED, (data) => { const banners = data.banners; console.log( `Received ${banners.length} Banner Cards with placement IDs:`, banners.map((banner) => banner.placementId) ); } ); ``` ```csharp This feature is not currently supported on Unity. ``` ```javascript This feature is not currently supported on Cordova. ``` ```dart StreamSubscription bannerStreamSubscription = braze.subscribeToBanners((List banners) { for (final banner in banners) { print("Received banner: " + banner.toString()); } }); ``` ```brightscript This feature is not currently supported on Roku. ``` ### 4단계: 배치 ID를 사용하여 삽입 {#insertBanner} **Tip:** 전체 단계별 튜토리얼은 [배치 ID로 배너 표시하기](https://www.braze.com/docs/ko/ko/developer_guide/banners/tutorial_displaying_banners/)를 확인하세요. 배너의 컨테이너 요소를 만듭니다. 너비와 높이를 설정해야 합니다. ```html
``` Web Braze SDK와 함께 일반 JavaScript를 사용하는 경우 [`insertBanner`](https://js.appboycdn.com/web-sdk/latest/doc/modules/braze.html#insertbanner) 메서드를 호출하여 컨테이너 요소의 내부 HTML을 교체합니다. ```javascript import * as braze from "@braze/web-sdk"; braze.initialize("sdk-api-key", { baseUrl: "sdk-base-url", allowUserSuppliedJavascript: true, // banners require you to opt-in to user-supplied javascript }); braze.subscribeToBannersUpdates((banners) => { // get this placement's banner. If it's `null` the user did not qualify for one. const globalBanner = braze.getBanner("global_banner"); if (!globalBanner) { return; } // choose where in the DOM you want to insert the banner HTML const container = document.getElementById("global-banner-container"); // Insert the banner which replaces the innerHTML of that container braze.insertBanner(globalBanner, container); // Special handling if the user is part of a Control Variant if (globalBanner.isControl) { // hide or collapse the container container.style.display = "none"; } }); braze.requestBannersRefresh(["global_banner", "navigation_square_banner"]); ``` Web Braze SDK와 함께 React를 사용하는 경우 `ref`와 함께 [`insertBanner`](https://js.appboycdn.com/web-sdk/latest/doc/modules/braze.html#insertbanner) 메서드를 호출하여 컨테이너 요소의 내부 HTML을 교체합니다. ```tsx import { useRef } from 'react'; import * as braze from "@braze/web-sdk"; export default function App() { const bannerRef = useRef(null); useEffect(() => { const globalBanner = braze.getBanner("global_banner"); if (!globalBanner || globalBanner.isControl) { // hide the container } else { // insert the banner to the container node braze.insertBanner(globalBanner, bannerRef.current); } }, []); return
} ``` **Tip:** 노출을 추적하려면 `isControl`에 대해서도 `insertBanner`를 호출해야 합니다. 그런 다음 컨테이너를 숨기거나 접을 수 있습니다. ```swift // To get access to the Banner model object: let globalBanner: Braze.Banner? AppDelegate.braze?.banners.getBanner(for: "global_banner", { banner in self.globalBanner = banner }) // UIKit implementation: // If you simply want the Banner view, initialize a `UIView` with the placement ID: if let braze = AppDelegate.braze { let bannerUIView = BrazeBannerUI.BannerUIView( placementId: "global_banner", braze: braze, // iOS does not perform automatic resizing or visibility changes. // Use the `processContentUpdates` parameter to adjust the size and visibility of your Banner according to your use case. processContentUpdates: { result in switch result { case .success(let updates): if let height = updates.height { // Adjust the visibility and/or height. } case .failure(let error): // Handle the error. } } ) } // SwiftUI implementation: // Similarly, if you want a Banner view in SwiftUI, use the corresponding `BannerView` initializer: if let braze = AppDelegate.braze { let bannerView = BrazeBannerUI.BannerView( placementId: "global_banner", braze: braze, // iOS does not perform automatic resizing or visibility changes. // Use the `processContentUpdates` parameter to adjust the size and visibility of your Banner according to your use case. processContentUpdates: { result in switch result { case .success(let updates): if let height = updates.height { // Adjust the visibility and/or height according to your parent controller. } case .failure(let error): // Handle the error. } } ) } ``` Java 코드에서 배너를 가져오려면 다음을 사용합니다: ```java Banner globalBanner = Braze.getInstance(context).getBanner("global_banner"); ``` 다음 XML을 포함하여 Android 뷰 레이아웃에 배너를 만들 수 있습니다: ```xml ``` Android 뷰를 사용하는 경우 다음 XML을 사용하세요: ```xml ``` Jetpack Compose를 사용하려면 앱 모듈에 `com.braze:android-sdk-jetpack-compose` 아티팩트를 추가하세요. 다른 Braze Android SDK 종속성과 동일한 버전을 사용합니다. 이 모듈은 `android-sdk-ui`와 별도이며 `com.braze.jetpackcompose.banners` 아래에 [`Banner`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.jetpackcompose.banners/-banner.html) 컴포저블을 제공합니다. **Note:** 일부 Compose UI 라이브러리는 자체 `Banner` 컴포저블을 정의합니다. Braze의 API를 호출하려면 `com.braze.jetpackcompose.banners.Banner`를 명시적으로 임포트하세요. ```kotlin import com.braze.jetpackcompose.banners.Banner @Composable fun myBannerSlot() { Banner(placementId = "global_banner") } ``` 선택적으로 `heightCallback`을 전달하여 배너 크기가 변경될 때 렌더링된 높이를 dp 단위로 받을 수 있습니다. 참조는 [`Banner`에 대한 KDoc](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.jetpackcompose.banners/-banner.html)을 확인하세요. Jetpack Compose 모듈을 추가하지 않는 경우 [`BannerView`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.ui.banners/-banner-view/index.html)를 [`AndroidView`](https://developer.android.com/reference/kotlin/androidx/compose/ui/viewinterop/AndroidView)로 래핑하세요: ```kotlin import android.view.ViewGroup import androidx.compose.runtime.Composable import androidx.compose.ui.viewinterop.AndroidView import com.braze.ui.banners.BannerView @Composable fun myBannerSlot() { AndroidView( factory = { context -> BannerView(context, "global_banner").apply { layoutParams = ViewGroup.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT ) } }, update = { it.placementId = "global_banner" } ) } ``` Kotlin에서 배너를 가져오려면 다음을 사용하세요: ```kotlin val banner = Braze.getInstance(context).getBanner("global_banner") ``` [React Native의 새 아키텍처](https://reactnative.dev/architecture/landing-page)를 사용하는 경우 `AppDelegate.mm`에 `BrazeBannerView`를 Fabric 구성요소로 등록해야 합니다. ```swift #ifdef RCT_NEW_ARCH_ENABLED /// Register the `BrazeBannerView` for use as a Fabric component. - (NSDictionary> *)thirdPartyFabricComponents { NSMutableDictionary * dictionary = [super thirdPartyFabricComponents].mutableCopy; dictionary[@"BrazeBannerView"] = [BrazeBannerView class]; return dictionary; } #endif ``` 가장 간단한 통합을 위해 배치 ID만 제공하는 다음 JavaScript XML(JSX) 스니펫을 뷰 계층 구조에 추가합니다. ```javascript ``` React Native에서 배너의 데이터 모델을 가져오거나 사용자 캐시에 해당 배치가 있는지 확인하려면 다음을 사용하세요: ```javascript const banner = await Braze.getBanner("global_banner"); ``` ```csharp This feature is not currently supported on Unity. ``` ```javascript This feature is not currently supported on Cordova. ``` 가장 간단한 통합을 위해 다음 위젯을 뷰 계층 구조에 추가하고 배치 ID만 제공하면 됩니다. ```dart BrazeBannerView( placementId: "global_banner", ), To get the Banner's data model in Flutter, use: ``` `getBanner` 메서드를 사용하여 사용자 캐시에 해당 배치가 있는지 확인할 수 있습니다. ```dart braze.getBanner("global_banner").then((banner) { if (banner == null) { // Handle null cases. } else { print(banner.toString()); } }); ``` ```brightscript This feature is not currently supported on Roku. ``` ### 5단계: 테스트 배너 보내기(선택 사항) {#handling-test-cards} 배너 Campaign을 시작하기 전에 [테스트 배너를 전송](https://www.braze.com/docs/ko/ko/user_guide/messaging/messaging_fundamentals/sending_test_messages/?tab=banners)하여 통합을 확인할 수 있습니다. 테스트 배너는 별도의 인메모리 캐시에 저장되며 앱 재시작 시 유지되지 않습니다. 추가 설정은 필요하지 않지만, 테스트를 표시할 수 있도록 테스트 기기가 포그라운드 푸시 알림을 수신할 수 있어야 합니다. **Note:** 테스트 배너는 다음 앱 세션에서 제거된다는 점을 제외하면 다른 배너와 동일합니다. ## 노출 기록 {#log-impressions} Braze는 SDK 메서드를 사용하여 배너를 삽입할 때 보이는 배너에 대해 자동으로 노출을 기록하므로—수동으로 노출을 추적할 필요가 없습니다. ## 클릭 기록 {#logging-clicks} 배너 클릭을 기록하는 데 사용되는 메서드는 배너가 렌더링되는 방식과 클릭 핸들러의 위치에 따라 다릅니다. ### 표준 배너 콘텐츠(자동) {#standard-banner-content-automatic} 기본 제공 SDK 메서드를 사용하여 배너를 삽입하고 배너가 표준 편집기 구성요소(이미지, 버튼, 텍스트)를 사용하는 경우 클릭이 자동으로 추적됩니다. SDK는 이러한 요소에 클릭 리스너를 연결하며 추가 코드가 필요하지 않습니다. ### 커스텀 코드 블록 {#custom-code-blocks} 배너가 Braze 대시보드의 **커스텀 코드** 편집기 블록을 사용하는 경우, 해당 커스텀 HTML 내에서 클릭을 기록하기 위해 `brazeBridge.logClick()`을 사용해야 합니다. SDK 메서드를 사용하여 배너를 렌더링하는 경우에도 마찬가지입니다. SDK는 커스텀 코드 내의 요소에 리스너를 자동으로 연결할 수 없기 때문입니다. ```html ``` 전체 참조는 [배너를 위한 커스텀 코드 및 JavaScript 브리지](https://www.braze.com/docs/ko/ko/user_guide/channels/banners/create_a_banner/#custom-code)를 참조하세요. `brazeBridge`는 배너의 내부 HTML과 상위 Braze SDK 간의 통신 계층을 제공합니다. ### 커스텀 UI 구현(헤드리스) {#custom-ui-implementations-headless} 배너의 [커스텀 속성](#custom-properties)을 사용하여 배너 HTML을 렌더링하는 대신 완전히 커스텀 UI를 구축하는 경우, 애플리케이션 코드에서 클릭과 노출을 수동으로 기록해야 합니다. SDK가 배너를 렌더링하지 않기 때문에 커스텀 UI 요소와의 상호작용을 자동으로 추적할 방법이 없습니다. 메서드 시그니처 및 전체 세부 정보는 [Braze SDK 참조 문서](https://www.braze.com/docs/ko/ko/developer_guide/references/)를 참조하세요. #### 노출 기록 {#logging-impressions} 커스텀 UI에서 배너가 "조회됨"으로 간주될 때 플랫폼의 배너 노출 메서드를 호출하세요. 중복 이벤트를 방지하기 위해 노출로 간주되는 기준에 대한 견고한 로직을 구축하세요. 예를 들어, 배너가 뷰포트에 진입할 때(또는 이에 상응하는 시점에)만 기록하고, 동일한 배너가 다시 스크롤되어 보이거나 새로운 뷰 이벤트 없이 구성요소가 다시 렌더링될 때는 다시 기록하지 마세요. ```javascript import * as braze from "@braze/web-sdk"; // Log impression when your custom UI considers the banner viewed (for example, once when it enters viewport) const banner = braze.getBanner("placement_id_homepage_top"); if (banner) { braze.logBannerImpressions([banner]); } ``` [Web SDK 참조](https://js.appboycdn.com/web-sdk/latest/doc/modules/braze.html#logbannerimpressions) ```kotlin // Log impression when your custom UI considers the banner viewed (for example, once when it enters viewport) Braze.getInstance(context).logBannerImpression("placement_id_homepage_top") ``` ```java // Log impression when your custom UI considers the banner viewed (for example, once when it enters viewport) Braze.getInstance(context).logBannerImpression("placement_id_homepage_top"); ``` [Android SDK 참조](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze/-i-braze/log-banner-impression.html) ```swift // Retrieve a banner and log an impression on it (for example, once when it enters viewport) braze.banners.getBanner(for: "placement_id_homepage_top") { banner in banner?.context.logImpression() } ``` [Swift SDK 참조](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/banner/context-swift.class/logimpression()) ```javascript // Log impression when your custom UI considers the banner viewed (for example, once when it enters viewport) Braze.logBannerImpression("placement_id_homepage_top"); ``` 최신 메서드 시그니처는 [React Native SDK 리포지토리](https://github.com/braze-inc/braze-react-native-sdk)를 참조하세요. ```dart // Log impression when your custom UI considers the banner viewed (for example, once when it enters viewport) braze.logBannerImpression("placement_id_homepage_top"); ``` [Flutter SDK 참조](https://pub.dev/documentation/braze_plugin/latest/braze_plugin/BrazePlugin/logBannerImpression.html) #### 클릭 기록 사용자가 커스텀 배너(또는 특정 버튼)를 탭할 때 플랫폼의 배너 클릭 메서드를 호출하세요. 클릭이 특정 버튼에 대한 것인 경우 선택적 `buttonId`를 전달하여 분석에서 클릭을 올바르게 귀속시킬 수 있습니다. ```javascript import * as braze from "@braze/web-sdk"; // Log click braze.logBannerClick("placement_id_homepage_top", buttonId); // buttonID is optional ``` [Web SDK 참조](https://js.appboycdn.com/web-sdk/latest/doc/modules/braze.html#logbannerclick) ```kotlin // Log click Braze.getInstance(context).logBannerClick("placement_id_homepage_top", buttonId) // buttonID parameter can be null ``` ```java // Log click Braze.getInstance(context).logBannerClick("placement_id_homepage_top", buttonId); // buttonID parameter can be null ``` [Android SDK 참조](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze/-i-braze/log-banner-click.html) ```swift // Retrieve a banner and log a click on it braze.banners.getBanner(for: "placement_id_homepage_top") { banner in banner?.context.logClick(buttonId: buttonId) // buttonID is optional } ``` [Swift SDK 참조](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/banner/context-swift.class/logclick(buttonid:)) ```javascript // Log click Braze.logBannerClick("placement_id_homepage_top", buttonId); // buttonID is optional ``` 최신 메서드 시그니처는 [React Native SDK 리포지토리](https://github.com/braze-inc/braze-react-native-sdk)를 참조하세요. ```dart // Log click braze.logBannerClicked("placement_id_homepage_top", buttonId); // buttonID parameter can be null ``` [Flutter SDK 참조](https://pub.dev/documentation/braze_plugin/latest/braze_plugin/BrazePlugin/logBannerClicked.html) ## 해제 기록 {#log-dismissals} 배너 해제는 사용자가 적극적으로 배너를 해제할 때 배치에서 배너를 프로그래밍 방식으로 제거합니다. 해제되면 해당 사용자에 대해 배너가 억제됩니다. 다음에 배치 목록이 새로고침될 때 사용자가 자격이 있는 경우 새 배너가 반환됩니다. ### 필수 조건 배너 해제를 기록하는 데 필요한 최소 SDK 버전은 다음과 같습니다: ### 통합 {#integrations} #### 표준 배너 통합(드래그 앤 드롭 편집기) {#standard-banner-integrations-drag-and-drop-editor} 배너가 드래그 앤 드롭 편집기를 사용하고 해제 버튼 구성요소를 포함하는 경우 추가 코드가 필요하지 않습니다. 사용자가 해제 버튼을 클릭하면 메시지가 숨겨지고, 해제가 트리거된 후 분석을 위한 해제 이벤트가 기록됩니다. #### 커스텀 코드 블록 배너가 **커스텀 코드** 편집기 블록을 사용하는 경우 배너의 HTML 내에서 `brazeBridge.closeMessage()`를 사용하여 직접 해제를 트리거할 수 있습니다. ```html ``` ### 배너 해제 시 커스텀 분석 기록 {#log-custom-analytics-on-banner-dismissal} 배너 해제 시 커스텀 분석 기록과 같은 추가 로직을 실행하려면 배너 뷰에서 선택적 `onDismiss` 콜백을 재정의하세요. 기본적으로 이 콜백은 비어 있습니다. Web SDK에는 `insertBanner`에 전용 `onDismiss` 콜백이 없습니다. 대신 `subscribeToBannersUpdates`를 사용하여 업데이트된 배너 맵에 배너가 더 이상 존재하지 않는지 확인하여 배너가 해제되었는지 감지하세요. ```javascript import * as braze from "@braze/web-sdk"; braze.subscribeToBannersUpdates((banners) => { const globalBanner = banners["global_banner"]; if (!globalBanner) { // The banner was dismissed or the user is no longer eligible. // Run any custom analytics here. console.log("Banner was dismissed"); return; } }); braze.requestBannersRefresh(["global_banner"]); ``` ```typescript import { useEffect } from "react"; import * as braze from "@braze/web-sdk"; useEffect(() => { const subscriptionId = braze.subscribeToBannersUpdates((banners) => { const globalBanner = banners["global_banner"]; if (!globalBanner) { // The banner was dismissed or the user is no longer eligible. // Run any custom analytics here. console.log("Banner was dismissed"); return; } }); braze.requestBannersRefresh(["global_banner"]); return () => { braze.removeSubscription(subscriptionId); }; }, []); ``` [`BannerView`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.ui.banners/-banner-view/index.html)에서 선택적 [`onDismissCallback`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.ui.banners/-banner-view/on-dismiss-callback.html) 속성을 설정하세요. ```java import android.util.Log; import com.braze.ui.banners.BannerView; import kotlin.Unit; // After obtaining your BannerView instance (for example from XML via findViewById, or `new BannerView(context, "global_banner")`) bannerView.setOnDismissCallback(() -> { Log.d(TAG, "Successfully dismissed banner with placementId: " + bannerView.getPlacementId()); // Run any custom logic here, such as logging custom analytics return Unit.INSTANCE; }); ``` ```kotlin import android.util.Log import com.braze.ui.banners.BannerView // After obtaining your BannerView instance (for example via findViewById or `BannerView(context, "global_banner")`) bannerView.onDismissCallback = { Log.d(TAG, "Successfully dismissed banner with placementId: ${bannerView.placementId}") // Run any custom logic here, such as logging custom analytics } ``` ```swift // After initializing your banner view instance using UIKit or SwiftUI bannerView.onDismiss = { dismissedBanner in print("Successfully dismissed banner with placementId: \(dismissedBanner.placementId)") // Run any custom logic here, such as logging custom analytics } ``` ### 보류 중인 해제 저장 한도 {#pending-dismissal-storage-cap} 해제 이벤트는 다음 `requestBannersRefresh` 호출 시 Braze 서버에 동기화될 때까지 보류 항목으로 로컬에 저장됩니다. **Warning:** 드문 경우지만 성공적인 동기화 없이 많은 수의 해제가 누적되면 오래된 보류 해제가 삭제될 수 있습니다. 이 경우 이전에 해제된 배너가 다음 성공적인 동기화가 완료될 때까지 다시 나타날 수 있습니다. 이 위험을 최소화하려면 앱이 네트워크 연결을 다시 확보할 때마다 `requestBannersRefresh`를 호출하세요. ## 크기 및 크기 조정 {#dimensions-and-sizing} 배너 크기 및 크기 조정에 대해 알아야 할 사항은 다음과 같습니다: - 컴포저에서 다양한 크기로 배너를 미리 볼 수 있지만, 해당 정보는 SDK에 저장되거나 전송되지 않습니다. - HTML은 렌더링되는 컨테이너의 전체 너비를 차지합니다. - 고정 크기 요소를 만들고 컴포저에서 해당 크기를 테스트하는 것을 권장합니다. ## 커스텀 속성 {#custom-properties} 배너 Campaign의 커스텀 속성을 사용하여 SDK를 통해 키-값 데이터를 검색하고 앱의 동작이나 외관을 수정할 수 있습니다. 예를 들어 다음과 같은 작업을 수행할 수 있습니다: - 서드파티 분석 또는 통합을 위한 메타데이터를 전송합니다. - `timestamp` 또는 JSON 오브젝트와 같은 메타데이터를 사용하여 조건 로직을 트리거합니다. - `ratio` 또는 `format`과 같은 포함된 메타데이터를 기반으로 배너의 동작을 제어합니다. ### 필수 조건 배너 Campaign에 [커스텀 속성을 추가](https://www.braze.com/docs/ko/ko/user_guide/channels/banners/create_a_banner/#custom-properties)해야 합니다. 또한 커스텀 속성에 접근하기 위해 필요한 최소 SDK 버전은 다음과 같습니다: ### 커스텀 속성에 접근하기 {#access-custom-properties} 배너의 커스텀 속성에 접근하려면 대시보드에서 정의된 속성 유형에 따라 다음 메서드 중 하나를 사용하세요. 키가 해당 유형의 속성과 일치하지 않거나 존재하지 않으면 메서드는 `null`을 반환합니다. ```javascript // Returns the Banner instance const banner = braze.getBanner("placement_id_homepage_top"); // banner may be undefined or null if (banner) { // Returns the string property const stringProperty = banner.getStringProperty("color"); // Returns the boolean property const booleanProperty = banner.getBooleanProperty("expanded"); // Returns the number property const numberProperty = banner.getNumberProperty("height"); // Returns the timestamp property (as a number) const timestampProperty = banner.getTimestampProperty("account_start"); // Returns the image URL property as a string of the URL const imageProperty = banner.getImageProperty("homepage_icon"); // Returns the JSON object property const jsonObjectProperty = banner.getJsonProperty("footer_settings"); } ``` ```swift // Passes the specified banner to the completion handler AppDelegate.braze?.banners.getBanner(for: "placement_id_homepage_top") { banner in // Returns the string property let stringProperty: String? = banner.stringProperty(key: "color") // Returns the boolean property let booleanProperty: Bool? = banner.boolProperty(key: "expanded") // Returns the number property as a double let numberProperty: Double? = banner.numberProperty(key: "height") // Returns the Unix UTC millisecond timestamp property as an integer let timestampProperty: Int? = banner.timestampProperty(key: "account_start") // Returns the image property as a String of the image URL let imageProperty: String? = banner.imageProperty(key: "homepage_icon") // Returns the JSON object property as a [String: Any] dictionary let jsonObjectProperty: [String: Any]? = banner.jsonObjectProperty(key: "footer_settings") } ``` ```java // Returns the Banner instance Banner banner = Braze.getInstance(context).getBanner("placement_id_homepage_top"); // banner may be undefined or null if (banner != null) { // Returns the string property String stringProperty = banner.getStringProperty("color"); // Returns the boolean property Boolean booleanProperty = banner.getBooleanProperty("expanded"); // Returns the number property Number numberProperty = banner.getNumberProperty("height"); // Returns the timestamp property (as a Long) Long timestampProperty = banner.getTimestampProperty("account_start"); // Returns the image URL property as a String of the URL String imageProperty = banner.getImageProperty("homepage_icon"); // Returns the JSON object property as a JSONObject JSONObject jsonObjectProperty = banner.getJSONProperty("footer_settings"); } ``` ```kotlin // Returns the Banner instance val banner: Banner = Braze.getInstance(context).getBanner("placement_id_homepage_top") ?: return // Returns the string property val stringProperty: String? = banner.getStringProperty("color") // Returns the boolean property val booleanProperty: Boolean? = banner.getBooleanProperty("expanded") // Returns the number property val numberProperty: Number? = banner.getNumberProperty("height") // Returns the timestamp property (as a Long) val timestampProperty: Long? = banner.getTimestampProperty("account_start") // Returns the image URL property as a String of the URL val imageProperty: String? = banner.getImageProperty("homepage_icon") // Returns the JSON object property as a JSONObject val jsonObjectProperty: JSONObject? = banner.getJSONProperty("footer_settings") ``` ```javascript // Get the Banner instance const banner = await Braze.getBanner('placement_id_homepage_top'); if (!banner) return; // Get the string property const stringProperty = banner.getStringProperty('color'); // Get the boolean property const booleanProperty = banner.getBooleanProperty('expanded'); // Get the number property const numberProperty = banner.getNumberProperty('height'); // Get the timestamp property (as a number) const timestampProperty = banner.getTimestampProperty('account_start'); // Get the image URL property as a string const imageProperty = banner.getImageProperty('homepage_icon'); // Get the JSON object property const jsonObjectProperty = banner.getJSONProperty('footer_settings'); ``` ```dart // Fetch the banner asynchronously _braze.getBanner(placementId).then(('placement_id_homepage_top') { // Get the string property final String? stringProperty = banner?.getStringProperty('color'); // Get the boolean property final bool? booleanProperty = banner?.getBooleanProperty('expanded'); // Get the number property final num? numberProperty = banner?.getNumberProperty('height'); // Get the timestamp property final int? timestampProperty = banner?.getTimestampProperty('account_start'); // Get the image URL property final String? imageProperty = banner?.getImageProperty('homepage_icon'); // Get the JSON object property final Map? jsonObjectProperty = banner?.getJSONProperty('footer_settings'); // Use these properties as needed in your UI or logic }); ``` # 테스트 배너 Source: /docs/ko/developer_guide/banners/testing/index.md # 테스트 배너 {#test-banners} > Campaign을 시작하기 전에 배너 메시지를 테스트하여 모든 미디어, 문구, 개인화 및 커스텀 속성이 올바르게 렌더링되는지 확인하는 방법을 알아보세요. 더 자세한 일반 정보는 [배너 소개](https://www.braze.com/docs/ko/ko/developer_guide/banners/)를 참조하세요. ## 필수 조건 {#prerequisites} Braze에서 배너 메시지를 테스트하려면 먼저 [Braze에서 배너 Campaign](https://www.braze.com/docs/ko/ko/user_guide/channels/banners/create_a_banner/)을 생성해야 합니다. 또한 테스트하려는 배치가 이미 [앱 또는 웹사이트에 배치되어 있는지](https://www.braze.com/docs/ko/ko/developer_guide/banners/placements/) 확인하세요. [콘텐츠 테스트 그룹](https://www.braze.com/docs/ko/ko/user_guide/administrative/app_settings/developer_console/internal_groups_tab/#content-test-groups) 또는 개별 사용자에게 테스트를 보내려면, 발송 전에 테스트 기기에서 푸시가 활성화되어 있고 테스트 사용자에 대해 유효한 푸시 토큰이 등록되어 있어야 합니다. ## 배너 테스트 {#test-a-banner} **Preview** to you preview your Banner or send a test message. ![Preview tab of the Banner composer.](https://www.braze.com/docs/ko/ko/assets/img/banners/select_preview.png?898229d959020b86215b0604a136dfae){: style="max-width:50%;"} Keep in mind, your preview may not be identical to the final render on a user's device due to differences across hardware. To send a test message, add either a content test group or one or more individual users as **Test Recipients**, then select **Send Test**. You'll be able to view your test message on the device for up to 5 minutes. You can then select **Copy preview link** to generate and copy a shareable preview link that shows what the banner will look like for a random user. The link will last for seven days before it needs to be regenerated. ![Preview tab of the Banner composer.](https://www.braze.com/docs/ko/ko/assets/img/banners/preview_banner.png?d8aab458e372815d934bd3cd9c3f3f43) While reviewing your test Banner, verify the following: - Is your Banner campaign assigned to a placement? - Do the images and media show up and act as expected on your targeted device types and screen sizes? - Do your links and buttons direct the user to where they should go? - Does the Liquid function as expected? Have you accounted for a default attribute value in the event that the Liquid returns no information? - Is your copy clear, concise, and correct? For more information, see [Send test messages](https://www.braze.com/docs/ko/ko/user_guide/messaging/messaging_fundamentals/sending_test_messages/). # 배너 분석 Source: /docs/ko/developer_guide/banners/analytics/index.md # 배너 분석 > 배너에 대한 분석을 검토하는 방법을 배우세요. 여기에는 캠페인 세부정보, 메시지 성과 및 과거 성과가 포함됩니다. 자세한 내용은 [배너 정보를](https://www.braze.com/docs/ko/ko/developer_guide/banners) 참조하세요. ## Viewing analytics Once you've launched your campaign, you can return to the details page for that campaign to view key metrics. Navigate to the **Campaigns** page and select your campaign to open the details page. For sent in Canvas, refer to [Canvas analytics](https://www.braze.com/docs/ko/ko/user_guide/engagement_tools/canvas/testing_canvases/measuring_and_testing_with_canvas_analytics/). **Tip:** Looking for definitions for the terms and metrics listed in your report? Refer to our From the **Campaign Analytics** tab, you can view your reports in a series of panels. You may see more or less than those listed in the sections below, but each has its own useful purpose. ### Time range By default, the time range for **Campaign Analytics** will display the last 90 days from the current time. This means that if the campaign was launched more than 90 days ago, the analytics will display as "0" for the given time range. To view all analytics for older campaigns, adjust the reporting time range. ### Campaign details The **Campaign Details** panel shows a high-level overview of the entire performance for your Review this panel to see overall metrics such as the number of messages sent to the number of recipients, the primary conversion rate, and the total revenue generated by this message. You can also review delivery, audience, and conversion settings from this page. **Note:** Analytics numbers in the dashboard and Snowflake may differ slightly. Braze measures numbers in the dashboard and records rows to Snowflake separately. Snowflake is the more precise data source, so if you see discrepancies between these sources, we recommend referring to Snowflake data. #### Estimated Audience and Current Audience Depending on how large your workspace is, the **Campaign Details** panel may label audience statistics **Estimated Audience** or **Current Audience**. The following table summarizes what each label means. | Footer label | When it is used | | --- | --- | | **Estimated Audience** | Braze does not run a full-database count by default. Audience size is estimated from a sample and extrapolated, similar to the **Reachable users** range in the segment builder. Margins of error are expected, especially for large workspaces or small segments as a share of the workspace. | | **Current Audience** | Braze can compute the default statistic with a full scan of workspace profiles, so the displayed audience size is a current, unsampled count (still subject to channel reachability, subscription rules, and other targeting options). | {: .reset-td-br-1 .reset-td-br-2 aria-label="Estimated Audience and Current Audience" } For details on sampling behavior, **Calculate exact statistics**, and segmenting **Reachable users**, see [Measure segment size](https://www.braze.com/docs/ko/ko/user_guide/audience/segments/measuring_segment_size/). #### Changes Since Last Viewed The number of updates to the campaign from other members of your team is tracked by the *Changes Since Last Viewed* metric on the campaign overview page. Select **Changes Since Last Viewed** to view a changelog of updates to the campaign's name, schedule, tags, message, audience, approval status, or team access configuration. For each update, you can see who performed the update and when. You can use this changelog to audit changes to your campaign. If you want to simplify your view, click **Add/Remove Columns** and clear any metrics as desired. By default, all metrics are displayed. ### Historical performance The **Historical Performance** panel allows you to view the metrics from the **Message Performance** panel as a graph over time. Use the filters at the top of the panel to modify the stats and channels shown in the graph. The time range of this graph will always mirror the time range specified at the top of the page. To get a day-by-day breakdown, click the hamburger menu and select **Download CSV** to receive a CSV export of the report. ![A graph of the Historical Performance panel with example statistics for an email from February 2021 to May 2022.](https://www.braze.com/docs/ko/ko/assets/img/cc-historical-performance.png?03e5e9b53261a881b6f96b5e65e70222) ### Conversion event details The **Conversion Event Details** panel shows you the performance of your conversion events for your campaign. For more information, refer to [Conversion Events](https://www.braze.com/docs/ko/ko/user_guide/engagement_tools/campaigns/building_campaigns/conversion_events/#step-3-view-results). ![The Conversion Event Details panel.](https://www.braze.com/docs/ko/ko/assets/img/cc-conversion.png?39e3903bd0948f87cac25bf481eb0ba5) ### Conversion correlation The **Conversion Correlation** panel gives you insight into what user attributes and behaviors help or hurt the outcomes you set for campaigns. For more information, refer to [Conversion correlation](https://www.braze.com/docs/ko/ko/user_guide/engagement_tools/testing/conversion_correlation/). ![The Conversion Correlation panel with an analysis on user attributes and behavior from the Primary Conversion Event - A.](https://www.braze.com/docs/ko/ko/assets/img/convcorr.png?9322bf2817e7a5fbecd4ceb3b850875f) ## Retention report Retention reports show you the rates at which your users have performed a selected retention event over time periods in a specific campaign or Canvas. For more information, refer to [Retention reports](https://www.braze.com/docs/ko/ko/user_guide/analytics/reporting/retention_reports/). ## Funnel report Funnel reporting offers a visual report that allows you to analyze the journeys your customers take after receiving a campaign or Canvas. If your campaign or Canvas uses a control group or multiple variants, you will be able to understand how the different variants have impacted the conversion funnel at a more granular level and optimize based on this data. For more information, refer to [Funnel reports](https://www.braze.com/docs/ko/ko/user_guide/analytics/reporting/funnel_reports/). # Content Cards에서 배너로 마이그레이션 Source: /docs/ko/developer_guide/banners/migrating_from_content_cards/index.md # Content Cards에서 배너로 마이그레이션 {#migrate-from-content-cards-to-banners} > 이 가이드는 배너 스타일 메시징 사용 사례를 위해 Content Cards에서 배너로 마이그레이션하는 데 도움을 줍니다. 배너는 애플리케이션의 특정 위치에 표시되는 인라인, 지속적인 인앱 및 웹 메시지에 이상적입니다. ## 왜 배너로 마이그레이션해야 할까요? {#why-migrate-to-banners} - 엔지니어링 팀이 커스텀 Content Cards를 구축하거나 유지 관리하고 있다면, 배너로 마이그레이션하면 지속적인 투자를 줄일 수 있습니다. 배너를 사용하면 마케터가 UI를 직접 제어할 수 있어 개발자가 다른 작업에 집중할 수 있습니다. - 새로운 홈페이지 메시지, 온보딩 흐름 또는 지속적인 공지를 시작하는 경우, Content Cards 위에 구축하기보다는 배너로 시작하세요. 실시간 개인화, 30일 만료 없음, 크기 제한 없음, 그리고 첫날부터 네이티브 우선순위를 활용할 수 있습니다. - 30일 만료 제한을 우회하거나 복잡한 재자격 로직을 관리하거나 오래된 개인화에 불만이 있는 경우, 배너는 이러한 문제를 네이티브로 해결합니다. 배너는 배너 스타일 메시징에서 Content Cards보다 여러 가지 장점을 제공합니다: ### 생산 가속화 {#accelerated-production} - **지속적인 엔지니어링 지원 부담 감소**: 마케터는 개발자의 도움 없이 드래그 앤 드롭 편집기와 커스텀 HTML을 사용하여 커스텀 메시지를 구축할 수 있습니다. - **유연한 커스터마이징 옵션**: 편집기에서 직접 디자인하거나, HTML을 사용하거나, 커스텀 등록정보가 있는 기존 데이터 모델을 활용하세요. ### 더 나은 UX {#better-ux} - **동적 콘텐츠 업데이트**: 배너는 매번 새로고침할 때마다 Liquid 로직과 자격을 새로고침하여 사용자가 항상 가장 관련성 높은 콘텐츠를 볼 수 있도록 합니다. - **네이티브 배치 지원**: 메시지가 피드가 아닌 특정 컨텍스트에 표시되어 더 나은 상황별 관련성을 제공합니다. - **네이티브 우선순위 지정**: 커스텀 로직 없이 표시 순서를 제어하여 메시지 계층 구조를 더 쉽게 관리할 수 있습니다. ### 지속성 {#persistence} - **만료 제한 없음**: 배너 Campaign은 Content Cards와 달리 30일 만료 제한이 없어 메시지를 진정으로 지속적으로 유지할 수 있습니다. ## 마이그레이션 시기 {#when-to-migrate} Content Cards를 다음 용도로 사용 중이라면 배너로 마이그레이션하는 것을 고려하세요: - 홈페이지 히어로, 제품 페이지 프로모션, 체크아웃 오퍼 - 지속적인 내비게이션 공지 또는 사이드바 메시지 - 30일 이상 실행되는 상시 메시지 - 실시간 개인화 및 자격을 원하는 메시지 ## Content Cards를 유지해야 할 때 {#when-to-keep-content-cards} 다음이 필요한 경우 Content Cards를 계속 사용하세요: - **피드 경험:** 여러 개의 스크롤 가능한 메시지 또는 카드 기반 "받은편지함"과 관련된 모든 사용 사례. - **특정 기능:** 연결된 콘텐츠 또는 프로모션 코드가 필요한 메시지. 배너는 이러한 기능을 기본적으로 지원하지 않습니다. - **트리거된 전달:** API 트리거 또는 실행 기반 전달이 반드시 필요한 사용 사례. 배너는 API 트리거 또는 실행 기반 전달을 지원하지 않지만, 실시간 자격 평가를 통해 사용자가 각 새로고침 시 Segment 멤버십에 따라 즉시 자격을 부여받거나 상실합니다. ## 마이그레이션 가이드 {#migration-guide} ### 필수 조건 {#prerequisites} 마이그레이션하기 전에 Braze SDK가 최소 버전 요구 사항을 충족하는지 확인하세요: ### 업데이트 구독 {#subscribe-to-updates} #### Content Cards 접근 방식 {#content-cards-approach} ```javascript import * as braze from "@braze/web-sdk"; braze.subscribeToContentCardsUpdates((cards) => { // Handle array of cards cards.forEach(card => { console.log("Card:", card.id); }); }); ``` ```kotlin Braze.getInstance(context).subscribeToContentCardsUpdates { cards -> // Handle array of cards cards.forEach { card -> Log.d(TAG, "Card: ${card.id}") } } ``` ```swift braze.contentCards.subscribeToUpdates { cards in // Handle array of cards for card in cards { print("Card: \(card.id)") } } ``` ```javascript Braze.addListener(Braze.Events.CONTENT_CARDS_UPDATED, (update) => { const cards = update.cards; // Handle array of cards cards.forEach(card => { console.log("Card:", card.id); }); }); ``` ```dart StreamSubscription contentCardsStreamSubscription = braze.subscribeToContentCards((List contentCards) { // Handle array of cards for (final card in contentCards) { print("Card: ${card.id}"); } }); ``` #### 배너 접근 방식 {#banners-approach} ```javascript import * as braze from "@braze/web-sdk"; braze.subscribeToBannersUpdates((banners) => { // Get banner for specific placement const banner = braze.getBanner("sample_placement_id"); if (banner) { console.log("Banner received for placement:", banner.placementId); } }); ``` ```kotlin Braze.getInstance(context).subscribeToBannersUpdates { update -> // Get banner for specific placement val banner = Braze.getInstance(context).getBanner("sample_placement_id") if (banner != null) { Log.d(TAG, "Banner received for placement: ${banner.placementId}") } } ``` ```swift braze.banners.subscribeToUpdates { banners in // Get banner for specific placement braze.banners.getBanner(for: "sample_placement_id") { banner in guard let banner = banner else { return } print("Banner received for placement: \(banner.placementId)") } } ``` ```javascript Braze.addListener(Braze.Events.BANNER_CARDS_UPDATED, (data) => { const banners = data.banners; // Get banner for specific placement Braze.getBanner("sample_placement_id").then(banner => { if (banner) { console.log("Banner received for placement:", banner.placementId); } }); }); ``` ```dart StreamSubscription bannerStreamSubscription = braze.subscribeToBanners((List banners) { // Get banner for specific placement braze.getBanner("sample_placement_id").then((banner) { if (banner != null) { print("Banner received for placement: ${banner.placementId}"); } }); }); ``` ### 콘텐츠 표시 {#display-content} **Note:** Content Cards는 커스텀 UI 로직으로 수동 렌더링할 수 있지만, 배너는 기본 제공 SDK 메서드로만 렌더링할 수 있습니다. #### Content Cards 접근 방식 ```javascript // Show default feed UI braze.showContentCards(document.getElementById("feed")); // Or manually render cards const cards = braze.getCachedContentCards(); cards.forEach(card => { // Custom rendering logic if (card instanceof braze.ClassicCard) { // Render classic card } }); ``` ```kotlin // Using default fragment val fragment = ContentCardsFragment() supportFragmentManager.beginTransaction() .replace(R.id.content_cards_container, fragment) .commit() // Or manually render cards val cards = Braze.getInstance(context).getCachedContentCards() cards.forEach { card -> when (card) { is ClassicCard -> { // Render classic card } } } ``` ```swift // Using default view controller let contentCardsController = BrazeContentCardUI.ViewController(braze: braze) navigationController?.pushViewController(contentCardsController, animated: true) // Or manually render cards let cards = braze.contentCards.cards for card in cards { switch card { case let card as Braze.ContentCard.Classic: // Render classic card default: break } } ``` ```javascript // Launch default feed Braze.launchContentCards(); // Or manually render cards const cards = await Braze.getContentCards(); cards.forEach(card => { if (card.type === 'CLASSIC') { // Render classic card } }); ``` ```dart // Launch default feed braze.launchContentCards(); // Or manually render cards final cards = await braze.getContentCards(); for (final card in cards) { if (card.type == 'CLASSIC') { // Render classic card } } ``` #### 배너 접근 방식 ```javascript braze.subscribeToBannersUpdates((banners) => { const banner = braze.getBanner("sample_placement_id"); if (!banner) { return; } const container = document.getElementById("global-banner-container"); braze.insertBanner(banner, container); if (banner.isControl) { container.style.display = "none"; } }); braze.requestBannersRefresh(["sample_placement_id"]); ``` ```kotlin // Using BannerView in XML // // Or programmatically val bannerView = BannerView(context).apply { placementId = "sample_placement_id" } container.addView(bannerView) Braze.getInstance(context).requestBannersRefresh(listOf("sample_placement_id")) ``` ```swift // Using BannerUIView let bannerView = BrazeBannerUI.BannerUIView( placementId: "sample_placement_id", braze: braze, processContentUpdates: { result in switch result { case .success(let updates): if let height = updates.height { // Update height constraint } case .failure: break } } ) view.addSubview(bannerView) braze.banners.requestBannersRefresh(placementIds: ["sample_placement_id"]) ``` ```javascript // Using BrazeBannerView component // Or get banner data const banner = await Braze.getBanner("sample_placement_id"); if (banner) { // Render custom banner UI } Braze.requestBannersRefresh(["sample_placement_id"]); ``` ```dart // Using BrazeBannerView widget BrazeBannerView( placementId: "sample_placement_id", ) // Or get banner data final banner = await braze.getBanner("sample_placement_id"); if (banner != null) { // Render custom banner UI } braze.requestBannersRefresh(["sample_placement_id"]); ``` ### 분석 로깅 (커스텀 구현) {#log-analytics-custom-implementations} **Note:** Content Cards와 배너 모두 기본 UI 구성요소를 사용할 때 자동으로 분석을 추적합니다. 아래 예시는 자체 UI를 구축하는 커스텀 구현을 위한 것입니다. #### Content Cards 접근 방식 ```javascript // Manual impression logging required for custom implementations cards.forEach(card => { braze.logContentCardImpressions([card]); }); // Manual click logging required for custom implementations card.logClick(); ``` ```kotlin // Manual impression logging required for custom implementations cards.forEach { card -> card.logImpression() } // Manual click logging required for custom implementations card.logClick() ``` ```swift // Manual impression logging required for custom implementations for card in cards { card.context?.logImpression() } // Manual click logging required for custom implementations card.context?.logClick() ``` ```javascript // Manual impression logging required for custom implementations cards.forEach(card => { Braze.logContentCardImpression(card.id); }); // Manual click logging required for custom implementations Braze.logContentCardClicked(card.id); ``` ```dart // Manual impression logging required for custom implementations for (final card in cards) { braze.logContentCardImpression(card); } // Manual click logging required for custom implementations braze.logContentCardClicked(card); ``` #### 배너 접근 방식 **Important:** `insertBanner()`를 사용할 때 분석이 자동으로 추적됩니다. `insertBanner()`를 사용할 때는 수동 로깅을 사용하지 마세요. ```javascript // Analytics are automatically tracked when using insertBanner() // Manual logging should not be used when using insertBanner() // For custom implementations, use manual logging methods: // Log impression braze.logBannerImpressions([banner]); // Log click (with optional buttonId) braze.logBannerClick("sample_placement_id", buttonId); ``` **Important:** BannerView를 사용할 때 분석이 자동으로 추적됩니다. BannerView를 사용할 때는 수동 로깅을 사용하지 마세요. ```kotlin // Analytics are automatically tracked when using BannerView // Manual logging should not be used for default BannerView // For custom implementations, use manual logging methods: // Log impression Braze.getInstance(context).logBannerImpression("sample_placement_id"); // Log click (with optional buttonId) Braze.getInstance(context).logBannerClick("sample_placement_id", buttonId); ``` **Important:** BannerUIView를 사용할 때 분석이 자동으로 추적됩니다. 기본 BannerUIView에서는 수동 로깅을 사용하지 마세요. ```swift // Analytics are automatically tracked when using BannerUIView // Manual logging should not be used for default BannerUIView // For custom implementations, use manual logging methods: // Get banner for specific placement braze.banners.getBanner(for: "sample_placement_id") { banner in guard let banner = banner else { return } // Log impression banner.context?.logImpression() // Log click (with optional buttonId) banner.context?.logClick(buttonId: buttonId) } // Control groups are automatically handled by BannerUIView ``` **Important:** BrazeBannerView를 사용할 때 분석이 자동으로 추적됩니다. 수동 로깅이 필요하지 않습니다. ```javascript // Analytics are automatically tracked when using BrazeBannerView // No manual logging required // Note: Manual logging methods for Banners are not yet supported in React Native // Control groups are automatically handled by BrazeBannerView ``` **Important:** BrazeBannerView를 사용할 때 분석이 자동으로 추적됩니다. 수동 로깅이 필요하지 않습니다. ```dart // Analytics are automatically tracked when using BrazeBannerView // No manual logging required // Note: Manual logging methods for Banners are not yet supported in Flutter // Control groups are automatically handled by BrazeBannerView ``` ### 등록정보 가져오기 {#getting-properties} #### Content Cards 접근 방식 ```javascript cards.forEach(card => { console.log("Card id:", card.id, "Extras:", card.extras); }); ``` ```kotlin cards.forEach { card -> Log.d(TAG, "Card id: ${card.id} Extras: ${card.extras}") } ``` ```swift for card in cards { print("Card id: \(card.id) Extras: \(card.extras)") } ``` ```javascript cards.forEach(card => { console.log("Card id:", card.id, "Extras:", card.extras); }); ``` ```dart for (final card in cards) { print("Card id: ${card.id} Extras: ${card.extras}"); } ``` #### 배너 접근 방식 ```javascript const banner = braze.getBanner("sample_placement_id"); if (!banner) { return; } console.log("Banner placement:", banner.placementId, "Properties:", banner.properties); ``` ```kotlin val banner = Braze.getInstance(context).getBanner("sample_placement_id") if (banner != null) { Log.d(TAG, "Banner placement: ${banner.placementId} Properties: ${banner.properties}") } ``` ```swift braze.banners.getBanner(for: "sample_placement_id") { banner in guard let banner = banner else { return } print("Banner placement: \(banner.placementId) Properties: \(banner.properties)") } ``` ```javascript const banner = await Braze.getBanner("sample_placement_id"); if (banner) { console.log("Banner placement:", banner.placementId, "Properties:", banner.properties); } ``` ```dart final banner = await braze.getBanner("sample_placement_id"); if (banner != null) { print("Banner placement: ${banner.placementId} Properties: ${banner.properties}"); } ``` ### 대조군 처리 {#handling-control-groups} #### Content Cards 접근 방식 ```javascript cards.forEach(card => { if (card.isControl) { // Logic for control cards ie. don't display but log analytics } else { // Logic for cards ie. render card } }); ``` ```kotlin cards.forEach { card -> if (card.isControl) { // Logic for control cards ie. don't display but log analytics } else { // Logic for cards ie. render card } } ``` ```swift for card in cards { if card.isControl { // Logic for control cards ie. don't display but log analytics } else { // Logic for cards ie. render card } } ``` ```javascript cards.forEach(card => { if (card.isControl) { // Logic for control cards ie. don't display but log analytics } else { // Logic for cards ie. render card } }); ``` ```dart for (final card in cards) { if (card.isControl) { // Logic for control cards ie. don't display but log analytics } else { // Logic for cards ie. render card } } ``` #### 배너 접근 방식 ```javascript braze.subscribeToBannersUpdates((banners) => { const banner = braze.getBanner("sample_placement_id"); if (!banner) { return; } const container = document.getElementById("global-banner-container"); // Always call insertBanner to track impression (including control) braze.insertBanner(banner, container); // Hide if control group if (banner.isControl) { container.style.display = "none"; } }); ``` ```kotlin // BannerView automatically handles control groups // No additional code needed val bannerView = BannerView(context).apply { placementId = "sample_placement_id" } ``` ```swift // BannerUIView automatically handles control groups // No additional code needed let bannerView = BrazeBannerUI.BannerUIView( placementId: "sample_placement_id", braze: braze ) ``` ```javascript // BrazeBannerView automatically handles control groups // No additional code needed ``` ```dart // BrazeBannerView automatically handles control groups // No additional code needed BrazeBannerView( placementId: "sample_placement_id", ) ``` ## 제한 사항 {#limitations} Content Cards에서 배너로 마이그레이션할 때 다음 제한 사항에 유의하세요: ### 트리거된 메시지 마이그레이션 {#migrating-triggered-messages} 배너는 스케줄된 전달 Campaign만 지원합니다. 이전에 API 트리거 또는 실행 기반이었던 메시지를 마이그레이션하려면 Segment 기반 타겟팅으로 변환하세요: - **예시:** API로 "프로필 완성" 카드를 트리거하는 대신, 지난 7일 이내에 가입했지만 프로필을 완료하지 않은 사용자를 위한 Segment를 생성하세요. - **실시간 자격:** 사용자는 Segment 멤버십에 따라 각 새로고침 시 배너 자격을 즉시 부여받거나 상실합니다. ### 기능 차이 {#feature-differences} | 기능 | Content Cards | 배너 | |---------|--------------|---------| | **콘텐츠 구조** | | 피드의 여러 카드 | ✅ 지원됨 | ✅ 캐러셀과 같은 구현을 위해 여러 배치를 생성할 수 있습니다. 배치당 하나의 배너만 반환됩니다. | | 여러 배치 | N/A | ✅ 여러 배치 지원 | | 카드 유형 (클래식, 캡션, 이미지 전용) | ✅ 여러 미리 정의된 유형 | ✅ 단일 HTML 기반 배너 (더 유연함) | | **콘텐츠 관리** | | 드래그 앤 드롭 편집기 | ❌ 커스터마이징에 개발자 필요 | ✅ 마케터가 엔지니어링 없이 생성/업데이트 가능 | | 커스텀 HTML/CSS | ❌ 카드 구조로 제한됨 | ✅ 전체 HTML/CSS 지원 | | 커스터마이징을 위한 키-값 페어 | ✅ 고급 커스터마이징에 필요 | ✅ 고급 커스터마이징을 위한 "등록정보"라는 강력한 타입의 키-값 페어 | | **지속성 및 만료** | | 카드 만료 | ✅ 지원됨 (30일 제한) | ✅ 지원됨 (만료 제한 없음) | | 진정한 지속성 | ❌ 최대 30일 | ✅ 무제한 지속성 | | **표시 및 타겟팅** | | 피드 UI | ✅ 기본 피드 사용 가능 | ❌ 배치 기반만 가능 | | 상황별 배치 | ❌ 피드 기반 | ✅ 네이티브 배치 지원 | | 네이티브 우선순위 | ❌ 커스텀 로직 필요 | ✅ 내장 우선순위 지정 | | **사용자 상호작용** | | 수동 해제 | ✅ 지원됨 | ❌ 지원되지 않음 | | 고정 카드 | ✅ 지원됨 | N/A | | **분석** | | 자동 분석 (기본 UI) | ✅ 지원됨 | ✅ 지원됨 | | 우선순위 정렬 | ❌ 지원되지 않음 | ✅ 지원됨 | | **콘텐츠 업데이트** | | Liquid 템플릿 새로고침 | ❌ 카드 전송/시작 시 한 번만 | ✅ 매번 새로고침 시 갱신 | | 자격 새로고침 | ❌ 카드 전송/시작 시 한 번만 | ✅ 매 세션마다 갱신 | {: .reset-td-br-1 .reset-td-br-2 .reset-td-br-3 aria-label="Feature differences" } ### 제품 제한 사항 {#product-limitations} - 배치당 최대 25개의 활성 메시지. - 새로고침 요청당 최대 10개의 배치 ID. 이를 초과하는 요청은 잘립니다. ### SDK 제한 사항 {#sdk-limitations} - 배너는 현재 .NET MAUI(Xamarin), Cordova, Unity, Vega 또는 TV 플랫폼에서 지원되지 않습니다. - 필수 조건에 나열된 최소 SDK 버전을 사용하고 있는지 확인하세요. ## 관련 문서 {#related-articles} - [배너 배치](https://www.braze.com/docs/ko/ko/developer_guide/banners/placements/) - [튜토리얼: 배치 ID로 배너 표시하기](https://www.braze.com/docs/ko/ko/developer_guide/banners/tutorial_displaying_banners/) - [배너 분석](https://www.braze.com/docs/ko/ko/developer_guide/banners/analytics/) - [배너 FAQ](https://www.braze.com/docs/ko/ko/developer_guide/banners/faq/) # Tutorial: 게재 위치 ID로 배너 표시하기 Source: /docs/ko/developer_guide/banners/tutorial_displaying_banners/index.md # Tutorial: 게재 위치 ID로 배너 표시하기 > 이 튜토리얼의 샘플 코드를 따라 배치 ID를 사용하여 배너를 표시하세요. 자세한 내용은 [배너를](https://www.braze.com/docs/ko/ko/developer_guide/banners/) 참조하세요. ## Prerequisites Before you can start this tutorial, verify that your Braze SDK meets the minimum version requirements: ## Displaying banners for the Web SDK **Important:** We're piloting this new tutorial format. [Tell us what you think](https://docs.google.com/forms/d/e/1FAIpQLSe_5uhWM7eXXk9F_gviO_pvA4rkYO3WA9B6tNJZ3TY91md5bw/viewform?usp=pp_url&entry.569173304=General+Feedback) — your feedback helps us improve future guides. ```js file=index.js import * as braze from "@braze/web-sdk"; braze.initialize("YOUR-API-KEY", { baseUrl: "YOUR-ENDPOINT", enableLogging: true, }); braze.subscribeToBannersUpdates((banners) => { // Get this placement's banner. If it's `null`, the user did not qualify for any banners. const globalBanner = braze.getBanner("global_banner"); if (!globalBanner) { return; } const container = document.getElementById("global-banner-container"); braze.insertBanner(globalBanner, container); if (globalBanner.isControl) { // Hide or collapse the container container.style.display = "none"; } }); braze.requestBannersRefresh(["global_banner", "navigation_square_banner"]); ``` ```html file=main.html
``` !!step lines-index.js=5 #### 1. Enable debugging (optional) To make troubleshooting easier while developing, consider enabling debugging. !!step lines-index.js=8-23 #### 2. Subscribe to Banner updates Use `subscribeToBannersUpdates()` to register a handler that runs whenever a Banner is updated. Inside the handler, call `braze.getBanner("global_banner")` to get the latest placement. !!step lines-index.js=15-22 #### 3. Insert the Banner and handle control groups Use `braze.insertBanner(banner, container)` to insert a Banner when it's returned. To ensure keep your layout clean, hide or collapse Banners that are apart of a control group (for example, when `isControl` is `true`). !!step lines-index.js=25 #### 4. Refresh your Banners After initializing the SDK, call `requestBannersRefresh(["global_banner", ...])` to ensure that Banners are refreshed at the start of each session. You can also call this function at any time to refresh Banner placements later. !!step lines-main.html=3 #### 5. Add a container for your Banner In your HTML, add a new `
` element and give it a short, Banner-related `id`, such as `global-banner-container`. Braze will use this `
` to insert your Banner into the page. ## Prerequisites Before you can start this tutorial, verify that your Braze SDK meets the minimum version requirements: ## Displaying banners for the Android SDK **Important:** We're piloting this new tutorial format. [Tell us what you think](https://docs.google.com/forms/d/e/1FAIpQLSe_5uhWM7eXXk9F_gviO_pvA4rkYO3WA9B6tNJZ3TY91md5bw/viewform?usp=pp_url&entry.569173304=General+Feedback) — your feedback helps us improve future guides. ```kotlin file=MainApplication.kt import android.app.Application import android.util.Log import com.braze.Braze import com.braze.configuration.BrazeConfig import com.braze.support.BrazeLogger public class MainApplication : Application() { override fun onCreate() { super.onCreate() // Turn on verbose Braze logging BrazeLogger.logLevel = Log.VERBOSE // Configure Braze with your SDK key and endpoint val config = BrazeConfig.Builder() .setApiKey("YOUR-API-KEY") .setCustomEndpoint("YOUR-ENDPOINT") .build() Braze.configure(this, config) // Subscribe to Banner updates Braze.getInstance(this) .subscribeToBannersUpdates { update -> for (banner in update.banners) { Log.d("brazeBanners", "Received banner for placement: ${banner.placementId}") // Add any custom banner logic you'd like } } } } ``` ```kotlin file=MainActivity.kt import android.os.Bundle import androidx.activity.ComponentActivity class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // Inflate the XML layout setContentView(R.layout.banners) // Refresh placements Braze.getInstance(this) .requestBannersRefresh( listOf("top-1") ) } } ``` ```xml file=banners.xml ``` !!step lines-MainApplication.kt=12 #### 1. Enable debugging (optional) To make troubleshooting easier while developing, consider enabling debugging. !!step lines-MainApplication.kt=21-28 #### 2. Subscribe to Banner updates Use `subscribeToBannersUpdates()` to register a handler that runs whenever a Banner is updated. !!step lines-MainActivity.kt=10-14 #### 3. Refresh your placements After initializing the Braze SDK, call `requestBannersRefresh(["PLACEMENT_ID"])` to fetch the latest Banner content for that placement. !!step lines-banners.xml=15-19 #### 4. Define `BannerView` in your `banners.xml` In `banners.xml`, declare a `` element with `app:placementId="PLACEMENT_ID"`. Braze will use this element to insert your Banner into your UI. ## Prerequisites Before you can start this tutorial, verify that your Braze SDK meets the minimum version requirements: ## Displaying banners for the Swift SDK **Important:** We're piloting this new tutorial format. [Tell us what you think](https://docs.google.com/forms/d/e/1FAIpQLSe_5uhWM7eXXk9F_gviO_pvA4rkYO3WA9B6tNJZ3TY91md5bw/viewform?usp=pp_url&entry.569173304=General+Feedback) — your feedback helps us improve future guides. ```swift file=AppDelegate.swift import UIKit import BrazeKit import BrazeUI class AppDelegate: UIResponder, UIApplicationDelegate { static var braze: Braze? = nil func application( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? ) -> Bool { // Braze configuration with your SDK API key and endpoint let configuration = Braze.Configuration(apiKey: "YOUR-API-TOKEN", endpoint: "YOUR-ENDPOINT") configuration.logger.level = .debug // Initialize Braze SDK instance AppDelegate.braze = Braze(configuration: configuration) // Request a banners refresh AppDelegate.braze?.banners.requestBannersRefresh(placementIds: ["top-1"]) return true } } ``` ```swift file=SampleApp.swift import SwiftUI @main struct SampleApp: App { // Bind the AppDelegate into the SwiftUI lifecycle @UIApplicationDelegateAdaptor(AppDelegate.self) var delegate var body: some Scene { WindowGroup { ContentView() } } } ``` ```swift file=BannerViewController.swift import UIKit import BrazeKit import BrazeUI final class BannerViewController: UIViewController { static let bannerPlacementID = "top-1" var bannerHeightConstraints: NSLayoutConstraint? lazy var contentView: UILabel = { let contentView = UILabel() contentView.text = "Your Content Here" contentView.textAlignment = .center contentView.translatesAutoresizingMaskIntoConstraints = false return contentView }() lazy var bannerView: BrazeBannerUI.BannerUIView = { var bannerView = BrazeBannerUI.BannerUIView( placementId: BannerViewController.bannerPlacementID, braze: AppDelegate.braze!, processContentUpdates: { [weak self] result in // Update layout properties when banner content has finished loading. DispatchQueue.main.async { guard let self else { return } switch result { case .success(let updates): if let height = updates.height { self.bannerView.isHidden = false self.bannerHeightConstraint?.constant = min(height, 80) } case .failure(let error): return } } } ) bannerView.translatesAutoresizingMaskIntoConstraints = false bannerView.isHidden = true return bannerView }() override func viewDidLoad() { super.viewDidLoad() self.view.addSubview(contentView) self.view.addSubview(bannerView) bannerHeightConstraint = bannerView.heightAnchor.constraint(equalToConstant: 0) NSLayoutConstraint.activate([ contentView.topAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.topAnchor), contentView.leadingAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.leadingAnchor), contentView.trailingAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.trailingAnchor), bannerView.topAnchor.constraint(equalTo: self.contentView.bottomAnchor), bannerView.leadingAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.leadingAnchor), bannerView.trailingAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.trailingAnchor), bannerView.bottomAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.bottomAnchor), bannerHeightConstraint!, ]) } } ``` !!step lines-AppDelegate.swift=14 #### 1. Enable debugging (optional) To make troubleshooting easier while developing, consider enabling debugging. !!step lines-AppDelegate.swift=20 #### 2. Refresh your placements After initializing the Braze SDK, `call requestBannersRefresh(placementIds: ["PLACEMENT_ID"])` to refresh Banner content at the start of each session. !!step lines-BannerViewController.swift=19-37 #### 3. Initialize the Banner and provide a callback Create a `BrazeBannerUI.BannerUIView` instance with your Braze object and placement ID, and provide a `processContentUpdates` callback to unhide the Banner and update its height constraint based on the provided content height. !!step lines-BannerViewController.swift=38-40 #### 4. Enable Auto Layout constraints Hide the Banner view by default, then disable autoresizing mask translation to enable Auto Layout constraints. !!step lines-BannerViewController.swift=43-58 #### 5. Anchor content and set height constraints Anchor your main content to the top using Auto Layout, and place the Banner view directly below it. Pin the Banner’s leading, trailing, and bottom edges to the safe area, and set an initial height constraint of `0` that will be updated when content loads. ```swift file=AppDelegate.swift import BrazeKit import BrazeUI class AppDelegate: UIResponder, UIApplicationDelegate { static var braze: Braze? = nil func application( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? ) -> Bool { // Braze configuration with your SDK API key and endpoint let configuration = Braze.Configuration(apiKey: "YOUR-API-TOKEN", endpoint: "YOUR-ENDPOINT") configuration.logger.level = .debug // Initialize Braze SDK instance AppDelegate.braze = Braze(configuration: configuration) // Request a banners refresh AppDelegate.braze?.banners.requestBannersRefresh(placementIds: ["top-1"]) return true } } ``` ```swift file=SampleApp.swift import SwiftUI @main struct SampleApp: App { // Bind the AppDelegate into the SwiftUI lifecycle @UIApplicationDelegateAdaptor(AppDelegate.self) var delegate var body: some Scene { WindowGroup { BannerSwiftUIView() } } } ``` ```swift file=BannerSwiftUIView.swift import BrazeKit import BrazeUI import SwiftUI @available(iOS 13.0, *) struct BannerSwiftUIView: View { static let bannerPlacementID = "top-1" @State var hasBannerForPlacement: Bool = false @State var contentHeight: CGFloat = 0 var body: some View { VStack { Text("Your Content Here") .frame(maxWidth: .infinity, maxHeight: .infinity) if let braze = AppDelegate.braze, hasBannerForPlacement { BrazeBannerUI.BannerView( placementId: BannerSwiftUIView.bannerPlacementID, braze: braze, processContentUpdates: { result in switch result { case .success(let updates): if let height = updates.height { self.contentHeight = height } case .failure: return } } ) .frame(height: min(contentHeight, 80)) } }.onAppear { AppDelegate.braze?.banners.getBanner( for: BannerSwiftUIView.bannerPlacementID, { banner in hasBannerForPlacement = banner != nil } ) } } } ``` !!step lines-AppDelegate.swift=13 #### 1. Enable debugging (optional) To make troubleshooting easier while developing, consider enabling debugging. !!step lines-AppDelegate.swift=19 #### 2. Refresh your placements After initializing the Braze SDK, call `requestBannersRefresh(placementIds: ["PLACEMENT_ID"])` to refresh Banner content at the start of each session. !!step lines-BannerSwiftUIView.swift=1-46 #### 3. Create a view component Create a reusable SwiftUI view component that displays available Banners and contains your main app content if needed. !!step lines-BannerSwiftUIView.swift=36-43 #### 4. Only display available Banners Only attempt to show `BrazeBannerUI.BannerView` if the SDK is initialized and Banner content exists for that user. In `.onAppear`, call `getBanner(for:placementID)` to set the state of `hasBannerForPlacement`. !!step lines-BannerSwiftUIView.swift=17-32 #### 5. Only show `BannerView` after it loads To avoid blank space in your UI, only show `BrazeBannerUI.BannerView` if a Banner is present and the SDK is initialized. !!step lines-BannerSwiftUIView.swift=23-32 #### 6. Dynamically update Banner height Use the `processContentUpdates` callback to fetch the Banner’s content height as soon as it loads. Update your SwiftUI state (`contentHeight`) and apply a `.frame(height:)` constraint using the provided height. !!step lines-BannerSwiftUIView.swift=34 #### 7. Limit the Banner height To ensure your Banner never exceeds the maximum height, apply a `.frame(height: min(contentHeight, 80))` modifier. This will keep your UI visually balanced regardless of the Banner's content. # 배너: 자주 묻는 질문 Source: /docs/ko/developer_guide/banners/faq/index.md # Frequently asked questions > These are answers to frequently asked questions about Banners in Braze. For more general information, see [About Banners](). ## When do Banner updates appear for users? Banners are refreshed with their latest data whenever you call the refresh method—there's no need to resend or update your Banner campaign. ## How many placements can I request in a session? In a single refresh request, you can request a maximum of 10 placements. For each one you request, Braze will return the highest-priority Banner a user is eligible for. Additional requests will return an error. For more information, see [Placement requests](). ## How many Banner campaigns can be active simultaneously? Each workspace can support up to 200 active Banner campaigns. If this limit is reached, you'll need to [archive or deactivate](https://www.braze.com/docs/ko/ko/user_guide/engagement_tools/messaging_fundamentals/about_statuses/#changing-the-status) an existing campaign before creating a new one. ## For campaigns sharing a placement, which Banner is displayed first? If a user qualifies for multiple Banner campaigns that share the same placement, the Banner with the highest priority will be displayed. For more information, see [Banner priority](). ## Can I use Banners in my existing Content Card feed? Banners are different from Content Cards, meaning you can’t use Banners and Content Cards in the same feed. To replace existing Content Card feeds with Banners, you’ll need to [create placements in your app or website](https://www.braze.com/docs/ko/ko/developer_guide/banners/placements/). ## Can Banners include video? The standard Banner composer supports images, text, and buttons. To include a video in a Banner, you could use a **Custom Code** block and render a video or embedded player in your app or website. ## Can I trigger a banner based on user actions? While Banners do not support [action-based delivery](https://www.braze.com/docs/ko/ko/user_guide/engagement_tools/campaigns/building_campaigns/delivery_types/triggered_delivery), you can target users based on their past actions using segmentation and priority. For example, to show a special Banner only to users who have completed a `purchase` event: 1. **Targeting:** In your campaign, target a segment of users who have performed the custom event `purchase` at least once. 2. **Priority:** If you have a general Banner for all users and this specific Banner for purchasers targeting the same placement, set the specific Banner's priority to **High** and the general Banner to **Medium** or **Low**. When the user starts a new session or refreshes Banners after performing the action, Braze evaluates their eligibility. If they match the "Purchase" segment, the high-priority Banner will be displayed. ## Can users dismiss a Banner? Yes. You can allow users to manually dismiss a Banner by turning on dismissal behavior in the Banner composer. See [Configure dismissal behavior](https://www.braze.com/docs/ko/ko/user_guide/channels/banners/create_a_banner/#dismiss-behavior) for details on turning on dismissal and customizing the dismiss button. Users can manually dismiss Banners only if dismissal behavior is enabled. If dismissal isn't enabled, you can control Banner visibility by managing user segment eligibility. When a user no longer meets the targeting criteria for a Banner campaign, they won't see it again on their next session. When a user dismisses a Banner, they're ineligible for that campaign by default. To let dismissed users see the Banner again, [configure re-eligibility](https://www.braze.com/docs/ko/ko/user_guide/channels/banners/create_a_banner/#re-eligibility) in the campaign's **Delivery Controls** step. Canvas Banner steps use Canvas re-entry settings to control re-eligibility instead. For example, if you display a promotional Banner until a user makes a purchase, logging an event such as `purchase_completed` can remove that user from the targeted segment, effectively hiding the Banner in subsequent sessions. ## Can I export Banners campaign analytics using the Braze API? Yes. You can use the [`/campaigns/data_series` endpoint](https://www.braze.com/docs/ko/ko/api/endpoints/export/campaigns/get_campaign_analytics/) to get data on how many Banner campaigns were viewed, clicked, or converted. ## When are users segmented? Users are segmented at the beginning of the session. If a campaign's targeted segments depend on custom attributes, custom events, or other targeting attributes, they must be present on the user at the beginning of the session. ## How can I compose Banners to ensure the lowest latency? The simpler the messaging in your Banner, the faster it will render. It’s best to test your Banner campaign against the expected latency for your use case. For example, be sure to test Liquid attributes like `catalog_items`. ## Are all Liquid tags supported? No. However, most Liquid tags are supported for Banner messages, except for `catalog_items` that are re-rendered using the [`:rerender` tag](https://www.braze.com/docs/ko/ko/user_guide/data/activation/catalogs/using_catalogs/#using-liquid). ## Can I capture click events? Yes. How click events are captured depends on how your Banner is rendered: - **Standard editor components:** If your Banner uses standard editor components (images, buttons, text), clicks are tracked automatically when using the SDK's insertion methods. - **Custom Code Blocks:** If you want to track clicks for elements within a Custom Code editor block, you must call `brazeBridge.logClick()` from within your custom HTML to track clicks. This applies even when using the SDK methods to insert and render the Banner. For the full reference, see [Custom code and JavaScript bridge for Banners](https://www.braze.com/docs/ko/ko/user_guide/message_building_by_channel/banners/custom_code/#javascript-bridge). - **Custom UI (headless):** If you're building a fully custom UI using the Banner's custom properties instead of rendering the Banner HTML, call `logClick()` on the Banner object from your application code. For more information, see [Logging clicks](https://www.braze.com/docs/ko/ko/developer_guide/banners/placements/#logging-clicks). # Braze SDK의 콘텐츠 카드 Source: /docs/ko/developer_guide/content_cards/index.md # 콘텐츠 카드 > Braze SDK의 콘텐츠 카드에 대해 배우고, 애플리케이션에 사용할 수 있는 다양한 데이터 모델과 카드별 속성을 포함합니다. **Tip:** Using Content Cards for banner-style messages? Try out [Banners](https://www.braze.com/docs/ko/ko/user_guide/message_building_by_channel/banners/)— perfect for inline, persistent in-app and web messages. **Note:** This guide uses code samples from the Braze Web SDK 4.0.0+. To upgrade to the latest Web SDK version, see [SDK Upgrade Guide](https://github.com/braze-inc/braze-web-sdk/blob/master/UPGRADE_GUIDE.md). ## Prerequisites Before you can use Content Cards, you'll need to [integrate the Braze Web SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=web) into your app. However, no additional setup is required. To build your own UI instead, see [Content Card Customization Guide](https://www.braze.com/docs/ko/ko/developer_guide/content_cards/). **Note:** Some ad blockers and browser privacy extensions can block the Braze Web SDK script or related network requests, which can prevent Content Cards from loading. If you're using the CDN integration method, consider switching to the [NPM integration method](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?subtab=package%20manager&sdktab=web), which stores SDK libraries locally on your website and can avoid some ad-blocker-related issues. ## Standard feed UI To use the included Content Cards UI, you'll need to specify where to show the feed on your website. In this example, we have a `
` in which we want to place the Content Cards feed. We'll use three buttons to hide, show, or toggle (hide or show based on its current state) the feed. ```html ``` When using the `toggleContentCards(parentNode, filterFunction)` and `showContentCards(parentNode, filterFunction)` methods, if no arguments are provided, all Content Cards will be shown in a fixed-position sidebar on the right-hand side of the page. Otherwise, the feed will be placed in the specified `parentNode` option. |Parameters | Description | |---|---| |`parentNode` | The HTML node to render the Content Cards into. If the parent node already has a Braze Content Cards view as a direct descendant, the existing Content Cards will be replaced. For example, you should pass in `document.querySelector(".my-container")`.| |`filterFunction` | A filter or sort function for cards displayed in this view. Invoked with the array of `Card` objects, sorted by `{pinned, date}`. Expected to return an array of sorted `Card` objects to render for this user. If omitted, all cards will be displayed. | {: .reset-td-br-1 .reset-td-br-2 aria-label="Standard feed UI" } [See the SDK reference docs](https://js.appboycdn.com/web-sdk/latest/doc/modules/braze.html#togglecontentcards) for more information on Content Card toggling. ## Testing Content Cards on the web You can test your Content Cards integration using your browser's developer tools. 1. Create a Content Card campaign and target your test user. 2. Log in to the website that has your Web SDK integration. 3. Open your browser console. For Chrome, right-click the page, select **Inspect**, then select the **Console** tab. 4. Run these commands in the console: - `window.braze.getCachedContentCards()` - `window.braze.toggleContentCards()` ## Card types and properties The Content Cards data model is available in the Web SDK and offers the following Content Card types: [ImageOnly](https://js.appboycdn.com/web-sdk/latest/doc/classes/braze.imageonly.html), [CaptionedImage](https://js.appboycdn.com/web-sdk/latest/doc/classes/braze.captionedimage.html), and [ClassicCard](https://js.appboycdn.com/web-sdk/latest/doc/classes/braze.classiccard.html). Each type inherits common properties from a base model [Card](https://js.appboycdn.com/web-sdk/latest/doc/classes/braze.card.html) and has the following additional properties. **Tip:** To log Content Card data, see [Logging analytics](https://www.braze.com/docs/ko/ko/developer_guide/content_cards/logging_analytics/). ### Base card model All Content Cards have these shared properties: |Property|Description| |---|---| | `expiresAt` | The UNIX timestamp of the card's expiration time.| | `extras`| (Optional) Key-value pair data formatted as a string object with a value string. | | `id` | (Optional) The id of the card. This will be reported back to Braze with events for analytics purposes. | | `pinned` | This property reflects if the card was set up as "pinned" in the dashboard.| | `updated` | The UNIX timestamp of when this card was last modified. | | `viewed` | This property reflects whether the user viewed the card or not.| | `isControl` | This property is `true` when a card is a "control" group within an A/B test.| {: .reset-td-br-1 .reset-td-br-2 aria-label="Base card model" } ### Image only [ImageOnly](https://js.appboycdn.com/web-sdk/latest/doc/classes/braze.imageonly.html) cards are clickable full-sized images. |Property|Description| |---|---| | `aspectRatio` | The aspect ratio of the card's image and serves as a hint before image loading completes. Note that the property may not be supplied in certain circumstances. | | `categories` | This property is purely for organization in your custom implementation; these categories can be set in the dashboard composer. | | `clicked` | This property indicates whether this card has ever been clicked on this device. | | `created` | The UNIX timestamp of the card's creation time from Braze. | | `dismissed` | This property indicates if this card has been dismissed. | | `dismissible` | This property reflects if the user can dismiss the card, removing it from view. | | `imageUrl` | The URL of the card's image.| | `linkText` | The display text for the URL. | | `url` | The URL that will be opened after the card is clicked on. | {: .reset-td-br-1 .reset-td-br-2 aria-label="Image only" } ### Captioned image [CaptionedImage](https://js.appboycdn.com/web-sdk/latest/doc/classes/braze.captionedimage.html) cards are clickable, full-sized images with accompanying descriptive text. |Property|Description| |---|---| | `aspectRatio` | The aspect ratio of the card's image and serves as a hint before image loading completes. Note that the property may not be supplied in certain circumstances. | | `categories` | This property is purely for organization in your custom implementation; these categories can be set in the dashboard composer. | | `clicked` | This property indicates whether this card has ever been clicked on this device. | | `created` | The UNIX timestamp of the card's creation time from Braze. | | `dismissed` | This property indicates if this card has been dismissed. | | `dismissible` | This property reflects if the user can dismiss the card, removing it from view. | | `imageUrl` | The URL of the card's image.| | `linkText` | The display text for the URL. | | `title` | The title text for this card. | | `url` | The URL that will be opened after the card is clicked on. | {: .reset-td-br-1 .reset-td-br-2 aria-label="Captioned image" } ### Classic The [ClassicCard](https://js.appboycdn.com/web-sdk/latest/doc/classes/braze.classiccard.html) model can contain an image with no text or a text with image. |Property|Description| |---|---| | `aspectRatio` | The aspect ratio of the card's image and serves as a hint before image loading completes. Note that the property may not be supplied in certain circumstances. | | `categories` | This property is purely for organization in your custom implementation; these categories can be set in the dashboard composer. | | `clicked` | This property indicates whether this card has ever been clicked on this device. | | `created` | The UNIX timestamp of the card's creation time from Braze. | | `description` | The body text for this card. | | `dismissed` | This property indicates if this card has been dismissed. | | `dismissible` | This property reflects if the user can dismiss the card, removing it from view. | | `imageUrl` | The URL of the card's image.| | `linkText` | The display text for the URL. | | `title` | The title text for this card. | | `url` | The URL that will be opened after the card is clicked on. | {: .reset-td-br-1 .reset-td-br-2 aria-label="Classic" } ## Control group If you use the default Content Cards feed, impressions and clicks will be automatically tracked. If you use a custom integration for Content Cards, you need need [log impressions](https://www.braze.com/docs/ko/ko/developer_guide/content_cards/logging_analytics/) when a Control Card would have been seen. As part of this effort, make sure to handle Control cards when logging impressions in an A/B test. These cards are blank, and while they aren’t seen by users, you should still log impressions in order to compare how they perform against non-Control cards. To determine if a Content Card is in the Control group for an A/B test, check the `card.isControl` property (Web SDK v4.5.0+) or check if the card is a `ControlCard` instance (`card instanceof braze.ControlCard`). ## Card methods ### Default feed methods Use these methods when displaying Content Cards using the Braze default feed UI: |Method | Description | |---|---| |[`showContentCards`](https://js.appboycdn.com/web-sdk/latest/doc/modules/braze.html#showcontentcards)| Displays the default Content Cards feed. Renders cards into a provided `parentNode` HTML element, or as a fixed-position sidebar on the right side of the page if no element is given. Accepts an optional `filterFunction` to sort or filter cards before display. | |[`hideContentCards`](https://js.appboycdn.com/web-sdk/latest/doc/modules/braze.html#hidecontentcards)| Hides the default Content Cards feed if it is currently showing. | |[`toggleContentCards`](https://js.appboycdn.com/web-sdk/latest/doc/modules/braze.html#togglecontentcards)| Shows the default Content Cards feed if it is hidden, or hides it if it is visible. If you need to display multiple Content Card feeds simultaneously, use `showContentCards` and `hideContentCards` instead. | {: .reset-td-br-1 .reset-td-br-2 aria-label="Default feed methods" } ### Custom feed methods Use these methods when building your own Content Card UI: |Method | Description | |---|---| |[`subscribeToContentCardsUpdates`](https://js.appboycdn.com/web-sdk/latest/doc/modules/braze.html#subscribetocontentcardsupdates)| Registers a callback function that is invoked whenever Content Cards are updated for the current user, such as on session start. Use this as the primary way to receive card data for your custom feed. Must be called before `openSession()` to receive updates on the initial session. | |[`getCachedContentCards`](https://js.appboycdn.com/web-sdk/latest/doc/modules/braze.html#getcachedcontentcards)| Returns all currently available cards from the most recent Content Cards refresh. Use this to immediately display cards on page load without waiting for a new server request, such as when the user returns to a page during an active session. | |[`requestContentCardsRefresh`](https://js.appboycdn.com/web-sdk/latest/doc/modules/braze.html#requestcontentcardsrefresh)| Requests an immediate refresh of Content Cards from Braze servers. By default, cards refresh on session start and when the default feed is reopened. Use this to force a refresh at other times, such as after a specific user action. Be aware of [rate limits](https://www.braze.com/docs/ko/ko/developer_guide/content_cards/customizing_cards/feed/#rate-limit). | |[`logContentCardImpressions`](https://js.appboycdn.com/web-sdk/latest/doc/modules/braze.html#logcontentcardimpressions)| Logs impression events for an array of cards. Call this when cards are rendered and visible to the user. Required for accurate campaign reporting when using a custom UI, as impressions are not tracked automatically outside the default feed. | |[`logContentCardClick`](https://js.appboycdn.com/web-sdk/latest/doc/modules/braze.html#logcontentcardclick)| Logs a click event for a single card. Call this when a user interacts with a card in your custom UI. Required for accurate campaign reporting, as clicks are not tracked automatically outside the default feed. | |[`handleBrazeAction`](https://js.appboycdn.com/web-sdk/latest/doc/modules/braze.html#handlebrazeaction)| Processes a card's URL and executes the configured on-click action, including Braze actions (`brazeActions://` URLs) and standard URL navigation. Call this in your card click handler to ensure on-click behaviors configured in the Braze dashboard are executed. | |[`dismissCard`](https://js.appboycdn.com/web-sdk/latest/doc/classes/braze.card.html#dismisscard)| Programmatically dismisses a card, removing it from the user's feed. Use this to allow users to dismiss cards in your custom UI. | {: .reset-td-br-1 .reset-td-br-2 aria-label="Custom feed methods" } For more details, refer to the [SDK reference documentation](https://js.appboycdn.com/web-sdk/latest/doc/modules/braze.html). ## Best practices ### Call methods in the correct order For custom feeds, Content Cards refresh only on session start if `subscribeToContentCardsUpdates()` is called before `openSession()`. Call your Braze methods in this order: ```javascript import * as braze from "@braze/web-sdk"; // Step 1: Initialize the SDK braze.initialize("YOUR-API-KEY", { baseUrl: "YOUR-SDK-ENDPOINT" }); // Step 2: Subscribe to card updates braze.subscribeToContentCardsUpdates((updates) => { const cards = updates.cards; renderCards(cards); }); // Step 3: Identify the user braze.changeUser("USER_ID"); // Step 4: Start the session braze.openSession(); ``` ### Use cached cards to persist content across page loads Because `subscribeToContentCardsUpdates()` invokes only its callback when there are new updates (such as on session start), cards can disappear from your custom feed if a user refreshes the page mid-session. To prevent this, use `getCachedContentCards()` to immediately render cards from the local cache, alongside your subscription for new updates: ```javascript import * as braze from "@braze/web-sdk"; function renderCards(cards) { const container = document.getElementById("content-cards"); container.textContent = ""; const displayedCards = []; cards.forEach(card => { if (card instanceof braze.ClassicCard || card instanceof braze.CaptionedImage) { const cardElement = document.createElement("div"); const h3 = document.createElement("h3"); h3.textContent = card.title || ""; cardElement.appendChild(h3); const p = document.createElement("p"); p.textContent = card.description || ""; cardElement.appendChild(p); if (card.imageUrl) { const img = document.createElement("img"); img.src = card.imageUrl; img.alt = card.title || ""; cardElement.appendChild(img); } if (card.url) { cardElement.addEventListener("click", () => { braze.logContentCardClick(card); braze.handleBrazeAction(card.url); }); } container.appendChild(cardElement); displayedCards.push(card); } }); if (displayedCards.length > 0) { braze.logContentCardImpressions(displayedCards); } } // Display cached cards immediately const cached = braze.getCachedContentCards(); if (cached && cached.cards.length > 0) { renderCards(cached.cards); } // Subscribe to future updates braze.subscribeToContentCardsUpdates((updates) => { renderCards(updates.cards); }); ``` ### Log analytics for custom feeds When using a custom UI, impressions, clicks, and dismissals are not tracked automatically. You must log each event manually: - **Impressions:** Call `logContentCardImpressions([card1, card2, ...])` with an array of card objects when cards become visible to the user. - **Clicks:** Call `logContentCardClick(card)` when a user interacts with a card. - **On-click behavior:** Call `handleBrazeAction(card.url)` to execute the card's configured on-click action (such as navigating to a URL or logging a custom event). **Warning:** The argument passed to `logContentCardClick()` must be an original Braze `Card` object. If you transform or reconstruct the card data (for example, by serializing and deserializing), clicks are not logged and you see the error: "card must be a Card object." ## Using Google Tag Manager Google Tag Manager works by injecting the [Braze CDN](https://www.braze.com/docs/ko/ko/developer_guide/platform_integration_guides/web/initial_sdk_setup#install-cdn) (a version of our Web SDK) directly into your website code, which means that all SDK methods are available just as if you had integrated the SDK without Google Tag Manager, except when implementing Content Cards. ### Setting up Content Cards For a standard integration of the Content Card feed, you can use a **Custom HTML** tag in Google Tag Manager. Add the following to your Custom HTML tag, which will activate the standard Content Card feed: ```html ``` ![Tag Configuration in Google Tag Manager of a Custom HTML tag that shows the Content Card feed.](https://www.braze.com/docs/ko/ko/assets/img/web-gtm/gtm_content_cards.png?0568575182a8f9fbe2e5acfd37f9a3c4) For more freedom over customizing the appearance of Content Cards and their feed, you can directly integrate Content Cards into your native website. There are two approaches you can take with this: using the standard feed UI or creating a custom feed UI. When implementing the [standard feed UI](https://www.braze.com/docs/ko/ko/developer_guide/platform_integration_guides/web/content_cards/integration/#standard-feed-ui), Braze methods must have `window.` added to the start of the method. For example, `braze.showContentCards` should instead be `window.braze.showContentCards`. For [custom feed](https://www.braze.com/docs/ko/ko/developer_guide/content_cards/creating_cards/) styling, the steps are the same as if you had integrated the SDK without GTM. For example, if you want to customize the width of the Content Card feed, you can paste the following into your CSS file: ```css body .ab-feed { width: 800px; } ``` ### Upgrading templates {#upgrading} To upgrade to the latest version of the Braze Web SDK, take the following three steps in your Google Tag Manager dashboard: 1. **Update tag template**
Go to the **Templates** page within your workspace. Here you should see an icon indicating an update is available.

![Templates page showing an update is available](https://www.braze.com/docs/ko/ko/assets/img/web-gtm/gtm-update-available.png?f36c891c9e7be7f37f873e17517a827b)

Click that icon and after reviewing the change, click to **Accept Update**.

![A screen comparing the old and new tag templates with a button to "Accept Update"](https://www.braze.com/docs/ko/ko/assets/img/web-gtm/gtm-accept-update.png?417cfc6f52f25fc1953509c10e637ed1)

2. **Update version number**
Once your tag template has been updated, edit the Braze Initialization Tag, and update the SDK version to the most recent `major.minor` version. For example, if the latest version is `4.1.2`, enter `4.1`. You can view a list of SDK versions in our [changelog](https://github.com/braze-inc/braze-web-sdk/blob/master/CHANGELOG.md).

![Braze Initialization Template with an input field to change the SDK Version](https://www.braze.com/docs/ko/ko/assets/img/web-gtm/gtm-version-number.png?9487bc3d9318863f2899478c0265715f)

3. **QA and publish**
Verify the new SDK version is working using Google Tag Manager's [debugging tool](https://support.google.com/tagmanager/answer/6107056?hl=en) prior to publishing an update to your tag container. ### Troubleshooting {#troubleshooting} #### Enable tag debugging {#debugging} Each Braze tag template has an optional **GTM Tag Debugging** checkbox which can be used to log debug messages to your web page's JavaScript console. ![Google Tag Manager's Debug tool](https://www.braze.com/docs/ko/ko/assets/img/web-gtm/gtm-tag-debugging.png?7389da92ab6dd818760e71b6f48dfbab) #### Enter debug mode Another way to help debug your Google Tag Manager integration is using Google's [Preview mode](https://support.google.com/tagmanager/answer/6107056) feature. This will help identify what values are being sent from your web page's data layer to each triggered Braze tag and will also explain which tags were or were not triggered. ![The Braze Initialization Tag summary page provides an overview of the tag, including information on which tags were triggered.](https://www.braze.com/docs/ko/ko/assets/img/web-gtm/gtm-debug-mode.png?f7a1f3c237c28bc521087754a40561f3) #### Verify tag sequencing for custom events {#tag-sequencing} If custom events or other actions aren't logging in Braze, a common cause is a race condition where an action tag (such as **Custom Event** or **Purchase**) fires before the **Braze Initialization** tag has completed. To fix this, configure [tag sequencing](https://support.google.com/tagmanager/answer/6238868) in GTM: 1. Open the action tag that isn't logging correctly. 2. Under **Advanced Settings** > **Tag Sequencing**, select **A tag that fires before \[this tag\]**. 3. Choose your **Braze Initialization** tag as the setup tag. This ensures the SDK is fully initialized before any action tags attempt to send data to Braze. #### Enable verbose logging To capture detailed logs for troubleshooting, you can enable verbose logging on your Google Tag Manager integration. These logs will appear in the **Console** tab of your browser's [developer tools](https://developer.mozilla.org/en-US/docs/Learn/Common_questions/What_are_browser_developer_tools). In your Google Tag Manager integration, navigate to your Braze Initialization Tag and select **Enable Web SDK Logging**. ![The Braze Initialization Tag summary page with the option to Enable Web SDK Logging turned on.](https://www.braze.com/docs/ko/ko/assets/img/web-gtm/gtm_verbose_logging.png?c5d9946843f312420b2b6e00ea669647) [changelog]: https://github.com/braze-inc/braze-web-sdk/blob/master/CHANGELOG.md ## Prerequisites Before you can use Braze Content Cards, you'll need to integrate the [Braze Android SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=android) into your app. However, no additional setup is required. ## Google fragments In Android, the Content Cards feed is implemented as a [fragment](https://developer.android.com/guide/components/fragments.html) available in the Braze Android UI project. The [`ContentCardsFragment`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.ui.contentcards/-content-cards-fragment/index.html) class will automatically refresh and display the contents of the Content Cards and log usage analytics. The cards that can appear in a user's `ContentCards` are created on the Braze dashboard. To learn how to add a fragment to an activity, see [Google's fragments documentation](https://developer.android.com/guide/fragments#Adding). ## Card types and properties The Content Cards data model is available in the Android SDK and offers the following unique Content Card types. Each type shares a base model, which allows them to inherit common properties from the base model, in addition to having their own unique properties. For full reference documentation, see [`com.braze.models.cards`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.cards/index.html). ### Base card model {#base-card-for-android} The [base card](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.cards/-card/index.html) model provides foundational behavior for all cards. |Property | Description | |---|---| |`getId()` | Returns the card's ID set by Braze.| |`getViewed()` | Returns a boolean reflects if the card is read or unread by the user.| |`getExtras()` | Returns a map of key-value extras for this card.| |`getCreated()` | Returns the unix timestamp of the card's creation time from Braze.| |`isPinned` | Returns a boolean that reflects whether the card is pinned.| |`getOpenUriInWebView()` | Returns a boolean that reflects whether Uris for this card should be opened
in the Braze WebView or not.| |`getExpiredAt()` | Gets the expiration date of the card.| |`isRemoved()` | Returns a boolean that reflects whether the end user has dismissed this card.| |`isDismissibleByUser()` | Returns a boolean that reflects whether the card is dismissible by the user.| |`isClicked()` | Returns a boolean that reflects the clicked state of this card.| |`isDismissed` | Returns a boolean that reflects whether the card has been dismissed. Set to `true` to mark the card as dismissed. If a card is already marked as dismissed, it cannot be marked as dismissed again.| |`isControl()` | Returns a boolean if this card is a control card and should not be rendered.| {: .reset-td-br-1 .reset-td-br-2 aria-label="Base card model #base-card-for-android" } ### Image only {#banner-image-card-for-android} [Image only cards](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.cards/-image-only-card/index.html) are clickable full-sized images. |Property | Description | |---|---| |`getImageUrl()` | Returns the URL of the card's image.| |`getUrl()` | Returns the URL that will be opened after the card is clicked. It can be a HTTP(s) URL or a protocol URL.| |`getDomain()` | Returns link text for the property URL.| {: .reset-td-br-1 .reset-td-br-2 aria-label="Image only #banner-image-card-for-android" } ### Captioned image {#captioned-image-card-for-android} [Captioned image cards](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.cards/-captioned-image-card/index.html) are clickable, full-sized images with accompanying descriptive text. |Property | Description | |---|---| |`getImageUrl()` | Returns the URL of the card's image.| |`getTitle()` | Returns the title text for the card.| |`getDescription()` | Returns the body text for the card.| |`getUrl()` | Returns the URL that will be opened after the card is clicked. It can be a HTTP(s) URL or a protocol URL.| |`getDomain()` | Returns the link text for the property URL. | {: .reset-td-br-1 .reset-td-br-2 aria-label="Captioned image #captioned-image-card-for-android" } ### Classic {#text-Announcement-card-for-android} A classic card without an image included will result in a [text announcement card](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.cards/-text-announcement-card/index.html). If an image is included, you will receive a [short news card](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.cards/-short-news-card/index.html). |Property | Description | |---|---| |`getTitle()` | Returns the title text for the card. | |`getDescription()` | Returns the body text for the card. | |`getUrl()` | Returns the URL that will be opened after the card is clicked. It can be a HTTP(s) URL or a protocol URL. | |`getDomain()` | Returns the link text for the property URL. | |`getImageUrl()` | Returns the URL of the card's image, applies only to the classic Short News Card. | |`isDismissed` | Returns a boolean that reflects whether the card has been dismissed. Set to `true` to mark the card as dismissed. If a card is already marked as dismissed, it cannot be marked as dismissed again. | {: .reset-td-br-1 .reset-td-br-2 aria-label="Classic #text-Announcement-card-for-android" } ## Card methods All [`Card`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.cards/index.html) data model objects offer the following analytics methods for logging user events to Braze servers. |Method | Description | |---|---| |`logImpression()` | Manually log an impression to Braze for a particular card. | |`logClick()` | Manually log a click to Braze for a particular card. | {: .reset-td-br-1 .reset-td-br-2 aria-label="Card methods" } ## Prerequisites Before you can use Content Cards, you'll need to integrate the [Braze Swift SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=swift) into your app. However, no additional setup is required. ## View controller contexts The default Content Cards UI can be integrated from the `BrazeUI` library of the Braze SDK. Create the Content Cards view controller using the `braze` instance. If you wish to intercept and react to the Content Card UI lifecycle, implement [`BrazeContentCardUIViewControllerDelegate`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazeui/brazecontentcarduiviewcontrollerdelegate) as the delegate for your `BrazeContentCardUI.ViewController`. **Note:** For more information about iOS view controller options, refer to the [Apple developer documentation](https://developer.apple.com/documentation/uikit/view_controllers/showing_and_hiding_view_controllers). The `BrazeUI` library of the Swift SDK provides two default view controller contexts: [navigation](#swift_navigation) or [modal](#swift_modal). This means you can integrate Content Cards in these contexts by adding a few lines of code to your app or site. Both views offer customization and styling options as described in the [customization guide](https://www.braze.com/docs/ko/ko/developer_guide/customization_guides/content_cards/customizing_styles/?tab=ios). You can also create a custom Content Card view controller instead of using the standard Braze one for even more customization options—refer to the [Content Cards UI tutorial](https://braze-inc.github.io/braze-swift-sdk/tutorials/braze/c2-contentcardsui/) for an example. **Important:** To handle control variant Content Cards in your custom UI, pass in your [`Braze.ContentCard.Control`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/contentcard/control(_:)) object, then call the `logImpression` method as you would with any other Content Card type. The object will implicitly log a control impression to inform our analytics of when a user would have seen the control card. ### Navigation A navigation controller is a view controller that manages one or more child view controllers in a navigation interface. Here is an example of pushing a `BrazeContentCardUI.ViewController` instance into a navigation controller: ```swift func pushViewController() { guard let braze = AppDelegate.braze else { return } let contentCardsController = BrazeContentCardUI.ViewController(braze: braze) // Implement and set `BrazeContentCardUIViewControllerDelegate` if you wish to intercept click actions. contentCardsController.delegate = self self.navigationController?.pushViewController(contentCardsController, animated: true) } ``` ```objc - (void)pushViewController { BRZContentCardUIViewController *contentCardsController = [[BRZContentCardUIViewController alloc] initWithBraze:self.braze]; // Implement and set `BrazeContentCardUIViewControllerDelegate` if you wish to intercept click actions. [contentCardsController setDelegate:self]; [self.navigationController pushViewController:contentCardsController animated:YES]; } ``` ### Modal Use modal presentations to create temporary interruptions in your app’s workflow, such as prompting the user for important information. This model view has a navigation bar on top and a **Done** button on the side of the bar. Here is an example of pushing a `BrazeContentCard.ViewController` instance into a modal controller: ```swift func presentModalViewController() { guard let braze = AppDelegate.braze else { return } let contentCardsModal = BrazeContentCardUI.ModalViewController(braze: braze) // Implement and set `BrazeContentCardUIViewControllerDelegate` if you wish to intercept click actions. contentCardsModal.viewController.delegate = self self.navigationController?.present(contentCardsModal, animated: true, completion: nil) } ``` ```objc - (void)presentModalViewController { BRZContentCardUIModalViewController *contentCardsModal = [[BRZContentCardUIModalViewController alloc] initWithBraze:AppDelegate.braze]; // Implement and set `BrazeContentCardUIViewControllerDelegate` if you wish to intercept click actions. [contentCardsModal.viewController setDelegate:self]; [self.navigationController presentViewController:contentCardsModal animated:YES completion:nil]; } ``` For example usage of `BrazeUI` view controllers, check out the corresponding Content Cards UI samples in our [Examples app](https://github.com/braze-inc/braze-swift-sdk/tree/main/Examples). ## Base card model The Content Cards data model is available in the `BrazeKit` module of the Braze Swift SDK. This module contains the following Content Card types, which are an implementation of the `Braze.ContentCard` type. For a full list of Content Card properties and their usage, see [`ContentCard` class](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/contentcard). - Image only - Captioned image - Classic - Classic image - Control To access the Content Cards data model, call `contentCards.cards` on your `braze` instance. See [Logging analytics](https://www.braze.com/docs/ko/ko/developer_guide/content_cards/logging_analytics/) for more information on subscribing to card data. **Note:** Keep in mind, `BrazeKit` offers an alternative [`ContentCardRaw`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/contentcardraw) class for Objective-C compatibility. ## Card methods Each card is initialized with a `Context` object, which contains various methods for managing your card's state. Call these methods when you want to modify the corresponding state property on a particular card object. | Method | Description | |--------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------| | `card.context?.logImpression()` | Log the content card impression event. | | `card.context?.logClick()` | Log the content card click event. | | `card.context?.processClickAction()` | Process a given [`ClickAction`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/contentcard/clickaction) input. | | `card.context?.logDismissed()` | Log the content card dismissed event. | | `card.context?.logError()` | Log an error related to the content card. | | `card.context?.loadImage()` | Load a given content card image from a URL. This method can be nil when the content card does not have an image. | {: .reset-td-br-1 .reset-td-br-2 aria-label="Card methods" } For more details, refer to the [`Context` class documentation](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/contentcardraw/context-swift.class) ## Prerequisites Before you can use this feature, you'll need to [integrate the Cordova Braze SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=cordova). ## Card Feeds The Braze SDK includes a default card feed. To show the default card feed, you can use the `launchContentCards()` method. This method handles all analytics tracking, dismissals, and rendering for a user's Content Cards. ## Content Cards You can use these additional methods to build a custom Content Cards Feed within your app: |Method | Description | |---|---| |`requestContentCardsRefresh()`|Sends a background request to request the latest Content Cards from the Braze SDK server.| |`getContentCardsFromServer(successCallback, errorCallback)`|Retrieves Content Cards from the Braze SDK. This will request the latest Content Cards from the server and return the list of cards upon completion.| |`getContentCardsFromCache(successCallback, errorCallback)`|Retrieves Content Cards from the Braze SDK. This will return the latest list of cards from the local cache, which was updated at the last refresh.| |`logContentCardClicked(cardId)`|Logs a click for the given Content Card ID.| |`logContentCardImpression(cardId)`|Logs an impression for the given Content Card ID.| |`logContentCardDismissed(cardId)`|Logs a dismissal for the given Content Card ID.| {: .reset-td-br-1 .reset-td-br-2 aria-label="Content Cards" } ## About Flutter Content Cards The Braze SDK includes a default card feed to get you started with Content Cards. To show the card feed, you can use the `braze.launchContentCards()` method. The default card feed included with the Braze SDK will handle all analytics tracking, dismissals, and rendering for a user's Content Cards. ## Prerequisites Before you can use this feature, you'll need to [integrate the Flutter Braze SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=flutter). ## Card methods You can use these additional methods to build a custom Content Cards Feed within your app by using the following methods available on the [plugin public interface](https://github.com/braze-inc/braze-flutter-sdk/blob/master/lib/braze_plugin.dart): | Method | Description | | ---------------------------------------------- | ------------------------------------------------------------------------------------------------------ | | `braze.requestContentCardsRefresh()` | Requests the latest Content Cards from the Braze SDK server. | | `braze.logContentCardClicked(contentCard)` | Logs a click for the given Content Card object. | | `braze.logContentCardImpression(contentCard)` | Logs an impression for the given Content Card object. | | `braze.logContentCardDismissed(contentCard)` | Logs a dismissal for the given Content Card object. | {: .reset-td-br-1 .reset-td-br-2 aria-label="Card methods" } ## Receiving Content Card data To receive Content Card data in your Flutter app, the `BrazePlugin` supports sending Content Card data by using [Dart Streams](https://dart.dev/tutorials/language/streams). The `BrazeContentCard` [object](https://pub.dev/documentation/braze_plugin/latest/braze_plugin/BrazeContentCard-class.html) supports a subset of fields available in the native model objects, including `description`, `title`, `image`, `url`, `extras`, and more. ### Listen for Content Card data in the Dart layer To receive Content Card data in the Dart layer, use the code below to create a `StreamSubscription` and call `braze.subscribeToContentCards()`. Remember to `cancel()` the stream subscription when it is no longer needed. ```dart // Create stream subscription StreamSubscription contentCardsStreamSubscription; contentCardsStreamSubscription = braze.subscribeToContentCards((List contentCards) { // Handle Content Cards } // Cancel stream subscription contentCardsStreamSubscription.cancel(); ``` For an example, see [main.dart](https://github.com/braze-inc/braze-flutter-sdk/blob/master/example/lib/main.dart) in the Braze Flutter SDK sample application. ### Forward Content Card data from the native iOS layer Content Card data is automatically forwarded from both the Android and iOS native layers. No additional setup is required. If you're using Flutter SDK 17.1.0 or earlier, Content Card data forwarding from the iOS native layer requires manual setup. Your application likely contains a `contentCards.subscribeToUpdates` callback that calls `BrazePlugin.processContentCards(contentCards)`. To migrate to Flutter SDK 18.0.0, remove the `BrazePlugin.processContentCards(_:)` call—data forwarding is now handled automatically. For an example, see [AppDelegate.swift](https://github.com/braze-inc/braze-flutter-sdk/blob/master/example/ios/Runner/AppDelegate.swift) in the Braze Flutter SDK sample application. #### Replaying the callback for Content Cards To store any Content Cards triggered before the callback is available and replay them after it is set, add the following entry to the `customConfigs` map when initializing the `BrazePlugin`: ```dart BrazePlugin braze = new BrazePlugin(customConfigs: {replayCallbacksConfigKey: true}); ``` ## About React Native Content Cards The Braze SDKs include a default card feed to get you started with Content Cards. To show the card feed, you can use the `Braze.launchContentCards()` method. The default card feed included with the Braze SDK will handle all analytics tracking, dismissals, and rendering for a user's Content Cards. ## Prerequisites Before you can use this feature, you'll need to [integrate the React Native Braze SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=react%20native). ## Cards methods To build your own UI, you can get a list of available cards, and listen for updates to cards: ```javascript // Set initial cards const [cards, setCards] = useState([]); // Listen for updates as a result of card refreshes, such as: // a new session, a manual refresh with `requestContentCardsRefresh()`, or after the timeout period Braze.addListener(Braze.Events.CONTENT_CARDS_UPDATED, async (update) => { setCards(update.cards); }); // Manually trigger a refresh of cards Braze.requestContentCardsRefresh(); ``` **Important:** If you choose to build your own UI to display cards, you must call `logContentCardImpression` in order to receive analytics for those cards. This includes `control` cards, which must be tracked even though they are not displayed to the user. You can use these additional methods to build a custom Content Cards Feed within your app: | Method | Description | | ---------------------------------------- | ------------------------------------------------------------------------------------------------------ | | `launchContentCards()` | Launches the Content Cards UI element. | | `requestContentCardsRefresh()` | Requests the latest Content Cards from the Braze SDK server. The resulting list of cards is passed to each of the previously registered [content card event listeners](#reactnative_cards-methods). | | `getContentCards()` | Retrieves Content Cards from the Braze SDK. This returns a promise that resolves with the latest list of cards from the server. | | `getCachedContentCards()` | Returns the most recent Content Cards array from the cache. | | `logContentCardClicked(cardId)` | Logs a click for the given Content Card ID. This method is used only for analytics. To execute the click action, call `processContentCardClickAction(cardId)` in addition. | | `logContentCardImpression(cardId)` | Logs an impression for the given Content Card ID. | | `logContentCardDismissed(cardId)` | Logs a dismissal for the given Content Card ID. | | `processContentCardClickAction(cardId)` | Perform the action of a particular card. | {: .reset-td-br-1 .reset-td-br-2 aria-label="Cards methods" } ## Card types and properties The Content Cards data model is available in the React Native SDK and offers the following Content Cards card types: [Image Only](#image-only), [Captioned Image](#captioned-image), and [Classic](#classic). There's also a special [Control](#control) card type, which is returned to users that are in the control group for a given card. Each type inherits common properties from a base model in addition to its own unique properties. **Tip:** For a full reference of the Content Card data model, see the [Android](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.cards/index.html) and [iOS](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/contentcard) documentation. ### Base card model The base card model provides foundational behavior for all cards. |Property | Description | |--------------|------------------------------------------------------------------------------------------------------------------------| |`id` | The card's ID set by Braze. | |`created` | The UNIX timestamp of the card's creation time from Braze. | |`expiresAt` | The UNIX timestamp of the card's expiration time. When the value is less than 0, it means the card never expires. | |`viewed` | Whether the card is read or unread by the user. This does not log analytics. | |`clicked` | Whether the card has been clicked by the user. | |`pinned` | Whether the card is pinned. | |`dismissed` | Whether the user has dismissed this card. Marking a card as dismissed that has already been dismissed will be a no-op. | |`dismissible` | Whether the card is dismissible by the user. | |`url` | (Optional) The URL string associated with the card click action. | |`openURLInWebView` | Whether URLs for this card should be opened in the Braze WebView or not. | |`isControl` | Whether this card is a control card. Control cards should not be displayed to the user. | |`extras` | The map of key-value extras for this card. | {: .reset-td-br-1 .reset-td-br-2 aria-label="Base card model" } For a full reference of the base card, see the [Android](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.cards/-card/index.html) and [iOS](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/contentcard/data-swift.struct) documentation. ### Image only Image only cards are clickable, full-sized images. |Property | Description | |-------------------|-------------------------------------------------------------------------------------------------------------------| |`type` | The Content Card type, `IMAGE_ONLY`. | |`image` | The URL of the card's image. | |`imageAspectRatio` | The aspect ratio of the card's image. It is meant to serve as a hint before image loading completes. Note that the property may not be supplied in certain circumstances. | {: .reset-td-br-1 .reset-td-br-2 aria-label="Image only" } For a full reference of the image only card, see the [Android](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.cards/-image-only-card/index.html) and [iOS](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/contentcard/imageonly-swift.struct) documentation. ### Captioned image Captioned image cards are clickable, full-sized images with accompanying descriptive text. |Property | Description | |-------------------|-------------------------------------------------------------------------------------------------------------------| |`type` | The Content Card type, `CAPTIONED`. | |`image` | The URL of the card's image. | |`imageAspectRatio` | The aspect ratio of the card's image. It is meant to serve as a hint before image loading completes. Note that the property may not be supplied in certain circumstances. | |`title` | The title text for the card. | |`cardDescription` | The description text for the card. | |`domain` | (Optional) The link text for the property URL, for example, `"braze.com/resources/"`. It can be displayed on the card's UI to indicate the action/direction of clicking on the card. | {: .reset-td-br-1 .reset-td-br-2 aria-label="Captioned image" } For a full reference of the captioned image card, see the [Android](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.cards/-captioned-image-card/index.html) and [iOS](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/contentcard/captionedimage-swift.struct) documentation. ### Classic Classic cards have a title, description, and an optional image on the left of the text. |Property | Description | |-------------------|-------------------------------------------------------------------------------------------------------------------| |`type` | The Content Card type, `CLASSIC`. | |`image` | (Optional) The URL of the card's image. | |`title` | The title text for the card. | |`cardDescription` | The description text for the card. | |`domain` | (Optional) The link text for the property URL, for example, `"braze.com/resources/"`. It can be displayed on the card's UI to indicate the action/direction of clicking on the card. | {: .reset-td-br-1 .reset-td-br-2 aria-label="Classic" } For a full reference of the classic (text announcement) Content Card, see the [Android](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.cards/-text-announcement-card/index.html) and [iOS](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/contentcard/classic-swift.struct) documentation. For the classic image (short news) card, see the [Android](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.cards/-short-news-card/index.html) and [iOS](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/contentcard/classicimage-swift.struct) documentation. ### Control Control cards include all of the base properties, with a few important differences. Most importantly: - The `isControl` property is guaranteed to be `true`. - The `extras` property is guaranteed to be empty. For a full reference of the control card, see the [Android](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.cards/-control-card/index.html) and [iOS](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/contentcard/control-swift.struct) documentation. ## Prerequisites Before you can use Content Cards, you'll need to integrate the [Braze Swift SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=swift) into your app. Then you'll need to complete the steps for setting up your tvOS app. **Important:** Keep in mind, you'll need to implement your own custom UI since Content Cards are supported via headless UI using the Swift SDK—which does not include any default UI or views for tvOS. ## Setting up your tvOS app ### Step 1: Create a new iOS app In Braze, select **Settings** > **App Settings**, then select **Add App**. Enter a name for your tvOS app, select **iOS**—_not tvOS_—then select **Add App**. ![ALT_TEXT.](https://www.braze.com/docs/ko/ko/assets/img/tvos.png?d1c5036d5b83f425591adb03556ca684){: style="width:70%"} **Warning:** If you select the **tvOS** checkbox, you will not be able to customize Content Cards for tvOS. ### Step 2: Get your app's API key In your app settings, select your new tvOS app then take note of your app's API key. You'll use this key to configure your app in Xcode. ![ALT_TEXT](https://www.braze.com/docs/ko/ko/assets/img/tvos1.png?9851deb799c1c88a248f97bd284c91cb){: style="width:70%"} ### Step 3: Integrate BrazeKit Use your app's API key to integrate the [Braze Swift SDK](https://github.com/braze-inc/braze-swift-sdk) into your tvOS project in Xcode. You only need to integrate BrazeKit from the Braze Swift SDK. ### Step 4: Create your custom UI Because Braze doesn't provide a default UI for content cards on tvOS, you'll need to customize it yourself. For a full walkthrough, see our step-by-step tutorial: [Customizing content cards for tvOS](https://braze-inc.github.io/braze-swift-sdk/documentation/braze/content-cards-customization/). For a sample project, see [Braze Swift SDK samples](https://github.com/braze-inc/braze-swift-sdk/tree/main/Examples#contentcards-custom-ui). ## Prerequisites Before you can use this feature, you'll need to [integrate the Unity Braze SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=unity). ## Displaying Content Cards natively {#unity-content-cards-native-ui} You can display the default UI for Content Cards using the following call: ```csharp Appboy.AppboyBinding.DisplayContentCards(); ``` ## Receiving Content Card data in Unity You may register Unity game objects to be notified of incoming Content Cards. We recommend setting game object listeners from the Braze configuration editor. If you need to configure your game object listener at runtime, use `AppboyBinding.ConfigureListener()` and specify `BrazeUnityMessageType.CONTENT_CARDS_UPDATED`. Note, you will additionally need to make a call to `AppboyBinding.RequestContentCardsRefresh()` to start receiving data in your game object listener on iOS. ## Parsing Content Cards Incoming `string` messages received in your Content Cards game object callback can be parsed into our pre-supplied [`ContentCard`](https://github.com/braze-inc/braze-unity-sdk/blob/master/Assets/Plugins/Appboy/Models/Cards/ContentCard.cs) model object for convenience. Parsing Content Cards requires JSON parsing, see the following example for details: ##### Example Content Cards callback ```csharp void ExampleCallback(string message) { try { JSONClass json = (JSONClass)JSON.Parse(message); // Content Card data is contained in the `mContentCards` field of the top level object. if (json["mContentCards"] != null) { JSONArray jsonArray = (JSONArray)JSON.Parse(json["mContentCards"].ToString()); Debug.Log(String.Format("Parsed content cards array with {0} cards", jsonArray.Count)); // Iterate over the card array to parse individual cards. for (int i = 0; i < jsonArray.Count; i++) { JSONClass cardJson = jsonArray[i].AsObject; try { ContentCard card = new ContentCard(cardJson); Debug.Log(String.Format("Created card object for card: {0}", card)); // Example of logging Content Card analytics on the ContentCard object card.LogImpression(); card.LogClick(); } catch { Debug.Log(String.Format("Unable to create and log analytics for card {0}", cardJson)); } } } } catch { throw new ArgumentException("Could not parse content card JSON message."); } } ``` ## Refreshing Content Cards To refresh Content Cards from Braze, call either of the following methods: ```csharp // results in a network request to Braze AppboyBinding.RequestContentCardsRefresh() AppboyBinding.RequestContentCardsRefreshFromCache() ``` ## Analytics Clicks and impressions must be manually logged for Content Cards not displayed directly by Braze. Use `LogClick()` and `LogImpression()` on [ContentCard](https://github.com/braze-inc/braze-unity-sdk/blob/master/Assets/Plugins/Appboy/Models/Cards/ContentCard.cs) to log clicks and impressions for specific cards. ## About .NET MAUI Content Cards The Braze .NET MAUI (formerly Xamarin) SDK includes a default card feed to get you started with Content Cards. The default card feed included with the Braze SDK will handle all analytics tracking, dismissals, and rendering for a user’s Content Cards. ## Prerequisites Before you can use this feature, you'll need to [integrate the .NET MAUI Braze SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=.net%20maui%20(xamarin)). ## Card types and properties The Braze .NET MAUI SDK has three unique Content Cards card types that share a base model: [Banner](#xamarin_banner), [Captioned Image](#xamarin_captioned-image), and [Classic](#xamarin_classic). Each type inherits common properties from a base model and has the following additional properties. ### Base card model |Property | Description | |-------------------|------------------------------------------------------------------------------------------------------------------------| |`idString` | The card's ID set by Braze. | |`created` | The UNIX timestamp of the card's creation time from Braze. | |`expiresAt` | The UNIX timestamp of the card's expiration time. When the value is less than 0, it means the card never expires. | |`viewed` | Whether the card is read or unread by the user. This does not log analytics. | |`clicked` | Whether the card has been clicked by the user. | |`pinned` | Whether the card is pinned. | |`dismissed` | Whether the user has dismissed this card. Marking a card as dismissed that has already been dismissed will be a no-op. | |`dismissible` | Whether the card is dismissible by the user. | |`urlString` | (Optional) The URL string associated with the card click action. | |`openUrlInWebView` | Whether URLs for this card should be opened in the Braze WebView or not. | |`isControlCard` | Whether this card is a control card. Control cards should not be displayed to the user. | |`extras` | The map of key-value extras for this card. | |`isTest` | Whether this card is a test card. | {: .reset-td-br-1 .reset-td-br-2 aria-label="Base card model" } For a full reference of the base card, see the [Android](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.cards/-card/index.html) and [iOS](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/contentcard/data-swift.struct) documentation. ### Banner Banner cards are clickable, full-sized images. |Property | Description | |-------------------|-------------------------------------------------------------------------------------------------------------------| |`image` | The URL of the card's image. | |`imageAspectRatio` | The aspect ratio of the card's image. It is meant to serve as a hint before image loading completes. Note that the property may not be supplied in certain circumstances. | {: .reset-td-br-1 .reset-td-br-2 aria-label="Banner" } For a full reference of the banner card, see the [Android](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.cards/-image-only-card/index.html) and [iOS](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/contentcard/imageonly-swift.struct) documentation (now renamed to image only). ### Captioned image Captioned image cards are clickable, full-sized images with accompanying descriptive text. |Property | Description | |-------------------|-------------------------------------------------------------------------------------------------------------------| |`image` | The URL of the card's image. | |`imageAspectRatio` | The aspect ratio of the card's image. It is meant to serve as a hint before image loading completes. Note that the property may not be supplied in certain circumstances. | |`title` | The title text for the card. | |`cardDescription` | The description text for the card. | |`domain` | (Optional) The link text for the property URL, for example, `"braze.com/resources/"`. It can be displayed on the card's UI to indicate the action/direction of clicking on the card. | {: .reset-td-br-1 .reset-td-br-2 aria-label="Captioned image" } For a full reference of the captioned image card, see the [Android](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.cards/-captioned-image-card/index.html) and [iOS](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/contentcard/captionedimage-swift.struct) documentation. ### Classic Classic cards have a title, description, and an optional image on the left of the text. |Property | Description | |-------------------|-------------------------------------------------------------------------------------------------------------------| |`image` | (Optional) The URL of the card's image. | |`title` | The title text for the card. | |`cardDescription` | The description text for the card. | |`domain` | (Optional) The link text for the property URL, for example, `"braze.com/resources/"`. It can be displayed on the card's UI to indicate the action/direction of clicking on the card. | {: .reset-td-br-1 .reset-td-br-2 aria-label="Classic" } For a full reference of the classic (text announcement) Content Card, see the [Android](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.cards/-text-announcement-card/index.html) and [iOS](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/contentcard/classic-swift.struct) documentation. For a full reference of the classic image (short news) card, see the [Android](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.cards/-short-news-card/index.html) and [iOS](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/contentcard/classicimage-swift.struct) documentation. ## Card methods You can use these additional methods to build a custom Content Cards Feed within your app: | Method | Description | | ---------------------------------------- | ------------------------------------------------------------------------------------------------------ | | `requestContentCardsRefresh()` | Requests the latest Content Cards from the Braze SDK server. | | `getContentCards()` | Retrieves Content Cards from the Braze SDK. This will return the latest list of cards from the server. | | `logContentCardClicked(cardId)` | Logs a click for the given Content Card ID. This method is used only for analytics. | | `logContentCardImpression(cardId)` | Logs an impression for the given Content Card ID. | | `logContentCardDismissed(cardId)` | Logs a dismissal for the given Content Card ID. | {: .reset-td-br-1 .reset-td-br-2 aria-label="Card methods" } # Content Cards 만들기 Source: /docs/ko/developer_guide/content_cards/creating_cards/index.md # Content Cards 만들기 {#create-content-cards} > 이 문서에서는 커스텀 Content Cards를 구현할 때 사용할 기본 접근 방식과 세 가지 일반적인 사용 사례에 대해 설명합니다. 기본적으로 수행할 수 있는 작업과 커스텀 코드가 필요한 작업을 이해하기 위해 Content Cards 커스터마이징 가이드의 다른 문서를 이미 읽었다고 가정합니다. 커스텀 Content Cards에 대한 [분석을 기록하는](https://www.braze.com/docs/ko/ko/developer_guide/content_cards/logging_analytics/) 방법을 이해하면 특히 유용합니다. **Tip:** Using Content Cards for banner-style messages? Try out [Banners](https://www.braze.com/docs/ko/ko/user_guide/message_building_by_channel/banners/)— perfect for inline, persistent in-app and web messages. ## 카드 만들기 {#creating-a-card} ### 1단계: 커스텀 UI 만들기 {#step-1-create-a-custom-ui} 먼저 카드를 렌더링하는 데 사용할 커스텀 HTML 구성요소를 만듭니다. 먼저 나만의 커스텀 프래그먼트를 생성합니다. 기본 [`ContentCardsFragment`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.ui.contentcards/-content-cards-fragment/index.html)는 기본 Content Cards 유형만 처리하도록 설계되었지만 좋은 출발점이 될 수 있습니다. 먼저 커스텀 뷰 컨트롤러 구성요소를 만듭니다. 기본 [`BrazeContentCardUI.ViewController`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazeui/brazecontentcardui/viewcontroller)는 기본 Content Cards 유형만 처리하도록 설계되었지만 좋은 출발점이 될 수 있습니다. ### 2단계: 카드 업데이트 구독 {#step-2-subscribe-to-card-updates} 카드가 새로고침될 때 데이터 업데이트를 구독하기 위해 콜백 함수를 등록합니다. Content Cards 오브젝트를 구문 분석하고 `title`, `cardDescription`, `imageUrl` 등의 페이로드 데이터를 추출한 다음, 결과 모델 데이터를 사용하여 커스텀 UI를 채울 수 있습니다. Content Cards 데이터 모델을 얻으려면 Content Cards 업데이트를 구독합니다. 다음 등록정보에 특히 주의하세요: * **`id`:** Content Cards ID 문자열을 나타냅니다. 커스텀 Content Cards에서 분석을 기록하는 데 사용되는 고유 식별자입니다. * **`extras`:** Braze 대시보드의 모든 키-값 페어를 포함합니다. `id`와 `extras` 이외의 모든 등록정보는 커스텀 Content Cards에서 구문 분석할 필요가 없습니다. 데이터 모델에 대한 자세한 내용은 각 플랫폼의 통합 문서를 참조하세요: [Android](https://www.braze.com/docs/ko/ko/developer_guide/content_cards/?sdktab=android), [iOS](https://www.braze.com/docs/ko/ko/developer_guide/content_cards/?sdktab=swift), [Web](https://www.braze.com/docs/ko/ko/developer_guide/content_cards/?sdktab=web). ```javascript import * as braze from "@braze/web-sdk"; braze.subscribeToContentCardsUpdates((updates) => { const cards = updates.cards; // For example: cards.forEach(card => { if (card.isControl) { // Do not display the control card, but remember to call `logContentCardImpressions([card])` } else if (card instanceof braze.ClassicCard || card instanceof braze.CaptionedImage) { // Use `card.title`, `card.imageUrl`, etc. } else if (card instanceof braze.ImageOnly) { // Use `card.imageUrl`, etc. } }) }); braze.openSession(); ``` **Note:** Content Cards는 `subscribeToContentCardsUpdates()`가 `openSession()` 이전에 호출된 경우에만 세션 시작 시 새로고침됩니다. 언제든지 [피드를 수동으로 새로고침](https://www.braze.com/docs/ko/ko/developer_guide/content_cards/customizing_cards/feed/)할 수도 있습니다. #### 2a단계: 비공개 구독자 변수 만들기 {#step-2a-create-a-private-subscriber-variable} 카드 업데이트를 구독하려면 먼저 커스텀 클래스에서 구독자를 보관할 비공개 변수를 선언합니다: ```java // subscriber variable private IEventSubscriber mContentCardsUpdatedSubscriber; ``` #### 2b단계: 업데이트 구독 {#step-2b-subscribe-to-updates} 다음 코드를 추가하여 Braze의 Content Cards 업데이트를 구독합니다. 일반적으로 커스텀 Content Cards 액티비티의 `Activity.onCreate()` 내부에 배치합니다: ```java // Remove the previous subscriber before rebuilding a new one with our new activity. Braze.getInstance(context).removeSingleSubscription(mContentCardsUpdatedSubscriber, ContentCardsUpdatedEvent.class); mContentCardsUpdatedSubscriber = new IEventSubscriber() { @Override public void trigger(ContentCardsUpdatedEvent event) { // List of all Content Cards List allCards = event.getAllCards(); // Your logic below } }; Braze.getInstance(context).subscribeToContentCardsUpdates(mContentCardsUpdatedSubscriber); Braze.getInstance(context).requestContentCardsRefresh(); ``` #### 2c단계: 구독 취소 {#step-2c-unsubscribe} 커스텀 액티비티가 화면에서 벗어날 때 구독을 취소합니다. 액티비티의 `onDestroy()` 라이프사이클 메서드에 다음 코드를 추가합니다: ```java Braze.getInstance(context).removeSingleSubscription(mContentCardsUpdatedSubscriber, ContentCardsUpdatedEvent.class); ``` #### 2a단계: 비공개 구독자 변수 만들기 카드 업데이트를 구독하려면 먼저 커스텀 클래스에서 구독자를 보관할 비공개 변수를 선언합니다: ```kotlin private var contentCardsUpdatedSubscriber: IEventSubscriber? = null ``` #### 2b단계: 업데이트 구독 다음 코드를 추가하여 Braze의 Content Cards 업데이트를 구독합니다. 일반적으로 커스텀 Content Cards 액티비티의 `Activity.onCreate()` 내부에 배치합니다: ```kotlin // Remove the previous subscriber before rebuilding a new one with our new activity. Braze.getInstance(context).subscribeToContentCardsUpdates(contentCardsUpdatedSubscriber) Braze.getInstance(context).requestContentCardsRefresh() // List of all Content Cards val allCards = event.allCards // Your logic below } Braze.getInstance(context).subscribeToContentCardsUpdates(mContentCardsUpdatedSubscriber) Braze.getInstance(context).requestContentCardsRefresh(true) ``` #### 2c단계: 구독 취소 커스텀 액티비티가 화면에서 벗어날 때 구독을 취소합니다. 액티비티의 `onDestroy()` 라이프사이클 메서드에 다음 코드를 추가합니다: ```kotlin Braze.getInstance(context).removeSingleSubscription(contentCardsUpdatedSubscriber, ContentCardsUpdatedEvent::class.java) ``` Content Cards 데이터 모델에 접근하려면 `braze` 인스턴스에서 [`contentCards.cards`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/contentcards-swift.class/cards)를 호출합니다. ```swift let cards: [Braze.ContentCard] = AppDelegate.braze?.contentCards.cards ``` 또한 Content Cards의 변경 사항을 관찰하기 위해 구독을 유지할 수 있습니다. 두 가지 방법 중 하나로 수행할 수 있습니다: 1. 취소 가능 항목 유지, 또는 2. `AsyncStream` 유지. ##### 취소 가능 항목 {#cancellable} ```swift // This subscription is maintained through a Braze cancellable, which will observe for changes until the subscription is cancelled. // You must keep a strong reference to the cancellable to keep the subscription active. // The subscription is canceled either when the cancellable is deinitialized or when you call its `.cancel()` method. let cancellable = AppDelegate.braze?.contentCards.subscribeToUpdates { [weak self] contentCards in // Implement your completion handler to respond to updates in `contentCards`. } ``` ##### AsyncStream ```swift let stream: AsyncStream<[Braze.ContentCard]> = AppDelegate.braze?.contentCards.cardsStream ``` ```objc NSArray *contentCards = AppDelegate.braze.contentCards.cards; ``` 또한 Content Cards에 대한 구독을 유지하려면 [`subscribeToUpdates`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/contentcards-swift.class/subscribetoupdates(_:))를 호출할 수 있습니다: ```objc // This subscription is maintained through Braze cancellable, which will continue to observe for changes until the subscription is cancelled. BRZCancellable *cancellable = [self.braze.contentCards subscribeToUpdates:^(NSArray *contentCards) { // Implement your completion handler to respond to updates in `contentCards`. }]; ``` ### 3단계: 분석 구현 {#step-3-implement-analytics} Content Cards 노출 횟수, 클릭 및 해제는 커스텀 뷰에서 자동으로 기록되지 않습니다. 모든 측정기준을 Braze 대시보드 분석에 올바르게 기록하려면 [각각의 메서드를 구현](https://www.braze.com/docs/ko/ko/developer_guide/content_cards/logging_analytics/)해야 합니다. ### 4단계: 카드 테스트(선택 사항) {#step-4-test-your-card-optional} Content Cards를 테스트하려면: 1. [`changeUser()`](https://js.appboycdn.com/web-sdk/latest/doc/modules/braze.html#changeuser) 메서드를 호출하여 애플리케이션에서 활성 사용자를 설정합니다. 2. Braze에서 **Campaigns**로 이동한 다음 [새 콘텐츠 카드 캠페인을 만듭니다](https://www.braze.com/docs/ko/ko/user_guide/channels/content_cards/create_a_content_card/). 3. 캠페인에서 **Test**를 선택한 다음 테스트 사용자의 `user-id`를 입력합니다. 준비가 완료되면 **Send Test**를 선택합니다. 곧 기기에서 Content Cards를 실행할 수 있습니다. ![Braze Content Cards 캠페인에서 테스트 수신자로 자신의 사용자 ID를 추가하여 Content Cards를 테스트할 수 있습니다.](https://www.braze.com/docs/ko/ko/assets/img/react-native/content-card-test.png?f0066f72bf53ec72ed55c34856064ac6 "Content Card Campaign Test") ## Content Cards 배치 {#content-card-placements} Content Cards는 다양한 방식으로 사용할 수 있습니다. 세 가지 일반적인 구현 방법은 메시지 센터, 동적 이미지 광고 또는 이미지 캐러셀로 사용하는 것입니다. 이러한 각 배치에 대해 [키-값 페어](https://www.braze.com/docs/ko/ko/developer_guide/customization_guides/content_cards/customizing_behavior/#key-value-pairs)(데이터 모델의 `extras` 등록정보)를 Content Cards에 할당하고, 값에 따라 런타임 중에 카드의 동작, 모양 또는 기능을 동적으로 조정합니다. ![](https://www.braze.com/docs/ko/ko/assets/img_archive/cc_placements.png?1d164a98534752857c2faae74733bb03){: style="border:0px;"} ### 메시지 받은편지함 {#message-inbox} Content Cards를 사용하여 메시지 센터를 시뮬레이션할 수 있습니다. 이 형식에서 각 메시지는 클릭 시 이벤트를 구동하는 [키-값 페어](https://www.braze.com/docs/ko/ko/developer_guide/customization_guides/content_cards/customizing_behavior/#key-value-pairs)를 포함하는 고유한 카드입니다. 이러한 키-값 페어는 사용자가 받은편지함 메시지를 클릭할 때 이동 위치를 결정할 때 애플리케이션이 확인하는 핵심 식별자입니다. 키-값 페어의 값은 임의로 지정할 수 있습니다. #### 예시 {#example} 예를 들어, 사용자가 읽기 추천을 활성화하도록 유도하는 콜투액션과 신규 가입자 Segment에 제공되는 쿠폰 코드 등 두 가지 메시지 카드를 만들 수 있습니다. `body`, `title`, `buttonText`와 같은 키에는 마케터가 설정할 수 있는 간단한 문자열 값이 있을 수 있습니다. `terms` 같은 키에는 법무 부서에서 승인한 작은 문구 모음을 제공하는 값이 있을 수 있습니다. `style` 및 `class_type` 같은 키에는 앱이나 사이트에서 카드가 렌더링되는 방식을 결정하기 위해 설정할 수 있는 문자열 값이 있습니다. 읽기 추천 카드의 키-값 페어: | 키 | 값 | |------------|----------------------------------------------------------------------| | `body` | Add your interests to your Politer Weekly profile for personal reading recommendations. | | `style` | info | | `class_type` | notification_center | | `card_priority` | 1 | {: .reset-td-br-1 .reset-td-br-2 role="presentation" } 신규 가입자 쿠폰의 키-값 페어: | 키 | 값 | |------------|------------------------------------------------------------------| | `title` | Subscribe for unlimited games | | `body` | End of Summer Special - Enjoy 10% off Politer games | | `buttonText` | Subscribe Now | | `style` | promo | | `class_type` | notification_center | | `card_priority` | 2 | | `terms` | new_subscribers_only | {: .reset-td-br-1 .reset-td-br-2 role="presentation" } **Android 추가 정보** Android 및 FireOS SDK에서 메시지 센터 로직은 Braze의 키-값 페어로 제공되는 `class_type` 값에 의해 구동됩니다. [`createContentCardable`](https://www.braze.com/docs/ko/ko/developer_guide/content_cards/) 메서드를 사용하여 이러한 클래스 유형을 필터링하고 식별할 수 있습니다. **클릭 시 동작에 `class_type` 사용**
Content Cards 데이터를 커스텀 클래스로 인플레이션할 때 데이터의 `ContentCardClass` 등록정보를 사용하여 데이터를 저장하는 데 사용해야 하는 구체적인 서브클래스를 결정합니다. ```kotlin private fun createContentCardable(metadata: Map, type: ContentCardClass?): ContentCardable?{ return when(type){ ContentCardClass.AD -> Ad(metadata) ContentCardClass.MESSAGE_WEB_VIEW -> WebViewMessage(metadata) ContentCardClass.NOTIFICATION_CENTER -> FullPageMessage(metadata) ContentCardClass.ITEM_GROUP -> Group(metadata) ContentCardClass.ITEM_TILE -> Tile(metadata) ContentCardClass.COUPON -> Coupon(metadata) else -> null } } ``` 그런 다음 메시지 목록에 대한 사용자 상호작용을 처리할 때 메시지 유형을 사용하여 사용자에게 표시할 뷰를 결정할 수 있습니다. ```kotlin override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) //... listView.onItemClickListener = AdapterView.OnItemClickListener { parent, view, position, id -> when (val card = dataProvider[position]){ is WebViewMessage -> { val intent = Intent(this, WebViewActivity::class.java) val bundle = Bundle() bundle.putString(WebViewActivity.INTENT_PAYLOAD, card.contentString) intent.putExtras(bundle) startActivity(intent) } is FullPageMessage -> { val intent = Intent(this, FullPageContentCard::class.java) val bundle = Bundle() bundle.putString(FullPageContentCard.CONTENT_CARD_IMAGE, card.icon) bundle.putString(FullPageContentCard.CONTENT_CARD_TITLE, card.messageTitle) bundle.putString(FullPageContentCard.CONTENT_CARD_DESCRIPTION, card.cardDescription) intent.putExtras(bundle) startActivity(intent) } } } } ``` **클릭 시 동작에 `class_type` 사용**
Content Cards 데이터를 커스텀 클래스로 인플레이션할 때 데이터의 `ContentCardClass` 등록정보를 사용하여 데이터를 저장하는 데 사용해야 하는 구체적인 서브클래스를 결정합니다. ```java private ContentCardable createContentCardable(Map metadata, ContentCardClass type){ switch(type){ case ContentCardClass.AD:{ return new Ad(metadata); } case ContentCardClass.MESSAGE_WEB_VIEW:{ return new WebViewMessage(metadata); } case ContentCardClass.NOTIFICATION_CENTER:{ return new FullPageMessage(metadata); } case ContentCardClass.ITEM_GROUP:{ return new Group(metadata); } case ContentCardClass.ITEM_TILE:{ return new Tile(metadata); } case ContentCardClass.COUPON:{ return new Coupon(metadata); } default:{ return null; } } } ``` 그런 다음 메시지 목록에 대한 사용자 상호작용을 처리할 때 메시지 유형을 사용하여 사용자에게 표시할 뷰를 결정할 수 있습니다. ```java @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState) //... listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView parent, View view, int position, long id){ ContentCardable card = dataProvider.get(position); if (card instanceof WebViewMessage){ Bundle intent = new Intent(this, WebViewActivity.class); Bundle bundle = new Bundle(); bundle.putString(WebViewActivity.INTENT_PAYLOAD, card.getContentString()); intent.putExtras(bundle); startActivity(intent); } else if (card instanceof FullPageMessage){ Intent intent = new Intent(this, FullPageContentCard.class); Bundle bundle = Bundle(); bundle.putString(FullPageContentCard.CONTENT_CARD_IMAGE, card.getIcon()); bundle.putString(FullPageContentCard.CONTENT_CARD_TITLE, card.getMessageTitle()); bundle.putString(FullPageContentCard.CONTENT_CARD_DESCRIPTION, card.getCardDescription()); intent.putExtras(bundle) startActivity(intent) } } }); } ``` ### 캐러셀 {#carousel} 완전 커스텀 캐러셀 피드에서 Content Cards를 설정하여 사용자가 스와이프하여 추가 추천 카드를 볼 수 있도록 할 수 있습니다. 기본적으로 Content Cards는 생성 날짜(최신 것부터)를 기준으로 정렬되며, 사용자는 자신이 받을 수 있는 모든 카드를 볼 수 있습니다. Content Cards 캐러셀을 구현하려면: 1. [Content Cards의 변경 사항](https://www.braze.com/docs/ko/ko/developer_guide/customization_guides/content_cards/customizing_feed/#refreshing-the-feed)을 관찰하고 Content Cards 도착을 처리하는 커스텀 로직을 만듭니다. 2. 커스텀 클라이언트 측 로직을 생성하여 캐러셀에 특정 개수의 카드를 한 번에 표시합니다. 예를 들어 배열에서 처음 다섯 개의 Content Cards 오브젝트를 선택하거나 키-값 페어를 도입하여 조건 로직을 구축할 수 있습니다. **Tip:** 캐러셀을 보조 Content Cards 피드로 구현하는 경우 [키-값 페어를 사용하여 카드를 올바른 피드에 정렬](https://www.braze.com/docs/ko/ko/developer_guide/customization_guides/content_cards/customizing_feed/#multiple-feeds)해야 합니다. ### 이미지 전용 {#image-only} Content Cards가 꼭 "카드"처럼 보일 필요는 없습니다. 예를 들어 Content Cards는 홈페이지 또는 지정된 페이지의 상단에 지속적으로 표시되는 동적 이미지로 나타날 수 있습니다. 이를 위해 마케터는 **이미지 전용** 유형의 Content Cards로 캠페인 또는 캔버스 단계를 만듭니다. 그런 다음, [Content Cards를 보조 콘텐츠](https://www.braze.com/docs/ko/ko/developer_guide/customization_guides/content_cards/customizing_behavior/#content-cards-as-supplemental-content)로 사용하기에 적합한 키-값 페어를 설정합니다. # 카드 커스터마이징 Source: /docs/ko/developer_guide/content_cards/customizing_cards/index.md **Tip:** Using Content Cards for banner-style messages? Try out [Banners](https://www.braze.com/docs/ko/ko/user_guide/message_building_by_channel/banners/)— perfect for inline, persistent in-app and web messages. # Content Cards 스타일 커스터마이즈 Source: /docs/ko/developer_guide/content_cards/customizing_cards/style/index.md # Content Cards 스타일 커스터마이즈 {#customize-the-style-of-content-cards} > Braze Content Cards에는 기본 모양과 느낌이 제공됩니다. 이 문서에서는 브랜드 아이덴티티에 맞게 Content Cards의 스타일을 지정하는 옵션을 다룹니다. 콘텐츠 카드 유형의 전체 목록은 [Content Cards 정보](https://www.braze.com/docs/ko/ko/developer_guide/content_cards/)를 참조하세요. ## 커스텀 스타일 생성 {#creating-a-custom-style} 기본 Content Cards UI는 Braze SDK의 UI 레이어에서 가져옵니다. 여기에서 카드의 스타일, 카드가 표시되는 순서, 사용자에게 피드가 표시되는 방식 등 특정 부분을 조정할 수 있습니다. ![기본 글꼴과 사각형 모서리가 있는 콘텐츠 카드와 둥근 모서리와 곡선 글꼴이 있는 콘텐츠 카드 두 개](https://www.braze.com/docs/ko/ko/assets/img/content_cards/content-card-customization-attributes.png?7b3bf29220daf2c82bb590aa371309d1) **Note:** `title`, `cardDescription`, `imageUrl` 등과 같은 Content Cards 속성은 [대시보드](https://www.braze.com/docs/ko/ko/user_guide/channels/content_cards/creative_details/)를 통해 직접 편집할 수 있으며, 이러한 세부 정보를 변경할 때 권장되는 방법입니다. Braze의 기본 스타일은 Braze SDK 내 CSS에서 정의됩니다. 애플리케이션에서 선택한 스타일을 재정의하면 배경 이미지, 글꼴 패밀리, 스타일, 크기, 애니메이션 등으로 표준 피드를 커스터마이즈할 수 있습니다. 예를 들어, 다음은 Content Cards가 800px 너비로 표시되도록 하는 재정의 예시입니다: ``` css body .ab-feed { width: 800px; } ``` 수정할 수 있는 속성의 전체 목록은 [Braze의 SDK 구성 옵션](https://js.appboycdn.com/web-sdk/latest/doc/modules/braze.html)을 참조하세요. 기본적으로 Android 및 FireOS SDK Content Cards는 표준 Android UI 가이드라인을 따르므로 매끄러운 경험을 제공합니다. 이러한 기본 스타일은 Braze SDK 배포의 [`res/values/styles.xml`](https://github.com/braze-inc/braze-android-sdk/blob/master/android-sdk-ui/src/main/res/values/styles.xml) 파일에서 확인할 수 있습니다: ```xml ``` Content Cards 스타일을 커스터마이즈하려면 이 기본 스타일을 재정의합니다. 스타일을 재정의하려면 프로젝트의 `styles.xml` 파일에 전체 스타일을 복사한 후 수정합니다. 모든 속성이 올바르게 설정되려면 전체 스타일을 로컬 `styles.xml` 파일에 복사해야 합니다. ```xml ``` ```xml ``` 기본적으로 Android 및 FireOS SDK Content Cards는 표준 Android UI 가이드라인을 따르므로 매끄러운 경험을 제공합니다. 두 가지 방법 중 하나로 스타일링을 적용할 수 있습니다. 첫 번째는 다음 예시처럼 [`ContentCardListStyling`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.jetpackcompose.contentcards.styling/-content-card-list-styling/index.html)과 [`ContentCardStyling`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.jetpackcompose.contentcards.styling/-content-card-styling/index.html)을 [`ContentCardsList`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.jetpackcompose.contentcards/-content-cards-list.html)에 전달하는 것입니다: ```kotlin ContentCardsList( style = ContentCardListStyling(listBackgroundColor = Color.Red), cardStyle = ContentCardStyling( titleTextStyle = TextStyle( fontFamily = fontFamily, fontSize = 25.sp ), shadowRadius = 10.dp, shortNewsContentCardStyle = BrazeShortNewsContentCardStyling( shadowRadius = 15.dp ) ) ) ``` 두 번째는 다음 예시처럼 [`BrazeStyle`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.jetpackcompose/-braze-style.html)을 사용하여 Braze 구성요소에 대한 글로벌 스타일링을 만드는 것입니다: ```kotlin BrazeStyle( contentCardStyle = ContentCardStyling( textAnnouncementContentCardStyle = BrazeTextAnnouncementContentCardStyling( cardBackgroundColor = Color.Red, descriptionTextStyle = TextStyle( fontFamily = fontFamily, fontSize = 25.sp, ) ), titleTextColor = Color.Magenta ) ) { // Your app here, including any ContentCardsList() in it } ``` Content Cards 뷰 컨트롤러를 사용하면 [`BrazeContentCardUI.ViewController.Attributes`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazeui/brazecontentcardui/viewcontroller/attributes-swift.struct) 구조체를 통해 모든 셀의 모양과 동작을 커스터마이즈할 수 있습니다. `Attributes`를 사용한 Content Cards 구성은 가장 간편한 옵션으로, 최소한의 설정만으로 Content Cards UI를 시작할 수 있습니다. **Important:** `Attributes`를 통한 커스터마이즈는 Swift에서만 가능합니다. **`Attributes.default` 수정** 정적 [`Attributes.defaults`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazeui/brazecontentcardui/viewcontroller/attributes-swift.struct/defaults) 변수를 직접 수정하여 Braze Content Cards UI 뷰 컨트롤러의 모든 인스턴스에 대한 모양과 느낌을 커스터마이즈합니다. 예를 들어 모든 셀의 기본 이미지 크기와 모서리 반경을 변경하려면 다음과 같이 합니다: ```swift BrazeContentCardUI.ViewController.Attributes.defaults.cellAttributes.cornerRadius = 20 BrazeContentCardUI.ViewController.Attributes.defaults.cellAttributes.classicImageSize = CGSize(width: 65, height: 65) ``` **Attributes를 사용하여 뷰 컨트롤러 초기화** Braze Content Cards UI 뷰 컨트롤러의 특정 인스턴스만 수정하려면 [`init(braze:attributes:)`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazeui/brazecontentcardui/viewcontroller/init(braze:attributes:)/) 이니셜라이저를 사용하여 커스텀 `Attributes` 구조체를 뷰 컨트롤러에 전달하세요. 예를 들어 뷰 컨트롤러의 특정 인스턴스에 대한 이미지 크기와 모서리 반경을 변경할 수 있습니다: ```swift var attributes = BrazeContentCardUI.ViewController.Attributes.defaults attributes.cellAttributes.cornerRadius = 20 attributes.cellAttributes.classicImageSize = CGSize(width: 65, height: 65) let viewController = BrazeContentCardUI.ViewController(braze: AppDelegate.braze, attributes: attributes) ``` **서브클래싱으로 셀 커스터마이즈** 또는 원하는 각 카드 유형에 대해 커스텀 클래스를 등록하여 커스텀 인터페이스를 만들 수도 있습니다. 기본 셀 대신 서브클래스를 사용하려면 `Attributes` 구조체의 [`cells`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazeui/brazecontentcardui/viewcontroller/attributes-swift.struct/cells) 속성을 수정합니다. 예를 들어: ```swift var attributes = BrazeContentCardUI.ViewController.Attributes.defaults // Register your own custom cell attributes.cells[BrazeContentCardUI.ClassicImageCell.identifier] = CustomClassicImageCell.self let viewController = BrazeContentCardUI.ViewController(braze: AppDelegate.braze, attributes: attributes) ``` **프로그래밍 방식으로 Content Cards 수정** `Attributes` 구조체에서 [`transform`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazeui/brazecontentcardui/viewcontroller/attributes-swift.struct/transform) 클로저를 할당하여 Content Cards를 프로그래밍 방식으로 변경할 수 있습니다. 아래 예시에서는 호환되는 카드의 `title`과 `description`을 수정합니다: ```swift var attributes = BrazeContentCardUI.ViewController.Attributes.defaults attributes.transform = { cards in cards.map { card in var card = card if let title = card.title { card.title = "[modified] \(title)" } if let description = card.description { card.description = "[modified] \(description)" } return card } } let viewController = BrazeContentCardUI.ViewController(braze: AppDelegate.braze, attributes: attributes) ``` 전체 예시는 [예시 샘플 앱](https://github.com/braze-inc/braze-swift-sdk/tree/main/Examples/Swift)에서 확인하세요. `Attributes`를 통한 Content Cards 커스터마이즈는 Objective-C에서 지원되지 않습니다. ## 커스터마이즈 예시 {#customization-examples} ### 커스텀 글꼴 {#custom-font} Content Cards에 사용되는 글꼴을 커스터마이즈하면 브랜드 아이덴티티를 유지하고 사용자에게 시각적으로 매력적인 경험을 제공할 수 있습니다. 다음 레시피를 사용하여 모든 Content Cards의 글꼴을 프로그래밍 방식으로 설정할 수 있습니다. 다른 웹 요소와 마찬가지로 CSS를 통해 Content Cards의 모양을 쉽게 커스터마이즈할 수 있습니다. CSS 파일 또는 인라인 스타일에서 `font-family` 속성을 사용하여 원하는 글꼴 이름 또는 글꼴 스택을 지정합니다. ```css /* CSS selector targeting the Content Card element */ .card-element { font-family: "Helvetica Neue", Arial, sans-serif; } ``` 프로그래밍 방식으로 기본 글꼴을 변경하려면 카드의 스타일을 설정하고 `fontFamily` 속성을 사용하여 Braze에 커스텀 글꼴 패밀리를 사용하도록 지시합니다. 예를 들어 캡션 이미지 카드의 모든 제목에서 글꼴을 업데이트하려면 `Braze.ContentCards.CaptionedImage.Title` 스타일을 재정의하고 커스텀 글꼴 패밀리를 참조합니다. 속성 값은 `res/font` 디렉터리에 있는 글꼴 패밀리를 가리켜야 합니다. 다음은 마지막 줄에서 커스텀 글꼴 패밀리 `my_custom_font_family`를 참조하는 축약된 예시입니다: ```xml ``` Android SDK의 글꼴 커스터마이즈에 대한 자세한 내용은 [글꼴 패밀리 가이드](https://www.braze.com/docs/ko/ko/developer_guide/platform_integration_guides/android/advanced_use_cases/font_customization/#font-customization)를 참조하세요. 기본 글꼴을 프로그래밍 방식으로 변경하려면 `ContentCardStyling`의 [`titleTextStyle`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.jetpackcompose.contentcards.styling/-content-card-styling/index.html#715371549%2FProperties%2F-1725759721)을 설정할 수 있습니다. 특정 카드 유형에 대해 `titleTextStyle`을 설정하려면 [`BrazeShortNewsContentCardStyling`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.jetpackcompose.contentcards.styling/-braze-short-news-content-card-styling/index.html)에서 설정한 후 `ContentCardStyling`의 [`shortNewsContentCardStyle`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.jetpackcompose.contentcards.styling/-content-card-styling/index.html#8580250%2FProperties%2F-1725759721)로 전달할 수도 있습니다. ```kotlin val fontFamily = FontFamily( Font(R.font.sailec_bold) ) ContentCardStyling( titleTextStyle = TextStyle( fontFamily = fontFamily ) ) ``` [`cellAttributes`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazeui/brazecontentcardui/viewcontroller/attributes-swift.struct/cellattributes/) 인스턴스 속성의 `Attributes`를 커스터마이즈하여 글꼴을 변경합니다. 예를 들어: ```swift var attributes = BrazeContentCardUI.ViewController.Attributes.defaults attributes.cellAttributes.titleFont = .preferredFont(textStyle: .callout, weight: .bold) attributes.cellAttributes.descriptionFont = .preferredFont(textStyle: .footnote, weight: .regular) attributes.cellAttributes.domainFont = .preferredFont(textStyle: .footnote, weight: .medium) let viewController = BrazeContentCardUI.ViewController.init(braze: braze, attributes: attributes) ``` `Attributes`를 통한 글꼴 커스터마이즈는 Objective-C에서 지원되지 않습니다. 커스텀 글꼴로 직접 UI를 구축하는 예시는 [예시 샘플 앱](https://github.com/braze-inc/braze-swift-sdk/blob/main/Examples/ObjC/Sources/ContentCards-Custom-UI/CardsInfoViewController.m#L97)에서 확인하세요. ### 커스텀 고정 아이콘 {#custom-pinned-icons} Content Cards를 만들 때 마케터는 카드를 고정할 수 있습니다. 고정 카드는 사용자의 피드 상단에 표시되며, 사용자가 해제할 수 없습니다. 카드 스타일을 커스터마이즈할 때 고정 아이콘의 모양도 변경할 수 있습니다. !["이 카드를 피드 상단에 고정" 옵션이 선택된 상태의 모바일 및 웹용 Braze Content Cards 미리보기를 나란히 표시합니다.](https://www.braze.com/docs/ko/ko/assets/img/cc_pin_to_top.png?a48fd827da3c7adb662f8aece660f43f){:style="border:none"} Content Cards 고정 아이콘의 구조는 다음과 같습니다: ```css
``` 다른 FontAwesome 아이콘을 사용하려면 `i` 요소의 클래스 이름을 원하는 아이콘의 클래스 이름으로 바꾸면 됩니다. 아이콘을 완전히 변경하려면 `i` 요소를 제거하고 커스텀 아이콘을 `ab-pinned-indicator`의 자식으로 추가하세요. 아이콘을 변경하는 방법은 여러 가지가 있지만, 한 가지 간단한 방법은 `ab-pinned-indicator` 요소에서 `replaceChildren()`을 사용하는 것입니다. 예를 들어: ```javascript // Get the parent element const pinnedIndicator = document.querySelector('.ab-pinned-indicator'); // Create a new custom icon element const customIcon = document.createElement('span'); customIcon.classList.add('customIcon'); // Replace the existing icon with the custom icon pinnedIndicator.replaceChildren(customIcon); ``` 커스텀 고정 아이콘을 설정하려면 `Braze.ContentCards.PinnedIcon` 스타일을 재정의합니다. 커스텀 이미지 자산은 `android:src` 요소에 선언해야 합니다. 예를 들어: ```xml ``` 기본 고정 아이콘을 변경하려면 `ContentCardStyling`의 [`pinnedResourceId`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.jetpackcompose.contentcards.styling/-content-card-styling/index.html#794044424%2FProperties%2F-1725759721)를 설정할 수 있습니다. 예를 들어: ```kotlin ContentCardStyling( pinnedResourceId = R.drawable.pushpin, pinnedImageAlignment = Alignment.TopCenter ) ``` `ContentCardStyling`의 [`pinnedComposable`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.jetpackcompose.contentcards.styling/-content-card-styling/index.html#1460938052%2FProperties%2F-1725759721)에서 Composable을 지정할 수도 있습니다. `pinnedComposable`이 지정되면 `pinnedResourceId` 값을 재정의합니다. ```kotlin ContentCardStyling( pinnedComposable = { Box(Modifier.fillMaxWidth()) { Text( modifier = Modifier .align(Alignment.Center) .width(50.dp), text = "This message is not read. Please read it." ) } } ) ``` [`cellAttributes`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazeui/brazecontentcardui/viewcontroller/attributes-swift.struct/cellattributes/) 인스턴스 속성의 `pinIndicatorColor` 및 `pinIndicatorImage` 속성을 수정하여 고정 아이콘을 커스터마이즈합니다. 예를 들어: ```swift var attributes = BrazeContentCardUI.ViewController.Attributes.defaults attributes.cellAttributes.pinIndicatorColor = .red attributes.cellAttributes.pinIndicatorImage = UIImage(named: "my-image") let viewController = BrazeContentCardUI.ViewController.init(braze: braze, attributes: attributes) ``` 서브클래싱을 사용하여 고정 표시기가 포함된 `BrazeContentCardUI.Cell`의 커스텀 버전을 만들 수도 있습니다. 예를 들어: ```swift var attributes = BrazeContentCardUI.ViewController.Attributes.defaults attributes.cells[BrazeContentCardUI.ClassicImageCell.identifier] = CustomClassicImageCell.self let viewController = BrazeContentCardUI.ViewController(braze: AppDelegate.braze, attributes: attributes) ``` `Attributes`를 통한 고정 표시기 커스터마이즈는 Objective-C에서 지원되지 않습니다. ### 읽지 않음 표시기 색상 변경 {#changing-the-unread-indicator-color} Content Cards 하단에는 카드 열람 여부를 나타내는 파란색 선이 표시됩니다. ![두 개의 Content Cards가 나란히 표시됩니다. 첫 번째 카드 하단에는 아직 열람하지 않았음을 나타내는 파란색 선이 있습니다. 두 번째 카드에는 파란색 선이 없으며 이미 열람했음을 나타냅니다.](https://www.braze.com/docs/ko/ko/assets/img/braze-content-cards-seen-unseen-behavior.png?00ecd6c2252753cde9662110012ab680) 카드의 읽지 않음 표시기 색상을 변경하려면 웹 페이지에 커스텀 CSS를 추가하세요. 예를 들어, 미열람 표시기의 색상을 녹색으로 설정하려면 다음과 같이 합니다: ```css .ab-unread-indicator { background-color: green; } ``` `colors.xml` 파일에서 `com_braze_content_cards_unread_bar_color` 값을 변경하여 읽지 않음 표시줄의 색상을 변경합니다: ```xml #1676d0 ``` 읽지 않음 표시줄의 색상을 변경하려면 `ContentCardStyling`에서 [`unreadIndicatorColor`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.jetpackcompose.contentcards.styling/-content-card-styling/index.html#-1669590042%2FProperties%2F-1725759721) 값을 수정합니다: ```kotlin ContentCardStyling( unreadIndicatorColor = Color.Red ) ``` `BrazeContentCardUI.ViewController` 인스턴스의 틴트 색상에 값을 할당하여 읽지 않음 표시줄의 색상을 변경합니다: ```swift let viewController = BrazeContentCardUI.ViewController(braze: AppDelegate.braze) viewController.view.tintColor = .systemGreen ``` 미열람 표시기만 수정하려면 `BrazeContentCardUI.ViewController.Attributes` 구조체의 `unviewedIndicatorColor` 속성에 접근할 수 있습니다. Braze `UITableViewCell` 구현을 사용하는 경우 셀이 그려지기 전에 속성에 접근하세요. 예를 들어, 미열람 표시기의 색상을 빨간색으로 설정하려면 다음과 같이 합니다: ```swift var attributes = BrazeContentCardUI.ViewController.Attributes.defaults attributes.cellAttributes.unviewedIndicatorColor = .red let viewController = BrazeContentCardUI.ViewController(braze: AppDelegate.braze, attributes: attributes) ``` 전체 예시는 [예시 샘플 앱](https://github.com/braze-inc/braze-swift-sdk/tree/main/Examples/Swift)에서 확인하세요. `BRZContentCardUIViewController`의 틴트 색상에 값을 할당하여 읽지 않음 표시줄의 색상을 변경합니다: ```objc BRZContentCardUIViewController *viewController = [[BRZContentCardUIViewController alloc] initWithBraze:AppDelegate.braze]; [viewController.view setTintColor:[UIColor systemGreenColor]]; ``` `Attributes`를 통해 미열람 표시기만 커스터마이즈하는 기능은 Objective-C에서 지원되지 않습니다. ### 다크 모드 {#dark-mode} 기기의 다크 모드 또는 라이트 모드에 따라 다른 이미지나 스타일을 표시하려면 Content Cards 메시지에서 [키-값 페어](https://www.braze.com/docs/ko/ko/user_guide/message_building_by_channel/content_cards/creative_details/#key-value-pairs)를 사용하세요. 예를 들어, 다크 모드 이미지 자산의 URL과 함께 `dark_mode_image`와 같은 키-값 페어를 추가합니다. 그런 다음 앱에서 기기의 현재 외관 모드를 확인하고 적절한 이미지를 표시하는 커스텀 로직을 추가합니다. ```swift if let darkImageUrl = card.extras["dark_mode_image"], view.traitCollection.userInterfaceStyle == .dark { // Use darkImageUrl for the image } ``` ```kotlin val darkModeImage = card.extras["dark_mode_image"] val isDarkMode = (resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES if (isDarkMode && darkModeImage != null) { // Use darkModeImage for the image } ``` ```javascript const darkModeImage = card.extras?.dark_mode_image; const isDarkMode = window.matchMedia("(prefers-color-scheme: dark)").matches; if (isDarkMode && darkModeImage) { // Use darkModeImage for the image } ``` 이 패턴은 텍스트, 색상, 레이아웃 등 외관에 따라 달라지는 모든 콘텐츠에 적용됩니다. 다크 모드 이미지 자산을 [미디어 라이브러리](https://www.braze.com/docs/ko/ko/user_guide/messaging/design_and_edit/media_library/image_specifications/)에 업로드한 다음 키-값 페어에서 참조하세요. ### 읽지 않음 표시기 비활성화 {#disabling-unread-indicator} `css`에 다음 스타일을 추가하여 읽지 않음 표시줄을 숨깁니다: ```css .ab-unread-indicator { display: none; } ``` `ContentCardViewHolder`에서 [`setUnreadBarVisible`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.ui.contentcards.view/-content-card-view-holder/set-unread-bar-visible.html?query=fun%20setUnreadBarVisible(isVisible:%20Boolean))을 `false`로 설정하여 읽지 않음 표시줄을 숨깁니다. 읽지 않음 표시기 비활성화는 Jetpack Compose에서 지원되지 않습니다. `Attributes` 구조체의 `attributes.cellAttributes.unviewedIndicatorColor` 속성을 `.clear`로 설정하여 읽지 않음 표시줄을 숨깁니다. `Attributes`를 통해 미열람 표시기만 커스터마이즈하는 기능은 Objective-C에서 지원되지 않습니다. # Content Cards의 동작 커스터마이즈 Source: /docs/ko/developer_guide/content_cards/customizing_cards/behavior/index.md # Content Cards의 동작 커스터마이즈 {#customize-the-behavior-of-content-cards} > 이 구현 가이드에서는 Content Cards의 동작 변경, 페이로드에 키-값 페어와 같은 추가 항목 추가, 일반적인 커스터마이즈 레시피에 대해 설명합니다. 콘텐츠 카드 유형의 전체 목록은 [Content Cards 정보](https://www.braze.com/docs/ko/ko/developer_guide/content_cards/)를 참조하세요. ## 키-값 페어 {#key-value-pairs} Braze를 사용하면 키-값 페어를 사용하여 Content Cards를 통해 사용자 기기에 추가 데이터 페이로드를 전송할 수 있습니다. 이를 통해 내부 측정기준을 추적하고, 앱 콘텐츠를 업데이트하고, 등록정보를 커스터마이즈할 수 있습니다. [대시보드를 사용하여 키-값 페어를 추가합니다](https://www.braze.com/docs/ko/ko/user_guide/message_building_by_channel/content_cards/create/#step-4-configure-additional-settings-optional). **Note:** 중첩된 JSON 값을 키-값 페어로 전송하는 것은 권장하지 않습니다. 대신 JSON을 보내기 전에 평탄화하세요. 키-값 페어는 `card` 오브젝트에 `extras`로 저장됩니다. 애플리케이션에서 추가 처리를 위해 카드와 함께 데이터를 전송하는 데 사용할 수 있습니다. `card.extras`를 호출하여 이러한 값에 액세스합니다. 키-값 페어는 `card` 오브젝트에 `extras`로 저장됩니다. 애플리케이션에서 추가 처리를 위해 카드와 함께 데이터를 전송하는 데 사용할 수 있습니다. `card.extras` 를 호출하여 이러한 값에 액세스합니다. 키-값 페어는 `card` 오브젝트에 `extras`로 저장됩니다. 애플리케이션에서 추가 처리를 위해 카드와 함께 데이터를 전송하는 데 사용할 수 있습니다. `card.extras` 를 호출하여 이러한 값에 액세스합니다. **Tip:** 마케팅 팀과 개발자 팀이 어떤 키-값 페어를 사용할지 조율하는 것이 중요합니다(예: `feed_type = brand_homepage`). 마케터가 Braze 대시보드에 입력하는 키-값 페어는 개발자가 앱 로직에 구축하는 키-값 페어와 정확히 일치해야 하기 때문입니다. ## 보조 콘텐츠로서의 Content Cards {#content-cards-as-supplemental-content} ![](https://www.braze.com/docs/ko/ko/assets/img/cc_implementation/supplementary.png?04f6645c99fe71ac7abb4cba8a46ccbd){: style="float:right;max-width:25%;margin-left:15px;border:0;"} Content Cards를 기존 피드에 원활하게 혼합하여 여러 피드의 데이터를 동시에 로드할 수 있습니다. 이를 통해 Braze Content Cards와 기존 피드 콘텐츠가 일관되고 조화로운 경험을 만들어 냅니다. 오른쪽 예시는 로컬 데이터와 Braze에서 제공하는 Content Cards로 채워지는 하이브리드 항목 목록이 포함된 피드를 보여줍니다. 이를 통해 Content Cards를 기존 콘텐츠와 구분할 수 없게 만들 수 있습니다. ### API 트리거 키-값 페어 {#api-triggered-key-value-pairs} [API 트리거 Campaign](https://www.braze.com/docs/ko/ko/user_guide/messaging/campaigns/schedule_your_campaign/api_triggered_delivery/)은 카드의 값이 외부 요인에 따라 사용자에게 표시할 콘텐츠를 결정할 때 사용할 수 있는 좋은 전략입니다. 예를 들어 보조 콘텐츠를 표시하려면 Liquid를 사용하여 키-값 페어를 설정합니다. 설정 시 `class_type`을 알아야 합니다. ![보조 Content Cards 사용 사례에 대한 키-값 페어입니다. 이 예시에서는 카드의 다양한 측면인 "tile_id", "tile_deeplink", "tile_title"이 Liquid를 사용하여 설정됩니다.](https://www.braze.com/docs/ko/ko/assets/img/cc_implementation/supplementary_content.png?1b9481939960e31dc1b1e5ef57d51b68){: style="max-width:60%;"} ## 대화형 콘텐츠로서의 Content Cards {#content-cards-as-interactive-content} ![화면 왼쪽 하단에 50% 프로모션을 표시하는 대화형 Content Card가 나타납니다. 클릭하면 프로모션이 장바구니에 적용됩니다.](https://www.braze.com/docs/ko/ko/assets/img/cc_implementation/discount2.png?28bacc3995ff935635bb24b7bb547e1b){: style="border:0;"}{: style="float:right;max-width:45%;border:0;margin-left:15px;"} Content Cards를 활용하여 사용자를 위한 역동적이고 인터랙티브한 경험을 만들 수 있습니다. 오른쪽 예시에서는 결제 시 Content Card 팝업이 표시되어 사용자에게 막바지 프로모션을 제공합니다. 이와 같은 카드를 잘 배치하면 특정 사용자 동작을 향한 '넛지'를 제공할 수 있습니다. 이 사용 사례의 키-값 페어에는 원하는 할인 금액으로 설정된 `discount_percentage`와 `coupon_code`로 설정된 `class_type`이 포함됩니다. 이러한 키-값 페어를 사용하여 결제 화면에서 유형별 Content Cards를 필터링하고 표시할 수 있습니다. 키-값 페어를 사용하여 여러 피드를 관리하는 방법에 대한 자세한 내용은 [기본 Content Card 피드 커스터마이즈](https://www.braze.com/docs/ko/ko/developer_guide/customization_guides/content_cards/customizing_feed/#multiple-feeds)를 참조하세요.

![](https://www.braze.com/docs/ko/ko/assets/img/cc_implementation/discount.png?0f629adf0e5b56e0d2dc52b0b9f3e087){: style="max-width:80%;"} ## Content Card 배지 {#content-card-badges} ![숫자 7이 표시된 빨간색 배지와 함께 Swifty라는 이름의 Braze 샘플 앱이 표시된 iPhone 홈 화면](https://www.braze.com/docs/ko/ko/assets/img/cc_implementation/ios-unread-badge.png?f8538f586f860532f0f670933ea72054){: style="max-width:35%;float:right;margin-left:15px;border:none;"} 배지는 사용자의 관심을 끌기에 적합한 작은 아이콘입니다. 배지를 사용하여 새로운 Content Card 콘텐츠를 사용자에게 알리면 사용자를 앱으로 다시 유도하고 세션을 늘릴 수 있습니다. ### 미열람 Content Card 수를 배지로 표시 {#displaying-the-number-of-unread-content-cards-as-a-badge} 미열람 Content Card 수를 앱 아이콘에 배지로 표시할 수 있습니다. 언제든지 다음을 호출하여 미열람 카드 수를 요청할 수 있습니다: ```javascript braze.getCachedContentCards().getUnviewedCardCount(); ``` 그런 다음 이 정보를 사용하여 미열람 Content Cards 수를 나타내는 배지를 표시할 수 있습니다. 자세한 내용은 SDK 참조 문서 를 참조하세요. 언제든지 다음을 호출하여 미열람 카드 수를 요청할 수 있습니다: ```java Braze.getInstance(context).getContentCardUnviewedCount(); ``` ```kotlin Braze.getInstance(context).contentCardUnviewedCount ``` 그런 다음 이 정보를 사용하여 미열람 Content Cards 수를 나타내는 배지를 표시할 수 있습니다. 자세한 내용은 SDK 참조 문서 를 참조하세요. 다음 샘플은 `braze.contentCards`를 사용하여 미열람 Content Card 수를 요청하고 표시합니다. 앱이 닫히고 사용자 세션이 종료된 후 이 코드는 카드 수를 요청하여 `viewed` 등록정보를 기준으로 카드 수를 필터링합니다. ```swift func applicationDidEnterBackground(_ application: UIApplication) ``` 이 메서드 내에서 주어진 세션 동안 사용자가 카드를 볼 때 배지 수를 적극적으로 업데이트하는 다음 코드를 구현합니다: ```swift let unreadCards = AppDelegate.braze?.contentCards.cards.filter { $0.viewed == false } UIApplication.shared.applicationIconBadgeNumber = unreadCards?.count ?? 0 ``` ```objc (void)applicationDidEnterBackground:(UIApplication *)application ``` 이 메서드 내에서 주어진 세션 동안 사용자가 카드를 볼 때 배지 수를 적극적으로 업데이트하는 다음 코드를 구현합니다: ```objc NSInteger unreadCardCount = 0; for (BRZContentCardRaw *card in AppDelegate.braze.contentCards.cards) { if (card.viewed == NO) { unreadCardCount += 1; } } [UIApplication sharedApplication].applicationIconBadgeNumber = unreadCardCount; ``` # Content Cards 피드 커스터마이즈 Source: /docs/ko/developer_guide/content_cards/customizing_cards/feed/index.md # Content Cards 피드 커스터마이즈 {#customize-the-feed-for-content-cards} > Content Cards 피드는 모바일 또는 웹 애플리케이션에 표시되는 Content Cards의 시퀀스입니다. 이 문서에서는 피드 새로고침 시기 설정, 카드 순서 지정, 여러 피드 관리, "빈 피드" 오류 메시지 등을 구성하는 방법을 다룹니다. 콘텐츠 카드 유형의 전체 목록은 [Content Cards 정보](https://www.braze.com/docs/ko/ko/developer_guide/content_cards/)를 참조하세요. ## About the session lifecycle A session refers to the period of time the Braze SDK tracks user activity in your app after it's launched. You can also force a new session by [calling the `changeUser()` method](https://www.braze.com/docs/ko/ko/developer_guide/analytics/setting_user_ids/#setting-a-user-id). By default, a session starts when you first call `braze.openSession()`. The session will remain active for up to `30` minutes of inactivity (unless you [change the default session timeout](https://www.braze.com/docs/ko/ko/developer_guide/analytics/tracking_sessions/?tab=web#change-session-timeout) or the user closes the app. **Note:** If you've set up the [activity lifecycle callback](https://www.braze.com/docs/ko/ko/developer_guide/platform_integration_guides/android/initial_sdk_setup/android_sdk_integration/#step-4-tracking-user-sessions-in-android) for Android, Braze will automatically call [`openSession()`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze/-i-braze/open-session.html) and [`closeSession()`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze/-i-braze/close-session.html) for each activity in your app. By default, a session starts when `openSession()` is first called. If your app goes to the background and then returns to the foreground, the SDK will check if more than 10 seconds have passed since the session started (unless you [change the default session timeout](https://www.braze.com/docs/ko/ko/developer_guide/analytics/tracking_sessions/?tab=android#change-session-timeout)). If so, a new session will begin. Keep in mind that if the user closes your app while it's in the background, session data may not be sent to Braze until they reopen the app. Calling `closeSession()` will not immediately end the session. Instead, it will end the session after 10 seconds if `openSession()` isn't called again by the user starting another activity. By default, a session starts when you call `Braze.init(configuration:)`. This occurs when the `UIApplicationWillEnterForegroundNotification` notification is triggered, meaning the app has entered the foreground. If your app goes to the background, `UIApplicationDidEnterBackgroundNotification` is triggered. The app does not remain in an active session while in the background. When your app returns to the foreground, the SDK compares the time elapsed since the session started against the session timeout (unless you [change the default session timeout](https://www.braze.com/docs/ko/ko/developer_guide/analytics/tracking_sessions/?tab=swift#change-session-timeout)). If the time since the session started exceeds the timeout period, a new session begins. ## 피드 새로고침 {#refreshing-the-feed} ### 자동 새로고침 {#automatic-refresh} 기본적으로 Content Cards 피드는 다음과 같은 경우 자동으로 새로고침됩니다: - 새 세션이 시작될 때 - 기본 Content Cards 피드를 닫았다가 마지막 새로고침 후 60초 이상 경과한 뒤 다시 열 때 **Tip:** 수동으로 새로고침하지 않고 최신 Content Cards를 동적으로 표시하려면 카드 생성 시 **At first impression**을 선택하세요. 이러한 카드는 사용 가능한 상태가 되면 새로고침됩니다. ### 수동 새로고침 {#manual-refresh} 특정 시간에 피드를 수동으로 새로고침하려면 다음과 같이 합니다: [`requestContentCardsRefresh()`](https://js.appboycdn.com/web-sdk/latest/doc/modules/braze.html#requestcontentcardsrefresh)를 호출하여 언제든지 웹 SDK에서 Braze Content Cards의 수동 새로고침을 요청할 수 있습니다. [`getCachedContentCards`](https://js.appboycdn.com/web-sdk/latest/doc/modules/braze.html#getcachedcontentcards)를 호출하여 마지막 Content Cards 새로고침에서 현재 사용 가능한 모든 카드를 가져올 수도 있습니다. ```javascript import * as braze from "@braze/web-sdk"; function refresh() { braze.requestContentCardsRefresh(); } ``` Content Cards 링크를 같은 탭이 아닌 새 브라우저 탭에서 열려면 웹 SDK 초기화 옵션에서 `openCardsInNewTab: true`를 설정하세요. 초기화 옵션에 대한 자세한 내용은 [웹 SDK 리포지토리 가이드](https://www.braze.com/docs/ko/ko/developer_guide/sdk_repository_guides/web/)를 참조하세요. [`requestContentCardsRefresh`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze/-i-braze/request-content-cards-refresh.html)를 호출하여 언제든지 Android SDK에서 Braze Content Cards의 수동 새로고침을 요청할 수 있습니다. ```java Braze.getInstance(context).requestContentCardsRefresh(); ``` ```kotlin Braze.getInstance(context).requestContentCardsRefresh() ``` [`Braze.ContentCards`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/contentcards-swift.class) 클래스의 [`requestRefresh`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/contentcards-swift.class/requestrefresh(_:)) 메서드를 호출하여 언제든지 Swift SDK에서 Braze Content Cards의 수동 새로고침을 요청할 수 있습니다: Swift에서 Content Cards는 선택적 완료 핸들러를 사용하거나 네이티브 Swift 동시성 API를 사용한 비동기 반환을 통해 새로고침할 수 있습니다. #### 완료 핸들러 {#completion-handler} ```swift AppDelegate.braze?.contentCards.requestRefresh { result in // Implement completion handler } ``` #### Async/Await ```swift let contentCards = await AppDelegate.braze?.contentCards.requestRefresh() ``` ```objc [AppDelegate.braze.contentCards requestRefreshWithCompletion:^(NSArray * contentCards, NSError * error) { // Implement completion handler }]; ``` ### 전체 동기화 vs. 부분 동기화 {#full-sync-vs-partial-sync} Braze SDK는 서버에서 Content Cards를 가져올 때 두 가지 유형의 동기화를 사용합니다: - **전체 동기화:** 사용자가 받을 수 있는 모든 Content Cards를 가져옵니다. 전체 동기화는 7일마다 자동으로 수행되거나 `changeUser()`가 호출될 때 수행됩니다. - **부분 동기화:** 마지막 요청 이후 새로운 Content Cards만 가져옵니다. 사용자가 새 카드를 받을 자격이 없는 경우 응답은 카드 0개를 반환합니다. 부분 동기화는 `requestContentCardsRefresh()`가 호출될 때마다 수행됩니다(마지막 전체 동기화 이후 7일이 경과한 경우에는 대신 전체 동기화가 트리거됩니다). 부분 동기화는 서버 부하와 기기 배터리 사용량을 줄여줍니다. 이미 수신된 Content Cards는 SDK에 로컬로 저장되므로, 부분 동기화가 새 카드 0개를 반환하더라도 사용자는 사용 가능한 카드를 계속 볼 수 있습니다. ### 사용량 제한 {#rate-limit} Braze는 토큰 버킷 알고리즘을 사용하여 다음과 같은 사용량 제한을 적용합니다: - 기기당 최대 5회의 새로고침 호출(사용자 간 공유 및 `openSession()` 호출 포함) - 한도에 도달하면 180초(3분)마다 새 호출이 가능해집니다 - 시스템은 언제든지 사용할 수 있도록 최대 5회의 호출을 보관합니다 - `subscribeToContentCards()`는 사용량 제한이 적용된 경우에도 캐시된 카드를 반환합니다 **Important:** Braze SDK는 성능 및 안정성을 위해 사용량 제한을 적용합니다. 자동화된 테스트를 실행하거나 수동 QA를 수행할 때 이 점을 유의하세요. 자세한 내용은 [Braze SDK 사용량 제한](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/rate_limits/)을 참조하세요. ## 표시되는 카드 순서 커스터마이즈 {#customizing-displayed-card-order} Content Cards가 표시되는 순서를 변경할 수 있습니다. 이를 통해 시간에 민감한 프로모션과 같은 특정 유형의 콘텐츠에 우선순위를 부여하여 사용자 경험을 세밀하게 조정할 수 있습니다. `showContentCards()`의 [`filterFunction`](https://js.appboycdn.com/web-sdk/latest/doc/modules/braze.html#showcontentcards) 매개변수를 사용하여 피드에서 Content Cards의 표시 순서를 커스터마이즈합니다. 예를 들어: ```javascript braze.showContentCards(null, (cards) => { return sortBrazeCards(cards); // Where sortBrazeCards is your sorting function that returns the sorted card array }); ``` [`ContentCardsFragment`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.ui.contentcards/-content-cards-fragment/index.html)는 [`IContentCardsUpdateHandler`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.cards/-card/extras.html)를 사용하여 Content Cards가 피드에 표시되기 전에 정렬 또는 수정을 처리합니다. 커스텀 업데이트 핸들러는 `ContentCardsFragment`의 [`setContentCardUpdateHandler`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.ui.contentcards/-content-cards-fragment/set-content-card-update-handler.html)를 통해 설정할 수 있습니다. 다음은 기본 `IContentCardsUpdateHandler`이며 커스터마이즈의 시작점으로 사용할 수 있습니다: **Java 예제 보기** ```java public class DefaultContentCardsUpdateHandler implements IContentCardsUpdateHandler { // Interface that must be implemented and provided as a public CREATOR // field that generates instances of your Parcelable class from a Parcel. public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { public DefaultContentCardsUpdateHandler createFromParcel(Parcel in) { return new DefaultContentCardsUpdateHandler(); } public DefaultContentCardsUpdateHandler[] newArray(int size) { return new DefaultContentCardsUpdateHandler[size]; } }; @Override public List handleCardUpdate(ContentCardsUpdatedEvent event) { List sortedCards = event.getAllCards(); // Sort by pinned, then by the 'updated' timestamp descending // Pinned before non-pinned Collections.sort(sortedCards, new Comparator() { @Override public int compare(Card cardA, Card cardB) { // A displays above B if (cardA.getIsPinned() && !cardB.getIsPinned()) { return -1; } // B displays above A if (!cardA.getIsPinned() && cardB.getIsPinned()) { return 1; } // At this point, both A & B are pinned or both A & B are non-pinned // A displays above B since A is newer if (cardA.getUpdated() > cardB.getUpdated()) { return -1; } // B displays above A since A is newer if (cardA.getUpdated() < cardB.getUpdated()) { return 1; } // At this point, every sortable field matches so keep the natural ordering return 0; } }); return sortedCards; } // Parcelable interface method @Override public int describeContents() { return 0; } // Parcelable interface method @Override public void writeToParcel(Parcel dest, int flags) { // No state is kept in this class so the parcel is left unmodified } } ``` **Kotlin 예제 보기** ```kotlin class DefaultContentCardsUpdateHandler : IContentCardsUpdateHandler { override fun handleCardUpdate(event: ContentCardsUpdatedEvent): List { val sortedCards = event.allCards // Sort by pinned, then by the 'updated' timestamp descending // Pinned before non-pinned sortedCards.sortWith(Comparator sort@{ cardA: Card, cardB: Card -> // A displays above B if (cardA.isPinned && !cardB.isPinned) { return@sort -1 } // B displays above A if (!cardA.isPinned && cardB.isPinned) { return@sort 1 } // At this point, both A & B are pinned or both A & B are non-pinned // A displays above B since A is newer if (cardA.updated > cardB.updated) { return@sort -1 } // B displays above A since A is newer if (cardA.updated < cardB.updated) { return@sort 1 } 0 }) return sortedCards } // Parcelable interface method override fun describeContents(): Int { return 0 } // Parcelable interface method override fun writeToParcel(dest: Parcel, flags: Int) { // No state is kept in this class so the parcel is left unmodified } companion object { // Interface that must be implemented and provided as a public CREATOR // field that generates instances of your Parcelable class from a Parcel. val CREATOR: Parcelable.Creator = object : Parcelable.Creator { override fun createFromParcel(`in`: Parcel): DefaultContentCardsUpdateHandler? { return DefaultContentCardsUpdateHandler() } override fun newArray(size: Int): Array { return arrayOfNulls(size) } } } } ``` **Tip:** `ContentCardsFragment` 소스는 [GitHub](https://github.com/braze-inc/braze-android-sdk/blob/master/android-sdk-ui/src/main/java/com/braze/ui/contentcards/ContentCardsFragment.kt)에서 확인할 수 있습니다. Jetpack Compose에서 Content Cards를 필터링하고 정렬하려면 `cardUpdateHandler` 매개변수를 설정합니다. 예를 들어: ```kotlin ContentCardsList( cardUpdateHandler = { it.sortedWith { cardA, cardB -> // A displays above B if (cardA.isPinned && !cardB.isPinned) { return@sortedWith -1 } // B displays above A if (!cardA.isPinned && cardB.isPinned) { return@sortedWith 1 } // At this point, both A & B are pinned or both A & B are non-pinned // A displays above B since A is newer if (cardA.updated > cardB.updated) { return@sortedWith -1 } // B displays above A since A is newer if (cardA.updated < cardB.updated) { return@sortedWith 1 } 0 } } ) ``` 정적 [`Attributes.defaults`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazeui/brazecontentcardui/viewcontroller/attributes-swift.struct/defaults) 변수를 직접 수정하여 카드 피드 순서를 커스터마이즈합니다. ```swift var attributes = BrazeContentCardUI.ViewController.Attributes.defaults attributes.transform = { cards in cards.sorted { if $0.pinned && !$1.pinned { return true } else if !$0.pinned && $1.pinned { return false } else { return $0.createdAt > $1.createdAt } } } let viewController = BrazeContentCardUI.ViewController(braze: AppDelegate.braze, attributes: attributes) ``` `BrazeContentCardUI.ViewController.Attributes`를 통한 커스터마이즈는 Objective-C에서 사용할 수 없습니다. ## "빈 피드" 메시지 커스터마이즈 {#customizing-empty-feed-message} 사용자가 Content Cards를 받을 자격이 없는 경우 SDK는 "We have no updates. Please check again later."라는 "빈 피드" 오류 메시지를 표시합니다. 이 "빈 피드" 오류 메시지는 다음과 같이 커스터마이즈할 수 있습니다: ![빈 피드 오류 메시지. "This is a custom empty state message."라고 표시됩니다.](https://www.braze.com/docs/ko/ko/assets/img/content_cards/content-card-customization-empty.png?f309ee8967ad09179432d3212ff8e073) 웹 SDK는 프로그래밍 방식으로 "빈 피드" 문구를 대체하는 기능을 지원하지 않습니다. 피드가 표시될 때마다 교체하도록 선택할 수 있지만, 피드를 새로고침하는 데 시간이 걸릴 수 있고 빈 피드 텍스트가 즉시 표시되지 않을 수 있으므로 권장하지 않습니다. [`ContentCardsFragment`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.ui.contentcards/-content-cards-fragment/index.html)에서 사용자가 Content Cards를 받을 자격이 없다고 판단하면 빈 피드 오류 메시지를 표시합니다. 특수 어댑터인 [`EmptyContentCardsAdapter`](https://github.com/braze-inc/braze-android-sdk/blob/master/android-sdk-ui/src/main/java/com/braze/ui/contentcards/adapters/EmptyContentCardsAdapter.kt)가 표준 [`ContentCardAdapter`](https://github.com/braze-inc/braze-android-sdk/blob/master/android-sdk-ui/src/main/java/com/braze/ui/contentcards/adapters/ContentCardAdapter.kt)를 대체하여 이 오류 메시지를 표시합니다. 커스텀 메시지를 설정하려면 문자열 리소스 `com_braze_feed_empty`를 재정의합니다. 이 메시지를 표시하는 데 사용되는 스타일은 [`Braze.ContentCardsDisplay.Empty`](https://github.com/braze-inc/braze-android-sdk/blob/2e386dfa59a87bfc24ef7cb6ff5adf6b16f44d24/android-sdk-ui/src/main/res/values/styles.xml#L522-L530)에서 확인할 수 있으며 다음 코드 스니펫에 재현되어 있습니다: ```xml ``` Content Cards 스타일 요소 커스터마이즈에 대한 자세한 내용은 [스타일 커스터마이즈](https://www.braze.com/docs/ko/ko/developer_guide/content_cards/customizing_cards/style/)를 참조하세요. Jetpack Compose로 "빈 피드" 오류 메시지를 커스터마이즈하려면 `emptyString`을 [`ContentCardsList`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.jetpackcompose.contentcards/-content-cards-list.html)에 전달할 수 있습니다. [`emptyTextStyle`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.jetpackcompose.contentcards.styling/-content-card-list-styling/index.html#1193499348%2FProperties%2F-1725759721)을 `ContentCardListStyling`에 전달하여 이 메시지를 추가로 커스터마이즈할 수도 있습니다. ```kotlin ContentCardsList( emptyString = "No messages today", style = ContentCardListStyling( emptyTextStyle = TextStyle(...) ) ) ``` 대신 표시하고 싶은 Composable이 있는 경우 `emptyComposable`을 [`ContentCardsList`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.jetpackcompose.contentcards/-content-cards-list.html)에 전달할 수 있습니다. `emptyComposable`을 지정하면 `emptyString`은 사용되지 않습니다. ```kotlin ContentCardsList( emptyComposable = { Image( painter = painterResource(id = R.drawable.noMessages), contentDescription = "No messages" ) } ) ``` 관련 [`Attributes`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazeui/brazecontentcardui/viewcontroller/attributes-swift.struct/defaults)를 설정하여 뷰 컨트롤러의 빈 상태를 커스터마이즈합니다. ```swift var attributes = BrazeContentCardUI.ViewController.Attributes.defaults attributes.emptyStateMessage = "This is a custom empty state message" attributes.emptyStateMessageFont = .preferredFont(forTextStyle: .title1) attributes.emptyStateMessageColor = .secondaryLabel ``` 앱의 [`ContentCardsLocalizable.strings`](https://github.com/braze-inc/braze-swift-sdk/tree/main/Sources/BrazeUI/Resources/Localization/en.lproj) 파일에서 현지화 가능한 Content Cards 문자열을 재정의하여 빈 Content Cards 피드에 자동으로 표시되는 문구를 변경합니다. **Note:** 이 메시지를 다른 로캘 언어로 업데이트하려면 [리소스 폴더 구조](https://github.com/braze-inc/braze-swift-sdk/tree/main/Sources/BrazeUI/Resources/Localization)에서 `ContentCardsLocalizable.strings` 문자열이 포함된 해당 언어를 찾으세요. ## 여러 피드 구현하기 {#implementing-multiple-feeds} Content Cards는 앱에서 필터링하여 특정 카드만 표시할 수 있으므로 다양한 사용 사례에 맞게 여러 Content Cards 피드를 운영할 수 있습니다. 예를 들어 트랜잭션 피드와 마케팅 피드를 모두 유지할 수 있습니다. 이를 위해 Braze 대시보드에서 키-값 페어를 설정하여 다양한 카테고리의 Content Cards를 생성합니다. 그런 다음 앱이나 사이트에서 이러한 유형의 Content Cards를 다르게 처리하는 피드를 만들어 일부 유형은 필터링하고 다른 유형은 표시할 수 있습니다. ### 1단계: 카드에 키-값 페어 설정 {#step-1-set-key-value-pairs-on-cards} 콘텐츠 카드 캠페인을 생성할 때 각 카드에 [키-값 페어 데이터](https://www.braze.com/docs/ko/ko/developer_guide/content_cards/customizing_cards/behavior/)를 설정합니다. 이 키-값 페어를 사용하여 카드를 분류합니다. 키-값 페어는 카드의 데이터 모델에 있는 `extras` 등록정보에 저장됩니다. 이 예제에서는 `feed_type` 키로 키-값 페어를 설정하여 카드가 표시될 Content Cards 피드를 지정합니다. 값은 `home_screen` 또는 `marketing`과 같이 커스텀 피드에 따라 달라집니다. ### 2단계: Content Cards 필터링 {#step-2-filter-content-cards} 키-값 페어가 할당되면 표시하려는 카드를 보여주고 다른 유형의 카드를 필터링하는 로직이 포함된 피드를 만듭니다. 이 예제에서는 키-값 페어가 `feed_type: "Transactional"`과 일치하는 카드만 표시합니다. 다음 예제에서는 `Transactional` 유형 카드에 대한 Content Cards 피드를 보여줍니다: ```javascript /** * @param {String} feed_type - value of the "feed_type" KVP to filter */ function showCardsByFeedType(feed_type) { braze.showContentCards(null, function(cards) { return cards.filter((card) => card.extras["feed_type"] === feed_type); }); } ``` 그런 다음 커스텀 피드에 대한 토글을 설정할 수 있습니다: ```javascript // show the "Transactional" feed when this button is clicked document.getElementById("show-transactional-feed").onclick = function() { showCardsByFeedType("Transactional"); }; ``` 자세한 내용은 [SDK 메서드 설명서](https://js.appboycdn.com/web-sdk/latest/doc/modules/braze.html#showcontentcards)를 참조하세요. 기본적으로 Content Cards 피드는 [`ContentCardsFragment`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.ui.contentcards/-content-cards-fragment/index.html)에 표시되며 [`IContentCardsUpdateHandler`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.ui.contentcards.handlers/-i-content-cards-update-handler/index.html)는 Braze SDK에서 [`ContentCardsUpdatedEvent`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.events/-content-cards-updated-event/index.html)를 수신한 후 표시할 카드 목록을 반환합니다. 그러나 카드를 정렬할 뿐 필터링은 직접 처리하지 않습니다. #### 2.1단계: 커스텀 핸들러 생성 {#step-21-create-a-custom-handler} 대시보드에서 [`Card.getExtras()`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.ui.contentcards.handlers/-i-content-cards-update-handler/index.html)로 설정한 키-값 페어를 사용하여 커스텀 [`IContentCardsUpdateHandler`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.ui.contentcards.handlers/-i-content-cards-update-handler/index.html)를 구현하여 Content Cards를 필터링한 다음, 앞서 설정한 `feed_type` 값과 일치하지 않는 카드를 목록에서 제거하도록 수정할 수 있습니다. **Java 예제 보기** ```java private IContentCardsUpdateHandler getUpdateHandlerForFeedType(final String desiredFeedType) { return new IContentCardsUpdateHandler() { @Override public List handleCardUpdate(ContentCardsUpdatedEvent event) { // Use the default card update handler for a first // pass at sorting the cards. This is not required // but is done for convenience. final List cards = new DefaultContentCardsUpdateHandler().handleCardUpdate(event); final Iterator cardIterator = cards.iterator(); while (cardIterator.hasNext()) { final Card card = cardIterator.next(); // Make sure the card has our custom KVP // from the dashboard with the key "feed_type" if (card.getExtras().containsKey("feed_type")) { final String feedType = card.getExtras().get("feed_type"); if (!desiredFeedType.equals(feedType)) { // The card has a feed type, but it doesn't match // our desired feed type, remove it. cardIterator.remove(); } } else { // The card doesn't have a feed // type at all, remove it cardIterator.remove(); } } // At this point, all of the cards in this list have // a feed type that explicitly matches the value we put // in the dashboard. return cards; } }; } ``` **Kotlin 예제 보기** ```kotlin private fun getUpdateHandlerForFeedType(desiredFeedType: String): IContentCardsUpdateHandler { return IContentCardsUpdateHandler { event -> // Use the default card update handler for a first // pass at sorting the cards. This is not required // but is done for convenience. val cards = DefaultContentCardsUpdateHandler().handleCardUpdate(event) val cardIterator = cards.iterator() while (cardIterator.hasNext()) { val card = cardIterator.next() // Make sure the card has our custom KVP // from the dashboard with the key "feed_type" if (card.extras.containsKey("feed_type")) { val feedType = card.extras["feed_type"] if (desiredFeedType != feedType) { // The card has a feed type, but it doesn't match // our desired feed type, remove it. cardIterator.remove() } } else { // The card doesn't have a feed // type at all, remove it cardIterator.remove() } } // At this point, all of the cards in this list have // a feed type that explicitly matches the value we put // in the dashboard. cards } } ``` #### 2.2단계: 프래그먼트에 추가 {#step-22-add-it-to-a-fragment} [`IContentCardsUpdateHandler`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.ui.contentcards.handlers/-i-content-cards-update-handler/index.html)를 생성한 후 이를 사용하는 [`ContentCardsFragment`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.ui.contentcards/-content-cards-fragment/index.html)를 생성합니다. 이 커스텀 피드는 다른 `ContentCardsFragment`와 마찬가지로 사용할 수 있습니다. 앱의 다른 부분에서 대시보드에 제공된 키에 따라 다른 Content Cards 피드를 표시합니다. 각 `ContentCardsFragment` 피드에는 각 프래그먼트의 커스텀 `IContentCardsUpdateHandler` 덕분에 고유한 카드 집합이 표시됩니다. **Java 예제 보기** ```java // We want a Content Cards feed that only shows "Transactional" cards. ContentCardsFragment customContentCardsFragment = new ContentCardsFragment(); customContentCardsFragment.setContentCardUpdateHandler(getUpdateHandlerForFeedType("Transactional")); ``` **Kotlin 예제 보기** ```kotlin // We want a Content Cards feed that only shows "Transactional" cards. val customContentCardsFragment = ContentCardsFragment() customContentCardsFragment.contentCardUpdateHandler = getUpdateHandlerForFeedType("Transactional") ``` 이 피드에 표시되는 Content Cards를 필터링하려면 `cardUpdateHandler`를 사용합니다. 예를 들어: ```kotlin ContentCardsList( cardUpdateHandler = { it.filter { card -> card.extras["feed_type"] == "Transactional" } } ) ``` The following example will show the Content Cards feed for `Transactional` type cards: ```swift // Filter cards by the `Transactional` feed type based on your key-value pair. let transactionalCards = cards.filter { $0.extras["feed_type"] as? String == "Transactional" } ``` 더 나아가, `Attributes` 구조체의 `transform` 등록정보를 설정하여 뷰 컨트롤러에 표시되는 카드를 필터링하면 기준에 맞는 카드만 표시할 수 있습니다. ```swift var attributes = BrazeContentCardUI.ViewController.Attributes.defaults attributes.transform = { cards in cards.filter { $0.extras["feed_type"] as? String == "Transactional" } } // Pass your attributes containing the transformed cards to the Content Card UI. let viewController = BrazeContentCardUI.ViewController(braze: AppDelegate.braze, attributes: attributes) ``` ```objc // Filter cards by the `Transactional` feed type based on your key-value pair. NSMutableArray *transactionalCards = [[NSMutableArray alloc] init]; for (BRZContentCardRaw *card in AppDelegate.braze.contentCards.cards) { if ([card.extras[@"feed_type"] isEqualToString:@"Transactional"]) { [transactionalCards addObject:card]; } } ``` # 로그 분석 Source: /docs/ko/developer_guide/content_cards/logging_analytics/index.md # 분석 기록 {#log-analytics} > When building a custom UI for Content Cards, you must manually log analytics like impressions, clicks, and dismissals, as this is only handled automatically for default card models. Logging these events is a standard part of a Content Card integration and is essential for accurate campaign reporting and billing. To do this, populate your custom UI with data from the Braze data models and then manually log the events. Once you understand how to log analytics, you can see common ways Braze customers [create custom Content Cards](https://www.braze.com/docs/ko/ko/developer_guide/content_cards/creating_cards/). ## Logging analytics When implementing your custom Content Cards, you can parse the Content Card objects and extract their payload data such as `title`, `cardDescription`, and `imageUrl`. Then, you can use the resulting model data to populate your custom UI. To obtain the Content Card data models, subscribe to Content Card updates. There are two properties to pay particular attention to: * **`id`**: Represents the Content Card ID string. This is the unique identifier used to log analytics from custom Content Cards. * **`extras`**: Encompasses all the key-value pairs from the Braze dashboard. All properties outside of `id` and `extras` are optional to parse for custom Content Cards. For more information on the data model, see each platform's integration article: [Android](https://www.braze.com/docs/ko/ko/developer_guide/content_cards/?sdktab=android), [iOS](https://www.braze.com/docs/ko/ko/developer_guide/content_cards/?sdktab=swift), [Web](https://www.braze.com/docs/ko/ko/developer_guide/content_cards/?sdktab=web). Register a callback function to subscribe for updates when cards are refreshed. ```javascript import * as braze from "@braze/web-sdk"; braze.subscribeToContentCardsUpdates((updates) => { const cards = updates.cards; // For example: cards.forEach(card => { if (card.isControl) { // Do not display the control card, but remember to call `logContentCardImpressions([card])` } else if (card instanceof braze.ClassicCard || card instanceof braze.CaptionedImage) { // Use `card.title`, `card.imageUrl`, etc. } else if (card instanceof braze.ImageOnly) { // Use `card.imageUrl`, etc. } }) }); braze.openSession(); ``` **Note:** Content Cards will only refresh on session start if a subscribe request is called before `openSession()`. You can always choose to [manually refresh the feed](https://www.braze.com/docs/ko/ko/developer_guide/content_cards/customizing_cards/feed/) as well. ### Step 1: Create a private subscriber variable To subscribe to card updates, first declare a private variable in your custom class to hold your subscriber: ```java // subscriber variable private IEventSubscriber mContentCardsUpdatedSubscriber; ``` ### Step 2: Subscribe to updates Next, add the following code to subscribe to Content Card updates from Braze, typically inside of your custom Content Cards activity's `Activity.onCreate()`: ```java // Remove the previous subscriber before rebuilding a new one with our new activity. Braze.getInstance(context).removeSingleSubscription(mContentCardsUpdatedSubscriber, ContentCardsUpdatedEvent.class); mContentCardsUpdatedSubscriber = new IEventSubscriber() { @Override public void trigger(ContentCardsUpdatedEvent event) { // List of all Content Cards List allCards = event.getAllCards(); // Your logic below } }; Braze.getInstance(context).subscribeToContentCardsUpdates(mContentCardsUpdatedSubscriber); Braze.getInstance(context).requestContentCardsRefresh(); ``` ### Step 3: Unsubscribe We also recommend unsubscribing when your custom activity moves out of view. Add the following code to your activity's `onDestroy()` lifecycle method: ```java Braze.getInstance(context).removeSingleSubscription(mContentCardsUpdatedSubscriber, ContentCardsUpdatedEvent.class); ``` ### Step 1: Create a private subscriber variable To subscribe to card updates, first declare a private variable in your custom class to hold your subscriber: ```kotlin private var contentCardsUpdatedSubscriber: IEventSubscriber? = null ``` ### Step 2: Subscribe to updates Next, add the following code to subscribe to Content Card updates from Braze, typically inside of your custom Content Cards activity's `Activity.onCreate()`: ```kotlin // Remove the previous subscriber before rebuilding a new one with our new activity. Braze.getInstance(context).removeSingleSubscription(contentCardsUpdatedSubscriber, ContentCardsUpdatedEvent::class.java) contentCardsUpdatedSubscriber = IEventSubscriber { event -> // List of all Content Cards val allCards = event.allCards // Your logic below } Braze.getInstance(context).subscribeToContentCardsUpdates(contentCardsUpdatedSubscriber) Braze.getInstance(context).requestContentCardsRefresh(true) ``` ### Step 3: Unsubscribe We also recommend unsubscribing when your custom activity moves out of view. Add the following code to your activity's `onDestroy()` lifecycle method: ```kotlin Braze.getInstance(context).removeSingleSubscription(contentCardsUpdatedSubscriber, ContentCardsUpdatedEvent::class.java) ``` To access the Content Cards data model, call [`contentCards.cards`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/contentcards-swift.class/cards) on your `braze` instance. ```swift let cards: [Braze.ContentCard] = AppDelegate.braze?.contentCards.cards ``` Additionally, you can also maintain a subscription to observe for changes in your Content Cards. You can do so in one of two ways: 1. Maintaining a cancellable; or 2. Maintaining an `AsyncStream`. ### Cancellable ```swift // This subscription is maintained through a Braze cancellable, which will observe for changes until the subscription is cancelled. // You must keep a strong reference to the cancellable to keep the subscription active. // The subscription is canceled either when the cancellable is deinitialized or when you call its `.cancel()` method. let cancellable = AppDelegate.braze?.contentCards.subscribeToUpdates { [weak self] contentCards in // Implement your completion handler to respond to updates in `contentCards`. } ``` ### AsyncStream ```swift let stream: AsyncStream<[Braze.ContentCard]> = AppDelegate.braze?.contentCards.cardsStream ``` ```objc NSArray *contentCards = AppDelegate.braze.contentCards.cards; ``` Additionally, if you wish to maintain a subscription to your content cards, you can call [`subscribeToUpdates`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/contentcards-swift.class/subscribetoupdates(_:)): ```objc // This subscription is maintained through Braze cancellable, which will continue to observe for changes until the subscription is cancelled. BRZCancellable *cancellable = [self.braze.contentCards subscribeToUpdates:^(NSArray *contentCards) { // Implement your completion handler to respond to updates in `contentCards`. }]; ``` To get the Content Card data, use the `getContentCards` method: ```javascript import Braze from "@braze/react-native-sdk"; const cards = await Braze.getContentCards(); ``` To listen for updates, subscribe to Content Card update events: ```javascript const subscription = Braze.addListener(Braze.Events.CONTENT_CARDS_UPDATED, (update) => { const cards = update.cards; cards.forEach(card => { if (card.isControl) { // Do not display the control card, but remember to log an impression } else { // Use card.title, card.cardDescription, card.image, etc. } }); }); ``` To request a manual refresh of Content Cards from Braze servers: ```javascript Braze.requestContentCardsRefresh(); ``` To get cached Content Cards without a network request: ```javascript const cachedCards = await Braze.getCachedContentCards(); ``` ## Logging events Logging valuable metrics like impressions, clicks, and dismissals is quick and simple. Set a custom click listener to manually handle these analytics. Log impression events when cards are viewed by users using [`logContentCardImpressions`](https://js.appboycdn.com/web-sdk/latest/doc/modules/braze.html#logcontentcardimpressions): ```javascript import * as braze from "@braze/web-sdk"; braze.logContentCardImpressions([card1, card2, card3]); ``` Log card click events when users interact with a card using [`logContentCardClick`](https://js.appboycdn.com/web-sdk/latest/doc/modules/braze.html#logcontentcardclick): ```javascript import * as braze from "@braze/web-sdk"; braze.logContentCardClick(card); ``` The [`BrazeManager`](https://github.com/braze-inc/braze-growth-shares-android-demo-app/blob/main/app/src/main/java/com/braze/advancedsamples/BrazeManager.kt) can reference Braze SDK dependencies such as the Content Card objects array list to get the [`Card`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.cards/-card/index.html) to call the Braze logging methods. Use the `ContentCardable` base class to easily reference and provide data to the `BrazeManager`. To log an impression or click on a card, call [`Card.logClick()`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.cards/-card/log-click.html) or [`Card.logImpression()`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.cards/-card/log-impression.html) respectively. You can manually log or set a Content Card as "dismissed" to Braze for a particular card with [`isDismissed`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.cards/-card/is-dismissed.html). If a card is already marked as dismissed, it cannot be marked as dismissed again. To create a custom click listener, create a class that implements [`IContentCardsActionListener`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.ui.contentcards.listeners/-i-content-cards-action-listener/index.html) and register it with [`BrazeContentCardsManager`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.ui.contentcards.managers/-braze-content-cards-manager/index.html). Implement the [`onContentCardClicked()`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.ui.contentcards.listeners/-i-content-cards-action-listener/on-content-card-clicked.html) method, which will be called when the user clicks a Content Card. Then, instruct Braze to use your Content Card click listener. For example: ```java BrazeContentCardsManager.getInstance().setContentCardsActionListener(new IContentCardsActionListener() { @Override public boolean onContentCardClicked(Context context, Card card, IAction cardAction) { return false; } @Override public void onContentCardDismissed(Context context, Card card) { } }); ``` For example: ```kotlin BrazeContentCardsManager.getInstance().contentCardsActionListener = object : IContentCardsActionListener { override fun onContentCardClicked(context: Context, card: Card, cardAction: IAction): Boolean { return false } override fun onContentCardDismissed(context: Context, card: Card) { } } ``` **Important:** To handle control variant Content Cards in your custom UI, pass in your [`com.braze.models.cards.Card`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.cards/-card/index.html) object, then call the `logImpression` method as you would with any other Content Card type. The object will implicitly log a control impression to inform our analytics of when a user would have seen the control card. Implement the [`BrazeContentCardUIViewControllerDelegate`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazeui/brazecontentcarduiviewcontrollerdelegate) protocol and set your delegate object as the `delegate` property of your `BrazeContentCardUI.ViewController`. This delegate will handle passing the data of your custom object back to Braze to be logged. For an example, see [Content Cards UI tutorial](https://braze-inc.github.io/braze-swift-sdk/tutorials/braze/c2-contentcardsui/). ```swift // Set the delegate when creating the Content Cards controller contentCardsController.delegate = delegate // Method to implement in delegate func contentCard( _ controller: BrazeContentCardUI.ViewController, shouldProcess clickAction: Braze.ContentCard.ClickAction, card: Braze.ContentCard ) -> Bool { // Intercept the content card click action here. return true } ``` ```objc // Set the delegate when creating the Content Cards controller contentCardsController.delegate = delegate; // Method to implement in delegate - (BOOL)contentCardController:(BRZContentCardUIViewController *)controller shouldProcess:(NSURL *)url card:(BRZContentCardRaw *)card { // Intercept the content card click action here. return YES; } ``` **Important:** To handle control variant Content Cards in your custom UI, pass in your [`Braze.ContentCard.Control`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/contentcard/control(_:)) object, then call the `logImpression` method as you would with any other Content Card type. The object will implicitly log a control impression to inform our analytics of when a user would have seen the control card. Log impression events when cards are viewed by users: ```javascript Braze.logContentCardImpression(card.id); ``` Log card click events when users interact with a card: ```javascript Braze.logContentCardClicked(card.id); ``` Log dismissal events when a user dismisses a card: ```javascript Braze.logContentCardDismissed(card.id); ``` ## Handling on-click behavior When a user clicks a Content Card in a custom feed, the on-click behavior (such as navigating to a URL, deep linking, or logging a custom event) is not handled automatically. Use [`handleBrazeAction`](https://js.appboycdn.com/web-sdk/latest/doc/modules/braze.html#handlebrazeaction) to process the card's URL and execute the configured on-click action, including Braze actions (`brazeActions://` URLs). ```javascript import * as braze from "@braze/web-sdk"; // In your card click handler function onCardClick(card) { // Log the click braze.logContentCardClick(card); // Handle the on-click behavior if (card.url) { braze.handleBrazeAction(card.url); } } ``` | Parameter | Description | |---|---| | `url` | A valid URL, or a valid Braze action URL with the scheme `brazeActions://`. | | `openLinkInNewTab` | (Optional) Whether the URL should open in a new tab. Defaults to `false`. | {: .reset-td-br-1 .reset-td-br-2 aria-label="Handling on-click behavior" } **Important:** If you don't call `handleBrazeAction()`, on-click behaviors configured in the Braze dashboard (such as "Log Custom Event" or "Navigate to URL") won't execute for cards displayed in a custom feed. On-click behavior is handled automatically by the default Content Cards UI. For custom implementations, use the [`IContentCardsActionListener`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.ui.contentcards.listeners/-i-content-cards-action-listener/index.html) interface described in the [Logging analytics](#logging-analytics) section above. On-click behavior is handled automatically by the default Content Cards UI. For custom implementations, use the [`BrazeContentCardUIViewControllerDelegate`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazeui/brazecontentcarduiviewcontrollerdelegate) protocol described in the [Logging analytics](#logging-analytics) section above. ## 고유 해제 수가 고유 노출 횟수보다 높은 경우 {#unique-dismissals-higher-than-unique-impressions} *고유 해제 수*가 *고유 노출 횟수*를 초과하는 경우, 커스텀 콘텐츠 카드 통합에서 해당 카드에 대한 노출 횟수를 기록하지 않고 해제만 기록한 것입니다. Braze의 기본 Content Cards UI는 두 가지를 모두 자동으로 기록하므로, 이 불일치는 커스텀 UI를 사용할 때만 나타납니다. 카드를 표시할 때마다 노출 횟수를 기록하고, 사용자가 카드를 해제할 때 해제를 기록하세요. 메서드 이름과 예시는 아래 플랫폼 섹션을 참조하세요. ## 콘텐츠 카드 분석 누락 {#missing-content-cards-analytics} 콘텐츠 카드가 앱에 올바르게 표시되지만 분석(노출 횟수, 클릭 수 등)이 지속적으로 수신되지 않는 경우, SDK 통합 문제일 가능성이 높습니다. - **커스텀 콘텐츠 카드 뷰(Android, iOS, Web):** 기본 Braze UI는 모든 플랫폼에서 노출 횟수와 클릭을 자동으로 기록합니다. 커스텀 콘텐츠 카드 뷰 또는 구현을 사용하는 경우, 애플리케이션 내에서 적절한 로깅 메서드를 명시적으로 호출해야 합니다. 해당 플랫폼의 [분석 기록](https://www.braze.com/docs/ko/ko/developer_guide/content_cards/logging_analytics/)을 참조하세요. 특히 커스텀 Web 구현의 경우, Braze Web SDK가 로드되었는지 확인하고, 브라우저 콘솔에서 오류를 확인하며, 카드 데이터가 수신되고 있는지 검증하세요. - **SDK 초기화 및 사용자 식별:** 카드를 표시하기 전에 SDK가 완전히 초기화되었는지 확인하세요. SDK가 초기화되지 않았거나, 지연 초기화 모드이거나, GDPR이 비활성화된 경우 이벤트는 대기줄에 추가되지 않고 자동으로 삭제됩니다. SDK는 익명 사용자에 대해서도 분석을 기록하지만, "고유 일일 노출 횟수"와 같은 대시보드 측정기준은 확인된 사용자 ID가 필요하므로 가능하면 카드가 표시되기 전에 `changeUser`를 호출하세요. ## 콘텐츠 카드 ID {#content-card-id} 수신자에게 Campaign을 전송할 때마다 새로운 콘텐츠 카드 ID가 생성됩니다. 동일한 사용자가 이후 전송에서 해당 Campaign을 다시 수신하면, Braze는 새로운 ID를 할당합니다. 커스텀 구현에서 노출 횟수, 클릭, 해제를 기록할 때 카드 `id`를 참조하세요. # Content Cards에서 딥링킹 Source: /docs/ko/developer_guide/content_cards/deep_linking/index.md # Content Cards에서 딥링킹 {#deep-linking-in-content-cards} > Braze SDK를 사용하여 Content Cards 내에서 딥링크하는 방법을 알아보세요. 딥링크에 대해 더 알아보려면 [딥링킹이란?](https://www.braze.com/docs/ko/ko/user_guide/messaging/design_and_edit/personalize/actions_and_media_urls/#what-is-deep-linking)을 확인하세요. 현재 Content Cards 딥링크는 웹 Braze SDK에서 지원되지 않습니다. ## Prerequisites Before you can use this feature, you'll need to [integrate the Android Braze SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=android). ## Creating a universal delegate The Android SDK provides the ability to set a single delegate object to custom handle all deep links opened by Braze across Content Cards, in-app messages, and push notifications. Your delegate object should implement the [`IBrazeDeeplinkHandler`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.ui/-braze-deeplink-handler/index.html) interface and be set using [`BrazeDeeplinkHandler.setBrazeDeeplinkHandler()`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.ui/-braze-deeplink-handler/-companion/set-braze-deeplink-handler.html). In most cases, the delegate should be set in your app's `Application.onCreate()`. The following is an example of overriding the default [`UriAction`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.ui.actions/-uri-action/index.html) behavior with custom intent flags and custom behavior for YouTube URLs: ```java public class CustomDeeplinkHandler implements IBrazeDeeplinkHandler { private static final String TAG = BrazeLogger.getBrazeLogTag(CustomDeeplinkHandler.class); @Override public void gotoUri(Context context, UriAction uriAction) { String uri = uriAction.getUri().toString(); // Open YouTube URLs in the YouTube app and not our app if (!StringUtils.isNullOrBlank(uri) && uri.contains("youtube.com")) { uriAction.setUseWebView(false); } CustomUriAction customUriAction = new CustomUriAction(uriAction); customUriAction.execute(context); } public static class CustomUriAction extends UriAction { public CustomUriAction(@NonNull UriAction uriAction) { super(uriAction); } @Override protected void openUriWithActionView(Context context, Uri uri, Bundle extras) { Intent intent = getActionViewIntent(context, uri, extras); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP); if (intent.resolveActivity(context.getPackageManager()) != null) { context.startActivity(intent); } else { BrazeLogger.w(TAG, "Could not find appropriate activity to open for deep link " + uri + "."); } } } } ``` ```kotlin class CustomDeeplinkHandler : IBrazeDeeplinkHandler { override fun gotoUri(context: Context, uriAction: UriAction) { val uri = uriAction.uri.toString() // Open YouTube URLs in the YouTube app and not our app if (!StringUtils.isNullOrBlank(uri) && uri.contains("youtube.com")) { uriAction.useWebView = false } val customUriAction = CustomUriAction(uriAction) customUriAction.execute(context) } class CustomUriAction(uriAction: UriAction) : UriAction(uriAction) { override fun openUriWithActionView(context: Context, uri: Uri, extras: Bundle) { val intent = getActionViewIntent(context, uri, extras) intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP if (intent.resolveActivity(context.packageManager) != null) { context.startActivity(intent) } else { BrazeLogger.w(TAG, "Could not find appropriate activity to open for deep link $uri.") } } } companion object { private val TAG = BrazeLogger.getBrazeLogTag(CustomDeeplinkHandler::class.java) } } ``` ## Deep linking to app settings To allow deep links to directly open your app's settings, you'll need a custom `BrazeDeeplinkHandler`. In the following example, the presence of a custom key-value pair called `open_notification_page` will make the deep link open the app's settings page: ```java BrazeDeeplinkHandler.setBrazeDeeplinkHandler(new IBrazeDeeplinkHandler() { @Override public void gotoUri(Context context, UriAction uriAction) { final Bundle extras = uriAction.getExtras(); if (extras.containsKey("open_notification_page")) { Intent intent = new Intent(); intent.setAction("android.settings.APP_NOTIFICATION_SETTINGS"); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); //for Android 5-7 intent.putExtra("app_package", context.getPackageName()); intent.putExtra("app_uid", context.getApplicationInfo().uid); // for Android 8 and later intent.putExtra("android.provider.extra.APP_PACKAGE", context.getPackageName()); context.startActivity(intent); } } }); ``` ```kotlin BrazeDeeplinkHandler.setBrazeDeeplinkHandler(object : IBrazeDeeplinkHandler { override fun gotoUri(context: Context, uriAction: UriAction) { val extras = uriAction.extras if (extras.containsKey("open_notification_page")) { val intent = Intent() intent.action = "android.settings.APP_NOTIFICATION_SETTINGS" intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK //for Android 5-7 intent.putExtra("app_package", context.packageName) intent.putExtra("app_uid", context.applicationInfo.uid) // for Android 8 and later intent.putExtra("android.provider.extra.APP_PACKAGE", context.packageName) context.startActivity(intent) } } }) ``` ## Customizing WebView activity {#Custom_Webview_Activity} When Braze opens website deeplinks inside the app, the deeplinks are handled by [`BrazeWebViewActivity`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.ui/-braze-web-view-activity/index.html). **Note:** For custom HTML in-app messages, links configured with `target="_blank"` open in the device's default web browser and are not handled by `BrazeWebViewActivity`. To change this: 1. Create a new Activity that handles the target URL from `Intent.getExtras()` with the key `com.braze.Constants.BRAZE_WEBVIEW_URL_EXTRA`. For an example, see [`BrazeWebViewActivity.kt`](https://github.com/braze-inc/braze-android-sdk/blob/master/android-sdk-ui/src/main/java/com/braze/ui/BrazeWebViewActivity.kt). 2. Add that activity to `AndroidManifest.xml` and set `exported` to `false`. ```xml ``` 3. Set your custom Activity in a `BrazeConfig` [builder object](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.configuration/-braze-config/-builder/set-custom-web-view-activity-class.html). Build the builder and pass it to [`Braze.configure()`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze/-braze/-companion/configure.html) in your [`Application.onCreate()`](https://developer.android.com/reference/android/app/Application.html#onCreate()). ```java BrazeConfig brazeConfig = new BrazeConfig.Builder() .setCustomWebViewActivityClass(MyCustomWebViewActivity::class) ... .build(); Braze.configure(this, brazeConfig); ``` ```kotlin val brazeConfig = BrazeConfig.Builder() .setCustomWebViewActivityClass(MyCustomWebViewActivity::class.java) ... .build() Braze.configure(this, brazeConfig) ``` ## Troubleshooting If deep links from push notifications aren't working on Android, try the following steps: 1. **Test the deep link outside of Braze.** Open the deep link URL from another app, such as email or a browser. If it doesn't open your app, the deep link may not be configured correctly in your `AndroidManifest.xml`. For more information, see Android's [Create Deep Links](https://developer.android.com/training/app-links/deep-linking) documentation. 2. **Check that automatic deep link handling is enabled.** Verify that `com_braze_handle_push_deep_links_automatically` is set to `true` in `braze.xml`, or set this option through [runtime configuration](https://www.braze.com/docs/ko/ko/developer_guide/sdk_initalization/?sdktab=android). Without this setting, Braze doesn't automatically open your app and deep link destination when someone taps a push notification. 3. **Verify your deep link handler delegate.** If you set a custom `IBrazeDeeplinkHandler`, confirm that your `gotoUri` implementation handles the URI and doesn't drop it. 4. **Test across channels.** If the same deep link works in an in-app message but not from push, the issue is likely in your push deep link handling, not in the deep link itself. ## Using Jetpack Compose To handle deeplinks when using Jetpack Compose with NavHost: 1. Ensure that the activity handling your deeplink is registered in the Android Manifest. ```xml ``` 2. In NavHost, specify which deeplinks you want it to handle. ```kotlin composableWithCompositionLocal( route = "YOUR_ROUTE_HERE", deepLinks = listOf(navDeepLink { uriPattern = "myapp://articles/{${MainDestinations.ARTICLE_ID_KEY}}" }), arguments = listOf( navArgument(MainDestinations.ARTICLE_ID_KEY) { type = NavType.LongType } ), ) { backStackEntry -> val arguments = requireNotNull(backStackEntry.arguments) val articleId = arguments.getLong(MainDestinations.ARTICLE_ID_KEY) ArticleDetail( articleId ) } ``` 3. Depending on your app architecture, you may need to handle the new intent that's sent to your current activity as well. ```kotlin DisposableEffect(Unit) { val listener = Consumer { navHostController.handleDeepLink(it) } addOnNewIntentListener(listener) onDispose { removeOnNewIntentListener(listener) } } ``` ## Prerequisites Before you can use this feature, you'll need to [integrate the Swift Braze SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=swift). **Tip:** For help choosing between custom scheme deep links, universal links, and "Open Web URL Inside App," see [iOS deep linking guide](https://www.braze.com/docs/ko/ko/developer_guide/push_notifications/ios_deep_linking_guide). For troubleshooting, see [Deep linking troubleshooting](https://www.braze.com/docs/ko/ko/developer_guide/push_notifications/deep_linking_troubleshooting). ## Handling deep links ### Step 1: Register a scheme {#register-a-scheme} To handle deep linking, a custom scheme must be stated in your `Info.plist` file. The navigation structure is defined by an array of dictionaries. Each of those dictionaries contains an array of strings. Use Xcode to edit your `Info.plist` file: 1. Add a new key, `URL types`. Xcode will automatically make this an array containing a dictionary called `Item 0`. 2. Within `Item 0`, add a key `URL identifier`. Set the value to your custom scheme. 3. Within `Item 0`, add a key `URL Schemes`. This will automatically be an array containing a `Item 0` string. 4. Set `URL Schemes` >> `Item 0` to your custom scheme. Alternatively, if you wish to edit your `Info.plist` file directly, you can follow this spec: ```html CFBundleURLTypes CFBundleURLName YOUR.SCHEME CFBundleURLSchemes YOUR.SCHEME ``` ### Step 2: Add a scheme allowlist You must declare the URL schemes you wish to pass to `canOpenURL(_:)` by adding the `LSApplicationQueriesSchemes` key to your app's Info.plist file. Attempting to call schemes outside this allowlist will cause the system to record an error in the device's logs, and the deep link will not open. An example of this error will look like this: ``` : -canOpenURL: failed for URL: "yourapp://deeplink" – error: "This app is not allowed to query for scheme yourapp" ``` For example, if an in-app message should open the Facebook app when tapped, the app has to have the Facebook custom scheme (`fb`) in your allowlist. Otherwise, the system will reject the deep link. Deep links that direct to a page or view inside your own app still require that your app's custom scheme be listed in your app's `Info.plist`. Your example allowlist might look something like: ```html LSApplicationQueriesSchemes myapp fb twitter ``` For more information, refer to [Apple's documentation](https://developer.apple.com/library/content/documentation/General/Reference/InfoPlistKeyReference/Articles/LaunchServicesKeys.html#//apple_ref/doc/uid/TP40009250-SW14) on the `LSApplicationQueriesSchemes` key. ### Step 3: Implement a handler After activating your app, iOS will call the method [`application:openURL:options:`](https://developer.apple.com/reference/uikit/uiapplicationdelegate/1623112-application?language=objc). The important argument is the [NSURL](https://developer.apple.com/library/ios/DOCUMENTATION/Cocoa/Reference/Foundation/Classes/NSURL_Class/Reference/Reference.html#//apple_ref/doc/c_ref/NSURL) object. ```swift func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool { let path = url.path let query = url.query // Insert your code here to take some action based upon the path and query. return true } ``` ```objc - (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary *)options { NSString *path = [url path]; NSString *query = [url query]; // Insert your code here to take some action based upon the path and query. return YES; } ``` ## App Transport Security (ATS) As defined by [Apple](https://developer.apple.com/library/prerelease/ios/releasenotes/General/WhatsNewIniOS/Articles/iOS9.html#//apple_ref/doc/uid/TP40016198-SW14), "App Transport Security is a feature that improves the security of connections between an app and web services. The feature consists of default connection requirements that conform to best practices for secure connections. Apps can override this default behavior and turn off transport security." ATS is applied by default. It requires that all connections use HTTPS and are encrypted using TLS 1.2 with forward secrecy. Refer to [Requirements for Connecting Using ATS](https://developer.apple.com/library/ios/documentation/General/Reference/InfoPlistKeyReference/Articles/CocoaKeys.html#//apple_ref/doc/uid/TP40009251-SW35) for more information. All images served by Braze to end devices are handled by a content delivery network ("CDN") that supports TLS 1.2 and is compatible with ATS. Unless they are specified as exceptions in your application's `Info.plist`, connections that do not follow these requirements will fail with errors that are similar to the following. **Example Error 1:** ```bash CFNetwork SSLHandshake failed (-9801) Error Domain=NSURLErrorDomain Code=-1200 "An SSL error has occurred, and a secure connection to the server cannot be made." ``` **Example Error 2:** ```bash NSURLSession/NSURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9802) ``` ATS compliance is enforced for links opened within the mobile app (our default handling of clicked links) and does not apply to sites opened externally via a web browser. ### Working with ATS You can handle ATS in either of the following ways, but we recommend **complying with ATS requirements**. Your Braze integration can satisfy ATS requirements by ensuring that any existing links you drive users to (for example, though in-app message and push campaigns) satisfy ATS requirements. While there are ways to bypass ATS restrictions, our recommendation is to ensure that all linked URLs are ATS-compliant. Given Apple's increasing emphasis on application security, the following approaches to allowing ATS exceptions are not guaranteed to be supported by Apple. You can allow a subset of links with certain domains or schemes to be treated as exceptions to the ATS rules. Your Braze integration will satisfy ATS requirements if every link you use in a Braze messaging channel is either ATS compliant or handled by an exception. To add a domain as an exception of the ATS, add following to your app's `Info.plist` file: ```html NSAppTransportSecurity NSAllowsArbitraryLoads NSExceptionDomains example.com NSExceptionAllowsInsecureHTTPLoads NSIncludesSubdomains ``` Refer to Apple's article on [app transport security keys](https://developer.apple.com/library/ios/documentation/General/Reference/InfoPlistKeyReference/Articles/CocoaKeys.html#//apple_ref/doc/uid/TP40009251-SW33) for more information. You can turn off ATS entirely. Note that this is not recommended practice, due to both lost security protections and future iOS compatibility. To disable ATS, insert the following in your app's `Info.plist` file: ```html NSAppTransportSecurity NSAllowsArbitraryLoads ``` ## Decoding URLs The SDK percent-encodes links to create valid `URL`s. All link characters that are not allowed in a properly formed URL, such as Unicode characters, will be percent escaped. To decode an encoded link, use the `String` property [`removingPercentEncoding`](https://developer.apple.com/documentation/swift/stringprotocol/removingpercentencoding). You must also return `true` in the `BrazeDelegate.braze(_:shouldOpenURL:)`. A call to action is required to trigger the handling of the URL by your app. For example: ```swift func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool { let urlString = url.absoluteString.removingPercentEncoding // Handle urlString return true } ``` ```objc - (BOOL)application:(UIApplication *)application openURL:(NSURL *)url options:(NSDictionary *)options { NSString *urlString = [url.absoluteString stringByRemovingPercentEncoding]; // Handle urlString return YES; } ``` ## Deep linking to app settings You can take advantage of `UIApplicationOpenSettingsURLString` to deep link users to your app's settings from Braze push notifications and in-app messages. To take users from your app into the iOS settings: 1. First, make sure your application is set up for either [scheme-based deep links](#swift_register-a-scheme) or [universal links](#swift_universal-links). 2. Decide on a URI for deep linking to the **Settings** page (for example, `myapp://settings` or `https://www.braze.com/settings`). 3. If you are using custom scheme-based deep links, add the following code to your `application:openURL:options:` method: ```swift func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool { let path = url.path if (path == "settings") { UIApplication.shared.openURL(URL(string:UIApplication.openSettingsURLString)!) } return true } ``` ```objc - (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary *)options { NSString *path = [url path]; if ([path isEqualToString:@"settings"]) { NSURL *settingsURL = [NSURL URLWithString:UIApplicationOpenSettingsURLString]; [[UIApplication sharedApplication] openURL:settingsURL]; } return YES; } ``` ## Customization options {#customization-options} ### Default WebView customization The `Braze.WebViewController` class displays web URLs opened by the SDK, typically when "Open Web URL Inside App" is selected for a web deep link. You can customize the `Braze.WebViewController` via the [`BrazeDelegate.braze(_:willPresentModalWithContext:)`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/brazedelegate/braze(_:willpresentmodalwithcontext:)-12sqy/) delegate method. ### Linking handling customization The `BrazeDelegate` protocol can be used to customize the handling of URLs such as deep links, web URLs, and universal links. To set the delegate during Braze initialization, set a delegate object on the `Braze` instance. Braze will then call your delegate's implementation of `shouldOpenURL` before handling any URIs. When a push notification or in-app message uses **Open web URL inside mobile app**, Braze passes `context.useWebView == true` on [`Braze.URLContext`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/urlcontext). When the message opens the URL in the system browser instead, `useWebView` is `false`. Inspect `context.useWebView` in `braze(_:shouldOpenURL:)` to branch your custom handling—for example, to open an in-app `WebViewController` only when the campaign requested in-app display. #### Universal links {#universal-links} Braze supports universal links in push notifications, in-app messages, and Content Cards. To enable universal link support, [`configuration.forwardUniversalLinks`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/configuration-swift.class/forwarduniversallinks) must be set to `true`. When enabled, Braze will forward universal links to your app's `AppDelegate` via the [`application:continueUserActivity:restorationHandler:`](https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1623072-application) method. Your application also needs to be set up to handle universal links. Refer to [Apple's documentation](https://developer.apple.com/documentation/xcode/supporting-universal-links-in-your-app) to ensure your application is configured correctly for universal links. **Warning:** Universal link forwarding requires access to the application entitlements. When running the application in a simulator, these entitlements are not directly available and universal links are not forwarded to the system handlers. To add support to simulator builds, you can add the application `.entitlements` file to the _Copy Bundle Resources_ build phase. See [`forwardUniversalLinks`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/configuration-swift.class/forwarduniversallinks) documentation for more details. **Note:** The SDK does not query your domains' `apple-app-site-association` file. It performs the differentiation between universal links and regular URLs by looking at the domain name only. As a result, the SDK does not respect any exclusion rule defined in the `apple-app-site-association` per [Supporting associated domains](https://developer.apple.com/documentation/xcode/supporting-associated-domains). ## Examples ### BrazeDelegate Here's an example using `BrazeDelegate`. For more information, see [Braze Swift SDK reference](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/brazedelegate). ```swift func braze(_ braze: Braze, shouldOpenURL context: Braze.URLContext) -> Bool { if context.url.host == "MY-DOMAIN.com" { // Custom handle link here return false } // Let Braze handle links otherwise return true } ``` ```objc - (BOOL)braze:(Braze *)braze shouldOpenURL:(BRZURLContext *)context { if ([[context.url.host lowercaseString] isEqualToString:@"MY-DOMAIN.com"]) { // Custom handle link here return NO; } // Let Braze handle links otherwise return YES; } ``` # 콘텐츠 카드에 GIF 삽입 Source: /docs/ko/developer_guide/content_cards/embedding_gifs/index.md # 콘텐츠 카드에 GIF 삽입 > Braze SDK를 사용하여 콘텐츠 카드에 GIF를 삽입하는 방법을 알아보세요. **Note:** 목록에 없는 래퍼 SDK의 경우 관련 네이티브 Android 또는 Swift 메서드를 대신 사용하세요. Android 및 Swift Braze SDK는 기본적으로 애니메이션 GIF를 지원하지 않으므로 대신 타사 도구를 사용하여 콘텐츠 카드 GIF를 구현해야 합니다. GIF 지원은 웹 SDK 통합에 기본적으로 포함되어 있습니다. ## About GIFs Braze offers the ability to use a custom image library to display animated GIFs. Although the example below uses [Glide](https://bumptech.github.io/glide/), any image library that supports GIFs is compatible. ## Integrating a custom image library ### Step 1: Creating the image loader delegate The Image Loader delegate must implement the following methods: * [`getInAppMessageBitmapFromUrl()`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.images/-i-braze-image-loader/get-in-app-message-bitmap-from-url.html) * [`getPushBitmapFromUrl()`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.images/-i-braze-image-loader/get-push-bitmap-from-url.html) * [`renderUrlIntoCardView()`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.images/-i-braze-image-loader/render-url-into-card-view.html) * [`renderUrlIntoInAppMessageView()`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.images/-i-braze-image-loader/render-url-into-in-app-message-view.html) * [`setOffline()`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.images/-i-braze-image-loader/set-offline.html) The integration example below is taken from the [Glide integration sample app](https://github.com/braze-inc/braze-android-sdk/tree/master/samples/glide-image-integration) included with the Braze Android SDK. ```java import com.braze.support.BrazeLogger; import com.bumptech.glide.load.resource.gif.GifDrawable; import android.graphics.drawable.Drawable; public class GlideBrazeImageLoader implements IBrazeImageLoader { private static final String TAG = GlideBrazeImageLoader.class.getName(); private RequestOptions mRequestOptions = new RequestOptions(); @Override public void renderUrlIntoCardView(Context context, Card card, String imageUrl, ImageView imageView, BrazeViewBounds viewBounds) { renderUrlIntoView(context, imageUrl, imageView); } @Override public void renderUrlIntoInAppMessageView(Context context, IInAppMessage inAppMessage, String imageUrl, ImageView imageView, BrazeViewBounds viewBounds) { renderUrlIntoView(context, imageUrl, imageView); } @Override public Bitmap getPushBitmapFromUrl(Context context, Bundle extras, String imageUrl, BrazeViewBounds viewBounds) { return getBitmapFromUrl(context, imageUrl, viewBounds); } @Override public Bitmap getInAppMessageBitmapFromUrl(Context context, IInAppMessage inAppMessage, String imageUrl, BrazeViewBounds viewBounds) { return getBitmapFromUrl(context, imageUrl, viewBounds); } private void renderUrlIntoView(Context context, String imageUrl, ImageView imageView) { try { final Drawable drawable = Glide.with(context) .load(imageUrl) .apply(mRequestOptions) .submit() .get(); imageView.post(() -> { imageView.setImageDrawable(drawable); if (drawable instanceof GifDrawable) { ((GifDrawable) drawable).start(); } }); } catch (Exception e) { BrazeLogger.e(TAG, "Failed to render URL into view: " + imageUrl, e); } } private Bitmap getBitmapFromUrl(Context context, String imageUrl, BrazeViewBounds viewBounds) { try { return Glide.with(context) .asBitmap() .apply(mRequestOptions) .load(imageUrl).submit().get(); } catch (Exception e) { Log.e(TAG, "Failed to retrieve bitmap at url: " + imageUrl, e); } return null; } @Override public void setOffline(boolean isOffline) { // If the loader is offline, then we should only be retrieving from the cache mRequestOptions = mRequestOptions.onlyRetrieveFromCache(isOffline); } } ``` ```kotlin import com.braze.support.BrazeLogger import com.bumptech.glide.load.resource.gif.GifDrawable class GlideBrazeImageLoader : IBrazeImageLoader { companion object { private val TAG = GlideBrazeImageLoader::class.qualifiedName } private var mRequestOptions = RequestOptions() override fun renderUrlIntoCardView(context: Context, card: Card, imageUrl: String, imageView: ImageView, viewBounds: BrazeViewBounds) { renderUrlIntoView(context, imageUrl, imageView) } override fun renderUrlIntoInAppMessageView(context: Context, inAppMessage: IInAppMessage, imageUrl: String, imageView: ImageView, viewBounds: BrazeViewBounds) { renderUrlIntoView(context, imageUrl, imageView) } override fun getPushBitmapFromUrl(context: Context, extras: Bundle, imageUrl: String, viewBounds: BrazeViewBounds): Bitmap? { return getBitmapFromUrl(context, imageUrl, viewBounds) } override fun getInAppMessageBitmapFromUrl(context: Context, inAppMessage: IInAppMessage, imageUrl: String, viewBounds: BrazeViewBounds): Bitmap? { return getBitmapFromUrl(context, imageUrl, viewBounds) } private fun renderUrlIntoView(context: Context, imageUrl: String, imageView: ImageView) { try { val drawable = Glide.with(context) .load(imageUrl) .apply(mRequestOptions) .submit() .get() imageView.post { imageView.setImageDrawable(drawable) if (drawable is GifDrawable) { drawable.start() } } } catch (e: Exception) { BrazeLogger.e(TAG, "Failed to render URL into view: $imageUrl", e) } } private fun getBitmapFromUrl(context: Context, imageUrl: String, viewBounds: BrazeViewBounds): Bitmap? { try { return Glide.with(context) .asBitmap() .apply(mRequestOptions) .load(imageUrl).submit().get() } catch (e: Exception) { Log.e(TAG, "Failed to retrieve bitmap at url: $imageUrl", e) } return null } override fun setOffline(isOffline: Boolean) { // If the loader is offline, then we should only be retrieving from the cache mRequestOptions = mRequestOptions.onlyRetrieveFromCache(isOffline) } } ``` ### Fixing image loading for Android SDK 36.0.0 and later In Android SDK 36.0.0 and later, `displayInAppMessage()` is a `suspend` function. This means `renderUrlIntoInAppMessageView()` runs on a background thread instead of the main thread. If your custom image loader calls `Glide.into(imageView)` in `renderUrlIntoInAppMessageView()`, your app can fail with "You must call this method on the main thread." To avoid this: 1. Load the image on the background thread with `submit().get()`. 2. Post the UI update to the main thread with `imageView.post { ... }`. 3. If the loaded result is a GIF drawable, start the animation after setting it on the view. This separates image loading from UI rendering, and keeps your custom image loader compatible with Android SDK 36.0.0 and later. This guidance applies to Android custom image loaders. Web in-app messages support GIFs out of the box. The following Kotlin sample uses placeholder values to show this pattern: ```kotlin private const val TAG = "SampleGlideLoader" private const val glideBrazeImageLoaderTag = "sample-loader" private fun renderUrlIntoView( context: Context, imageUrl: String, imageView: ImageView ) { try { val drawable: Drawable = Glide.with(context) .load(imageUrl) .apply(mRequestOptions) .submit() .get() imageView.post { imageView.setImageDrawable(drawable) if (drawable is GifDrawable) { drawable.start() } } } catch (e: Exception) { Log.e(TAG, "$glideBrazeImageLoaderTag renderUrlIntoView failed: url=$imageUrl", e) } } ``` ### Step 2: Setting the image loader delegate The Braze SDK will use any custom image loader set with [`IBrazeImageLoader`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.images/-i-braze-image-loader/index.html). We recommend setting the custom image loader in a custom application subclass: ```java public class GlideIntegrationApplication extends Application { @Override public void onCreate() { super.onCreate(); Braze.getInstance(context).setImageLoader(new GlideBrazeImageLoader()); } } ``` ```kotlin class GlideIntegrationApplication : Application() { override fun onCreate() { super.onCreate() Braze.getInstance(context).imageLoader = GlideBrazeImageLoader() } } ``` ## Custom Image Loading with Jetpack Compose To override image loading with Jetpack Compose, you can pass in a value to [`imageComposable`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.jetpackcompose.contentcards.styling/-content-card-styling/index.html#-808910455%2FProperties%2F-1725759721). This function will take a `Card` and render the image and the modifiers needed. Alternatively, you can use `customCardComposer` of `ContentCardsList` to render the entire card. In the following example, Glide's Compose library is used for the cards listed in the `imageComposable` function: ```kotlin ContentCardsList( cardStyle = ContentCardStyling( imageComposable = { card -> when (card.cardType) { CardType.CAPTIONED_IMAGE -> { val captionedImageCard = card as CaptionedImageCard GlideImage( modifier = Modifier .fillMaxWidth() .wrapContentHeight() .run { if (captionedImageCard.aspectRatio > 0) { aspectRatio(captionedImageCard.aspectRatio) } else { this } }, contentScale = ContentScale.Crop, model = captionedImageCard.url, loading = placeholder(R.drawable.pushpin), contentDescription = "" ) } CardType.IMAGE -> { val imageOnlyCard = card as ImageOnlyCard GlideImage( modifier = Modifier .fillMaxWidth() .run { if (imageOnlyCard.aspectRatio > 0) { aspectRatio(imageOnlyCard.aspectRatio) } else { this } }, contentScale = ContentScale.Crop, model = imageOnlyCard.url, loading = placeholder(R.drawable.pushpin), contentDescription = "" ) } CardType.SHORT_NEWS -> { val shortNews = card as ShortNewsCard GlideImage( modifier = Modifier .width(100.dp) .height(100.dp), model = shortNews.url, loading = placeholder(R.drawable.pushpin), contentDescription = "" ) } else -> Unit } } ) ) ``` ## Prerequisites Before you can use this feature, you'll need to [integrate the Swift Braze SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=swift). ## Integrating a custom image library ### Step 1: Integrate SDWebImage Integrate the [SDWebImage repository](https://github.com/SDWebImage/SDWebImage) into your Xcode project. ### Step 2: Create a new Swift file In your Xcode project, create a new file named `SDWebImageGIFViewProvider.swift` and import the following: ```swift import UIKit import BrazeUI import SDWebImage ``` ### Step 3: Add `GIFViewProvider` Next, add our sample SDWebImage [`GIFViewProvider`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazeui/gifviewprovider/). Your file should be similar to the following: ```swift import UIKit import BrazeUI import SDWebImage extension GIFViewProvider { /// A GIF view provider using [SDWebImage](https://github.com/SDWebImage/SDWebImage) as a /// rendering library. public static let sdWebImage = Self( view: { SDAnimatedImageView(image: image(for: $0)) }, updateView: { ($0 as? SDAnimatedImageView)?.image = image(for: $1) } ) private static func image(for url: URL?) -> UIImage? { guard let url else { return nil } return url.pathExtension == "gif" ? SDAnimatedImage(contentsOfFile: url.path) : UIImage(contentsOfFile: url.path) } } ``` ### Step 4: Modify your `AppDelegate.swift` In your project's `AppDelegate.swift`, add GIF support to your `BrazeUI` components using `GIFViewProvider`. Your file should be similar to the following: ```swift import UIKit import BrazeKit import BrazeUI @main class AppDelegate: UIResponder, UIApplicationDelegate { static var braze: Braze? = nil func application( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? ) -> Bool { /* ... */ GIFViewProvider.shared = .sdWebImage return true } } ``` # Tutorial: 콘텐츠 카드로 받은편지함 만들기 Source: /docs/ko/developer_guide/content_cards/content_card_inbox/index.md # Tutorial: 콘텐츠 카드로 받은편지함 만들기 > 이 튜토리얼의 샘플 코드를 따라 Braze 콘텐츠 카드로 받은편지함을 구축하세요. ## Prerequisites Before you can use this feature, you'll need to [integrate the Android Braze SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=android). ## Android(Compose)용 콘텐츠 카드로 받은편지함 만들기 **Important:** We're piloting this new tutorial format. [Tell us what you think](https://docs.google.com/forms/d/e/1FAIpQLSe_5uhWM7eXXk9F_gviO_pvA4rkYO3WA9B6tNJZ3TY91md5bw/viewform?usp=pp_url&entry.569173304=General+Feedback) — your feedback helps us improve future guides. ```kotlin file=MainApplication.kt import android.app.Application import com.braze.Braze import com.braze.support.BrazeLogger import com.braze.configuration.BrazeConfig import android.util.Log class ContentCardsApplication : Application() { override fun onCreate() { super.onCreate() // Turn on verbose Braze logging BrazeLogger.enableVerboseLogging() // Configure Braze with your SDK key & endpoint val config = BrazeConfig.Builder() .setApiKey("YOUR_API_KEY") .setCustomEndpoint("YOUR_API_ENDPOINT") .build() Braze.configure(this, config) } } ``` ```kotlin file=ContentCardsInboxScreen.kt import android.content.Intent import android.net.Uri import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.material3.* import androidx.compose.runtime.* import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import com.braze.Braze import com.braze.events.ContentCardsUpdatedEvent import com.braze.events.IEventSubscriber import com.braze.models.cards.* @Composable fun ContentCardInboxScreen() { val context = LocalContext.current var cards by remember { mutableStateOf>(emptyList()) } val loggedImpressions = remember { mutableSetOf() } DisposableEffect(Unit) { val subscriber = IEventSubscriber { event -> cards = event.allCards.filter { !it.isControl } } Braze.getInstance(context).subscribeToContentCardsUpdates(subscriber) Braze.getInstance(context).requestContentCardsRefresh(false) onDispose { Braze.getInstance(context) .removeSingleSubscription(subscriber, ContentCardsUpdatedEvent::class.java) } } Column(modifier = Modifier.fillMaxSize()) { Text( text = "Message Inbox", fontSize = 20.sp, fontWeight = FontWeight.Bold, modifier = Modifier.padding(start = 16.dp, end = 16.dp, top = 12.dp, bottom = 8.dp) ) LazyColumn( modifier = Modifier .fillMaxSize() .padding(horizontal = 16.dp) ) { items(cards, key = { it.id }) { card -> ContentCardItem( card = card, onImpression = { if (!loggedImpressions.contains(card.id)) { card.logImpression() loggedImpressions.add(card.id) } }, onClick = { card.logClick() card.url?.let { context.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(it))) } } ) } } } } @Composable fun ContentCardItem( card: Card, onImpression: () -> Unit, onClick: () -> Unit ) { // Log impression when the card becomes visible LaunchedEffect(card.id) { onImpression() } val title = when (card) { is CaptionedImageCard -> card.title is ShortNewsCard -> card.title is TextAnnouncementCard -> card.title else -> null } val description = when (card) { is CaptionedImageCard -> card.description is ShortNewsCard -> card.description is TextAnnouncementCard -> card.description else -> null } Card( modifier = Modifier .fillMaxWidth() .padding(vertical = 4.dp) .clickable { onClick() } ) { Column(modifier = Modifier.padding(16.dp)) { title?.let { Text( text = it, fontWeight = FontWeight.Bold, fontSize = 16.sp ) } description?.let { Spacer(modifier = Modifier.height(4.dp)) Text( text = it, fontSize = 14.sp ) } } } } ``` !!단계 lines-MainApplication.kt=12 #### 1\. 디버깅 활성화(선택 사항) 개발 중 문제 해결을 쉽게 하기 위해 디버깅을 활성화하는 것을 고려하세요. !!단계 lines-ContentCardsInboxScreen.kt=47-69 #### 2\. UI 뷰 구축하기 Jetpack Compose의 경우, 스크롤 가능한 목록에 콘텐츠 카드를 표시하기 위해 [`LazyColumn`]()을 사용하세요. !!단계 lines-ContentCardsInboxScreen.kt=25-37 #### 3\. 콘텐츠 카드 업데이트 구독하기 구독 생명주기를 관리하기 위해 [`DisposableEffect`]()을 사용하여 컴포저블이 구성에서 벗어날 때 적절한 정리를 보장하세요. !!단계 lines-ContentCardsInboxScreen.kt=84-95 #### 4\. 커스텀 받은편지함 UI 구축하기 [속성]()과 같은 콘텐츠 카드 `title`, `description`, `url`를 사용하면 특정 UI 요구 사항에 맞는 콘텐츠 카드를 구축할 수 있습니다. 이 경우, 우리는 Jetpack Compose의 `Card`와 `Column` 컴포저블로 받은편지함을 구축하고 있습니다. !!단계 lines-ContentCardsInboxScreen.kt=57,62 #### 5\. 노출 및 클릭 추적하기 콘텐츠 카드에 대해 사용할 수 있는 [`logImpressions`]() 및 [`logClick`]() 메서드를 사용하여 노출 및 클릭을 기록할 수 있습니다. 노출은 사용자가 카드를 볼 때 한 번만 기록해야 합니다. 카드가 보일 때 노출을 기록하기 위해 `LaunchedEffect`을 사용하세요. 노출이 올바르게 기록되도록 앱의 뷰 생명주기와 사용 사례를 고려해야 할 수 있습니다. ## Android용 콘텐츠 카드로 받은편지함 만들기 (RecyclerView) ```kotlin file=MainApplication.kt import android.app.Application import com.braze.Braze import com.braze.support.BrazeLogger import com.braze.configuration.BrazeConfig import android.util.Log class ContentCardsApplication : Application() { override fun onCreate() { super.onCreate() // Turn on verbose Braze logging BrazeLogger.enableVerboseLogging() // Configure Braze with your SDK key & endpoint val config = BrazeConfig.Builder() .setApiKey("YOUR_API_KEY") .setCustomEndpoint("YOUR_API_ENDPOINT") .build() Braze.configure(this, config) } } ``` ```kotlin file=ContentCardInboxActivity.kt import android.content.Intent import android.net.Uri import android.os.Bundle import androidx.activity.ComponentActivity import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import android.view.* import android.widget.TextView import com.braze.Braze import com.braze.events.ContentCardsUpdatedEvent import com.braze.events.IEventSubscriber import com.braze.models.cards.* class ContentCardsActivity : ComponentActivity() { private val cards = mutableListOf() private var subscriber: IEventSubscriber? = null private lateinit var recyclerView: RecyclerView private val adapter = ContentCardAdapter() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.content_card_inbox) recyclerView = findViewById(R.id.contentCardsRecyclerView) recyclerView.layoutManager = LinearLayoutManager(this) recyclerView.adapter = adapter // Prepare the subscriber (attach/detach in onStart/onStop) subscriber = IEventSubscriber { event -> runOnUiThread { cards.clear() cards.addAll(event.allCards.filter { !it.isControl }) adapter.notifyDataSetChanged() } } } override fun onStart() { super.onStart() subscriber?.let { Braze.getInstance(this).subscribeToContentCardsUpdates(it) } // Fetch fresh cards Braze.getInstance(this).requestContentCardsRefresh(false) } override fun onStop() { // Avoid leaks by removing the subscription when not visible Braze.getInstance(this) .removeSingleSubscription(subscriber, ContentCardsUpdatedEvent::class.java) super.onStop() } inner class ContentCardAdapter : RecyclerView.Adapter() { inner class CardViewHolder(v: View) : RecyclerView.ViewHolder(v) { val title: TextView = v.findViewById(android.R.id.text1) val description: TextView = v.findViewById(android.R.id.text2) } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CardViewHolder { val view = LayoutInflater.from(parent.context) .inflate(android.R.layout.simple_list_item_2, parent, false) return CardViewHolder(view) } override fun getItemCount() = cards.size override fun onBindViewHolder(holder: CardViewHolder, position: Int) { val card = cards[position] val title = when (card) { is CaptionedImageCard -> card.title is ShortNewsCard -> card.title is TextAnnouncementCard -> card.title else -> null } val description = when (card) { is CaptionedImageCard -> card.description is ShortNewsCard -> card.description is TextAnnouncementCard -> card.description else -> null } holder.title.text = title.orEmpty() holder.description.text = description.orEmpty() // Naive impression guard: only log the first time we bind a not-yet-viewed card. if (!card.viewed) card.logImpression() holder.itemView.setOnClickListener { card.logClick() card.url?.let { startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(it))) } } } } } ``` ```xml file=content_card_inbox.xml ``` !!단계 lines-MainApplication.kt=12 #### 1\. 디버깅 활성화(선택 사항) 개발 중 문제 해결을 쉽게 하기 위해 디버깅을 활성화하는 것을 고려하세요. !!단계 lines-content_card_inbox.xml=1-24 #### 2\. UI 뷰 구축하기 이 튜토리얼에서는 Android의 [`RecyclerView`]()를 사용하여 콘텐츠 카드를 표시하지만, 사용 사례에 맞는 클래스와 구성 요소로 UI를 구축하는 것을 권장합니다. Braze는 기본적으로 UI를 제공하지만, 이 튜토리얼은 외관과 동작을 사용자 정의하기 위해 사용자 정의 보기를 만드는 방법을 안내합니다. !!단계 lines-ContentCardInboxActivity.kt=29-35,40-42,44 #### 3\. 콘텐츠 카드 업데이트 구독하기 새 콘텐츠 카드가 사용 가능할 때 UI가 응답하도록 [`subscribeToContentCardsUpdates`]()를 사용하세요. 여기에서 구독자는 활동 생명 주기 후크 내에서 등록 및 제거됩니다. !!단계 lines-ContentCardInboxActivity.kt=73-84 #### 4\. 커스텀 받은편지함 UI 구축하기 [속성]()과 같은 콘텐츠 카드 `title`, `description`, `url`를 사용하면 특정 UI 요구 사항에 맞는 콘텐츠 카드를 구축할 수 있습니다. 이 경우, 우리는 Android의 기본 `RecyclerView`로 받은편지함을 구축하고 있습니다. !!단계 lines-ContentCardInboxActivity.kt=90,93 #### 5\. 노출 및 클릭 추적하기 콘텐츠 카드에 대해 사용할 수 있는 [`logImpressions`]() 및 [`logClick`]() 메서드를 사용하여 노출 및 클릭을 기록할 수 있습니다. 노출은 사용자가 카드를 볼 때 한 번만 기록해야 합니다. 여기에서는 카드별 플래그를 사용하여 중복 로그를 방지하는 단순한 메커니즘을 사용합니다. 노출이 올바르게 기록되도록 앱의 뷰 생명주기와 사용 사례를 고려해야 할 수 있습니다. ## Prerequisites Before you can use this feature, you'll need to [integrate the Swift Braze SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=swift). 또한 [Swift용 인앱 메시지 활성화](https://www.braze.com/docs/ko/ko/developer_guide/in_app_messages/?sdktab=swift#swift_enabling-in-app-messages)가 필요합니다. ## Swift용 콘텐츠 카드로 받은편지함 만들기 **Important:** We're piloting this new tutorial format. [Tell us what you think](https://docs.google.com/forms/d/e/1FAIpQLSe_5uhWM7eXXk9F_gviO_pvA4rkYO3WA9B6tNJZ3TY91md5bw/viewform?usp=pp_url&entry.569173304=General+Feedback) — your feedback helps us improve future guides. ```swift file=AppDelegate.swift import SwiftUI import BrazeKit import BrazeUI class AppDelegate: UIResponder, UIApplicationDelegate { static var braze: Braze! func application( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? ) -> Bool { // Braze configuration with your SDK API key and endpoint let configuration = Braze.Configuration(apiKey: "YOUR_API_ENDPOINT", endpoint: "YOUR_API_KEY") configuration.logger.level = .debug // Initialize Braze SDK instance AppDelegate.braze = Braze(configuration: configuration) return true } } struct InboxViewControllerRepresentable: UIViewControllerRepresentable { func makeUIViewController(context: Context) -> UINavigationController { let vc = BrazeInboxViewController(style: .plain) return UINavigationController(rootViewController: vc) } func updateUIViewController(_ uiViewController: UINavigationController, context: Context) {} } struct ContentView: View { var body: some View { NavigationView { InboxViewControllerRepresentable() .navigationTitle("Message Inbox") } } } ``` ```swift file=SampleApp.swift import SwiftUI @main struct SampleApp: App { @UIApplicationDelegateAdaptor(AppDelegate.self) var delegate var body: some Scene { WindowGroup { ContentView() } } } ``` ```swift file=BrazeInboxView.swift import SwiftUI import UIKit import BrazeKit class BrazeInboxViewController: UITableViewController { private var cards: [Braze.ContentCard] = [] private var subscription: Any? private var loggedImpressions = Set() override func viewDidLoad() { super.viewDidLoad() tableView.register(UITableViewCell.self, forCellReuseIdentifier: "CardCell") tableView.rowHeight = 100 subscription = AppDelegate.braze.contentCards.subscribeToUpdates { [weak self] updatedCards in self?.cards = updatedCards self?.tableView.reloadData() } AppDelegate.braze.contentCards.requestRefresh() } override func numberOfSections(in tableView: UITableView) -> Int { 1 } override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { cards.count } override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let card = cards[indexPath.row] let cell = tableView.dequeueReusableCell(withIdentifier: "CardCell", for: indexPath) // Work with the content card's title and description cell.textLabel?.numberOfLines = 2 cell.textLabel?.text = [card.title, card.description].compactMap { $0 }.joined(separator: "\n") return cell } override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { let card = cards[indexPath.row] card.logClick(using: AppDelegate.braze) if let url = card.clickAction?.url { UIApplication.shared.open(url, options: [:], completionHandler: nil) } tableView.deselectRow(at: indexPath, animated: true) } override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) { let card = cards[indexPath.row] if !loggedImpressions.contains(card.id) { card.logImpression(using: AppDelegate.braze) } } } ``` !!단계 lines-AppDelegate.swift=15 #### 1\. 디버깅 활성화(선택 사항) 개발 중 문제 해결을 쉽게 하기 위해 디버깅을 활성화하는 것을 고려하세요. !!단계 lines-BrazeInboxView.swift=5 #### 2\. UI 뷰 구축하기 이 튜토리얼에서는 Swift의 [`UITableViewController`](https://developer.apple.com/documentation/uikit/uitableviewcontroller)를 사용하지만, 사용 사례에 맞는 클래스와 구성 요소로 UI를 구축하는 것을 권장합니다. !!단계 라인-BrazeInboxView.swift=15-20 #### 3\. 콘텐츠 카드 업데이트 구독하기 콘텐츠 카드 리스너에 가입하여 최신 업데이트를 받고, 그 후 `requestRefresh()`을 호출하여 해당 사용자에 대한 최신 콘텐츠 카드를 요청하세요. !!단계 lines-BrazeInboxView.swift=34-35 #### 4\. 커스텀 받은편지함 UI 구축하기 [콘텐츠 카드](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/contentcard)을 사용하여 `title`, `description`, `imageUrl`와 같은 특정 UI 요구 사항에 맞는 콘텐츠 카드를 구축할 수 있습니다. 이 경우, 우리는 Swift의 기본 테이블 API로 받은편지함을 구축하고 있습니다. !!단계 lines-BrazeInboxView.swift=8,43,49-56 #### 5\. 노출 및 클릭 추적하기 콘텐츠 카드에 사용할 수 있는 [`logClick(using:)`]() 및 [`logImpression(using:)`]() 메서드를 사용하여 노출 및 클릭을 기록할 수 있습니다. 또한, 해제에 대해 [`logDismissed(using:)`]()을 사용할 수 있습니다. 노출은 사용자가 볼 때 한 번만 기록되어야 합니다. 여기서는 `Set`과 `willDisplay`를 사용하여 단순한 메커니즘을 사용하여 이를 달성합니다. 앱의 UI 생명 주기와 사용 사례를 고려하여 노출이 올바르게 기록되도록 해야 할 수 있습니다. ## Prerequisites Before you can use this feature, you'll need to [integrate the Web Braze SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=web). 그러나 추가 설정은 필요하지 않습니다. ## 웹용 콘텐츠 카드로 받은편지함 만들기 **Important:** We're piloting this new tutorial format. [Tell us what you think](https://docs.google.com/forms/d/e/1FAIpQLSe_5uhWM7eXXk9F_gviO_pvA4rkYO3WA9B6tNJZ3TY91md5bw/viewform?usp=pp_url&entry.569173304=General+Feedback) — your feedback helps us improve future guides. ```js file=main.js import * as braze from "@braze/web-sdk"; // Uncomment this if you'd like to run braze web SDK methods in the console // window.braze = braze; // initialize the Braze SDK braze.initialize("YOUR_API_KEY", { baseUrl: "YOUR_API_ENDPOINT", enableLogging: true, }); braze.openSession(); // --- DOM refs --- const listEl = document.getElementById("cards-list"); // --- State for impression de-duping & lookup --- const loggedImpressions = new Set(); const idToCard = new Map(); let observer = null; // Utility: clean observer between renders function resetObserver() { if (observer) observer.disconnect(); observer = new IntersectionObserver(onIntersect, { threshold: 0.6 }); } // Intersection callback: logs impression once when ≥60% visible function onIntersect(entries) { entries.forEach((entry) => { if (!entry.isIntersecting) return; const id = entry.target.dataset.cardId; if (!id || loggedImpressions.has(id)) return; const card = idToCard.get(id); if (!card) return; // Log a single-card impression and stop observing this element braze.logContentCardImpressions([card]); loggedImpressions.add(id); observer.unobserve(entry.target); }); } // Renders cards into the DOM, sets up click + visibility tracking function renderCards(cards) { // Rebuild lookup and observer each render idToCard.clear(); resetObserver(); listEl.textContent = ""; // clear list cards.forEach((card) => { // Skip control-group cards in UI; (optional) you could log impressions for them elsewhere if (card.isControl) return; idToCard.set(card.id, card); const item = document.createElement("article"); item.className = "card-item"; item.dataset.cardId = card.id; const h3 = document.createElement("h3"); h3.textContent = card.title || ""; const p = document.createElement("p"); p.textContent = card.description || ""; let img = undefined; if (card.imageUrl) { img = document.createElement("img"); img.src = card.imageUrl; item.append(img); } const children = [h3, p]; if (img) { children.push(img); } item.append(...children); // Click tracking + action item.addEventListener("click", (e) => { braze.logContentCardClick(card); if (card.url) { // any url-handling logic for your use case } }); listEl.appendChild(item); observer.observe(item); }); } // Subscribe to updates *then* ask for a refresh braze.subscribeToContentCardsUpdates((updates) => { const cards = updates.cards || []; renderCards(cards); }); braze.requestContentCardsRefresh(); ``` ```html file=index.html

Message Inbox

``` !!단계 lines-main.js=3-4,9 #### 1\. 디버깅 활성화(선택 사항) 개발 중 문제 해결을 쉽게 하기 위해 디버깅을 활성화하는 것을 고려하세요. 선택적으로, 콘솔에서 Braze Web SDK 메서드를 실행할 수도 있습니다. !!단계 lines-index.html=1-44 #### 2\. UI 구축 받은편지함 페이지를 위한 UI를 만듭니다. 여기서는 ID `cards-list`가 있는 `div`을 포함하는 기본 HTML 페이지를 구축하고 있습니다. 이는 콘텐츠 카드를 렌더링하기 위한 대상 컨테이너로 사용됩니다. !!단계 lines-main.js=96-99,101 #### 3\. 콘텐츠 카드 업데이트 구독하기 최신 업데이트를 받기 위해 콘텐츠 카드 리스너에 가입하고, 그 후 [`requestContentCardsRefresh()`]()을 호출하여 해당 사용자에 대한 최신 콘텐츠 카드를 요청합니다. 또는 세션 시작 시 자동 새로 고침을 위해 `openSession()` 이전에 가입자를 호출합니다. !!단계 lines-main.js=64,67,70-74 #### 4\. 받은편지함 요소 구축 콘텐츠 카드 [attributes]()와 같은 `title`, `description`, 및 `url`를 사용하면 특정 UI 요구 사항에 맞게 콘텐츠 카드를 표시할 수 있습니다. !!단계 lines-main.js=22-25,28-43,84,91 #### 5\. 노출 및 클릭 추적하기 콘텐츠 카드에 대해 사용할 수 있는 [`logContentCardImpressions`]() 및 [`logContentCardClick`]() 메서드를 사용하여 노출 및 클릭을 기록할 수 있습니다. 또한, 해제에 대해 [`logCardDismissal`]()을 사용할 수 있습니다. 노출은 사용자가 볼 때 한 번만 기록되어야 합니다. 여기서 `IntersectionObserver`와 `Set`가 `card.id`로 키가 지정되어 중복 로그를 방지합니다. 앱의 UI 생명 주기와 사용 사례를 고려하여 노출이 올바르게 기록되도록 해야 할 수 있습니다. # Braze SDK용 인앱 메시지 Source: /docs/ko/developer_guide/in_app_messages/index.md # 인앱 메시지 > 인앱 메시지와 Braze SDK에 대한 설정 방법에 대해 알아보세요. ## Prerequisites Before you can use this feature, you'll need to [integrate the Web Braze SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=web). However, no additional setup is required. ## Message types All in-app messages inherit their prototype from [`InAppMessage`](https://js.appboycdn.com/web-sdk/latest/doc/classes/braze.inappmessage.html), which defines basic behavior and traits for all in-app messages. The prototypical subclasses are [`SlideUpMessage`](https://js.appboycdn.com/web-sdk/latest/doc/classes/braze.slideupmessage.html), [`ModalMessage`](https://js.appboycdn.com/web-sdk/latest/doc/classes/braze.modalmessage.html), [`FullScreenMessage`](https://js.appboycdn.com/web-sdk/latest/doc/classes/braze.fullscreenmessage.html), and [`HtmlMessage`](https://js.appboycdn.com/web-sdk/latest/doc/classes/braze.htmlmessage.html). Each in-app message type is customizable across content, images, icons, click actions, analytics, display, and delivery. [`SlideUp`](https://js.appboycdn.com/web-sdk/latest/doc/classes/braze.slideupmessage.html) in-app messages are so-named because traditionally on mobile platforms, they "slide up" or "slide down" from the top or bottom of the screen. In the Braze Web SDK, these messages are displayed as more of a Growl or Toast style notification to align with the web's dominant paradigm. They cover a small portion of the screen and provide an effective and non-intrusive messaging capability. ![An in-app message sliding from the bottom of a phone screen displaying "Humans are complicated. Custom engagement shouldn't be." In the background is the same in-app message displayed in the bottom corner of a web page.](https://www.braze.com/docs/ko/ko/assets/img/slideup-behavior.gif?7239589ee8c964f354440e07ca4b9db1){: style="border:0px;"} [`Modal`](https://js.appboycdn.com/web-sdk/latest/doc/classes/braze.modalmessage.html) in-app messages appear in the center of the screen and are framed by a translucent panel. Useful for more critical messaging, they can be equipped with up to two click action and analytics-enabled buttons. ![A modal in-app message in the center of a phone screen displaying "Humans are complicated. Custom engagement shouldn't be." In the background is the same in-app message displayed in the center of a web page.](https://www.braze.com/docs/ko/ko/assets/img/modal-behavior.gif?00fa4f83404c611c82cb0816f682e3f3){: style="border:0px;"} [`Full`](https://js.appboycdn.com/web-sdk/latest/doc/classes/braze.fullscreenmessage.html) in-app messages are useful for maximizing the content and impact of your user communication. On narrow browser windows (for example, the mobile web), `full` in-app messages take up the entire browser window. On larger browser windows, `full` in-app messages appear similarly to `modal` in-app messages. The upper half of a `full` in-app message contains an image, and the lower half allows up to eight lines of text as well as up to two click action, and analytics-enabled buttons ![A full screen in-app message shown across an entire phone screen displaying, "Humans are complicated. Custom engagement shouldn't be." In the background is the same in-app message displayed largely in the center of a web page.](https://www.braze.com/docs/ko/ko/assets/img/full-screen-behavior.gif?b47edcbdd910efce932489d1fa592bd0){: style="border:0px;"} [`HTML`](https://js.appboycdn.com/web-sdk/latest/doc/classes/braze.htmlmessage.html) in-app messages are useful for creating fully customized user content. User-defined HTML is displayed in an iFrame and may contain rich content, such as images, fonts, videos, and interactive elements, allowing for full control over message appearance and functionality. These support a JavaScript `brazeBridge` interface to call methods on the Braze Web SDK from within your HTML, see our [best practices](https://www.braze.com/docs/ko/ko/user_guide/message_building_by_channel/in-app_messages/best_practices/) for more details. **Important:** To enable HTML in-app messages through the Web SDK, you **must** supply the `allowUserSuppliedJavascript` initialization option to Braze, for example, `braze.initialize('YOUR-API_KEY', {allowUserSuppliedJavascript: true})`. This is for security reasons. HTML in-app messages can execute JavaScript, so we require a site maintainer to enable them. The following example shows a paginated HTML in-app message: ![An HTML in-app message with the a carousel of content and interactive buttons.](https://www.braze.com/docs/ko/ko/assets/img_archive/ios-html-full-iam.gif?4c6c9603065d4c430d406677e8cb6045) ## Prerequisites Before you can use this feature, you'll need to [integrate the Android Braze SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=android). You'll also need to enable in-app messages. ## Message types Braze offers several default in-app message types, each customizable with messages, images, [Font Awesome](https://fontawesome.com/icons?d=gallery&p=2) icons, click actions, analytics, color schemes, and more. Their basic behavior and traits are defined by the [`IInAppMessage`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.inappmessage/-i-in-app-message/index.html) interface, in a subclass called [`InAppMessageBase`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.inappmessage/-in-app-message-base/index.html). `IInAppMessage` also includes a subinterface, [`IInAppMessageImmersive`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.inappmessage/-i-in-app-message-immersive/index.html), which lets you add close, click-action, and analytics [buttons](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.inappmessage/-message-button/index.html) to your app. **Important:** Keep in mind, in-app messages containing buttons will include the `clickAction` message in the final payload if the click action is added prior to adding the button text. [`slideup`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.inappmessage/-in-app-message-slideup/index.html) in-app messages are so-named because they "slide up" or "slide down" from the top or bottom of the screen. They cover a small portion of the screen and provide an effective and non-intrusive messaging capability. The `slideup` in-app message object extends [`InAppMessageBase`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.inappmessage/-in-app-message-base/index.html). ![An in-app message sliding from the bottom of a phone screen displaying "Humans are complicated. Custom engagement shouldn't be." In the background is the same in-app message displayed in the bottom right corner of a web page.](https://www.braze.com/docs/ko/ko/assets/img/slideup-behavior.gif?7239589ee8c964f354440e07ca4b9db1){: style="border:0px;"} [`modal`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.inappmessage/-in-app-message-modal/index.html) in-app messages appear in the center of the screen and are framed by a translucent panel. Useful for more critical messaging, they can be equipped with two click-action and analytics-enabled buttons. This message type is a subclass of [`InAppMessageImmersiveBase`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.inappmessage/-in-app-message-immersive-base/index.html), an abstract class that implements `IInAppMessageImmersive`, giving you the option to add custom functionality to your locally generated in-app messages. ![A modal in-app message in the center of a phone screen displaying "Humans are complicated. Custom engagement shouldn't be." In the background is the same in-app message displayed in the center of a web page.](https://www.braze.com/docs/ko/ko/assets/img/modal-behavior.gif?00fa4f83404c611c82cb0816f682e3f3){: style="border:0px;"} [`full`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.inappmessage/-in-app-message-full/index.html) in-app messages are useful for maximizing the content and impact of your user communication. The upper half of a `full` in-app message contains an image, and the lower half displays text and up to two click action and analytics-enabled buttons. This message type extends [`InAppMessageImmersiveBase`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.inappmessage/-in-app-message-immersive-base/index.html), giving you the option to add custom functionality to your locally generated in-app messages. ![A full screen in-app message shown across an entire phone screen displaying, "Humans are complicated. Custom engagement shouldn't be." In the background is the same in-app message displayed largely in the center of a web page.](https://www.braze.com/docs/ko/ko/assets/img_archive/In-App_Full.png?ecd62a88d38438aaebbda4cdcc22aa00) [`HTML`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.inappmessage/-in-app-message-html/index.html) in-app messages are useful for creating fully customized user content. User-defined HTML in-app message content is displayed in a `WebView` and may optionally contain other rich content, such as images and fonts, allowing for full control over message appearance and functionality. This message type implements [`IInAppMessageHtml`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.inappmessage/-i-in-app-message-html/index.html), which is a subclass of [`IInAppMessage`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.inappmessage/-i-in-app-message/index.html). **Note:** On Android, links configured with `target="_blank"` in custom HTML in-app messages open in the device's default web browser. Android in-app messages support a JavaScript `brazeBridge` interface to call methods on the Braze Android SDK from within your HTML, see our JavaScript bridge page for more details. ![An HTML in-app message with the a carousel of content and interactive buttons.](https://www.braze.com/docs/ko/ko/assets/img/full-screen-behavior.gif?b47edcbdd910efce932489d1fa592bd0){: style="border:0px;"} **Important:** We currently do not support the display of custom HTML in-app messages in an iFrame on the iOS and Android platforms. **Tip:** You can also define custom in-app message views for your app. For a full walkthrough, see [Setting custom factories](https://www.braze.com/docs/ko/ko/developer_guide/in_app_messages/customization#android_setting-custom-factories). ## Enabling in-app messages ### Step 1: Register `BrazeInAppMessageManager` In-app message display is managed by the [`BrazeInAppMessageManager`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.ui.inappmessage/-braze-in-app-message-manager/index.html) class. Every activity in your app must be registered with the `BrazeInAppMessageManager` to allow it to add in-app message views to the view hierarchy. There are two ways to accomplish this: The [activity lifecycle callback integration](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration#android_step-4-enable-user-session-tracking) handles in-app message registration automatically; no extra integration is required. This is the recommended method for handling in-app message registration. **Warning:** If you're using activity lifecycle callback for automatic registration, do not complete this step. In your [`Application.onCreate()`](https://developer.android.com/reference/android/app/Application.html#onCreate()), call [`ensureSubscribedToInAppMessageEvents()`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.ui.inappmessage/-braze-in-app-message-manager/ensure-subscribed-to-in-app-message-events.html): ```java BrazeInAppMessageManager.getInstance().ensureSubscribedToInAppMessageEvents(context); ``` ```kotlin BrazeInAppMessageManager.getInstance().ensureSubscribedToInAppMessageEvents(context) ``` In every activity where in-app messages can be shown, call [`registerInAppMessageManager()`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.ui.inappmessage/-braze-in-app-message-manager/register-in-app-message-manager.html) in that activity's `onResume()`: ```java @Override public void onResume() { super.onResume(); // Registers the BrazeInAppMessageManager for the current Activity. This Activity will now listen for // in-app messages from Braze. BrazeInAppMessageManager.getInstance().registerInAppMessageManager(activity); } ``` ```kotlin public override fun onResume() { super.onResume() // Registers the BrazeInAppMessageManager for the current Activity. This Activity will now listen for // in-app messages from Braze. BrazeInAppMessageManager.getInstance().registerInAppMessageManager(this) } ``` In every activity where [`registerInAppMessageManager()`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.ui.inappmessage/-braze-in-app-message-manager/register-in-app-message-manager.html) was called, call [`unregisterInAppMessageManager()`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.ui.inappmessage/-braze-in-app-message-manager/unregister-in-app-message-manager.html) in that activity's `onPause()`: ```java @Override public void onPause() { super.onPause(); // Unregisters the BrazeInAppMessageManager for the current Activity. BrazeInAppMessageManager.getInstance().unregisterInAppMessageManager(activity); } ``` ```kotlin public override fun onPause() { super.onPause() // Unregisters the BrazeInAppMessageManager. BrazeInAppMessageManager.getInstance().unregisterInAppMessageManager(this) } ``` ### Step 2: Update the manager's blocklist (optional) In your integration, you may require that certain activities in your app should not show in-app messages. The [activity lifecycle callback integration](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration#android_step-4-enable-user-session-tracking) provides an easy way to accomplish this. The following sample code adds two activities to the in-app message registration blocklist, `SplashActivity` and `SettingsActivity`: ```java public class MyApplication extends Application { @Override public void onCreate() { super.onCreate(); Set inAppMessageBlocklist = new HashSet<>(); inAppMessageBlocklist.add(SplashActivity.class); inAppMessageBlocklist.add(SettingsActivity.class); registerActivityLifecycleCallbacks(new BrazeActivityLifecycleCallbackListener(inAppMessageBlocklist)); } } ``` ```kotlin class MyApplication : Application() { override fun onCreate() { super.onCreate() val inAppMessageBlocklist = HashSet>() inAppMessageBlocklist.add(SplashActivity::class.java) inAppMessageBlocklist.add(SettingsActivity::class.java) registerActivityLifecycleCallbacks(BrazeActivityLifecycleCallbackListener(inAppMessageBlocklist)) } } ``` ## Prerequisites Before you can use this feature, you'll need to [integrate the Swift Braze SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=swift). You'll also need to enable in-app messages. ## Message types Each in-app message type is highly customizable across content, images, icons, click actions, analytics, display, and delivery. They are enumerated types of `Braze.InAppMessage`, which defines basic behavior and traits for all in-app messages. For the full list of in-app message properties and usage, see the [`InAppMessage` class](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/inappmessage). These are the available in-app message types in Braze and how they will look like for end-users. [`Slideup`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/inappmessage/slideup-swift.struct) in-app messages are given this name because they "slide up" or "slide down" from the top or bottom of the screen. They cover a small portion of the screen and provide an effective and non-intrusive messaging capability. ![A slideup in-app message at the bottom and the top of a phone screen.](https://www.braze.com/docs/ko/ko/assets/img/slideup-spec.png?5e0eb3225ef5a9ca264817b8267aad45){: style="max-width:35%;border:none;"} [`Modal`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/inappmessage/modal-swift.struct) in-app messages appear in the center of the screen and are framed by a translucent panel. Useful for more critical messaging, they can be equipped with up to two analytics-enabled buttons. ![A modal in-app message in the center of a phone screen.](https://www.braze.com/docs/ko/ko/assets/img/modal-header-text.png?cef10f16ce8c681a237e5352cebf76f9){: style="max-width:35%;border:none;"} [`Modal Image`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/inappmessage/modalimage-swift.struct) in-app messages appear in the center of the screen and are framed by a translucent panel. These messages are similar to the `Modal` type except without header or message text. Useful for more critical messaging, they can be equipped with up to two analytics-enabled buttons. ![A modal image in-app message in the center of a phone screen.](https://www.braze.com/docs/ko/ko/assets/img/modal-full-image.png?2cda602759102cab22396c78978d712b){: style="max-width:35%;border:none;"} [`Full`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/inappmessage/full-swift.struct) in-app messages are useful for maximizing the content and impact of your user communication. The upper half of a `Full` in-app message contains an image, and the lower half displays text and up to two analytics-enabled buttons. ![A fullscreen in-app message shown across an entire phone screen.](https://www.braze.com/docs/ko/ko/assets/img/full-screen-header-text.png?803a758bf53c33ebc3ff63797676339b){: style="max-width:35%;border:none;"} [`Full Image`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/inappmessage/fullimage-swift.struct) in-app messages are similar to `Full` in-app messages except without header or message text. This message type is useful for maximizing the content and impact of your user communication. A `Full Image` in-app message contains an image spanning the entire screen, with the option to display up to two analytics-enabled buttons. ![A fullscreen image in-app message shown across an entire phone screen.](https://www.braze.com/docs/ko/ko/assets/img/full-screen-image.png?b29bfae801d78d57fcc7c9fdcb7cc0cc){: style="max-width:35%;border:none;"} [`HTML`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/inappmessage/html-swift.struct) in-app messages are useful for creating fully customized user content. User-defined HTML Full in-app message content is displayed in a `WKWebView`and may optionally contain other rich content, such as images and fonts, allowing for full control over message appearance and functionality.

iOS in-app messages support a JavaScript `brazeBridge` interface to call methods on the Braze Web SDK from within your HTML, see our [best practices](https://www.braze.com/docs/ko/ko/user_guide/message_building_by_channel/in-app_messages/best_practices/) for more details. The following example shows a paginated HTML Full in-app message: ![An HTML in-app message with a carousel of content and interactive buttons.](https://www.braze.com/docs/ko/ko/assets/img_archive/ios-html-full-iam.gif?4c6c9603065d4c430d406677e8cb6045) Note that we currently do not support the display of custom HTML in-app messages in an iFrame on the iOS and Android platforms. [`Control`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/inappmessage/control-swift.struct) in-app messages do not contain a UI component and are used primarily for analytics purposes. This type is used to verify receipt of an in-app message sent to a control group. For further details about Intelligent Selection and control groups, refer to [Intelligent Selection](https://www.braze.com/docs/ko/ko/user_guide/brazeai/intelligence/intelligent_selection/). ## Enabling in-app messages ### Step 1: Create an implementation of `BrazeInAppMessagePresenter` To let Braze display in-app messages, create an implementation of the `BrazeInAppMessagePresenter` protocol and assign it to the optional `inAppMessagePresenter` on your Braze instance. You can also use the default Braze UI presenter by instantiating a `BrazeInAppMessageUI` object. Note that you will need to import the `BrazeUI` library to access the `BrazeInAppMessageUI` class. ```swift AppDelegate.braze?.inAppMessagePresenter = BrazeInAppMessageUI() ``` ```objc AppDelegate.braze.inAppMessagePresenter = [[BrazeInAppMessageUI alloc] init]; ``` ### Step 2: Handle no matching triggers Implement [`BrazeDelegate.(_:noMatchingTriggerForEvent)`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/brazedelegate/braze(_:nomatchingtriggerforevent:)-8rt7y/) within the relevant `BrazeDelegate` class. When Braze fails to find a matching trigger for a particular event, it will call this method automatically. ## Prerequisites Before you can use this feature, you'll need to [integrate the Android Braze SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=android). ## About TV and OTT support The Android Braze SDK natively supports displaying in-app messages on OTT devices like Android TV or Fire Stick. However, there's some key differences between native Android and OTT in-app messages. For OTT devices: - In-app messages that require touch mode, such as slideup, are disabled on OTT. - The currently selected or focused item, such as a button or close button, will be highlighted. - Body clicks on the in-app message itself, such as not on a button, are not supported. ## Prerequisites Before you can use this feature, you'll need to [integrate the Cordova Braze SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=cordova). ## Message types Braze offers several default in-app message types, each customizable with messages, images, [Font Awesome](https://fontawesome.com/icons?d=gallery&p=2) icons, click actions, analytics, color schemes, and more. Their basic behavior and traits are defined by the [`IInAppMessage`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.inappmessage/-i-in-app-message/index.html) interface, in a subclass called [`InAppMessageBase`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.inappmessage/-in-app-message-base/index.html). `IInAppMessage` also includes a subinterface, [`IInAppMessageImmersive`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.inappmessage/-i-in-app-message-immersive/index.html), which lets you add close, click-action, and analytics [buttons](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.inappmessage/-message-button/index.html) to your app. **Important:** Keep in mind, in-app messages containing buttons will include the `clickAction` message in the final payload if the click action is added prior to adding the button text. [`slideup`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.inappmessage/-in-app-message-slideup/index.html) in-app messages are so-named because they "slide up" or "slide down" from the top or bottom of the screen. They cover a small portion of the screen and provide an effective and non-intrusive messaging capability. The `slideup` in-app message object extends [`InAppMessageBase`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.inappmessage/-in-app-message-base/index.html). ![An in-app message sliding from the bottom of a phone screen displaying "Humans are complicated. Custom engagement shouldn't be." In the background is the same in-app message displayed in the bottom right corner of a web page.](https://www.braze.com/docs/ko/ko/assets/img/slideup-behavior.gif?7239589ee8c964f354440e07ca4b9db1){: style="border:0px;"} [`modal`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.inappmessage/-in-app-message-modal/index.html) in-app messages appear in the center of the screen and are framed by a translucent panel. Useful for more critical messaging, they can be equipped with two click-action and analytics-enabled buttons. This message type is a subclass of [`InAppMessageImmersiveBase`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.inappmessage/-in-app-message-immersive-base/index.html), an abstract class that implements `IInAppMessageImmersive`, giving you the option to add custom functionality to your locally generated in-app messages. ![A modal in-app message in the center of a phone screen displaying "Humans are complicated. Custom engagement shouldn't be." In the background is the same in-app message displayed in the center of a web page.](https://www.braze.com/docs/ko/ko/assets/img/modal-behavior.gif?00fa4f83404c611c82cb0816f682e3f3){: style="border:0px;"} [`full`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.inappmessage/-in-app-message-full/index.html) in-app messages are useful for maximizing the content and impact of your user communication. The upper half of a `full` in-app message contains an image, and the lower half displays text and up to two click action and analytics-enabled buttons. This message type extends [`InAppMessageImmersiveBase`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.inappmessage/-in-app-message-immersive-base/index.html), giving you the option to add custom functionality to your locally generated in-app messages. ![A full screen in-app message shown across an entire phone screen displaying, "Humans are complicated. Custom engagement shouldn't be." In the background is the same in-app message displayed largely in the center of a web page.](https://www.braze.com/docs/ko/ko/assets/img_archive/In-App_Full.png?ecd62a88d38438aaebbda4cdcc22aa00) [`HTML`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.inappmessage/-in-app-message-html/index.html) in-app messages are useful for creating fully customized user content. User-defined HTML in-app message content is displayed in a `WebView` and may optionally contain other rich content, such as images and fonts, allowing for full control over message appearance and functionality. This message type implements [`IInAppMessageHtml`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.inappmessage/-i-in-app-message-html/index.html), which is a subclass of [`IInAppMessage`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.inappmessage/-i-in-app-message/index.html). **Note:** On Android, links configured with `target="_blank"` in custom HTML in-app messages open in the device's default web browser. Android in-app messages support a JavaScript `brazeBridge` interface to call methods on the Braze Android SDK from within your HTML, see our JavaScript bridge page for more details. ![An HTML in-app message with the a carousel of content and interactive buttons.](https://www.braze.com/docs/ko/ko/assets/img/full-screen-behavior.gif?b47edcbdd910efce932489d1fa592bd0){: style="border:0px;"} **Important:** We currently do not support the display of custom HTML in-app messages in an iFrame on the iOS and Android platforms. **Tip:** You can also define custom in-app message views for your app. For a full walkthrough, see [Setting custom factories](https://www.braze.com/docs/ko/ko/developer_guide/in_app_messages/customization#android_setting-custom-factories). Each in-app message type is highly customizable across content, images, icons, click actions, analytics, display, and delivery. They are enumerated types of `Braze.InAppMessage`, which defines basic behavior and traits for all in-app messages. For the full list of in-app message properties and usage, see the [`InAppMessage` class](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/inappmessage). These are the available in-app message types in Braze and how they will look like for end-users. [`Slideup`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/inappmessage/slideup-swift.struct) in-app messages are given this name because they "slide up" or "slide down" from the top or bottom of the screen. They cover a small portion of the screen and provide an effective and non-intrusive messaging capability. ![A slideup in-app message at the bottom and the top of a phone screen.](https://www.braze.com/docs/ko/ko/assets/img/slideup-spec.png?5e0eb3225ef5a9ca264817b8267aad45){: style="max-width:35%;border:none;"} [`Modal`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/inappmessage/modal-swift.struct) in-app messages appear in the center of the screen and are framed by a translucent panel. Useful for more critical messaging, they can be equipped with up to two analytics-enabled buttons. ![A modal in-app message in the center of a phone screen.](https://www.braze.com/docs/ko/ko/assets/img/modal-header-text.png?cef10f16ce8c681a237e5352cebf76f9){: style="max-width:35%;border:none;"} [`Modal Image`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/inappmessage/modalimage-swift.struct) in-app messages appear in the center of the screen and are framed by a translucent panel. These messages are similar to the `Modal` type except without header or message text. Useful for more critical messaging, they can be equipped with up to two analytics-enabled buttons. ![A modal image in-app message in the center of a phone screen.](https://www.braze.com/docs/ko/ko/assets/img/modal-full-image.png?2cda602759102cab22396c78978d712b){: style="max-width:35%;border:none;"} [`Full`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/inappmessage/full-swift.struct) in-app messages are useful for maximizing the content and impact of your user communication. The upper half of a `Full` in-app message contains an image, and the lower half displays text and up to two analytics-enabled buttons. ![A fullscreen in-app message shown across an entire phone screen.](https://www.braze.com/docs/ko/ko/assets/img/full-screen-header-text.png?803a758bf53c33ebc3ff63797676339b){: style="max-width:35%;border:none;"} [`Full Image`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/inappmessage/fullimage-swift.struct) in-app messages are similar to `Full` in-app messages except without header or message text. This message type is useful for maximizing the content and impact of your user communication. A `Full Image` in-app message contains an image spanning the entire screen, with the option to display up to two analytics-enabled buttons. ![A fullscreen image in-app message shown across an entire phone screen.](https://www.braze.com/docs/ko/ko/assets/img/full-screen-image.png?b29bfae801d78d57fcc7c9fdcb7cc0cc){: style="max-width:35%;border:none;"} [`HTML`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/inappmessage/html-swift.struct) in-app messages are useful for creating fully customized user content. User-defined HTML Full in-app message content is displayed in a `WKWebView`and may optionally contain other rich content, such as images and fonts, allowing for full control over message appearance and functionality.

iOS in-app messages support a JavaScript `brazeBridge` interface to call methods on the Braze Web SDK from within your HTML, see our [best practices](https://www.braze.com/docs/ko/ko/user_guide/message_building_by_channel/in-app_messages/best_practices/) for more details. The following example shows a paginated HTML Full in-app message: ![An HTML in-app message with a carousel of content and interactive buttons.](https://www.braze.com/docs/ko/ko/assets/img_archive/ios-html-full-iam.gif?4c6c9603065d4c430d406677e8cb6045) Note that we currently do not support the display of custom HTML in-app messages in an iFrame on the iOS and Android platforms. [`Control`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/inappmessage/control-swift.struct) in-app messages do not contain a UI component and are used primarily for analytics purposes. This type is used to verify receipt of an in-app message sent to a control group. For further details about Intelligent Selection and control groups, refer to [Intelligent Selection](https://www.braze.com/docs/ko/ko/user_guide/brazeai/intelligence/intelligent_selection/). ## Prerequisites Before you can use this feature, you'll need to [integrate the Flutter Braze SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=flutter). ## Message types Braze offers several default in-app message types, each customizable with messages, images, [Font Awesome](https://fontawesome.com/icons?d=gallery&p=2) icons, click actions, analytics, color schemes, and more. Their basic behavior and traits are defined by the [`IInAppMessage`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.inappmessage/-i-in-app-message/index.html) interface, in a subclass called [`InAppMessageBase`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.inappmessage/-in-app-message-base/index.html). `IInAppMessage` also includes a subinterface, [`IInAppMessageImmersive`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.inappmessage/-i-in-app-message-immersive/index.html), which lets you add close, click-action, and analytics [buttons](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.inappmessage/-message-button/index.html) to your app. **Important:** Keep in mind, in-app messages containing buttons will include the `clickAction` message in the final payload if the click action is added prior to adding the button text. [`slideup`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.inappmessage/-in-app-message-slideup/index.html) in-app messages are so-named because they "slide up" or "slide down" from the top or bottom of the screen. They cover a small portion of the screen and provide an effective and non-intrusive messaging capability. The `slideup` in-app message object extends [`InAppMessageBase`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.inappmessage/-in-app-message-base/index.html). ![An in-app message sliding from the bottom of a phone screen displaying "Humans are complicated. Custom engagement shouldn't be." In the background is the same in-app message displayed in the bottom right corner of a web page.](https://www.braze.com/docs/ko/ko/assets/img/slideup-behavior.gif?7239589ee8c964f354440e07ca4b9db1){: style="border:0px;"} [`modal`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.inappmessage/-in-app-message-modal/index.html) in-app messages appear in the center of the screen and are framed by a translucent panel. Useful for more critical messaging, they can be equipped with two click-action and analytics-enabled buttons. This message type is a subclass of [`InAppMessageImmersiveBase`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.inappmessage/-in-app-message-immersive-base/index.html), an abstract class that implements `IInAppMessageImmersive`, giving you the option to add custom functionality to your locally generated in-app messages. ![A modal in-app message in the center of a phone screen displaying "Humans are complicated. Custom engagement shouldn't be." In the background is the same in-app message displayed in the center of a web page.](https://www.braze.com/docs/ko/ko/assets/img/modal-behavior.gif?00fa4f83404c611c82cb0816f682e3f3){: style="border:0px;"} [`full`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.inappmessage/-in-app-message-full/index.html) in-app messages are useful for maximizing the content and impact of your user communication. The upper half of a `full` in-app message contains an image, and the lower half displays text and up to two click action and analytics-enabled buttons. This message type extends [`InAppMessageImmersiveBase`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.inappmessage/-in-app-message-immersive-base/index.html), giving you the option to add custom functionality to your locally generated in-app messages. ![A full screen in-app message shown across an entire phone screen displaying, "Humans are complicated. Custom engagement shouldn't be." In the background is the same in-app message displayed largely in the center of a web page.](https://www.braze.com/docs/ko/ko/assets/img_archive/In-App_Full.png?ecd62a88d38438aaebbda4cdcc22aa00) [`HTML`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.inappmessage/-in-app-message-html/index.html) in-app messages are useful for creating fully customized user content. User-defined HTML in-app message content is displayed in a `WebView` and may optionally contain other rich content, such as images and fonts, allowing for full control over message appearance and functionality. This message type implements [`IInAppMessageHtml`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.inappmessage/-i-in-app-message-html/index.html), which is a subclass of [`IInAppMessage`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.inappmessage/-i-in-app-message/index.html). **Note:** On Android, links configured with `target="_blank"` in custom HTML in-app messages open in the device's default web browser. Android in-app messages support a JavaScript `brazeBridge` interface to call methods on the Braze Android SDK from within your HTML, see our JavaScript bridge page for more details. ![An HTML in-app message with the a carousel of content and interactive buttons.](https://www.braze.com/docs/ko/ko/assets/img/full-screen-behavior.gif?b47edcbdd910efce932489d1fa592bd0){: style="border:0px;"} **Important:** We currently do not support the display of custom HTML in-app messages in an iFrame on the iOS and Android platforms. **Tip:** You can also define custom in-app message views for your app. For a full walkthrough, see [Setting custom factories](https://www.braze.com/docs/ko/ko/developer_guide/in_app_messages/customization#android_setting-custom-factories). Each in-app message type is highly customizable across content, images, icons, click actions, analytics, display, and delivery. They are enumerated types of `Braze.InAppMessage`, which defines basic behavior and traits for all in-app messages. For the full list of in-app message properties and usage, see the [`InAppMessage` class](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/inappmessage). These are the available in-app message types in Braze and how they will look like for end-users. [`Slideup`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/inappmessage/slideup-swift.struct) in-app messages are given this name because they "slide up" or "slide down" from the top or bottom of the screen. They cover a small portion of the screen and provide an effective and non-intrusive messaging capability. ![A slideup in-app message at the bottom and the top of a phone screen.](https://www.braze.com/docs/ko/ko/assets/img/slideup-spec.png?5e0eb3225ef5a9ca264817b8267aad45){: style="max-width:35%;border:none;"} [`Modal`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/inappmessage/modal-swift.struct) in-app messages appear in the center of the screen and are framed by a translucent panel. Useful for more critical messaging, they can be equipped with up to two analytics-enabled buttons. ![A modal in-app message in the center of a phone screen.](https://www.braze.com/docs/ko/ko/assets/img/modal-header-text.png?cef10f16ce8c681a237e5352cebf76f9){: style="max-width:35%;border:none;"} [`Modal Image`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/inappmessage/modalimage-swift.struct) in-app messages appear in the center of the screen and are framed by a translucent panel. These messages are similar to the `Modal` type except without header or message text. Useful for more critical messaging, they can be equipped with up to two analytics-enabled buttons. ![A modal image in-app message in the center of a phone screen.](https://www.braze.com/docs/ko/ko/assets/img/modal-full-image.png?2cda602759102cab22396c78978d712b){: style="max-width:35%;border:none;"} [`Full`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/inappmessage/full-swift.struct) in-app messages are useful for maximizing the content and impact of your user communication. The upper half of a `Full` in-app message contains an image, and the lower half displays text and up to two analytics-enabled buttons. ![A fullscreen in-app message shown across an entire phone screen.](https://www.braze.com/docs/ko/ko/assets/img/full-screen-header-text.png?803a758bf53c33ebc3ff63797676339b){: style="max-width:35%;border:none;"} [`Full Image`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/inappmessage/fullimage-swift.struct) in-app messages are similar to `Full` in-app messages except without header or message text. This message type is useful for maximizing the content and impact of your user communication. A `Full Image` in-app message contains an image spanning the entire screen, with the option to display up to two analytics-enabled buttons. ![A fullscreen image in-app message shown across an entire phone screen.](https://www.braze.com/docs/ko/ko/assets/img/full-screen-image.png?b29bfae801d78d57fcc7c9fdcb7cc0cc){: style="max-width:35%;border:none;"} [`HTML`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/inappmessage/html-swift.struct) in-app messages are useful for creating fully customized user content. User-defined HTML Full in-app message content is displayed in a `WKWebView`and may optionally contain other rich content, such as images and fonts, allowing for full control over message appearance and functionality.

iOS in-app messages support a JavaScript `brazeBridge` interface to call methods on the Braze Web SDK from within your HTML, see our [best practices](https://www.braze.com/docs/ko/ko/user_guide/message_building_by_channel/in-app_messages/best_practices/) for more details. The following example shows a paginated HTML Full in-app message: ![An HTML in-app message with a carousel of content and interactive buttons.](https://www.braze.com/docs/ko/ko/assets/img_archive/ios-html-full-iam.gif?4c6c9603065d4c430d406677e8cb6045) Note that we currently do not support the display of custom HTML in-app messages in an iFrame on the iOS and Android platforms. [`Control`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/inappmessage/control-swift.struct) in-app messages do not contain a UI component and are used primarily for analytics purposes. This type is used to verify receipt of an in-app message sent to a control group. For further details about Intelligent Selection and control groups, refer to [Intelligent Selection](https://www.braze.com/docs/ko/ko/user_guide/brazeai/intelligence/intelligent_selection/). ## Enabling in-app messages The Braze Flutter SDK automatically sets up the default in-app message presenter on both Android and iOS. In-app messages are displayed and forwarded to the Dart layer without additional setup. ### Customizing the in-app message presenter on iOS To override the default in-app message presenter on iOS, use the `postInitialization` closure in `BrazePlugin.configure(_:postInitialization:)`. Your custom presenter must call `BrazePlugin.processInAppMessage(message)` to forward in-app message data to the Dart layer. ```swift import BrazeUI BrazePlugin.configure( { configuration in // Set non-API-key configurations here. }, postInitialization: { braze in let customPresenter = CustomInAppMessagePresenter() braze.inAppMessagePresenter = customPresenter } ) ``` In the custom presenter class, call `BrazePlugin.processInAppMessage(message)` and `super.present(message: message)` to forward data to Dart and display the default UI. ```swift class CustomInAppMessagePresenter: BrazeInAppMessageUI { override func present(message: Braze.InAppMessage) { BrazePlugin.processInAppMessage(message) super.present(message: message) } } ``` **Note:** This step is for iOS only. The default implementation for in-app messages is already set up on Android. To set up the default presenter for in-app messages on iOS, create an implementation of the `BrazeInAppMessagePresenter` protocol and assign it to the optional `inAppMessagePresenter` on your Braze instance. You can also use the default Braze UI presenter by instantiating a `BrazeInAppMessageUI` object. You must import the `BrazeUI` library to access the `BrazeInAppMessageUI` class. ```swift import BrazeUI override func application( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil ) -> Bool { ... let braze = BrazePlugin.initBraze(configuration) braze.inAppMessagePresenter = BrazeInAppMessageUI() AppDelegate.braze = braze return true } ``` ```objc @import BrazeUI; - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { ... Braze *braze = [BrazePlugin initBraze:configuration]; braze.inAppMessagePresenter = [[BrazeInAppMessageUI alloc] init]; AppDelegate.braze = braze; [self.window makeKeyAndVisible]; return YES; } ``` For more information about accessing in-app message data, refer to [Logging in-app message data](https://www.braze.com/docs/ko/ko/developer_guide/in_app_messages/logging_message_data?sdktab=flutter). ## Prerequisites Before you can use this feature, you'll need to [integrate the React Native Braze SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=react%20native). ## Message types Braze offers several default in-app message types, each customizable with messages, images, [Font Awesome](https://fontawesome.com/icons?d=gallery&p=2) icons, click actions, analytics, color schemes, and more. Their basic behavior and traits are defined by the [`IInAppMessage`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.inappmessage/-i-in-app-message/index.html) interface, in a subclass called [`InAppMessageBase`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.inappmessage/-in-app-message-base/index.html). `IInAppMessage` also includes a subinterface, [`IInAppMessageImmersive`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.inappmessage/-i-in-app-message-immersive/index.html), which lets you add close, click-action, and analytics [buttons](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.inappmessage/-message-button/index.html) to your app. **Important:** Keep in mind, in-app messages containing buttons will include the `clickAction` message in the final payload if the click action is added prior to adding the button text. [`slideup`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.inappmessage/-in-app-message-slideup/index.html) in-app messages are so-named because they "slide up" or "slide down" from the top or bottom of the screen. They cover a small portion of the screen and provide an effective and non-intrusive messaging capability. The `slideup` in-app message object extends [`InAppMessageBase`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.inappmessage/-in-app-message-base/index.html). ![An in-app message sliding from the bottom of a phone screen displaying "Humans are complicated. Custom engagement shouldn't be." In the background is the same in-app message displayed in the bottom right corner of a web page.](https://www.braze.com/docs/ko/ko/assets/img/slideup-behavior.gif?7239589ee8c964f354440e07ca4b9db1){: style="border:0px;"} [`modal`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.inappmessage/-in-app-message-modal/index.html) in-app messages appear in the center of the screen and are framed by a translucent panel. Useful for more critical messaging, they can be equipped with two click-action and analytics-enabled buttons. This message type is a subclass of [`InAppMessageImmersiveBase`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.inappmessage/-in-app-message-immersive-base/index.html), an abstract class that implements `IInAppMessageImmersive`, giving you the option to add custom functionality to your locally generated in-app messages. ![A modal in-app message in the center of a phone screen displaying "Humans are complicated. Custom engagement shouldn't be." In the background is the same in-app message displayed in the center of a web page.](https://www.braze.com/docs/ko/ko/assets/img/modal-behavior.gif?00fa4f83404c611c82cb0816f682e3f3){: style="border:0px;"} [`full`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.inappmessage/-in-app-message-full/index.html) in-app messages are useful for maximizing the content and impact of your user communication. The upper half of a `full` in-app message contains an image, and the lower half displays text and up to two click action and analytics-enabled buttons. This message type extends [`InAppMessageImmersiveBase`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.inappmessage/-in-app-message-immersive-base/index.html), giving you the option to add custom functionality to your locally generated in-app messages. ![A full screen in-app message shown across an entire phone screen displaying, "Humans are complicated. Custom engagement shouldn't be." In the background is the same in-app message displayed largely in the center of a web page.](https://www.braze.com/docs/ko/ko/assets/img_archive/In-App_Full.png?ecd62a88d38438aaebbda4cdcc22aa00) [`HTML`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.inappmessage/-in-app-message-html/index.html) in-app messages are useful for creating fully customized user content. User-defined HTML in-app message content is displayed in a `WebView` and may optionally contain other rich content, such as images and fonts, allowing for full control over message appearance and functionality. This message type implements [`IInAppMessageHtml`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.inappmessage/-i-in-app-message-html/index.html), which is a subclass of [`IInAppMessage`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.inappmessage/-i-in-app-message/index.html). **Note:** On Android, links configured with `target="_blank"` in custom HTML in-app messages open in the device's default web browser. Android in-app messages support a JavaScript `brazeBridge` interface to call methods on the Braze Android SDK from within your HTML, see our JavaScript bridge page for more details. ![An HTML in-app message with the a carousel of content and interactive buttons.](https://www.braze.com/docs/ko/ko/assets/img/full-screen-behavior.gif?b47edcbdd910efce932489d1fa592bd0){: style="border:0px;"} **Important:** We currently do not support the display of custom HTML in-app messages in an iFrame on the iOS and Android platforms. **Tip:** You can also define custom in-app message views for your app. For a full walkthrough, see [Setting custom factories](https://www.braze.com/docs/ko/ko/developer_guide/in_app_messages/customization#android_setting-custom-factories). Each in-app message type is highly customizable across content, images, icons, click actions, analytics, display, and delivery. They are enumerated types of `Braze.InAppMessage`, which defines basic behavior and traits for all in-app messages. For the full list of in-app message properties and usage, see the [`InAppMessage` class](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/inappmessage). These are the available in-app message types in Braze and how they will look like for end-users. [`Slideup`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/inappmessage/slideup-swift.struct) in-app messages are given this name because they "slide up" or "slide down" from the top or bottom of the screen. They cover a small portion of the screen and provide an effective and non-intrusive messaging capability. ![A slideup in-app message at the bottom and the top of a phone screen.](https://www.braze.com/docs/ko/ko/assets/img/slideup-spec.png?5e0eb3225ef5a9ca264817b8267aad45){: style="max-width:35%;border:none;"} [`Modal`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/inappmessage/modal-swift.struct) in-app messages appear in the center of the screen and are framed by a translucent panel. Useful for more critical messaging, they can be equipped with up to two analytics-enabled buttons. ![A modal in-app message in the center of a phone screen.](https://www.braze.com/docs/ko/ko/assets/img/modal-header-text.png?cef10f16ce8c681a237e5352cebf76f9){: style="max-width:35%;border:none;"} [`Modal Image`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/inappmessage/modalimage-swift.struct) in-app messages appear in the center of the screen and are framed by a translucent panel. These messages are similar to the `Modal` type except without header or message text. Useful for more critical messaging, they can be equipped with up to two analytics-enabled buttons. ![A modal image in-app message in the center of a phone screen.](https://www.braze.com/docs/ko/ko/assets/img/modal-full-image.png?2cda602759102cab22396c78978d712b){: style="max-width:35%;border:none;"} [`Full`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/inappmessage/full-swift.struct) in-app messages are useful for maximizing the content and impact of your user communication. The upper half of a `Full` in-app message contains an image, and the lower half displays text and up to two analytics-enabled buttons. ![A fullscreen in-app message shown across an entire phone screen.](https://www.braze.com/docs/ko/ko/assets/img/full-screen-header-text.png?803a758bf53c33ebc3ff63797676339b){: style="max-width:35%;border:none;"} [`Full Image`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/inappmessage/fullimage-swift.struct) in-app messages are similar to `Full` in-app messages except without header or message text. This message type is useful for maximizing the content and impact of your user communication. A `Full Image` in-app message contains an image spanning the entire screen, with the option to display up to two analytics-enabled buttons. ![A fullscreen image in-app message shown across an entire phone screen.](https://www.braze.com/docs/ko/ko/assets/img/full-screen-image.png?b29bfae801d78d57fcc7c9fdcb7cc0cc){: style="max-width:35%;border:none;"} [`HTML`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/inappmessage/html-swift.struct) in-app messages are useful for creating fully customized user content. User-defined HTML Full in-app message content is displayed in a `WKWebView`and may optionally contain other rich content, such as images and fonts, allowing for full control over message appearance and functionality.

iOS in-app messages support a JavaScript `brazeBridge` interface to call methods on the Braze Web SDK from within your HTML, see our [best practices](https://www.braze.com/docs/ko/ko/user_guide/message_building_by_channel/in-app_messages/best_practices/) for more details. The following example shows a paginated HTML Full in-app message: ![An HTML in-app message with a carousel of content and interactive buttons.](https://www.braze.com/docs/ko/ko/assets/img_archive/ios-html-full-iam.gif?4c6c9603065d4c430d406677e8cb6045) Note that we currently do not support the display of custom HTML in-app messages in an iFrame on the iOS and Android platforms. [`Control`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/inappmessage/control-swift.struct) in-app messages do not contain a UI component and are used primarily for analytics purposes. This type is used to verify receipt of an in-app message sent to a control group. For further details about Intelligent Selection and control groups, refer to [Intelligent Selection](https://www.braze.com/docs/ko/ko/user_guide/brazeai/intelligence/intelligent_selection/). ## Data model The in-app message model is available in the React Native SDK. Braze has four in-app message types that share the same data model: **slideup**, **modal**, **full** and **HTML full**. ### Messages The in-app message model provides the base for all in-app messages. |Property | Description | |------------------|------------------------------------------------------------------------------------------------------------------------| |`inAppMessageJsonString` | The message JSON representation. | |`message` | The message text. | |`header` | The message header. | |`uri` | The URI associated with the button click action. | |`imageUrl` | The message image URL. | |`zippedAssetsUrl` | The zipped assets prepared to display HTML content. | |`useWebView` | Indicates whether the button click action should redirect using a web view. | |`duration` | The message display duration. | |`clickAction` | The button click action type. The types are: `URI`, and `NONE`. | |`dismissType` | The message close type. The two types are: `SWIPE` and `AUTO_DISMISS`. | |`messageType` | The in-app message type supported by the SDK. The four types are: `SLIDEUP`, `MODAL`, `FULL` and `HTML_FULL`. | |`extras` | The message extras dictionary. Default value: `[:]`. | |`buttons` | The list of buttons on the in-app message. | |`toString()` | The message as a String representation. | {: .reset-td-br-1 .reset-td-br-2 aria-label="Messages" } For a full reference of the in-app message model, see the [Android](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.inappmessage/index.html) and [iOS](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/inappmessage) documentation. ### Buttons Buttons can be added to in-app messages to perform actions and log analytics. The button model provides the base for all in-app message buttons. |Property | Description | |------------------|-----------------------------------------------------------------------------------------------------------------------------| |`text` | The text on the button. | |`uri` | The URI associated with the button click action. | |`useWebView` | Indicates whether the button click action should redirect using a web view. | |`clickAction` | The type of click action processed when the user clicks on the button. The types are: `URI`, and `NONE`. | |`id` | The button ID on the message. | |`toString()` | The button as a String representation. | {: .reset-td-br-1 .reset-td-br-2 aria-label="Buttons" } For a full reference of button model, see the [Android](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.inappmessage/-message-button/index.html) and [iOS](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/inappmessage/button) documentation. ## Prerequisites Before you can use this feature, you'll need to [integrate the Roku Braze SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=roku). Additionally, in-app messages will only be sent to Roku devices running the minimum supported SDK version: ## Message types Braze offers several default in-app message types, each customizable with messages, images, [Font Awesome](https://fontawesome.com/icons?d=gallery&p=2) icons, click actions, analytics, color schemes, and more. Their basic behavior and traits are defined by the [`IInAppMessage`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.inappmessage/-i-in-app-message/index.html) interface, in a subclass called [`InAppMessageBase`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.inappmessage/-in-app-message-base/index.html). `IInAppMessage` also includes a subinterface, [`IInAppMessageImmersive`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.inappmessage/-i-in-app-message-immersive/index.html), which lets you add close, click-action, and analytics [buttons](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.inappmessage/-message-button/index.html) to your app. **Important:** Keep in mind, in-app messages containing buttons will include the `clickAction` message in the final payload if the click action is added prior to adding the button text. [`slideup`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.inappmessage/-in-app-message-slideup/index.html) in-app messages are so-named because they "slide up" or "slide down" from the top or bottom of the screen. They cover a small portion of the screen and provide an effective and non-intrusive messaging capability. The `slideup` in-app message object extends [`InAppMessageBase`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.inappmessage/-in-app-message-base/index.html). ![An in-app message sliding from the bottom of a phone screen displaying "Humans are complicated. Custom engagement shouldn't be." In the background is the same in-app message displayed in the bottom right corner of a web page.](https://www.braze.com/docs/ko/ko/assets/img/slideup-behavior.gif?7239589ee8c964f354440e07ca4b9db1){: style="border:0px;"} [`modal`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.inappmessage/-in-app-message-modal/index.html) in-app messages appear in the center of the screen and are framed by a translucent panel. Useful for more critical messaging, they can be equipped with two click-action and analytics-enabled buttons. This message type is a subclass of [`InAppMessageImmersiveBase`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.inappmessage/-in-app-message-immersive-base/index.html), an abstract class that implements `IInAppMessageImmersive`, giving you the option to add custom functionality to your locally generated in-app messages. ![A modal in-app message in the center of a phone screen displaying "Humans are complicated. Custom engagement shouldn't be." In the background is the same in-app message displayed in the center of a web page.](https://www.braze.com/docs/ko/ko/assets/img/modal-behavior.gif?00fa4f83404c611c82cb0816f682e3f3){: style="border:0px;"} [`full`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.inappmessage/-in-app-message-full/index.html) in-app messages are useful for maximizing the content and impact of your user communication. The upper half of a `full` in-app message contains an image, and the lower half displays text and up to two click action and analytics-enabled buttons. This message type extends [`InAppMessageImmersiveBase`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.inappmessage/-in-app-message-immersive-base/index.html), giving you the option to add custom functionality to your locally generated in-app messages. ![A full screen in-app message shown across an entire phone screen displaying, "Humans are complicated. Custom engagement shouldn't be." In the background is the same in-app message displayed largely in the center of a web page.](https://www.braze.com/docs/ko/ko/assets/img_archive/In-App_Full.png?ecd62a88d38438aaebbda4cdcc22aa00) [`HTML`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.inappmessage/-in-app-message-html/index.html) in-app messages are useful for creating fully customized user content. User-defined HTML in-app message content is displayed in a `WebView` and may optionally contain other rich content, such as images and fonts, allowing for full control over message appearance and functionality. This message type implements [`IInAppMessageHtml`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.inappmessage/-i-in-app-message-html/index.html), which is a subclass of [`IInAppMessage`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.inappmessage/-i-in-app-message/index.html). **Note:** On Android, links configured with `target="_blank"` in custom HTML in-app messages open in the device's default web browser. Android in-app messages support a JavaScript `brazeBridge` interface to call methods on the Braze Android SDK from within your HTML, see our JavaScript bridge page for more details. ![An HTML in-app message with the a carousel of content and interactive buttons.](https://www.braze.com/docs/ko/ko/assets/img/full-screen-behavior.gif?b47edcbdd910efce932489d1fa592bd0){: style="border:0px;"} **Important:** We currently do not support the display of custom HTML in-app messages in an iFrame on the iOS and Android platforms. **Tip:** You can also define custom in-app message views for your app. For a full walkthrough, see [Setting custom factories](https://www.braze.com/docs/ko/ko/developer_guide/in_app_messages/customization#android_setting-custom-factories). Each in-app message type is highly customizable across content, images, icons, click actions, analytics, display, and delivery. They are enumerated types of `Braze.InAppMessage`, which defines basic behavior and traits for all in-app messages. For the full list of in-app message properties and usage, see the [`InAppMessage` class](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/inappmessage). These are the available in-app message types in Braze and how they will look like for end-users. [`Slideup`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/inappmessage/slideup-swift.struct) in-app messages are given this name because they "slide up" or "slide down" from the top or bottom of the screen. They cover a small portion of the screen and provide an effective and non-intrusive messaging capability. ![A slideup in-app message at the bottom and the top of a phone screen.](https://www.braze.com/docs/ko/ko/assets/img/slideup-spec.png?5e0eb3225ef5a9ca264817b8267aad45){: style="max-width:35%;border:none;"} [`Modal`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/inappmessage/modal-swift.struct) in-app messages appear in the center of the screen and are framed by a translucent panel. Useful for more critical messaging, they can be equipped with up to two analytics-enabled buttons. ![A modal in-app message in the center of a phone screen.](https://www.braze.com/docs/ko/ko/assets/img/modal-header-text.png?cef10f16ce8c681a237e5352cebf76f9){: style="max-width:35%;border:none;"} [`Modal Image`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/inappmessage/modalimage-swift.struct) in-app messages appear in the center of the screen and are framed by a translucent panel. These messages are similar to the `Modal` type except without header or message text. Useful for more critical messaging, they can be equipped with up to two analytics-enabled buttons. ![A modal image in-app message in the center of a phone screen.](https://www.braze.com/docs/ko/ko/assets/img/modal-full-image.png?2cda602759102cab22396c78978d712b){: style="max-width:35%;border:none;"} [`Full`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/inappmessage/full-swift.struct) in-app messages are useful for maximizing the content and impact of your user communication. The upper half of a `Full` in-app message contains an image, and the lower half displays text and up to two analytics-enabled buttons. ![A fullscreen in-app message shown across an entire phone screen.](https://www.braze.com/docs/ko/ko/assets/img/full-screen-header-text.png?803a758bf53c33ebc3ff63797676339b){: style="max-width:35%;border:none;"} [`Full Image`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/inappmessage/fullimage-swift.struct) in-app messages are similar to `Full` in-app messages except without header or message text. This message type is useful for maximizing the content and impact of your user communication. A `Full Image` in-app message contains an image spanning the entire screen, with the option to display up to two analytics-enabled buttons. ![A fullscreen image in-app message shown across an entire phone screen.](https://www.braze.com/docs/ko/ko/assets/img/full-screen-image.png?b29bfae801d78d57fcc7c9fdcb7cc0cc){: style="max-width:35%;border:none;"} [`HTML`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/inappmessage/html-swift.struct) in-app messages are useful for creating fully customized user content. User-defined HTML Full in-app message content is displayed in a `WKWebView`and may optionally contain other rich content, such as images and fonts, allowing for full control over message appearance and functionality.

iOS in-app messages support a JavaScript `brazeBridge` interface to call methods on the Braze Web SDK from within your HTML, see our [best practices](https://www.braze.com/docs/ko/ko/user_guide/message_building_by_channel/in-app_messages/best_practices/) for more details. The following example shows a paginated HTML Full in-app message: ![An HTML in-app message with a carousel of content and interactive buttons.](https://www.braze.com/docs/ko/ko/assets/img_archive/ios-html-full-iam.gif?4c6c9603065d4c430d406677e8cb6045) Note that we currently do not support the display of custom HTML in-app messages in an iFrame on the iOS and Android platforms. [`Control`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/inappmessage/control-swift.struct) in-app messages do not contain a UI component and are used primarily for analytics purposes. This type is used to verify receipt of an in-app message sent to a control group. For further details about Intelligent Selection and control groups, refer to [Intelligent Selection](https://www.braze.com/docs/ko/ko/user_guide/brazeai/intelligence/intelligent_selection/). ## Enabling in-app messages ### Step 1: Add an observer To process in-app messages, you can add an observer on `BrazeTask.BrazeInAppMessage`: ```brightscript m.BrazeTask.observeField("BrazeInAppMessage", "onInAppMessageReceived") ``` ### Step 2: Access triggered messages Then within your handler, you have access to the highest in-app message that your campaigns have triggered: ```brightscript sub onInAppMessageReceived() in_app_message = m.BrazeTask.BrazeInAppMessage ... end sub ``` ## Message fields ### Handling The following lists the fields you will need to handle your in-app messages: | Fields | Description | | ------ | ----------- | | `buttons` | List of buttons (could be an empty list). | | `click_action` | `"URI"` or `"NONE"`. Use this field to indicate whether the in-app message should open to a URI link or close the message when clicked. When there are no buttons, this should happen when the user clicks "OK" when the in-app message is displayed. | | `dismiss_type` | `"AUTO_DISMISS"` or `"SWIPE"`. Use this field to indicate whether your in-app message will auto dismiss or require a swipe to dismiss. | | `display_delay` | How long (seconds) to wait until displaying the in-app message. | | `duration` | How long (milliseconds) the message should be displayed when `dismiss_type` is set to `"AUTO_DISMISS"`. | | `extras` | Key-value pairs. | | `header` | The header text. | | `id` | The ID used to log impressions or clicks. | | `image_url` | In-app message image URL. | | `message` | Message body text. | | `uri` | Your URI users will be sent to based on your `click_action`. This field must be included when `click_action` is `"URI"`. | {: .reset-td-br-1 .reset-td-br-2 aria-label="Handling" } **Important:** For in-app messages containing buttons, the message `click_action` will also be included in the final payload if the click action is added prior to adding the button text. ### Styling There are also various styling fields that you could choose to use from the dashboard: | Fields | Description | | ------ | ----------- | | `bg_color` | Background color. | | `close_button_color` | Close button color. | | `frame_color` | The color of the background screen overlay. | | `header_text_color` | Header text color. | | `message_text_color` | Message text color. | | `text_align` | "START", "CENTER", or "END". Your selected text alignment. | {: .reset-td-br-1 .reset-td-br-2 aria-label="Styling" } Alternatively, you could implement the in-app message and style it within your Roku application using a standard palette: ### Buttons | Fields | Description | | ------ | ----------- | | `click_action` | `"URI"` or `"NONE"`. Use this field to indicate whether the in-app message should open to a URI link or close the message when clicked. | | `id` | The ID value of the button itself. | | `text` | The text to display on the button. | | `uri` | Your URI users will be sent to based on your `click_action`. This field must be included when `click_action` is `"URI"`. | {: .reset-td-br-1 .reset-td-br-2 aria-label="Buttons" } **Important:** Keep in mind, you'll need to implement your own custom UI since in-app messaging is supported via headless UI using the Swift SDK—which does not include any default UI or views for tvOS. ## Prerequisites Before you can use this feature, you'll need to [integrate the Swift Braze SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=swift). ## Enabling in-app messages ### Step 1: Create a new iOS app In Braze, select **Settings** > **App Settings**, then select **Add App**. Enter a name for your tvOS app, select **iOS**—_not tvOS_—then select **Add App**. ![ALT_TEXT.](https://www.braze.com/docs/ko/ko/assets/img/tvos.png?d1c5036d5b83f425591adb03556ca684){: style="width:70%"} **Warning:** If you select the **tvOS** checkbox, you will not be able to customize in-app messages for tvOS. ### Step 2: Get your app's API key In your app settings, select your new tvOS app then take note of your app's API key. You'll use this key to configure your app in Xcode. ![ALT_TEXT](https://www.braze.com/docs/ko/ko/assets/img/tvos1.png?9851deb799c1c88a248f97bd284c91cb){: style="width:70%"} ### Step 3: Integrate BrazeKit Use your app's API key to integrate the [Braze Swift SDK](https://github.com/braze-inc/braze-swift-sdk) into your tvOS project in Xcode. You only need to integrate BrazeKit from the Braze Swift SDK. ### Step 4: Create your custom UI Because Braze doesn't provide a default UI for in-app messages on tvOS, you'll need to customize it yourself. For a full walkthrough, see our step-by-step tutorial: [Customizing in-app messages for tvOS](https://braze-inc.github.io/braze-swift-sdk/documentation/braze/in-app-message-customization). For a sample project, see [Braze Swift SDK samples](https://github.com/braze-inc/braze-swift-sdk/tree/main/Examples#inappmessages-custom-ui). ## Prerequisites Before you can use this feature, you'll need to [integrate the Unity Braze SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=unity). ## Message types Braze offers several default in-app message types, each customizable with messages, images, [Font Awesome](https://fontawesome.com/icons?d=gallery&p=2) icons, click actions, analytics, color schemes, and more. Their basic behavior and traits are defined by the [`IInAppMessage`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.inappmessage/-i-in-app-message/index.html) interface, in a subclass called [`InAppMessageBase`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.inappmessage/-in-app-message-base/index.html). `IInAppMessage` also includes a subinterface, [`IInAppMessageImmersive`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.inappmessage/-i-in-app-message-immersive/index.html), which lets you add close, click-action, and analytics [buttons](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.inappmessage/-message-button/index.html) to your app. **Important:** Keep in mind, in-app messages containing buttons will include the `clickAction` message in the final payload if the click action is added prior to adding the button text. [`slideup`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.inappmessage/-in-app-message-slideup/index.html) in-app messages are so-named because they "slide up" or "slide down" from the top or bottom of the screen. They cover a small portion of the screen and provide an effective and non-intrusive messaging capability. The `slideup` in-app message object extends [`InAppMessageBase`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.inappmessage/-in-app-message-base/index.html). ![An in-app message sliding from the bottom of a phone screen displaying "Humans are complicated. Custom engagement shouldn't be." In the background is the same in-app message displayed in the bottom right corner of a web page.](https://www.braze.com/docs/ko/ko/assets/img/slideup-behavior.gif?7239589ee8c964f354440e07ca4b9db1){: style="border:0px;"} [`modal`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.inappmessage/-in-app-message-modal/index.html) in-app messages appear in the center of the screen and are framed by a translucent panel. Useful for more critical messaging, they can be equipped with two click-action and analytics-enabled buttons. This message type is a subclass of [`InAppMessageImmersiveBase`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.inappmessage/-in-app-message-immersive-base/index.html), an abstract class that implements `IInAppMessageImmersive`, giving you the option to add custom functionality to your locally generated in-app messages. ![A modal in-app message in the center of a phone screen displaying "Humans are complicated. Custom engagement shouldn't be." In the background is the same in-app message displayed in the center of a web page.](https://www.braze.com/docs/ko/ko/assets/img/modal-behavior.gif?00fa4f83404c611c82cb0816f682e3f3){: style="border:0px;"} [`full`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.inappmessage/-in-app-message-full/index.html) in-app messages are useful for maximizing the content and impact of your user communication. The upper half of a `full` in-app message contains an image, and the lower half displays text and up to two click action and analytics-enabled buttons. This message type extends [`InAppMessageImmersiveBase`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.inappmessage/-in-app-message-immersive-base/index.html), giving you the option to add custom functionality to your locally generated in-app messages. ![A full screen in-app message shown across an entire phone screen displaying, "Humans are complicated. Custom engagement shouldn't be." In the background is the same in-app message displayed largely in the center of a web page.](https://www.braze.com/docs/ko/ko/assets/img_archive/In-App_Full.png?ecd62a88d38438aaebbda4cdcc22aa00) [`HTML`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.inappmessage/-in-app-message-html/index.html) in-app messages are useful for creating fully customized user content. User-defined HTML in-app message content is displayed in a `WebView` and may optionally contain other rich content, such as images and fonts, allowing for full control over message appearance and functionality. This message type implements [`IInAppMessageHtml`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.inappmessage/-i-in-app-message-html/index.html), which is a subclass of [`IInAppMessage`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.inappmessage/-i-in-app-message/index.html). **Note:** On Android, links configured with `target="_blank"` in custom HTML in-app messages open in the device's default web browser. Android in-app messages support a JavaScript `brazeBridge` interface to call methods on the Braze Android SDK from within your HTML, see our JavaScript bridge page for more details. ![An HTML in-app message with the a carousel of content and interactive buttons.](https://www.braze.com/docs/ko/ko/assets/img/full-screen-behavior.gif?b47edcbdd910efce932489d1fa592bd0){: style="border:0px;"} **Important:** We currently do not support the display of custom HTML in-app messages in an iFrame on the iOS and Android platforms. **Tip:** You can also define custom in-app message views for your app. For a full walkthrough, see [Setting custom factories](https://www.braze.com/docs/ko/ko/developer_guide/in_app_messages/customization#android_setting-custom-factories). Each in-app message type is highly customizable across content, images, icons, click actions, analytics, display, and delivery. They are enumerated types of `Braze.InAppMessage`, which defines basic behavior and traits for all in-app messages. For the full list of in-app message properties and usage, see the [`InAppMessage` class](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/inappmessage). These are the available in-app message types in Braze and how they will look like for end-users. [`Slideup`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/inappmessage/slideup-swift.struct) in-app messages are given this name because they "slide up" or "slide down" from the top or bottom of the screen. They cover a small portion of the screen and provide an effective and non-intrusive messaging capability. ![A slideup in-app message at the bottom and the top of a phone screen.](https://www.braze.com/docs/ko/ko/assets/img/slideup-spec.png?5e0eb3225ef5a9ca264817b8267aad45){: style="max-width:35%;border:none;"} [`Modal`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/inappmessage/modal-swift.struct) in-app messages appear in the center of the screen and are framed by a translucent panel. Useful for more critical messaging, they can be equipped with up to two analytics-enabled buttons. ![A modal in-app message in the center of a phone screen.](https://www.braze.com/docs/ko/ko/assets/img/modal-header-text.png?cef10f16ce8c681a237e5352cebf76f9){: style="max-width:35%;border:none;"} [`Modal Image`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/inappmessage/modalimage-swift.struct) in-app messages appear in the center of the screen and are framed by a translucent panel. These messages are similar to the `Modal` type except without header or message text. Useful for more critical messaging, they can be equipped with up to two analytics-enabled buttons. ![A modal image in-app message in the center of a phone screen.](https://www.braze.com/docs/ko/ko/assets/img/modal-full-image.png?2cda602759102cab22396c78978d712b){: style="max-width:35%;border:none;"} [`Full`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/inappmessage/full-swift.struct) in-app messages are useful for maximizing the content and impact of your user communication. The upper half of a `Full` in-app message contains an image, and the lower half displays text and up to two analytics-enabled buttons. ![A fullscreen in-app message shown across an entire phone screen.](https://www.braze.com/docs/ko/ko/assets/img/full-screen-header-text.png?803a758bf53c33ebc3ff63797676339b){: style="max-width:35%;border:none;"} [`Full Image`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/inappmessage/fullimage-swift.struct) in-app messages are similar to `Full` in-app messages except without header or message text. This message type is useful for maximizing the content and impact of your user communication. A `Full Image` in-app message contains an image spanning the entire screen, with the option to display up to two analytics-enabled buttons. ![A fullscreen image in-app message shown across an entire phone screen.](https://www.braze.com/docs/ko/ko/assets/img/full-screen-image.png?b29bfae801d78d57fcc7c9fdcb7cc0cc){: style="max-width:35%;border:none;"} [`HTML`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/inappmessage/html-swift.struct) in-app messages are useful for creating fully customized user content. User-defined HTML Full in-app message content is displayed in a `WKWebView`and may optionally contain other rich content, such as images and fonts, allowing for full control over message appearance and functionality.

iOS in-app messages support a JavaScript `brazeBridge` interface to call methods on the Braze Web SDK from within your HTML, see our [best practices](https://www.braze.com/docs/ko/ko/user_guide/message_building_by_channel/in-app_messages/best_practices/) for more details. The following example shows a paginated HTML Full in-app message: ![An HTML in-app message with a carousel of content and interactive buttons.](https://www.braze.com/docs/ko/ko/assets/img_archive/ios-html-full-iam.gif?4c6c9603065d4c430d406677e8cb6045) Note that we currently do not support the display of custom HTML in-app messages in an iFrame on the iOS and Android platforms. [`Control`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/inappmessage/control-swift.struct) in-app messages do not contain a UI component and are used primarily for analytics purposes. This type is used to verify receipt of an in-app message sent to a control group. For further details about Intelligent Selection and control groups, refer to [Intelligent Selection](https://www.braze.com/docs/ko/ko/user_guide/brazeai/intelligence/intelligent_selection/). ## Prerequisites Before you can use this feature, you'll need to [integrate the .NET MAUI Braze SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=.net%20maui%20(xamarin)). ## Message types Braze offers several default in-app message types, each customizable with messages, images, [Font Awesome](https://fontawesome.com/icons?d=gallery&p=2) icons, click actions, analytics, color schemes, and more. Their basic behavior and traits are defined by the [`IInAppMessage`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.inappmessage/-i-in-app-message/index.html) interface, in a subclass called [`InAppMessageBase`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.inappmessage/-in-app-message-base/index.html). `IInAppMessage` also includes a subinterface, [`IInAppMessageImmersive`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.inappmessage/-i-in-app-message-immersive/index.html), which lets you add close, click-action, and analytics [buttons](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.inappmessage/-message-button/index.html) to your app. **Important:** Keep in mind, in-app messages containing buttons will include the `clickAction` message in the final payload if the click action is added prior to adding the button text. [`slideup`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.inappmessage/-in-app-message-slideup/index.html) in-app messages are so-named because they "slide up" or "slide down" from the top or bottom of the screen. They cover a small portion of the screen and provide an effective and non-intrusive messaging capability. The `slideup` in-app message object extends [`InAppMessageBase`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.inappmessage/-in-app-message-base/index.html). ![An in-app message sliding from the bottom of a phone screen displaying "Humans are complicated. Custom engagement shouldn't be." In the background is the same in-app message displayed in the bottom right corner of a web page.](https://www.braze.com/docs/ko/ko/assets/img/slideup-behavior.gif?7239589ee8c964f354440e07ca4b9db1){: style="border:0px;"} [`modal`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.inappmessage/-in-app-message-modal/index.html) in-app messages appear in the center of the screen and are framed by a translucent panel. Useful for more critical messaging, they can be equipped with two click-action and analytics-enabled buttons. This message type is a subclass of [`InAppMessageImmersiveBase`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.inappmessage/-in-app-message-immersive-base/index.html), an abstract class that implements `IInAppMessageImmersive`, giving you the option to add custom functionality to your locally generated in-app messages. ![A modal in-app message in the center of a phone screen displaying "Humans are complicated. Custom engagement shouldn't be." In the background is the same in-app message displayed in the center of a web page.](https://www.braze.com/docs/ko/ko/assets/img/modal-behavior.gif?00fa4f83404c611c82cb0816f682e3f3){: style="border:0px;"} [`full`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.inappmessage/-in-app-message-full/index.html) in-app messages are useful for maximizing the content and impact of your user communication. The upper half of a `full` in-app message contains an image, and the lower half displays text and up to two click action and analytics-enabled buttons. This message type extends [`InAppMessageImmersiveBase`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.inappmessage/-in-app-message-immersive-base/index.html), giving you the option to add custom functionality to your locally generated in-app messages. ![A full screen in-app message shown across an entire phone screen displaying, "Humans are complicated. Custom engagement shouldn't be." In the background is the same in-app message displayed largely in the center of a web page.](https://www.braze.com/docs/ko/ko/assets/img_archive/In-App_Full.png?ecd62a88d38438aaebbda4cdcc22aa00) [`HTML`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.inappmessage/-in-app-message-html/index.html) in-app messages are useful for creating fully customized user content. User-defined HTML in-app message content is displayed in a `WebView` and may optionally contain other rich content, such as images and fonts, allowing for full control over message appearance and functionality. This message type implements [`IInAppMessageHtml`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.inappmessage/-i-in-app-message-html/index.html), which is a subclass of [`IInAppMessage`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.inappmessage/-i-in-app-message/index.html). **Note:** On Android, links configured with `target="_blank"` in custom HTML in-app messages open in the device's default web browser. Android in-app messages support a JavaScript `brazeBridge` interface to call methods on the Braze Android SDK from within your HTML, see our JavaScript bridge page for more details. ![An HTML in-app message with the a carousel of content and interactive buttons.](https://www.braze.com/docs/ko/ko/assets/img/full-screen-behavior.gif?b47edcbdd910efce932489d1fa592bd0){: style="border:0px;"} **Important:** We currently do not support the display of custom HTML in-app messages in an iFrame on the iOS and Android platforms. **Tip:** You can also define custom in-app message views for your app. For a full walkthrough, see [Setting custom factories](https://www.braze.com/docs/ko/ko/developer_guide/in_app_messages/customization#android_setting-custom-factories). Each in-app message type is highly customizable across content, images, icons, click actions, analytics, display, and delivery. They are enumerated types of `Braze.InAppMessage`, which defines basic behavior and traits for all in-app messages. For the full list of in-app message properties and usage, see the [`InAppMessage` class](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/inappmessage). These are the available in-app message types in Braze and how they will look like for end-users. [`Slideup`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/inappmessage/slideup-swift.struct) in-app messages are given this name because they "slide up" or "slide down" from the top or bottom of the screen. They cover a small portion of the screen and provide an effective and non-intrusive messaging capability. ![A slideup in-app message at the bottom and the top of a phone screen.](https://www.braze.com/docs/ko/ko/assets/img/slideup-spec.png?5e0eb3225ef5a9ca264817b8267aad45){: style="max-width:35%;border:none;"} [`Modal`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/inappmessage/modal-swift.struct) in-app messages appear in the center of the screen and are framed by a translucent panel. Useful for more critical messaging, they can be equipped with up to two analytics-enabled buttons. ![A modal in-app message in the center of a phone screen.](https://www.braze.com/docs/ko/ko/assets/img/modal-header-text.png?cef10f16ce8c681a237e5352cebf76f9){: style="max-width:35%;border:none;"} [`Modal Image`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/inappmessage/modalimage-swift.struct) in-app messages appear in the center of the screen and are framed by a translucent panel. These messages are similar to the `Modal` type except without header or message text. Useful for more critical messaging, they can be equipped with up to two analytics-enabled buttons. ![A modal image in-app message in the center of a phone screen.](https://www.braze.com/docs/ko/ko/assets/img/modal-full-image.png?2cda602759102cab22396c78978d712b){: style="max-width:35%;border:none;"} [`Full`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/inappmessage/full-swift.struct) in-app messages are useful for maximizing the content and impact of your user communication. The upper half of a `Full` in-app message contains an image, and the lower half displays text and up to two analytics-enabled buttons. ![A fullscreen in-app message shown across an entire phone screen.](https://www.braze.com/docs/ko/ko/assets/img/full-screen-header-text.png?803a758bf53c33ebc3ff63797676339b){: style="max-width:35%;border:none;"} [`Full Image`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/inappmessage/fullimage-swift.struct) in-app messages are similar to `Full` in-app messages except without header or message text. This message type is useful for maximizing the content and impact of your user communication. A `Full Image` in-app message contains an image spanning the entire screen, with the option to display up to two analytics-enabled buttons. ![A fullscreen image in-app message shown across an entire phone screen.](https://www.braze.com/docs/ko/ko/assets/img/full-screen-image.png?b29bfae801d78d57fcc7c9fdcb7cc0cc){: style="max-width:35%;border:none;"} [`HTML`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/inappmessage/html-swift.struct) in-app messages are useful for creating fully customized user content. User-defined HTML Full in-app message content is displayed in a `WKWebView`and may optionally contain other rich content, such as images and fonts, allowing for full control over message appearance and functionality.

iOS in-app messages support a JavaScript `brazeBridge` interface to call methods on the Braze Web SDK from within your HTML, see our [best practices](https://www.braze.com/docs/ko/ko/user_guide/message_building_by_channel/in-app_messages/best_practices/) for more details. The following example shows a paginated HTML Full in-app message: ![An HTML in-app message with a carousel of content and interactive buttons.](https://www.braze.com/docs/ko/ko/assets/img_archive/ios-html-full-iam.gif?4c6c9603065d4c430d406677e8cb6045) Note that we currently do not support the display of custom HTML in-app messages in an iFrame on the iOS and Android platforms. [`Control`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/inappmessage/control-swift.struct) in-app messages do not contain a UI component and are used primarily for analytics purposes. This type is used to verify receipt of an in-app message sent to a control group. For further details about Intelligent Selection and control groups, refer to [Intelligent Selection](https://www.braze.com/docs/ko/ko/user_guide/brazeai/intelligence/intelligent_selection/). ## 다음 단계 더 깊이 들어갈 준비가 되셨나요? 이 단계별 튜토리얼을 확인하세요: - 메시지 전달 타이밍을 [지연 및 복원된 트리거 메시지](https://www.braze.com/docs/ko/ko/developer_guide/in_app_messages/tutorials/deferring_triggered_messages)로 미세 조정하세요. - 메시지 타겟팅을 [조건부 표시 규칙 설정](https://www.braze.com/docs/ko/ko/developer_guide/in_app_messages/tutorials/conditionally_displaying_messages)으로 개선하세요. - 키-값 쌍으로 [메시지 스타일링을 사용자 정의하여](https://www.braze.com/docs/ko/ko/developer_guide/in_app_messages/tutorials/customizing_message_styling) 브랜드의 외관을 일치시키세요. # Braze SDK에 대한 앱 내 메시지 사용자 정의 Source: /docs/ko/developer_guide/in_app_messages/customization/index.md # 앱 내 메시지 사용자 정의 > Braze SDK의 인앱 메시지를 사용자 지정하는 방법을 알아보세요. 고급 스타일링 기술에 대한 튜토리얼을 확인하세요 [키-값 쌍을 사용하여 메시지 스타일링 사용자 정의](https://www.braze.com/docs/ko/ko/developer_guide/in_app_messages/tutorials/customizing_message_styling). ## Prerequisites Before you can use this feature, you'll need to [integrate the Web Braze SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=web). ## Custom styles Braze UI elements come with a default look and feel that create a neutral in-app message experience and aim for consistency with other Braze mobile platforms. The default Braze styles are defined in CSS within the Braze SDK. ### Setting a default style By overriding selected styles in your application, you can customize our standard in-app message types with your own background images, font families, styles, sizes, animations, and more. For instance, the following is an example override that will cause an in-app message's headers to appear italicized: ```css body .ab-in-app-message .ab-message-header { font-style: italic; } ``` See the [JSDocs](https://js.appboycdn.com/web-sdk/latest/doc/classes/braze.inappmessage.html) for more information. ### Customizing the z-index By default, in-app messages are displayed using `z-index: 9001`. This is configurable using the `inAppMessageZIndex ` [initialization option](https://js.appboycdn.com/web-sdk/latest/doc/modules/braze.html#initializationoptions) in the scenario that your website styles elements with higher values than that. ```javascript braze.initialize("YOUR-API-KEY", { baseUrl: "YOUR-API-ENDPOINT", inAppMessageZIndex: 12000 }); ``` **Important:** This feature is only available for Web Braze SDK v3.3.0 and later. ## Customizing message dismissals By default, when an in-app message is showing, pressing the escape button or a click on the grayed-out background of the page will dismiss the message. Configure the `requireExplicitInAppMessageDismissal` [initialization option](https://js.appboycdn.com/web-sdk/latest/doc/modules/braze.html#initializationoptions) to `true` to prevent this behavior and require an explicit button click to dismiss messages. ```javascript import * as braze from "@braze/web-sdk"; braze.initialize("YOUR-API-KEY", { baseUrl: "YOUR-API-ENDPOINT", requireExplicitInAppMessageDismissal: true }); ``` ## Customizing display timing To override the default display timing, remove calls to `braze.automaticallyShowInAppMessages()` and handle messages in `braze.subscribeToInAppMessage()`. Register your callback before `braze.openSession()`, so you can intercept session-start messages and decide whether to display or defer each message. By default, Braze displays in-app messages when they are triggered and eligible to display. If you need different behavior for your app experience, use a custom callback to defer or display messages based on your own logic. The following example shows how to subscribe to triggered in-app messages, defer selected messages, and display deferred messages later: ```javascript import * as braze from "@braze/web-sdk"; braze.initialize("YOUR-API-KEY", { baseUrl: "YOUR-API-ENDPOINT" }); braze.subscribeToInAppMessage(function (message) { // Control-group messages should always be "shown" to log analytics. if (message.isControl || message instanceof braze.ControlMessage) { braze.showInAppMessage(message); return; } const shouldDefer = true; // Replace with your own display logic if (shouldDefer) { braze.deferInAppMessage(message); return; } braze.showInAppMessage(message); }); braze.openSession(); // Later, when your app is ready to display a deferred message: const deferredMessage = braze.getDeferredInAppMessage(); if (deferredMessage) { braze.showInAppMessage(deferredMessage); } ``` For related delivery customization guidance, see: - [Web `deferInAppMessage` reference](https://js.appboycdn.com/web-sdk/latest/doc/modules/braze.html#deferinappmessage) - [Web `subscribeToInAppMessage` reference](https://js.appboycdn.com/web-sdk/latest/doc/modules/braze.html#subscribetoinappmessage) ## Opening links in a new tab To set your in-app message links to open in a new tab, set the `openInAppMessagesInNewTab` option to `true` to force all links from in-app message clicks open in a new tab or window. ```javascript braze.initialize('api-key', { openInAppMessagesInNewTab: true} ); ``` ## Prerequisites Before you can use this feature, you'll need to [integrate the Android Braze SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=android). You'll also need to [set up in-app messages](https://www.braze.com/docs/ko/ko/developer_guide/in_app_messages). ## Setting custom manager listeners While the `BrazeInAppMessageManager` listener can automatically handle the display and lifecycle of in-app messages, you'll need to implement a custom manager listener if you'd like to fully customize your messages. The Braze SDK has a default `DefaultHtmlInAppMessageActionListener` class that is used if no custom listener is defined and takes appropriate action automatically. If you require more control over how a user interacts with different buttons inside a custom HTML in-app message, implement a custom `IHtmlInAppMessageActionListener` class. This listener applies to __both__ messages built with custom HTML and messages created using the Drag-and-Drop (DnD) editor. It does not apply to traditional IAMs. Traditional IAMs are Braze's built-in, SDK-rendered message types (for example, slideup, modal, and full) created in the original in-app message composer using predefined layouts. Unlike custom HTML and DnD IAMs, they do not run through the HTML action listener flow. If you set a custom `IHtmlInAppMessageActionListener`, its logic will override the default click behavior for _all_ DnD messages. Please ensure your marketing team is aware of this, as it may affect their campaigns in unexpected ways. ### Step 1: Implement the custom manager listener #### Step 1.1: Implement `IInAppMessageManagerListener` Create a class that implements [`IInAppMessageManagerListener`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.ui.inappmessage.listeners/-i-in-app-message-manager-listener/index.html). The callbacks in your `IInAppMessageManagerListener` will also be called at various points in the in-app message lifecycle. For example, if you set a custom manager listener when an in-app message is received from Braze, the [`beforeInAppMessageDisplayed()`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.ui.inappmessage.listeners/-i-in-app-message-manager-listener/before-in-app-message-displayed.html) method will be called. If your implementation of this method returns [`InAppMessageOperation.DISCARD`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.ui.inappmessage/-in-app-message-operation/-d-i-s-c-a-r-d/index.html), that signals to Braze that the in-app message will be handled by the host app and should not be displayed by Braze. If [`InAppMessageOperation.DISPLAY_NOW`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.ui.inappmessage/-in-app-message-operation/-d-i-s-p-l-a-y_-n-o-w/index.html) is returned, Braze will attempt to display the in-app message. This method should be used if you choose to display the in-app message in a customized manner. `IInAppMessageManagerListener` also includes delegate methods for message clicks and buttons, which can be used in cases like intercepting a message when a button or message is clicked for further processing. #### Step 1.2: Hook into IAM view lifecycle methods (optional) The [`IInAppMessageManagerListener`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.ui.inappmessage.listeners/-i-in-app-message-manager-listener/index.html) interface has in-app message view methods called at distinct points in the in-app message view lifecycle. These methods are called in the following order: 1. [`beforeInAppMessageViewOpened`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.ui.inappmessage.listeners/-i-in-app-message-manager-listener/before-in-app-message-view-opened.html): Called just before the in-app message is added to the activity's view. The in-app message is not yet visible to the user at this time. 2. [`afterInAppMessageViewOpened`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.ui.inappmessage.listeners/-i-in-app-message-manager-listener/after-in-app-message-view-opened.html): Called just after the in-app message is added to the activity's view. The in-app message is now visible to the user at this time. 3. [`beforeInAppMessageViewClosed`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.ui.inappmessage.listeners/-i-in-app-message-manager-listener/before-in-app-message-view-closed.html): Called just before the in-app message is removed from the activity's view. The in-app message is still visible to the user at this time. 4. [`afterInAppMessageViewClosed`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.ui.inappmessage.listeners/-i-in-app-message-manager-listener/after-in-app-message-view-closed.html): Called just after the in-app message is removed from the activity's view. The in-app message is no longer visible to the user at this time. Note that the time between [`afterInAppMessageViewOpened`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.ui.inappmessage.listeners/-i-in-app-message-manager-listener/after-in-app-message-view-opened.html) and [`beforeInAppMessageViewClosed`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.ui.inappmessage.listeners/-i-in-app-message-manager-listener/before-in-app-message-view-closed.html) is when the in-app message view is on screen, visible to the user. **Note:** Implementation of these methods is not required. They're only provided to track and inform the in-app message view lifecycle. You can leave these method implementations empty. Create a class that implements [`IHtmlInAppMessageActionListener`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.ui.inappmessage.listeners/-i-html-in-app-message-action-listener/index.html). The callbacks in your `IHtmlInAppMessageActionListener` will be called whenever the user initiates any of the following actions inside the HTML in-app message: - Clicks on the close button - Fires a custom event - Clicks on a URL inside HTML in-app message ```java public class CustomHtmlInAppMessageActionListener implements IHtmlInAppMessageActionListener { private final Context mContext; public CustomHtmlInAppMessageActionListener(Context context) { mContext = context; } @Override public void onCloseClicked(IInAppMessage inAppMessage, String url, Bundle queryBundle) { Toast.makeText(mContext, "HTML In App Message closed", Toast.LENGTH_LONG).show(); BrazeInAppMessageManager.getInstance().hideCurrentlyDisplayingInAppMessage(false); } @Override public boolean onCustomEventFired(IInAppMessage inAppMessage, String url, Bundle queryBundle) { Toast.makeText(mContext, "Custom event fired. Ignoring.", Toast.LENGTH_LONG).show(); return true; } @Override public boolean onOtherUrlAction(IInAppMessage inAppMessage, String url, Bundle queryBundle) { Toast.makeText(mContext, "Custom url pressed: " + url + " . Ignoring", Toast.LENGTH_LONG).show(); BrazeInAppMessageManager.getInstance().hideCurrentlyDisplayingInAppMessage(false); return true; } } ``` ```kotlin class CustomHtmlInAppMessageActionListener(private val mContext: Context) : IHtmlInAppMessageActionListener { override fun onCloseClicked(inAppMessage: IInAppMessage, url: String, queryBundle: Bundle) { Toast.makeText(mContext, "HTML In App Message closed", Toast.LENGTH_LONG).show() BrazeInAppMessageManager.getInstance().hideCurrentlyDisplayingInAppMessage(false) } override fun onCustomEventFired(inAppMessage: IInAppMessage, url: String, queryBundle: Bundle): Boolean { Toast.makeText(mContext, "Custom event fired. Ignoring.", Toast.LENGTH_LONG).show() return true } override fun onOtherUrlAction(inAppMessage: IInAppMessage, url: String, queryBundle: Bundle): Boolean { Toast.makeText(mContext, "Custom url pressed: $url . Ignoring", Toast.LENGTH_LONG).show() BrazeInAppMessageManager.getInstance().hideCurrentlyDisplayingInAppMessage(false) return true } } ``` ### Step 2: Instruct Braze to use the custom manager listener After you create `IInAppMessageManagerListener`, call `BrazeInAppMessageManager.getInstance().setCustomInAppMessageManagerListener()` to instruct `BrazeInAppMessageManager` to use your custom `IInAppMessageManagerListener` instead of the default listener. Do this in your [`Application.onCreate()`](https://developer.android.com/reference/android/app/Application.html#onCreate()) before any other calls to Braze, so the custom listener is set before any in-app messages are displayed. #### Altering in-app messages before display When a new in-app message is received, and there is already an in-app message being displayed, the new message will be put onto the top of the stack and can be displayed at a later time. However, if there is no in-app message being displayed, the following delegate method in `IInAppMessageManagerListener` will be called: ```java @Override public InAppMessageOperation beforeInAppMessageDisplayed(IInAppMessage inAppMessage) { return InAppMessageOperation.DISPLAY_NOW; } ``` ```kotlin override fun beforeInAppMessageDisplayed(inAppMessage: IInAppMessage): InAppMessageOperation { return InAppMessageOperation.DISPLAY_NOW } ``` The `InAppMessageOperation()` return value can control when the message should be displayed. The suggested usage of this method would be to delay messages in certain parts of the app by returning `DISPLAY_LATER` when in-app messages would be distracting to the user's app experience. | `InAppMessageOperation` return value | Behavior | | -------------------------- | -------- | | `DISPLAY_NOW` | The message will be displayed | | `DISPLAY_LATER` | The message will be returned to the stack and displayed at the next available opportunity | | `DISCARD` | The message will be discarded | | `null` | The message will be ignored. This method should **NOT** return `null` | {: .reset-td-br-1 .reset-td-br-2 .reset-td-br-3 aria-label="Altering in-app messages before display" } See [`InAppMessageOperation`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.ui.inappmessage/-in-app-message-operation/index.html) for more details. **Tip:** If you choose to `DISCARD` the in-app message and replace it with your in-app message view, you will need to log in-app message clicks and impressions manually. On Android, this is done by calling `logClick` and `logImpression` on in-app messages and `logButtonClick` on immersive in-app messages. **Tip:** Once an in-app message has been placed on the stack, you can request for it to be retrieved and displayed at any time by calling [`BrazeInAppMessageManager.getInstance().requestDisplayInAppMessage()`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.ui.inappmessage/-braze-in-app-message-manager/request-display-in-app-message.html). This method requests Braze to display the next available in-app message from the stack. After your `IHtmlInAppMessageActionListener` is created, call `BrazeInAppMessageManager.getInstance().setCustomHtmlInAppMessageActionListener()` to instruct `BrazeInAppMessageManager` to use your custom `IHtmlInAppMessageActionListener` instead of the default action listener. We recommend setting your `IHtmlInAppMessageActionListener` in your [`Application.onCreate()`](https://developer.android.com/reference/android/app/Application.html#onCreate()) before any other calls to Braze. This will set the custom action listener before any in-app message is displayed: ```java BrazeInAppMessageManager.getInstance().setCustomHtmlInAppMessageActionListener(new CustomHtmlInAppMessageActionListener(context)); ``` ```kotlin BrazeInAppMessageManager.getInstance().setCustomHtmlInAppMessageActionListener(CustomHtmlInAppMessageActionListener(context)) ``` ## Setting custom factories You can override a number of defaults through custom factory objects. These can be registered with the Braze SDK as needed to achieve the desired results. However, if you decide to override a factory, you'll likely need to explicitly defer to the default or reimplement the functionality provided by the Braze default. The following code snippet illustrates how to supply custom implementations of the `IInAppMessageViewFactory` and the `IInAppMessageViewWrapperFactory` interfaces. **In-app message types**
```kotlin class BrazeDemoApplication : Application(){ override fun onCreate() { super.onCreate() registerActivityLifecycleCallbacks(BrazeActivityLifecycleCallbackListener(true, true)) BrazeInAppMessageManager.getInstance().setCustomInAppMessageViewWrapperFactory(CustomInAppMessageViewWrapperFactory()) BrazeInAppMessageManager.getInstance().setCustomInAppMessageViewFactory(CustomInAppMessageViewFactory()) } } ``` **In-app message types**
```java public class BrazeDemoApplication extends Application { @Override public void onCreate{ super.onCreate(); registerActivityLifecycleCallbacks(new BrazeActivityLifecycleCallbackListener(true, true)); BrazeInAppMessageManager.getInstance().setCustomInAppMessageViewWrapperFactory(new CustomInAppMessageViewWrapperFactory()); BrazeInAppMessageManager.getInstance().setCustomInAppMessageViewFactory(new CustomInAppMessageViewFactory()); } } ``` Braze in-app message types are versatile enough to cover most custom use cases. However, if you want to fully define the visual appearance of your in-app messages instead of using a default type, Braze makes this possible by setting a custom view factory. The `BrazeInAppMessageManager` automatically handles placing the in-app message model into the existing activity view hierarchy by default using [`DefaultInAppMessageViewWrapper`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.ui.inappmessage/-default-in-app-message-view-wrapper/index.html). If you need to customize how in-app messages are placed into the view hierarchy, you should use a custom [`IInAppMessageViewWrapperFactory`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.ui.inappmessage/-i-in-app-message-view-wrapper-factory/index.html). In-app messages have preset animation behavior. `Slideup` messages slide into the screen; `full` and `modal` messages fade in and out. If you want to define custom animation behaviors for your in-app messages, Braze makes this possible by setting up a custom animation factory. ### Step 1: Implement the factory Create a class that implements [`IInAppMessageViewFactory`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.ui.inappmessage/-i-in-app-message-view-factory/index.html): ```java public class CustomInAppMessageViewFactory implements IInAppMessageViewFactory { @Override public View createInAppMessageView(Activity activity, IInAppMessage inAppMessage) { // Uses a custom view for slideups, modals, and full in-app messages. // HTML in-app messages and any other types will use the Braze default in-app message view factories switch (inAppMessage.getMessageType()) { case SLIDEUP: case MODAL: case FULL: // Use a custom view of your choosing return createMyCustomInAppMessageView(); default: // Use the default in-app message factories final IInAppMessageViewFactory defaultInAppMessageViewFactory = BrazeInAppMessageManager.getInstance().getDefaultInAppMessageViewFactory(inAppMessage); return defaultInAppMessageViewFactory.createInAppMessageView(activity, inAppMessage); } } } ``` ```kotlin class CustomInAppMessageViewFactory : IInAppMessageViewFactory { override fun createInAppMessageView(activity: Activity, inAppMessage: IInAppMessage): View { // Uses a custom view for slideups, modals, and full in-app messages. // HTML in-app messages and any other types will use the Braze default in-app message view factories when (inAppMessage.messageType) { MessageType.SLIDEUP, MessageType.MODAL, MessageType.FULL -> // Use a custom view of your choosing return createMyCustomInAppMessageView() else -> { // Use the default in-app message factories val defaultInAppMessageViewFactory = BrazeInAppMessageManager.getInstance().getDefaultInAppMessageViewFactory(inAppMessage) return defaultInAppMessageViewFactory!!.createInAppMessageView(activity, inAppMessage) } } } } ``` Create a class that implements [`IInAppMessageViewWrapperFactory`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.ui.inappmessage/-i-in-app-message-view-wrapper-factory/index.html) and returns an [`IInAppMessageViewWrapper`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.ui.inappmessage/-i-in-app-message-view-wrapper/index.html). This factory is called immediately after the in-app message view is created. The easiest way to implement a custom [`IInAppMessageViewWrapper`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.ui.inappmessage/-i-in-app-message-view-wrapper/index.html) is just to extend the default [`DefaultInAppMessageViewWrapper`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.ui.inappmessage/-default-in-app-message-view-wrapper/index.html): ```java public class CustomInAppMessageViewWrapper extends DefaultInAppMessageViewWrapper { public CustomInAppMessageViewWrapper(View inAppMessageView, IInAppMessage inAppMessage, IInAppMessageViewLifecycleListener inAppMessageViewLifecycleListener, BrazeConfigurationProvider brazeConfigurationProvider, Animation openingAnimation, Animation closingAnimation, View clickableInAppMessageView) { super(inAppMessageView, inAppMessage, inAppMessageViewLifecycleListener, brazeConfigurationProvider, openingAnimation, closingAnimation, clickableInAppMessageView); } @Override public void open(@NonNull Activity activity) { super.open(activity); Toast.makeText(activity.getApplicationContext(), "Opened in-app message", Toast.LENGTH_SHORT).show(); } @Override public void close() { super.close(); Toast.makeText(mInAppMessageView.getContext().getApplicationContext(), "Closed in-app message", Toast.LENGTH_SHORT).show(); } } ``` ```kotlin class CustomInAppMessageViewWrapper(inAppMessageView: View, inAppMessage: IInAppMessage, inAppMessageViewLifecycleListener: IInAppMessageViewLifecycleListener, brazeConfigurationProvider: BrazeConfigurationProvider, openingAnimation: Animation, closingAnimation: Animation, clickableInAppMessageView: View) : DefaultInAppMessageViewWrapper(inAppMessageView, inAppMessage, inAppMessageViewLifecycleListener, brazeConfigurationProvider, openingAnimation, closingAnimation, clickableInAppMessageView) { override fun open(activity: Activity) { super.open(activity) Toast.makeText(activity.applicationContext, "Opened in-app message", Toast.LENGTH_SHORT).show() } override fun close() { super.close() Toast.makeText(mInAppMessageView.context.applicationContext, "Closed in-app message", Toast.LENGTH_SHORT).show() } } ``` Create a class that implements [`IInAppMessageAnimationFactory`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.ui.inappmessage/-i-in-app-message-animation-factory/index.html): ```java public class CustomInAppMessageAnimationFactory implements IInAppMessageAnimationFactory { @Override public Animation getOpeningAnimation(IInAppMessage inAppMessage) { Animation animation = new AlphaAnimation(0, 1); animation.setInterpolator(new AccelerateInterpolator()); animation.setDuration(2000L); return animation; } @Override public Animation getClosingAnimation(IInAppMessage inAppMessage) { Animation animation = new AlphaAnimation(1, 0); animation.setInterpolator(new DecelerateInterpolator()); animation.setDuration(2000L); return animation; } } ``` ```kotlin class CustomInAppMessageAnimationFactory : IInAppMessageAnimationFactory { override fun getOpeningAnimation(inAppMessage: IInAppMessage): Animation { val animation: Animation = AlphaAnimation(0, 1) animation.interpolator = AccelerateInterpolator() animation.duration = 2000L return animation } override fun getClosingAnimation(inAppMessage: IInAppMessage): Animation { val animation: Animation = AlphaAnimation(1, 0) animation.interpolator = DecelerateInterpolator() animation.duration = 2000L return animation } } ``` ### Step 2: Instruct Braze to use the factory After your `IInAppMessageViewFactory` is created, call `BrazeInAppMessageManager.getInstance().setCustomInAppMessageViewFactory()` to instruct `BrazeInAppMessageManager` to use your custom `IInAppMessageViewFactory` instead of the default view factory. **Tip:** We recommend setting your `IInAppMessageViewFactory` in your `Application.onCreate()` before any other calls to Braze. This will set the custom view factory before any in-app message is displayed. #### How it works The `slideup` in-app message view implements [`IInAppMessageView`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.ui.inappmessage.views/-i-in-app-message-view/index.html). The `full` and `modal` type message views implement [`IInAppMessageImmersiveView`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.ui.inappmessage.views/-i-in-app-message-immersive-view/index.html). Implementing one of these classes allows Braze to add click listeners to your custom view where appropriate. All Braze view classes extend Android's [`View`](http://developer.android.com/reference/android/view/View.html) class. Implementing `IInAppMessageView` allows you to define a certain portion of your custom view as clickable. Implementing [`IInAppMessageImmersiveView`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.ui.inappmessage.views/-i-in-app-message-immersive-view/index.html) allows you to define message button views and a close button view. After your [`IInAppMessageViewWrapper`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.ui.inappmessage/-i-in-app-message-view-wrapper/index.html) is created, call [`BrazeInAppMessageManager.getInstance().setCustomInAppMessageViewWrapperFactory()`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.ui.inappmessage/-in-app-message-manager-base/set-custom-in-app-message-view-factory.html) to instruct `BrazeInAppMessageManager` to use your custom [`IInAppMessageViewWrapperFactory`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.ui.inappmessage/-i-in-app-message-view-wrapper-factory/index.html) instead of the default view wrapper factory. We recommend setting your [`IInAppMessageViewWrapperFactory`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.ui.inappmessage/-i-in-app-message-view-wrapper-factory/index.html) in your [`Application.onCreate()`](https://developer.android.com/reference/android/app/Application.html#onCreate()) before any other calls to Braze. This will set the custom view wrapper factory before any in-app message is displayed: ```java BrazeInAppMessageManager.getInstance().setCustomInAppMessageViewWrapperFactory(new CustomInAppMessageViewWrapper()); ``` ```kotlin BrazeInAppMessageManager.getInstance().setCustomInAppMessageViewWrapperFactory(CustomInAppMessageViewWrapper()) ``` Once your `IInAppMessageAnimationFactory` is created, call `BrazeInAppMessageManager.getInstance().setCustomInAppMessageAnimationFactory()` to instruct `BrazeInAppMessageManager` to use your custom `IInAppMessageAnimationFactory` instead of the default animation factory. We recommend setting your `IInAppMessageAnimationFactory` in your [`Application.onCreate()`](https://developer.android.com/reference/android/app/Application.html#onCreate()) before any other calls to Braze. This will set the custom animation factory before any in-app message is displayed. ## Custom styles Braze UI elements come with a default look and feel that matches the Android standard UI guidelines and provides a seamless experience. This reference article covers custom in-app messaging styling for your Android or FireOS application. ### Setting a default style You can see default styles in the Braze SDK's [`styles.xml`](https://github.com/braze-inc/braze-android-sdk/blob/master/android-sdk-ui/src/main/res/values/styles.xml) file: ```xml ``` If you would prefer, you can override these styles to create a look and feel that better suits your app. To override a style, copy it in its entirety to the `styles.xml` file in your project and make modifications. The whole style must be copied over to your local `styles.xml` file for all attributes to be correctly set. Note that these custom styles are for changes to individual UI elements, not wholesale changes to layouts. Layout-level changes need to be handled with custom views. **Note:** You can customize some colors directly in your Braze campaign without modifying the XML. Keep in mind, colors set in the Braze dashboard will override colors you set anywhere else. ### Customizing the font You can set a custom font by locating the typeface in the `res/font` directory. To use it, override the style for message text, headers, and button text and use the `fontFamily` attribute to instruct Braze to use your custom font family. For example, to update the font on your in-app message button text, override the `Braze.InAppMessage.Button` style and reference your custom font family. The attribute value should point to a font family in your `res/font` directory. Here is a truncated example with a custom font family, `my_custom_font_family`, referenced on the last line: ```xml ``` Aside from the `Braze.InAppMessage.Button` style for button text, the style for message text is `Braze.InAppMessage.Message` and the style for message headers is `Braze.InAppMessage.Header`. If you want to use your custom font family across all possible in-app message text, you can set your font family on the `Braze.InAppMessage` style, which is the parent style for all in-app messages. **Important:** As with other custom styles, the entire style must be copied over to your local `styles.xml` file for all attributes to be correctly set. ## Message dismissals ### Swiping to dismiss slideup messages By default, slideup in-app messages can be dismissed with a swipe gesture. The direction of the swipe depends on the slideup position: - **Left or right swipe:** Dismisses the slideup regardless of its position. - **Slideup from the bottom:** Swiping from top to bottom dismisses the message. Swiping from bottom to top does not dismiss it. - **Slideup from the top:** Swiping from bottom to top dismisses the message. Swiping from top to bottom does not dismiss it. This swipe behavior is built into the default [`DefaultInAppMessageViewWrapper`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.ui.inappmessage/-default-in-app-message-view-wrapper/index.html) and applies only to slideup in-app messages. Modal and full in-app messages don't support swipe-to-dismiss. To customize this behavior, you can implement a [custom view wrapper factory](#android_setting-custom-factories). **Note:** Tapping outside of a slideup message does not dismiss it by default. This behavior differs from modal messages, which can be configured for outside tap dismissal. For slideups, use the swipe gesture or the close button to dismiss the message. ### Disabling back button dismissals By default, the hardware back button dismisses Braze in-app messages. This behavior can be disabled on a per-message basis via [`BrazeInAppMessageManager.setBackButtonDismissesInAppMessageView()`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.ui.inappmessage/-in-app-message-manager-base/set-back-button-dismisses-in-app-message-view.html). In the following example, `disable_back_button` is a custom key-value pair set on the in-app message that signifies whether the message should allow for the back button to dismiss the message: ```java BrazeInAppMessageManager.getInstance().setCustomInAppMessageManagerListener(new DefaultInAppMessageManagerListener() { @Override public void beforeInAppMessageViewOpened(View inAppMessageView, IInAppMessage inAppMessage) { super.beforeInAppMessageViewOpened(inAppMessageView, inAppMessage); final Map extras = inAppMessage.getExtras(); if (extras != null && extras.containsKey("disable_back_button")) { BrazeInAppMessageManager.getInstance().setBackButtonDismissesInAppMessageView(false); } } @Override public void afterInAppMessageViewClosed(IInAppMessage inAppMessage) { super.afterInAppMessageViewClosed(inAppMessage); BrazeInAppMessageManager.getInstance().setBackButtonDismissesInAppMessageView(true); } }); ``` ```kotlin BrazeInAppMessageManager.getInstance().setCustomInAppMessageManagerListener(object : DefaultInAppMessageManagerListener() { override fun beforeInAppMessageViewOpened(inAppMessageView: View, inAppMessage: IInAppMessage) { super.beforeInAppMessageViewOpened(inAppMessageView, inAppMessage) val extras = inAppMessage.extras if (extras != null && extras.containsKey("disable_back_button")) { BrazeInAppMessageManager.getInstance().setBackButtonDismissesInAppMessageView(false) } } override fun afterInAppMessageViewClosed(inAppMessage: IInAppMessage) { super.afterInAppMessageViewClosed(inAppMessage) BrazeInAppMessageManager.getInstance().setBackButtonDismissesInAppMessageView(true) } }) ``` **Note:** Note that if this functionality is disabled, the host activity's hardware back button default behavior will be used instead. This may lead to the back button closing the application instead of the displayed in-app message. ### Enabling outside tap dismissals By default, dismissing the modal using an outside tap is set to `false`. Setting this value to `true` will result in the modal in-app message being dismissed when the user taps outside of the in-app message. This behavior can be toggled on by calling: ```java BrazeInAppMessageManager.getInstance().setClickOutsideModalViewDismissInAppMessageView(true) ``` ## Customizing the orientation To set a fixed orientation for an in-app message, first [set a custom in-app message manager listener](https://www.braze.com/docs/ko/ko/developer_guide/in_app_messages/customization/?sdktab=android#android_setting-custom-manager-listeners). Then, update the orientation on the `IInAppMessage` object in the `beforeInAppMessageDisplayed()` delegate method: ```java public InAppMessageOperation beforeInAppMessageDisplayed(IInAppMessage inAppMessage) { // Set the orientation to portrait inAppMessage.setOrientation(Orientation.PORTRAIT); return InAppMessageOperation.DISPLAY_NOW; } ``` ```kotlin override fun beforeInAppMessageDisplayed(inAppMessage: IInAppMessage): InAppMessageOperation { // Set the orientation to portrait inAppMessage.orientation = Orientation.PORTRAIT return InAppMessageOperation.DISPLAY_NOW } ``` For tablet devices, in-app messages will appear in the user's preferred orientation style regardless of actual screen orientation. ## Disabling dark theme {#android-in-app-message-dark-theme-customization} By default, `IInAppMessageManagerListener`'s `beforeInAppMessageDisplayed()` checks the system settings and conditionally enables dark theme styling on the message with the following code: ```java @Override public InAppMessageOperation beforeInAppMessageDisplayed(IInAppMessage inAppMessage) { if (inAppMessage instanceof IInAppMessageThemeable && ViewUtils.isDeviceInNightMode(BrazeInAppMessageManager.getInstance().getApplicationContext())) { ((IInAppMessageThemeable) inAppMessage).enableDarkTheme(); } return InAppMessageOperation.DISPLAY_NOW; } ``` ```kotlin override fun beforeInAppMessageDisplayed(inAppMessage: IInAppMessage): InAppMessageOperation { if (inAppMessage is IInAppMessageThemeable && ViewUtils.isDeviceInNightMode(BrazeInAppMessageManager.getInstance().applicationContext!!)) { (inAppMessage as IInAppMessageThemeable).enableDarkTheme() } return InAppMessageOperation.DISPLAY_NOW } ``` To change this, you can call [`enableDarkTheme`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.inappmessage/-i-in-app-message-themeable/enable-dark-theme.html) at any step in the pre-display process to implement your own conditional logic. ## Customizing the Google Play review prompt Due to the limitations and restrictions set by Google, custom Google Play review prompts are not currently supported by Braze. While some users have been able to integrate these prompts successfully, others have shown low success rates due to [Google Play quotas](https://developer.android.com/guide/playcore/in-app-review#quotas). Integrate at your own risk. Refer to documentation on [Google Play in-app review prompts](https://developer.android.com/guide/playcore/in-app-review). ## Prerequisites Before you can use this feature, you'll need to [integrate the Swift Braze SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=swift). ## Setting up the UI delegate (required) To customize the presentation of in-app messages and react to various lifecycle events, you'll need to set up [`BrazeInAppMessageUIDelegate`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazeui/brazeinappmessageuidelegate). This is a delegate protocol used for receiving and processing triggered in-app message payloads, receiving display lifecycle events, and controlling display timing. To use `BrazeInAppMessageUIDelegate`, you must: - Use the default [`BrazeInAppMessageUI`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazeui/brazeinappmessageui) implementation as your `inAppMessagePresenter`. - Include the `BrazeUI` library in your project. ### Step 1: Implement the `BrazeInAppMessageUIDelegate` protocol First, implement the `BrazeInAppMessageUIDelegate` protocol and any corresponding methods you wish. In our example below, we are implementing this protocol in our application's `AppDelegate` class. ```swift extension AppDelegate: BrazeInAppMessageUIDelegate { // Implement your protocol methods here. } ``` ```objc @interface AppDelegate () @end @implementation AppDelegate // Implement your protocol methods here. @end ``` ### Step 2: Assign the `delegate` object Assign the `delegate` object on the `BrazeInAppMessageUI` instance before assigning this in-app message UI as your `inAppMessagePresenter`. ```swift let inAppMessageUI = BrazeInAppMessageUI() inAppMessageUI.delegate = self AppDelegate.braze?.inAppMessagePresenter = inAppMessageUI ``` ```objc BrazeInAppMessageUI *inAppMessageUI = [[BrazeInAppMessageUI alloc] init]; inAppMessageUI.delegate = self; AppDelegate.braze.inAppMessagePresenter = inAppMessageUI; ``` **Important:** Not all delegate methods are available in Objective-C due to the incompatibility of their parameters with the language runtime. **Tip:** For a step-by-step implementation of the in-app message UI delegate, refer to this [tutorial](https://braze-inc.github.io/braze-swift-sdk/tutorials/braze/c1-inappmessageui). ## On-click behavior Each `Braze.InAppMessage` object contains a corresponding [`ClickAction`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/inappmessage/clickaction), which defines the behavior upon clicking. ### Click action types The `clickAction` property on your `Braze.InAppMessage` defaults to `.none` but can be set to one of the following values: | `ClickAction` | On-Click Behavior | | -------------------------- | -------- | | `.url(URL, useWebView: Bool)` | Opens the given URL in an external browser. If `useWebView` is set to `true`, it will open in a web view. | | `.none` | The message will be dismissed when clicked. | {: .reset-td-br-1 .reset-td-br-2 aria-label="Click action types" } **Important:** For in-app messages containing buttons, the message `clickAction` will also be included in the final payload if the click action is added prior to adding the button text. ### Customizing on-click behavior To customize this behavior, you may modify the `clickAction` property by referring to the following sample: ```swift func inAppMessage( _ ui: BrazeInAppMessageUI, prepareWith context: inout BrazeInAppMessageUI.PresentationContext ) { if let newUrl = URL(string: "{your-url}") { context.message.clickAction = .url(newUrl, useWebView: true) } } ``` The `inAppMessage(_:prepareWith:)` method is not available in Objective-C. ### Handling the custom behavior The following [`BrazeInAppMessageUIDelegate`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazeui/brazeinappmessageuidelegate) delegate method is called when a user clicks an in-app message. This callback is triggered for user-initiated clicks on in-app message buttons and HTML in-app message buttons (links), and a button ID is provided as an optional parameter for these interactions. This callback is not invoked for programmatic clicks triggered through `brazeBridge.logClick()`. ```swift func inAppMessage( _ ui: BrazeInAppMessageUI, shouldProcess clickAction: Braze.InAppMessage.ClickAction, buttonId: String?, message: Braze.InAppMessage, view: InAppMessageView ) -> Bool ``` ```objc - (BOOL)inAppMessage:(BrazeInAppMessageUI *)ui shouldProcess:(enum BRZInAppMessageRawClickAction)clickAction url:(NSURL *)uri buttonId:(NSString *)buttonId message:(BRZInAppMessageRaw *)message view:(UIView *)view; ``` This method returns a boolean value to indicate if Braze should continue to execute the click action. ```swift func inAppMessage( _ ui: BrazeInAppMessageUI, shouldProcess clickAction: Braze.InAppMessage.ClickAction, buttonId: String?, message: Braze.InAppMessage, view: InAppMessageView ) -> Bool { guard let buttonId, let idInt = Int(buttonId) else { return true } var button: BrazeKit.Braze.InAppMessage.Button? = nil switch message { case .modal(let modal): button = modal.buttons[idInt] case .modalImage(let modalImage): button = modalImage.buttons[idInt] case .full(let full): button = full.buttons[idInt] case .fullImage(let fullImage): button = fullImage.buttons[idInt] default: break } print(button?.id) print(button?.text) print(button?.clickAction) return true } ``` ```objc - (BOOL)inAppMessage:(BrazeInAppMessageUI *)ui shouldProcess:(enum BRZInAppMessageRawClickAction)clickAction url:(NSURL *)uri buttonId:(NSString *)buttonId message:(BRZInAppMessageRaw *)message view:(UIView *)view { NSInteger buttonInt = [buttonId integerValue]; if (message.type == BRZInAppMessageRawTypeFull || message.type == BRZInAppMessageRawTypeModal) { BRZInAppMessageRawButton *button = message.buttons[buttonInt]; NSLog(@"%ld", (long)button.identifier); NSLog(@"%@", button.text); NSLog(@"%ld", (long)button.clickAction); } return YES; } ``` ## Swiping to dismiss slideup messages By default, slideup in-app messages can be dismissed with a swipe gesture. The direction of the swipe depends on the slideup position: - **Left or right swipe:** Dismisses the slideup regardless of its position. - **Slideup from the bottom:** Swiping from top to bottom dismisses the message. Swiping from bottom to top does not dismiss it. - **Slideup from the top:** Swiping from bottom to top dismisses the message. Swiping from top to bottom does not dismiss it. This swipe behavior is built into the default `BrazeInAppMessageUI` [`SlideupView`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazeui/brazeinappmessageui/slideupview) and applies only to slideup in-app messages. Modal and full in-app messages don't support swipe-to-dismiss. To further customize the slideup view, including swipe behavior, you can modify the [`SlideupView.Attributes`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazeui/brazeinappmessageui/slideupview/attributes-swift.struct) or provide a custom view via subclassing. **Note:** Tapping outside of a slideup message does not dismiss it. For modal or full in-app messages, you can enable outside tap dismissals using the `dismissOnBackgroundTap` attribute described below. ## Customizing modal dismissals To enable outside tap dismissals, you can modify the `dismissOnBackgroundTap` property on the `Attributes` struct of the in-app message type you wish to customize. For example, if you wish to enable this feature for modal image in-app messages, you can configure the following: ```swift BrazeInAppMessageUI.ModalImageView.Attributes.defaults.dismissOnBackgroundTap = true ``` Customization via `Attributes` is not available in Objective-C. The default value is `false`. This determines if the modal in-app message will be dismissed when the user taps outside of the in-app message. | `DismissModalOnOutsideTap` | Description | |----------|-------------| | `true` | Modal in-app messages will be dismissed on outside tap. | | `false` | Default, modal in-app messages will not be dismissed on outside tap. | {: .reset-td-br-1 .reset-td-br-2 aria-label="Customizing modal dismissals" } For more details on in-app message customization, refer to this [article](https://braze-inc.github.io/braze-swift-sdk/documentation/braze/in-app-message-customization). ## Customizing message orientation You can customize the orientation of your in-app messages. You can set a new default orientation for all messages or set a custom orientation for a single message. To choose a default orientation for all in-app messages, use the [`inAppMessage(_:prepareWith:)`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazeui/brazeinappmessageuidelegate/inappmessage(_:preparewith:)-11fog) method to set the `preferredOrientation` property on the `PresentationContext`. For example, to set portrait as the default orientation: ```swift func inAppMessage( _ ui: BrazeInAppMessageUI, prepareWith context: inout BrazeInAppMessageUI.PresentationContext ) { context.preferredOrientation = .portrait } ``` ```objc - (void)inAppMessage:(BrazeInAppMessageUI *)ui prepareWith:(BrazeInAppMessageUIPresentationContextRaw *)context { context.preferredOrientation = BRZInAppMessageRawOrientationPortrait; } ``` To set the orientation for a single message, modify the `orientation` property of `Braze.InAppMessage`: ```swift // Set inAppMessage orientation to support any configuration inAppMessage.orientation = .any // Set inAppMessage orientation to only display in portrait inAppMessage.orientation = .portrait // Set inAppMessage orientation to only display in landscape inAppMessage.orientation = .landscape ``` ```objc // Set inAppMessage orientation to support any configuration inAppMessage.orientation = BRZInAppMessageRawOrientationAny; // Set inAppMessage orientation to only display in portrait inAppMessage.orientation = BRZInAppMessageRawOrientationPortrait; // Set inAppMessage orientation to only display in landscape inAppMessage.orientation = BRZInAppMessageRawOrientationLandscape; ``` After the in-app message is displayed, any device orientation changes while the message is still being displayed will cause the message to rotate with the device (provided it's supported by the message's `orientation` configuration). The device orientation must also be supported by the in-app message's `orientation` property for the message to display. Additionally, the `preferredOrientation` setting will only be respected if it is included in your application's supported interface orientations under the **Deployment Info** section of your target's settings in Xcode. ![Supported orientations in Xcode.](https://www.braze.com/docs/ko/ko/assets/img/supported_interface_orientations_xcode.png?79fd9f5e4c58ef88e3ab26db7e77897c) **Note:** The orientation is applied only for the presentation of the message. After the device changes orientation, the message view adopts one of the orientations it supports. On smaller devices (iPhones, iPod Touch), setting a landscape orientation for a modal or full in-app message may lead to truncated content. ## Customizing display timing You can control if an available in-app message will display during certain points of your user experience. If there are situations where you would not want the in-app message to appear, such as during a fullscreen game or on a loading screen, you can delay or discard pending in-app message messages. To control the timing of in-app message, use the `inAppMessage(_:displayChoiceForMessage:)` [delegate method](https://braze-inc.github.io/braze-swift-sdk/documentation/brazeui/brazeinappmessageuidelegate/inappmessage(_:displaychoiceformessage:)-9w1nb) to set the `BrazeInAppMessageUI.DisplayChoice` property. ```swift func inAppMessage( _ ui: BrazeInAppMessageUI, displayChoiceForMessage message: Braze.InAppMessage ) -> BrazeInAppMessageUI.DisplayChoice ``` ```objc - (enum BRZInAppMessageUIDisplayChoice)inAppMessage:(BrazeInAppMessageUI *)ui displayChoiceForMessage:(BRZInAppMessageRaw *)message ``` Configure `BrazeInAppMessageUI.DisplayChoice` to return one of the following values: | Display Choice | Behavior | | ----------------------------------- | --------------------------------------------------------------------------------------------------------------------------- | | `.now` | The message will be displayed immediately. This is the default value. | | `.reenqueue` | The message will be not be displayed and will be placed back on the top of the stack. | | `.later` | The message will be not be displayed and will be placed back on the top of the stack. (Deprecated, please use `.reenqueue`) | | `.discard` | The message will be discarded and will not be displayed. | {: .reset-td-br-1 .reset-td-br-2 aria-label="Customizing display timing" } **Tip:** For a sample of `InAppMessageUI`, check out our [Swift Braze SDK repository](https://github.com/braze-inc/braze-swift-sdk/tree/main/Examples/Swift/Sources/InAppMessageUI) and [Objective-C](https://github.com/braze-inc/braze-swift-sdk/tree/main/Examples/ObjC/Sources/InAppMessageUI). ## Hiding the status bar For `Full`, `FullImage` and `HTML` in-app messages, the SDK will hide the status bar by default. For other types of in-app messages, the status bar is left untouched. To configure this behavior, use the `inAppMessage(_:prepareWith:)` [delegate method](https://braze-inc.github.io/braze-swift-sdk/documentation/brazeui/brazeinappmessageuidelegate/inappmessage(_:preparewith:)-11fog) to set the `statusBarHideBehavior` property on the `PresentationContext`. This field takes one of the following values: | Status Bar Hide Behavior | Description | | ----------------------------------- | ------------------------------------------------------------------------------------- | | `.auto` | The message view decides the status bar hidden state. | | `.hidden` | Always hide the status bar. | | `.visible` | Always display the status bar. | {: .reset-td-br-1 .reset-td-br-2 aria-label="Hiding the status bar" } ## Disabling dark mode To prevent in-app messages from adopting dark mode styling when the user device has dark mode enabled, implement the `inAppMessage(_:prepareWith:)` [delegate method](https://braze-inc.github.io/braze-swift-sdk/documentation/brazeui/brazeinappmessageuidelegate/inappmessage(_:preparewith:)-11fog) method. The `PresentationContext` passed to the method contains a reference to the `InAppMessage` object to be presented. Each `InAppMessage` has a `themes` property containing a `dark` and `light` mode theme. If you set the `themes.dark` property to `nil`, Braze will automatically present the in-app message using its light theme. In-app message types with buttons have an additional `themes` object on their `buttons` property. To prevent buttons from adopting dark mode styling, you can use [`map(_:)`](https://developer.apple.com/documentation/swift/array/map(_:)-87c4d) to create a new array of buttons with a `light` theme and no `dark` theme. ```swift func inAppMessage( _ ui: BrazeInAppMessageUI, prepareWith context: inout BrazeInAppMessageUI.PresentationContext ) { switch context.message { case .slideup: guard var slideup = context.message.slideup else { return } slideup.themes.dark = nil context.message.slideup = slideup case .modal: guard var modal = context.message.modal else { return } modal.themes.dark = nil modal.buttons = modal.buttons.map { var newButton = $0 newButton.themes = .init(themes: ["light": $0.themes.light]) return newButton } context.message.modal = modal case .modalImage: guard var modalImage = context.message.modalImage else { return } modalImage.themes.dark = nil modalImage.buttons = modalImage.buttons.map { var newButton = $0 newButton.themes = .init(themes: ["light": $0.themes.light]) return newButton } context.message.modalImage = modalImage case .full: guard var full = context.message.full else { return } full.themes.dark = nil full.buttons = full.buttons.map { var newButton = $0 newButton.themes = .init(themes: ["light": $0.themes.light]) return newButton } context.message.full = full case .fullImage: guard var fullImage = context.message.fullImage else { return } fullImage.themes.dark = nil fullImage.buttons = fullImage.buttons.map { var newButton = $0 newButton.themes = .init(themes: ["light": $0.themes.light]) return newButton } context.message.fullImage = fullImage default: break } } ``` ```objc - (void)inAppMessage:(BrazeInAppMessageUI *)ui prepareWith:(BrazeInAppMessageUIPresentationContextRaw *)context { switch (context.message.type) { case BRZInAppMessageRawTypeSlideup: { NSMutableDictionary *updatedThemes = [context.message.themes mutableCopy]; [updatedThemes removeObjectForKey:@"dark"]; context.message.themes = updatedThemes; break; } case BRZInAppMessageRawTypeModal: case BRZInAppMessageRawTypeFull: { NSMutableDictionary *updatedThemes = [context.message.themes mutableCopy]; [updatedThemes removeObjectForKey:@"dark"]; context.message.themes = updatedThemes; NSMutableArray *updatedButtons = [NSMutableArray arrayWithCapacity:context.message.buttons.count]; for (BRZInAppMessageRawButton *button in context.message.buttons) { BRZInAppMessageRawButtonTheme *lightTheme = BRZInAppMessageRawButtonTheme.defaultLight; BRZInAppMessageRawButton *newButton = [button mutableCopy]; newButton.textColor = lightTheme.textColor; newButton.backgroundColor = lightTheme.backgroundColor; newButton.borderColor = lightTheme.borderColor; [updatedButtons addObject:newButton]; } context.message.buttons = updatedButtons; break; } default: break; } } ``` ## Customizing the app store review prompt You can use in-app messages in a campaign to ask users for an App Store review. **Note:** Because this example prompt overrides default behavior of Braze, we cannot automatically track impressions if it is implemented. You must [log your own analytics](https://www.braze.com/docs/ko/ko/developer_guide/analytics/). ### Step 1: Set the in-app message delegate First, set the [`BrazeInAppMessageUIDelegate`](https://www.braze.com/docs/ko/ko/developer_guide/in_app_messages/customization/#swift_setting-up-the-ui-delegate-required) in your app. ### Step 2: Disable the default App Store review message Next, implement the `inAppMessage(_:displayChoiceForMessage:)` [delegate method](https://braze-inc.github.io/braze-swift-sdk/documentation/brazeui/brazeinappmessageuidelegate/inappmessage(_:displaychoiceformessage:)-9w1nb) to disable the default App Store review message. ```swift func inAppMessage(_ ui: BrazeInAppMessageUI, displayChoiceForMessage message: Braze.InAppMessage) -> BrazeInAppMessageUI.DisplayChoice { if message.extras["AppStore Review"] != nil, let messageUrl = message.clickAction.url { UIApplication.shared.open(messageUrl, options: [:], completionHandler: nil) return .discard } else { return .now } } ``` ```objc - (enum BRZInAppMessageUIDisplayChoice)inAppMessage:(BrazeInAppMessageUI *)ui displayChoiceForMessage:(BRZInAppMessageRaw *)message { if (message.extras != nil && message.extras[@"AppStore Review"] != nil) { [[UIApplication sharedApplication] openURL:message.url options:@{} completionHandler:nil]; return BRZInAppMessageUIDisplayChoiceDiscard; } else { return BRZInAppMessageUIDisplayChoiceNow; } } ``` ### Step 3: Create a deep link In your deep link handling code, add the following code to process the `{YOUR-APP-SCHEME}:app-store-review` deep link. Note that you will need to import `StoreKit` to use `SKStoreReviewController`: ```swift func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool { let urlString = url.absoluteString.removingPercentEncoding if (urlString == "{YOUR-APP-SCHEME}:app-store-review") { SKStoreReviewController.requestReview() return true; } // Other deep link handling code… } ``` ```objc - (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary *)options { NSString *urlString = url.absoluteString.stringByRemovingPercentEncoding; if ([urlString isEqualToString:@"{YOUR-APP-SCHEME}:app-store-review"]) { [SKStoreReviewController requestReview]; return YES; } // Other deep link handling code… } ``` ### Step 4: Set custom on-click behavior Next, create an in-app messaging campaign with the following: - The key-value pair `"AppStore Review" : "true"` - The on-click behavior set to "Deep Link Into App", using the deep link `{YOUR-APP-SCHEME}:app-store-review`. **Tip:** Apple limits App Store review prompts to a maximum of three times per year for each user, so your campaign should be [rate-limited](https://www.braze.com/docs/ko/ko/user_guide/engagement_tools/campaigns/building_campaigns/rate-limiting/) to three times per year per user.

Users may turn off App Store review prompts. As a result, your custom review prompt should not promise that a native App Store review prompt will appear or directly ask for a review. ## Prerequisites Before you can use this feature, you'll need to [integrate the React Native Braze SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=react%20native). ## Methods for logging You can use these methods by passing your `BrazeInAppMessage` instance to log analytics and perform actions: | Method | Description | | --------------------------------------------------------- | ------------------------------------------------------------------------------------- | | `logInAppMessageClicked(inAppMessage)` | Logs a click for the provided in-app message data. | | `logInAppMessageImpression(inAppMessage)` | Logs an impression for the provided in-app message data. | | `logInAppMessageButtonClicked(inAppMessage, buttonId)` | Logs a button click for the provided in-app message data and button ID. | | `hideCurrentInAppMessage()` | Dismisses the currently displayed in-app message. | | `performInAppMessageAction(inAppMessage)` | Performs the action for an in-app message. | | `performInAppMessageButtonAction(inAppMessage, buttonId)` | Performs the action for an in-app message button. | {: .reset-td-br-1 .reset-td-br-2 aria-label="Methods for logging" } ## Handling message data In most cases, you can use the `Braze.addListener` method to register event listeners to handle data coming from in-app messages. Additionally, you can access the in-app message data in the JavaScript layer by calling the `Braze.subscribeToInAppMessage` method to have the SDKs publish an `inAppMessageReceived` event when an in-app message is triggered. Pass a callback to this method to execute your own code when the in-app message is triggered and received by the listener. To customize how message data is handled, refer to the following implementation examples: To enhance the default behavior, or if you don't have access to customize the native iOS or Android code, we recommend that you disable the default UI while still receiving in-app message events from Braze. To disable the default UI, pass `false` to the `Braze.subscribeToInAppMessage` method and use the in-app message data to construct your own message in JavaScript. Note that you will need to manually log analytics on your messages if you choose to disable the default UI. ```javascript import Braze from "@braze/react-native-sdk"; // Option 1: Listen for the event directly via `Braze.addListener`. // // You may use this method to accomplish the same thing if you don't // wish to make any changes to the default Braze UI. Braze.addListener(Braze.Events.IN_APP_MESSAGE_RECEIVED, (event) => { console.log(event.inAppMessage); }); // Option 2: Call `subscribeToInAppMessage`. // // Pass in `false` to disable the automatic display of in-app messages. Braze.subscribeToInAppMessage(false, (event) => { console.log(event.inAppMessage); // Use `event.inAppMessage` to construct your own custom message UI. }); ``` To include more advanced logic to determine whether or not to show an in-app message using the built-in UI, implement in-app messages through the native layer. **Warning:** Since this is an advanced customization option, note that overriding the default Braze implementation will also nullify the logic to emit in-app message events to your JavaScript listeners. If you wish to still use `Braze.subscribeToInAppMessage` or `Braze.addListener` as described in [Accessing in-app message data](#accessing-in-app-message-data), you will need to handle publishing the events yourself. Implement the `IInAppMessageManagerListener` as described in our Android article on [Custom Manager Listener](https://www.braze.com/docs/ko/ko/developer_guide/in_app_messages/customization/?sdktab=android#android_setting-custom-manager-listeners). In your `beforeInAppMessageDisplayed` implementation, you can access the `inAppMessage` data, send it to the JavaScript layer, and decide to show or not show the native message based on the return value. For more on these values, see our [Android documentation](https://www.braze.com/docs/ko/ko/developer_guide/in_app_messages/). ```java // In-app messaging @Override public InAppMessageOperation beforeInAppMessageDisplayed(IInAppMessage inAppMessage) { WritableMap parameters = new WritableNativeMap(); parameters.putString("inAppMessage", inAppMessage.forJsonPut().toString()); getReactNativeHost() .getReactInstanceManager() .getCurrentReactContext() .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class) .emit("inAppMessageReceived", parameters); // Note: return InAppMessageOperation.DISCARD if you would like // to prevent the Braze SDK from displaying the message natively. return InAppMessageOperation.DISPLAY_NOW; } ``` ### Overriding the default UI delegate By default, [`BrazeInAppMessageUI`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazeui/brazeinappmessageui/) is created and assigned when you initialize the `braze` instance. `BrazeInAppMessageUI` is an implementation of the [`BrazeInAppMessagePresenter`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/brazeinappmessagepresenter) protocol and comes with a `delegate` property that can be used to customize the handling of in-app messages that have been received. 1. Implement the `BrazeInAppMessageUIDelegate` delegate as described in [our iOS article here](https://braze-inc.github.io/braze-swift-sdk/tutorials/braze/c1-inappmessageui). 2. In the `inAppMessage(_:displayChoiceForMessage:)` delegate method, you can access the `inAppMessage` data, send it to the JavaScript layer, and decide to show or not show the native message based on the return value. For more details on these values, see our [iOS documentation](https://braze-inc.github.io/braze-swift-sdk/documentation/brazeui/brazeinappmessageuidelegate/). ```objc - (enum BRZInAppMessageUIDisplayChoice)inAppMessage:(BrazeInAppMessageUI *)ui displayChoiceForMessage:(BRZInAppMessageRaw *)message { // Convert the message to a JavaScript representation. NSData *inAppMessageData = [message json]; NSString *inAppMessageString = [[NSString alloc] initWithData:inAppMessageData encoding:NSUTF8StringEncoding]; NSDictionary *arguments = @{ @"inAppMessage" : inAppMessageString }; // Send to JavaScript. [self sendEventWithName:@"inAppMessageReceived" body:arguments]; // Note: Return `BRZInAppMessageUIDisplayChoiceDiscard` if you would like // to prevent the Braze SDK from displaying the message natively. return BRZInAppMessageUIDisplayChoiceNow; } ``` To use this delegate, assign it to `brazeInAppMessagePresenter.delegate` after initializing the `braze` instance. **Note:** `BrazeUI` can only be imported in Objective-C or Swift. If you are using Objective-C++, you will need to handle this in a separate file. ```objc @import BrazeUI; - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { BRZConfiguration *configuration = [[BRZConfiguration alloc] initWithApiKey:apiKey endpoint:endpoint]; Braze *braze = [BrazeReactBridge initBraze:configuration]; ((BrazeInAppMessageUI *)braze.inAppMessagePresenter).delegate = [[CustomDelegate alloc] init]; AppDelegate.braze = braze; } ``` ### Overriding the default native UI If you wish to fully customize the presentation of your in-app messages at the native iOS layer, conform to the [`BrazeInAppMessagePresenter`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/brazeinappmessagepresenter) protocol and assign your custom presenter following the sample below: ```objc BRZConfiguration *configuration = [[BRZConfiguration alloc] initWithApiKey:apiKey endpoint:endpoint]; Braze *braze = [BrazeReactBridge initBraze:configuration]; braze.inAppMessagePresenter = [[MyCustomPresenter alloc] init]; AppDelegate.braze = braze; ``` ## Customizing the display behavior You can change the display behavior of in-app messages at runtime via the following: ```csharp // Sets in-app messages to display immediately when triggered. Appboy.AppboyBinding.SetInAppMessageDisplayAction(BrazeUnityInAppMessageDisplayActionType.IAM_DISPLAY_NOW); // Sets in-app messages to display at a later time and be saved in a stack. Appboy.AppboyBinding.SetInAppMessageDisplayAction(BrazeUnityInAppMessageDisplayActionType.IAM_DISPLAY_LATER); // Sets in-app messages to be discarded after being triggered. Appboy.AppboyBinding.SetInAppMessageDisplayAction(BrazeUnityInAppMessageDisplayActionType.IAM_DISCARD); ``` ## Setting a custom listener If you require more control over how a user interacts with in-app messages, use a `BrazeInAppMessageListener` and assign it to `Appboy.AppboyBinding.inAppMessageListener`. For any delegates you don't want to use, you can simply leave them as `null`. ```csharp BrazeInAppMessageListener listener = new BrazeInAppMessageListener() { BeforeInAppMessageDisplayed = BeforeInAppMessageDisplayed, OnInAppMessageButtonClicked = OnInAppMessageButtonClicked, OnInAppMessageClicked = OnInAppMessageClicked, OnInAppMessageHTMLClicked = OnInAppMessageHTMLClicked, OnInAppMessageDismissed = OnInAppMessageDismissed, }; Appboy.AppboyBinding.inAppMessageListener = listener; public void BeforeInAppMessageDisplayed(IInAppMessage inAppMessage) { // Executed before an in-app message is displayed. } public void OnInAppMessageButtonClicked(IInAppMessage inAppMessage, InAppMessageButton inAppMessageButton) { // Executed whenever an in-app message button is clicked. } public void OnInAppMessageClicked(IInAppMessage inAppMessage) { // Executed whenever an in-app message is clicked. } public void OnInAppMessageHTMLClicked(IInAppMessage inAppMessage, Uri uri) { // Executed whenever an HTML in-app message is clicked. } public void OnInAppMessageDismissed(IInAppMessage inAppMessage) { // Executed whenever an in-app message is dismissed without a click. } ``` # Braze SDK를 통해 인앱 메시지 트리거하기 Source: /docs/ko/developer_guide/in_app_messages/triggering_messages/index.md # 인앱 메시지 트리거 {#trigger-in-app-messages} > Braze SDK를 통해 인앱 메시지를 트리거하는 방법을 알아보세요. ## 메시지 트리거 및 전달 {#message-triggers-and-delivery} 인앱 메시지는 SDK가 다음 커스텀 이벤트 유형 중 하나를 기록할 때 트리거됩니다: `Session Start`, `Push Click`, `Any Purchase`, `Specific Purchase`, `Custom Event`(마지막 두 개는 강력한 속성 필터를 포함합니다). 사용자 세션이 시작되면 Braze는 모든 적격 인앱 메시지를 기기에 전달하는 동시에 자산을 프리페칭하여 표시 지연 시간을 최소화합니다. 트리거 이벤트에 적격 인앱 메시지가 두 개 이상 있는 경우 우선순위가 가장 높은 메시지만 전달됩니다. 자세한 내용은 [세션 수명 주기](https://www.braze.com/docs/ko/ko/developer_guide/analytics/tracking_sessions/#about-the-session-lifecycle)를 참조하세요. **Note:** 인앱 메시지는 API 또는 API 이벤트를 통해 트리거할 수 없으며—SDK에서 기록한 커스텀 이벤트만 트리거할 수 있습니다. 로깅에 대해 자세히 알아보려면 [커스텀 이벤트 로깅](https://www.braze.com/docs/ko/ko/developer_guide/analytics/logging_events/)을 참조하세요. ## 인앱 메시지 유형 {#types-of-in-app-messages} Braze는 세션 시작 시 다음 유형의 인앱 메시지를 사용자 기기에 전송합니다: `inapp` 및 `templated_iam`. 대시보드 사용자로서 다른 유형을 직접 볼 수는 없지만, Braze는 설정과 콘텐츠에 따라 이를 다르게 처리합니다. ### `inapp` (표준) {#inapp-standard} `inapp`(또는 "[표준](https://www.braze.com/docs/ko/ko/user_guide/message_building_by_channel/in-app_messages/#standard-message-types)") 인앱 메시지는 Braze가 이미 알고 있는 커스텀 속성 등 필요한 정보가 이미 템플릿화되어 있습니다. 일반적으로 인앱 메시지가 기기에 다운로드되면 트리거 이벤트가 발생할 때 기기가 오프라인이거나 비행기 모드인 경우에도 SDK가 `inapp` 인앱 메시지를 표시합니다. ### `templated_iam` (템플릿) {#templated_iam-templated} `templated_iam`(또는 "템플릿") 인앱 메시지는 아직 필요한 정보가 템플릿화되지 않은 상태입니다. Braze는 메시지가 표시되기 전에 정보를 가져오기 위해 추가 요청을 해야 합니다. In-app messages are delivered as templated in-app messages when **Re-evaluate campaign eligibility before displaying** is selected or if any of the following Liquid tags exist in the message: - `canvas_entry_properties` - `connected_content` - SMS variables such as `{sms.${*}}` - `catalog_items` - `catalog_selection_items` - `event_properties` This means that during session start, the device will receive the trigger of that in-app message instead of the entire message. When the user triggers the in-app message, the user's device will make a network request to fetch the actual message. **Note:** The message will not be delivered if the device doesn't have access to the internet. The message might not be delivered if the Liquid logic takes too long to resolve. ## 키-값 페어 {#key-value-pairs} Braze에서 Campaign을 생성할 때 키-값 페어를 `extras`로 설정할 수 있으며, 인앱 메시징 오브젝트가 이를 활용하여 앱으로 데이터를 전송할 수 있습니다. ```javascript import * as braze from "@braze/web-sdk"; braze.subscribeToInAppMessage(function(inAppMessage) { // control group messages should always be "shown" // this will log an impression and not show a visible message if (inAppMessage instanceof braze.ControlMessage) { return braze.showInAppMessage(inAppMessage); } if (inAppMessage instanceof braze.InAppMessage) { const extras = inAppMessage.extras; if (extras) { for (const key in extras) { console.log("key: " + key + ", value: " + extras[key]); } } } braze.showInAppMessage(inAppMessage); }); ``` ```java Map getExtras() ``` ```kotlin extras: Map ``` **Tip:** 자세한 내용은 [KDoc](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.inappmessage/-i-in-app-message/index.html#1498425856%2FProperties%2F-1725759721)을 참조하세요. 다음 예제는 커스텀 로직을 사용하여 `extras`의 키-값 페어에 따라 인앱 메시지 표시를 설정합니다. 전체 커스터마이징 예시를 보려면 [샘플 앱](https://github.com/braze-inc/braze-swift-sdk/tree/main/Examples)을 확인하세요. ```swift let customization = message.extras["custom-display"] as? String if customization == "colorful-slideup" { // Perform your custom logic. } ``` ```objc if ([message.extras[@"custom-display"] isKindOfClass:[NSString class]]) { NSString *customization = message.extras[@"custom-display"]; if ([customization isEqualToString:@"colorful-slideup"]) { // Perform your custom logic. } } ``` ## 자동 트리거 비활성화하기 {#disabling-automatic-triggers} 기본적으로 인앱 메시지는 자동으로 트리거됩니다. 이 기능을 비활성화하려면: 로딩 스니펫 내에서 `braze.automaticallyShowInAppMessages()` 호출을 제거한 다음, 인앱 메시지 표시 여부를 처리하는 커스텀 로직을 생성합니다. ```javascript braze.subscribeToInAppMessage(function(inAppMessage) { // control group messages should always be "shown" // this will log an impression and not show a visible message if (inAppMessage.isControl) { // v4.5.0+, otherwise use `inAppMessage instanceof braze.ControlMessage` return braze.showInAppMessage(inAppMessage); } // Display the in-app message. You could defer display here by pushing this message to code within your own application. // If you don't want to use the display capabilities in Braze, you could alternatively pass the in-app message to your own display code here. if ( should_show_the_message_according_to_your_custom_logic ) { braze.showInAppMessage(inAppMessage); } else { // do nothing } }); ``` **Important:** `braze.automaticallyShowInAppMessages()`를 제거하지 않고 `braze.showInAppMessage`를 호출하면 메시지가 두 번 표시될 수 있습니다. 메시지 타이밍에 대한 고급 제어(트리거된 메시지 지연 및 복원 포함)에 대해서는 [튜토리얼: 트리거된 메시지 지연 및 복원](https://www.braze.com/docs/ko/ko/developer_guide/in_app_messages/tutorials/deferring_triggered_messages/)을 참조하세요. 1. 커스텀 리스너를 설정하려면 [`IInAppMessageManagerListener`](https://www.braze.com/docs/ko/ko/developer_guide/in_app_messages/customization/?sdktab=android&tab=global%20listener#android_step-1-implement-the-custom-manager-listener)를 구현하세요. 2. [`beforeInAppMessageDisplayed()`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.ui.inappmessage.listeners/-i-in-app-message-manager-listener/before-in-app-message-displayed.html) 메서드를 업데이트하여 [`InAppMessageOperation.DISCARD`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.ui.inappmessage/-in-app-message-operation/-d-i-s-c-a-r-d/index.html)를 반환하도록 하세요. 메시지 타이밍에 대한 고급 제어(나중에 표시 및 재대기열 포함)에 대해서는 [메시지 커스터마이징](https://www.braze.com/docs/ko/ko/developer_guide/in_app_messages/customization/?tab=global%20listener&subtab=kotlin#android_step-2-instruct-braze-to-use-the-custom-manager-listener) 페이지를 참조하세요. 1. 앱에서 `BrazeInAppMessageUIDelegate` 델리게이트를 구현합니다. 전체 안내를 보려면 [튜토리얼: 인앱 메시지 UI](https://braze-inc.github.io/braze-swift-sdk/tutorials/braze/c1-inappmessageui)를 참조하세요. 2. `inAppMessage(_:displayChoiceForMessage:)` 델리게이트 메서드를 업데이트하여 `.discard`를 반환하도록 합니다. 메시지 타이밍에 대한 고급 제어(트리거된 메시지 지연 및 복원 포함)에 대해서는 [튜토리얼: 트리거된 메시지 지연 및 복원](https://www.braze.com/docs/ko/ko/developer_guide/in_app_messages/tutorials/deferring_triggered_messages/)을 참조하세요. 1. `2.2.0` 버전 이상에서 기본적으로 활성화되어 있는 자동 통합 초기화 프로그램을 사용하고 있는지 확인합니다. 2. `braze.xml` 파일에 다음 줄을 추가하여 인앱 메시지 작업의 기본값을 `DISCARD`로 설정합니다. ```xml DISCARD ``` Android의 경우, Braze 구성 에디터에서 **Automatically Display In-App Messages**를 선택 해제합니다. 또는 Unity 프로젝트의 `braze.xml`에서 `com_braze_inapp_show_inapp_messages_automatically`를 `false`로 설정할 수 있습니다. 초기 인앱 메시지 표시 동작은 Braze 구성에서 "In App Message Manager Initial Display Operation"을 사용하여 설정할 수 있습니다. iOS의 경우, Braze 구성 에디터에서 게임 오브젝트 리스너를 설정하고 **Braze Displays In-App Messages**가 선택되어 있지 않은지 확인합니다. 초기 인앱 메시지 표시 동작은 Braze 구성에서 "In App Message Manager Initial Display Operation"을 사용하여 설정할 수 있습니다. ## 한 세션에서 두 개의 인앱 메시지 연결하기 {#chaining-two-in-app-messages-in-one-session} 세션 시작 시 인앱 메시지를 트리거한 다음, 첫 번째 메시지에서 버튼을 누른 후 두 번째 인앱 메시지를 트리거할 수 있습니다. 이를 위해 두 번째 메시지를 트리거할 버튼 클릭에 대한 커스텀 이벤트를 기록합니다. 두 번째 메시지의 트리거는 이미 기기에 있어야 하며(사용자가 이미 두 번째 메시지에 적격해야 함), 기기 측에서 발생해야 합니다(Braze SDK는 Braze 서버에서 발생하는 커스텀 속성 변경을 감지하지 않습니다). 인앱 메시지 트리거 간 기본 30초 쿨다운을 변경하여 여러 인앱 메시지를 빠르게 연속으로 표시해야 합니다. 플랫폼별 구성에 대해서는 [기본 사용량 제한 재정의하기](#overriding-the-default-rate-limit)를 참조하세요. ## 기본 사용량 제한 재정의하기 {#overriding-the-default-rate-limit} 기본적으로 SDK는 트리거된 인앱 메시지를 30초에 한 번으로 제한합니다. 이를 재정의하려면 Braze 인스턴스가 초기화되기 전에 구성 파일에 다음 속성을 추가하세요. 이 값은 초 단위의 새로운 사용량 제한으로 사용됩니다. 프로덕션 앱의 경우, 사용자가 연속적인 인앱 메시지에 압도되지 않도록 이 값을 10초 미만으로 설정하지 마세요. 테스트 및 샘플 앱 플로우의 경우 5초가 일반적인 설정입니다. 테스트를 위해 이 간격을 `0`으로 설정할 수 있습니다. 그러나 `0`초 간격이 여러 인앱 메시지를 동시에 표시하도록 강제하지는 않습니다. 하나의 인앱 메시지가 이미 표시 중인 경우, 현재 메시지가 닫힐 때까지 다른 트리거된 메시지는 표시되지 않습니다. ```javascript // Sets the minimum time interval between triggered in-app messages to 5 seconds instead of the default 30 braze.initialize('YOUR-API-KEY', { minimumIntervalBetweenTriggerActionsInSeconds: 5 }) ``` ```xml 5 ``` ```swift let configuration = Braze.Configuration( apiKey: "YOUR-APP-IDENTIFIER-API-KEY", endpoint: "YOUR-BRAZE-ENDPOINT" ) // Sets the minimum trigger time interval to 5 seconds configuration.triggerMinimumTimeInterval = 5 let braze = Braze(configuration: configuration) AppDelegate.braze = braze ``` ```objc BRZConfiguration *configuration = [[BRZConfiguration alloc] initWithApiKey:@"" endpoint:@""]; // Sets the minimum trigger time interval to 5 seconds configuration.triggerMinimumTimeInterval = 5; Braze *braze = [BrazePlugin initBraze:configuration]; AppDelegate.braze = braze; ``` ## 수동으로 메시지 트리거하기 {#manually-triggering-messages} 기본적으로 인앱 메시지는 SDK가 커스텀 이벤트를 기록할 때 자동으로 트리거됩니다. 그러나 이 외에도 다음 메서드를 사용하여 수동으로 메시지를 트리거할 수 있습니다. ### 서버 측 이벤트 사용 {#using-a-server-side-event} 현재 웹 Braze SDK는 서버 측 이벤트를 사용하여 메시지를 수동으로 트리거하는 기능을 지원하지 않습니다. 서버에서 전송한 이벤트를 사용하여 인앱 메시지를 트리거하려면, 기기에 무음 푸시 알림을 보내 커스텀 푸시 콜백이 SDK 기반 이벤트를 기록할 수 있도록 합니다. 그러면 이 이벤트가 사용자에게 표시되는 인앱 메시지를 트리거합니다. #### 1단계: 무음 푸시 수신을 위한 푸시 콜백 만들기 {#step-1-create-a-push-callback-to-receive-the-silent-push} 커스텀 푸시 콜백을 등록하여 특정 무음 푸시 알림을 수신 대기합니다. 자세한 내용은 [푸시 알림 설정](https://www.braze.com/docs/ko/ko/developer_guide/push_notifications/#android_setting-up-push-notifications)을 참조하세요. 인앱 메시지를 전달하기 위해 두 개의 이벤트가 기록됩니다. 하나는 서버에서, 다른 하나는 커스텀 푸시 콜백에서 기록됩니다. 동일한 이벤트가 중복되지 않도록 하려면, 푸시 콜백 내에서 기록된 이벤트는 일반적인 명명 규칙을 따라야 하며(예: "인앱 메시지 트리거 이벤트"), 서버에서 보낸 이벤트와 같은 이름이 아니어야 합니다. 이를 준수하지 않으면 단일 사용자 동작에 대해 기록된 중복 이벤트가 세분화 및 사용자 데이터에 영향을 미칠 수 있습니다. ```java Braze.getInstance(context).subscribeToPushNotificationEvents(event -> { final Bundle kvps = event.getNotificationPayload().getBrazeExtras(); if (kvps.containsKey("IS_SERVER_EVENT")) { BrazeProperties eventProperties = new BrazeProperties(); // The campaign name is a string extra that clients can include in the push String campaignName = kvps.getString("CAMPAIGN_NAME"); eventProperties.addProperty("campaign_name", campaignName); Braze.getInstance(context).logCustomEvent("IAM Trigger", eventProperties); } }); ``` ```kotlin Braze.getInstance(applicationContext).subscribeToPushNotificationEvents { event -> val kvps = event.notificationPayload.brazeExtras if (kvps.containsKey("IS_SERVER_EVENT")) { val eventProperties = BrazeProperties() // The campaign name is a string extra that clients can include in the push val campaignName = kvps.getString("CAMPAIGN_NAME") eventProperties.addProperty("campaign_name", campaignName) Braze.getInstance(applicationContext).logCustomEvent("IAM Trigger", eventProperties) } } ``` #### 2단계: 푸시 Campaign 만들기 {#step-2-create-a-push-campaign} 서버 전송 이벤트를 통해 트리거되는 [무음 푸시 Campaign](https://www.braze.com/docs/ko/ko/developer_guide/push_notifications/silent/?sdktab=android)을 만듭니다. ![server_event 커스텀 이벤트 트리거를 사용한 실행 기반 전달로 구성된 무음 푸시 Campaign의 전달 단계.](https://www.braze.com/docs/ko/ko/assets/img_archive/serverSentPush.png?ba3ed6cdbb6033f36d1e824f9ac5c350) 푸시 Campaign에는 이 푸시 Campaign이 SDK 커스텀 이벤트를 기록하기 위해 전송되었음을 나타내는 키-값 페어 추가 항목이 포함되어야 합니다. 이 이벤트는 인앱 메시지를 트리거하는 데 사용됩니다. ![두 개의 키-값 페어: IS_SERVER_EVENT는 "true"로 설정되고, CAMPAIGN_NAME은 "예시 캠페인 이름"으로 설정됩니다.](https://www.braze.com/docs/ko/ko/assets/img_archive/kvpConfiguration.png?2bd106b4fee497321c428fe8b8a6ccbe){: style="max-width:70%;" } 앞의 푸시 콜백 샘플 코드는 키-값 페어를 인식하고 적절한 SDK 커스텀 이벤트를 기록합니다. "인앱 메시지 트리거" 이벤트에 첨부할 이벤트 속성정보를 포함하려면 푸시 페이로드의 키-값 페어에서 이를 전달하면 됩니다. 이 예시에서는 후속 인앱 메시지의 캠페인 이름이 포함되었습니다. 그러면 커스텀 푸시 콜백은 커스텀 이벤트를 기록할 때 이벤트 속성정보의 매개변수로 값을 전달할 수 있습니다. #### 3단계: 인앱 메시지 Campaign 만들기 {#step-3-create-an-in-app-message-campaign} Braze 대시보드에서 사용자에게 표시되는 인앱 메시지 Campaign을 생성하세요. 이 Campaign에는 실행 기반 전달이 있어야 하며, 커스텀 푸시 콜백 내에서 기록된 커스텀 이벤트에서 트리거되어야 합니다. 다음 예제에서는 초기 무음 푸시의 일부로 이벤트 속성정보를 전송하여 트리거할 특정 인앱 메시지를 구성합니다. !["campaign_name"이 "IAM 캠페인 이름 예시"와 일치할 때 인앱 메시지가 트리거되는 실행 기반 전달 Campaign.](https://www.braze.com/docs/ko/ko/assets/img_archive/iam_event_trigger.png?131d09d4b7d8389dca24630f1e3ad054) 앱이 포그라운드에 있지 않은 상태에서 서버에서 전송된 이벤트가 기록되면, 이벤트는 기록되지만 인앱 메시지는 표시되지 않습니다. 애플리케이션이 포그라운드에 올 때까지 이벤트를 지연시키려면, 앱이 포그라운드에 진입할 때까지 이벤트를 해제하거나 지연시키도록 커스텀 푸시 수신기에 확인 로직을 포함해야 합니다. #### 1단계: 무음 푸시 및 키-값 페어 처리 {#step-1-handle-silent-push-and-key-value-pairs} 다음 함수를 구현하고 [`application(_:didReceiveRemoteNotification:fetchCompletionHandler:)` 메서드](https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1623013-application/) 내에서 호출하세요: ```swift func handleExtras(userInfo: [AnyHashable : Any]) { print("A push was received") if userInfo != nil && (userInfo["IS_SERVER_EVENT"] as? String) != nil && (userInfo["CAMPAIGN_NAME"] as? String) != nil { AppDelegate.braze?.logCustomEvent("IAM Trigger", properties: ["campaign_name": userInfo["CAMPAIGN_NAME"]]) } } ``` ```objc - (void)handleExtrasFromPush:(NSDictionary *)userInfo { NSLog(@"A push was received."); if (userInfo !=nil && userInfo[@"IS_SERVER_EVENT"] !=nil && userInfo[@"CAMPAIGN_NAME"]!=nil) { [AppDelegate.braze logCustomEvent:@"IAM Trigger" properties:@{@"campaign_name": userInfo[@"CAMPAIGN_NAME"]}]; } }; ``` 무음 푸시가 수신되면 SDK에서 기록한 이벤트 "인앱 메시지 트리거"가 고객 프로필에 기록됩니다. **Important:** 푸시 메시지가 SDK에서 기록한 커스텀 이벤트를 기록하는 데 사용되므로, Braze는 이 솔루션을 활성화하기 위해 각 사용자의 푸시 토큰을 저장해야 합니다. iOS 사용자의 경우, Braze는 사용자가 OS의 푸시 프롬프트를 받은 시점부터만 토큰을 저장합니다. 그 이전에는 푸시를 사용하여 사용자에게 도달할 수 없으며, 위의 솔루션은 사용할 수 없습니다. #### 2단계: 무음 푸시 Campaign 생성 {#step-2-create-a-silent-push-campaign} 서버 전송 이벤트를 통해 트리거되는 [무음 푸시 Campaign](https://www.braze.com/docs/ko/ko/developer_guide/push_notifications/silent/?sdktab=swift)을 생성합니다. ![고객 프로필에 커스텀 이벤트 "server_event"가 있는 사용자에게 전달될 실행 기반 인앱 메시지 Campaign.](https://www.braze.com/docs/ko/ko/assets/img_archive/iosServerSentPush.png?f2398c5efce1eef517dc7eabe0b5801b) 푸시 Campaign에는 이 푸시 Campaign이 SDK 커스텀 이벤트를 기록하기 위해 전송되었음을 나타내는 키-값 페어 추가 항목이 포함되어야 합니다. 이 이벤트는 인앱 메시지를 트리거하는 데 사용됩니다. ![두 개의 키-값 페어를 가진 실행 기반 전달 인앱 메시지 Campaign. "CAMPAIGN_NAME"은 "인앱 메시지 이름 예시"로 설정되고, "IS_SERVER_EVENT"는 "true"로 설정됩니다.](https://www.braze.com/docs/ko/ko/assets/img_archive/iOSServerPush.png?e84dc261f2b58bc43d35748e9c7db7f7) `application(_:didReceiveRemoteNotification:fetchCompletionHandler:)` 메서드 내 코드가 `IS_SERVER_EVENT` 키를 확인하고, 이 키가 존재하면 SDK 커스텀 이벤트를 기록합니다. 푸시 페이로드의 키-값 페어 추가 항목 내에서 원하는 값을 전송하여 이벤트 이름이나 이벤트 속성정보를 변경할 수 있습니다. 커스텀 이벤트를 기록할 때 이러한 추가 항목은 이벤트 이름의 매개변수 또는 이벤트 속성정보로 사용할 수 있습니다. #### 3단계: 인앱 메시지 Campaign 만들기 Braze 대시보드에서 사용자에게 표시되는 인앱 메시지 Campaign을 생성하세요. 이 Campaign은 실행 기반 전달이어야 하며, `application(_:didReceiveRemoteNotification:fetchCompletionHandler:)` 메서드 내에서 기록된 커스텀 이벤트에서 트리거되어야 합니다. 다음 예제에서는 초기 무음 푸시의 일부로 이벤트 속성정보를 전송하여 트리거할 특정 인앱 메시지를 구성합니다. ![사용자가 "인앱 메시지 트리거"라는 커스텀 이벤트를 수행할 때, "campaign_name"이 "IAM 캠페인 이름 예시"와 일치하는 사용자에게 전달되는 실행 기반 인앱 메시지 Campaign.](https://www.braze.com/docs/ko/ko/assets/img_archive/iosIAMeventTrigger.png?2f425e73fa63c23e0270be6007c72cbe) **Note:** 이 인앱 메시지는 애플리케이션이 포그라운드에 있을 때 무음 푸시를 수신해야만 트리거됩니다. ### 미리 정의된 메시지 표시 {#displaying-a-pre-defined-message} 미리 정의된 인앱 메시지를 수동으로 표시하려면 다음 메서드를 사용하세요: 웹 SDK의 경우, `braze.showInAppMessage(inAppMessage)`를 사용하여 인앱 메시지를 표시합니다. 자세한 내용과 예시는 [실시간 메시지 표시](#displaying-a-message-in-real-time)를 참조하세요. ```java BrazeInAppMessageManager.getInstance().addInAppMessage(inAppMessage); ``` ```kotlin BrazeInAppMessageManager.getInstance().addInAppMessage(inAppMessage) ``` ```swift if let inAppMessage = AppDelegate.braze?.inAppMessagePresenter?.nextAvailableMessage() { AppDelegate.braze?.inAppMessagePresenter?.present(message: inAppMessage) } ``` ### 실시간 메시지 표시 {#displaying-a-message-in-real-time} 대시보드에서 사용할 수 있는 동일한 커스터마이징 옵션을 사용하여 로컬 인앱 메시지를 실시간으로 생성하고 표시할 수도 있습니다. 이를 위해: ```javascript // Displays a slideup type in-app message. var message = new braze.SlideUpMessage("Welcome to Braze! This is an in-app message."); message.slideFrom = braze.InAppMessage.SlideFrom.TOP; braze.showInAppMessage(message); ``` ```java // Initializes a new slideup type in-app message and specifies its message. InAppMessageSlideup inAppMessage = new InAppMessageSlideup(); inAppMessage.setMessage("Welcome to Braze! This is a slideup in-app message."); ``` ```kotlin // Initializes a new slideup type in-app message and specifies its message. val inAppMessage = InAppMessageSlideup() inAppMessage.message = "Welcome to Braze! This is a slideup in-app message." ``` **Important:** 소프트 키보드가 화면에 표시될 때는 인앱 메시지를 표시하지 마세요. 이 상황에서는 렌더링이 정의되지 않습니다. `inAppMessagePresenter`에서 [`present(message:)`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/brazeinappmessagepresenter/present(message:)) 메서드를 수동으로 호출합니다. 예를 들어: ```swift let customInAppMessage = Braze.InAppMessage.slideup( .init(message: "YOUR_CUSTOM_SLIDEUP_MESSAGE", slideFrom: .bottom, themes: .defaults) ) AppDelegate.braze?.inAppMessagePresenter?.present(message: customInAppMessage) ``` ```objc BRZInAppMessageRaw *customInAppMessage = [[BRZInAppMessageRaw alloc] init]; customInAppMessage.type = BRZInAppMessageRawTypeSlideup; customInAppMessage.message = @"YOUR_CUSTOM_SLIDEUP_MESSAGE"; customInAppMessage.slideFrom = BRZInAppMessageRawSlideFromBottom; customInAppMessage.themes = @{ @"light": BRZInAppMessageRawTheme.defaultLight, @"dark": BRZInAppMessageRawTheme.defaultDark }; [AppDelegate.braze.inAppMessagePresenter presentMessage:customInAppMessage]; ``` **Note:** 인앱 메시지를 직접 생성하면 모든 분석 추적을 옵트아웃하게 되며, `message.context`를 사용하여 클릭 및 노출 횟수 로깅을 수동으로 처리해야 합니다. 스택에서 다음 메시지를 표시하려면 `DisplayNextInAppMessage()` 메서드를 사용합니다. `DISPLAY_LATER` 또는 `BrazeUnityInAppMessageDisplayActionType.IAM_DISPLAY_LATER`를 인앱 메시지 표시 동작으로 선택하면 메시지가 이 스택에 저장됩니다. ```csharp Appboy.AppboyBinding.DisplayNextInAppMessage(); ``` ## 인앱 메시지 지연 원인 {#causes-of-in-app-message-delays} 세션 시작 후 몇 초 뒤에 인앱 메시지 Campaign을 수신하는 경우, 다음과 같은 원인으로 지연이 발생했을 수 있습니다: - Campaign 트리거 지연 - 커스터마이징 - 트리거 이벤트가 예상보다 늦게 기록됨(예: `templated_iam`의 경우) ## 웹용 이탈 의도 메시지 {#exit-intent-messages-for-web} 이탈 의도 메시지는 방문자가 웹사이트를 떠나기 전에 중요한 정보를 전달하는 데 사용되는 비침해적 인앱 메시지입니다. 웹 SDK에서 이러한 메시지 유형에 대한 트리거를 설정하려면, 웹사이트에 이탈 의도 라이브러리(예: [ouibounce의 오픈 소스 라이브러리](https://github.com/carlsednaoui/ouibounce))를 구현한 다음, 다음 코드를 사용하여 `'exit intent'`를 Braze의 커스텀 이벤트로 기록하세요. 이제 향후 인앱 메시지 Campaign에서 이 메시지 유형을 커스텀 이벤트 트리거로 사용할 수 있습니다. ```javascript var _ouibounce = ouibounce(false, { callback: function() { braze.logCustomEvent('exit intent'); } }); ``` # HTML 인앱 메시지 Source: /docs/ko/developer_guide/in_app_messages/html_messages/index.md # HTML 인앱 메시지 {#html-in-app-messages} > 앱에 Braze JavaScript 인터페이스를 추가하여 Braze API를 사용해 커스텀 WebView에서 [HTML 인앱 메시지](https://www.braze.com/docs/ko/ko/user_guide/channels/in_app_messages/message_types/custom_html/)를 생성하는 방법을 알아보세요. ## Prerequisites Before you can use this feature, you'll need to [integrate the Android Braze SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=android). ## About HTML messages With the Braze JavaScript interface, you can leverage Braze inside the custom WebViews within your app. The [`InAppMessageJavascriptInterface`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.ui.inappmessage.jsinterface/-in-app-message-javascript-interface/index.html) is responsible for: 1. Injecting the Braze JavaScript bridge into your WebView, as outlined in [User Guide: HTML in-app messages](https://www.braze.com/docs/ko/ko/user_guide/message_building_by_channel/in-app_messages/customize/#custom-html-messages). 2. Passing the bridge methods received from your WebView to the [Braze Android SDK](https://github.com/braze-inc/braze-android-sdk). ## Adding the interface to a WebView Using Braze functionality from a WebView in your app can be done by adding the Braze JavaScript interface to your WebView. After the interface has been added, the same API available for [User Guide: HTML in-app messages](https://www.braze.com/docs/ko/ko/user_guide/message_building_by_channel/in-app_messages/customize/#custom-html-messages) will be available within your custom WebView. ```java String javascriptString = BrazeFileUtils.getAssetFileStringContents(context.getAssets(), "braze-html-bridge.js"); myWebView.loadUrl("javascript:" + javascriptString); final InAppMessageJavascriptInterface javascriptInterface = new InAppMessageJavascriptInterface(context, inAppMessage); myWebView.addJavascriptInterface(javascriptInterface, "brazeInternalBridge"); ``` ```kotlin val javascriptString = context.assets.getAssetFileStringContents("braze-html-bridge.js") myWebView.loadUrl("javascript:" + javascriptString!!) val javascriptInterface = InAppMessageJavascriptInterface(context, inAppMessage) myWebView.addJavascriptInterface(javascriptInterface, "brazeInternalBridge") ``` ## Embedding YouTube content YouTube and other HTML5 content can play in HTML in-app messages. This requires hardware acceleration to be enabled in the activity where the in-app message is being displayed; see the [Android developer guide](https://developer.android.com/guide/topics/graphics/hardware-accel.html#controlling) for more details. Hardware acceleration is only available on Android API versions 11 and later. The following is an example of an embedded YouTube video in an HTML snippet: ```html
X
``` ## Using deep links When using deep links or external links in Android HTML in-app messages, **do not** call `brazeBridge.closeMessage()` in your JavaScript. The SDK's internal logic automatically closes the in-app message when it redirects to a link. Calling `brazeBridge.closeMessage()` interferes with this process and may cause the message to become unresponsive when users return to your app. The following is an example of a deep link in a code snippet: ```javascript ``` ## Prerequisites Before you can use this feature, you'll need to [integrate the Swift Braze SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=swift). ## About HTML messages With the Braze JavaScript interface, you can leverage Braze inside the custom WebViews within your app. The interface's [`ScriptMessageHandler`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/webviewbridge/scriptmessagehandler) is responsible for: 1. Injecting the Braze JavaScript bridge into your WebView, as outlined in [User Guide: HTML in-app messages](https://www.braze.com/docs/ko/ko/user_guide/message_building_by_channel/in-app_messages/customize/#custom-html-messages). 2. Passing the bridge methods received from your WebView to the [Braze Swift SDK](https://github.com/braze-inc/braze-swift-sdk). ## Adding the interface to a WebView First, add the [`ScriptMessageHandler`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/webviewbridge/scriptmessagehandler) from `WebViewBridge` to your app. ```swift let scriptMessageHandler = Braze.WebViewBridge.ScriptMessageHandler(braze: braze) ``` Add the initialized `scriptMessageHandler` to a WkWebView's `userContentController`. ```swift configuration.userContentController.add( scriptMessageHandler, name: Braze.WebViewBridge.ScriptMessageHandler.name ) ``` Then create the WebView using your configuration. ```swift let webView = WKWebView(frame: .zero, configuration: configuration) ``` When you're finished, your code should be similar to the following: ```swift // Create the script message handler using your initialized Braze instance. let scriptMessageHandler = Braze.WebViewBridge.ScriptMessageHandler(braze: braze) // Create a web view configuration and setup the script message handler. let configuration = WKWebViewConfiguration() configuration.userContentController.addUserScript( Braze.WebViewBridge.ScriptMessageHandler.script ) configuration.userContentController.add( scriptMessageHandler, name: Braze.WebViewBridge.ScriptMessageHandler.name ) // Create the webview using the configuration let webView = WKWebView(frame: .zero, configuration: configuration) ``` ## Example: Logging a custom event In the following example, `BrazeBridge` logs a custom event from existing web content to the Braze Swift SDK. ```javascript Logging data via BrazeBridge Example ``` # Braze SDK용 인앱 메시지 딥링킹 Source: /docs/ko/developer_guide/in_app_messages/deep_linking/index.md # 인앱 메시지 딥링킹 {#in-app-message-deep-linking} > Braze SDK의 인앱 메시지 내에서 딥링크하는 방법을 알아보세요. ## Prerequisites Before you can use this feature, you'll need to [integrate the Android Braze SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=android). ## Creating a universal delegate The Android SDK provides the ability to set a single delegate object to custom handle all deep links opened by Braze across Content Cards, in-app messages, and push notifications. Your delegate object should implement the [`IBrazeDeeplinkHandler`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.ui/-braze-deeplink-handler/index.html) interface and be set using [`BrazeDeeplinkHandler.setBrazeDeeplinkHandler()`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.ui/-braze-deeplink-handler/-companion/set-braze-deeplink-handler.html). In most cases, the delegate should be set in your app's `Application.onCreate()`. The following is an example of overriding the default [`UriAction`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.ui.actions/-uri-action/index.html) behavior with custom intent flags and custom behavior for YouTube URLs: ```java public class CustomDeeplinkHandler implements IBrazeDeeplinkHandler { private static final String TAG = BrazeLogger.getBrazeLogTag(CustomDeeplinkHandler.class); @Override public void gotoUri(Context context, UriAction uriAction) { String uri = uriAction.getUri().toString(); // Open YouTube URLs in the YouTube app and not our app if (!StringUtils.isNullOrBlank(uri) && uri.contains("youtube.com")) { uriAction.setUseWebView(false); } CustomUriAction customUriAction = new CustomUriAction(uriAction); customUriAction.execute(context); } public static class CustomUriAction extends UriAction { public CustomUriAction(@NonNull UriAction uriAction) { super(uriAction); } @Override protected void openUriWithActionView(Context context, Uri uri, Bundle extras) { Intent intent = getActionViewIntent(context, uri, extras); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP); if (intent.resolveActivity(context.getPackageManager()) != null) { context.startActivity(intent); } else { BrazeLogger.w(TAG, "Could not find appropriate activity to open for deep link " + uri + "."); } } } } ``` ```kotlin class CustomDeeplinkHandler : IBrazeDeeplinkHandler { override fun gotoUri(context: Context, uriAction: UriAction) { val uri = uriAction.uri.toString() // Open YouTube URLs in the YouTube app and not our app if (!StringUtils.isNullOrBlank(uri) && uri.contains("youtube.com")) { uriAction.useWebView = false } val customUriAction = CustomUriAction(uriAction) customUriAction.execute(context) } class CustomUriAction(uriAction: UriAction) : UriAction(uriAction) { override fun openUriWithActionView(context: Context, uri: Uri, extras: Bundle) { val intent = getActionViewIntent(context, uri, extras) intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP if (intent.resolveActivity(context.packageManager) != null) { context.startActivity(intent) } else { BrazeLogger.w(TAG, "Could not find appropriate activity to open for deep link $uri.") } } } companion object { private val TAG = BrazeLogger.getBrazeLogTag(CustomDeeplinkHandler::class.java) } } ``` ## Deep linking to app settings To allow deep links to directly open your app's settings, you'll need a custom `BrazeDeeplinkHandler`. In the following example, the presence of a custom key-value pair called `open_notification_page` will make the deep link open the app's settings page: ```java BrazeDeeplinkHandler.setBrazeDeeplinkHandler(new IBrazeDeeplinkHandler() { @Override public void gotoUri(Context context, UriAction uriAction) { final Bundle extras = uriAction.getExtras(); if (extras.containsKey("open_notification_page")) { Intent intent = new Intent(); intent.setAction("android.settings.APP_NOTIFICATION_SETTINGS"); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); //for Android 5-7 intent.putExtra("app_package", context.getPackageName()); intent.putExtra("app_uid", context.getApplicationInfo().uid); // for Android 8 and later intent.putExtra("android.provider.extra.APP_PACKAGE", context.getPackageName()); context.startActivity(intent); } } }); ``` ```kotlin BrazeDeeplinkHandler.setBrazeDeeplinkHandler(object : IBrazeDeeplinkHandler { override fun gotoUri(context: Context, uriAction: UriAction) { val extras = uriAction.extras if (extras.containsKey("open_notification_page")) { val intent = Intent() intent.action = "android.settings.APP_NOTIFICATION_SETTINGS" intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK //for Android 5-7 intent.putExtra("app_package", context.packageName) intent.putExtra("app_uid", context.applicationInfo.uid) // for Android 8 and later intent.putExtra("android.provider.extra.APP_PACKAGE", context.packageName) context.startActivity(intent) } } }) ``` ## Customizing WebView activity {#Custom_Webview_Activity} When Braze opens website deeplinks inside the app, the deeplinks are handled by [`BrazeWebViewActivity`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.ui/-braze-web-view-activity/index.html). **Note:** For custom HTML in-app messages, links configured with `target="_blank"` open in the device's default web browser and are not handled by `BrazeWebViewActivity`. To change this: 1. Create a new Activity that handles the target URL from `Intent.getExtras()` with the key `com.braze.Constants.BRAZE_WEBVIEW_URL_EXTRA`. For an example, see [`BrazeWebViewActivity.kt`](https://github.com/braze-inc/braze-android-sdk/blob/master/android-sdk-ui/src/main/java/com/braze/ui/BrazeWebViewActivity.kt). 2. Add that activity to `AndroidManifest.xml` and set `exported` to `false`. ```xml ``` 3. Set your custom Activity in a `BrazeConfig` [builder object](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.configuration/-braze-config/-builder/set-custom-web-view-activity-class.html). Build the builder and pass it to [`Braze.configure()`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze/-braze/-companion/configure.html) in your [`Application.onCreate()`](https://developer.android.com/reference/android/app/Application.html#onCreate()). ```java BrazeConfig brazeConfig = new BrazeConfig.Builder() .setCustomWebViewActivityClass(MyCustomWebViewActivity::class) ... .build(); Braze.configure(this, brazeConfig); ``` ```kotlin val brazeConfig = BrazeConfig.Builder() .setCustomWebViewActivityClass(MyCustomWebViewActivity::class.java) ... .build() Braze.configure(this, brazeConfig) ``` ## Troubleshooting If deep links from push notifications aren't working on Android, try the following steps: 1. **Test the deep link outside of Braze.** Open the deep link URL from another app, such as email or a browser. If it doesn't open your app, the deep link may not be configured correctly in your `AndroidManifest.xml`. For more information, see Android's [Create Deep Links](https://developer.android.com/training/app-links/deep-linking) documentation. 2. **Check that automatic deep link handling is enabled.** Verify that `com_braze_handle_push_deep_links_automatically` is set to `true` in `braze.xml`, or set this option through [runtime configuration](https://www.braze.com/docs/ko/ko/developer_guide/sdk_initalization/?sdktab=android). Without this setting, Braze doesn't automatically open your app and deep link destination when someone taps a push notification. 3. **Verify your deep link handler delegate.** If you set a custom `IBrazeDeeplinkHandler`, confirm that your `gotoUri` implementation handles the URI and doesn't drop it. 4. **Test across channels.** If the same deep link works in an in-app message but not from push, the issue is likely in your push deep link handling, not in the deep link itself. ## Using Jetpack Compose To handle deeplinks when using Jetpack Compose with NavHost: 1. Ensure that the activity handling your deeplink is registered in the Android Manifest. ```xml ``` 2. In NavHost, specify which deeplinks you want it to handle. ```kotlin composableWithCompositionLocal( route = "YOUR_ROUTE_HERE", deepLinks = listOf(navDeepLink { uriPattern = "myapp://articles/{${MainDestinations.ARTICLE_ID_KEY}}" }), arguments = listOf( navArgument(MainDestinations.ARTICLE_ID_KEY) { type = NavType.LongType } ), ) { backStackEntry -> val arguments = requireNotNull(backStackEntry.arguments) val articleId = arguments.getLong(MainDestinations.ARTICLE_ID_KEY) ArticleDetail( articleId ) } ``` 3. Depending on your app architecture, you may need to handle the new intent that's sent to your current activity as well. ```kotlin DisposableEffect(Unit) { val listener = Consumer { navHostController.handleDeepLink(it) } addOnNewIntentListener(listener) onDispose { removeOnNewIntentListener(listener) } } ``` ## Prerequisites Before you can use this feature, you'll need to [integrate the Swift Braze SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=swift). **Tip:** For help choosing between custom scheme deep links, universal links, and "Open Web URL Inside App," see [iOS deep linking guide](https://www.braze.com/docs/ko/ko/developer_guide/push_notifications/ios_deep_linking_guide). For troubleshooting, see [Deep linking troubleshooting](https://www.braze.com/docs/ko/ko/developer_guide/push_notifications/deep_linking_troubleshooting). ## Handling deep links ### Step 1: Register a scheme {#register-a-scheme} To handle deep linking, a custom scheme must be stated in your `Info.plist` file. The navigation structure is defined by an array of dictionaries. Each of those dictionaries contains an array of strings. Use Xcode to edit your `Info.plist` file: 1. Add a new key, `URL types`. Xcode will automatically make this an array containing a dictionary called `Item 0`. 2. Within `Item 0`, add a key `URL identifier`. Set the value to your custom scheme. 3. Within `Item 0`, add a key `URL Schemes`. This will automatically be an array containing a `Item 0` string. 4. Set `URL Schemes` >> `Item 0` to your custom scheme. Alternatively, if you wish to edit your `Info.plist` file directly, you can follow this spec: ```html CFBundleURLTypes CFBundleURLName YOUR.SCHEME CFBundleURLSchemes YOUR.SCHEME ``` ### Step 2: Add a scheme allowlist You must declare the URL schemes you wish to pass to `canOpenURL(_:)` by adding the `LSApplicationQueriesSchemes` key to your app's Info.plist file. Attempting to call schemes outside this allowlist will cause the system to record an error in the device's logs, and the deep link will not open. An example of this error will look like this: ``` : -canOpenURL: failed for URL: "yourapp://deeplink" – error: "This app is not allowed to query for scheme yourapp" ``` For example, if an in-app message should open the Facebook app when tapped, the app has to have the Facebook custom scheme (`fb`) in your allowlist. Otherwise, the system will reject the deep link. Deep links that direct to a page or view inside your own app still require that your app's custom scheme be listed in your app's `Info.plist`. Your example allowlist might look something like: ```html LSApplicationQueriesSchemes myapp fb twitter ``` For more information, refer to [Apple's documentation](https://developer.apple.com/library/content/documentation/General/Reference/InfoPlistKeyReference/Articles/LaunchServicesKeys.html#//apple_ref/doc/uid/TP40009250-SW14) on the `LSApplicationQueriesSchemes` key. ### Step 3: Implement a handler After activating your app, iOS will call the method [`application:openURL:options:`](https://developer.apple.com/reference/uikit/uiapplicationdelegate/1623112-application?language=objc). The important argument is the [NSURL](https://developer.apple.com/library/ios/DOCUMENTATION/Cocoa/Reference/Foundation/Classes/NSURL_Class/Reference/Reference.html#//apple_ref/doc/c_ref/NSURL) object. ```swift func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool { let path = url.path let query = url.query // Insert your code here to take some action based upon the path and query. return true } ``` ```objc - (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary *)options { NSString *path = [url path]; NSString *query = [url query]; // Insert your code here to take some action based upon the path and query. return YES; } ``` ## App Transport Security (ATS) As defined by [Apple](https://developer.apple.com/library/prerelease/ios/releasenotes/General/WhatsNewIniOS/Articles/iOS9.html#//apple_ref/doc/uid/TP40016198-SW14), "App Transport Security is a feature that improves the security of connections between an app and web services. The feature consists of default connection requirements that conform to best practices for secure connections. Apps can override this default behavior and turn off transport security." ATS is applied by default. It requires that all connections use HTTPS and are encrypted using TLS 1.2 with forward secrecy. Refer to [Requirements for Connecting Using ATS](https://developer.apple.com/library/ios/documentation/General/Reference/InfoPlistKeyReference/Articles/CocoaKeys.html#//apple_ref/doc/uid/TP40009251-SW35) for more information. All images served by Braze to end devices are handled by a content delivery network ("CDN") that supports TLS 1.2 and is compatible with ATS. Unless they are specified as exceptions in your application's `Info.plist`, connections that do not follow these requirements will fail with errors that are similar to the following. **Example Error 1:** ```bash CFNetwork SSLHandshake failed (-9801) Error Domain=NSURLErrorDomain Code=-1200 "An SSL error has occurred, and a secure connection to the server cannot be made." ``` **Example Error 2:** ```bash NSURLSession/NSURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9802) ``` ATS compliance is enforced for links opened within the mobile app (our default handling of clicked links) and does not apply to sites opened externally via a web browser. ### Working with ATS You can handle ATS in either of the following ways, but we recommend **complying with ATS requirements**. Your Braze integration can satisfy ATS requirements by ensuring that any existing links you drive users to (for example, though in-app message and push campaigns) satisfy ATS requirements. While there are ways to bypass ATS restrictions, our recommendation is to ensure that all linked URLs are ATS-compliant. Given Apple's increasing emphasis on application security, the following approaches to allowing ATS exceptions are not guaranteed to be supported by Apple. You can allow a subset of links with certain domains or schemes to be treated as exceptions to the ATS rules. Your Braze integration will satisfy ATS requirements if every link you use in a Braze messaging channel is either ATS compliant or handled by an exception. To add a domain as an exception of the ATS, add following to your app's `Info.plist` file: ```html NSAppTransportSecurity NSAllowsArbitraryLoads NSExceptionDomains example.com NSExceptionAllowsInsecureHTTPLoads NSIncludesSubdomains ``` Refer to Apple's article on [app transport security keys](https://developer.apple.com/library/ios/documentation/General/Reference/InfoPlistKeyReference/Articles/CocoaKeys.html#//apple_ref/doc/uid/TP40009251-SW33) for more information. You can turn off ATS entirely. Note that this is not recommended practice, due to both lost security protections and future iOS compatibility. To disable ATS, insert the following in your app's `Info.plist` file: ```html NSAppTransportSecurity NSAllowsArbitraryLoads ``` ## Decoding URLs The SDK percent-encodes links to create valid `URL`s. All link characters that are not allowed in a properly formed URL, such as Unicode characters, will be percent escaped. To decode an encoded link, use the `String` property [`removingPercentEncoding`](https://developer.apple.com/documentation/swift/stringprotocol/removingpercentencoding). You must also return `true` in the `BrazeDelegate.braze(_:shouldOpenURL:)`. A call to action is required to trigger the handling of the URL by your app. For example: ```swift func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool { let urlString = url.absoluteString.removingPercentEncoding // Handle urlString return true } ``` ```objc - (BOOL)application:(UIApplication *)application openURL:(NSURL *)url options:(NSDictionary *)options { NSString *urlString = [url.absoluteString stringByRemovingPercentEncoding]; // Handle urlString return YES; } ``` ## Deep linking to app settings You can take advantage of `UIApplicationOpenSettingsURLString` to deep link users to your app's settings from Braze push notifications and in-app messages. To take users from your app into the iOS settings: 1. First, make sure your application is set up for either [scheme-based deep links](#swift_register-a-scheme) or [universal links](#swift_universal-links). 2. Decide on a URI for deep linking to the **Settings** page (for example, `myapp://settings` or `https://www.braze.com/settings`). 3. If you are using custom scheme-based deep links, add the following code to your `application:openURL:options:` method: ```swift func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool { let path = url.path if (path == "settings") { UIApplication.shared.openURL(URL(string:UIApplication.openSettingsURLString)!) } return true } ``` ```objc - (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary *)options { NSString *path = [url path]; if ([path isEqualToString:@"settings"]) { NSURL *settingsURL = [NSURL URLWithString:UIApplicationOpenSettingsURLString]; [[UIApplication sharedApplication] openURL:settingsURL]; } return YES; } ``` ## Customization options {#customization-options} ### Default WebView customization The `Braze.WebViewController` class displays web URLs opened by the SDK, typically when "Open Web URL Inside App" is selected for a web deep link. You can customize the `Braze.WebViewController` via the [`BrazeDelegate.braze(_:willPresentModalWithContext:)`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/brazedelegate/braze(_:willpresentmodalwithcontext:)-12sqy/) delegate method. ### Linking handling customization The `BrazeDelegate` protocol can be used to customize the handling of URLs such as deep links, web URLs, and universal links. To set the delegate during Braze initialization, set a delegate object on the `Braze` instance. Braze will then call your delegate's implementation of `shouldOpenURL` before handling any URIs. When a push notification or in-app message uses **Open web URL inside mobile app**, Braze passes `context.useWebView == true` on [`Braze.URLContext`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/urlcontext). When the message opens the URL in the system browser instead, `useWebView` is `false`. Inspect `context.useWebView` in `braze(_:shouldOpenURL:)` to branch your custom handling—for example, to open an in-app `WebViewController` only when the campaign requested in-app display. #### Universal links {#universal-links} Braze supports universal links in push notifications, in-app messages, and Content Cards. To enable universal link support, [`configuration.forwardUniversalLinks`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/configuration-swift.class/forwarduniversallinks) must be set to `true`. When enabled, Braze will forward universal links to your app's `AppDelegate` via the [`application:continueUserActivity:restorationHandler:`](https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1623072-application) method. Your application also needs to be set up to handle universal links. Refer to [Apple's documentation](https://developer.apple.com/documentation/xcode/supporting-universal-links-in-your-app) to ensure your application is configured correctly for universal links. **Warning:** Universal link forwarding requires access to the application entitlements. When running the application in a simulator, these entitlements are not directly available and universal links are not forwarded to the system handlers. To add support to simulator builds, you can add the application `.entitlements` file to the _Copy Bundle Resources_ build phase. See [`forwardUniversalLinks`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/configuration-swift.class/forwarduniversallinks) documentation for more details. **Note:** The SDK does not query your domains' `apple-app-site-association` file. It performs the differentiation between universal links and regular URLs by looking at the domain name only. As a result, the SDK does not respect any exclusion rule defined in the `apple-app-site-association` per [Supporting associated domains](https://developer.apple.com/documentation/xcode/supporting-associated-domains). ## Examples ### BrazeDelegate Here's an example using `BrazeDelegate`. For more information, see [Braze Swift SDK reference](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/brazedelegate). ```swift func braze(_ braze: Braze, shouldOpenURL context: Braze.URLContext) -> Bool { if context.url.host == "MY-DOMAIN.com" { // Custom handle link here return false } // Let Braze handle links otherwise return true } ``` ```objc - (BOOL)braze:(Braze *)braze shouldOpenURL:(BRZURLContext *)context { if ([[context.url.host lowercaseString] isEqualToString:@"MY-DOMAIN.com"]) { // Custom handle link here return NO; } // Let Braze handle links otherwise return YES; } ``` # Braze SDK의 인앱 메시지에 GIF 삽입 Source: /docs/ko/developer_guide/in_app_messages/gifs/index.md # 인앱 메시지에 GIF 삽입 > Braze SDK용 인앱 메시지에 GIF를 삽입하는 방법을 알아보세요. ## About GIFs Braze offers the ability to use a custom image library to display animated GIFs. Although the example below uses [Glide](https://bumptech.github.io/glide/), any image library that supports GIFs is compatible. ## Integrating a custom image library ### Step 1: Creating the image loader delegate The Image Loader delegate must implement the following methods: * [`getInAppMessageBitmapFromUrl()`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.images/-i-braze-image-loader/get-in-app-message-bitmap-from-url.html) * [`getPushBitmapFromUrl()`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.images/-i-braze-image-loader/get-push-bitmap-from-url.html) * [`renderUrlIntoCardView()`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.images/-i-braze-image-loader/render-url-into-card-view.html) * [`renderUrlIntoInAppMessageView()`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.images/-i-braze-image-loader/render-url-into-in-app-message-view.html) * [`setOffline()`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.images/-i-braze-image-loader/set-offline.html) The integration example below is taken from the [Glide integration sample app](https://github.com/braze-inc/braze-android-sdk/tree/master/samples/glide-image-integration) included with the Braze Android SDK. ```java import com.braze.support.BrazeLogger; import com.bumptech.glide.load.resource.gif.GifDrawable; import android.graphics.drawable.Drawable; public class GlideBrazeImageLoader implements IBrazeImageLoader { private static final String TAG = GlideBrazeImageLoader.class.getName(); private RequestOptions mRequestOptions = new RequestOptions(); @Override public void renderUrlIntoCardView(Context context, Card card, String imageUrl, ImageView imageView, BrazeViewBounds viewBounds) { renderUrlIntoView(context, imageUrl, imageView); } @Override public void renderUrlIntoInAppMessageView(Context context, IInAppMessage inAppMessage, String imageUrl, ImageView imageView, BrazeViewBounds viewBounds) { renderUrlIntoView(context, imageUrl, imageView); } @Override public Bitmap getPushBitmapFromUrl(Context context, Bundle extras, String imageUrl, BrazeViewBounds viewBounds) { return getBitmapFromUrl(context, imageUrl, viewBounds); } @Override public Bitmap getInAppMessageBitmapFromUrl(Context context, IInAppMessage inAppMessage, String imageUrl, BrazeViewBounds viewBounds) { return getBitmapFromUrl(context, imageUrl, viewBounds); } private void renderUrlIntoView(Context context, String imageUrl, ImageView imageView) { try { final Drawable drawable = Glide.with(context) .load(imageUrl) .apply(mRequestOptions) .submit() .get(); imageView.post(() -> { imageView.setImageDrawable(drawable); if (drawable instanceof GifDrawable) { ((GifDrawable) drawable).start(); } }); } catch (Exception e) { BrazeLogger.e(TAG, "Failed to render URL into view: " + imageUrl, e); } } private Bitmap getBitmapFromUrl(Context context, String imageUrl, BrazeViewBounds viewBounds) { try { return Glide.with(context) .asBitmap() .apply(mRequestOptions) .load(imageUrl).submit().get(); } catch (Exception e) { Log.e(TAG, "Failed to retrieve bitmap at url: " + imageUrl, e); } return null; } @Override public void setOffline(boolean isOffline) { // If the loader is offline, then we should only be retrieving from the cache mRequestOptions = mRequestOptions.onlyRetrieveFromCache(isOffline); } } ``` ```kotlin import com.braze.support.BrazeLogger import com.bumptech.glide.load.resource.gif.GifDrawable class GlideBrazeImageLoader : IBrazeImageLoader { companion object { private val TAG = GlideBrazeImageLoader::class.qualifiedName } private var mRequestOptions = RequestOptions() override fun renderUrlIntoCardView(context: Context, card: Card, imageUrl: String, imageView: ImageView, viewBounds: BrazeViewBounds) { renderUrlIntoView(context, imageUrl, imageView) } override fun renderUrlIntoInAppMessageView(context: Context, inAppMessage: IInAppMessage, imageUrl: String, imageView: ImageView, viewBounds: BrazeViewBounds) { renderUrlIntoView(context, imageUrl, imageView) } override fun getPushBitmapFromUrl(context: Context, extras: Bundle, imageUrl: String, viewBounds: BrazeViewBounds): Bitmap? { return getBitmapFromUrl(context, imageUrl, viewBounds) } override fun getInAppMessageBitmapFromUrl(context: Context, inAppMessage: IInAppMessage, imageUrl: String, viewBounds: BrazeViewBounds): Bitmap? { return getBitmapFromUrl(context, imageUrl, viewBounds) } private fun renderUrlIntoView(context: Context, imageUrl: String, imageView: ImageView) { try { val drawable = Glide.with(context) .load(imageUrl) .apply(mRequestOptions) .submit() .get() imageView.post { imageView.setImageDrawable(drawable) if (drawable is GifDrawable) { drawable.start() } } } catch (e: Exception) { BrazeLogger.e(TAG, "Failed to render URL into view: $imageUrl", e) } } private fun getBitmapFromUrl(context: Context, imageUrl: String, viewBounds: BrazeViewBounds): Bitmap? { try { return Glide.with(context) .asBitmap() .apply(mRequestOptions) .load(imageUrl).submit().get() } catch (e: Exception) { Log.e(TAG, "Failed to retrieve bitmap at url: $imageUrl", e) } return null } override fun setOffline(isOffline: Boolean) { // If the loader is offline, then we should only be retrieving from the cache mRequestOptions = mRequestOptions.onlyRetrieveFromCache(isOffline) } } ``` ### Fixing image loading for Android SDK 36.0.0 and later In Android SDK 36.0.0 and later, `displayInAppMessage()` is a `suspend` function. This means `renderUrlIntoInAppMessageView()` runs on a background thread instead of the main thread. If your custom image loader calls `Glide.into(imageView)` in `renderUrlIntoInAppMessageView()`, your app can fail with "You must call this method on the main thread." To avoid this: 1. Load the image on the background thread with `submit().get()`. 2. Post the UI update to the main thread with `imageView.post { ... }`. 3. If the loaded result is a GIF drawable, start the animation after setting it on the view. This separates image loading from UI rendering, and keeps your custom image loader compatible with Android SDK 36.0.0 and later. This guidance applies to Android custom image loaders. Web in-app messages support GIFs out of the box. The following Kotlin sample uses placeholder values to show this pattern: ```kotlin private const val TAG = "SampleGlideLoader" private const val glideBrazeImageLoaderTag = "sample-loader" private fun renderUrlIntoView( context: Context, imageUrl: String, imageView: ImageView ) { try { val drawable: Drawable = Glide.with(context) .load(imageUrl) .apply(mRequestOptions) .submit() .get() imageView.post { imageView.setImageDrawable(drawable) if (drawable is GifDrawable) { drawable.start() } } } catch (e: Exception) { Log.e(TAG, "$glideBrazeImageLoaderTag renderUrlIntoView failed: url=$imageUrl", e) } } ``` ### Step 2: Setting the image loader delegate The Braze SDK will use any custom image loader set with [`IBrazeImageLoader`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.images/-i-braze-image-loader/index.html). We recommend setting the custom image loader in a custom application subclass: ```java public class GlideIntegrationApplication extends Application { @Override public void onCreate() { super.onCreate(); Braze.getInstance(context).setImageLoader(new GlideBrazeImageLoader()); } } ``` ```kotlin class GlideIntegrationApplication : Application() { override fun onCreate() { super.onCreate() Braze.getInstance(context).imageLoader = GlideBrazeImageLoader() } } ``` ## Custom Image Loading with Jetpack Compose To override image loading with Jetpack Compose, you can pass in a value to [`imageComposable`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.jetpackcompose.contentcards.styling/-content-card-styling/index.html#-808910455%2FProperties%2F-1725759721). This function will take a `Card` and render the image and the modifiers needed. Alternatively, you can use `customCardComposer` of `ContentCardsList` to render the entire card. In the following example, Glide's Compose library is used for the cards listed in the `imageComposable` function: ```kotlin ContentCardsList( cardStyle = ContentCardStyling( imageComposable = { card -> when (card.cardType) { CardType.CAPTIONED_IMAGE -> { val captionedImageCard = card as CaptionedImageCard GlideImage( modifier = Modifier .fillMaxWidth() .wrapContentHeight() .run { if (captionedImageCard.aspectRatio > 0) { aspectRatio(captionedImageCard.aspectRatio) } else { this } }, contentScale = ContentScale.Crop, model = captionedImageCard.url, loading = placeholder(R.drawable.pushpin), contentDescription = "" ) } CardType.IMAGE -> { val imageOnlyCard = card as ImageOnlyCard GlideImage( modifier = Modifier .fillMaxWidth() .run { if (imageOnlyCard.aspectRatio > 0) { aspectRatio(imageOnlyCard.aspectRatio) } else { this } }, contentScale = ContentScale.Crop, model = imageOnlyCard.url, loading = placeholder(R.drawable.pushpin), contentDescription = "" ) } CardType.SHORT_NEWS -> { val shortNews = card as ShortNewsCard GlideImage( modifier = Modifier .width(100.dp) .height(100.dp), model = shortNews.url, loading = placeholder(R.drawable.pushpin), contentDescription = "" ) } else -> Unit } } ) ) ``` ## Prerequisites Before you can use this feature, you'll need to [integrate the Swift Braze SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=swift). ## Integrating a custom image library ### Step 1: Integrate SDWebImage Integrate the [SDWebImage repository](https://github.com/SDWebImage/SDWebImage) into your Xcode project. ### Step 2: Create a new Swift file In your Xcode project, create a new file named `SDWebImageGIFViewProvider.swift` and import the following: ```swift import UIKit import BrazeUI import SDWebImage ``` ### Step 3: Add `GIFViewProvider` Next, add our sample SDWebImage [`GIFViewProvider`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazeui/gifviewprovider/). Your file should be similar to the following: ```swift import UIKit import BrazeUI import SDWebImage extension GIFViewProvider { /// A GIF view provider using [SDWebImage](https://github.com/SDWebImage/SDWebImage) as a /// rendering library. public static let sdWebImage = Self( view: { SDAnimatedImageView(image: image(for: $0)) }, updateView: { ($0 as? SDAnimatedImageView)?.image = image(for: $1) } ) private static func image(for url: URL?) -> UIImage? { guard let url else { return nil } return url.pathExtension == "gif" ? SDAnimatedImage(contentsOfFile: url.path) : UIImage(contentsOfFile: url.path) } } ``` ### Step 4: Modify your `AppDelegate.swift` In your project's `AppDelegate.swift`, add GIF support to your `BrazeUI` components using `GIFViewProvider`. Your file should be similar to the following: ```swift import UIKit import BrazeKit import BrazeUI @main class AppDelegate: UIResponder, UIApplicationDelegate { static var braze: Braze? = nil func application( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? ) -> Bool { /* ... */ GIFViewProvider.shared = .sdWebImage return true } } ``` # Braze 소프트웨어 개발 키트를 통해 앱 내 인앱 메시지 데이터 기록 Source: /docs/ko/developer_guide/in_app_messages/logging_message_data/index.md # 인앱 메시지 데이터 기록 > Braze SDK를 통해 인앱 메시지(IAM) 데이터를 로그하는 방법을 알아보세요. ## Prerequisites Before you can use this feature, you'll need to [integrate the Web Braze SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=web). ## Logging message data Logging in-app message [impressions](https://js.appboycdn.com/web-sdk/latest/doc/modules/braze.html#loginappmessageimpression) and [clicks](https://js.appboycdn.com/web-sdk/latest/doc/modules/braze.html#loginappmessagebuttonclick) is performed automatically when you use the `showInAppMessage` or `automaticallyShowInAppMessage` method. If you do not use either method and opt to manually display the message using your own UI code, use the following methods to log analytics: ```javascript // Registers that a user has viewed an in-app message with the Braze server. braze.logInAppMessageImpression(inAppMessage); // Registers that a user has clicked on the specified in-app message with the Braze server. braze.logInAppMessageClick(inAppMessage); // Registers that a user has clicked a specified in-app message button with the Braze server. braze.logInAppMessageButtonClick(button, inAppMessage); // Registers that a user has clicked on a link in an HTML in-app message with the Braze server. braze.logInAppMessageHtmlClick(inAppMessage, buttonId?, url?) ``` ## Prerequisites Before you can use this feature, you'll need to [integrate the Flutter Braze SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=flutter). ## Logging message data To log analytics using your `BrazeInAppMessage`, pass the instance into the desired analytics function: - `logInAppMessageClicked` - `logInAppMessageImpression` - `logInAppMessageButtonClicked` (along with the button index) For example: ```dart // Log a click braze.logInAppMessageClicked(inAppMessage); // Log an impression braze.logInAppMessageImpression(inAppMessage); // Log button index `0` being clicked braze.logInAppMessageButtonClicked(inAppMessage, 0); ``` ## Accessing message data To access in-app message data in your Flutter app, the `BrazePlugin` supports sending in-app message data using [Dart Streams](https://dart.dev/tutorials/language/streams). The `BrazeInAppMessage` object supports a subset of fields available in the native model objects, including `uri`, `message`, `header`, `buttons`, `extras`, and more. ### Listen for in-app message data in the Dart layer To receive in-app message data in the Dart layer, use the code below to create a `StreamSubscription` and call `braze.subscribeToInAppMessages()`. Remember to `cancel()` the stream subscription when it is no longer needed. ```dart // Create stream subscription StreamSubscription inAppMessageStreamSubscription; inAppMessageStreamSubscription = braze.subscribeToInAppMessages((BrazeInAppMessage inAppMessage) { // Handle in-app messages } // Cancel stream subscription inAppMessageStreamSubscription.cancel(); ``` For an example, see [main.dart](https://github.com/braze-inc/braze-flutter-sdk/blob/master/example/lib/main.dart) in the Braze Flutter SDK sample application. ### Forward in-app message data from the native layer In-app message data is automatically forwarded from both the Android and iOS native layers. No additional setup is required. If you're using Flutter SDK 17.1.0 or earlier, in-app message data forwarding from the iOS native layer requires manual setup. Your application likely contains one of the following. To migrate to Flutter SDK 18.0.0, remove the `BrazePlugin.processInAppMessage(_:)` call—data forwarding is now handled automatically. Remove the `BrazePlugin.processInAppMessage(_:)` call from your [`willPresent` delegate implementation](https://braze-inc.github.io/braze-swift-sdk/documentation/brazeui/brazeinappmessageuidelegate/inappmessage(_:willpresent:view:)-4pzvv). Remove the `BrazePlugin.processInAppMessage(message)` call from your custom presenter's [`present(message:)`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazeui/brazeinappmessageui/present(message:)-f2ra) implementation: ```swift class CustomInAppMessagePresenter: BrazeInAppMessageUI { override func present(message: Braze.InAppMessage) { // Pass in-app message data to the Dart layer. BrazePlugin.processInAppMessage(message) // If you want the default UI to display the in-app message. super.present(message: message) } } ``` ### Replaying the callback for in-app messages (optional) To store any in-app messages triggered before the callback is available and replay them after it is set, add the following entry to the `customConfigs` map when initializing the `BrazePlugin`: ```dart BrazePlugin braze = new BrazePlugin(customConfigs: {replayCallbacksConfigKey: true}); ``` ## Prerequisites Before you can use this feature, you'll need to [integrate the React Native Braze SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=react%20native). ## Methods for logging You can use these methods by passing your `BrazeInAppMessage` instance to log analytics and perform actions: | Method | Description | | --------------------------------------------------------- | ------------------------------------------------------------------------------------- | | `logInAppMessageClicked(inAppMessage)` | Logs a click for the provided in-app message data. | | `logInAppMessageImpression(inAppMessage)` | Logs an impression for the provided in-app message data. | | `logInAppMessageButtonClicked(inAppMessage, buttonId)` | Logs a button click for the provided in-app message data and button ID. | | `hideCurrentInAppMessage()` | Dismisses the currently displayed in-app message. | | `performInAppMessageAction(inAppMessage)` | Performs the action for an in-app message. | | `performInAppMessageButtonAction(inAppMessage, buttonId)` | Performs the action for an in-app message button. | {: .reset-td-br-1 .reset-td-br-2 aria-label="Methods for logging" } ## Handling message data In most cases, you can use the `Braze.addListener` method to register event listeners to handle data coming from in-app messages. Additionally, you can access the in-app message data in the JavaScript layer by calling the `Braze.subscribeToInAppMessage` method to have the SDKs publish an `inAppMessageReceived` event when an in-app message is triggered. Pass a callback to this method to execute your own code when the in-app message is triggered and received by the listener. To customize how message data is handled, refer to the following implementation examples: To enhance the default behavior, or if you don't have access to customize the native iOS or Android code, we recommend that you disable the default UI while still receiving in-app message events from Braze. To disable the default UI, pass `false` to the `Braze.subscribeToInAppMessage` method and use the in-app message data to construct your own message in JavaScript. Note that you will need to manually log analytics on your messages if you choose to disable the default UI. ```javascript import Braze from "@braze/react-native-sdk"; // Option 1: Listen for the event directly via `Braze.addListener`. // // You may use this method to accomplish the same thing if you don't // wish to make any changes to the default Braze UI. Braze.addListener(Braze.Events.IN_APP_MESSAGE_RECEIVED, (event) => { console.log(event.inAppMessage); }); // Option 2: Call `subscribeToInAppMessage`. // // Pass in `false` to disable the automatic display of in-app messages. Braze.subscribeToInAppMessage(false, (event) => { console.log(event.inAppMessage); // Use `event.inAppMessage` to construct your own custom message UI. }); ``` To include more advanced logic to determine whether or not to show an in-app message using the built-in UI, implement in-app messages through the native layer. **Warning:** Since this is an advanced customization option, note that overriding the default Braze implementation will also nullify the logic to emit in-app message events to your JavaScript listeners. If you wish to still use `Braze.subscribeToInAppMessage` or `Braze.addListener` as described in [Accessing in-app message data](#accessing-in-app-message-data), you will need to handle publishing the events yourself. Implement the `IInAppMessageManagerListener` as described in our Android article on [Custom Manager Listener](https://www.braze.com/docs/ko/ko/developer_guide/in_app_messages/customization/?sdktab=android#android_setting-custom-manager-listeners). In your `beforeInAppMessageDisplayed` implementation, you can access the `inAppMessage` data, send it to the JavaScript layer, and decide to show or not show the native message based on the return value. For more on these values, see our [Android documentation](https://www.braze.com/docs/ko/ko/developer_guide/in_app_messages/). ```java // In-app messaging @Override public InAppMessageOperation beforeInAppMessageDisplayed(IInAppMessage inAppMessage) { WritableMap parameters = new WritableNativeMap(); parameters.putString("inAppMessage", inAppMessage.forJsonPut().toString()); getReactNativeHost() .getReactInstanceManager() .getCurrentReactContext() .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class) .emit("inAppMessageReceived", parameters); // Note: return InAppMessageOperation.DISCARD if you would like // to prevent the Braze SDK from displaying the message natively. return InAppMessageOperation.DISPLAY_NOW; } ``` ### Overriding the default UI delegate By default, [`BrazeInAppMessageUI`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazeui/brazeinappmessageui/) is created and assigned when you initialize the `braze` instance. `BrazeInAppMessageUI` is an implementation of the [`BrazeInAppMessagePresenter`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/brazeinappmessagepresenter) protocol and comes with a `delegate` property that can be used to customize the handling of in-app messages that have been received. 1. Implement the `BrazeInAppMessageUIDelegate` delegate as described in [our iOS article here](https://braze-inc.github.io/braze-swift-sdk/tutorials/braze/c1-inappmessageui). 2. In the `inAppMessage(_:displayChoiceForMessage:)` delegate method, you can access the `inAppMessage` data, send it to the JavaScript layer, and decide to show or not show the native message based on the return value. For more details on these values, see our [iOS documentation](https://braze-inc.github.io/braze-swift-sdk/documentation/brazeui/brazeinappmessageuidelegate/). ```objc - (enum BRZInAppMessageUIDisplayChoice)inAppMessage:(BrazeInAppMessageUI *)ui displayChoiceForMessage:(BRZInAppMessageRaw *)message { // Convert the message to a JavaScript representation. NSData *inAppMessageData = [message json]; NSString *inAppMessageString = [[NSString alloc] initWithData:inAppMessageData encoding:NSUTF8StringEncoding]; NSDictionary *arguments = @{ @"inAppMessage" : inAppMessageString }; // Send to JavaScript. [self sendEventWithName:@"inAppMessageReceived" body:arguments]; // Note: Return `BRZInAppMessageUIDisplayChoiceDiscard` if you would like // to prevent the Braze SDK from displaying the message natively. return BRZInAppMessageUIDisplayChoiceNow; } ``` To use this delegate, assign it to `brazeInAppMessagePresenter.delegate` after initializing the `braze` instance. **Note:** `BrazeUI` can only be imported in Objective-C or Swift. If you are using Objective-C++, you will need to handle this in a separate file. ```objc @import BrazeUI; - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { BRZConfiguration *configuration = [[BRZConfiguration alloc] initWithApiKey:apiKey endpoint:endpoint]; Braze *braze = [BrazeReactBridge initBraze:configuration]; ((BrazeInAppMessageUI *)braze.inAppMessagePresenter).delegate = [[CustomDelegate alloc] init]; AppDelegate.braze = braze; } ``` ### Overriding the default native UI If you wish to fully customize the presentation of your in-app messages at the native iOS layer, conform to the [`BrazeInAppMessagePresenter`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/brazeinappmessagepresenter) protocol and assign your custom presenter following the sample below: ```objc BRZConfiguration *configuration = [[BRZConfiguration alloc] initWithApiKey:apiKey endpoint:endpoint]; Braze *braze = [BrazeReactBridge initBraze:configuration]; braze.inAppMessagePresenter = [[MyCustomPresenter alloc] init]; AppDelegate.braze = braze; ``` ## Prerequisites Before you can use this feature, you'll need to [integrate the Android Braze SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=android). ## Logging message data You will need to make sure certain functions are called to handle the analytics for your campaign. ### Displayed messages When a message is displayed or seen, log an impression: ```brightscript LogInAppMessageImpression(in_app_message.id, brazetask) ``` ### Clicked messages Once a user clicks on the message, log a click and then process `in_app_message.click_action`: ```brightscript LogInAppMessageClick(in_app_message.id, brazetask) ``` ### Clicked buttons If the user clicks on a button, log the button click and then process `inappmessage.buttons[selected].click_action`: ```brightscript LogInAppMessageButtonClick(inappmessage.id, inappmessage.buttons[selected].id, brazetask) ``` ### After processing a message After processing an in-app message, you should clear the field: ```brightscript m.BrazeTask.BrazeInAppMessage = invalid ``` ## Subscribing to in-app messages You may register Unity game objects to be notified of incoming in-app messages. We recommend setting game object listeners from the Braze configuration editor. In the configuration editor, listeners must be set separately for Android and iOS. If you need to configure your game object listener at runtime, use `AppboyBinding.ConfigureListener()` and specify `BrazeUnityMessageType.IN_APP_MESSAGE`. ## Parsing messages Incoming `string` messages received in your in-app message game object callback can be parsed into our pre-supplied model objects for convenience. Use `InAppMessageFactory.BuildInAppMessage()` to parse your in-app message. The resulting object will either be an instance of [`IInAppMessage.cs`](https://github.com/braze-inc/braze-unity-sdk/blob/18cb8ee89f1841c576eb954793edb6e06f9130b4/Assets/Plugins/Appboy/Models/InAppMessage/IInAppMessage.cs) or [`IInAppMessageImmersive.cs`](https://github.com/braze-inc/braze-unity-sdk/blob/18cb8ee89f1841c576eb954793edb6e06f9130b4/Assets/Plugins/Appboy/Models/InAppMessage/IInAppMessageImmersive.cs) depending on its type. ```csharp // Automatically logs a button click, if present. void InAppMessageReceivedCallback(string message) { IInAppMessage inApp = InAppMessageFactory.BuildInAppMessage(message); if (inApp is IInAppMessageImmersive) { IInAppMessageImmersive inAppImmersive = inApp as IInAppMessageImmersive; if (inAppImmersive.Buttons != null && inAppImmersive.Buttons.Count > 0) { inAppImmersive.LogButtonClicked(inAppImmersive.Buttons[0].ButtonID); } } } ``` ## Logging message data Clicks and impressions must be manually logged for in-app messages not displayed directly by Braze. Use `LogClicked()` and `LogImpression()` on [`IInAppMessage`](https://github.com/braze-inc/braze-unity-sdk/blob/18cb8ee89f1841c576eb954793edb6e06f9130b4/Assets/Plugins/Appboy/Models/InAppMessage/IInAppMessage.cs) to log clicks and impressions on your message. Use `LogButtonClicked(int buttonID)` on [`IInAppMessageImmersive`](https://github.com/braze-inc/braze-unity-sdk/blob/18cb8ee89f1841c576eb954793edb6e06f9130b4/Assets/Plugins/Appboy/Models/InAppMessage/IInAppMessageImmersive.cs) to log button clicks. Note that buttons are represented as lists of[`InAppMessageButton`](https://github.com/braze-inc/braze-unity-sdk/blob/18cb8ee89f1841c576eb954793edb6e06f9130b4/Assets/Plugins/Appboy/Models/InAppMessage/InAppMessageButton.cs) instances, each of which contains a `ButtonID`. # Braze SDK를 위한 테스트 메시지 전송 Source: /docs/ko/developer_guide/in_app_messages/sending_test_messages/index.md # Sending test messages > Before sending out a messaging campaign to your users, you may want to test it to make sure it looks right and operates in the intended manner. You can use the dashboard to create and send test messages with push notifications, in-app messages (IAM), or email. ## Sending a test message ### Step 1: Create a designated test segment After you set up a test segment, you can use it to test any of your Braze messaging channels. When set up correctly, this only needs to be done a single time. To set up a test segment, go to **Segments** and create a new segment. Select **Add Filter**, then choose a one of the test filters. ![A Braze test campaign displaying the filters available in the targeting step.](https://www.braze.com/docs/ko/ko/assets/img_archive/testmessages1.png?c440e858d187b30c92b316dfa12b9774) With test filters, you can ensure that only users with a specific email address or [external user ID](https://www.braze.com/docs/ko/ko/developer_guide/platform_integration_guides/swift/analytics/setting_user_ids/#setting-user-ids) are sent the test message. ![A dropdown menu displaying several filters listed under a heading that reads Testing](https://www.braze.com/docs/ko/ko/assets/img_archive/testmessages2.png?8c289defede0c6ba588c9b8ba8d0c9f5) Both email address and external user ID filters offer the following options: | Operator | Description | |------------------|--------------------------------------------------------------------------------------------------------------------------------| | `equals` | This will look for an exact match of the email or user ID that you provide. Use this if you only want to send the test campaigns to devices associated with a single email or user ID. | | `does not equal` | Use this if you want to exclude a particular email or user ID from test campaigns. | | `matches` | This will find users that have email addresses or user IDs that match part of the search term you provide. You could use this to find only the users that have an `@yourcompany.com` address, allowing you to send messages to everyone on your team. | {: .reset-td-br-1 .reset-td-br-2 aria-label="Step 1: Create a designated test segment a class="margin-fix" name="test-segment"/a" } You can select multiple specific emails using the "`matches`" option and separating the email addresses with a | character. For example: "`matches`" "`email1@braze.com` | `email2@braze.com`". You can also combine multiple operators together. For example, the test segment could include an email address filter that "`matches`" "`@braze.com`" and another filter that "`does not equal`" "`sales@braze.com`". After adding the testing filters to your test segment, you can verify it's working by selecting **Preview** or by selecting **Settings** > **CSV Export All User Data** to export that segment's user data to a CSV file. ![A section of a Braze campaign titled Segment Details](https://www.braze.com/docs/ko/ko/assets/img_archive/testmessages3.png?78e031a18aad06f510fd2ac4946bf7c5) **Note:** Exporting the segment's User Data to a CSV file is the most accurate verification method, as the preview will only show a sample of your users and may not include all users. ### Step 2: Send the message You can send a message using the Braze dashboard or the command line. To send test push notifications or in-app messages, you need to target your previously created test segment. Begin by creating your campaign and following the usual steps. When you reach the **Target Audiences** step, select your test segment from the dropdown menu. ![A Braze test campaign displaying the segments available in the targeting step.](https://www.braze.com/docs/ko/ko/assets/img_archive/test_segment.png?25ae32cc99d02e6dcdd9b66a0adf75e4) Confirm your campaign and launch it to test your push notification and in-app messages. **Note:** Be sure to select **Allow users to become re-eligible to receive campaign** under the **Schedule** portion of the campaign composer if you intend to use a single campaign to send a test message to yourself more than once. If you're only testing email messages, you do not necessarily have to set up a test segment. In the first step of the campaign composer where you compose your campaign's email message, click **Send Test** and enter the email address to which you wish to send a test email. ![A Braze campaign with the Test Send tab selected](https://www.braze.com/docs/ko/ko/assets/img_archive/testmessages45.png?883cb58cd3adf2e8315817db896b7914) **Tip:** You can also enable or disable [TEST (or SEED)](https://www.braze.com/docs/ko/ko/user_guide/administrative/app_settings/email_settings/#append-email-subject-lines) being appended on your test messages. Alternatively, you can send a single notification using cURL and the [Braze Messaging API](https://www.braze.com/docs/ko/ko/api/endpoints/messaging/). Note that these examples make a request using the `US-01` instance. To find out yours, refer to [API endpoints](https://www.braze.com/docs/ko/ko/api/basics/#endpoints). ```bash curl -X POST -H "Content-Type: application/json" -H "Authorization: Bearer {BRAZE_API_KEY}" -d '{ "external_user_ids":["EXTERNAL_USER_ID"], "messages": { "android_push": { "title":"Test push title", "alert":"Test push", "extra":{ "CUSTOM_KEY":"CUSTOM_VALUE" } } } }' https://rest.iad-01.braze.com/messages/send ``` ```bash curl -X POST -H "Content-Type: application/json" -H "Authorization: Bearer {BRAZE_API_KEY}" -d '{ "external_user_ids":["EXTERNAL_USER_ID"], "messages": { "apple_push": { "alert": "Test push", "extra": { "CUSTOM_KEY" :"CUSTOM_VALUE" } } } }' https://rest.iad-01.braze.com/messages/send ``` ```bash curl -X POST -H "Content-Type: application/json" -H "Authorization: Bearer {BRAZE_API_KEY}" -d '{ "external_user_ids":["EXTERNAL_USER_ID"], "messages": { "kindle_push": { "title":"Test push title", "alert":"Test push", "extra":{ "CUSTOM_KEY":"CUSTOM_VALUE" } } } }' https://rest.iad-01.braze.com/messages/send ``` Replace the following: | Placeholder | Description | |---------------------|-----------------------------------------------------------| | `BRAZE_API_KEY` | Your Braze API key used for authentication. In Braze, go to **Settings** > **API Keys** to locate your key. | | `EXTERNAL_USER_ID` | The external user ID used to send your message to a specific user. In Braze, go to **Audience** > **Search users**, then search for a user. | | `CUSTOM_KEY` | (Optional) A custom key for additional data. | | `CUSTOM_VALUE` | (Optional) A custom value assigned to your custom key. | {: .reset-td-br-1 .reset-td-br-2 aria-label="Step 2: Send the message" } ## Test limitations There are a few situations where test messages don't have complete feature parity with launching a campaign or Canvas to a real set of users. In these instances, to validate this behavior, you should launch the campaign or Canvas to a limited set of test users. - Viewing the Braze [preference center](https://www.braze.com/docs/ko/ko/user_guide/message_building_by_channel/email/managing_user_subscriptions/#subscription-groups) from **Test Messages** will cause the submit button to be grayed out. - The list-unsubscribe header is not included in emails sent by the test message functionality. - For in-app messages and Content Cards, the target user must have a push token for the target device. # 튜토리얼 Source: /docs/ko/developer_guide/in_app_messages/tutorials/index.md
# Tutorial: 조건부 인앱 메시지 표시 Source: /docs/ko/developer_guide/in_app_messages/tutorials/conditionally_displaying_messages/index.md # Tutorial: 조건부 인앱 메시지 표시 > 이 튜토리얼의 샘플 코드를 따라 Braze SDK를 사용하여 인앱 메시지를 조건부로 표시하세요. ## Prerequisites Before you can use this feature, you'll need to [integrate the Web Braze SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=web). 그러나 추가 설정은 필요하지 않습니다. ## 웹용 인앱 메시지를 조건부로 표시하기 **Important:** We're piloting this new tutorial format. [Tell us what you think](https://docs.google.com/forms/d/e/1FAIpQLSe_5uhWM7eXXk9F_gviO_pvA4rkYO3WA9B6tNJZ3TY91md5bw/viewform?usp=pp_url&entry.569173304=General+Feedback) — your feedback helps us improve future guides. ```js file=index.js import * as braze from "@braze/web-sdk"; // Remove any calls to `braze.automaticallyShowInAppMessages()` braze.initialize("YOUR-API-KEY", { baseUrl: "YOUR-ENDPOINT", enableLogging: true, }); braze.subscribeToInAppMessage(function (message) { if ( location.pathname === "/checkout" || document.getElementById("#checkout") ) { // do not show the message } else { braze.showInAppMessage(message); } }); ``` !!단계 lines-index.js=2 #### 1\. 다음에 대한 호출을 제거합니다. `automaticallyShowInAppMessages()` 에 대한 호출을 모두 제거하세요. [`automaticallyShowInAppMessages()`](https://js.appboycdn.com/web-sdk/latest/doc/modules/braze.html#automaticallyshowinappmessages)호출은 나중에 구현하는 모든 사용자 정의 로직을 재정의하므로 제거하세요. !!step lines-index.js=6 #### 2\. 디버깅 활성화(선택 사항) 개발 중 문제 해결을 쉽게 하기 위해 디버깅을 활성화하는 것을 고려하세요. !!step lines-index.js=9-18 #### 3\. 인앱 메시지 업데이트 구독 에 콜백을 등록하여 [`subscribeToInAppMessage(callback)`](https://js.appboycdn.com/web-sdk/latest/doc/modules/braze.html#subscribetoinappmessage) 로 콜백을 등록하면 인앱 메시지가 트리거될 때마다 `message` 을 수신할 수 있습니다. !!step lines-index.js=10-13 #### 4\. 조건부 논리 만들기 사용자 지정 로직을 만들어 메시지가 표시되는 시기를 제어할 수 있습니다. 이 예제에서 로직은 URL에 `"checkout"` 이 포함되어 있는지 또는 페이지에 `#checkout` 요소가 있는지 확인합니다. !!step lines-index.js=16 #### 5\. 다음과 같은 메시지를 표시합니다. `showInAppMessage` 메시지를 표시하려면 [`showInAppMessage(message)`](https://js.appboycdn.com/web-sdk/latest/doc/modules/braze.html#showinappmessage). 생략하면 메시지를 건너뜁니다. ## Prerequisites Before you can use this feature, you'll need to [integrate the Android Braze SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=android). 또한 [안드로이드용 인앱 메시지 활성화](https://www.braze.com/docs/ko/ko/developer_guide/in_app_messages/?sdktab=android#android_enabling-in-app-messages)가 필요합니다. ## Android용 인앱 메시지 조건부 표시하기 **Important:** We're piloting this new tutorial format. [Tell us what you think](https://docs.google.com/forms/d/e/1FAIpQLSe_5uhWM7eXXk9F_gviO_pvA4rkYO3WA9B6tNJZ3TY91md5bw/viewform?usp=pp_url&entry.569173304=General+Feedback) — your feedback helps us improve future guides. ```kotlin file=MainApplication.kt import android.app.Application import com.braze.Braze import com.braze.support.BrazeLogger import com.braze.configuration.BrazeConfig import com.braze.ui.inappmessage.BrazeInAppMessageManager import com.braze.BrazeActivityLifecycleCallbackListener import com.braze.ui.inappmessage.listeners.IInAppMessageManagerListener import com.braze.models.inappmessage.IInAppMessage import com.braze.ui.inappmessage.InAppMessageOperation import android.util.Log class MyApplication : Application() { override fun onCreate() { super.onCreate() // Enable verbose Braze SDK logs BrazeLogger.logLevel = Log.VERBOSE // Initialize Braze val brazeConfig = BrazeConfig.Builder() .setApiKey("YOUR-API-KEY") .setCustomEndpoint("YOUR-ENDPOINT") .build() Braze.configure(this, brazeConfig) registerActivityLifecycleCallbacks( BrazeActivityLifecycleCallbackListener() ) // Set up in-app message listener BrazeInAppMessageManager.getInstance().setCustomInAppMessageManagerListener(object : IInAppMessageManagerListener { override fun beforeInAppMessageDisplayed(inAppMessage: IInAppMessage): InAppMessageOperation { // Check if we should show the message val shouldShow = inAppMessage.extras["should_display_message"] == "true" return if (shouldShow) { // Show the message using Braze's UI InAppMessageOperation.DISPLAY_NOW } else { // Discard the message (or we could also create our own UI using KVP values) InAppMessageOperation.DISCARD } } }) } } ``` !!step lines-MainApplication.kt=17 #### 1\. 디버깅 활성화(선택 사항) 개발 중 문제 해결을 쉽게 하기 위해 디버깅을 활성화하는 것을 고려하세요. !!step lines-MainApplication.kt=26-28 #### 2\. 활동 수명 주기 콜백 등록 Braze의 기본 리스너를 등록하여 인앱 메시지 생명 주기를 처리합니다. !!step lines-MainApplication.kt=30-44 #### 3\. 인앱 메시지 수신기 설정 `BrazeInAppMessageManager` 을 사용하여 메시지가 표시되기 전에 가로채는 사용자 지정 수신기를 설정하세요. !!step lines-MainApplication.kt=34-42 #### 4\. 조건부 논리 만들기 사용자 지정 로직을 사용하여 메시지 표시 타이밍을 제어합니다. 이 예제에서 사용자 지정 로직은 `should_display_message` 추가가 `"true"` 으로 설정되어 있는지 확인합니다. !!step lines-MainApplication.kt=38,41 #### 5\. 메시지 반환 또는 삭제 메시지를 표시하려면 `DISPLAY_NOW`, 숨기려면 `DISCARD` 과 함께 `InAppMessageOperation` 을 반환합니다. ## Prerequisites Before you can use this feature, you'll need to [integrate the Swift Braze SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=swift). 또한 [스위프트용 인앱 메시지 활성화](https://www.braze.com/docs/ko/ko/developer_guide/in_app_messages/?sdktab=swift#swift_enabling-in-app-messages)가 필요합니다. ## Swift용 인앱 메시지를 조건부로 표시하기 **Important:** We're piloting this new tutorial format. [Tell us what you think](https://docs.google.com/forms/d/e/1FAIpQLSe_5uhWM7eXXk9F_gviO_pvA4rkYO3WA9B6tNJZ3TY91md5bw/viewform?usp=pp_url&entry.569173304=General+Feedback) — your feedback helps us improve future guides. ```swift file=AppDelegate.swift import SwiftUI import BrazeKit import BrazeUI class AppDelegate: NSObject, UIApplicationDelegate, BrazeInAppMessageUIDelegate { static var braze: Braze? func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool { // 1. Braze configuration with your SDK API key and endpoint let configuration = Braze.Configuration(apiKey: "YOUR_API_ENDPOINT", endpoint: "YOUR_API_KEY") configuration.logger.level = .debug // 2. Initialize Braze SDK instance let brazeInstance = Braze(configuration: configuration) AppDelegate.braze = brazeInstance // 3. Set up Braze In-App Message UI and delegate let inAppMessageUI = BrazeInAppMessageUI() inAppMessageUI.delegate = self brazeInstance.inAppMessagePresenter = inAppMessageUI return true } func inAppMessage(_ ui: BrazeInAppMessageUI, displayChoiceForMessage message: Braze.InAppMessage) -> BrazeInAppMessageUI.DisplayChoice { if let showFlag = message.extras["should_display_message"] as? String, showFlag == "true" { return .now } else { return .discard } } } ``` ```swift file=SampleApp.swift import SwiftUI @main struct SampleApp: App { @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate var body: some Scene { WindowGroup { YourView() } } } ``` !!단계 lines-AppDelegate.swift=5 #### 1\. 구현 `BrazeInAppMessageUIDelegate` 앱디렉티브 클래스에서 앱디렉티브의 [`BrazeInAppMessageUIDelegate`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazeui/brazeinappmessageui/delegate) 를 구현하여 나중에 `inAppMessage` 메서드를 재정의할 수 있도록 합니다. !!step lines-AppDelegate.swift=12 #### 2\. 디버깅 활성화(선택 사항) 개발 중 문제 해결을 쉽게 하기 위해 디버깅을 활성화하는 것을 고려하세요. !!step lines-AppDelegate.swift=19-21 #### 3\. Braze UI와 델리게이트를 설정하세요 `BrazeInAppMessageUI()`은 기본적으로 인앱 메시지를 렌더링합니다. `self` 을 델리게이트로 지정하면 메시지가 표시되기 전에 메시지를 가로채서 처리할 수 있습니다. !!step lines-AppDelegate.swift=26-33 #### 4\. 조건부 논리로 `DisplayChoice` 재정의 오버라이드 [`inAppMessage(_:displayChoiceForMessage:)`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazeui/brazeinappmessageuidelegate/inappmessage(_:displaychoiceformessage:)-9w1nb) 를 사용하여 메시지 표시 여부를 결정할 수 있습니다. 메시지를 표시하려면 `.now`, 표시하지 않으려면 `.discard` 을 반환합니다. # Tutorial: 키-값 쌍을 사용하여 스타일링 사용자 지정하기 Source: /docs/ko/developer_guide/in_app_messages/tutorials/customizing_message_styling/index.md # Tutorial: 키-값 쌍을 사용하여 메시지 스타일 지정하기 > 이 튜토리얼의 샘플 코드를 따라 Braze SDK에서 키-값 쌍을 사용하여 인앱 메시지 스타일링을 사용자 지정하세요. ## Prerequisites Before you can use this feature, you'll need to [integrate the Web Braze SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=web). 그러나 추가 설정은 필요하지 않습니다. ## 웹용 키-값 쌍을 사용하여 메시지 스타일 지정 사용자 지정하기 **Important:** We're piloting this new tutorial format. [Tell us what you think](https://docs.google.com/forms/d/e/1FAIpQLSe_5uhWM7eXXk9F_gviO_pvA4rkYO3WA9B6tNJZ3TY91md5bw/viewform?usp=pp_url&entry.569173304=General+Feedback) — your feedback helps us improve future guides. ```js file=index.js import * as braze from "@braze/web-sdk"; // Remove any calls to `braze.automaticallyShowInAppMessages()` braze.initialize("YOUR-API-KEY", { baseUrl: "YOUR-ENDPOINT", enableLogging: true, }); braze.subscribeToInAppMessage(function (message) { const extras = message.extras; const customTemplateType = extras["custom-template"] || ""; const customColor = extras["custom-color"] || ""; const customMessageId = extras["message-id"] || ""; if (customTemplateType) { // add your own custom code to render this message } else { // otherwise, use Braze built-in UI braze.showInAppMessage(message); } }); ``` !!단계 lines-index.js=2 #### 1\. `automaticallyShowInAppMessages()`에 대한 호출을 제거하십시오. 나중에 구현하는 모든 사용자 정의 논리를 재정의하므로 [`automaticallyShowInAppMessages()`](https://js.appboycdn.com/web-sdk/latest/doc/modules/braze.html#automaticallyshowinappmessages)에 대한 호출을 제거하십시오. !!단계 lines-index.js=6 #### 2\. 디버깅 활성화(선택 사항) 개발 중 문제 해결을 쉽게 하기 위해 디버깅을 활성화하는 것을 고려하세요. !!단계 라인-index.js=9-21 #### 3\. 인앱 메시지 콜백 핸들러에 가입하십시오. 인앱 메시지가 트리거될 때마다 메시지를 받기 위해 [`subscribeToInAppMessage(callback)`](https://js.appboycdn.com/web-sdk/latest/doc/modules/braze.html#subscribetoinappmessage)에 콜백을 등록하십시오. !!단계 lines-index.js=10-13 #### 4\. `message.extras` 속성에 액세스 `message.extras` 을 사용하여 사용자 지정 유형, 스타일 지정 속성 또는 대시보드에 정의된 기타 값에 액세스합니다. 모든 값은 문자열로 반환됩니다. !!단계 lines-index.js=19 #### 5\. 조건부 호출 `showInAppMessage` 메시지를 표시하려면 [`showInAppMessage(message)`](https://js.appboycdn.com/web-sdk/latest/doc/modules/braze.html#showinappmessage). 그렇지 않으면 필요에 따라 사용자 지정 속성을 사용합니다. ## Prerequisites Before you can use this feature, you'll need to [integrate the Android Braze SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=android). Android용 인앱 메시지를 [활성화해야 합니다.](https://www.braze.com/docs/ko/ko/developer_guide/in_app_messages/?sdktab=android#android_enabling-in-app-messages) ## Android용 키-값 쌍을 사용하여 메시지 스타일 지정하기 **Important:** We're piloting this new tutorial format. [Tell us what you think](https://docs.google.com/forms/d/e/1FAIpQLSe_5uhWM7eXXk9F_gviO_pvA4rkYO3WA9B6tNJZ3TY91md5bw/viewform?usp=pp_url&entry.569173304=General+Feedback) — your feedback helps us improve future guides. ```kotlin file=MainApplication.kt package com.example.brazedevlab import android.app.Application import com.braze.Braze import com.braze.support.BrazeLogger import com.braze.configuration.BrazeConfig import com.braze.ui.inappmessage.BrazeInAppMessageManager import com.braze.BrazeActivityLifecycleCallbackListener import com.braze.ui.inappmessage.listeners.IInAppMessageManagerListener import com.braze.models.inappmessage.IInAppMessage import com.braze.ui.inappmessage.InAppMessageOperation import android.util.Log class MyApplication : Application() { override fun onCreate() { super.onCreate() // Enable verbose Braze SDK logs BrazeLogger.logLevel = Log.VERBOSE // Initialize Braze val brazeConfig = BrazeConfig.Builder() .setApiKey("YOUR-API-KEY") .setCustomEndpoint("YOUR-ENDPOINT") .build() Braze.configure(this, brazeConfig) registerActivityLifecycleCallbacks( BrazeActivityLifecycleCallbackListener() ) // Set up custom in-app message view factory BrazeInAppMessageManager.getInstance() .setCustomInAppMessageViewFactory(CustomInAppMessageViewFactory()) } } ``` ```kotlin file=CustomInAppMessageViewFactory.kt import android.app.Activity import android.graphics.Color import android.view.View import com.braze.models.inappmessage.IInAppMessage import com.braze.ui.inappmessage.BrazeInAppMessageManager import com.braze.ui.inappmessage.IInAppMessageViewFactory class CustomInAppMessageViewFactory : IInAppMessageViewFactory { override fun createInAppMessageView( activity: Activity, inAppMessage: IInAppMessage ): View { // 1) Obtain Braze’s default view factory for this message type val defaultFactory = BrazeInAppMessageManager.getInstance() .getDefaultInAppMessageViewFactory(inAppMessage) ?: throw IllegalStateException( "Braze default IAM view factory is missing" ) // 2) Inflate the default view val iamView = defaultFactory .createInAppMessageView(activity, inAppMessage) ?: throw IllegalStateException( "Braze default IAM view is null" ) // 3) Get your KVP extras val extras = inAppMessage.extras ?: emptyMap() val customization = extras["customization"] val overrideColor = extras["custom-color"] // 4) Style your root view if (customization == "slideup-attributes" && overrideColor != null) { try { iamView.setBackgroundColor(Color.parseColor(overrideColor)) } catch (_: IllegalArgumentException) { // ignore bad styling } } return iamView } } ``` !!단계 lines-MainApplication.kt=19 #### 1\. 디버깅 활성화(선택 사항) 개발 중 문제 해결을 쉽게 하기 위해 디버깅을 활성화하는 것을 고려하세요. !!단계 라인-MainApplication.kt=28-30 #### 2\. 활동 수명 주기 콜백 등록 Braze의 기본 리스너를 등록하여 인앱 메시지 생명 주기를 처리합니다. !!단계 lines-CustomInAppMessageViewFactory.kt=8 #### 3\. 사용자 지정 뷰 팩토리 클래스 만들기 클래스가 다음을 준수하는지 확인합니다. [`IInAppMessageViewFactory`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.ui.inappmessage/-i-in-app-message-view-factory/index.html) 를 준수하는지 확인하여 사용자 지정 메시지 보기를 구성하고 반환할 수 있도록 합니다. !!단계 라인-CustomInAppMessageViewFactory.kt=15-20 #### 4\. 브레이즈의 기본 공장에 위임하기 조건부 변경 사항을 적용하기 전에 기본 팩토리로 위임하여 Braze의 기본 제공 스타일링을 유지하세요. !!단계 lines-CustomInAppMessageViewFactory.kt=30-32,35-41 #### 5\. 다음에서 키-값 쌍에 액세스합니다. `inAppMessage.extras` `inAppMessage.extras` 을 사용하여 사용자 지정 유형, 스타일 지정 속성 또는 대시보드에 정의된 기타 값에 액세스합니다. 뷰를 반환하기 전에 스타일 재정의를 적용합니다. !!단계 라인-MainApplication.kt=33-34 #### 6\. 사용자 지정 구현 `IInAppMessageViewFactory` 구현 [`IInAppMessageViewFactory`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.ui.inappmessage/-i-in-app-message-view-factory/index.html) 를 구현하여 인앱 메시지 보기를 구성하고 렌더링하세요. ## Prerequisites Before you can use this feature, you'll need to [integrate the Swift Braze SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=swift). Swift용 인앱 메시지를 [활성화해야 합니다.](https://www.braze.com/docs/ko/ko/developer_guide/in_app_messages/?sdktab=swift#swift_enabling-in-app-messages) ## Swift에서 키-값 쌍을 사용하여 메시지 스타일 지정하기 **Important:** We're piloting this new tutorial format. [Tell us what you think](https://docs.google.com/forms/d/e/1FAIpQLSe_5uhWM7eXXk9F_gviO_pvA4rkYO3WA9B6tNJZ3TY91md5bw/viewform?usp=pp_url&entry.569173304=General+Feedback) — your feedback helps us improve future guides. ```swift file=AppDelegate.swift import UIKit import BrazeKit import BrazeUI class AppDelegate: UIResponder, UIApplicationDelegate, BrazeInAppMessageUIDelegate { var window: UIWindow? static var braze: Braze? func application( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? ) -> Bool { let configuration = Braze.Configuration( apiKey: "YOUR-API-KEY", endpoint: "YOUR-ENDPOINT" ) configuration.logger.level = .debug let braze = Braze(configuration: configuration) AppDelegate.braze = braze // Set up Braze In-App Message UI and delegate let inAppMessageUI = BrazeInAppMessageUI() inAppMessageUI.delegate = self brazeInstance.inAppMessagePresenter = inAppMessageUI return true } func inAppMessage( _ ui: BrazeInAppMessageUI, prepareWith context: inout BrazeInAppMessageUI.PresentationContext ) { let customization = context.message.extras["customization"] as? String if customization == "slideup-attributes" { // Create a new attributes object and make customizations. var attributes = context.attributes?.slideup attributes?.font = UIFont(name: "Chalkduster", size: 17)! attributes?.imageSize = CGSize(width: 65, height: 65) attributes?.cornerRadius = 20 attributes?.imageCornerRadius = 10 if #available(iOS 13.0, *) { attributes?.cornerCurve = .continuous attributes?.imageCornerCurve = .continuous } context.attributes?.slideup = attributes } } } ``` ```swift file=SampleApp.swift import SwiftUI @main struct SampleApp: App { @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate var body: some Scene { WindowGroup { YourView() } } } ``` !!단계 lines-AppDelegate.swift=5 #### 1\. 구현 `BrazeInAppMessageUIDelegate` `AppDelegate` 클래스에서 [`BrazeInAppMessageUIDelegate`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazeui/brazeinappmessageui/delegate) 를 구현하여 나중에 `inAppMessage` 메서드를 재정의할 수 있도록 합니다. !!단계 lines-AppDelegate.swift=17 #### 2\. 디버깅 활성화(선택 사항) 개발 중 문제 해결을 쉽게 하기 위해 디버깅을 활성화하는 것을 고려하세요. !!단계 lines-AppDelegate.swift=30-50 #### 3\. 메시지가 표시되기 전에 메시지 준비하기 메시지를 준비하는 동안 Braze는 `inAppMessage(_:prepareWith:)` 으로 전화합니다. 이를 사용하여 스타일을 사용자 지정하거나 키-값 쌍을 기반으로 로직을 적용할 수 있습니다. !!단계 lines-AppDelegate.swift=34 #### 4\. 다음에서 키-값 쌍에 액세스합니다. `message.extras` `message.extras` 을 사용하여 사용자 지정 유형, 스타일 지정 속성 또는 대시보드에 정의된 기타 값에 액세스합니다. !!단계 라인-AppDelegate.swift=38-46 #### 5\. 메시지의 스타일 지정 속성 업데이트하기 를 사용하여 [`inAppMessage(_:prepareWith:)`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazeui/brazeinappmessageuidelegate/inappmessage(_:preparewith:)-11fog) 을 사용하여 `PresentationContext` 에 액세스하여 스타일 속성을 직접 수정할 수 있습니다. 각 인앱 메시지 유형은 서로 다른 속성을 노출합니다. # Tutorial: 트리거된 메시지를 연기하고 복원하기 Source: /docs/ko/developer_guide/in_app_messages/tutorials/deferring_triggered_messages/index.md # Tutorial: 트리거된 메시지를 연기하고 복원하기 > 이 튜토리얼의 샘플 코드를 따라 Braze SDK를 사용하여 인앱 메시지를 연기하고 복원하세요. ## Prerequisites Before you can use this feature, you'll need to [integrate the Web Braze SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=web). 그러나 추가 설정은 필요하지 않습니다. ## 웹을 위한 트리거된 메시지 지연 및 복원 **Important:** We're piloting this new tutorial format. [Tell us what you think](https://docs.google.com/forms/d/e/1FAIpQLSe_5uhWM7eXXk9F_gviO_pvA4rkYO3WA9B6tNJZ3TY91md5bw/viewform?usp=pp_url&entry.569173304=General+Feedback) — your feedback helps us improve future guides. ```js file=index.js import * as braze from "@braze/web-sdk"; // Remove any calls to `braze.automaticallyShowInAppMessages()` braze.initialize("YOUR-API-KEY", { baseUrl: "YOUR-ENDPOINT", enableLogging: true, }); braze.subscribeToInAppMessage(function (message) { const shouldDefer = true; // customize for your own logic if (shouldDefer) { braze.deferInAppMessage(message); } else { braze.showInAppMessage(message); } }); // elsewhere in your app document.getElementById("button").onclick = function () { const deferredMessage = braze.getDeferredInAppMessage(); if (deferredMessage) { braze.showInAppMessage(deferredMessage); } }; ``` !!단계 lines-index.js=2 #### 1\. `automaticallyShowInAppMessages()`에 대한 호출을 제거하십시오. 나중에 구현하는 모든 사용자 정의 논리를 재정의하므로 [`automaticallyShowInAppMessages()`](https://js.appboycdn.com/web-sdk/latest/doc/modules/braze.html#automaticallyshowinappmessages)에 대한 호출을 제거하십시오. !!단계 lines-index.js=6 #### 2\. 디버깅 활성화(선택 사항) 개발 중 문제 해결을 쉽게 하기 위해 디버깅을 활성화하는 것을 고려하세요. !!단계 lines-index.js=9-16 #### 3\. 인앱 메시지 콜백 핸들러에 가입하십시오. 인앱 메시지가 트리거될 때마다 메시지를 받기 위해 [`subscribeToInAppMessage(callback)`](https://js.appboycdn.com/web-sdk/latest/doc/modules/braze.html#subscribetoinappmessage)에 콜백을 등록하십시오. !!단계 lines-index.js=11-12 #### 4\. `message` 인스턴스를 지연하십시오. 메시지를 지연시키려면 [`deferInAppMessage(message)`](https://js.appboycdn.com/web-sdk/latest/doc/modules/braze.html#deferinappmessage)을(를) 호출하십시오. Braze는 이 메시지를 직렬화하고 저장하여 향후 페이지 로드 시 표시할 수 있습니다. !!단계 lines-index.js=18-24 #### 5\. 이전에 보류된 메시지를 검색하십시오 이전에 보류된 메시지를 검색하려면 [`getDeferredInAppMessage()`](https://js.appboycdn.com/web-sdk/latest/doc/modules/braze.html#getdeferredinappmessage)을 호출하십시오. !!단계 lines-index.js=21-23 #### 6\. 보류된 메시지를 표시하십시오 보류된 메시지를 검색한 후, [`showInAppMessage(message)`](https://js.appboycdn.com/web-sdk/latest/doc/modules/braze.html#showinappmessage)에 전달하여 표시하십시오. !!단계 lines-index.js=13-15 #### 7\. 메시지를 즉시 표시하십시오 메시지를 보류하는 대신 표시하려면 [`showInAppMessage(message)`](https://js.appboycdn.com/web-sdk/latest/doc/modules/braze.html#showinappmessage)을 `subscribeToInAppMessage` 콜백에서 직접 호출하십시오. ## Prerequisites Before you can use this feature, you'll need to [integrate the Android Braze SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=android). Android에 대한 [앱 내 메시지 활성화](https://www.braze.com/docs/ko/ko/developer_guide/in_app_messages/?sdktab=android#android_enabling-in-app-messages)도 필요합니다. ## Android용 트리거된 메시지 연기 및 복원하기 **Important:** We're piloting this new tutorial format. [Tell us what you think](https://docs.google.com/forms/d/e/1FAIpQLSe_5uhWM7eXXk9F_gviO_pvA4rkYO3WA9B6tNJZ3TY91md5bw/viewform?usp=pp_url&entry.569173304=General+Feedback) — your feedback helps us improve future guides. ```kotlin file=MainApplication.kt import android.app.Application import com.braze.Braze import com.braze.support.BrazeLogger import com.braze.configuration.BrazeConfig import com.braze.ui.inappmessage.BrazeInAppMessageManager import com.braze.BrazeActivityLifecycleCallbackListener import com.braze.ui.inappmessage.listeners.IInAppMessageManagerListener import com.braze.models.inappmessage.IInAppMessage import com.braze.ui.inappmessage.InAppMessageOperation import android.util.Log class MyApplication : Application() { companion object { private var instance: MyApplication? = null fun getInstance(): MyApplication = instance!! } private var showMessage = false override fun onCreate() { super.onCreate() instance = this // Enable verbose Braze SDK logs BrazeLogger.logLevel = Log.VERBOSE // Initialize Braze val brazeConfig = BrazeConfig.Builder() .setApiKey("YOUR-API-KEY") .setCustomEndpoint("YOUR-ENDPOINT") .build() Braze.configure(this, brazeConfig) registerActivityLifecycleCallbacks( BrazeActivityLifecycleCallbackListener() ) // Set up in-app message listener BrazeInAppMessageManager.getInstance().setCustomInAppMessageManagerListener(object : IInAppMessageManagerListener { override fun beforeInAppMessageDisplayed(inAppMessage: IInAppMessage): InAppMessageOperation { return if (showMessage) { // Show the message using Braze's UI InAppMessageOperation.DISPLAY_NOW } else { // Re-enqueue the message for later InAppMessageOperation.DISPLAY_LATER } } }) } fun showDeferredMessage(show: Boolean) { showMessage = show BrazeInAppMessageManager.getInstance().requestDisplayInAppMessage() } } ``` ```kotlin file=MainActivity.kt import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.compose.foundation.layout.* import androidx.compose.material.Button import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { ContentView() } } } @Composable fun ContentView() { Column( modifier = Modifier.padding(16.dp), verticalArrangement = Arrangement.spacedBy(20.dp) ) { // ... your UI Button(onClick = { MyApplication.getInstance().showDeferredMessage(true) }) { Text("Show Deferred IAM") } } } ``` !!단계 줄-MainApplication.kt=13-16 #### 1\. 싱글톤 `Application` 인스턴스 생성하기 동반 객체를 사용하여 `Application` 클래스를 싱글톤으로 노출하여 코드에서 나중에 접근할 수 있도록 합니다. !!단계 줄-MainApplication.kt=25 #### 2\. 디버깅 활성화(선택 사항) 개발 중 문제 해결을 쉽게 하기 위해 디버깅을 활성화하는 것을 고려하세요. !!단계 줄-MainApplication.kt=34-36 #### 3\. 활동 수명 주기 콜백 등록 Braze의 기본 리스너를 등록하여 인앱 메시지 생명 주기를 처리합니다. !!단계 줄-MainApplication.kt=39-49 #### 4\. 인앱 메시지 리스너 설정하기 `BrazeInAppMessageManager`을 사용하여 메시지가 표시되기 전에 가변 리스너를 설정합니다. !!단계 줄-MainApplication.kt=43,46 #### 5\. 조건 로직 생성하기 타이밍을 제어하기 위해 `showMessage` 플래그를 사용하세요—지금 메시지를 표시하려면 `DISPLAY_NOW`를 반환하거나 연기하려면 `DISPLAY_LATER`을 반환하세요. !!단계 lines-MainApplication.kt=52-55 #### 6\. 지연된 메시지를 표시하는 메서드를 만드세요 다음 인앱 메시지를 트리거하기 위해 `showDeferredMessage`을 사용하세요. `showMessage`가 `true`일 때, 리스너는 `DISPLAY_NOW`를 반환합니다. !!단계 lines-MainActivity.kt=29 #### 7\. UI에서 메서드를 트리거하세요 이전의 지연된 메시지를 표시하려면, 버튼이나 탭과 같은 UI에서 `showDeferredMessage(true)`을 호출하세요. ## Prerequisites Before you can use this feature, you'll need to [integrate the Swift Braze SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=swift). Swift에 대한 [앱 내 메시지 활성화](https://www.braze.com/docs/ko/ko/developer_guide/in_app_messages/?sdktab=swift#swift_enabling-in-app-messages)도 필요합니다. ## Swift용 트리거된 메시지 지연 및 복원 **Important:** We're piloting this new tutorial format. [Tell us what you think](https://docs.google.com/forms/d/e/1FAIpQLSe_5uhWM7eXXk9F_gviO_pvA4rkYO3WA9B6tNJZ3TY91md5bw/viewform?usp=pp_url&entry.569173304=General+Feedback) — your feedback helps us improve future guides. ```swift file=AppDelegate.swift import SwiftUI import BrazeKit import BrazeUI class AppDelegate: UIResponder, UIApplicationDelegate, BrazeInAppMessageUIDelegate { static private(set) var shared: AppDelegate! private var braze: Braze! public var showMessage: Bool = false func application( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? ) -> Bool { AppDelegate.shared = self // 1. Braze configuration with your SDK API key and endpoint let configuration = Braze.Configuration(apiKey: "a1fc095b-ae3d-40f4-bb33-3fb5176562c0", endpoint: "sondheim.braze.com") configuration.logger.level = .debug // 2. Initialize Braze SDK instance braze = Braze(configuration: configuration) // 3. Set up Braze In-App Message UI and delegate let ui = BrazeInAppMessageUI() ui.delegate = self braze.inAppMessagePresenter = ui return true } func inAppMessage( _ ui: BrazeInAppMessageUI, displayChoiceForMessage message: Braze.InAppMessage ) -> BrazeInAppMessageUI.DisplayChoice { if !showMessage { return .reenqueue } return .now } func showDeferredMessage(showMessage: Bool) { self.showMessage = showMessage (braze.inAppMessagePresenter as? BrazeInAppMessageUI)?.presentNext() } } ``` ```swift file=SampleApp.swift import SwiftUI @main struct IAMDeferApp: App { @UIApplicationDelegateAdaptor(AppDelegate.self) var delegate var body: some Scene { WindowGroup { ContentView() } } } ``` ```swift file=ContentView.swift import SwiftUI struct ContentView: View { var body: some View { VStack(spacing: 20) { // ...your UI Button("Show Deferred IAM") { AppDelegate.shared.showDeferredMessage(showMessage: true) } } .padding() } } ``` !!단계 lines-AppDelegate.swift=5 #### 1\. `BrazeInAppMessageUIDelegate`을 구현하세요 당신의 `AppDelegate` 클래스에서, 나중에 `inAppMessage` 메서드를 재정의할 수 있도록 [`BrazeInAppMessageUIDelegate`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazeui/brazeinappmessageuidelegate)를 구현하세요. !!단계 lines-AppDelegate.swift=19 #### 2\. 디버깅 활성화(선택 사항) 개발 중 문제 해결을 쉽게 하기 위해 디버깅을 활성화하는 것을 고려하세요. !!단계 lines-AppDelegate.swift=25-27 #### 3\. Braze UI와 델리게이트를 설정하세요 `BrazeInAppMessageUI()`은 기본적으로 인앱 메시지를 렌더링합니다. `self`를 델리게이트로 할당하면, 메시지가 표시되기 전에 가로채고 처리할 수 있습니다. 인스턴스를 저장하는 것을 잊지 마세요. 나중에 지연된 메시지를 복원하는 데 필요합니다. !!단계 lines-AppDelegate.swift=32-41 #### 4\. 조건 로직으로 `DisplayChoice`을 재정의하세요 메시지가 표시되어야 할 때를 결정하기 위해 [`inAppMessage(_:displayChoiceForMessage:)`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazeui/brazeinappmessageuidelegate/inappmessage(_:displaychoiceformessage:)-9w1nb)을(를) 재정의하십시오. 즉시 표시하려면 `.now`을(를) 반환하고, 나중에 표시하려면 `.reenqueue`을(를) 반환하십시오. !!단계 lines-AppDelegate.swift=43-46 #### 5\. 지연된 메시지를 표시하는 메서드를 만드십시오. 스택에서 다음 지연된 메시지를 표시하기 위해 `showDeferredMessage(true)`을(를) 호출하는 메서드를 만드십시오. 호출되면 `showMessage`는 `true`으로 설정되어 대리자가 `.now`를 반환합니다. !!단계 lines-ContentView.swift=1-14 #### 5\. UI에서 메서드를 트리거하세요 이전의 지연된 메시지를 표시하려면, 버튼이나 탭과 같은 UI에서 `showDeferredMessage(true)`을 호출하세요. # Braze SDK 인앱 메시지 문제 해결 Source: /docs/ko/developer_guide/in_app_messages/troubleshooting/index.md # 인앱 메시지 문제 해결 {#troubleshoot-in-app-messages} > 이 페이지를 사용하여 인앱 메시지가 기기에 전달되지 않거나 표시되지 않는 이유를 진단하세요. 대시보드 설정(우선순위, 트리거, Segment, 재자격)에 대해서는 [인앱 메시지 FAQ](https://www.braze.com/docs/ko/ko/user_guide/channels/in_app_messages/faq/)를 참조하세요. 디버깅을 시작하기 전에 자신을 [테스트 사용자](https://www.braze.com/docs/ko/ko/user_guide/administer/global/user_management/internal_groups/#adding-test-users)로 추가하고 [테스트 메시지 발송](https://www.braze.com/docs/ko/ko/developer_guide/in_app_messages/sending_test_messages/)을 검토하세요. ## 시작하기: 증상 매칭 {#start-here-match-your-symptom} | 증상 | 이동 | | --- | --- | | 한 명의 사용자에게 인앱 메시지가 표시되지 않음 | [한 명의 사용자](#in-app-message-not-shown-for-one-user) | | 하나의 플랫폼(Android, iOS 또는 Web)에서 인앱 메시지가 표시되지 않음 | [하나의 플랫폼](#in-app-message-not-shown-on-one-platform) | | **Canvas** 단계의 인앱 메시지가 표시되지 않음 | [Canvas 인앱 메시지](#canvas-in-app-messages) | | 인앱 메시지가 늦게 또는 지연 후에 표시됨 | [타이밍 및 지연 표시](#timing-and-delayed-display) | | 노출 횟수 또는 클릭 수가 잘못 보임 | [노출 횟수 및 분석](#impressions-and-analytics) | | 이벤트 사용자 로그에서 `triggers`가 누락되거나 비어 있음 | [전달 문제 해결](#delivery-troubleshooting) | | 트리거가 반환되었지만 기기에 아무것도 표시되지 않음 | [플랫폼별 표시 문제 해결](#platform-specific-display-troubleshooting) | | 인앱 메시지 자산 로드 실패(iOS, `NSURLError` -1008) | [자산 로드(Swift 탭)](https://www.braze.com/docs/ko/ko/developer_guide/in_app_messages/troubleshooting/?sdktab=swift#asset-loading) | {: .reset-td-br-1 .reset-td-br-2 aria-label="인앱 메시지 증상" } ## 표준 조사 경로 {#standard-investigation-path} 모든 인시던트에 대해 이 워크플로를 사용하세요. 1단계부터 시작합니다. 1. 테스트 기기에서 **세션 시작**이 기록되었는지 확인합니다. 인앱 메시지는 세션 시작 시 요청됩니다. 2. [이벤트 사용자 로그](https://www.braze.com/docs/ko/ko/user_guide/administer/global/workspace_settings/logs_and_alerts/event_user_log/)를 열고 해당 세션 시작에 대한 SDK 요청을 찾습니다. **Response Data**에서: - 원시 JSON에서 `respond_with`에 `"triggers": true`가 포함되어 있는지 확인합니다. - **Requested Responses** 행에 **`triggers`**가 포함되어야 합니다. - **Trigger In-App Message** 행에 해당 요청에 대해 반환된 각 인앱 메시지가 나열됩니다. - `triggers` 키가 없거나 **Trigger In-App Message** 행이 없으면 [메시지가 요청되지 않는 문제 해결](#troubleshoot-messages-not-being-requested)로 이동하세요. - `triggers`가 있지만 비어 있으면(`[]`) [메시지가 반환되지 않는 문제 해결](#troubleshoot-messages-not-being-returned)로 이동하세요. - **Trigger In-App Message** 행이 있지만 아무것도 표시되지 않으면 [플랫폼별 표시 문제 해결](#platform-specific-display-troubleshooting)로 이동하세요. - 각 트리거 페이로드에는 `type`이 포함됩니다: `inapp`(표준) 또는 `templated_iam`(표시 전 템플릿 요청 필요). [인앱 메시지 유형](https://www.braze.com/docs/ko/ko/developer_guide/in_app_messages/triggering_messages/#types-of-in-app-messages)을 참조하세요. 3. 대시보드 측 자격(Segment, 재자격, 빈도 제한, 우선순위, 대조군)에 대해서는 [전달 문제 해결](#delivery-troubleshooting) 및 [인앱 메시지 FAQ](https://www.braze.com/docs/ko/ko/user_guide/channels/in_app_messages/faq/)를 참조하세요. 4. 기기 측 표시 문제(델리게이트, 사용량 제한, 방향, 세션 타임아웃)에 대해서는 [플랫폼별 표시 문제 해결](#platform-specific-display-troubleshooting)에서 SDK 탭을 선택하세요. ## Canvas 인앱 메시지 {#canvas-in-app-messages} **증상:** 사용자가 Canvas 인앱 메시지 단계에 진입했지만 예상 시점에 메시지가 표시되지 않았습니다. 대부분의 Canvas 및 인앱 메시지 티켓은 세 가지 동작에 의해 발생합니다: 1. **다음 세션 표시:** Canvas 인앱 메시지는 단계가 처리된 후 *다음* 세션 시작 시 자격이 부여됩니다. 세션 중간에 즉시 표시되지 않습니다. Canvas FAQ의 [Canvas에서 인앱 메시지는 언제 발송되나요?](https://www.braze.com/docs/ko/ko/user_guide/messaging/canvas/faqs/#when-are-in-app-messages-in-canvas-sent)를 참조하세요. 2. **단계 진입 시 전달 유효성 검사:** 메시지 단계에서 **메시지 발송 시 오디언스 유효성 검사**가 활성화된 경우, Segment 멤버십과 빈도 제한은 표시 시점이 아닌 사용자가 **단계에 진입할 때** 평가됩니다. [전달 유효성 검사](https://www.braze.com/docs/ko/ko/user_guide/messaging/canvas/canvas_components/message_step/#delivery-validations)를 참조하세요. 3. **지연 및 세션 타임아웃:** 사용자가 SDK 세션 타임아웃보다 긴 지연 단계에 진입하면, 인앱 메시지 단계 전에 새 세션을 시작할 수 있습니다. 표시를 기대하는 세션 시작 시 메시지가 가져와지지 않을 수 있습니다. 가용 기간, 만료, Canvas 분석에서 _발송 수_ 0에 대해서는 Canvas FAQ의 [인앱 메시지 및 전달](https://www.braze.com/docs/ko/ko/user_guide/messaging/canvas/faqs/#messages-and-delivery)을 참조하세요. **Important:** Canvas의 인앱 메시지는 REST API가 아닌 SDK를 통해 전송된 이벤트에 의해서만 트리거될 수 있습니다. ## 한 명의 사용자에게 인앱 메시지가 표시되지 않음 {#in-app-message-not-shown-for-one-user} **증상:** 한 명의 사용자가 예상된 인앱 메시지를 받지 못했으며, 다른 사용자는 영향을 받지 않을 수 있습니다. 다음 항목을 확인하세요: - SDK가 새 인앱 메시지를 요청하는 **세션 시작** 시점에 사용자가 Segment에 포함되어 있었나요? - Campaign 또는 Canvas 타겟팅 규칙에 따라 사용자가 자격이 있거나 재자격이 있었나요? [Campaign 및 Canvas 재자격](https://www.braze.com/docs/ko/ko/user_guide/messaging/messaging_fundamentals/re_eligibility/)을 참조하세요. - [빈도 제한](https://www.braze.com/docs/ko/ko/user_guide/messaging/messaging_fundamentals/frequency_capping/)이 적용되었나요? - 사용자가 Campaign 대조군에 포함되었나요? Campaign이 A/B 테스트로 구성되어 있는지 확인하세요. - 더 높은 우선순위의 인앱 메시지가 대신 표시되었나요? 인앱 메시지 FAQ의 [같은 세션에서 여러 인앱 메시지가 표시될 수 있나요?](https://www.braze.com/docs/ko/ko/user_guide/channels/in_app_messages/faq/#can-multiple-in-app-messages-display-in-the-same-session)를 참조하세요. - 기기가 Campaign에서 지정한 방향이었나요? - 트리거 간 기본 30초 최소 간격에 의해 메시지가 억제되었나요? [기본 사용량 제한 재정의](https://www.braze.com/docs/ko/ko/developer_guide/in_app_messages/triggering_messages/#overriding-the-default-rate-limit)를 참조하세요. 그런 다음 [표준 조사 경로](#standard-investigation-path)를 따르세요. ## 하나의 플랫폼에서 인앱 메시지가 표시되지 않음 {#in-app-message-not-shown-on-one-platform} **증상:** Android, iOS 또는 Web에서 인앱 메시지가 표시되지 않지만 다른 플랫폼에서는 작동할 수 있습니다. | 가능한 원인 | 확인 사항 | | --- | --- | | 잘못된 **Send To** 타겟 | Campaign 또는 Canvas 단계가 적절하게 **Mobile Apps** 또는 **Web Browsers**를 타겟으로 하는지 확인하세요. Web 전용 Campaign은 Android 기기에 발송되지 않습니다. | | 커스텀 UI 또는 핸들러가 표시를 억제함 | 델리게이트(모바일) 또는 [`braze.subscribeToInAppMessage`](https://js.appboycdn.com/web-sdk/latest/doc/modules/braze.html#subscribetoinappmessage)(Web)를 검토하세요. [커스터마이징](https://www.braze.com/docs/ko/ko/developer_guide/in_app_messages/customization/) 및 아래 SDK 탭을 참조하세요. | | 이 플랫폼에서 통합이 작동한 적이 없음 | 이 플랫폼과 앱 버전에서 이전에 인앱 메시지가 표시된 적이 있는지 확인하세요. | | 기기에서 트리거가 실행되지 않음 | 트리거는 SDK를 통해 로컬에서 발생해야 합니다. REST API 호출로는 SDK에서 인앱 메시지를 트리거할 수 없습니다. [메시지 트리거](https://www.braze.com/docs/ko/ko/developer_guide/in_app_messages/triggering_messages/)를 참조하세요. | | 이벤트 사용자 로그에서 `triggers`가 비어 있음 | Segment, 재자격, 빈도 제한 또는 대조군 문제입니다. [메시지가 반환되지 않는 문제 해결](#troubleshoot-messages-not-being-returned)을 참조하세요. | {: .reset-td-br-1 .reset-td-br-2 aria-label="플랫폼 증상 원인" } ## 모든 사용자에게 인앱 메시지가 표시되지 않음 {#in-app-message-not-shown-for-all-users} **증상:** 인앱 메시지를 받은 사용자가 없거나 예상보다 적습니다. 다음 항목을 확인하세요: - 대시보드와 앱 통합에서 트리거 동작이 올바르게 구성되어 있나요? - 더 높은 우선순위의 인앱 메시지가 Campaign을 가로챘나요? [인앱 메시지 FAQ](https://www.braze.com/docs/ko/ko/user_guide/channels/in_app_messages/faq/#can-multiple-in-app-messages-display-in-the-same-session)를 참조하세요. - 최신 SDK 버전을 사용하고 있나요? 일부 인앱 메시지 유형에는 최소 SDK 요구 사항이 있습니다. - 세션이 올바르게 통합되어 있나요? 이 앱에서 세션 분석이 작동하는지 확인하세요. - 커스터마이징된 UI 라이브러리가 표시를 방해하고 있나요? [커스터마이징](https://www.braze.com/docs/ko/ko/developer_guide/in_app_messages/customization/)을 참조하세요. 그런 다음 [표준 조사 경로](#standard-investigation-path)를 따르세요. ## 타이밍 및 지연 표시 {#timing-and-delayed-display} **증상:** 인앱 메시지가 예상보다 늦게 표시되거나 새 세션이 시작될 때까지 표시되지 않았습니다. 일반적인 원인: - **Campaign 세션 시작 프리페치:** 인앱 메시지는 세션 시작 시 캐시되고 트리거가 실행될 때 표시됩니다. 다음 세션 시작 전에 발생하는 트리거는 해당 세션까지 표시되지 않습니다. [메시지 트리거](https://www.braze.com/docs/ko/ko/developer_guide/in_app_messages/triggering_messages/)를 참조하세요. - **Canvas 다음 세션 동작:** [Canvas 인앱 메시지](#canvas-in-app-messages)를 참조하세요. - **스케줄된 대시보드 지연:** Campaign 또는 단계에 지연이 구성되어 있는지 확인하세요. - **트리거 동기화 경합:** 사용자가 세션 시작 직후 이벤트를 기록하면 트리거가 아직 동기화되지 않았을 수 있습니다. 세션 시작으로 트리거하고 의도한 이벤트로 세분화하여 이벤트 이후 다음 세션에 전달되도록 하는 것을 고려하세요. - **순차적 인앱 메시지:** 투어에서 메시지를 지연하거나 복원하는 경우 [트리거된 인앱 메시지 지연](https://www.braze.com/docs/ko/ko/developer_guide/in_app_messages/tutorials/deferring_triggered_messages/)을 참조하세요. - **대용량 자산 또는 느린 CDN:** HTML 인앱 메시지의 이미지와 동영상을 최적화하세요. 모바일에서는 느린 네트워크에서 표시 전에 이미지가 다운로드될 수 있습니다. 플랫폼 참고 사항은 아래 SDK 탭을 선택하세요. **Note:** 인앱 메시지가 세션 시작으로 트리거되고 연장된 세션 타임아웃을 설정한 경우, 해당 기간 내에 앱을 닫았다가 다시 열어도 세션이 새로고침되지 않습니다. 예를 들어, 300초 타임아웃의 경우 세션 시작 인앱 메시지는 세션이 실제로 새로고침될 때까지 표시되지 않습니다. 이것이 테스트에 영향을 미치는 경우 세션 타임아웃 또는 트리거 유형을 조정하세요. ## 전달 문제 해결 {#delivery-troubleshooting} 대부분의 인앱 메시지 문제는 **전달**(기기가 트리거를 수신하지 못함) 또는 **표시**(트리거가 도착했지만 표시되지 않음)입니다. 먼저 [전달](#troubleshooting-in-app-message-delivery)을 확인한 다음 [표시](#platform-specific-display-troubleshooting)를 점검하세요. ### 전달 문제 해결 {#troubleshooting-in-app-message-delivery} SDK는 세션 시작 시 Braze 서버에 인앱 메시지를 요청합니다. SDK가 트리거를 요청하고 Braze가 이를 반환하는지 확인하세요. #### 메시지가 요청되고 반환되는지 확인 {#check-if-messages-are-requested-and-returned} 1. 자신을 [테스트 사용자](https://www.braze.com/docs/ko/ko/user_guide/administer/global/user_management/internal_groups/#adding-test-users)로 추가합니다. 2. 사용자를 타겟으로 하는 인앱 메시지 Campaign을 설정합니다. 3. 애플리케이션에서 새 세션을 시작합니다. 4. [이벤트 사용자 로그](https://www.braze.com/docs/ko/ko/user_guide/administer/global/workspace_settings/logs_and_alerts/event_user_log/)에서 세션 시작 이벤트에 대한 SDK 요청을 찾습니다. **Response Data**에서: - 원시 JSON에서 `respond_with`에 `"triggers": true`가 포함되어 있는지 확인합니다. - **Requested Responses** 행에 응답의 최상위 키가 나열됩니다. 인앱 메시지의 경우 **`triggers`**가 있어야 합니다. - **Trigger In-App Message** 행에 해당 요청에 대해 반환된 각 인앱 메시지가 나열됩니다. 그런 다음 분류합니다: - `triggers` 키 또는 **Trigger In-App Message** 행이 없으면 [메시지가 요청되지 않는 문제 해결](#troubleshoot-messages-not-being-requested)을 참조하세요. - `triggers`가 있지만 비어 있으면(`[]`) [메시지가 반환되지 않는 문제 해결](#troubleshoot-messages-not-being-returned)을 참조하세요. - **Trigger In-App Message** 행이 있지만 기기에 아무것도 표시되지 않으면 [플랫폼별 표시 문제 해결](#platform-specific-display-troubleshooting)을 참조하세요. - 각 트리거 페이로드에는 `type`이 포함됩니다: `inapp`(표준) 또는 `templated_iam`(표시 전 템플릿 요청 필요). [인앱 메시지 유형](https://www.braze.com/docs/ko/ko/developer_guide/in_app_messages/triggering_messages/#types-of-in-app-messages)을 참조하세요. 5. 응답 데이터에 올바른 인앱 메시지가 나타나는지 확인합니다. ![SDK 요청 및 응답 데이터가 포함된 이벤트 사용자 로그](https://www.braze.com/docs/ko/ko/assets/img_archive/event_user_log_iams.png?fd8f7c0f05a549b6a529b92744f37f96) ##### 메시지가 요청되지 않는 문제 해결 {#troubleshoot-messages-not-being-requested} 인앱 메시지가 요청되지 않는 경우, 앱이 세션을 올바르게 추적하지 못하고 있을 수 있습니다. 인앱 메시지는 세션 시작 시 새로고침됩니다. 세션 타임아웃 의미론에 따라 앱이 세션을 시작하고 있는지 확인하세요: ![성공적인 세션 시작 이벤트를 표시하는 이벤트 사용자 로그의 SDK 요청](https://www.braze.com/docs/ko/ko/assets/img_archive/event_user_log_session_start.png?972201c9c20f018bc85d97167638f04e) ##### 메시지가 반환되지 않는 문제 해결 {#troubleshoot-messages-not-being-returned} 인앱 메시지가 반환되지 않는 경우, 타겟팅 또는 자격 문제일 가능성이 높습니다: 1. Segment에 사용자가 포함되어 있지 않습니다. - 사용자의 [**참여**](https://www.braze.com/docs/ko/ko/user_guide/audience/manage_audience/user_profiles/#engagement-tab) 탭에서 예상 Segment를 확인하세요. 2. 사용자가 이미 메시지를 받았고 재자격이 없었습니다. - [재자격 설정](https://www.braze.com/docs/ko/ko/user_guide/messaging/messaging_fundamentals/re_eligibility/) 및 [인앱 메시지 FAQ](https://www.braze.com/docs/ko/ko/user_guide/channels/in_app_messages/faq/#campaigns)를 확인하세요. 3. 사용자가 빈도 제한에 도달했습니다. - [빈도 제한 설정](https://www.braze.com/docs/ko/ko/user_guide/messaging/messaging_fundamentals/frequency_capping/)을 확인하세요. 4. 사용자가 대조군에 포함되었습니다. - **Received campaign variant** 필터를 **Control**로 설정한 Segment를 생성하거나, 통합 테스트 중에는 대조군을 옵트아웃하세요. 5. 더 높은 우선순위의 인앱 메시지가 우선했습니다. [인앱 메시지 FAQ](https://www.braze.com/docs/ko/ko/user_guide/channels/in_app_messages/faq/#can-multiple-in-app-messages-display-in-the-same-session)를 참조하세요. 아카이브된 Campaign, 트리거 구성, 방해금지 시간에 대해서는 [인앱 메시지 FAQ](https://www.braze.com/docs/ko/ko/user_guide/channels/in_app_messages/faq/)를 참조하세요. ## 노출 횟수 및 분석 {#impressions-and-analytics} **증상:** 노출 횟수 또는 클릭 수가 예상과 일치하지 않습니다. - **_노출 횟수_가 _고유 노출 횟수_보다 큼:** 사용자가 여러 기기를 사용하거나 스케줄된 지연으로 인해 동일한 사용자가 두 번 이상 자격을 얻는 경우 예상되는 결과입니다. [Campaign 및 Canvas 재자격](https://www.braze.com/docs/ko/ko/user_guide/messaging/messaging_fundamentals/re_eligibility/)을 참조하세요. - **노출 횟수가 예상보다 낮음:** 사용자가 메시지를 보지 않았을 수 있고(노출 횟수는 표시 시 기록됨), 여러 높은 우선순위 메시지가 서로를 가로챌 수 있으며, 트리거 동기화 경합이 적용될 수 있습니다. Canvas 인앱 메시지에 대해서는 [Canvas 인앱 메시지](#canvas-in-app-messages)를 참조하세요. 전체 측정기준 정의는 [인앱 메시지 보고](https://www.braze.com/docs/ko/ko/user_guide/channels/in_app_messages/reporting/) 및 [인앱 메시지 FAQ](https://www.braze.com/docs/ko/ko/user_guide/channels/in_app_messages/faq/)를 참조하세요. - **노출 횟수가 이전보다 낮음:** Segment 및 Campaign 변경 로그를 검토하세요. 더 높은 우선순위의 Campaign에서 동일한 트리거 이벤트를 재사용하지 않았는지 확인하세요. ![사용자가 마지막으로 Campaign을 확인한 이후 7개의 변경 사항이 있는 Campaign 세부 정보 페이지의 변경 로그 보기 링크](https://www.braze.com/docs/ko/ko/assets/img_archive/trouble4.png?d1b004eed1ccaf74f475397ebbae7958) 델리게이트 또는 커스텀 핸들러를 사용하여 인앱 메시지를 수동으로 표시하는 경우, 노출 횟수와 클릭 수를 직접 기록해야 합니다. Swift 및 Android 세부 사항은 [플랫폼별 표시 문제 해결](#platform-specific-display-troubleshooting)에서 SDK 탭을 참조하고, Web의 경우 [인앱 메시지 데이터 기록](https://www.braze.com/docs/ko/ko/developer_guide/in_app_messages/logging_message_data/)을 참조하세요. ## 플랫폼별 표시 문제 해결 {#platform-specific-display-troubleshooting} 이벤트 사용자 로그에 **Trigger In-App Message** 행이 나타나지만 기기에 아무것도 표시되지 않는 경우, SDK 탭을 선택하여 표시 점검(델리게이트, 사용량 제한, 방향, 커스텀 핸들러)을 확인하세요. ### Troubleshooting display {#troubleshooting-in-app-message-display} If your app is requesting and receiving in-app messages but they aren't showing, device-side logic may be preventing display: 1. Is the trigger event firing as expected? To test, configure the message to trigger on a different action (such as session start) and verify whether it displays. 3. Failed image downloads prevent in-app messages with images from displaying. Check device logs for download failures. Try removing the image temporarily to see if the message displays. ### Troubleshooting display {#troubleshooting-in-app-message-display} If your app is requesting and receiving in-app messages but they aren't showing, device-side logic may be preventing display: 1. Is the trigger event firing as expected? To test, configure the message to trigger on a different action (such as session start) and verify whether it displays. 3. Failed image downloads prevent in-app messages with images from displaying. Check device logs for download failures. Try removing the image temporarily to see if the message displays. ### Troubleshooting display {#troubleshooting-in-app-message-display} If your app is requesting and receiving in-app messages but they aren't showing, device-side logic may be preventing display: 1. Is the trigger event firing as expected? To test, configure the message to trigger on a different action (such as session start) and verify whether it displays. 3. Failed image downloads prevent in-app messages with images from displaying. Check device logs for download failures. Try removing the image temporarily to see if the message displays. ### Troubleshooting asset loading (`NSURLError` code `-1008`) {#asset-loading} When integrating Braze alongside third-party network logging libraries, developers can commonly run into an `NSURLError` with the domain code `-1008`. This error indicates that assets like images and fonts could not be retrieved or failed to cache. To work around such cases, you must register Braze CDN URLs to the list of domains that should be ignored by these libraries. #### Domains The full list of CDN domains is as listed below: * `"appboy-images.com"` * `"braze-images.com"` * `"cdn.braze.eu"` * `"cdn.braze.com"` #### Examples Below are libraries that are known to conflict with Braze asset caching, along with example code to work around the issue. If your project uses a library that causes an unavailable resource error and is not listed below, consult the documentation of that library for similar usage APIs. ##### Netfox ```swift NFX.sharedInstance().ignoreURLs(["https://cdn.braze.com"]) ``` ```objc [NFX.sharedInstance ignoreURLs:@[@"https://cdn.braze.com"]]; ``` ##### NetGuard ```swift NetGuard.blackListHosts.append(contentsOf: ["cdn.braze.com"]) ``` ```objc NSMutableArray *blackListHosts = [NetGuard.blackListHosts mutableCopy]; [blackListHosts addObject:@"cdn.braze.com"]; NetGuard.blackListHosts = blackListHosts; ``` ##### XNLogger ```swift let brazeAssetsHostFilter = XNHostFilter(host: "https://cdn.braze.com") XNLogger.shared.addFilters([brazeAssetsHostFilter]) ``` ```objc XNHostFilter *brazeAssetsHostFilter = [[XNHostFilter alloc] initWithHost: @"https://cdn.braze.com"]; [XNLogger.shared addFilters:@[brazeAssetsHostFilter]]; ``` # Braze SDK용 푸시 알림 Source: /docs/ko/developer_guide/push_notifications/index.md # 푸시 알림 {#push-notifications} > [푸시 알림](https://www.braze.com/docs/ko/ko/user_guide/channels/push/)을 사용하면 중요한 이벤트가 발생할 때 앱에서 알림을 보낼 수 있습니다. 전달할 새 인스턴트 메시지, 송출할 뉴스 속보 알림 또는 오프라인으로 시청할 수 있도록 다운로드할 준비가 된 사용자가 좋아하는 TV 프로그램의 최신 에피소드가 있을 때 푸시 알림을 전송할 수 있습니다. 또한 애플리케이션이 필요할 때만 실행되므로 백그라운드 가져오기보다 더 효율적입니다. **Note:** **웹 URL로 리디렉션**과 **앱 내에서 웹 URL 열기**가 선택되지 않았는데도 링크가 앱 내에서 열리는 경우, 앱이 해당 URL을 직접 처리하고 있을 수 있습니다(예: iOS의 유니버설 링크 또는 Android의 앱 링크). 브라우저에서 링크를 열려면, 사용자가 알림을 탭할 때 앱이 해당 URL을 시스템 브라우저로 위임하는지 확인하거나, 클릭 동작이 Braze 대시보드 설정과 일치하도록 앱의 URL 처리 방식을 조정하세요. 클릭 동작 및 URL 처리 구성 방법은 해당 플랫폼의 푸시 설명서를 참조하세요. **Note:** This guide uses code samples from the Braze Web SDK 4.0.0+. To upgrade to the latest Web SDK version, see [SDK Upgrade Guide](https://github.com/braze-inc/braze-web-sdk/blob/master/UPGRADE_GUIDE.md). ## Prerequisites Before you can use this feature, you'll need to [integrate the Web Braze SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=web). ## Push protocols Web push notifications are implemented using the [W3C push standard](http://www.w3.org/TR/push-api/), which most major browsers support. For more information on specific push protocol standards and browser support, you can review resources from [Apple](https://developer.apple.com/notifications/safari-push-notifications/) [Mozilla](https://developer.mozilla.org/en-us/docs/web/api/push_api#browser_compatibility) and [Microsoft](https://developer.microsoft.com/en-us/microsoft-edge/status/pushapi/). ## Setting up push notifications ### Step 1: Configure your service worker In your project's `service-worker.js` file, add the following snippet and set the [`manageServiceWorkerExternally`](https://js.appboycdn.com/web-sdk/latest/doc/modules/braze.html#initialize) initialization option to `true` when initializing the Web SDK. **Important:** Your web server must return a `Content-Type: application/javascript` when serving your service worker file. Additionally, if your service worker file is not `service-worker.js` named, you'll need to use the `serviceWorkerLocation` [initialization option](https://js.appboycdn.com/web-sdk/latest/doc/modules/braze.html#initializationoptions). ### Step 2: Register the browser To immediately request push permissions from a user so their browser can receive push notifications, call `braze.requestPushPermission()`. To test if push is supported in their browser first, call `braze.isPushSupported()`. You can also [send a soft push prompt](https://www.braze.com/docs/ko/ko/developer_guide/push_notifications/soft_push_prompts/?sdktab=web) to the user before requesting push permission to show your own push-related UI. **Important:** On macOS, both **Google Chrome** and **Google Chrome Helper (Alerts)** must be enabled by the end-user in **System Settings > Notifications** before push notifications can be displayed—even if permissions are granted. ### Step 3: Disable `skipWaiting` (optional) The Braze service worker file will automatically call `skipWaiting` upon install. If you'd like to disable this functionality, add the following code to your service worker file, after importing Braze: ## Unsubscribing a user To unsubscribe a user, call `braze.unregisterPush()`. **Important:** Recent versions of Safari and Firefox require that you call this method from a short-lived event handler (such as from a button-click handler or soft push prompt). This is consistent with [Chrome's user experience best practices](https://docs.google.com/document/d/1WNPIS_2F0eyDm5SS2E6LZ_75tk6XtBSnR1xNjWJ_DPE) for push registration. ## Alternate domains To integrate web push, your domain must be [secure](https://w3c.github.io/webappsec-secure-contexts/), which generally means `https`, `localhost`, and other exceptions as defined in the [W3C push standard](https://www.w3.org/TR/service-workers/#security-considerations). You'll also need to be able to register a Service Worker at the root of your domain, or at least be able to control the HTTP headers for that file. This article covers how to integrate Braze Web Push on an alternate domain. ### Use cases If you can't meet all of the criteria outlined in the [W3C push standard](https://www.w3.org/TR/service-workers/#security-considerations), you can use this method to add a push prompt dialog to your website instead. This can be helpful if you want to let your users opt-in from an `http` website or a browser extension popup that's preventing your push prompt from displaying. ### Considerations Keep in mind, like many workarounds on the web, browsers continually evolve, and this method may not be viable in the future. Before continuing, ensure that: - You own a separate secure domain (`https://`) and permissions to register a Service Worker on that domain. - Users are logged in to your website which ensures push tokens are match to the correct profile. **Important:** You cannot use this method to implement push notifications for Shopify. Shopify will automatically remove the headers need to deliver push this way. ### Setting up an alternate push domain To make the following example clear, we'll use use `http://insecure.com` and `https://secure.com` as our two domains with the goal of getting visitors to register for push on `http://insecure.com`. This example could also be applied to a `chrome-extension://` scheme for a browser extension's popup page. #### Step 1: Initiate prompting flow On `insecure.com`, open a new window to your secure domain using a URL parameter to pass the currently logged-in user's Braze external ID. **http://insecure.com** ```html ``` #### Step 2: Register for push At this point, `secure.com` will open a popup window in which you can initialize the Braze Web SDK for the same user ID and request the user's permission for Web push. **https://secure.com/push-registration.html** #### Step 3: Communicate between domains (optional) Now that users can opt-in from this workflow originating on `insecure.com`, you may want to modify your site based on if the user is already opted-in or not. There's no point in asking the user to register for push if they already are. You can use iFrames and the [`postMessage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage) API to communicate between your two domains. **insecure.com** On our `insecure.com` domain, we will ask the secure domain (where push is _actually_ registered) for information on the current user's push registration: ```html ``` **secure.com/push-status.html** ## Frequently Asked Questions (FAQ) ### Service workers #### What if I can't register a service worker in the root directory? By default, a service worker can only be used within the same directory it is registered in. For example, if your service worker file exists in `/assets/service-worker.js`, it would only be possible to register it within `example.com/assets/*` or a subdirectory of the `assets` folder, but not on your homepage (`example.com/`). For this reason, it is recommended to host and register the service worker in the root directory (such as `https://example.com/service-worker.js`). If you cannot register a service worker in your root domain, an alternative approach is to use the [`Service-Worker-Allowed`](https://w3c.github.io/ServiceWorker/#service-worker-script-response) HTTP header when serving your service worker file. By configuring your server to return `Service-Worker-Allowed: /` in the response for the service worker, this will instruct the browser to broaden the scope and allow it to be used from within a different directory. #### Can I create a service worker using a Tag Manager? No, service workers must be hosted on your website's server and can't be loaded via Tag Manager. ### Site security #### Is HTTPS required? Yes. Web standards require that the domain requesting push notification permission be secure. #### When is a site considered "secure"? A site is considered secure if it matches one of the following secure-origin patterns. Braze Web push notifications are built on this open standard, so man-in-the-middle attacks are prevented. - `(https, , *)` - `(wss, *, *)` - `(, localhost, )` - `(, .localhost, *)` - `(, 127/8, )` - `(, ::1/128, *)` - `(file, *, —)` - `(chrome-extension, *, —)` #### What if a secure site is not available? While industry best practice is to make your whole site secure, customers who cannot secure their site domain can work around the requirement by using a secure modal. Read more in our guide to using [Alternate push domain](https://www.braze.com/docs/ko/ko/developer_guide/platform_integration_guides/web/push_notifications/alternate_push_domain) or view a [working demo](http://appboyj.com/modal-test.html). ## Prerequisites Before you can use this feature, you'll need to [integrate the Android Braze SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=android). ## Built-in features The following features are built into the Braze Android SDK. To use any other push notification features, you will need to [set up push notifications](#android_setting-up-push-notifications) for your app. |Feature|Description| |-------|-----------| |Push Stories|Android Push Stories are built into the Braze Android SDK by default. To learn more, see [Push Stories](https://www.braze.com/docs/ko/ko/user_guide/message_building_by_channel/push/advanced_push_options/push_stories/).| |Push Primers|Push primer campaigns encourage your users to enable push notifications on their device for your app. This can be done without SDK customization using our [no code push primer](https://www.braze.com/docs/ko/ko/user_guide/message_building_by_channel/push/best_practices/push_primer_messages/).| {: .reset-td-br-1 .reset-td-br-2 aria-label="Built-in features" } ## About the push notification lifecycle {#push-notification-lifecycle} The following flowchart shows how Braze handles the push notification lifecycle, such as permission prompts, token generation, and message delivery. ```mermaid --- config: theme: neutral --- flowchart TD %% Permission flow subgraph Permission[Push Permissions] B{Android version of the device?} B -->|Android 13+| C["requestPushPermissionPrompt() called"] B -->|Android 12 and earlier| D[No permissions required] %% Connect Android 12 path to Braze state D --> H3[Braze: user subscription state] H3 --> J3[Defaults to 'subscribed' when user profile created] C --> E{Did the user grant push permission?} E -->|Yes| F[POST_NOTIFICATIONS permission granted] E -->|No| G[POST_NOTIFICATIONS permission denied] %% Braze subscription state updates F --> H1[Braze: user subscription state] G --> H2[Braze: user subscription state] H1 --> I1{Automatically opt in after permission granted?} I1 -->|true| J1[Set to 'opted-in'] I1 -->|false| J2[Remains 'subscribed'] H2 --> K1[Remains 'subscribed'
or 'unsubscribed'] %% Subscription state legend subgraph BrazeStates[Braze subscription states] L1['Subscribed' - default state
when user profile created] L2['Opted-in' - user explicitly
wants push notifications] L3['Unsubscribed' - user explicitly
opted out of push] end %% Note about user-level states note1[Note: These states are user-level
and apply across all devices for the user] %% Connect states to legend J1 -.-> L2 J2 -.-> L1 J3 -.-> L1 K1 -.-> L3 note1 -.-> BrazeStates end %% Styling classDef permissionClass fill:#e3f2fd,stroke:#1565c0,stroke-width:2px classDef tokenClass fill:#e8f5e9,stroke:#2e7d32,stroke-width:2px classDef sdkClass fill:#fff3e0,stroke:#e65100,stroke-width:2px classDef configClass fill:#f3e5f5,stroke:#7b1fa2,stroke-width:2px classDef displayClass fill:#ffebee,stroke:#c62828,stroke-width:2px classDef deliveryClass fill:#fce4ec,stroke:#c2185b,stroke-width:2px classDef brazeClass fill:#e8f5e9,stroke:#2e7d32,stroke-width:3px class A,B,C,E,F,G permissionClass class H,I tokenClass class J,K sdkClass class N,O,P configClass class R,S,S1,T,U,V displayClass class W,X,X1,X2,Y,Z deliveryClass class H1,H2,H3,I1,J1,J2,J3,K1,L1,L2,L3,note1 brazeClass ``` ```mermaid --- config: theme: neutral --- flowchart TD %% Token generation flow subgraph Token[Token Generation] H["Braze SDK initialized"] --> Q{Is FCM auto-registration enabled?} Q -->|Yes| L{Is required configuration present?} Q -->|No| M[No FCM token generated] L -->|Yes| I[Generate FCM token] L -->|No| M I --> K[Register token with Braze] %% Configuration requirements subgraph Config[Required configuration] N['google-services.json' file is present] O['com.google.firebase:firebase-messaging' in gradle] P['com.google.gms.google-services' plugin in gradle] end %% Connect config to check N -.-> L O -.-> L P -.-> L end %% Styling classDef permissionClass fill:#e3f2fd,stroke:#1565c0,stroke-width:2px classDef tokenClass fill:#e8f5e9,stroke:#2e7d32,stroke-width:2px classDef sdkClass fill:#fff3e0,stroke:#e65100,stroke-width:2px classDef configClass fill:#f3e5f5,stroke:#7b1fa2,stroke-width:2px classDef displayClass fill:#ffebee,stroke:#c62828,stroke-width:2px classDef deliveryClass fill:#fce4ec,stroke:#c2185b,stroke-width:2px classDef brazeClass fill:#e8f5e9,stroke:#2e7d32,stroke-width:3px class A,B,C,E,F,G permissionClass class H,I tokenClass class J,K sdkClass class N,O,P configClass class R,S,S1,T,U,V displayClass class W,X,X1,X2,Y,Z deliveryClass class H1,H2,H3,I1,J1,J2,J3,K1,L1,L2,L3,note1 brazeClass ``` ```mermaid --- config: theme: neutral fontSize: 10 --- flowchart TD subgraph Display[Push Display] %% Push delivery flow W[Push sent to FCM servers] --> X{Did FCM receive push?} X -->|App is terminated| Y[FCM cannot deliver push to the app] X -->|Delivery conditions met| X1[App receives push from FCM] X1 --> X2[Braze SDK receives push] X2 --> R[Push type?] %% Push Display Flow R -->|Standard push| S{Is push permission required?} R -->|Silent push| T[Braze SDK processes silent push] S -->|Yes| S1{Did the user grant push permission?} S -->|No| V[Notification is shown to the user] S1 -->|Yes| V S1 -->|No| U[Notification is not shown to the user] end %% Styling classDef permissionClass fill:#e3f2fd,stroke:#1565c0,stroke-width:2px classDef tokenClass fill:#e8f5e9,stroke:#2e7d32,stroke-width:2px classDef sdkClass fill:#fff3e0,stroke:#e65100,stroke-width:2px classDef configClass fill:#f3e5f5,stroke:#7b1fa2,stroke-width:2px classDef displayClass fill:#ffebee,stroke:#c62828,stroke-width:2px classDef deliveryClass fill:#fce4ec,stroke:#c2185b,stroke-width:2px classDef brazeClass fill:#e8f5e9,stroke:#2e7d32,stroke-width:3px class A,B,C,E,F,G permissionClass class H,I tokenClass class J,K sdkClass class N,O,P configClass class R,S,S1,T,U,V displayClass class W,X,X1,X2,Y,Z deliveryClass class H1,H2,H3,I1,J1,J2,J3,K1,L1,L2,L3,note1 brazeClass ``` ## Setting up push notifications **Tip:** To check out a sample app using FCM with the Braze Android SDK, see [Braze: Firebase Push Sample App](https://github.com/braze-inc/braze-android-sdk/tree/master/samples/firebase-push). ### Rate limits Firebase Cloud Messaging (FCM) API has a default rate limit of 600,000 requests per minute. If you reach this limit, Braze will automatically try again in a few minutes. To request an increase, contact [Firebase Support](https://firebase.google.com/support). ### Step 1: Add Firebase to your project First, add Firebase to your Android project. For step-by-step instructions, see Google's [Firebase setup guide](https://firebase.google.com/docs/android/setup). ### Step 2: Add Cloud Messaging to your dependencies Next, add the Cloud Messaging library to your project dependencies. In your Android project, open `build.gradle`, then add the following line to your `dependencies` block. ```gradle implementation "google.firebase:firebase-messaging:+" ``` Your dependencies should look similar to the following: ```gradle dependencies { implementation project(':android-sdk-ui') implementation "com.google.firebase:firebase-messaging:+" } ``` ### Step 3: Enable the Firebase Cloud Messaging API In Google Cloud, select the project your Android app is using, then enable the [Firebase Cloud Messaging API](https://console.cloud.google.com/apis/library/fcm.googleapis.com). ![Enabled Firebase Cloud Messaging API](https://www.braze.com/docs/ko/ko/assets/img/android/push_integration/create_a_service_account/firebase-cloud-messaging-api-enabled.png?da5af516c5eab2865056a248406f7a8f){: style="max-width:80%;"} ### Step 4: Create a service account {#service-account} Next, create a new service account, so Braze can make authorized API calls when registering FCM tokens. In Google Cloud, go to **Service Accounts**, then choose your project. On the **Service Accounts** page, select **Create Service Account**. ![A project's service account home page with "Create Service Account" highlighted.](https://www.braze.com/docs/ko/ko/assets/img/android/push_integration/create_a_service_account/select-create-service-account.png?06a4afaf11e0d044900fbf49eaf980c5) Enter a service account name, ID, and description, then select **Create and continue**. In the **Role** field, find and select **Firebase Cloud Messaging API Admin** from the list of roles. For more restrictive access, create a [custom role](https://cloud.google.com/iam/docs/creating-custom-roles) with the `cloudmessaging.messages.create` permission, then choose it from the list instead. When you're finished, select **Done**. **Warning:** Be sure to select **Firebase Cloud Messaging _API_ Admin**, not **Firebase Cloud Messaging Admin**. ![The form for "Grant this service account access to project" with "Firebase Cloud Messaging API Admin" selected as the role.](https://www.braze.com/docs/ko/ko/assets/img/android/push_integration/create_a_service_account/add-fcm-api-admin.png?f4552c25937169d7eb2d9e09f4fea296) ### Step 5: Generate JSON credentials {#json} Next, generate JSON credentials for your FCM service account. On Google Cloud IAM & Admin, go to **Service Accounts**, then choose your project. Locate the FCM service account [you created earlier](#android_service-account), then select  **Actions** > **Manage Keys**. ![The project's service account homepage with the "Actions" menu open.](https://www.braze.com/docs/ko/ko/assets/img/android/push_integration/generate_json_credentials/select-manage-keys.png?3da57ece332b89e7d332a240ee4405e3) Select **Add Key** > **Create new key**. ![The selected service account with the "Add Key" menu open.](https://www.braze.com/docs/ko/ko/assets/img/android/push_integration/generate_json_credentials/select-create-new-key.png?8ed2f8cc053903ccef9c098604e6c26e) Choose **JSON**, then select **Create**. If you created your service account using a different Google Cloud project ID than your FCM project ID, you'll need to manually update the value assigned to the `project_id` in your JSON file. Be sure to remember where you downloaded the key—you'll need it in the next step. ![The form for creating a private key with "JSON" selected.](https://www.braze.com/docs/ko/ko/assets/img/android/push_integration/generate_json_credentials/select-create.png?a9f9ec288b77f9e2922e2fd68fc5e3e1){: style="max-width:65%;"} **Warning:** Private keys could pose a security risk if compromised. Store your JSON credentials in a secure location for now—you'll delete your key after you upload it to Braze. ### Step 6: Upload your JSON credentials to Braze Next, upload your JSON credentials to your Braze dashboard. In Braze, select  **Settings** > **App Settings**. ![The "Settings" menu open in Braze with "App Settings" highlighted.](https://www.braze.com/docs/ko/ko/assets/img/android/push_integration/upload_json_credentials/select-app-settings.png?8f1231dc6eac885988f3201d6921cec3) Under your Android app's **Push Notification Settings**, choose **Firebase**, then select **Upload JSON File** and upload the credentials [you generated earlier](#android_json). When you're finished, select **Save**. ![The form for "Push Notification Settings" with "Firebase" selected as the push provider.](https://www.braze.com/docs/ko/ko/assets/img/android/push_integration/upload_json_credentials/upload-json-file.png?98d4a93fa663294fccb3db920980da70) **Warning:** Private keys could pose a security risk if compromised. Now that your key is uploaded to Braze, delete the file [you generated previously](#android_json). ### Step 7: Set up automatic token registration When one of your users opt-in for push notifications, your app needs to generate an FCM token on their device before you can send them push notifications. With the Braze SDK, you can enable automatic FCM token registration for each user's device in your project's Braze configuration files. First, go to Firebase Console, open your project, then select  **Settings** > **Project settings**. ![The Firebase project with the "Settings" menu open.](https://www.braze.com/docs/ko/ko/assets/img/android/push_integration/set_up_automatic_token_registration/select-project-settings.png?9f5e0865e0fb698d08b31cc74069c256) Select **Cloud Messaging**, then under **Firebase Cloud Messaging API (V1)**, copy the number in the **Sender ID** field. ![The Firebase project's "Cloud Messaging" page with the "Sender ID" highlighted.](https://www.braze.com/docs/ko/ko/assets/img/android/push_integration/set_up_automatic_token_registration/copy-sender-id.png?f27e894f55060ddb1e698581ed8bb912) Next, open your Android Studio project and use your Firebase Sender ID to enable automatic FCM token registration within your `braze.xml` or `BrazeConfig`. To configure automatic FCM token registration, add the following lines to your `braze.xml` file: ```xml true FIREBASE_SENDER_ID ``` Replace `FIREBASE_SENDER_ID` with the value you copied from your Firebase project settings. Your `braze.xml` should look similar to the following: ```xml 12345ABC-6789-DEFG-0123-HIJK456789LM true 603679405392 ``` To configure automatic FCM token registration, add the following lines to your `BrazeConfig`: ```java .setIsFirebaseCloudMessagingRegistrationEnabled(true) .setFirebaseCloudMessagingSenderIdKey("FIREBASE_SENDER_ID") ``` ```kotlin .setIsFirebaseCloudMessagingRegistrationEnabled(true) .setFirebaseCloudMessagingSenderIdKey("FIREBASE_SENDER_ID") ``` Replace `FIREBASE_SENDER_ID` with the value you copied from your Firebase project settings. Your `BrazeConfig` should look similar to the following: ```java BrazeConfig brazeConfig = new BrazeConfig.Builder() .setApiKey("12345ABC-6789-DEFG-0123-HIJK456789LM") .setCustomEndpoint("sdk.iad-01.braze.com") .setSessionTimeout(60) .setHandlePushDeepLinksAutomatically(true) .setGreatNetworkDataFlushInterval(10) .setIsFirebaseCloudMessagingRegistrationEnabled(true) .setFirebaseCloudMessagingSenderIdKey("603679405392") .build(); Braze.configure(this, brazeConfig); ``` ```kotlin val brazeConfig = BrazeConfig.Builder() .setApiKey("12345ABC-6789-DEFG-0123-HIJK456789LM") .setCustomEndpoint("sdk.iad-01.braze.com") .setSessionTimeout(60) .setHandlePushDeepLinksAutomatically(true) .setGreatNetworkDataFlushInterval(10) .setIsFirebaseCloudMessagingRegistrationEnabled(true) .setFirebaseCloudMessagingSenderIdKey("603679405392") .build() Braze.configure(this, brazeConfig) ``` **Tip:** If you'd like manually register FCM tokens instead, you can call [`Braze.setRegisteredPushToken()`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze/-braze/registered-push-token.html) inside your app's [`onCreate()`](https://developer.android.com/reference/android/app/Application.html#onCreate()) method. ### Step 8: Remove automatic requests in your application class To prevent Braze from triggering unnecessary network requests every time you send silent push notifications, remove any automatic network requests configured in your `Application` class's `onCreate()` method. For more information see, [Android Developer Reference: Application](https://developer.android.com/reference/android/app/Application). ## Displaying notifications ### Step 1: Register Braze Firebase Messaging Service You can either create a new, existing, or non-Braze Firebase Messaging Service. Choose whichever best meets your specific needs. Braze includes a service to handle push receipt and open intents. Our `BrazeFirebaseMessagingService` class will need to be registered in your `AndroidManifest.xml`: ```xml ``` Our notification code also uses `BrazeFirebaseMessagingService` to handle open and click action tracking. This service must be registered in the `AndroidManifest.xml` to function correctly. Also, remember that Braze prefixes notifications from our system with a unique key so that we only render notifications sent from our systems. You may register additional services separately to render notifications sent from other FCM services. See [`AndroidManifest.xml`](https://github.com/braze-inc/braze-android-sdk/blob/master/samples/firebase-push/src/main/AndroidManifest.xml) in the Firebase push sample app. **Important:** Before Braze SDK 3.1.1, `AppboyFcmReceiver` was used to handle FCM push. The `AppboyFcmReceiver` class should be removed from your manifest and replaced with the preceding integration. If you already have a Firebase Messaging Service registered, you can pass [`RemoteMessage`](https://firebase.google.com/docs/reference/android/com/google/firebase/messaging/RemoteMessage) objects to Braze via [`BrazeFirebaseMessagingService.handleBrazeRemoteMessage()`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.push/-braze-firebase-messaging-service/-companion/handle-braze-remote-message.html). This method will only display a notification if the [`RemoteMessage`](https://firebase.google.com/docs/reference/android/com/google/firebase/messaging/RemoteMessage) object originated from Braze and will safely ignore if not. ```java public class MyFirebaseMessagingService extends FirebaseMessagingService { @Override public void onMessageReceived(RemoteMessage remoteMessage) { super.onMessageReceived(remoteMessage); if (BrazeFirebaseMessagingService.handleBrazeRemoteMessage(this, remoteMessage)) { // This Remote Message originated from Braze and a push notification was displayed. // No further action is needed. } else { // This Remote Message did not originate from Braze. // No action was taken and you can safely pass this Remote Message to other handlers. } } } ``` ```kotlin class MyFirebaseMessagingService : FirebaseMessagingService() { override fun onMessageReceived(remoteMessage: RemoteMessage?) { super.onMessageReceived(remoteMessage) if (BrazeFirebaseMessagingService.handleBrazeRemoteMessage(this, remoteMessage)) { // This Remote Message originated from Braze and a push notification was displayed. // No further action is needed. } else { // This Remote Message did not originate from Braze. // No action was taken and you can safely pass this Remote Message to other handlers. } } } ``` If you have another Firebase Messaging Service you would also like to use, you can also specify a fallback Firebase Messaging Service to call if your application receives a push that isn't from Braze. In your `braze.xml`, specify: ```xml true com.company.OurFirebaseMessagingService ``` or set via [runtime configuration:](https://www.braze.com/docs/ko/ko/developer_guide/sdk_initalization/?sdktab=android) ```java BrazeConfig brazeConfig = new BrazeConfig.Builder() .setFallbackFirebaseMessagingServiceEnabled(true) .setFallbackFirebaseMessagingServiceClasspath("com.company.OurFirebaseMessagingService") .build(); Braze.configure(this, brazeConfig); ``` ```kotlin val brazeConfig = BrazeConfig.Builder() .setFallbackFirebaseMessagingServiceEnabled(true) .setFallbackFirebaseMessagingServiceClasspath("com.company.OurFirebaseMessagingService") .build() Braze.configure(this, brazeConfig) ``` ### Step 2: Conform small icons to design guidelines For general information about Android notification icons, visit the [Notifications overview](https://developer.android.com/guide/topics/ui/notifiers/notifications). Starting in Android N, you should update or remove small notification icon assets that involve color. The Android system (not the Braze SDK) ignores all non-alpha and transparency channels in action icons and the notification small icon. In other words, Android will convert all parts of your notification small icon to monochrome except for transparent regions. To create a notification small icon asset that displays properly: - Remove all colors from the image except for white. - All other non-white regions of the asset should be transparent. **Note:** A common symptom of an improper asset is the small notification icon rendering as a solid monochrome square. This is due to the Android system not being able to find any transparent regions in the notification small icon asset. The following large and small icons pictured are examples of properly designed icons: ![A small icon appearing in the bottom corner of a large icons beside a message that says "Hey I'm on my way to the bar but.."](https://www.braze.com/docs/ko/ko/assets/img_archive/large_and_small_notification_icon.png?3231bf42436a261175a9cc890b4443bf "Large and Small Notification Icon") ### Step 3: Configure notification icons {#configure-icons} #### Specifying icons in braze.xml Braze allows you to configure your notification icons by specifying drawable resources in your `braze.xml`: ```xml REPLACE_WITH_YOUR_ICON REPLACE_WITH_YOUR_ICON ``` Setting a small notification icon is required. **If you do not set one, Braze will default to using the application icon as the small notification icon, which may look suboptimal.** Setting a large notification icon is optional but recommended. #### Specifying icon accent color The notification icon accent color can be overridden in your `braze.xml`. If the color is not specified, the default color is the same gray Lollipop uses for system notifications. ```xml 0xFFf33e3e ``` You may also optionally use a color reference: ```xml @color/my_color_here ``` ### Step 4: Add deep links #### Enabling automatic deep link opening To enable Braze to automatically open your app and any deep links when a push notification is clicked, set `com_braze_handle_push_deep_links_automatically` to `true`, in your `braze.xml`: ```xml true ``` This flag can also be set via [runtime configuration](https://www.braze.com/docs/ko/ko/developer_guide/sdk_initalization/?sdktab=android): ```java BrazeConfig brazeConfig = new BrazeConfig.Builder() .setHandlePushDeepLinksAutomatically(true) .build(); Braze.configure(this, brazeConfig); ``` ```kotlin val brazeConfig = BrazeConfig.Builder() .setHandlePushDeepLinksAutomatically(true) .build() Braze.configure(this, brazeConfig) ``` If you want to custom handle deep links, you will need to create a push callback that listens for push received and opened intents from Braze. For more information, see [Using a callback for push events](https://www.braze.com/docs/ko/ko/developer_guide/push_notifications/customization#android_using-a-callback-for-push-events). ## Handling foreground notifications By default, when a push notification arrives while your app is in the foreground on Android, the system displays it automatically. To have Braze process the push notification payload (for analytics tracking, deep link handling, and custom processing), route the incoming push data to Braze inside your `FirebaseMessagingService.onMessageReceived` method. ### How it works When you call `BrazeFirebaseMessagingService.handleBrazeRemoteMessage`, Braze determines if the payload is a Braze push notification and, if so, creates and displays the notification with the `NotificationManagerCompat` method. Unlike iOS, Android displays notifications regardless of whether the app is in the foreground or background. ```java package com.example.push; import com.braze.push.BrazeFirebaseMessagingService; import com.google.firebase.messaging.FirebaseMessagingService; import com.google.firebase.messaging.RemoteMessage; public class MyFirebaseMessagingService extends FirebaseMessagingService { @Override public void onMessageReceived(RemoteMessage remoteMessage) { super.onMessageReceived(remoteMessage); // Let Braze process the payload and display the notification if (BrazeFirebaseMessagingService.handleBrazeRemoteMessage(this, remoteMessage)) { // Braze successfully handled the push notification } else { // Handle non-Braze messages } } } ``` ```kotlin package com.example.push import com.braze.push.BrazeFirebaseMessagingService import com.google.firebase.messaging.FirebaseMessagingService import com.google.firebase.messaging.RemoteMessage class MyFirebaseMessagingService : FirebaseMessagingService() { override fun onMessageReceived(remoteMessage: RemoteMessage) { super.onMessageReceived(remoteMessage) // Let Braze process the payload and display the notification if (BrazeFirebaseMessagingService.handleBrazeRemoteMessage(this, remoteMessage)) { // Braze successfully handled the push notification } else { // Handle non-Braze messages } } } ``` For more information, see the [Firebase integration sample](https://github.com/braze-inc/braze-android-sdk/blob/master/samples/firebase-push/src/main/java/com/braze/firebasepush/FirebaseMessagingService.kt) in the Braze Android SDK repository. ### Customizing foreground behavior If you want custom foreground behavior, such as suppressing the system notification or showing an in-app UI instead, you can: - Use `subscribeToPushNotificationEvents` to react to push events and handle deep links with the `BrazeNotificationUtils.routeUserWithNotificationOpenedIntent` method. For more information, see the [Firebase push sample](https://github.com/braze-inc/braze-android-sdk/blob/master/samples/firebase-push/src/main/java/com/braze/firebasepush/FirebaseApplication.kt). - Build and post your own notification using a custom `IBrazeNotificationFactory`, or suppress the notification by not calling `notificationManager.notify` in your handling path. For more information on customizing notifications, see [Custom notification factory](https://www.braze.com/docs/ko/ko/developer_guide/push_notifications/customization/?sdktab=android#custom-notification-factory). #### Creating custom deep links Follow the instructions found within the [Android developer documentation](http://developer.android.com/training/app-indexing/deep-linking.html) on deep linking if you have not already added deep links to your app. To learn more about what deep links are, see our [FAQ article](https://www.braze.com/docs/ko/ko/user_guide/messaging/design_and_edit/personalize/actions_and_media_urls/#what-is-deep-linking). #### Adding deep links The Braze dashboard supports setting deep links or web URLs in push notifications campaigns and Canvases that will be opened when the notification is clicked. ![The 'On Click Behavior' setting in the Braze dashboard with 'Deep Link Into Application' selected from the dropdown.](https://www.braze.com/docs/ko/ko/assets/img_archive/deep_link_click_action.png?7414cf7c78b097ac301be69fca3c5547 "Deep Link Click Action") #### Customizing back stack behavior The Android SDK, by default, will place your host app's main launcher activity in the back stack when following push deep links. Braze allows you to set a custom activity to open in the back stack in place of your main launcher activity or to disable the back stack altogether. For example, to set an activity called `YourMainActivity` as the back stack activity using [runtime configuration](https://www.braze.com/docs/ko/ko/developer_guide/sdk_initalization/?sdktab=android): ```java BrazeConfig brazeConfig = new BrazeConfig.Builder() .setPushDeepLinkBackStackActivityEnabled(true) .setPushDeepLinkBackStackActivityClass(YourMainActivity.class) .build(); Braze.configure(this, brazeConfig); ``` ```kotlin val brazeConfig = BrazeConfig.Builder() .setPushDeepLinkBackStackActivityEnabled(true) .setPushDeepLinkBackStackActivityClass(YourMainActivity.class) .build() Braze.configure(this, brazeConfig) ``` See the equivalent configuration for your `braze.xml`. Note that the class name must be the same as returned by `Class.forName()`. ```xml true your.package.name.YourMainActivity ``` ### Step 5: Define notification channels The Braze Android SDK supports [Android notification channels](https://developer.android.com/preview/features/notification-channels.html). If a Braze notification does not contain the ID for a notification channel or that a Braze notification contains an invalid channel ID, Braze will display the notification with the default notification channel defined in the SDK. Company users use [Android Notification Channels](https://www.braze.com/docs/ko/ko/user_guide/message_building_by_channel/push/android/notification_channels/) within the platform to group notifications. To set the user facing name of the default Braze notification channel, use [`BrazeConfig.setDefaultNotificationChannelName()`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.configuration/-braze-config/-builder/set-default-notification-channel-name.html). To set the user facing description of the default Braze notification channel, use [`BrazeConfig.setDefaultNotificationChannelDescription()`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.configuration/-braze-config/-builder/set-default-notification-channel-description.html). Update any API campaigns with the [Android push object](https://www.braze.com/docs/ko/ko/api/objects_filters/messaging/android_object/) parameter to include the `notification_channel` field. If this field is not specified, Braze will send the notification payload with the [dashboard fallback](https://www.braze.com/docs/ko/ko/user_guide/message_building_by_channel/push/android/notification_channels/#dashboard-fallback-channel) channel ID. Other than the default notification channel, Braze will not create any channels. All other channels must be programmatically defined by the host app and then entered into the Braze dashboard. The default channel name and description can also be configured in `braze.xml`. ```xml Your channel name Your channel description ``` ### Step 6: Test notification display and analytics #### Testing display At this point, you should be able to see notifications sent from Braze. To test this, go to the **Campaigns** page on your Braze dashboard and create a **Push Notification** campaign. Choose **Android Push** and design your message. Then click the eye icon in the composer to get the test sender. Enter the user ID or email address of your current user and click **Send Test**. You should see the push show up on your device. ![The 'Test' tab of a push notification campaign in the Braze dashboard.](https://www.braze.com/docs/ko/ko/assets/img_archive/android_push_test.png?ee8f7372a8c3f7d77dc9ebd5e131a1f0 "Android Push Test") For issues related to push display, see our [troubleshooting guide](https://www.braze.com/docs/ko/ko/developer_guide/push_notifications/troubleshooting/?sdktab=android). #### Testing analytics At this point, you should also have analytics logging for push notification opens. Clicking on the notification when it arrives should result in the **Direct Opens** on your campaign results page to increase by 1. Check out our [push reporting](https://www.braze.com/docs/ko/ko/user_guide/message_building_by_channel/push/push_reporting/) article for a break down on push analytics. For issues related to push analytics, see our [troubleshooting guide](https://www.braze.com/docs/ko/ko/developer_guide/push_notifications/troubleshooting/?sdktab=android). #### Testing from command line If you'd like to test in-app and push notifications via the command-line interface, you can send a single notification through the terminal via cURL and the [messaging API](https://www.braze.com/docs/ko/ko/api/endpoints/messaging/). You will need to replace the following fields with the correct values for your test case: - `YOUR_API_KEY` (Go to **Settings** > **API Keys**.) - `YOUR_EXTERNAL_USER_ID` (Search for a profile on the **Search Users** page.) - `YOUR_KEY1` (optional) - `YOUR_VALUE1` (optional) ```bash curl -X POST -H "Content-Type: application/json" -H "Authorization: Bearer {YOUR_API_KEY}" -d '{ "external_user_ids":["YOUR_EXTERNAL_USER_ID"], "messages": { "android_push": { "title":"Test push title", "alert":"Test push", "extra": { "YOUR_KEY1":"YOUR_VALUE1" } } } }' https://rest.iad-01.braze.com/messages/send ``` This example uses the `US-01` instance. If you are not on this instance, replace the `US-01` endpoint with [your endpoint](https://www.braze.com/docs/ko/ko/api/basics/#endpoints). ## Conversation push notifications ![Android notification shade showing a Conversations section with three grouped conversation notifications from different contacts.](https://www.braze.com/docs/ko/ko/assets/img/android/push/conversations_android.png?e93b0b2e074ac12cac3a56619b22117b){: style="float:right;max-width:35%;margin-left:15px;border: 0;"} The [people and conversations initiative](https://developer.android.com/guide/topics/ui/conversations) is a multi-year Android initiative that aims to elevate people and conversations in the system surfaces of the phone. This priority is based on the fact that communication and interaction with other people is still the most valued and important functional area for the majority of Android users across all demographics. ### Usage requirements - This notification type requires the Braze Android SDK v15.0.0+ and Android 11+ devices. - Unsupported devices or SDKs will fallback to a standard push notification. This feature is only available over the Braze REST API. See the [Android push object](https://www.braze.com/docs/ko/ko/api/objects_filters/messaging/android_object#android-conversation-push-object) for more information. ## FCM quota exceeded errors When your limit for Firebase Cloud Messaging (FCM) is exceeded, Google returns "quota exceeded" errors. The default limit for FCM is 600,000 requests per minute. Braze retries sending according to Google's recommended best practices. However, a large volume of these errors can prolong sending time by several minutes. To mitigate potential impact, Braze will send you an alert that the rate limit is being exceeded and steps you can take to prevent the errors. To check your current limit, go to your **Google Cloud Console** > **APIs & Services** > **Firebase Cloud Messaging API** > **Quotas & System Limits**, or visit the [FCM API Quotas page](https://console.cloud.google.com/apis/api/fcm.googleapis.com/quotas). ### Best practices We recommend these best practices to keep these error volumes low. #### Request a rate limit increase from FCM To request a rate limit increase from FCM, you can contact [Firebase Support](https://firebase.google.com/support) directly or do the following: 1. Go to the [FCM API Quotas page](https://console.cloud.google.com/apis/api/fcm.googleapis.com/quotas). 2. Locate the **Send requests per minute** quota. 3. Select **Edit Quota**. 4. Enter a new value and submit your request. #### Apply a workspace rate limit You can apply a workspace rate limit for Android push notifications. This can help regulate the delivery rate of your outgoing messages. For more details, see [Workspace messaging rate limits](https://www.braze.com/docs/ko/ko/user_guide/administrative/app_settings/messaging_rate_limits). ## Rate limits Push notifications are rate-limited, so don't be afraid of sending as many as your application needs. iOS and the Apple Push Notification service (APNs) servers will control how often they are delivered, and you won't get into trouble for sending too many. If your push notifications are throttled, they might be delayed until the next time the device sends a keep-alive packet or receives another notification. ## Setting up push notifications ### Step 1: Upload your APNs token Before you can send an iOS push notification using Braze, you need to upload your `.p8` push notification file, as described in [Apple's developer documentation](https://developer.apple.com/documentation/usernotifications/establishing-a-token-based-connection-to-apns): 1. In your Apple developer account, go to [**Certificates, Identifiers & Profiles**](https://developer.apple.com/account/ios/certificate). 2. Under **Keys**, select **All** and click the add button (+) in the upper-right corner. 3. Under **Key Description**, enter a unique name for the signing key. 4. Under **Key Services**, select the **Apple Push Notification service (APNs)** checkbox, then click **Continue**. Click **Confirm**. 5. Note the key ID. Click **Download** to generate and download the key. Make sure to save the downloaded file in a secure place, as you cannot download this more than once. 6. In Braze, go to **Settings** > **App Settings** and upload the `.p8` file under **Apple Push Certificate**. You can upload either your development or production push certificate. To test push notifications after your app is live in the App Store, its recommended to set up a separate workspace for the development version of your app. 7. When prompted, enter your app's [bundle ID](https://developer.apple.com/documentation/foundation/nsbundle/1418023-bundleidentifier), [key ID](https://developer.apple.com/help/account/manage-keys/get-a-key-identifier/), and [team ID](https://developer.apple.com/help/account/manage-your-team/locate-your-team-id). You'll also need to specify whether to send notifications to your app's development or production environment, which is defined by its provisioning profile. 8. When you're finished, select **Save**. ### Step 2: Enable push capabilities In Xcode, go to the **Signing & Capabilities** section of the main app target and add the push notifications capability. ![The 'Signing & Capabilities' section in an Xcode project.](https://www.braze.com/docs/ko/ko/assets/img_archive/Enable_push_capabilities.png?8a3957eea917ba442294b7dbbe60732f) ### Step 3: Set up push handling You can use the Swift SDK to automate the processing of remote notifications received from Braze. This is the simplest way to handle push notifications and is the recommended handling method. #### Step 3.1: Enable automation in the push property To enable the automatic push integration, set the `automation` property of the `push` configuration to `true`: ```swift let configuration = Braze.Configuration(apiKey: "{YOUR-BRAZE-API-KEY}", endpoint: "{YOUR-BRAZE-API-ENDPOINT}") configuration.push.automation = true ``` ```objc BRZConfiguration *configuration = [[BRZConfiguration alloc] initWithApiKey:@"{YOUR-BRAZE-API-KEY}" endpoint:@"{YOUR-BRAZE-API-ENDPOINT}"]; configuration.push.automation = [[BRZConfigurationPushAutomation alloc] initEnablingAllAutomations:YES]; ``` This instructs the SDK to: - Register your application for push notification on the system. - Request the push notification authorization/permission at initialization. - Dynamically provide implementations for the push notification related system delegate methods. **Note:** The automation steps performed by the SDK are compatible with pre-existing push notification handling integrations in your codebase. The SDK only automates the processing of remote notification received from Braze. Any system handler implemented to process your own or another third party SDK remote notifications will continue to work when `automation` is enabled. **Warning:** The SDK must be initialized on the main thread to enable push notification automation. SDK initialization must happen before the application has finished launching or in your AppDelegate [`application(_:didFinishLaunchingWithOptions:)`](https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1622921-application) implementation. If your application requires additional setup before initializing the SDK, please refer to the [Delayed Initialization](https://www.braze.com/docs/ko/ko/developer_guide/sdk_initalization/?sdktab=swift) documentation page. #### Step 3.2: Override individual configurations (optional) For more granular control, each automation step can be enabled or disabled individually: ```swift // Enable all automations and disable the automatic notification authorization request at launch. configuration.push.automation = true configuration.push.automation.requestAuthorizationAtLaunch = false ``` ```objc // Enable all automations and disable the automatic notification authorization request at launch. configuration.push.automation = [[BRZConfigurationPushAutomation alloc] initEnablingAllAutomations:YES]; configuration.push.automation.requestAuthorizationAtLaunch = NO; ``` See [`Braze.Configuration.Push.Automation`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/configuration-swift.class/push-swift.class/automation-swift.class) for all available options and [`automation`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/configuration-swift.class/push-swift.class/automation-swift.property) for more information on the automation behavior. **Note:** If you rely on push notifications for additional behavior specific to your app, you may still be able to use automatic push integration instead of manual push notification integration. The [`subscribeToUpdates(_:)`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/notifications-swift.class/subscribetoupdates(_:)) method provides a way to be notified of remote notifications processed by Braze. #### Step 3.1: Register for push notifications with APNs Include the appropriate code sample within your app's [`application:didFinishLaunchingWithOptions:` delegate method](https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1622921-application) so that your users' devices can register with APNs. Ensure that you call all push integration code in your application's main thread. Braze also provides default push categories for push action button support, which must be manually added to your push registration code. Refer to [push action buttons](https://www.braze.com/docs/ko/ko/developer_guide/push_notifications/customization/?sdktab=swift#swift_customizing-push-categories) for additional integration steps. Add the following code to the `application:didFinishLaunchingWithOptions:` method of your app delegate. **Note:** The following code sample includes integration for provisional push authentication (lines 5 and 6). If you are not planning on using provisional authorization in your app, you can remove the lines of code that add `UNAuthorizationOptionProvisional` to the `requestAuthorization` options.
Visit [iOS notification options](https://www.braze.com/docs/ko/ko/user_guide/message_building_by_channel/push/ios/notification_options/) to learn more about push provisional authentication. ```swift application.registerForRemoteNotifications() let center = UNUserNotificationCenter.current() center.setNotificationCategories(Braze.Notifications.categories) center.delegate = self var options: UNAuthorizationOptions = [.alert, .sound, .badge] if #available(iOS 12.0, *) { options = UNAuthorizationOptions(rawValue: options.rawValue | UNAuthorizationOptions.provisional.rawValue) } center.requestAuthorization(options: options) { granted, error in print("Notification authorization, granted: \(granted), error: \(String(describing: error))") } ``` ```objc [application registerForRemoteNotifications]; UNUserNotificationCenter *center = UNUserNotificationCenter.currentNotificationCenter; [center setNotificationCategories:BRZNotifications.categories]; center.delegate = self; UNAuthorizationOptions options = UNAuthorizationOptionAlert | UNAuthorizationOptionSound | UNAuthorizationOptionBadge; if (@available(iOS 12.0, *)) { options = options | UNAuthorizationOptionProvisional; } [center requestAuthorizationWithOptions:options completionHandler:^(BOOL granted, NSError *_Nullable error) { NSLog(@"Notification authorization, granted: %d, " @"error: %@)", granted, error); }]; ``` **Warning:** You must assign your delegate object using `center.delegate = self` synchronously before your app finishes launching, preferably in `application:didFinishLaunchingWithOptions:`. Not doing so may cause your app to miss incoming push notifications. Visit Apple's [`UNUserNotificationCenterDelegate`](https://developer.apple.com/documentation/usernotifications/unusernotificationcenterdelegate) documentation to learn more. If your app calls `wipeData()` and later re-enables the Braze SDK in the same app run, you must call `registerForRemoteNotifications()` again to re-populate the device token used by the SDK. #### Step 3.2: Register push tokens with Braze Once APNs registration is complete, pass the resulting `deviceToken` to Braze to enable for push notifications for the user. Add the following code to your app's `application(_:didRegisterForRemoteNotificationsWithDeviceToken:)` method: ```swift AppDelegate.braze?.notifications.register(deviceToken: deviceToken) ``` Add the following code to your app's `application:didRegisterForRemoteNotificationsWithDeviceToken:` method: ```objc [AppDelegate.braze.notifications registerDeviceToken:deviceToken]; ``` **Important:** The `application:didRegisterForRemoteNotificationsWithDeviceToken:` delegate method is called every time after `application.registerForRemoteNotifications()` is called.

If you are migrating to Braze from another push service and your user's device has already registered with APNs, this method will collect tokens from existing registrations the next time the method is called, and users will not have to re-opt-in to push. #### Step 3.3: Enable push handling Next, pass the received push notifications along to Braze. This step is necessary for logging push analytics and link handling. Ensure that you call all push integration code in your application's main thread. ##### Default push handling To enable the Braze default push handling, add the following code to your app's `application(_:didReceiveRemoteNotification:fetchCompletionHandler:)` method: ```swift if let braze = AppDelegate.braze, braze.notifications.handleBackgroundNotification( userInfo: userInfo, fetchCompletionHandler: completionHandler ) { return } completionHandler(.noData) ``` Next, add the following to your app's `userNotificationCenter(_:didReceive:withCompletionHandler:)` method: ```swift if let braze = AppDelegate.braze, braze.notifications.handleUserNotification( response: response, withCompletionHandler: completionHandler ) { return } completionHandler() ``` To enable the Braze default push handling, add the following code to your application's `application:didReceiveRemoteNotification:fetchCompletionHandler:` method: ```objc BOOL processedByBraze = AppDelegate.braze != nil && [AppDelegate.braze.notifications handleBackgroundNotificationWithUserInfo:userInfo fetchCompletionHandler:completionHandler]; if (processedByBraze) { return; } completionHandler(UIBackgroundFetchResultNoData); ``` Next, add the following code to your app's `(void)userNotificationCenter:didReceiveNotificationResponse:withCompletionHandler:` method: ```objc BOOL processedByBraze = AppDelegate.braze != nil && [AppDelegate.braze.notifications handleUserNotificationWithResponse:response withCompletionHandler:completionHandler]; if (processedByBraze) { return; } completionHandler(); ``` ##### Foreground push handling To enable foreground push notifications and let Braze recognize them when they're received, implement `UNUserNotificationCenter.userNotificationCenter(_:willPresent:withCompletionHandler:)`. If a user taps your foreground notification, the `userNotificationCenter(_:didReceive:withCompletionHandler:)` push delegate will be called and Braze will log the push click event. ```swift func userNotificationCenter( _ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions ) -> Void) { if let braze = AppDelegate.braze { // Forward notification payload to Braze for processing. braze.notifications.handleForegroundNotification(notification: notification) } // Configure application's foreground notification display options. if #available(iOS 14.0, *) { completionHandler([.list, .banner]) } else { completionHandler([.alert]) } } ``` To enable foreground push notifications and let Braze recognize them when they're received, implement `userNotificationCenter:willPresentNotification:withCompletionHandler:`. If a user taps your foreground notification, the `userNotificationCenter:didReceiveNotificationResponse:withCompletionHandler:` push delegate will be called and Braze will log the push click event. ```objc - (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler { if (AppDelegate.braze != nil) { // Forward notification payload to Braze for processing. [AppDelegate.braze.notifications handleForegroundNotificationWithNotification:notification]; } // Configure application's foreground notification display options. if (@available(iOS 14.0, *)) { completionHandler(UNNotificationPresentationOptionList | UNNotificationPresentationOptionBanner); } else { completionHandler(UNNotificationPresentationOptionAlert); } } ``` ## Testing notifications {#push-testing} If you'd like to test in-app and push notifications via the command line, you can send a single notification through the terminal via CURL and the [messaging API](https://www.braze.com/docs/ko/ko/api/endpoints/messaging/send_messages/post_send_messages/). You will need to replace the following fields with the correct values for your test case: - `YOUR_API_KEY` - available at **Settings** > **API Keys**. - `YOUR_EXTERNAL_USER_ID` - available on the **Search Users** page. See [assigning user IDs](https://www.braze.com/docs/ko/ko/developer_guide/platform_integration_guides/swift/analytics/setting_user_ids/#assigning-a-user-id) for more information. - `YOUR_KEY1` (optional) - `YOUR_VALUE1` (optional) In the following example, the `US-01` instance is being used. If you're not on this instance, refer to our [API documentation](https://www.braze.com/docs/ko/ko/api/basics/) to see which endpoint to make requests to. ```bash curl -X POST -H "Content-Type: application/json" -H "Authorization: Bearer {YOUR_API_KEY}" -d '{ "external_user_ids":["YOUR_EXTERNAL_USER_ID"], "messages": { "apple_push": { "alert":"Test push", "extra": { "YOUR_KEY1":"YOUR_VALUE1" } } } }' https://rest.iad-01.braze.com/messages/send ``` ## Subscribing to push notifications updates To access the push notification payloads processed by Braze, use the [`Braze.Notifications.subscribeToUpdates(payloadTypes:_:)`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/notifications-swift.class/subscribetoupdates(payloadtypes:_:)/) method. You can use the `payloadTypes` parameter to specify whether you'd like to subscribe to notifications involving push open events, push received events, or both. ```swift // This subscription is maintained through a Braze cancellable, which will observe for changes until the subscription is cancelled. // You must keep a strong reference to the cancellable to keep the subscription active. // The subscription is canceled either when the cancellable is deinitialized or when you call its `.cancel()` method. let cancellable = AppDelegate.braze?.notifications.subscribeToUpdates(payloadTypes: [.open, .received]) { payload in print("Braze processed notification with title '\(payload.title)' and body '\(payload.body)'") } ``` **Important:** Keep in mind, push received events will only trigger for foreground notifications and `content-available` background notifications. It will not trigger for notifications received while terminated or for background notifications without the `content-available` field. ```objc NSInteger filtersValue = BRZNotificationsPayloadTypeFilter.opened.rawValue | BRZNotificationsPayloadTypeFilter.received.rawValue; BRZNotificationsPayloadTypeFilter *filters = [[BRZNotificationsPayloadTypeFilter alloc] initWithRawValue: filtersValue]; BRZCancellable *cancellable = [notifications subscribeToUpdatesWithPayloadTypes:filters update:^(BRZNotificationsPayload * _Nonnull payload) { NSLog(@"Braze processed notification with title '%@' and body '%@'", payload.title, payload.body); }]; ``` **Important:** Keep in mind, push received events will only trigger for foreground notifications and `content-available` background notifications. It will not trigger for notifications received while terminated or for background notifications without the `content-available` field. **Note:** When using the automatic push integration, `subscribeToUpdates(_:)` is the only way to be notified of remote notifications processed by Braze. The `UIAppDelegate` and `UNUserNotificationCenterDelegate` system methods are not called when the notification is automatically processed by Braze. **Tip:** Create your push notification subscription in `application(_:didFinishLaunchingWithOptions:)` to ensure your subscription is triggered after an end-user taps a notification while your app is in a terminated state. ## Handling foreground notifications By default, when a push notification arrives while your app is in the foreground, iOS does not display it automatically. To display push notifications in the foreground and track them with Braze analytics, call the `handleForegroundNotification(notification:)` method inside your `UNUserNotificationCenterDelegate.userNotificationCenter(_:willPresent:withCompletionHandler:)` implementation. ### How it works When you call `handleForegroundNotification(notification:)`, Braze processes the notification payload to log analytics and handle any deep links or button actions. The actual display behavior is controlled by the `UNNotificationPresentationOptions` you pass to the completion handler. ```swift import BrazeKit import UserNotifications extension AppDelegate: UNUserNotificationCenterDelegate { func userNotificationCenter( _ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void ) { // Let Braze process the notification payload if let braze = AppDelegate.braze { braze.notifications.handleForegroundNotification(notification: notification) } // Control how the notification appears in the foreground if #available(iOS 14.0, *) { completionHandler([.banner, .list, .sound]) } else { completionHandler([.alert, .sound]) } } } ``` For a complete example, see the [push notifications manual integration sample](https://github.com/braze-inc/braze-swift-sdk/blob/e31907eaa0dbd151dc2e6826de66cc494242ba60/Examples/Swift/Sources/PushNotifications-Manual/AppDelegate.swift#L1-L120) in the Braze Swift SDK repository. ## Push primers {#push-primers} Push primer campaigns encourage your users to enable push notifications on their device for your app. This can be done without SDK customization using our [no code push primer](https://www.braze.com/docs/ko/ko/user_guide/message_building_by_channel/push/best_practices/push_primer_messages/). ## Dynamic APNs gateway management Dynamic Apple Push Notification Service (APNs) gateway management enhances the reliability and efficiency of iOS push notifications by automatically detecting the correct APNs environment. Previously, you would manually select APNs environments (development or production) for your push notifications, which sometimes led to incorrect gateway configurations, delivery failures, and `BadDeviceToken` errors. With dynamic APNs gateway management, you'll have: - **Improved reliability:** Notifications are always delivered to the correct APNs environment, reducing failed deliveries. - **Simplified configuration:** You no longer need to manually manage APNs gateway settings. - **Error resilience:** Invalid or missing gateway values are gracefully handled, providing uninterrupted service. ### Prerequisites Braze supports Dynamic APNs gateway management for push notifications on iOS with the following SDK version requirement: ### How it works When an iOS app integrates with the Braze Swift SDK, it sends device-related data, including [`aps-environment`](https://developer.apple.com/documentation/bundleresources/entitlements/aps-environment) to the Braze SDK API, if available. The `apns_gateway` value indicates whether the app is using the development (`dev`) or production (`prod`) APNs environment. Braze also stores the reported gateway value for each device. If a new, valid gateway value is received, Braze updates the stored value automatically. When Braze sends a push notification: - If a valid gateway value (dev or prod) is stored for the device, Braze uses it to determine the correct APNs environment. - If no gateway value is stored, Braze defaults to the APNs environment configured in the **App Settings** page. ### Frequently asked questions #### Why was this feature introduced? With dynamic APNs gateway management, the correct environment is selected automatically. Previously, you had to manually configure the APNs gateway, which could lead to `BadDeviceToken` errors, token invalidation, and potential APNs rate-limiting issues. #### How does this impact push delivery performance? This feature improves delivery rates by always routing push tokens to the correct APNs environment, avoiding failures caused by misconfigured gateways. #### Can I disable this feature? Dynamic APNs Gateway Management is turned on by default and provides reliability improvements. If you have specific use cases that require manual gateway selection, contact [Braze Support](https://www.braze.com/docs/ko/ko/user_guide/administrative/access_braze/support/). ## About push notifications for Android TV ![](https://www.braze.com/docs/ko/ko/assets/img/Television.png?bf36c19525c113aaa61e5554d969b4b3){: style="float:right;max-width:25%;margin-left:15px; border: 0"} While not a native feature, Android TV push integration is made possible by leveraging the Braze Android SDK and Firebase Cloud Messaging to register a push token for Android TV. It is, however, necessary to build a UI to display the notification payload after it is received. ## Prerequisites To use this feature, you'll need to complete the following: - [Integrate the Braze Android SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=android) - [Set up push notifications for the Braze Android SDK](https://www.braze.com/docs/ko/ko/developer_guide/platform_integration_guides/android/push_notifications/?tab=android) ## Setting up push notifications To set up push notifications for Android TV: 1. Create a custom view in your app to display your notifications. 2. Create a [custom notification factory](https://www.braze.com/docs/ko/ko/developer_guide/push_notifications/customization#customization-display). This will override the default SDK behavior and allow you to manually display the notifications. By returning `null`, this will prevent the SDK from processing and will require custom code to display the notification. After these steps have been completed, you can start sending push to Android TV!

3. (Optional) To track click analytics effectively, set up click analytics tracking. This can be achieved by creating a [push callback](https://www.braze.com/docs/ko/ko/developer_guide/push_notifications/customization#push-callback) to listen for Braze push opened and received intents. **Note:** These notifications **will not persist** and will only be visible to the user when the device displays them. This is due to Android TV's notification center not supporting historical notifications. ## Testing Android TV push notifications To test if your push implementation is successful, send a notification from the Braze dashboard as you would normally for an Android device. - **If the application is closed**: The push message will display a toast notification on the screen. - **If the application is open**: You have the opportunity to display the message in your own hosted UI. We recommend following the UI styling of our Android Mobile SDK in-app messages. ## Best practices For marketers using Braze, launching a campaign to Android TV will be identical to launching a push to Android mobile apps. To target these devices exclusively, we recommend selecting the Android TV App in segmentation. The delivered and clicked response returned by FCM will follow the same convention as a mobile Android device; therefore, any errors will be visible in the message activity log. ## Prerequisites Before you can use this feature, you'll need to [integrate the Cordova Braze SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=cordova). After you integrate the SDK, basic push notification functionality is enabled by default. To use [rich push notifications](https://www.braze.com/docs/ko/ko/developer_guide/push_notifications/rich/?sdktab=cordova) and [push stories](https://www.braze.com/docs/ko/ko/developer_guide/push_notifications/push_stories/?sdktab=cordova), you'll need to set them up individually. To use iOS push messages, you also need to upload a valid push certificate. **Warning:** Anytime you add, remove, or update your Cordova plugins, Cordova will overwrite the Podfile in your iOS app's Xcode project. This means you’ll need to set these features up again anytime you modify your Cordova plugins. ## Enabling push deep linking By default, the Braze Cordova SDK doesn't automatically handle deep links from push notifications. To enable push deep linking, follow the configuration steps in [Deep linking](https://www.braze.com/docs/ko/ko/developer_guide/cordova/deep_linking/). For more details about these and other push configuration options, see [Optional configurations](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration?sdktab=cordova#optional). ## Disabling basic push notifications (iOS only) After you integrate the Braze Cordova SDK for iOS, basic push notification functionality is enabled by default. To disable this functionality in your iOS app, add the following to your `config.xml` file. For more information, see [Optional configurations](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration?sdktab=cordova#optional). ```xml ``` ## Prerequisites Before you can use this feature, you'll need to [integrate the Flutter Braze SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=flutter). ## Setting up push notifications ### Step 1: Complete the initial setup #### Step 1.1: Register for push Register for push using Google’s Firebase Cloud Messaging (FCM) API. For a full walkthrough, refer to the following steps from the [Native Android push integration guide](https://www.braze.com/docs/ko/ko/developer_guide/platform_integration_guides/android/push_notifications/?tab=android/): 1. [Add Firebase to your project](https://www.braze.com/docs/ko/ko/developer_guide/platform_integration_guides/android/push_notifications/android/integration/standard_integration/#step-1-add-firebase-to-your-project). 2. [Add Cloud Messaging to your dependencies](https://www.braze.com/docs/ko/ko/developer_guide/platform_integration_guides/android/push_notifications/android/integration/standard_integration/#step-2-add-cloud-messaging-to-your-dependencies). 3. [Create a service account](https://www.braze.com/docs/ko/ko/developer_guide/platform_integration_guides/android/push_notifications/android/integration/standard_integration/#step-3-create-a-service-account). 4. [Generate JSON credentials](https://www.braze.com/docs/ko/ko/developer_guide/platform_integration_guides/android/push_notifications/android/integration/standard_integration/#step-4-generate-json-credentials). 5. [Upload your JSON credentials to Braze](https://www.braze.com/docs/ko/ko/developer_guide/platform_integration_guides/android/push_notifications/android/integration/standard_integration/#step-5-upload-your-json-credentials-to-braze). #### Step 1.2: Get your Google Sender ID First, go to Firebase Console, open your project, then select  **Settings** > **Project settings**. ![The Firebase project with the "Settings" menu open.](https://www.braze.com/docs/ko/ko/assets/img/android/push_integration/set_up_automatic_token_registration/select-project-settings.png?9f5e0865e0fb698d08b31cc74069c256) Select **Cloud Messaging**, then under **Firebase Cloud Messaging API (V1)**, copy the **Sender ID** to your clipboard. ![The Firebase project's "Cloud Messaging" page with the "Sender ID" highlighted.](https://www.braze.com/docs/ko/ko/assets/img/android/push_integration/set_up_automatic_token_registration/copy-sender-id.png?f27e894f55060ddb1e698581ed8bb912) #### Step 1.3: Update your `braze.xml` Add the following to your `braze.xml` file. Replace `FIREBASE_SENDER_ID` with the sender ID you copied previously. ```xml true FIREBASE_SENDER_ID ``` #### Step 1.1: Upload APNs certificates Generate an Apple Push Notification service (APNs) certificate and uploaded it to the Braze dashboard. For a full walkthrough, see [Uploading your APNs certificate](https://www.braze.com/docs/ko/ko/developer_guide/platform_integration_guides/swift/push_notifications/integration/#step-1-upload-your-apns-certificate). #### Step 1.2: Add push notification support to your app Follow the [native iOS integration guide](https://www.braze.com/docs/ko/ko/developer_guide/platform_integration_guides/swift/push_notifications/integration/?tab=objective-c#automatic-push-integration). ### Step 2: Listen for push notification events (optional) To listen for push notification events that Braze has detected and handled, call `subscribeToPushNotificationEvents()` and pass in an argument to execute. **Note:** Braze push notification events are available on both Android and iOS. Due to platform differences, iOS will only detect Braze push events when a user has interacted with a notification. ```dart // Create stream subscription StreamSubscription pushEventsStreamSubscription; pushEventsStreamSubscription = braze.subscribeToPushNotificationEvents((BrazePushEvent pushEvent) { print("Push Notification event of type ${pushEvent.payloadType} seen. Title ${pushEvent.title}\n and deeplink ${pushEvent.url}"); // Handle push notification events }); // Cancel stream subscription pushEventsStreamSubscription.cancel(); ``` ##### Push notification event fields **Note:** Because of platform limitations on iOS, the Braze SDK can only process push payloads while the app is in the foreground. Listeners will only trigger for the `push_opened` event type on iOS after a user has interacted with a push. For a full list of push notification fields, refer to the table below: | Field Name | Type | Description | | ------------------ | --------- | ----------- | | `payloadType` | String | Specifies the notification payload type. The two values that are sent from the Braze Flutter SDK are `push_opened` and `push_received`. Only `push_opened` events are supported on iOS. | | `url` | String | Specifies the URL that was opened by the notification. | | `useWebview` | Boolean | If `true`, URL will open in-app in a modal webview. If `false`, the URL will open in the device browser. | | `title` | String | Represents the title of the notification. | | `body` | String | Represents the body or content text of the notification. | | `summaryText` | String | Represents the summary text of the notification. This is mapped from `subtitle` on iOS. | | `badgeCount` | Number | Represents the badge count of the notification. | | `timestamp` | Number | Represents the time at which the payload was received by the application. | | `isSilent` | Boolean | If `true`, the payload is received silently. For details on sending Android silent push notifications, refer to [Silent push notifications on Android](https://www.braze.com/docs/ko/ko/developer_guide/push_notifications/silent/?sdktab=android). For details on sending iOS silent push notifications, refer to [Silent push notifications on iOS](https://www.braze.com/docs/ko/ko/developer_guide/push_notifications/silent/?sdktab=swift). | | `isBrazeInternal`| Boolean | This will be `true` if a notification payload was sent for an internal SDK feature, such as Feature Flag sync or uninstall tracking. The payload is received silently for the user. | | `imageUrl` | String | Specifies the URL associated with the notification image. | | `brazeProperties` | Object | Represents Braze properties associated with the campaign (key-value pairs). | | `ios` | Object | Represents iOS-specific fields. | | `android` | Object | Represents Android-specific fields. | {: .reset-td-br-1 .reset-td-br-2 aria-label="Push notification event fields" } ### Step 3: Test displaying push notifications To test your integration after configuring push notifications in the native layer: 1. Set an active user in the Flutter application. To do so, initialize your plugin by calling `braze.changeUser('your-user-id')`. 2. Head to **Campaigns** and create a new push notification campaign. Choose the platforms that you'd like to test. 3. Compose your test notification and head over to the **Test** tab. Add the same `user-id` as the test user and click **Send Test**. 4. You should receive the notification on your device shortly. You may need to check in the Notification Center or update Settings if it doesn't display. **Tip:** Starting with Xcode 14, you can test remote push notifications on an iOS simulator. ## Prerequisites Before you can use this feature, you'll need to [integrate the Android Braze SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=android). ## Setting up push notifications Newer phones manufactured by [Huawei](https://huaweimobileservices.com/) come equipped with Huawei Mobile Services (HMS) - a service used to deliver push instead of Google's Firebase Cloud Messaging (FCM). ### Step 1: Register for a Huawei developer account Before getting started, you'll need to register and set up a [Huawei Developer account](https://developer.huawei.com/consumer/en/console). In your Huawei account, go to **My Projects > Project Settings > App Information**, and take note of the `App ID` and `App secret`. ![](https://www.braze.com/docs/ko/ko/assets/img/huawei/huawei-credentials.png?b06eeb235330781a1d837e9d2d1733b7) ### Step 2: Create a new Huawei app in the Braze dashboard In the Braze dashboard, go to **App Settings**, listed under the **Settings** navigation. Click **+ Add App**, provide a name (such as My Huawei App), select `Android` as the platform. ![](https://www.braze.com/docs/ko/ko/assets/img/huawei/huawei-create-app.png?a6844b04811452719ae57875b2eb1261){: style="max-width:60%;"} Once your new Braze app has been created, locate the push notification settings and select `Huawei` as the push provider. Next, provide your `Huawei Client Secret` and `Huawei App ID`. ![](https://www.braze.com/docs/ko/ko/assets/img/huawei/huawei-dashboard-credentials.png?c91a9f413f10a2ef3043876640b61dac) ### Step 3: Integrate the Huawei messaging SDK into your app Huawei has provided an [Android integration codelab](https://developer.huawei.com/consumer/en/codelab/HMSPushKit/index.html) detailing integrating the Huawei Messaging Service into your application. Follow those steps to get started. After completing the codelab, you will need to create a custom [Huawei Message Service](https://developer.huawei.com/consumer/en/doc/development/HMS-References/push-HmsMessageService-cls) to obtain push tokens and forward messages to the Braze SDK. ```java public class CustomPushService extends HmsMessageService { @Override public void onNewToken(String token) { super.onNewToken(token); Braze.getInstance(this.getApplicationContext()).setRegisteredPushToken(token); } @Override public void onMessageReceived(RemoteMessage remoteMessage) { super.onMessageReceived(remoteMessage); if (BrazeHuaweiPushHandler.handleHmsRemoteMessageData(this.getApplicationContext(), remoteMessage.getDataOfMap())) { // Braze has handled the Huawei push notification } } } ``` ```kotlin class CustomPushService: HmsMessageService() { override fun onNewToken(token: String?) { super.onNewToken(token) Braze.getInstance(applicationContext).setRegisteredPushToken(token!!) } override fun onMessageReceived(hmsRemoteMessage: RemoteMessage?) { super.onMessageReceived(hmsRemoteMessage) if (BrazeHuaweiPushHandler.handleHmsRemoteMessageData(applicationContext, hmsRemoteMessage?.dataOfMap)) { // Braze has handled the Huawei push notification } } } ``` After adding your custom push service, add the following to your `AndroidManifest.xml`: ```xml ``` ### Step 4: Handle foreground notifications By default, when a push notification arrives while your app is in the foreground, Huawei displays it automatically. To have Braze process the push notification payload (for analytics tracking, deep link handling, and custom processing), route the incoming push data to Braze inside your `HmsMessageService.onMessageReceived` method. When you call `BrazeHuaweiPushHandler.handleHmsRemoteMessageData`, Braze determines if the payload is a Braze push notification and, if so, creates and displays the notification. For more information, see [Handling foreground notifications](https://www.braze.com/docs/ko/ko/developer_guide/push_notifications/?sdktab=android#handling-foreground-notifications) in the Android push notifications documentation. For a complete example, see the [Huawei handler reference](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.push/-braze-huawei-push-handler/index.html) in the Braze Android SDK documentation. ### Step 5: Test your push notifications (optional) At this point, you've created a new Huawei Android app in the Braze dashboard, configured it with your Huawei developer credentials, and have integrated the Braze and Huawei SDKs into your app. Next, we can test out the integration by testing a new push campaign in Braze. #### Step 5.1: Create a new push notification campaign In the **Campaigns** page, create a new campaign, and choose **Push Notification** as your message type. After you name your campaign, choose **Android Push** as the push platform. ![The campaign creation composer displaying the available push platforms.](https://www.braze.com/docs/ko/ko/assets/img/huawei/huawei-test-push-platforms.png?8e7eecaa5984912c2a042862716c2398) Next, compose your push campaign with a title and message. #### Step 5.2: Send a test push In the **Test** tab, enter your user ID, which you've set in your app using the [`changeUser(USER_ID_STRING)` method](https://www.braze.com/docs/ko/ko/developer_guide/platform_integration_guides/android/analytics/setting_user_ids/#assigning-a-user-id), and click **Send Test** to send a test push. ![The test tab in the campaign creation composer shows you can send a test message to yourself by providing your user ID and entering it into the "Add Individual Users" field.](https://www.braze.com/docs/ko/ko/assets/img/huawei/huawei-test-send.png?42dce83b469c90564ae79d3f4d37c572) At this point, you should receive a test push notification on your Huawei (HMS) device from Braze. #### Step 5.3: Set up Huawei segmentation (optional) Since your Huawei app in the Braze dashboard is built upon the Android push platform, you have the flexibility to send push to all Android users (Firebase Cloud Messaging and Huawei Mobile Services), or you can choose to segment your campaign audience to specific apps. To send push to only Huawei apps, [create a new Segment](https://www.braze.com/docs/ko/ko/user_guide/engagement_tools/segments/creating_a_segment/#step-3-choose-your-app-or-platform) and select your Huawei App within the **Apps** section. ![](https://www.braze.com/docs/ko/ko/assets/img/huawei/huawei-segmentation.png?3e7b24b199a37e61f4606496cda6f982) Of course, if you want to send the same push to all Android push providers, you can choose not to specify the app which will send to all Android apps configured within the current workspace. ## Prerequisites Before you can use this feature, you'll need to [integrate the React Native Braze SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=react%20native). ## Setting up push notifications {#setting-up-push-notifications} ### Step 1: Complete the initial setup #### Prerequisites Before you can use Expo for push notifications, you'll need to [set up the Braze Expo plugin](https://www.braze.com/docs/ko/ko/developer_guide/platform_integration_guides/react_native/sdk_integration/?tab=expo). #### Step 1.1: Update your `app.json` file Next update your `app.json` file for Android and iOS: - **Android:** Add the `enableFirebaseCloudMessaging` option. - **iOS:** Add the `enableBrazeIosPush` option. #### Step 1.2: Add your Google Sender ID First, go to Firebase Console, open your project, then select  **Settings** > **Project settings**. ![The Firebase project with the "Settings" menu open.](https://www.braze.com/docs/ko/ko/assets/img/android/push_integration/set_up_automatic_token_registration/select-project-settings.png?9f5e0865e0fb698d08b31cc74069c256) Select **Cloud Messaging**, then under **Firebase Cloud Messaging API (V1)**, copy the **Sender ID** to your clipboard. ![The Firebase project's "Cloud Messaging" page with the "Sender ID" highlighted.](https://www.braze.com/docs/ko/ko/assets/img/android/push_integration/set_up_automatic_token_registration/copy-sender-id.png?f27e894f55060ddb1e698581ed8bb912) Next, open your project's `app.json` file and set your `firebaseCloudMessagingSenderId` property to the Sender ID in your clipboard. For example: ``` "firebaseCloudMessagingSenderId": "693679403398" ``` #### Step 1.3: Add the path to your Google Services JSON In your project's `app.json` file, add the path to your `google-services.json` file. This file is required when setting `enableFirebaseCloudMessaging: true` in your configuration. ```json { "expo": { "android": { "googleServicesFile": "PATH_TO_GOOGLE_SERVICES" }, "plugins": [ [ "@braze/expo-plugin", { "androidApiKey": "YOUR-ANDROID-API-KEY", "iosApiKey": "YOUR-IOS-API-KEY", "enableBrazeIosPush": true, "enableFirebaseCloudMessaging": true, "firebaseCloudMessagingSenderId": "YOUR-FCM-SENDER-ID", "androidHandlePushDeepLinksAutomatically": true } ], ] } } ``` Note that you will need to use these settings instead of the native setup instructions if you are depending on additional push notification libraries like [Expo Notifications](https://docs.expo.dev/versions/latest/sdk/notifications/). If you are not using the Braze Expo plugin, or would like to configure these settings natively instead, register for push by referring to the [Native Android push integration guide](https://www.braze.com/docs/ko/ko/developer_guide/platform_integration_guides/android/push_notifications/?tab=android/). If you are not using the Braze Expo plugin, or would like to configure these settings natively instead, register for push by referring to the following steps from the [Native iOS push integration guide](https://www.braze.com/docs/ko/ko/developer_guide/push_notifications/?sdktab=swift): #### Step 1.1: Request for push permissions If you don't plan on requesting push permissions when the app is launched, omit the `requestAuthorizationWithOptions:completionHandler:` call in your AppDelegate. Then, skip to [Step 2](#reactnative_step-2-request-push-notifications-permission). Otherwise, follow the [native iOS integration guide](https://www.braze.com/docs/ko/ko/developer_guide/platform_integration_guides/swift/push_notifications/integration/?tab=objective-c#automatic-push-integration). #### Step 1.2 (Optional): Migrate your push key If you were previously using `expo-notifications` to manage your push key, run `expo fetch:ios:certs` from your application's root folder. This will download your push key (a .p8 file), which can then be uploaded to the Braze dashboard. ### Step 2: Request push notifications permission Use the `Braze.requestPushPermission()` method (available on v1.38.0 and up) to request permission for push notifications from the user on iOS and Android 13+. For Android 12 and below, this method is a no-op. This method takes in a required parameter that specifies which permissions the SDK should request from the user on iOS. These options have no effect on Android. ```javascript const permissionOptions = { alert: true, sound: true, badge: true, provisional: false }; Braze.requestPushPermission(permissionOptions); ``` #### Step 2.1: Listen for push notifications (optional) You can additionally subscribe to events where Braze has detected and handled an incoming push notification. Use the listener key `Braze.Events.PUSH_NOTIFICATION_EVENT`. **Important:** iOS push received events will only trigger for foreground notifications and `content-available` background notifications. It will not trigger for notifications received while terminated or for background notifications without the `content-available` field. ```javascript Braze.addListener(Braze.Events.PUSH_NOTIFICATION_EVENT, data => { console.log(`Push Notification event of type ${data.payload_type} seen. Title ${data.title}\n and deeplink ${data.url}`); console.log(JSON.stringify(data, undefined, 2)); }); ``` ##### Push notification event fields For a full list of push notification fields, refer to the table below: | Field Name | Type | Description | | ------------------ | --------- | ----------- | | `payload_type` | String | Specifies the notification payload type. The two values that are sent from the Braze React Native SDK are `push_opened` and `push_received`. | | `url` | String | Specifies the URL that was opened by the notification. | | `use_webview` | Boolean | If `true`, URL will open in-app in a modal webview. If `false`, the URL will open in the device browser. | | `title` | String | Represents the title of the notification. | | `body` | String | Represents the body or content text of the notification. | | `summary_text` | String | Represents the summary text of the notification. This is mapped from `subtitle` on iOS. | | `badge_count` | Number | Represents the badge count of the notification. | | `timestamp` | Number | Represents the time at which the payload was received by the application. | | `is_silent` | Boolean | If `true`, the payload is received silently. For details on sending Android silent push notifications, refer to [Silent push notifications on Android](https://www.braze.com/docs/ko/ko/developer_guide/push_notifications/silent/?sdktab=android). For details on sending iOS silent push notifications, refer to [Silent push notifications on iOS](https://www.braze.com/docs/ko/ko/developer_guide/push_notifications/silent/?sdktab=swift). | | `is_braze_internal`| Boolean | This will be `true` if a notification payload was sent for an internal SDK feature, such as Feature Flag sync or uninstall tracking. The payload is received silently for the user. | | `image_url` | String | Specifies the URL associated with the notification image. | | `braze_properties` | Object | Represents Braze properties associated with the campaign (key-value pairs). | | `ios` | Object | Represents iOS-specific fields. | | `android` | Object | Represents Android-specific fields. | {: .reset-td-br-1 .reset-td-br-2 aria-label="Push notification event fields" } ### Step 3: Enable deep linking (optional) To enable Braze to handle deep links inside React components when a push notification is clicked, first implement the steps described in [React Native Linking](https://reactnative.dev/docs/linking) library, or with your solution of choice. Then, follow the additional steps below. To learn more about what deep links are, see our [FAQ article](https://www.braze.com/docs/ko/ko/user_guide/messaging/design_and_edit/personalize/actions_and_media_urls/#what-is-deep-linking). **Important:** If you're migrating an existing React Native push integration, re-test deep linking after you upgrade the Braze SDK, React Native, Expo, or related libraries. Confirm that: - [React Native Linking](https://reactnative.dev/docs/linking) is still configured and handling your deep link URLs. - Your iOS initial push payload handling (see [Step 3.1: Store the push notification payload on app launch](#step-3-1)) is implemented and still called on app launch. - Any native delegate or listener methods you use to handle push click events are still registered and invoked as expected. If you're using the [Braze Expo plugin](https://www.braze.com/docs/ko/ko/developer_guide/platforms/react_native/sdk_integration/?tab=expo#step-2-choose-a-setup-option), you can handle push notification deep links automatically by setting `androidHandlePushDeepLinksAutomatically` to `true` in your `app.json`. To handle deep links manually instead, refer to the native Android documentation: [Adding deep links](https://www.braze.com/docs/ko/ko/developer_guide/push_notifications/deep_linking). #### Step 3.1: Store the push notification payload on app launch **Note:** This is supported as of React Native SDK 19.1.0. Add `populateInitialPushPayloadFromIntent` to your main activity's `onCreate()` method. This must be called before React Native initializes to capture the initial Intent data. For example: ```kotlin override fun onCreate(savedInstanceState: Bundle?) { BrazeReactUtils.populateInitialPushPayloadFromIntent(intent) super.onCreate(savedInstanceState) } ``` #### Step 3.2: Handle deep links from a closed state In addition to the base scenarios handled by [React Native Linking](https://reactnative.dev/docs/linking), implement the `Braze.getInitialPushPayload` method and retrieve the `url` value to account for deep links from push notifications that open your app when it isn't running. For example: ```javascript // Handles deep links when an app is launched from a hard close via push click. Braze.getInitialPushPayload(pushPayload => { if (pushPayload) { console.log('Braze.getInitialPushPayload is ' + pushPayload); showToast('Initial URL is ' + pushPayload.url); handleOpenUrl({ pushPayload.url }); } }); ``` **Note:** This method requires the native setup in Step 3.1 for your platform. If you're using the Braze Expo plugin, this may be handled automatically. **Important:** To handle deep links from push notifications on iOS, you must also configure link handling in your native iOS layer. This includes registering a custom URL scheme and implementing a URL handler in your `AppDelegate`. For full setup instructions, see [Handling deep links](https://www.braze.com/docs/ko/ko/developer_guide/platforms/swift/in_app_messages/deep_linking/?tab=objective-c) in the native iOS documentation. #### Step 3.1: Store the push notification payload on app launch {#step-3-1} **Note:** Skip step 3.1 if you're using the Braze Expo plugin, as this functionality is handled automatically. For iOS, add `populateInitialPayloadFromLaunchOptions` to your AppDelegate's `didFinishLaunchingWithOptions` method. For example: ```objc - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { // ... Perform regular React Native setup BRZConfiguration *configuration = [[BRZConfiguration alloc] initWithApiKey:apiKey endpoint:endpoint]; configuration.triggerMinimumTimeInterval = 1; configuration.logger.level = BRZLoggerLevelInfo; Braze *braze = [BrazeReactBridge initBraze:configuration]; AppDelegate.braze = braze; [self registerForPushNotifications]; [[BrazeReactUtils sharedInstance] populateInitialPayloadFromLaunchOptions:launchOptions]; return [super application:application didFinishLaunchingWithOptions:launchOptions]; } ``` ```swift func application( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil ) -> Bool { // ... Perform regular React Native setup let configuration = Braze.Configuration(apiKey: apiKey, endpoint: endpoint) configuration.triggerMinimumTimeInterval = 1 configuration.logger.level = .info let braze = BrazeReactBridge.initBraze(configuration) AppDelegate.braze = braze registerForPushNotifications() BrazeReactUtils.shared().populateInitialPayload(fromLaunchOptions: launchOptions) return super.application(application, didFinishLaunchingWithOptions: launchOptions) } ``` #### Step 3.2: Handle deep links from a closed state In addition to the base scenarios handled by [React Native Linking](https://reactnative.dev/docs/linking), implement the `Braze.getInitialPushPayload` method and retrieve the `url` value to account for deep links from push notifications that open your app when it isn't running. For example: ```javascript // Handles deep links when an app is launched from a hard close via push click. Braze.getInitialPushPayload(pushPayload => { if (pushPayload) { console.log('Braze.getInitialPushPayload is ' + pushPayload); showToast('Initial URL is ' + pushPayload.url); handleOpenUrl({ pushPayload.url }); } }); ``` **Note:** This method requires the native setup in Step 3.1 for your platform. If you're using the Braze Expo plugin, this may be handled automatically. #### Step 3.3: Enable Universal Links (optional) To enable [universal linking](https://www.braze.com/docs/ko/ko/developer_guide/push_notifications/deep_linking/?sdktab=swift#universal-links) support, implement a Braze delegate that determines whether to open a given URL, then register it with your Braze instance. Create a `BrazeReactDelegate.swift` file in your `iOS` directory and add the following. Replace `YOUR_DOMAIN_HOST` with your actual domain. ```swift import Foundation import BrazeKit import UIKit class BrazeReactDelegate: NSObject, BrazeDelegate { /// This delegate method determines whether to open a given URL. /// Reference the context to get additional details about the URL payload. func braze(_ braze: Braze, shouldOpenURL context: Braze.URLContext) -> Bool { if let host = context.url.host, host.caseInsensitiveCompare("YOUR_DOMAIN_HOST") == .orderedSame { // Sample custom handling of universal links let application = UIApplication.shared let userActivity = NSUserActivity(activityType: NSUserActivityTypeBrowsingWeb) userActivity.webpageURL = context.url // Routes to the `continueUserActivity` method, which should be handled in your AppDelegate. application.delegate?.application?( application, continue: userActivity, restorationHandler: { _ in } ) return false } // Let Braze handle links otherwise return true } } ``` Then, create and register your `BrazeReactDelegate` in `didFinishLaunchingWithOptions` of your project's `AppDelegate.swift` file. ```swift import BrazeKit class AppDelegate: UIResponder, UIApplicationDelegate { static var braze: Braze? // Keep a strong reference to the BrazeDelegate so it is not deallocated. private var brazeDelegate: BrazeReactDelegate? func application( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil ) -> Bool { // Other setup code (e.g., Braze initialization) brazeDelegate = BrazeReactDelegate() AppDelegate.braze?.delegate = brazeDelegate return true } } ``` Create a `BrazeReactDelegate.h` file in your `iOS` directory and then add the following code snippet. ```objc #import #import @interface BrazeReactDelegate: NSObject @end ``` Next, create a `BrazeReactDelegate.m` file and then add the following code snippet. Replace `YOUR_DOMAIN_HOST` with your actual domain. ```objc #import "BrazeReactDelegate.h" #import @implementation BrazeReactDelegate /// This delegate method determines whether to open a given URL. /// /// Reference the `BRZURLContext` object to get additional details about the URL payload. - (BOOL)braze:(Braze *)braze shouldOpenURL:(BRZURLContext *)context { if ([[context.url.host lowercaseString] isEqualToString:@"YOUR_DOMAIN_HOST"]) { // Sample custom handling of universal links UIApplication *application = UIApplication.sharedApplication; NSUserActivity* userActivity = [[NSUserActivity alloc] initWithActivityType:NSUserActivityTypeBrowsingWeb]; userActivity.webpageURL = context.url; // Routes to the `continueUserActivity` method, which should be handled in your `AppDelegate`. [application.delegate application:application continueUserActivity:userActivity restorationHandler:^(NSArray> * _Nullable restorableObjects) {}]; return NO; } // Let Braze handle links otherwise return YES; } @end ``` Then, create and register your `BrazeReactDelegate` in `didFinishLaunchingWithOptions` of your project's `AppDelegate.m` file. ```objc #import "BrazeReactUtils.h" #import "BrazeReactDelegate.h" @interface AppDelegate () // Keep a strong reference to the BrazeDelegate to ensure it is not deallocated. @property (nonatomic, strong) BrazeReactDelegate *brazeDelegate; @end - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { // Other setup code self.brazeDelegate = [[BrazeReactDelegate alloc] init]; braze.delegate = self.brazeDelegate; } ``` For an example integration, reference our sample app [here](https://github.com/braze-inc/braze-react-native-sdk/blob/master/BrazeProject/ios/BrazeProject/AppDelegate.mm). ### Step 4: Handle foreground notifications Foreground notification handling works differently depending on your platform and setup. Choose the approach that matches your integration: For iOS, foreground notification handling is the same as the native Swift integration. Call `handleForegroundNotification(notification:)` inside your `UNUserNotificationCenterDelegate.userNotificationCenter(_:willPresent:withCompletionHandler:)` implementation. For complete details and code examples, see [Handling foreground notifications](https://www.braze.com/docs/ko/ko/developer_guide/push_notifications/?sdktab=swift#handling-foreground-notifications) in the Swift push notifications documentation. For Android, foreground notification handling is the same as the native Android integration. Call `BrazeFirebaseMessagingService.handleBrazeRemoteMessage` inside your `FirebaseMessagingService.onMessageReceived` method. For complete details and code examples, see [Handling foreground notifications](https://www.braze.com/docs/ko/ko/developer_guide/push_notifications/?sdktab=android#handling-foreground-notifications) in the Android push notifications documentation. In Expo-managed workflow, you don't call native notification handlers directly. Instead, use the Expo Notifications API to control foreground presentation, while the Braze Expo Plugin handles native processing automatically. ```javascript import * as Notifications from 'expo-notifications'; import Braze from '@braze/react-native-sdk'; // Control foreground presentation in Expo Notifications.setNotificationHandler({ handleNotification: async () => ({ shouldShowAlert: true, // Show alert while in foreground shouldPlaySound: false, shouldSetBadge: false, }), }); // React to Braze push events const subscription = Braze.addListener('pushNotificationEvent', (event) => { console.log('Braze push event', { type: event.payload_type, // "push_received" | "push_opened" title: event.title, url: event.url, is_silent: event.is_silent, }); // Handle deep links, custom behavior, etc. }); // Handle initial payload when app launches via push Braze.getInitialPushPayload((payload) => { if (payload) { console.log('Initial push payload', payload); } }); ``` **Note:** In Expo-managed workflow, the Braze Expo Plugin handles native push processing automatically. You control foreground UI via the Expo Notifications presentation options shown above. For bare workflow integrations, follow the native iOS and Android approaches instead. ### Step 5: Send a test push notification At this point, you should be able to send notifications to the devices. Adhere to the following steps to test your push integration. **Note:** Starting in macOS 13, on certain devices, you can test iOS push notifications on an iOS 16+ simulator running on Xcode 14 or higher. For further details, refer to the [Xcode 14 Release Notes](https://developer.apple.com/documentation/xcode-release-notes/xcode-14-release-notes). 1. Set an active user in the React Native application by calling `Braze.changeUserId('your-user-id')` method. 2. Head to **Campaigns** and create a new push notification campaign. Choose the platforms that you'd like to test. 3. Compose your test notification and head over to the **Test** tab. Add the same `user-id` as the test user and click **Send Test**. You should receive the notification on your device shortly. ![A Braze push campaign showing you can add your own user ID as a test recipient to test your push notification.](https://www.braze.com/docs/ko/ko/assets/img/react-native/push-notification-test.png?567f5b19e26e7493613a19f9d1204549 "Push Campaign Test") ## Using the Expo plugin After you [set up push notifications for Expo](#reactnative_setting-up-push-notifications), you can use it to handle the following push notifications behaviors—without needing to write any code in the native Android or iOS layers. ### Forwarding Android push to additional FMS If you want to use an additional Firebase Messaging Service (FMS), you can specify a fallback FMS to call if your application receives a push that isn't from Braze. For example: ```json { "expo": { "plugins": [ [ "@braze/expo-plugin", { ... "androidFirebaseMessagingFallbackServiceEnabled": true, "androidFirebaseMessagingFallbackServiceClasspath": "com.company.OurFirebaseMessagingService" } ] ] } } ``` ### Using app extensions with Expo Application Services {#app-extensions} If you are using Expo Application Services (EAS) and have enabled `enableBrazeIosRichPush` or `enableBrazeIosPushStories`, you will need to declare the corresponding bundle identifiers for each app extension in your project. There are multiple ways you can approach this step, depending on how your project is configured to manage code signing with EAS. One approach is to use the `appExtensions` configuration in your `app.json` file by following Expo's [app extensions documentation](https://docs.expo.dev/build-reference/app-extensions/). Alternatively, you can set up the `multitarget` setting in your `credentials.json` file by following Expo's [local credentials documentation](https://docs.expo.dev/app-signing/local-credentials/#multi-target-project). ### Troubleshooting These are common troubleshooting steps for push notification integrations with the Braze React Native SDK and Expo plugin. #### Push notifications stopped working {#troubleshooting-stopped-working} If push notifications through the Expo plugin have stopped working: 1. Check that the Braze SDK is still tracking sessions. 2. Check that the SDK wasn't disabled by an explicit or implicit call to `wipeData`. 3. Review any recent upgrades to Expo or it's related libraries, as there may be conflicts with your Braze configuration. 4. Review recently added project dependencies and check if they are manually overriding your existing push notification delegate methods. **Tip:** For iOS integrations, you can also reference our [push notification setup tutorial](https://braze-inc.github.io/braze-swift-sdk/tutorials/braze/b1-standard-push-notifications) to help you identify potential conflicts with your project dependencies. #### Device token won't register with Braze {#troubleshooting-token-registration} If your device token won't register with Braze, first review [Push notifications stopped working](#troubleshooting-stopped-working). If your issue persists, there may be a separate dependency interfering with your Braze push notification configuration. You can try removing it or manually call `Braze.registerPushToken` instead. #### Deep links from push notifications don't open {#troubleshooting-deep-links} If deep links from push notifications stop opening after a migration, check the following: 1. Verify your [React Native Linking](https://reactnative.dev/docs/linking) setup is still valid in your upgraded app. 2. For iOS native integrations, confirm you implemented `populateInitialPayloadFromLaunchOptions` and `Braze.getInitialPushPayload` so that, when the app is launched from a terminated state, it can retrieve the initial push payload and pass its `url` into your deep link handler. 3. If you're using the Braze Expo plugin, verify `androidHandlePushDeepLinksAutomatically` is set correctly for your implementation. 4. Review recently added dependencies for overrides to notification handling or app delegate behavior. If you've completed these checks and the issue persists, [open a support ticket](https://www.braze.com/docs/ko/ko/user_guide/administrative/access_braze/support/) and include SDK logs plus reproduction steps. ## Prerequisites Before you can use this feature, you'll need to [integrate the Web Braze SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=web). You'll also need to [set up push notifications](https://www.braze.com/docs/ko/ko/developer_guide/push_notifications/?sdktab=web) for the Web SDK. Note that you can only send push notifications to iOS and iPadOS users that are using [Safari v16.4](https://developer.apple.com/documentation/safari-release-notes/safari-16_4-release-notes) or later. ## Setting up Safari push for mobile ### Step 1: Create a manifest file {#manifest} A [Web Application Manifest](https://developer.mozilla.org/en-US/docs/Web/Manifest) is a JSON file that controls how your website is presented when installed to a user's home screen. For example, you can set the background theme color and icon that the [App Switcher](https://support.apple.com/en-us/HT202070) uses, whether it renders as full screen to resemble a native app, or whether the app should open in landscape or portrait mode. Create a new `manifest.json` file in your website's root directory, with the following mandatory fields. ```json { "name": "your app name", "short_name": "your app name", "display": "fullscreen", "icons": [{ "src": "favicon.ico", "sizes": "128x128", }] } ``` The full list of supported fields can be found [here](https://developer.mozilla.org/en-US/docs/Web/Manifest). ### Step 2: Link the manifest file {#manifest-link} Add the following `` tag to your website's `` element pointing to where your manifest file is hosted. ```html ``` ### Step 3: Add a service worker {#service-worker} Your website must have a service worker file that imports the Braze service-worker library, as described in our [web push integration guide](https://www.braze.com/docs/ko/ko/developer_guide/platform_integration_guides/web/push_notifications/integration/#step-1-configure-your-sites-service-worker). ### Step 4: Add to home screen {#add-to-homescreen} Popular browsers (such as Safari, Chrome, FireFox, and Edge) all support web push notifications in their later versions. To request push permission on iOS or iPadOS, your website must be added to the user's home screen by selecting **Share To** > **Add to Home Screen**. [Add to Homescreen](https://support.apple.com/guide/iphone/bookmark-favorite-webpages-iph42ab2f3a7/ios#iph4f9a47bbc) lets users bookmark your website, adding your icon to their valuable home screen real estate. ![An iPhone showing options to bookmark a website and save to the home screen](https://www.braze.com/docs/ko/ko/assets/img/push_implementation_guide/add-to-homescreen.png?f05fb625cce85d8d4b4816deae375bf8){: style="max-width:40%"} ### Step 5: Show the native push prompt {#push-prompt} After the app has been added to your home screen you can now request push permission when the user takes an action (such as clicking a button). This can be done using the [`requestPushPermission`](https://js.appboycdn.com/web-sdk/latest/doc/modules/braze.html#requestpushpermission) method, or with a [no-code push primer in-app message](https://www.braze.com/docs/ko/ko/user_guide/message_building_by_channel/push/best_practices/push_primer_messages/). **Note:** After you accept or decline the prompt, you need to delete and reinstall the website to your home screen to be able to show the prompt again. ![A push prompt asking to "allow" or "don't allow" Notifications](https://www.braze.com/docs/ko/ko/assets/img/push_implementation_guide/safari-mobile-push-prompt.png?f6652f453a2f06d6b173c92d3cd85dc1){: style="max-width:40%"} For example: ```typescript import { requestPushPermission } from "@braze/web-sdk"; button.onclick = function(){ requestPushPermission(() => { console.log(`User accepted push prompt`); }, (temporary) => { console.log(`User ${temporary ? "temporarily dismissed" : "permanently denied"} push prompt`); }); }; ``` ## Next steps Next, send yourself a [test message](https://www.braze.com/docs/ko/ko/developer_guide/in_app_messages/sending_test_messages/) to validate the integration. After your integration is complete, you can use our [no-code push primer messages](https://www.braze.com/docs/ko/ko/user_guide/message_building_by_channel/push/best_practices/push_primer_messages/) to optimize your push opt-in rates. ## Prerequisites Before you can use this feature, you'll need to [integrate the Unity Braze SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=unity). ## Setting up push notification ### Step 1: Set up the platform #### Step 1.1: Enable Firebase To get started, follow the [Firebase Unity setup documentation](https://firebase.google.com/docs/unity/setup). **Note:** Integrating the Firebase Unity SDK may cause your `AndroidManifest.xml` to be overridden. If that occurs, make sure to revert it to the original. #### Step 1.2: Set your Firebase credentials You need to input your Firebase Server Key and Sender ID into the Braze dashboard. To do this, log in to the [Firebase Developers Console](https://console.firebase.google.com/) and select your Firebase project. Next, select **Cloud Messaging** under **Settings** and copy the Server Key and Sender ID:
![](https://www.braze.com/docs/ko/ko/assets/img_archive/finding_firebase_server_key.png?de34d7ce2b1ae4b9c4a9d543b5b40585 "FirebaseServerKey") In Braze, select your Android app on the **App Settings** page under **Manage Settings**. Next, enter your Firebase Server Key in the **Firebase Cloud Messaging Server Key** field and Firebase Sender ID in the **Firebase Cloud Messaging Sender** ID field. ![](https://www.braze.com/docs/ko/ko/assets/img_archive/fcm_api_insert.png?f02bb98f5a702d5268f5ddaeee0c27cc "FCMKey") #### Step 1.1: Verify integration method Braze provides a native Unity solution for automating iOS push integrations. If you you'd like to set up and manage your integration manually instead, see [Swift: Push Notifications](https://www.braze.com/docs/ko/ko/developer_guide/push_notifications/?sdktab=swift). Otherwise, continue to the next step. **Note:** Our automatic push notification solution takes advantage of iOS 12's Provisional Authorization feature and is not available to use with the native push prompt pop-up. #### Step 1.1: Enable ADM 1. Create an account with the [Amazon Apps & Games Developer Portal](https://developer.amazon.com/public) if you have not already done so. 2. Obtain [OAuth credentials (Client ID and Client Secret) and an ADM API key](https://developer.amazon.com/public/apis/engage/device-messaging/tech-docs/02-obtaining-adm-credentials). 3. Enable **Automatic ADM Registration Enabled** in the Unity Braze Configuration window. - Alternatively, you may add the following line to your `res/values/braze.xml` file to enable ADM registration: ```xml true ``` ### Step 2: Configure push notifications #### Step 2.1: Configure push settings {#unity_step-21-configure-push-settings} The Braze SDK can automatically handle push registration with the Firebase Cloud Messaging Servers to have devices receive push notifications. In Unity, enable **Automate Unity Android Integration**, then configure the following **Push Notification** settings. | Setting | Description | |----------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------| | Automatic Firebase Cloud Messaging Registration Enabled | Instructs the Braze SDK to automatically retrieve and send an FCM push token for a device. | | Firebase Cloud Messaging Sender ID | The Sender ID from your Firebase console. | | Handle Push Deeplinks Automatically | Whether the SDK should handle opening deep links or opening the app when push notifications are clicked. | | Small Notification Icon Drawable | Android drawable resource reference for the small icon shown when a push arrives. Enter the full reference including the `@drawable/` prefix (for example, `@drawable/hourglass_icon`). The automated integration writes this value into `braze.xml` as entered. If you leave this empty, the notification uses the application icon as the small icon. | | Large Notification Icon Drawable | Optional large icon for notifications. Use the same `@drawable/` format as the small icon (for example, `@drawable/my_large_icon`). | {: .reset-td-br-1 .reset-td-br-2 aria-label="Step 2.1: Configure push settings" } **Note:** **Small Notification Icon Drawable** and **Large Notification Icon Drawable** appear under **Push Configuration** in **Braze > Braze Configuration**. Both values are written into `braze.xml` as you enter them. Include the `@drawable/` prefix yourself—the Braze Unity integration does not add it for you (for example, `@drawable/hourglass_icon`). #### Step 2.1: Upload your APNs token Before you can send an iOS push notification using Braze, you need to upload your `.p8` push notification file, as described in [Apple's developer documentation](https://developer.apple.com/documentation/usernotifications/establishing-a-token-based-connection-to-apns): 1. In your Apple developer account, go to [**Certificates, Identifiers & Profiles**](https://developer.apple.com/account/ios/certificate). 2. Under **Keys**, select **All** and click the add button (+) in the upper-right corner. 3. Under **Key Description**, enter a unique name for the signing key. 4. Under **Key Services**, select the **Apple Push Notification service (APNs)** checkbox, then click **Continue**. Click **Confirm**. 5. Note the key ID. Click **Download** to generate and download the key. Make sure to save the downloaded file in a secure place, as you cannot download this more than once. 6. In Braze, go to **Settings** > **App Settings** and upload the `.p8` file under **Apple Push Certificate**. You can upload either your development or production push certificate. To test push notifications after your app is live in the App Store, its recommended to set up a separate workspace for the development version of your app. 7. When prompted, enter your app's [bundle ID](https://developer.apple.com/documentation/foundation/nsbundle/1418023-bundleidentifier), [key ID](https://developer.apple.com/help/account/manage-keys/get-a-key-identifier/), and [team ID](https://developer.apple.com/help/account/manage-your-team/locate-your-team-id). You'll also need to specify whether to send notifications to your app's development or production environment, which is defined by its provisioning profile. 8. When you're finished, select **Save**. #### Step 2.2: Enable automatic push Open the Braze Configuration Settings in the Unity Editor by navigating to **Braze > Braze Configuration**. Check **Integrate Push With Braze** to automatically register users for push notifications, pass push tokens to Braze, track analytics for push opens, and take advantage of our default push notification handling. #### Step 2.3: Enable background push (optional) Check **Enable Background Push** if you want to enable `background mode` for push notifications. This allows the system to wake your application from the `suspended` state when a push notification arrives, enabling your application to download content in response to push notifications. Checking this option is required for our uninstall tracking functionality. ![The Unity editor shows the Braze configuration options. In this editor, the "Automate Unity iOS integration", "Integrate push with braze", and "Enable background push" are enabled.](https://www.braze.com/docs/ko/ko/assets/img/unity/ios/unity_ios_enable_background.png?df3d5a5cc6379f663c3451fe060d71e6) #### Step 2.4: Disable automatic registration (optional) Users who have not yet opted-in to push notifications will automatically be authorized for push upon opening your application. To disable this feature and manually register users for push, check **Disable Automatic Push Registration**. - If **Disable Provisional Authorization** is not checked on iOS 12 or later, the user will be provisionally (silently) authorized to receive quiet push. If checked, the user will be shown the native push prompt. - If you need to configure exactly when the prompt is shown at runtime, disable automatic registration from the Braze configuration editor and use `AppboyBinding.PromptUserForPushPermissions()` instead. ![The Unity editor shows the Braze configuration options. In this editor, the "Automate Unity iOS integration", "integrate push with braze", and "disable automatic push registration" are enabled.](https://www.braze.com/docs/ko/ko/assets/img/unity/ios/unity_ios_disable_auto_push.png?9699a7386b70856be28344c6b6d884ee) #### Step 2.1: Update `AndroidManifest.xml` If your app does not have an `AndroidManifest.xml`, you can use the following as a template. Otherwise, if you already have an `AndroidManifest.xml`, ensure that any of the following missing sections are added to your existing `AndroidManifest.xml`. ```xml ``` #### Step 2.2: Store your ADM API key First, [generate an ADM API Key for your app](https://developer.amazon.com/public/apis/engage/device-messaging/tech-docs/02-obtaining-adm-credentials), then save the key to a file named `api_key.txt` and add it in your project's [`Assets/`](https://docs.unity3d.com/Manual/AndroidAARPlugins.html) directory. **Important:** Amazon will not recognize your key if `api_key.txt` contains any white space characters, such as a trailing line break. Next, in your `mainTemplate.gradle` file, add the following: ```gradle task copyAmazon(type: Copy) { def unityProjectPath = $/file:///**DIR_UNITYPROJECT**/$.replace("\\", "/") from unityProjectPath + '/Assets/api_key.txt' into new File(projectDir, 'src/main/assets') } preBuild.dependsOn(copyAmazon) ``` #### Step 2.3: Add ADM Jar The required ADM Jar file may be placed anywhere in your project according to the [Unity JAR documentation](https://docs.unity3d.com/Manual/AndroidJARPlugins.html). #### Step 2.4: Add Client Secret and Client ID to your Braze dashboard Lastly, you must add the Client Secret and Client ID you obtained in [Step 1](#unity_step-1-enable-adm) to the Braze dashboard's **Manage Settings** page. ![](https://www.braze.com/docs/ko/ko/assets/img_archive/fire_os_dashboard.png?725b1fd9d8208f4a861323e5fc16a376) ### Step 3: Set push listeners #### Step 3.1: Enable push received listener The push received listener is fired when a user receives a push notification. To send the push payload to Unity, set the name of your game object and push the received listener callback method under the **Set Push Received Listener**. #### Step 3.2: Enable push opened listener The push opened listener is fired when a user launches the app by clicking on a push notification. To send the push payload to Unity, set the name of your game object and push opened listener callback method under the **Set Push Opened Listener**. #### Step 3.3: Enable push deleted listener The push deleted listener is fired when a user swipes away or dismisses a push notification. To send the push payload to Unity, set the name of your game object and push deleted listener callback method under the **Set Push Deleted Listener**. #### Push listener example The following example implements the `BrazeCallback` game object using a callback method name of `PushNotificationReceivedCallback`, `PushNotificationOpenedCallback`, and `PushNotificationDeletedCallback` respectively. ![This implementation example graphic shows the Braze configuration options mentioned in the preceding sections and a C# code snippet.](https://www.braze.com/docs/ko/ko/assets/img/unity/android/unity_android_full_push_listener.png?c39b6b947880ebfe57b14dd66a2e6b73 "Android Full Listener Example") ```csharp public class MainMenu : MonoBehaviour { void PushNotificationReceivedCallback(string message) { #if UNITY_ANDROID Debug.Log("PushNotificationReceivedCallback message: " + message); PushNotification pushNotification = new PushNotification(message); Debug.Log("Push Notification received: " + pushNotification); #elif UNITY_IOS ApplePushNotification pushNotification = new ApplePushNotification(message); Debug.Log("Push received Notification event: " + pushNotification); #endif } void PushNotificationOpenedCallback(string message) { #if UNITY_ANDROID Debug.Log("PushNotificationOpenedCallback message: " + message); PushNotification pushNotification = new PushNotification(message); Debug.Log("Push Notification opened: " + pushNotification); #elif UNITY_IOS ApplePushNotification pushNotification = new ApplePushNotification(message); Debug.Log("Push opened Notification event: " + pushNotification); #endif } void PushNotificationDeletedCallback(string message) { #if UNITY_ANDROID Debug.Log("PushNotificationDeletedCallback message: " + message); PushNotification pushNotification = new PushNotification(message); Debug.Log("Push Notification dismissed: " + pushNotification); #endif } } ``` #### Step 3.1: Enable push received listener The push received listener is fired when a user receives a push notification while actively using the application (such as when the app is foregrounded). Set the push received listener in the Braze configuration editor. If you need to configure your game object listener at runtime, use `AppboyBinding.ConfigureListener()` and specify `BrazeUnityMessageType.PUSH_RECEIVED`. ![The Unity editor shows the Braze configuration options. In this editor, the "Set Push Received Listener" option is expanded, and the "Game Object Name" (AppBoyCallback) and "Callback Method Name" (PushNotificationReceivedCallback) are provided.](https://www.braze.com/docs/ko/ko/assets/img/unity/ios/unity_ios_push_received.png?c740dcf00019a0c74d243db9dfda0bfc) #### Step 3.2: Enable push opened listener The push opened listener is fired when a user launches the app by clicking on a push notification. To send the push payload to Unity, set the name of your game object and push opened listener callback method under the **Set Push Opened Listener** option: ![The Unity editor shows the Braze configuration options. In this editor, the "Set Push Received Listener" option is expanded, and the "Game Object Name" (AppBoyCallback) and "Callback Method Name" (PushNotificationOpenedCallback) are provided.](https://www.braze.com/docs/ko/ko/assets/img/unity/ios/unity_ios_push_opened.png?ac12ca0b1e4ab041389ac74ccac979c6) If you need to configure your game object listener at runtime, use `AppboyBinding.ConfigureListener()` and specify `BrazeUnityMessageType.PUSH_OPENED`. #### Push listener example The following example implements the `AppboyCallback` game object using a callback method name of `PushNotificationReceivedCallback` and `PushNotificationOpenedCallback`, respectively. ![This implementation example graphic shows the Braze configuration options mentioned in the preceding sections and a C# code snippet.](https://www.braze.com/docs/ko/ko/assets/img/unity/ios/unity_ios_appboy_callback.png?aae6baaf6053cd5ff3c6c512a94bfcfe) ```csharp public class MainMenu : MonoBehaviour { void PushNotificationReceivedCallback(string message) { #if UNITY_ANDROID Debug.Log("PushNotificationReceivedCallback message: " + message); PushNotification pushNotification = new PushNotification(message); Debug.Log("Push Notification received: " + pushNotification); #elif UNITY_IOS ApplePushNotification pushNotification = new ApplePushNotification(message); Debug.Log("Push received Notification event: " + pushNotification); #endif } void PushNotificationOpenedCallback(string message) { #if UNITY_ANDROID Debug.Log("PushNotificationOpenedCallback message: " + message); PushNotification pushNotification = new PushNotification(message); Debug.Log("Push Notification opened: " + pushNotification); #elif UNITY_IOS ApplePushNotification pushNotification = new ApplePushNotification(message); Debug.Log("Push opened Notification event: " + pushNotification); #endif } } ``` By updating your `AndroidManifest.xml` in the [previous step](#unity_step-21-update-androidmanifestxml), push listeners were automatically set up when you added the following lines. So, no further setup is required. ```xml ``` **Note:** To learn more about ADM push listeners, see [Amazon: Integrate Amazon Device Messaging](https://developer.amazon.com/docs/video-skills-fire-tv-apps/integrate-adm.html). ## Optional configurations #### Deep linking to in-app resources Although Braze can handle standard deep links (such as website URLs, Android URIs, etc.) by default, creating custom deep links requires an additional Manifest setup. For setup guidance, visit [Deep Linking to In-App Resources](https://developer.android.com/training/app-links/deep-linking). #### Adding Braze push notification icons **Important:** Do not add notification icon images under `Assets/Plugins/Android/res`. Unity [deprecated providing Android resources in that path](https://support.unity.com/hc/en-us/articles/115005875443-Providing-Android-resources-in-Assets-Plugins-Android-res-is-deprecated), which can surface build warnings or validation errors. Package your icon drawables in an [Android Archive (AAR) plug-in](https://docs.unity3d.com/Manual/AndroidAARPlugins.html) or Android library project instead so they merge into the built app's resources like any other drawable. To add push icons to your project, create an AAR plug-in or Android library that contains the icon image files under `res/drawable*` (or density-specific folders), then reference each icon in **Braze > Braze Configuration** using the full `@drawable/` resource name (see [Step 2.1: Configure push settings](#unity_step-21-configure-push-settings)). For Unity's packaging and import steps, see [Android Library Projects and Android Archive plug-ins](https://docs.unity3d.com/Manual/AndroidAARPlugins.html). For small icon artwork rules (alpha-only, no color), see [Android push notifications](https://www.braze.com/docs/ko/ko/developer_guide/push_notifications/?sdktab=android), Step 2: Conform small icons to design guidelines. #### Push token callback To receive a copy of Braze device tokens from the OS, set a delegate using `AppboyBinding.SetPushTokenReceivedFromSystemDelegate()`. There are no optional configurations for ADM at this time. ## Prerequisites Before you can use this feature, you'll need to [integrate the .NET MAUI Braze SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=.net%20maui%20(xamarin)). ## Setting up push notifications **Tip:** To see how namespaces change between Java and C#, check out our [Xample sample app on GitHub](https://github.com/braze-inc/braze-xamarin-sdk/tree/master/appboy-component/samples/android-net-maui/BrazeAndroidMauiSampleApp/BrazeAndroidMauiSampleApp). To integrate push notifications for .NET MAUI (formerly Xamarin), you'll need to complete the steps for native Android push notifications. The following steps are only a summary. For a full walkthrough, see the [native push notification guide](https://www.braze.com/docs/ko/ko/developer_guide/platform_integration_guides/android/push_notifications/?tab=android/). ### Step 1: Update your project 1. Add Firebase to your Android project. 2. Add the Cloud Messaging library to your Android project's `build.gradle`: ```gradle implementation "google.firebase:firebase-messaging:+" ``` ### Step 2: Create your JSON credentials 1. In Google Cloud, enable the [Firebase Cloud Messaging API](https://console.cloud.google.com/apis/library/fcm.googleapis.com). 2. Select **Service Accounts** > your project > **Create Service Account**, then enter a service account name, ID, and description. When you're finished, select **Create and continue**. 3. In the **Role** field, find and select **Firebase Cloud Messaging API Admin** from the list of roles. 4. In **Service Accounts**, choose your project, then select  **Actions** > **Manage Keys** > **Add Key** > **Create new key**. Choose **JSON**, then select **Create**. ### Step 3: Upload your JSON credentials 1. In Braze, select  **Settings** > **App Settings**. Under your Android app's **Push Notification Settings**, choose **Firebase**, then select **Upload JSON File** and upload the credentials you generated earlier. When you're finished, select **Save**. 2. Enable automatic FCM token registration, by going to Firebase Console. Open your project, then select  **Settings** > **Project settings**. Select **Cloud Messaging**, then under **Firebase Cloud Messaging API (V1)**, copy the number in the **Sender ID** field. 3. In your Android Studio project and the following to your `braze.xml`. ```xml true FIREBASE_SENDER_ID ``` **Important:** To prevent Braze from triggering unnecessary network requests every time you send silent push notifications, remove any automatic network requests configured in your `Application` class's `onCreate()` method. For more information see, [Android Developer Reference: Application](https://developer.android.com/reference/android/app/Application). ### Step 1: Complete the initial setup See the [Swift integration instructions](https://www.braze.com/docs/ko/ko/developer_guide/push_notifications/?sdktab=swift) for information about setting up your application with push and storing your credentials on our server. Refer to the [iOS MAUI](https://github.com/braze-inc/braze-xamarin-sdk/tree/master/appboy-component/samples/ios-net-maui/BrazeiOSMauiSampleApp) sample application for more details. ### Step 2: Request push notifications permission Our .NET MAUI SDK now supports automatic push set up. Set up push automation and permissions by adding the following code to your Braze instance configuration: ```csharp configuration.Push.Automation = new BRZConfigurationPushAutomation(true); configuration.Push.Automation.RequestAuthorizationAtLaunch = false; ``` Refer to the [iOS MAUI](https://github.com/braze-inc/braze-xamarin-sdk/tree/master/appboy-component/samples/ios-net-maui/BrazeiOSMauiSampleApp) sample application for more details. For more details, see the Xamarin documentation for [Enhanced User Notifications in Xamarin.iOS](https://learn.microsoft.com/en-us/previous-versions/xamarin/ios/platform/user-notifications/enhanced-user-notifications?tabs=macos). # Braze SDK용 푸시 알림 커스텀 설정 Source: /docs/ko/developer_guide/push_notifications/customization/index.md # 푸시 알림 커스텀 설정 > Braze SDK의 푸시 알림을 사용자 지정하는 방법을 알아보세요. ## Prerequisites Before you can use this feature, you'll need to [integrate the Android Braze SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=android). You'll also need to [set up push notifications](https://www.braze.com/docs/ko/ko/developer_guide/push_notifications/?sdktab=android). ## Using a callback for push events {#push-callback} Braze provides a [`subscribeToPushNotificationEvents()`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze/-i-braze/subscribe-to-push-notification-events.html) callback for when push notifications are received, opened, or dismissed. It is recommended to place this callback in your `Application.onCreate()` in order to not miss any events occurring while your application is not running. **Note:** If previously using a Custom Broadcast Receiver for this functionality in your application, you can safely remove it in favor of this integration option. ```java Braze.getInstance(context).subscribeToPushNotificationEvents(event -> { final BrazeNotificationPayload parsedData = event.getNotificationPayload(); // // The type of notification itself // final boolean isPushOpenEvent = event.getEventType() == BrazePushEventType.NOTIFICATION_OPENED; final boolean isPushReceivedEvent = event.getEventType() == BrazePushEventType.NOTIFICATION_RECEIVED; // Sent when a user has dismissed a notification final boolean isPushDeletedEvent = event.getEventType() == BrazePushEventType.NOTIFICATION_DELETED; // // Notification data // final String pushTitle = parsedData.getTitleText(); final Long pushArrivalTimeMs = parsedData.getNotificationReceivedTimestampMillis(); final String deeplink = parsedData.getDeeplink(); // // Custom KVP data // final String myCustomKvp1 = parsedData.getBrazeExtras().getString("my first kvp"); final String myCustomKvp2 = parsedData.getBrazeExtras().getString("my second kvp"); }); ``` ```kotlin Braze.getInstance(context).subscribeToPushNotificationEvents { event -> val parsedData = event.notificationPayload // // The type of notification itself // val isPushOpenEvent = event.eventType == BrazePushEventType.NOTIFICATION_OPENED val isPushReceivedEvent = event.eventType == BrazePushEventType.NOTIFICATION_RECEIVED // Sent when a user has dismissed a notification val isPushDeletedEvent = event.eventType == BrazePushEventType.NOTIFICATION_DELETED // // Notification data // val pushTitle = parsedData.titleText val pushArrivalTimeMs = parsedData.notificationReceivedTimestampMillis val deeplink = parsedData.deeplink // // Custom KVP data // val myCustomKvp1 = parsedData.brazeExtras.getString("my first kvp") val myCustomKvp2 = parsedData.brazeExtras.getString("my second kvp") } ``` **Tip:** With notification action buttons, `BRAZE_PUSH_INTENT_NOTIFICATION_OPENED` intents fire when buttons with `opens app` or `deep link` actions are clicked. Deep link and extras handling remains the same. Buttons with `close` actions don't fire `BRAZE_PUSH_INTENT_NOTIFICATION_OPENED` intents and dismiss the notification automatically. **Important:** Create your push notification listener in `Application.onCreate` to ensure your listener is triggered after an end-user taps a notification while your app is in a terminated state. ## Customizing notification display {#customization-display} ### Step 1: Create your custom notification factory In some scenarios, you may wish to customize push notifications in ways that would be cumbersome or unavailable server side. To give you complete control of notification display, we've added the ability to define your own [`IBrazeNotificationFactory`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze/-i-braze-notification-factory/index.html) to create notification objects for display by Braze. If a custom `IBrazeNotificationFactory` is set, Braze will call your factory's `createNotification()` method upon push receipt before the notification is displayed to the user. Braze will pass in a `Bundle` containing Braze push data and another `Bundle` containing custom key-value pairs sent either via the dashboard or the messaging APIs: Braze will pass in a [`BrazeNotificationPayload`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.push/-braze-notification-payload/index.html) containing data from the Braze push notification. ```java // Factory method implemented in your custom IBrazeNotificationFactory @Override public Notification createNotification(BrazeNotificationPayload brazeNotificationPayload) { // Example of getting notification title String title = brazeNotificationPayload.getTitleText(); // Example of retrieving a custom KVP ("my_key" -> "my_value") String customKvp = brazeNotificationPayload.getBrazeExtras().getString("my_key"); } ``` ```kotlin // Factory method implemented in your custom IBrazeNotificationFactory override fun createNotification(brazeNotificationPayload: BrazeNotificationPayload): Notification { // Example of getting notification title val title = brazeNotificationPayload.getTitleText() // Example of retrieving a custom KVP ("my_key" -> "my_value") val customKvp = brazeNotificationPayload.getBrazeExtras().getString("my_key") } ``` You can return `null` from your custom `createNotification()` method to not show the notification at all, use `BrazeNotificationFactory.getInstance().createNotification()` to obtain our default `notification` object for that data and modify it before display, or generate a completely separate `notification` object for display. **Note:** For documentation on Braze push data keys, refer to the [Android SDK](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze/-constants/index.html). ### Step 2: Set your custom notification factory To instruct Braze to use your custom notification factory, use the `setCustomBrazeNotificationFactory` method to set your [`IBrazeNotificationFactory`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze/-i-braze-notification-factory/index.html): ```java setCustomBrazeNotificationFactory(IBrazeNotificationFactory brazeNotificationFactory); ``` ```kotlin setCustomBrazeNotificationFactory(brazeNotificationFactory: IBrazeNotificationFactory) ``` The recommended place to set your custom `IBrazeNotificationFactory` is in the `Application.onCreate()` application lifecycle method (not activity). This will allow the notification factory to be set correctly whenever your app process is active. **Important:** Creating your own notification from scratch is an advanced use case and should be done only with thorough testing and a deep understanding of the Braze push functionality. For example, you must make sure your notification logs push opens correctly. To unset your custom [`IBrazeNotificationFactory`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze/-i-braze-notification-factory/index.html) and return to default Braze handling for push, pass in `null` to our custom notification factory setter: ```java setCustomBrazeNotificationFactory(null); ``` ```kotlin setCustomBrazeNotificationFactory(null) ``` ## Rendering multicolor text In Braze SDK version 3.1.1, HTML can be sent to a device to render multicolor text in push notifications. ![An Android push message "Multicolor Push test message" where the letters are different colors, italicized and given a background color.](https://www.braze.com/docs/ko/ko/assets/img/multicolor_android_push.png?5a515501661c5953a76737951378e789){: style="max-width:40%;"} This example is rendered with the following HTML: ```html

MultiColor Push

test message

``` Keep in mind that, Android limits which HTML elements and tags are valid in your push notifications. For example, `marquee` is not allowed. **Important:** Multicolor text rendering is device-specific and may not display based on Android device or version. To render multicolor text in a push notification, you can update your `braze.xml` or `BrazeConfig`: Add the following in your `braze.xml`: ```xml true ``` Add the following in your [`BrazeConfig`](https://www.braze.com/docs/ko/ko/developer_guide/platform_integration_guides/android/advanced_use_cases/runtime_configuration/#runtime-configuration): ```java BrazeConfig brazeConfig = new BrazeConfig.Builder() .setPushHtmlRenderingEnabled(true) .build(); Braze.configure(this, brazeConfig); ``` ```kotlin val brazeConfig = BrazeConfig.Builder() .setPushHtmlRenderingEnabled(true) .build() Braze.configure(this, brazeConfig) ``` ### Supported HTML tags Currently, Google doesn't list their supported HTML tags for Android directly in their documentation—this information can only be found in their [Git repository's `Html.java` file](https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/text/Html.java). Keep this in mind when referencing the following table, as this information was pulled from this file, and their supported HTML tags could be subject to change.
Category HTML Tag Description
Basic Text Styling <b>, <strong> Bold text
<i>, <em> Italic text
<u> Underline text
<s>, <strike>, <del> Strikethrough text
<sup> Superscript text
<sub> Subscript text
<tt> Monospace text
Size/Font <big>, <small> Relative text size changes
<font color="..."> Sets foreground color
<span> (with inline CSS) Inline styles (e.g., color, background)
Paragraph & Block <p>, <div> Block-level sections
<br> Line break
<blockquote> Quoted block
<ul> + <li> Unordered list with bullets
Headings <h1> - <h6> Headings (various sizes)
Links & Images <a href="..."> Clickable link
<img src="..."> Inline image
Other Inline <em>, <strong>, <dfn>, <cite> Synonyms for italic or bold
{: .reset-td-br-1 .reset-td-br-2 .reset-td-br-3 aria-label="Supported HTML tags" } ## Rendering inline images ### How it works You can showcase a larger image within your Android push notification using inline image push. With this design, users won't have to manually expand the push to enlarge the image. Unlike regular Android push notifications, inline image push images are in a 3:2 aspect ratio. ![](https://www.braze.com/docs/ko/ko/assets/img/android/push/inline_image_push_android_1.png?bf2ba99d9b6423b4d8d94e5d8d9c5908){: style="max-width:50%;"} ### Compatibility While you can send inline images to any device, devices and SDKs that don't meet the minimum versions will display a standard image instead. For inline images to display properly, both the Android Braze SDK v10.0.0+ and a device running Android M+ are required. The SDK must also be enabled for the image to render. **Note:** Devices running Android 12 will render differently due to changes in custom push notification styles. ### Sending an inline image push When creating an Android push message, this feature is available in the **Notification Type** dropdown. ![The push campaign editor showing the location of the "Notification Type" dropdown (above the standard push preview).](https://www.braze.com/docs/ko/ko/assets/img/android/push/android_inline_image_notification_type.png?fc31f4cadbcef37649e3b980d03ce868) ## Settings There are many advanced settings available for Android push notifications sent through the Braze dashboard. This article will describe these features and how to use them successfully. ![](https://www.braze.com/docs/ko/ko/assets/img_archive/android_advanced_settings.png?8131f34243617db90fdf5780cbc3cf33) ### Notification ID {#notification-id} A **Notification ID** is a unique identifier for a message category of your choosing that informs the messaging service to only respect the most recent message from that ID. Setting a notification ID allows you to send just the most recent and relevant message, rather than a stack of outdated, irrelevant ones. ### Firebase Messaging Delivery priority {#fcm-priority} The [Firebase Messaging Delivery Priority](https://firebase.google.com/docs/cloud-messaging/android/message-priority#setting-priority-for-messages) field lets you control whether a push is sent with "normal" or "high" priority to Firebase Cloud Messaging. ### Time to live (TTL) {#ttl} The **Time to Live** (TTL) field allows you to set a custom length of time to store messages with the push messaging service. The default values for time to live are four weeks for FCM and 31 days for ADM. ### Summary text {#summary-text} The summary text allows you to set additional text in the expanded notification view. It also serves as a caption for notifications with images. ![An Android message with the title "This is the title for the notification." and summary text "This is the summary text for the notification."](https://www.braze.com/docs/ko/ko/assets/img/android/push/collapsed-android-notification.png?fd42100702f714bd5cb65f742509c9b7){: style="max-width:65%;"} The summary text will display under the body of the message in the expanded view. ![An Android message with the title "This is the title for the notification." and summary text "This is the summary text for the notification."](https://www.braze.com/docs/ko/ko/assets/img/android/push/expanded-android-notification.png?18786f441d0d9d6b65bfcce34c43198d){: style="max-width:65%;"} For push notifications that include images, the message text will be shown in the collapsed view, while the summary text will be displayed as the image caption when the notification is expanded. ### Custom URIs {#custom-uri} The **Custom URI** feature allows you to specify a Web URL or an Android resource to navigate to when the notification is clicked. If no custom URI is specified, clicking on the notification brings users into your app. You can use the custom URI to deep link inside your app and direct users to resources that exist outside of your app. This can be specified via the [Messaging API](https://www.braze.com/docs/ko/ko/api/endpoints/messaging/) or our dashboard under **Advanced Settings** in the push composer as pictured: ![The deep linking advanced setting in the Braze push composer.](https://www.braze.com/docs/ko/ko/assets/img_archive/deep_link.png?30080909d43633ac9ca7ac8d115a686a) ### Notification display priority {#notification-priority} **Important:** The Notification Display Priority setting is no longer used on devices running Android O or newer. For newer devices, set the priority through [notification channel configuration](https://developer.android.com/training/notify-user/channels#importance). The priority level of a push notification affects how your notification is displayed in the notification tray relative to other notifications. It can also affect the speed and manner of delivery, as normal and lower priority messages may be sent with slightly higher latency or batched to preserve battery life, whereas high priority messages are always sent immediately. In Android O, notification priority became a property of notification channels. You will need to work with your developer to define the priority for a channel during its configuration and then use the dashboard to select the proper channel when sending your notification sounds. For devices running versions of Android before O, specifying a priority level for Android notifications is possible via the Braze dashboard and messaging API. To message your full user base with a specific priority, we recommend that you indirectly specify the priority through [notification channel configuration](https://developer.android.com/training/notify-user/channels#importance) (to target O+ devices) *and* send the individual priority from the dashboard (to target <O devices). The priority levels that you can set on Android or Fire OS push notifications are: | Priority | Description/Intended Use | `priority` value (for API messages) | |----------|--------------------------|-------------------------------------| | Max | Urgent or time-critical messages | `2` | | High | Important communication, such as a new message from a friend | `1` | | Default | Most notifications - use if your message doesn't explicitly fall under any of the other priority types | `0` | | Low | Information that you want users to know about but does not require immediate action | `-1` | | Min | Contextual or background information. | `-2` | {: .reset-td-br-1 .reset-td-br-2 .reset-td-br-3 aria-label="Notification display priority #notification-priority" } For more information, refer to Google's [Android notification](http://developer.android.com/design/patterns/notifications.html) documentation. ### Sounds {#sounds} In Android O, notification sounds became a property of notification channels. You will need to work with your developer to define the sound for a channel during its configuration and then use the dashboard to select the proper channel when sending your notifications. For devices running versions of Android before O, Braze allows you to set the sound of an individual push message through the dashboard composer. You can do so by specifying a local sound resource on the device (for example, `android.resource://com.mycompany.myapp/raw/mysound`). Specifying "default" in this field will play the default notification sound on the device. This can be specified via the [Messaging API](https://www.braze.com/docs/ko/ko/api/endpoints/messaging/) or the dashboard under **Advanced Settings** in the push composer. ![The sound advanced setting in the Braze push composer.](https://www.braze.com/docs/ko/ko/assets/img_archive/sound_android.png?70e53c2fdff6155d172b0399de090593) Enter the full sound resource URI (for example, `android.resource://com.mycompany.myapp/raw/mysound`) into the dashboard prompt. To message your full user base with a specific sound, we recommend that you indirectly specify the sound through [notification channel configuration](https://developer.android.com/training/notify-user/channels) (to target O+ devices) *and* send the individual sound from the dashboard (to target <O devices). ## Prerequisites Before you can use this feature, you'll need to [integrate the Swift Braze SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=swift). You'll also need to [set up push notifications](https://www.braze.com/docs/ko/ko/developer_guide/push_notifications/?sdktab=swift). ## Customizing action buttons {#push-action-buttons-integration} The Braze Swift SDK provides URL handling support for push action buttons. There are four sets of default push action buttons for Braze default push categories: `Accept/Decline`, `Yes/No`, `Confirm/Cancel`, and `More`. ![A GIF of a push message being pulled down to display two customizable action buttons.](https://www.braze.com/docs/ko/ko/assets/img_archive/iOS8Action.gif?d3553a68ae1aa0c3a58da9e65174f404){: style="max-width:60%"} ### Manually registering action buttons **Important:** Manually registering push action buttons are not recommended. If you [set up push notifications](https://www.braze.com/docs/ko/ko/developer_guide/push_notifications/?sdktab=swift) using the `configuration.push.automation` configuration option, Braze automatically registers the action buttons for the default push categories and handles the push action button click analytics and URL routing. However, you can choose to manually register push action buttons instead. #### Step 1: Adding Braze default push categories {#registering} Use the following code to register for the default push categories when you [register for push](https://www.braze.com/docs/ko/ko/developer_guide/platform_integration_guides/swift/push_notifications/integration/#step-4-register-push-tokens-with-braze): a ```swift UNUserNotificationCenter.current().setNotificationCategories(Braze.Notifications.categories) ``` ```objc [[UNUserNotificationCenter currentNotificationCenter] setNotificationCategories:BRZNotifications.categories]; ``` **Note:** Clicking on push action buttons with background activation mode will only dismiss the notification and not open the app. The next time the user opens the app, the button click analytics for these actions will be flushed to the server. #### Step 2: Enable interactive push handling {#enable-push-handling} To enable our push action button handling, including click analytics and URL routing, add the following code to your app's `didReceive(_:completionHandler:)` delegate method: ```swift AppDelegate.braze?.notifications.handleUserNotification(response: response, withCompletionHandler: completionHandler) ``` ```objc [AppDelegate.braze.notifications handleUserNotificationWithResponse:response withCompletionHandler:completionHandler]; ``` If you use the `UNNotification` framework and have implemented the Braze [notification methods](https://www.braze.com/docs/ko/ko/developer_guide/platform_integration_guides/swift/push_notifications/integration/#step-5-enable-push-handling), you should already have this method integrated. ## Customizing push categories {#customizing-push-categories} In addition to providing a set of default push categories, Braze supports custom notification categories and actions. After you register categories in your application, you can use the Braze dashboard to send these custom notification categories to your users. Here's an example that leverages the `LIKE_CATEGORY` displayed on the device: ![A push message displaying two push action buttons "unlike" and "like".](https://www.braze.com/docs/ko/ko/assets/img_archive/push_example_category.png?342eb7b8bc6d24142ee32606e22f8eee) ### Step 1: Register a category To register a category in your app, use a similar approach to the following: ```swift Braze.Notifications.categories.insert( .init(identifier: "LIKE_CATEGORY", actions: [ .init(identifier: "LIKE_IDENTIFIER", title: "Like", options: [.foreground]), .init(identifier: "UNLIKE_IDENTIFIER", title: "Unlike", options: [.foreground]) ], intentIdentifiers: [] ) ) UNUserNotificationCenter.current().setNotificationCategories(Braze.Notifications.categories) ``` ```objc NSMutableSet *categories = [BRZNotifications.categories mutableCopy]; UNNotificationAction *likeAction = [UNNotificationAction actionWithIdentifier:@"LIKE_IDENTIFIER" title:@"Like" options:UNNotificationActionOptionForeground]; UNNotificationAction *unlikeAction = [UNNotificationAction actionWithIdentifier:@"UNLIKE_IDENTIFIER" title:@"Unlike" options:UNNotificationActionOptionForeground]; UNNotificationCategory *likeCategory = [UNNotificationCategory categoryWithIdentifier:@"LIKE_CATEGORY" actions:@[likeAction, unlikeAction] intentIdentifiers:@[] options:UNNotificationCategoryOptionNone]; [categories addObject:likeCategory]; [UNUserNotificationCenter.currentNotificationCenter setNotificationCategories:categories]; ``` **Note:** When you create a `UNNotificationAction`, you can specify a list of action options. For example, `.foreground` lets your users open your app after tapping the action button. This is necessary for navigational on-click behaviors, such as "Open App" and "Deep Link into Application". If you want an action button that simply dismisses the notification without opening the app, leave `.foreground` out of the action's `options` array. For more information, see [`UNNotificationActionOptions`](https://developer.apple.com/documentation/usernotifications/unnotificationactionoptions). ### Step 2: Select your categories After you register a category, use the Braze dashboard to send notifications of that type to users. **Tip:** You only need to define action buttons on the Braze dashboard for behaviors that can't be created locally in your Swift code, such as deep linking into your app or redirecting to a web URL. These actions need to be configured on the dashboard so they can define what URL or deep link to open. For action buttons that simply dismiss the notification without opening the app, you don't need to configure them on the dashboard—dismissal behavior is handled automatically by iOS. Just register your custom category and its actions in your app code, then enter the matching category name on the dashboard. 1. In the Braze dashboard, select **Messaging** > **Push Notifications**, then choose your iOS [push campaign](https://www.braze.com/docs/ko/ko/user_guide/message_building_by_channel/push/creating_a_push_message). 2. Under **Compose push notification**, turn on **Action Buttons**. 3. In the **iOS Notification Category** dropdown, select **Enter pre-registered custom iOS Category**. 4. Finally, enter one of the categories you created earlier. The following example, uses the custom category: `LIKE_CATEGORY`. ![The push notification campaign dashboard with the setup for custom categories.](https://www.braze.com/docs/ko/ko/assets/img_archive/ios-notification-category.png?3773374b98a8bfab3549164f0b8eedd1) ### Example: Custom push category {#example-custom-push-category} Suppose you want to create a push notification with two action buttons: **Manage**, which deep links into your app, and **Keep**, which simply dismisses the notification. In the following example, the `MANAGE_IDENTIFIER` action includes the `.foreground` option, which opens the app when tapped—this is necessary because it will deep link into a specific part of the app. The `KEEP_IDENTIFIER` action uses an empty options array, meaning it will dismiss the notification without opening the app. ```swift Braze.Notifications.categories.insert( .init(identifier: "YOUR_CATEGORY", actions: [ .init(identifier: "KEEP_IDENTIFIER", title: "Keep", options: []), .init(identifier: "MANAGE_IDENTIFIER", title: "Manage", options: [.foreground]) ], intentIdentifiers: [] ) ) UNUserNotificationCenter.current().setNotificationCategories(Braze.Notifications.categories) ``` Because `MANAGE_IDENTIFIER` deep links into the app, you would set up that action button on the Braze dashboard with the associated deep link URL. However, you don't need to define a button on the dashboard for `KEEP_IDENTIFIER` because it only dismisses the notification. On the dashboard, you only need to enter the category name (for example, `YOUR_CATEGORY`) to match what you registered in your app code. ## Customizing badges Badges are small icons that are ideal for getting a user's attention. You can specify a badge count in the [**Settings**](https://www.braze.com/docs/ko/ko/developer_guide/push_notifications/customization/?sdktab=swift#swift_settings) tab when you compose a push notification using the Braze dashboard. You may also update your badge count manually through your application's [`applicationIconBadgeNumber`](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIApplication_Class/index.html#//apple_ref/occ/instp/UIApplication/applicationIconBadgeNumber) property or the [remote notification payload](https://developer.apple.com/library/content/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/CreatingtheNotificationPayload.html#//apple_ref/doc/uid/TP40008194-CH10-SW1). Braze will automatically clear the badge count when a Braze notification is received while the app is in the foreground. Manually setting the badge number to 0 will also clear notifications in the notification center. If you do not have a plan for clearing badges as part of normal app operation or by sending pushes that clear the badge, you should clear the badge when the app becomes active by adding the following code to your app's `applicationDidBecomeActive:` delegate method: ```swift // For iOS 16.0+ let center = UNUserNotificationCenter.current() do { try await center.setBadgeCount(0) } catch { // Handle errors } // Prior to iOS 16. Deprecated in iOS 17+. UIApplication.shared.applicationIconBadgeNumber = 0 ``` ```objc // For iOS 16.0+ UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter]; [center setBadgeCount:0 withCompletionHandler:^(NSError * _Nullable error) { if (error != nil) { // Handle errors } }]; // Prior to iOS 16. Deprecated in iOS 17+. [UIApplication sharedApplication].applicationIconBadgeNumber = 0; ``` ## Customizing sounds ### Step 1: Host the sound in your app Custom push notification sounds must be hosted locally within the main bundle of your app. The following audio data formats are accepted: - Linear PCM - MA4 - µLaw - aLaw You can package the audio data in an AIFF, WAV, or CAF file. In Xcode, add the sound file to your project as a non-localized resource of the application bundle. **Note:** Custom sounds must be under 30 seconds when played. If a custom sound is over that limit, the default system sound is played instead. #### Converting sound files You can use the afconvert tool to convert sounds. For example, to convert the 16-bit linear PCM system sound Submarine.aiff to IMA4 audio in a CAF file, use the following command in the terminal: ```bash afconvert /System/Library/Sounds/Submarine.aiff ~/Desktop/sub.caf -d ima4 -f caff -v ``` **Tip:** You can inspect a sound to determine its data format by opening it in QuickTime Player and choosing **Show Movie Inspector** from the **Movie** menu. ### Step 2: Provide a protocol URL for the sound You must specify a protocol URL that directs to the location of the sound file in your app. There are two methods for doing this: * Use the `sound` parameter of the [Apple push object](https://www.braze.com/docs/ko/ko/api/objects_filters/messaging/apple_object#apple-push-object) to pass the URL to Braze. * Specify the URL in the dashboard. In the [push composer](https://www.braze.com/docs/ko/ko/user_guide/message_building_by_channel/push/creating_a_push_message/#step-3-select-notification-type-ios-and-android), select **Settings** and enter the protocol URL in the **Sound** field. ![The push composer in the Braze dashboard](https://www.braze.com/docs/ko/ko/assets/img_archive/sound_push_ios.png?c035b34ffb6c0f720f6d2c08ca1ba2b2) If the specified sound file doesn't exist or the keyword "default" is entered, Braze will use the default device alert sound. Aside from our dashboard, sound can also be configured via our [messaging API][12]. See the Apple Developer Documentation regarding [preparing custom alert sounds](https://developer.apple.com/library/content/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/SupportingNotificationsinYourApp.html) for additional information. ## Settings When creating a push campaign through the dashboard, click the **Settings** tab on the **Compose** step to view the advanced settings available. ![](https://www.braze.com/docs/ko/ko/assets/img_archive/ios_advanced_settings.png?16f142abe70d854830708b0cb21d9465) ### Key-value pairs Braze allows you to send custom-defined string key-value pairs, known as `extras`, along with a push notification to your application. Extras can be defined via the dashboard or API and will be available as key-value pairs within the `notification` dictionary passed to your push delegate implementations. ### Alert options Select the **Alert Options** checkbox to see a dropdown of key-values available to adjust how the notification appears on devices. ### Adding content-available flag Check the **Add Content-Available Flag** checkbox to instruct devices to download new content in the background. Most commonly, this can be checked if you are interested in sending [silent notifications](https://www.braze.com/docs/ko/ko/developer_guide/push_notifications/silent/?sdktab=swift). ### Adding mutable-content flag Check the **Add Mutable-Content Flag** checkbox to enable advanced receiver customization. This flag will automatically be sent when composing a [rich notification](https://www.braze.com/docs/ko/ko/developer_guide/push_notifications/rich/?sdktab=swift), regardless of the value of this checkbox. ### Collapse ID Specify a collapse ID to coalesce similar notifications. If you send multiple notifications with the same collapse ID, the device will only show the most recently received notification. Refer to Apple's documentation on [coalesced notifications](https://developer.apple.com/library/content/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/APNSOverview.html#//apple_ref/doc/uid/TP40008194-CH8-SW1). ### Expiry Checking the **Expiry** checkbox will allow setting an expiration time for your message. Should a user's device lose connectivity, Braze will continue to try and send the message until the specified time. If this is not set, the platform will default to an expiration of 30 days. Note that push notifications that expire before delivery are not considered failed and will not be recorded as a bounce. ## Prerequisites Before you can use this feature, you'll need to [integrate the Android Braze SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=android). You'll also need to [set up push notifications](https://www.braze.com/docs/ko/ko/developer_guide/push_notifications/?sdktab=android). ## Settings There are many advanced settings available for FireOS push notifications sent through the Braze dashboard. This article will describe these features and how to use them successfully. ![](https://www.braze.com/docs/ko/ko/assets/img_archive/android_advanced_settings.png?8131f34243617db90fdf5780cbc3cf33) ### Time to live (TTL) {#ttl} The **Time to Live** (TTL) field allows you to set a custom length of time to store messages with the push messaging service. The default values for time to live are four weeks for FCM and 31 days for ADM. ### Summary text {#summary-text} The summary text allows you to set additional text in the expanded notification view. It also serves as a caption for notifications with images. ![An Android message with the title "This is the title for the notification." and summary text "This is the summary text for the notification."](https://www.braze.com/docs/ko/ko/assets/img/android/push/collapsed-android-notification.png?fd42100702f714bd5cb65f742509c9b7){: style="max-width:65%;"} The summary text will display under the body of the message in the expanded view. ![An Android message with the title "This is the title for the notification." and summary text "This is the summary text for the notification."](https://www.braze.com/docs/ko/ko/assets/img/android/push/expanded-android-notification.png?18786f441d0d9d6b65bfcce34c43198d){: style="max-width:65%;"} For push notifications that include images, the message text will be shown in the collapsed view, while the summary text will be displayed as the image caption when the notification is expanded. ### Custom URIs {#custom-uri} The **Custom URI** feature allows you to specify a Web URL or an Android resource to navigate to when the notification is clicked. If no custom URI is specified, clicking on the notification brings users into your app. You can use the custom URI to deep link inside your app and direct users to resources that exist outside of your app. This can be specified via the [Messaging API](https://www.braze.com/docs/ko/ko/api/endpoints/messaging) or our dashboard under **Advanced Settings** in the push composer as pictured: ![The deep linking advanced setting in the Braze push composer.](https://www.braze.com/docs/ko/ko/assets/img_archive/deep_link.png?30080909d43633ac9ca7ac8d115a686a) ### Notification display priority **Important:** The Notification Display Priority setting is no longer used on devices running Android O or newer. For newer devices, set the priority through [notification channel configuration](https://developer.android.com/training/notify-user/channels#importance). The priority level of a push notification affects how your notification is displayed in the notification tray relative to other notifications. It can also affect the speed and manner of delivery, as normal and lower priority messages may be sent with slightly higher latency or batched to preserve battery life whereas high priority messages are always sent immediately. In Android O, notification priority became a property of notification channels. You will need to work with your developer to define the priority for a channel during its configuration and then use the dashboard to select the proper channel when sending your notification sounds. For devices running versions of Android before O, specifying a priority level for FireOS notifications is possible via the Braze dashboard and messaging API. To message your full user base with a specific priority, we recommend that you indirectly specify the priority through [notification channel configuration](https://developer.android.com/training/notify-user/channels#importance) (to target O+ devices) *and* send the individual priority from the dashboard (to target <O devices). The priority levels that you can set on Fire OS push notifications are: | Priority | Description/Intended Use | `priority` value (for API messages) | |----------|--------------------------|-------------------------------------| | Max | Urgent or time-critical messages | `2` | | High | Important communication, such as a new message from a friend | `1` | | Default | Most notifications - use if your message doesn't explicitly fall under any of the other priority types | `0` | | Low | Information that you want users to know about but does not require immediate action | `-1` | | Min | Contextual or background information. | `-2` | {: .reset-td-br-1 .reset-td-br-2 .reset-td-br-3 aria-label="Notification display priority" } For more information, refer to Google's [Android notification](http://developer.android.com/design/patterns/notifications.html) documentation. ### Sounds {#sounds} In Android O, notification sounds became a property of notification channels. You will need to work with your developer to define the sound for a channel during its configuration and then use the dashboard to select the proper channel when sending your notifications. For devices running versions of Android before O, Braze allows you to set the sound of an individual push message through the dashboard composer. You can do so by specifying a local sound resource on the device (for example, `android.resource://com.mycompany.myapp/raw/mysound`). Specifying "default" in this field will play the default notification sound on the device. This can be specified via the [Messaging API](https://www.braze.com/docs/ko/ko/api/endpoints/messaging) or the dashboard under **Settings** in the push composer. ![The sound advanced setting in the Braze push composer.](https://www.braze.com/docs/ko/ko/assets/img_archive/sound_android.png?70e53c2fdff6155d172b0399de090593) Enter the full sound resource URI (for example, `android.resource://com.mycompany.myapp/raw/mysound`) into the dashboard prompt. To message your full user base with a specific sound, we recommend that you indirectly specify the sound through [notification channel configuration](https://developer.android.com/training/notify-user/channels) (to target O+ devices) *and* send the individual sound from the dashboard (to target <O devices). ## Prerequisites Before you can use this feature, you'll need to [integrate the React Native Braze SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=react%20native). You must also [set up push notifications](https://www.braze.com/docs/ko/ko/developer_guide/push_notifications/?sdktab=react%20native). ## Push customization in React Native The Braze React Native SDK does not expose push notification customization (action buttons, categories, custom notification factories) through its JavaScript API. These features require native configuration in your iOS and Android projects. The following table shows which features require native configuration: | Feature | iOS | Android | | --- | --- | --- | | Action buttons | Configure in native Swift/Objective-C | Configure in native Java/Kotlin | | Push categories | Configure in native Swift/Objective-C | Configure in native Java/Kotlin | | Custom notification factory | N/A | Configure in native Java/Kotlin | | Badge customization | Configure in native Swift/Objective-C | N/A | | Custom sounds | Configure in native Swift/Objective-C | Configure in native Java/Kotlin | {: .reset-td-br-1 .reset-td-br-2 .reset-td-br-3 aria-label="Push customization in React Native" } ### iOS customization To add push action buttons, categories, badges, or custom sounds on iOS, implement the native configuration in your `AppDelegate` (Swift or Objective-C). See [Customize push notifications – Swift](https://www.braze.com/docs/ko/ko/developer_guide/push_notifications/customization/?sdktab=swift) for step-by-step instructions. ### Android customization To add push action buttons, categories, or a custom notification factory on Android, implement the native configuration in your Android project. See [Customize push notifications – Android](https://www.braze.com/docs/ko/ko/developer_guide/push_notifications/customization/?sdktab=android) for step-by-step instructions. # Braze SDK용 푸시 알림 딥링킹 Source: /docs/ko/developer_guide/push_notifications/deep_linking/index.md # 푸시 알림의 딥링크 > Braze SDK에 무음 푸시 알림을 설정하는 방법을 알아보세요. ## Prerequisites Before you can use this feature, you'll need to [integrate the Android Braze SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=android). ## Creating a universal delegate The Android SDK provides the ability to set a single delegate object to custom handle all deep links opened by Braze across Content Cards, in-app messages, and push notifications. Your delegate object should implement the [`IBrazeDeeplinkHandler`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.ui/-braze-deeplink-handler/index.html) interface and be set using [`BrazeDeeplinkHandler.setBrazeDeeplinkHandler()`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.ui/-braze-deeplink-handler/-companion/set-braze-deeplink-handler.html). In most cases, the delegate should be set in your app's `Application.onCreate()`. The following is an example of overriding the default [`UriAction`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.ui.actions/-uri-action/index.html) behavior with custom intent flags and custom behavior for YouTube URLs: ```java public class CustomDeeplinkHandler implements IBrazeDeeplinkHandler { private static final String TAG = BrazeLogger.getBrazeLogTag(CustomDeeplinkHandler.class); @Override public void gotoUri(Context context, UriAction uriAction) { String uri = uriAction.getUri().toString(); // Open YouTube URLs in the YouTube app and not our app if (!StringUtils.isNullOrBlank(uri) && uri.contains("youtube.com")) { uriAction.setUseWebView(false); } CustomUriAction customUriAction = new CustomUriAction(uriAction); customUriAction.execute(context); } public static class CustomUriAction extends UriAction { public CustomUriAction(@NonNull UriAction uriAction) { super(uriAction); } @Override protected void openUriWithActionView(Context context, Uri uri, Bundle extras) { Intent intent = getActionViewIntent(context, uri, extras); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP); if (intent.resolveActivity(context.getPackageManager()) != null) { context.startActivity(intent); } else { BrazeLogger.w(TAG, "Could not find appropriate activity to open for deep link " + uri + "."); } } } } ``` ```kotlin class CustomDeeplinkHandler : IBrazeDeeplinkHandler { override fun gotoUri(context: Context, uriAction: UriAction) { val uri = uriAction.uri.toString() // Open YouTube URLs in the YouTube app and not our app if (!StringUtils.isNullOrBlank(uri) && uri.contains("youtube.com")) { uriAction.useWebView = false } val customUriAction = CustomUriAction(uriAction) customUriAction.execute(context) } class CustomUriAction(uriAction: UriAction) : UriAction(uriAction) { override fun openUriWithActionView(context: Context, uri: Uri, extras: Bundle) { val intent = getActionViewIntent(context, uri, extras) intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP if (intent.resolveActivity(context.packageManager) != null) { context.startActivity(intent) } else { BrazeLogger.w(TAG, "Could not find appropriate activity to open for deep link $uri.") } } } companion object { private val TAG = BrazeLogger.getBrazeLogTag(CustomDeeplinkHandler::class.java) } } ``` ## Deep linking to app settings To allow deep links to directly open your app's settings, you'll need a custom `BrazeDeeplinkHandler`. In the following example, the presence of a custom key-value pair called `open_notification_page` will make the deep link open the app's settings page: ```java BrazeDeeplinkHandler.setBrazeDeeplinkHandler(new IBrazeDeeplinkHandler() { @Override public void gotoUri(Context context, UriAction uriAction) { final Bundle extras = uriAction.getExtras(); if (extras.containsKey("open_notification_page")) { Intent intent = new Intent(); intent.setAction("android.settings.APP_NOTIFICATION_SETTINGS"); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); //for Android 5-7 intent.putExtra("app_package", context.getPackageName()); intent.putExtra("app_uid", context.getApplicationInfo().uid); // for Android 8 and later intent.putExtra("android.provider.extra.APP_PACKAGE", context.getPackageName()); context.startActivity(intent); } } }); ``` ```kotlin BrazeDeeplinkHandler.setBrazeDeeplinkHandler(object : IBrazeDeeplinkHandler { override fun gotoUri(context: Context, uriAction: UriAction) { val extras = uriAction.extras if (extras.containsKey("open_notification_page")) { val intent = Intent() intent.action = "android.settings.APP_NOTIFICATION_SETTINGS" intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK //for Android 5-7 intent.putExtra("app_package", context.packageName) intent.putExtra("app_uid", context.applicationInfo.uid) // for Android 8 and later intent.putExtra("android.provider.extra.APP_PACKAGE", context.packageName) context.startActivity(intent) } } }) ``` ## Customizing WebView activity {#Custom_Webview_Activity} When Braze opens website deeplinks inside the app, the deeplinks are handled by [`BrazeWebViewActivity`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.ui/-braze-web-view-activity/index.html). **Note:** For custom HTML in-app messages, links configured with `target="_blank"` open in the device's default web browser and are not handled by `BrazeWebViewActivity`. To change this: 1. Create a new Activity that handles the target URL from `Intent.getExtras()` with the key `com.braze.Constants.BRAZE_WEBVIEW_URL_EXTRA`. For an example, see [`BrazeWebViewActivity.kt`](https://github.com/braze-inc/braze-android-sdk/blob/master/android-sdk-ui/src/main/java/com/braze/ui/BrazeWebViewActivity.kt). 2. Add that activity to `AndroidManifest.xml` and set `exported` to `false`. ```xml ``` 3. Set your custom Activity in a `BrazeConfig` [builder object](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.configuration/-braze-config/-builder/set-custom-web-view-activity-class.html). Build the builder and pass it to [`Braze.configure()`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze/-braze/-companion/configure.html) in your [`Application.onCreate()`](https://developer.android.com/reference/android/app/Application.html#onCreate()). ```java BrazeConfig brazeConfig = new BrazeConfig.Builder() .setCustomWebViewActivityClass(MyCustomWebViewActivity::class) ... .build(); Braze.configure(this, brazeConfig); ``` ```kotlin val brazeConfig = BrazeConfig.Builder() .setCustomWebViewActivityClass(MyCustomWebViewActivity::class.java) ... .build() Braze.configure(this, brazeConfig) ``` ## Troubleshooting If deep links from push notifications aren't working on Android, try the following steps: 1. **Test the deep link outside of Braze.** Open the deep link URL from another app, such as email or a browser. If it doesn't open your app, the deep link may not be configured correctly in your `AndroidManifest.xml`. For more information, see Android's [Create Deep Links](https://developer.android.com/training/app-links/deep-linking) documentation. 2. **Check that automatic deep link handling is enabled.** Verify that `com_braze_handle_push_deep_links_automatically` is set to `true` in `braze.xml`, or set this option through [runtime configuration](https://www.braze.com/docs/ko/ko/developer_guide/sdk_initalization/?sdktab=android). Without this setting, Braze doesn't automatically open your app and deep link destination when someone taps a push notification. 3. **Verify your deep link handler delegate.** If you set a custom `IBrazeDeeplinkHandler`, confirm that your `gotoUri` implementation handles the URI and doesn't drop it. 4. **Test across channels.** If the same deep link works in an in-app message but not from push, the issue is likely in your push deep link handling, not in the deep link itself. ## Using Jetpack Compose To handle deeplinks when using Jetpack Compose with NavHost: 1. Ensure that the activity handling your deeplink is registered in the Android Manifest. ```xml ``` 2. In NavHost, specify which deeplinks you want it to handle. ```kotlin composableWithCompositionLocal( route = "YOUR_ROUTE_HERE", deepLinks = listOf(navDeepLink { uriPattern = "myapp://articles/{${MainDestinations.ARTICLE_ID_KEY}}" }), arguments = listOf( navArgument(MainDestinations.ARTICLE_ID_KEY) { type = NavType.LongType } ), ) { backStackEntry -> val arguments = requireNotNull(backStackEntry.arguments) val articleId = arguments.getLong(MainDestinations.ARTICLE_ID_KEY) ArticleDetail( articleId ) } ``` 3. Depending on your app architecture, you may need to handle the new intent that's sent to your current activity as well. ```kotlin DisposableEffect(Unit) { val listener = Consumer { navHostController.handleDeepLink(it) } addOnNewIntentListener(listener) onDispose { removeOnNewIntentListener(listener) } } ``` ## Prerequisites Before you can use this feature, you'll need to [integrate the Swift Braze SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=swift). **Tip:** For help choosing between custom scheme deep links, universal links, and "Open Web URL Inside App," see [iOS deep linking guide](https://www.braze.com/docs/ko/ko/developer_guide/push_notifications/ios_deep_linking_guide). For troubleshooting, see [Deep linking troubleshooting](https://www.braze.com/docs/ko/ko/developer_guide/push_notifications/deep_linking_troubleshooting). ## Handling deep links ### Step 1: Register a scheme {#register-a-scheme} To handle deep linking, a custom scheme must be stated in your `Info.plist` file. The navigation structure is defined by an array of dictionaries. Each of those dictionaries contains an array of strings. Use Xcode to edit your `Info.plist` file: 1. Add a new key, `URL types`. Xcode will automatically make this an array containing a dictionary called `Item 0`. 2. Within `Item 0`, add a key `URL identifier`. Set the value to your custom scheme. 3. Within `Item 0`, add a key `URL Schemes`. This will automatically be an array containing a `Item 0` string. 4. Set `URL Schemes` >> `Item 0` to your custom scheme. Alternatively, if you wish to edit your `Info.plist` file directly, you can follow this spec: ```html CFBundleURLTypes CFBundleURLName YOUR.SCHEME CFBundleURLSchemes YOUR.SCHEME ``` ### Step 2: Add a scheme allowlist You must declare the URL schemes you wish to pass to `canOpenURL(_:)` by adding the `LSApplicationQueriesSchemes` key to your app's Info.plist file. Attempting to call schemes outside this allowlist will cause the system to record an error in the device's logs, and the deep link will not open. An example of this error will look like this: ``` : -canOpenURL: failed for URL: "yourapp://deeplink" – error: "This app is not allowed to query for scheme yourapp" ``` For example, if an in-app message should open the Facebook app when tapped, the app has to have the Facebook custom scheme (`fb`) in your allowlist. Otherwise, the system will reject the deep link. Deep links that direct to a page or view inside your own app still require that your app's custom scheme be listed in your app's `Info.plist`. Your example allowlist might look something like: ```html LSApplicationQueriesSchemes myapp fb twitter ``` For more information, refer to [Apple's documentation](https://developer.apple.com/library/content/documentation/General/Reference/InfoPlistKeyReference/Articles/LaunchServicesKeys.html#//apple_ref/doc/uid/TP40009250-SW14) on the `LSApplicationQueriesSchemes` key. ### Step 3: Implement a handler After activating your app, iOS will call the method [`application:openURL:options:`](https://developer.apple.com/reference/uikit/uiapplicationdelegate/1623112-application?language=objc). The important argument is the [NSURL](https://developer.apple.com/library/ios/DOCUMENTATION/Cocoa/Reference/Foundation/Classes/NSURL_Class/Reference/Reference.html#//apple_ref/doc/c_ref/NSURL) object. ```swift func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool { let path = url.path let query = url.query // Insert your code here to take some action based upon the path and query. return true } ``` ```objc - (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary *)options { NSString *path = [url path]; NSString *query = [url query]; // Insert your code here to take some action based upon the path and query. return YES; } ``` ## App Transport Security (ATS) As defined by [Apple](https://developer.apple.com/library/prerelease/ios/releasenotes/General/WhatsNewIniOS/Articles/iOS9.html#//apple_ref/doc/uid/TP40016198-SW14), "App Transport Security is a feature that improves the security of connections between an app and web services. The feature consists of default connection requirements that conform to best practices for secure connections. Apps can override this default behavior and turn off transport security." ATS is applied by default. It requires that all connections use HTTPS and are encrypted using TLS 1.2 with forward secrecy. Refer to [Requirements for Connecting Using ATS](https://developer.apple.com/library/ios/documentation/General/Reference/InfoPlistKeyReference/Articles/CocoaKeys.html#//apple_ref/doc/uid/TP40009251-SW35) for more information. All images served by Braze to end devices are handled by a content delivery network ("CDN") that supports TLS 1.2 and is compatible with ATS. Unless they are specified as exceptions in your application's `Info.plist`, connections that do not follow these requirements will fail with errors that are similar to the following. **Example Error 1:** ```bash CFNetwork SSLHandshake failed (-9801) Error Domain=NSURLErrorDomain Code=-1200 "An SSL error has occurred, and a secure connection to the server cannot be made." ``` **Example Error 2:** ```bash NSURLSession/NSURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9802) ``` ATS compliance is enforced for links opened within the mobile app (our default handling of clicked links) and does not apply to sites opened externally via a web browser. ### Working with ATS You can handle ATS in either of the following ways, but we recommend **complying with ATS requirements**. Your Braze integration can satisfy ATS requirements by ensuring that any existing links you drive users to (for example, though in-app message and push campaigns) satisfy ATS requirements. While there are ways to bypass ATS restrictions, our recommendation is to ensure that all linked URLs are ATS-compliant. Given Apple's increasing emphasis on application security, the following approaches to allowing ATS exceptions are not guaranteed to be supported by Apple. You can allow a subset of links with certain domains or schemes to be treated as exceptions to the ATS rules. Your Braze integration will satisfy ATS requirements if every link you use in a Braze messaging channel is either ATS compliant or handled by an exception. To add a domain as an exception of the ATS, add following to your app's `Info.plist` file: ```html NSAppTransportSecurity NSAllowsArbitraryLoads NSExceptionDomains example.com NSExceptionAllowsInsecureHTTPLoads NSIncludesSubdomains ``` Refer to Apple's article on [app transport security keys](https://developer.apple.com/library/ios/documentation/General/Reference/InfoPlistKeyReference/Articles/CocoaKeys.html#//apple_ref/doc/uid/TP40009251-SW33) for more information. You can turn off ATS entirely. Note that this is not recommended practice, due to both lost security protections and future iOS compatibility. To disable ATS, insert the following in your app's `Info.plist` file: ```html NSAppTransportSecurity NSAllowsArbitraryLoads ``` ## Decoding URLs The SDK percent-encodes links to create valid `URL`s. All link characters that are not allowed in a properly formed URL, such as Unicode characters, will be percent escaped. To decode an encoded link, use the `String` property [`removingPercentEncoding`](https://developer.apple.com/documentation/swift/stringprotocol/removingpercentencoding). You must also return `true` in the `BrazeDelegate.braze(_:shouldOpenURL:)`. A call to action is required to trigger the handling of the URL by your app. For example: ```swift func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool { let urlString = url.absoluteString.removingPercentEncoding // Handle urlString return true } ``` ```objc - (BOOL)application:(UIApplication *)application openURL:(NSURL *)url options:(NSDictionary *)options { NSString *urlString = [url.absoluteString stringByRemovingPercentEncoding]; // Handle urlString return YES; } ``` ## Deep linking to app settings You can take advantage of `UIApplicationOpenSettingsURLString` to deep link users to your app's settings from Braze push notifications and in-app messages. To take users from your app into the iOS settings: 1. First, make sure your application is set up for either [scheme-based deep links](#swift_register-a-scheme) or [universal links](#swift_universal-links). 2. Decide on a URI for deep linking to the **Settings** page (for example, `myapp://settings` or `https://www.braze.com/settings`). 3. If you are using custom scheme-based deep links, add the following code to your `application:openURL:options:` method: ```swift func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool { let path = url.path if (path == "settings") { UIApplication.shared.openURL(URL(string:UIApplication.openSettingsURLString)!) } return true } ``` ```objc - (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary *)options { NSString *path = [url path]; if ([path isEqualToString:@"settings"]) { NSURL *settingsURL = [NSURL URLWithString:UIApplicationOpenSettingsURLString]; [[UIApplication sharedApplication] openURL:settingsURL]; } return YES; } ``` ## Customization options {#customization-options} ### Default WebView customization The `Braze.WebViewController` class displays web URLs opened by the SDK, typically when "Open Web URL Inside App" is selected for a web deep link. You can customize the `Braze.WebViewController` via the [`BrazeDelegate.braze(_:willPresentModalWithContext:)`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/brazedelegate/braze(_:willpresentmodalwithcontext:)-12sqy/) delegate method. ### Linking handling customization The `BrazeDelegate` protocol can be used to customize the handling of URLs such as deep links, web URLs, and universal links. To set the delegate during Braze initialization, set a delegate object on the `Braze` instance. Braze will then call your delegate's implementation of `shouldOpenURL` before handling any URIs. When a push notification or in-app message uses **Open web URL inside mobile app**, Braze passes `context.useWebView == true` on [`Braze.URLContext`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/urlcontext). When the message opens the URL in the system browser instead, `useWebView` is `false`. Inspect `context.useWebView` in `braze(_:shouldOpenURL:)` to branch your custom handling—for example, to open an in-app `WebViewController` only when the campaign requested in-app display. #### Universal links {#universal-links} Braze supports universal links in push notifications, in-app messages, and Content Cards. To enable universal link support, [`configuration.forwardUniversalLinks`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/configuration-swift.class/forwarduniversallinks) must be set to `true`. When enabled, Braze will forward universal links to your app's `AppDelegate` via the [`application:continueUserActivity:restorationHandler:`](https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1623072-application) method. Your application also needs to be set up to handle universal links. Refer to [Apple's documentation](https://developer.apple.com/documentation/xcode/supporting-universal-links-in-your-app) to ensure your application is configured correctly for universal links. **Warning:** Universal link forwarding requires access to the application entitlements. When running the application in a simulator, these entitlements are not directly available and universal links are not forwarded to the system handlers. To add support to simulator builds, you can add the application `.entitlements` file to the _Copy Bundle Resources_ build phase. See [`forwardUniversalLinks`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/configuration-swift.class/forwarduniversallinks) documentation for more details. **Note:** The SDK does not query your domains' `apple-app-site-association` file. It performs the differentiation between universal links and regular URLs by looking at the domain name only. As a result, the SDK does not respect any exclusion rule defined in the `apple-app-site-association` per [Supporting associated domains](https://developer.apple.com/documentation/xcode/supporting-associated-domains). ## Examples ### BrazeDelegate Here's an example using `BrazeDelegate`. For more information, see [Braze Swift SDK reference](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/brazedelegate). ```swift func braze(_ braze: Braze, shouldOpenURL context: Braze.URLContext) -> Bool { if context.url.host == "MY-DOMAIN.com" { // Custom handle link here return false } // Let Braze handle links otherwise return true } ``` ```objc - (BOOL)braze:(Braze *)braze shouldOpenURL:(BRZURLContext *)context { if ([[context.url.host lowercaseString] isEqualToString:@"MY-DOMAIN.com"]) { // Custom handle link here return NO; } // Let Braze handle links otherwise return YES; } ``` ## Prerequisites Before you can implement deep linking into your Flutter iOS app, configure your URL schemes in your `Info.plist` file. For details, refer to [Deep linking for iOS](https://www.braze.com/docs/ko/ko/developer_guide/push_notifications/deep_linking/?sdktab=swift#url-schemes). For Flutter Android, no additional native setup is required if you're handling deep links on the Dart layer. The minimal implementation shown in this article is sufficient for most Flutter apps. If you need advanced native-layer link handling (such as custom `IBrazeDeeplinkHandler` implementations), refer to [Deep linking for Android](https://www.braze.com/docs/ko/ko/developer_guide/push_notifications/deep_linking/?sdktab=android). ## Implementing deep linking ### Step 1: Set up Flutter's built-in handling 1. In your Xcode project, open your `Info.plist` file. 2. Add a new key-value pair. 3. Set the key to `FlutterDeepLinkingEnabled`. 4. Set the type to `Boolean`. 5. Set the value to `YES`. ![An example project's `Info.plist` file with the added key-value pair.](https://www.braze.com/docs/ko/ko/assets/img/flutter/flutter-ios-deep-link-info-plist.png?a14c27fd33268008de35220161f94242 "Xcode Project Info.plist File") 1. In your Android Studio project, open your `AndroidManifest.xml` file. 2. Locate `.MainActivity` in your `activity` tags. 3. Within the `activity` tag, add the following `meta-data` tag: ```xml ``` ### Step 2: Forward data to the Dart layer (optional) You can use native, first-party, or third-party link handling for complex use cases, such as sending a user to a specific location in your app, or calling a specific function. #### Example: Deep linking to an alert dialog **Note:** While the following example does not rely on additional packages, you can use a similar approach to implement native, first-party, or third-party packages, such as [`go_router`](https://pub.dev/packages/go_router). Additional Dart code may be required. First, a method channel is used in the native layer to forward the deep link's URL string data to the Dart layer. ```swift extension AppDelegate { // Delegate method for handling custom scheme links. override func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool { forwardURL(url) return true } // Delegate method for handling universal links. override func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool { guard userActivity.activityType == NSUserActivityTypeBrowsingWeb, let url = userActivity.webpageURL else { return false } forwardURL(url) return true } private func forwardURL(_ url: URL) { guard let controller: FlutterViewController = window?.rootViewController as? FlutterViewController else { return } let deepLinkChannel = FlutterMethodChannel(name: "deepLinkChannel", binaryMessenger: controller.binaryMessenger) deepLinkChannel.invokeMethod("receiveDeepLink", arguments: url.absoluteString) } } ``` ```kotlin class MainActivity : FlutterActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) handleDeepLink(intent) } override fun onNewIntent(intent: Intent) { super.onNewIntent(intent) handleDeepLink(intent) } private fun handleDeepLink(intent: Intent) { val binaryMessenger = flutterEngine?.dartExecutor?.binaryMessenger if (intent?.action == Intent.ACTION_VIEW && binaryMessenger != null) { MethodChannel(binaryMessenger, "deepLinkChannel") .invokeMethod("receivedDeepLink", intent?.data.toString()) } } } ``` Next, a callback function is used in the Dart layer to display an alert dialogue using the URL string data sent previously. ```dart MethodChannel('deepLinkChannel').setMethodCallHandler((call) async { deepLinkAlert(call.arguments, context); }); void deepLinkAlert(String link, BuildContext context) { showDialog( context: context, builder: (BuildContext context) { return AlertDialog( title: Text("Deep Link Alert"), content: Text("Opened with deep link: $link"), actions: [ TextButton( child: Text("Close"), onPressed: () { Navigator.of(context).pop(); }, ), ], ); }, ); } ``` ## Prerequisites Before you can use this feature, you'll need to [integrate the Cordova Braze SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=cordova). ## Enabling push deep linking By default, the Braze Cordova SDK doesn't automatically handle push deep linking from notifications. To enable push deep linking, add the following preferences to the `platform` element in your project's `config.xml` file. ```xml ``` ```xml ``` To customize back stack behavior when deep links are followed, you can also add these optional preferences: ```xml ``` For a full list of available push configuration options, see [Optional configurations](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration?sdktab=cordova#optional). # iOS 딥링킹 가이드 Source: /docs/ko/developer_guide/push_notifications/ios_deep_linking_guide/index.md # iOS 딥링킹 가이드 {#ios-deep-linking-guide} > 이 가이드는 사용 중인 메시징 채널과 Branch 같은 타사 링크 제공업체 사용 여부에 따라 iOS 앱에 적합한 딥링킹 전략을 선택하는 데 도움을 줍니다. 구현 세부 사항은 [딥링킹](https://www.braze.com/docs/ko/ko/developer_guide/push_notifications/deep_linking/?sdktab=swift)을 참조하세요. 문제 해결은 [딥링킹 문제 해결](https://www.braze.com/docs/ko/ko/developer_guide/push_notifications/deep_linking_troubleshooting/)을 참조하세요. ## 링크 유형 선택 {#choosing-a-link-type} iOS 앱에서 Braze 메시지의 링크를 처리하는 방법은 세 가지입니다. 각각의 방식은 서로 다르게 작동하며, 서로 다른 채널과 사용 사례에 적합합니다. | 링크 유형 | 예시 | 적합한 용도 | 앱이 설치되지 않은 상태에서도 열리나요? | |---|---|---|---| | **커스텀 스킴** | `myapp://products/123` | 푸시 알림, 인앱 메시지, Content Cards | 아니요 — 링크 실패 | | **유니버설 링크** | `https://myapp.com/products/123` | 이메일, SMS, 클릭 추적이 있는 채널 | 예 — 웹으로 대체됩니다 | | **앱 내에서 웹 URL 열기** | 모든 `https://` URL | 모달 WebView에서 웹 콘텐츠 표시 | 해당 없음 — WebView에 표시됨 | {: .reset-td-br-1 .reset-td-br-2 .reset-td-br-3 .reset-td-br-4 aria-label="Choosing a link type" } ### 커스텀 스킴 딥링크 {#custom-scheme-deep-links} 커스텀 스킴 딥링크(예: `myapp://products/123`)는 앱을 특정 화면으로 직접 엽니다. 타사에 의해 링크가 수정되지 않는 채널에서 가장 간단한 옵션입니다. **다음과 같은 경우 커스텀 스킴 딥링크를 사용하세요:** - 푸시 알림, 인앱 메시지 또는 Content Cards 발송 - 앱이 설치되어 있지 않을 때 링크가 작동할 필요가 없는 경우 - 클릭 추적(이메일 서비스 공급자 링크 래핑)이 필요하지 않은 경우 **다음과 같은 경우에는 커스텀 스킴 딥링크를 사용하지 마세요:** - 이메일 발송 — 이메일 서비스 공급자는 클릭 추적을 위해 링크를 래핑하므로 커스텀 스킴이 깨집니다 - 앱이 설치되지 않은 경우 웹 페이지로 대체되는 링크가 필요한 경우 ### 유니버설 링크 {#universal-links} 유니버설 링크(예: `https://myapp.com/products/123`)는 iOS가 브라우저에서 열지 않고 앱으로 라우팅할 수 있는 표준 HTTPS URL입니다. 서버 측 구성(AASA 파일)과 앱 측 설정(Associated Domains 권한)이 필요합니다. **다음과 같은 경우에 유니버설 링크를 사용하세요:** - 이메일 발송 시. 이메일 서비스 공급자가 클릭 추적을 위해 링크를 래핑하므로 링크는 반드시 HTTPS여야 합니다. - 링크가 래핑되거나 단축되는 SMS 또는 기타 채널을 통해 발송하는 경우. - 앱이 설치되지 않았을 때 웹 페이지로 대체되는 링크가 필요한 경우. - Branch 또는 AppsFlyer 같은 타사 링크 제공업체를 사용하는 경우. **다음과 같은 경우에는 유니버설 링크를 사용하지 마세요:** - 푸시 알림, 인앱 메시지 또는 Content Cards에서만 딥링크가 필요한 경우. 커스텀 스킴이 더 간단합니다. ### "앱 내에서 웹 URL 열기" {#open-web-url-inside-app} 이 옵션은 앱 내의 모달 WebView에서 웹 페이지를 엽니다. `Braze.WebViewController`를 사용하여 Braze SDK가 전적으로 처리하므로 URL 처리 코드를 직접 작성할 필요가 없습니다. **다음과 같은 경우에 "앱 내에서 웹 URL 열기"를 사용하세요:** - 앱을 벗어나지 않고 웹 페이지(예: 프로모션이나 문서)를 표시하고 싶은 경우. - URL이 특정 앱 화면으로의 딥링크가 아닌 표준 HTTPS 웹 페이지인 경우. **다음과 같은 경우에는 "앱 내에서 웹 URL 열기"를 사용하지 마세요:** - 앱에서 특정 뷰로 이동해야 하는 경우. 대신 커스텀 스킴이나 유니버설 링크를 사용하세요. - 웹 페이지에 인증이 필요하거나 임베딩을 차단하는 콘텐츠 보안 정책 헤더가 있는 경우. ## 각 링크 유형에 필요한 사항 {#what-you-need-for-each-link-type} ### 커스텀 스킴 딥링크 | 요구 사항 | 세부 정보 | |---|---| | AASA 파일 | 필요하지 않음 | | `Info.plist` | `CFBundleURLTypes` 아래에 스킴을 등록하고 `LSApplicationQueriesSchemes`에 추가 | | 앱 델리게이트 메서드 | URL을 구문 분석하고 이동하도록 `application(_:open:options:)` 구현 | | Braze SDK 구성 | 없음 — SDK는 기본적으로 커스텀 스킴 URL을 엽니다 | {: .reset-td-br-1 .reset-td-br-2 aria-label="Custom scheme deep links" } ### 유니버설 링크 | 요구 사항 | 세부 정보 | |---|---| | AASA 파일 | 필수 — `https://yourdomain.com/.well-known/apple-app-site-association`에 호스팅 | | Associated Domains | Xcode의 **Signing & Capabilities** 아래에 `applinks:yourdomain.com` 추가 | | 앱 델리게이트 메서드 | `NSUserActivity`를 처리하도록 `application(_:continue:restorationHandler:)` 구현 | | Braze SDK 구성 | `configuration.forwardUniversalLinks = true` 설정 | | BrazeDelegate(선택 사항) | 커스텀 라우팅을 위해 `braze(_:shouldOpenURL:)` 구현(예: Branch) | {: .reset-td-br-1 .reset-td-br-2 aria-label="Universal links" } **Important:** Braze를 통해 이메일을 발송하는 경우, 이메일 서비스 공급자(SendGrid, SparkPost 또는 Amazon SES)가 링크를 클릭 추적 도메인으로 래핑합니다. AASA 파일은 기본 도메인뿐만 아니라 클릭 추적 도메인에도 호스팅해야 합니다. 전체 설정 방법은 [유니버설 링크 및 앱 링크](https://www.braze.com/docs/ko/ko/user_guide/channels/email/customize/universal_links_and_app_links/)를 참조하세요. ### "앱 내에서 웹 URL 열기" | 요구 사항 | 세부 정보 | |---|---| | AASA 파일 | 필요하지 않음 | | 앱 델리게이트 메서드 | 필요 없음 — SDK가 자동으로 처리합니다 | | Braze SDK 구성 | 없음 — Campaign 작성기에서 **Open Web URL Inside App**을 선택 | {: .reset-td-br-1 .reset-td-br-2 aria-label="Open Web URL Inside App" } ## AASA 파일이 필요한 경우 {#when-aasa} Apple App Site Association(AASA) 파일은 **유니버설 링크**를 사용할 때만 필요합니다. 앱이 처리할 수 있는 URL을 iOS에 알려줍니다. 다음과 같은 경우 AASA 파일이 필요합니다: - 이메일 Campaign에서 딥링크를 발송하는 경우(이메일 서비스 공급자가 링크를 HTTPS 클릭 추적 URL로 래핑하기 때문입니다). - SMS Campaign에서 딥링크를 발송하는 경우(링크가 HTTPS URL로 단축될 수 있기 때문입니다). - Branch, AppsFlyer 또는 다른 링크 제공업체를 사용하는 경우(해당 업체들이 자체 HTTPS 도메인을 사용하기 때문입니다). - 푸시 알림, 인앱 메시지 또는 Content Cards에서 유니버설 링크를 사용하는 경우(덜 일반적이지만 `forwardUniversalLinks = true`로 가능합니다). 다음과 같은 경우에는 AASA 파일이 필요하지 않습니다: - 푸시 알림, 인앱 메시지 또는 Content Cards에서 커스텀 스킴 딥링크(예: `myapp://`)만 사용하는 경우. - **앱 내에서 웹 URL 열기** 옵션을 사용하는 경우. AASA 설정 방법은 [유니버설 링크 및 앱 링크](https://www.braze.com/docs/ko/ko/user_guide/message_building_by_channel/email/universal_links/#setting-up-universal-links-and-app-links)를 참조하세요. ## 앱 코드에서 링크를 처리해야 하는 경우 {#when-app-code} 구현할 델리게이트 메서드는 사용하는 링크 유형에 따라 달라집니다: | 델리게이트 메서드 | 처리 대상 | 구현 시점 | |---|---|---| | `application(_:open:options:)` | 커스텀 스킴 딥링크(`myapp://`) | 모든 채널에서 커스텀 스킴 딥링크를 사용하는 경우 | | `application(_:continue:restorationHandler:)` | 유니버설 링크(`https://`) | 이메일, SMS 또는 `forwardUniversalLinks = true`와 함께 유니버설 링크를 사용하는 경우 | | `BrazeDelegate.braze(_:shouldOpenURL:)` | SDK가 여는 모든 URL | 커스텀 라우팅 로직이 필요한 경우(예: Branch, 조건부 처리, 분석) | {: .reset-td-br-1 .reset-td-br-2 .reset-td-br-3 aria-label="When you need app code to handle links #when-app-code" } **Tip:** Branch 같은 타사 링크 제공업체를 사용하는 경우, URL을 가로채서 해당 제공업체의 SDK로 전달하도록 `BrazeDelegate.braze(_:shouldOpenURL:)`를 구현하세요. 전체 예제는 [딥링킹용 Branch](https://www.braze.com/docs/ko/ko/partners/message_orchestration/deeplinking/branch_for_deeplinking/)를 참조하세요. ## Braze에서 Branch 사용하기 {#branch} [Branch](https://www.braze.com/docs/ko/ko/partners/message_orchestration/deeplinking/branch_for_deeplinking/)를 링크 제공업체로 사용하는 경우, 표준 유니버설 링크 구성 외에 몇 가지 추가 단계가 필요합니다: 1. **Branch SDK**: [Branch 설명서](https://help.branch.io/developers-hub/docs/native-sdks-overview)에 따라 Branch SDK를 통합하세요. 2. **Associated Domains**: Xcode의 **Signing & Capabilities** 아래에 Branch 도메인(예: `applinks:yourapp.app.link`)을 추가하세요. 3. **BrazeDelegate**: Branch 링크를 Braze가 직접 처리하지 않고 Branch SDK로 라우팅하도록 `braze(_:shouldOpenURL:)`를 구현하세요. 4. **유니버설 링크 전달**: Braze SDK 구성에서 `configuration.forwardUniversalLinks = true`를 설정하세요. 구현 세부 사항 및 디버깅 안내는 [딥링킹용 Branch](https://www.braze.com/docs/ko/ko/partners/message_orchestration/deeplinking/branch_for_deeplinking/)를 참조하세요. # 딥링킹 문제 해결 Source: /docs/ko/developer_guide/push_notifications/deep_linking_troubleshooting/index.md # 딥링킹 문제 해결 {#deep-linking-troubleshooting} > 이 페이지는 iOS에서 발생하는 일반적인 딥링킹 문제와 그 진단 방법을 다룹니다. 적합한 링크 유형 선택에 대한 도움말은 [iOS 딥링킹 가이드](https://www.braze.com/docs/ko/ko/developer_guide/push_notifications/ios_deep_linking_guide/)를 참조하세요. 구현 세부 사항은 [딥링킹](https://www.braze.com/docs/ko/ko/developer_guide/push_notifications/deep_linking/?sdktab=swift)을 참조하세요. ## 커스텀 스킴 딥링크가 올바른 뷰를 열지 않는 경우 {#custom-scheme-deep-link-doesnt-open-the-correct-view} 커스텀 스킴 딥링크(예: `myapp://products/123`)가 앱을 열지만 의도한 화면으로 이동하지 않는 경우: 1. **스킴이 등록되어 있는지 확인하세요.** Xcode에서 `Info.plist`의 `CFBundleURLTypes` 아래에 스킴이 나열되어 있는지 확인하세요. 2. **핸들러를 확인하세요.** `application(_:open:options:)`에 중단점을 설정하여 호출되는지 확인하고 `url` 매개변수를 검사하세요. 3. **링크를 독립적으로 테스트하세요.** 터미널에서 다음 명령어를 실행하여 Braze 외부에서 딥링크를 테스트하세요: ```bash xcrun simctl openurl booted "myapp://products/123" ``` 해당 링크가 여기서 작동하지 않는다면, 문제는 Braze가 아닌 앱의 URL 처리 방식에 있습니다. 4. **URL 형식을 확인하세요.** Campaign에 포함된 URL이 핸들러가 예상하는 것과 일치하는지 확인하세요. 흔히 발생하는 실수로는 경로 구성 요소가 누락되거나 대소문자가 잘못된 경우가 있습니다. ## 유니버설 링크가 앱 대신 Safari에서 열리는 경우 {#universal-link-opens-in-safari-instead-of-the-app} 유니버설 링크(예: `https://myapp.com/products/123`)가 앱 대신 Safari에서 열리는 경우: ### Associated Domains 권한 확인 {#verify-the-associated-domains-entitlement} Xcode에서 앱 타겟 > **Signing & Capabilities**로 이동하여 **Associated Domains** 아래에 `applinks:yourdomain.com`이 나열되어 있는지 확인하세요. ### AASA 파일 유효성 검사 {#validate-the-aasa-file} Apple App Site Association(AASA) 파일은 다음 위치 중 하나에 호스팅되어야 합니다: - `https://yourdomain.com/.well-known/apple-app-site-association` - `https://yourdomain.com/apple-app-site-association` 다음 사항을 확인하세요: - 파일이 유효한 인증서를 사용하여 HTTPS로 제공됩니다. - `Content-Type`이 `application/json`입니다. - 파일 크기가 128KB 미만입니다. - `appID`가 팀 ID 및 번들 ID와 일치합니다(예: `ABCDE12345.com.example.myapp`). - `paths` 또는 `components` 배열에 예상하는 URL 패턴이 포함되어 있습니다. AASA를 검증하려면 [Apple의 검색 검증 도구](https://search.developer.apple.com/appsearch-validation-tool/)를 사용하거나 다음 명령어를 실행하세요: ```bash swcutil dl -d yourdomain.com ``` ### `AppDelegate` 확인 {#check-the-appdelegate} `AppDelegate`에 `application(_:continue:restorationHandler:)`가 구현되어 있고 `NSUserActivity`를 올바르게 처리하는지 확인하세요: ```swift func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool { guard userActivity.activityType == NSUserActivityTypeBrowsingWeb, let url = userActivity.webpageURL else { return false } // Handle the URL return true } ``` ### Braze SDK 구성 확인 {#verify-braze-sdk-configuration} Braze에서 전송된 푸시 알림, 인앱 메시지 또는 Content Cards에서 유니버설 링크를 사용하는 경우 `forwardUniversalLinks`가 활성화되어 있는지 확인하세요: ```swift let configuration = Braze.Configuration(apiKey: "", endpoint: "") configuration.forwardUniversalLinks = true ``` **Note:** 유니버설 링크 전달에는 애플리케이션 권한에 대한 접근이 필요합니다. 시뮬레이터에서 실행할 때는 이러한 권한을 직접 사용할 수 없습니다. 시뮬레이터에서 테스트하려면 **Copy Bundle Resources** 빌드 단계에 `.entitlements` 파일을 추가하세요. ### 길게 누르기 문제 확인 {#check-for-the-long-press-issue} 유니버설 링크를 길게 눌러 **열기**를 선택하면 iOS가 해당 도메인의 유니버설 링크 연결을 "해제"할 수 있습니다. 이는 iOS의 알려진 동작입니다. 재설정하려면 링크를 다시 길게 누르고 **[앱 이름]에서 열기**를 선택하세요. ## 이메일의 딥링크가 앱을 열지 않는 경우 {#deep-link-from-email-doesnt-open-the-app} 이메일 링크는 이메일 서비스 공급자의 클릭 추적 시스템을 거치며, 이 시스템은 링크를 추적 도메인(예: `https://click.yourdomain.com/...`)으로 래핑합니다. 이메일에서 유니버설 링크가 작동하려면 기본 도메인뿐만 아니라 클릭 추적 도메인에도 AASA 파일을 구성해야 합니다. ### 클릭 추적 도메인 AASA 확인 {#verify-click-tracking-domain-aasa} 1. 이메일 서비스 공급자 설정(SendGrid, SparkPost 또는 Amazon SES)에서 클릭 추적 도메인을 확인하세요. 2. `https://your-click-tracking-domain/.well-known/apple-app-site-association`에 AASA 파일을 호스팅하세요. 3. 클릭 추적 도메인의 AASA 파일에 동일한 `appID`와 유효한 경로 패턴이 포함되어 있는지 확인하세요. 이메일 서비스 공급자별 설정 지침은 [유니버설 링크 및 앱 링크](https://www.braze.com/docs/ko/ko/user_guide/channels/email/customize/universal_links_and_app_links/)를 참조하세요. ### 리디렉션 체인 확인 {#check-the-redirect-chain} 일부 이메일 서비스 공급자는 클릭 추적 URL에서 최종 URL로 리디렉션을 수행합니다. 유니버설 링크는 iOS가 *초기* 도메인(클릭 추적 도메인)을 앱과 연결된 것으로 인식할 때만 작동합니다. 리디렉션이 AASA 검사를 우회하면 링크가 Safari에서 열립니다. 테스트 방법: 1. 자신에게 테스트 이메일을 보내세요. 2. 링크를 길게 누르고 URL을 확인하세요 — 이것이 클릭 추적 URL입니다. 3. 이 도메인에 유효한 AASA 파일이 있는지 확인하세요. ## 딥링크가 푸시에서는 작동하지만 인앱 메시지에서는 작동하지 않는 경우(또는 그 반대) {#deep-link-works-from-push-but-not-from-in-app-messages-or-vice-versa} ### BrazeDelegate 확인 {#check-the-brazedelegate} `BrazeDelegate.braze(_:shouldOpenURL:)`를 구현하는 경우, 채널 전반에 걸쳐 링크를 일관되게 처리하는지 확인하세요. `context` 매개변수에는 소스 채널이 포함됩니다. 특정 채널의 링크를 실수로 필터링할 수 있는 조건 로직이 있는지 확인하세요. ### 상세 로깅 활성화 {#enable-verbose-logging} [상세 로깅을 활성화](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/verbose_logging/)하고 문제를 재현하세요. `Opening` 로그 항목을 찾으세요: ``` Opening '': - channel: - useWebView: - isUniversalLink: ``` 작동하는 채널과 작동하지 않는 채널의 로그 출력을 비교하세요. `useWebView` 또는 `isUniversalLink`의 차이는 SDK가 링크를 다르게 해석하고 있음을 나타냅니다. ### 커스텀 디스플레이 델리게이트 확인 {#check-for-custom-display-delegates} 커스텀 인앱 메시지 디스플레이 델리게이트 또는 Content Card 클릭 핸들러를 사용하는 경우, 링크 이벤트가 처리를 위해 Braze SDK로 올바르게 전달되는지 확인하세요. ## "앱 내에서 웹 URL 열기" 시 빈 페이지 또는 깨진 페이지가 표시되는 경우 {#open-web-url-inside-app-shows-a-blank-or-broken-page} **앱 내에서 웹 URL 열기**를 선택했을 때 빈 페이지 또는 깨진 WebView가 표시되는 경우: 1. **URL이 HTTPS를 사용하는지 확인하세요.** SDK의 WebView는 ATS 호환 URL을 요구합니다. HTTP 링크는 오류 없이 조용히 실패합니다. 2. **Content Security Policy 헤더를 확인하세요.** 대상 웹 페이지가 `X-Frame-Options: DENY` 또는 제한적인 `Content-Security-Policy`를 설정하면 WebView에서 렌더링이 차단됩니다. 3. **커스텀 스킴으로의 리디렉션을 확인하세요.** 웹 페이지가 커스텀 스킴(예: `myapp://`)으로 리디렉션되는 경우 WebView는 이를 처리할 수 없습니다. 4. **Safari에서 URL을 테스트하세요.** 해당 기기의 Safari에서 페이지가 로드되지 않는다면, WebView에서도 로드되지 않습니다. ## Braze에서 Branch 문제 해결 {#branch} [Branch](https://www.braze.com/docs/ko/ko/partners/message_orchestration/deeplinking/branch_for_deeplinking/)를 링크 제공업체로 사용하는 경우: ### BrazeDelegate가 Branch로 라우팅하는지 확인 {#verify-the-brazedelegate-routes-to-branch} `BrazeDelegate`가 Branch 링크를 가로채서 Branch SDK로 전달해야 합니다. 다음 사항을 확인하세요: ```swift func braze(_ braze: Braze, shouldOpenURL context: Braze.URLContext) -> Bool { if let host = context.url.host, host.contains("app.link") { // Route to Branch SDK Branch.getInstance.handleDeepLink(context.url) return false } // Let Braze handle other links return true } ``` `shouldOpenURL`이 Branch 링크에 대해 `true`를 반환하면, Braze가 Branch로 라우팅하지 않고 직접 처리합니다. ### Branch 링크 도메인 확인 {#check-branch-link-domain} `BrazeDelegate`의 Branch 도메인이 실제 Branch 링크 도메인과 일치하는지 확인하세요. Branch는 여러 도메인 형식을 사용합니다: - `yourapp.app.link` (기본값) - `yourapp-alternate.app.link` (대체) - 커스텀 도메인 (Branch 대시보드에서 설정한 경우) ### 두 SDK의 로깅 모두 활성화 {#enable-both-sdks-logging} 링크가 체인에서 끊어지는 지점을 진단하려면: 1. [Braze 상세 로깅](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/verbose_logging/)을 활성화하세요 — SDK가 링크를 수신했는지 확인하기 위해 `Opening '':` 항목을 찾으세요. 2. [Branch 테스트 모드](https://help.branch.io/developers-hub/docs/ios-basic-integration#test-deep-linking)를 활성화하세요 — Branch 대시보드에서 링크 클릭 이벤트를 확인하세요. 1. [Braze 상세 로깅](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/verbose_logging/)을 활성화하세요. SDK가 링크를 수신했는지 확인하기 위해 `Opening '':` 항목을 찾으세요. 2. [Branch 테스트 모드](https://help.branch.io/developers-hub/docs/ios-basic-integration#test-deep-linking)를 활성화하세요. Branch 대시보드에서 링크 클릭 이벤트를 확인하세요. 3. Braze가 링크를 기록했지만 Branch에서 클릭이 감지되지 않는다면, `BrazeDelegate` 라우팅 로직에 문제가 있을 가능성이 높습니다. ### Branch 대시보드 구성 확인 {#check-branch-dashboard-configuration} Branch 대시보드에서 다음을 확인하세요: - 앱의 **Bundle ID**와 **Team ID**가 Xcode 프로젝트와 일치합니다. - **Associated Domains**에 Branch 링크 도메인이 포함되어 있습니다. - Branch AASA 파일이 유효합니다(Branch는 `app.link` 도메인에서 자동으로 호스팅합니다). ### Branch 링크를 독립적으로 테스트 {#test-branch-links-independently} 문제를 격리하기 위해 Braze 외부에서 Branch 링크를 테스트하세요: 1. 기기에서 Safari로 Branch 링크를 여세요. 앱이 열리지 않는다면, 문제는 Braze가 아닌 Branch 또는 AASA 구성에 있습니다. 2. Branch 링크를 메모 앱에 붙여넣고 탭하세요. 유니버설 링크는 Safari 주소창보다 메모 앱에서 더 안정적으로 작동합니다. ## 일반적인 디버깅 팁 {#general-debugging-tips} ### 상세 로깅 사용 {#use-verbose-logging} [상세 로깅을 활성화](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/verbose_logging/)하여 SDK가 링크를 처리하는 방식을 정확히 확인하세요. 확인해야 할 주요 항목: | 로그 항목 | 의미 | |---|---| | `Opening '': - channel: notification` | SDK가 푸시 알림의 링크를 처리 중입니다 | | `Opening '': - channel: inAppMessage` | SDK가 인앱 메시지의 링크를 처리 중입니다 | | `Opening '': - channel: contentCard` | SDK가 Content Card의 링크를 처리 중입니다 | | `useWebView: true` | SDK가 앱 내 WebView에서 URL을 엽니다 | | `isUniversalLink: true` | SDK가 해당 URL을 유니버설 링크로 식별했습니다 | {: .reset-td-br-1 .reset-td-br-2 aria-label="상세 로깅 사용" } 이러한 로그를 읽는 방법에 대한 자세한 내용은 [상세 로그 읽기](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/verbose_logging/)를 참조하세요. ### 링크를 독립적으로 테스트 {#test-links-in-isolation} Braze를 통해 테스트하기 전에, 딥링크 또는 유니버설 링크가 단독으로 작동하는지 확인하세요: - **커스텀 스킴**: 터미널에서 `xcrun simctl openurl booted "myapp://path"`를 실행하세요. - **유니버설 링크**: 실제 기기의 메모 앱에 URL을 붙여넣고 탭하세요. Safari 주소창에서 테스트하지 마세요. iOS는 입력된 URL과 탭한 링크를 다르게 처리합니다. - **Branch 링크**: 기기의 메모 앱에서 Branch 링크를 여세요. ### 실제 기기에서 테스트 {#test-on-a-physical-device} 유니버설 링크는 iOS 시뮬레이터에서 제한적으로 지원됩니다. 정확한 결과를 위해 항상 실제 기기에서 테스트하세요. 시뮬레이터에서 테스트해야 하는 경우, **Copy Bundle Resources** 빌드 단계에 `.entitlements` 파일을 추가하세요. # Braze SDK를 위한 조용한 푸시 알림 설정 Source: /docs/ko/developer_guide/push_notifications/silent/index.md # 조용한 푸시 알림 > Braze SDK에 무음 푸시 알림을 설정하는 방법을 알아보세요. ## Prerequisites Before you can use this feature, you'll need to [integrate the Android Braze SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=android). You'll also need to [set up push notifications](https://www.braze.com/docs/ko/ko/developer_guide/push_notifications/?sdktab=android). ## Setting up silent push notifications Silent notifications are available through the Braze [Messaging API](https://www.braze.com/docs/ko/ko/api/endpoints/messaging/). To take advantage of them, you need to set the `send_to_sync` flag to `true` within the [Android push object](https://www.braze.com/docs/ko/ko/api/objects_filters/messaging/android_object/) and ensure there are no `title` or `alert` fields set as it will cause errors when used alongside `send_to_sync`—however, you can include data `extras` within the object. ## Prerequisites Before you can use this feature, you'll need to [integrate the Swift Braze SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=swift). You'll also need to [set up push notifications](https://www.braze.com/docs/ko/ko/developer_guide/push_notifications/?sdktab=swift). ## iOS limitations The iOS operating system may gate notifications for some features. Note that if you are experiencing difficulties with these features, the iOS's silent notifications gate might be the cause. For more details, refer to Apple's [instance method](https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1623013-application) and [unreceived notifications](https://developer.apple.com/library/content/technotes/tn2265/_index.html#//apple_ref/doc/uid/DTS40010376-CH1-TNTAG23) documentation. ## Setting up silent push notifications To use silent push notifications to trigger background work, you must configure your app to receive notifications even when it is in the background. To do this, add the Background Modes capability using the **Signing & Capabilities** pane to the main app target in Xcode. Select the **Remote notifications** checkbox. ![Xcode showing the "remote notifications" mode checkbox under "capabilities".](https://www.braze.com/docs/ko/ko/assets/img_archive/background_mode.png?15bb65e9a98f4b01af0c73c3917d6950 "background mode enabled") Even with the remote notifications background mode enabled, the system will not launch your app into the background if the user has force-quit the application. The user must explicitly launch the application or reboot the device before the app can be automatically launched into the background by the system. For more information, refer to [pushing background updates](https://developer.apple.com/documentation/usernotifications/setting_up_a_remote_notification_server/pushing_background_updates_to_your_app) and the `application:didReceiveRemoteNotification:fetchCompletionHandler:` [documentation](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIApplicationDelegate_Protocol/index.html#//apple_ref/occ/intfm/UIApplicationDelegate/application:didReceiveRemoteNotification:fetchCompletionHandler:). ## Sending silent push notifications To send a silent push notification, set the `content-available` flag to `1` in a push notification payload. **Note:** What Apple calls a remote notification is just a normal push notification with the `content-available` flag set. The `content-available` flag can be set in the Braze dashboard as well as within our [Apple push object](https://www.braze.com/docs/ko/ko/api/objects_filters/messaging/apple_object/) in the [messaging API](https://www.braze.com/docs/ko/ko/api/endpoints/messaging/). **Warning:** Attaching both a title and body with `content-available=1` is not recommended because it can lead to undefined behavior. To ensure that a notification is truly silent, exclude both the title and body when setting the `content-available` flag to `1.` For further details, refer to the official [Apple documentation on background updates](https://developer.apple.com/documentation/usernotifications/setting_up_a_remote_notification_server/pushing_background_updates_to_your_app). ![The Braze dashboard showing the "content-available" checkbox found in the "settings" tab of the push composer.](https://www.braze.com/docs/ko/ko/assets/img_archive/remote_notification.png?7c9ef06cb8e9c148d37019f5e01d0ce6 "content available") When sending a silent push notification, you might also want to include some data in the notification payload, so your application can reference the event. This could save you a few networking requests and increase the responsiveness of your app. ## Ignoring internal push notifications Braze uses silent push notifications to internally handle certain advanced features, such as uninstall tracking. If your app takes automatic actions on application launches or background pushes, consider gating that activity so it's not triggered by any internal push notifications. For example, if you have logic that calls your servers for new content upon every background push or application launch, you may want to prevent triggering Braze’s internal pushes to avoid unnecessary network traffic. Because Braze sends certain kinds of internal pushes to all users at approximately the same time, significant server load may occur if on-launch network calls from internal pushes are not gated. ### Step 1: Check your app for automatic actions Check your application for automatic actions in the following places and update your code to ignore Braze’s internal pushes: 1. **Push Receivers.** Background push notifications will call `application:didReceiveRemoteNotification:fetchCompletionHandler:` on the `UIApplicationDelegate`. 2. **Application Delegate.** Background pushes can launch [suspended](https://developer.apple.com/documentation/uikit/app_and_environment/managing_your_app_s_life_cycle) apps into the background, triggering the `application:willFinishLaunchingWithOptions:` and `application:didFinishLaunchingWithOptions:` methods on your `UIApplicationDelegate`. Check the `launchOptions` of these methods to determine if the application has been launched from a background push. ### Step 2: Use the internal push utility method You can use the static utility method in `Braze.Notifications` to check if your app has received or was launched by a Braze internal push. [`Braze.Notifications.isInternalNotification(_:)`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/notifications-swift.class/isinternalnotification(_:)) returns `true` for all Braze internal push notifications, which include uninstall tracking and [Feature flags](https://www.braze.com/docs/ko/ko/user_guide/messaging/feature_flags/) sync notifications. For example: ```swift func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) { if (!Braze.Notifications.isInternalNotification(userInfo)) { // Gated logic here (for example pinging server for content) } } ``` ```objc - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult result))completionHandler { if (![BRZNotifications isInternalNotification:userInfo]) { // Gated logic here (for example pinging server for content) } } ``` ## Prerequisites Before you can use this feature, you'll need to [integrate the Android Braze SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=android). You'll also need to [set up push notifications](https://www.braze.com/docs/ko/ko/developer_guide/push_notifications/?sdktab=android). ## Setting up silent push notifications Silent notifications are available through the Braze [Messaging API](https://www.braze.com/docs/ko/ko/api/endpoints/messaging/). To take advantage of them, you need to set the `send_to_sync` flag to `true` within the [Android push object](https://www.braze.com/docs/ko/ko/api/objects_filters/messaging/android_object/) and ensure there are no `title` or `alert` fields set as it will cause errors when used alongside `send_to_sync`—however, you can include data `extras` within the object. # Braze SDK를 위한 리치 푸시 알림 설정 Source: /docs/ko/developer_guide/push_notifications/rich/index.md # 다양한 푸시 알림 > Braze SDK에 대한 리치 푸시 알림을 설정하는 방법을 알아보세요. ## Prerequisites Before you can use this feature, you'll need to [integrate the Swift Braze SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=swift). You'll also need to [set up push notifications](https://www.braze.com/docs/ko/ko/developer_guide/push_notifications/?sdktab=swift). ## Setting up rich push notifications ### Step 1: Creating a service extension To create a [notification service extension](https://developer.apple.com/reference/usernotifications/unnotificationserviceextension), navigate to **File > New > Target** in Xcode and select **Notification Service Extension**. ![](https://www.braze.com/docs/ko/ko/assets/img_archive/ios10_se_at.png?ad077697c9a4c7c7bc3ca07a6405c05d){: style="max-width:90%"} Ensure that **Embed In Application** is set to embed the extension in your application. ### Step 2: Setting up the notification service extension A notification service extension is its own binary that is bundled with your app. It must be set up in the [Apple Developer Portal](https://developer.apple.com) with its own app ID and provisioning profile. The notification service extension's bundle ID must be distinct from your main app target's bundle ID. For example, if your app's bundle ID is `com.company.appname`, you can use `com.company.appname.AppNameServiceExtension` for your service extension. ### Step 3: Adding an App Group In Xcode, add the App Groups capability from the **Signing & Capabilities** pane to your main app target as well as the Notification Service Extension target. Then, click the **+** button. Use your app's bundle ID to create the app group. For example, if your app's bundle ID is `com.company.appname`, you can name your app group `group.com.company.appname.xyz`. **Important:** App Groups in this context refer to Apple's [App Groups Entitlement](https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_security_application-groups) and not your Braze workspace (previously app group) ID. You need a shared App Group so your main app and the Notification Service Extension can access shared data. If you do not add your app to an app group, your app may fail to populate certain fields from the push payload and will not work fully as expected. ### Step 4: Integrating rich push notifications For a step-by-step guide on integrating rich push notifications with `BrazeNotificationService`, refer to our [tutorial](https://braze-inc.github.io/braze-swift-sdk/tutorials/braze/b2-rich-push-notifications). To see a sample, refer to the usage in [`NotificationService`](https://github.com/braze-inc/braze-swift-sdk/blob/main/Examples/Swift/Sources/PushNotificationsServiceExtension/NotificationService.swift) of our Examples app. #### Adding the rich push framework to your app After following the [Swift Package Manager integration guide](https://www.braze.com/docs/ko/ko/developer_guide/platform_integration_guides/swift/sdk_integration/?tab=swift%20package%20manager/), add `BrazeNotificationService` to your `Notification Service Extension` by doing the following: 1. In Xcode, under frameworks and libraries, select the add icon to add a framework.

![The plus icon is located under frameworks and libraries in Xcode.](https://www.braze.com/docs/ko/ko/assets/img_archive/rich_notification.png?aacc2bc0878ec1e3bf74e346f2cd7132)

2. Select the "BrazeNotificationService" framework.

![The "BrazeNotificationService framework can be selected in the modal that opens.](https://www.braze.com/docs/ko/ko/assets/img_archive/rich_notification2.png?13b077cd5a0a9723eff10fc48a6bc70c) Add the following to your Podfile: ```ruby target 'YourAppTarget' do pod 'BrazeKit' pod 'BrazeUI' pod 'BrazeLocation' end target 'YourNotificationServiceExtensionTarget' do pod 'BrazeNotificationService' end # Only include the below if you want to also integrate Push Stories target 'YourNotificationContentExtensionTarget' do pod 'BrazePushStory' end ``` **Note:** For instructions to implement Push Stories, see the [documentation](https://www.braze.com/docs/ko/ko/developer_guide/platform_integration_guides/swift/push_notifications/push_story/?tab=swift%20package%20manager). After updating the Podfile, navigate to the directory of your Xcode app project within your terminal and run `pod install`. To add `BrazeNotificationService.xcframework` to your `Notification Service Extension`, see [Manual integration](https://www.braze.com/docs/ko/ko/developer_guide/platform_integration_guides/swift/sdk_integration?tab=manual/). ![](https://www.braze.com/docs/ko/ko/assets/img/swift/rich_push/manual1.png?43f3a21a35ff7bd8ba2e787947a860b3) #### Using your own UNNotificationServiceExtension If you need to use your own UNNotificationServiceExtension, you can instead call [`brazeHandle`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazenotificationservice/brazehandle(request:contenthandler:)) in your `didReceive` method. ```swift import BrazeNotificationService import UserNotifications class NotificationService: UNNotificationServiceExtension { override func didReceive( _ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void ) { if brazeHandle(request: request, contentHandler: contentHandler) { return } // Custom handling here contentHandler(request.content) } } ``` ### Step 5: Configuring the App Group in Braze Before initializing Braze, assign the name of your app group to your Braze configuration's [`push.appGroup`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/configuration-swift.class/push-swift.class/appgroup) property. ```swift let configuration = Braze.Configuration(apiKey: "", endpoint: "") configuration.push.appGroup = "REPLACE_WITH_APPGROUP" let braze = Braze(configuration: configuration) ``` ### Step 6: Creating a rich notification in your dashboard Your marketing team can also create rich notifications from the dashboard. Create a push notification through the push composer and attach an image or GIF, or provide a URL that hosts an image, GIF, or video. Note that assets are downloaded on the receipt of push notifications, so you should plan for large, synchronous spikes in requests if you are hosting your content. ## Prerequisites Before you can use this feature, you'll need to [integrate the Cordova Braze SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=cordova). You'll also need to [set up push notifications](https://www.braze.com/docs/ko/ko/developer_guide/push_notifications/?sdktab=cordova). ## Setting up rich push notifications ### Step 1: Create a notification service extension In your Xcode project, create a notification service extension. For a full walkthrough, see [iOS Rich Push Notifications Tutorial](https://braze-inc.github.io/braze-swift-sdk/tutorials/braze/b2-rich-push-notifications). ### Step 2: Add a new target Open your Podfile and add `BrazeNotificationService` to the notification service extension target [you just created](#cordova_step-1-create-a-notification-service-extension). If `BrazeNotificationService` is already added to a target, remove it before continuing. To avoid duplicate symbol errors, use static linking. ```ruby target 'NOTIFICATION_SERVICE_EXTENSION' do use_frameworks! :linkage => :static pod 'BrazeNotificationService' end ``` Replace `NOTIFICATION_SERVICE_EXTENSION` with the name of your notification service extension. Your Podfile should be similar to the following: ```ruby target 'MyAppRichNotificationService' do use_frameworks! :linkage => :static pod 'BrazeNotificationService' end ``` ### Step 3: Reinstall your CocoaPods dependencies In the terminal, go to your project's iOS directory and reinstall your CocoaPod dependencies. ```bash cd PATH_TO_PROJECT/platform/ios pod install ``` ## Prerequisites Before you can use this feature, you'll need to [integrate the React Native Braze SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=react%20native). You'll also need to [set up push notifications](https://www.braze.com/docs/ko/ko/developer_guide/push_notifications/?sdktab=react%20native). ## Using Expo to enable rich push notifications For the React Native SDK, **rich push notifications are available for Android by default**. To enable rich push notifications on iOS using Expo, configure the `enableBrazeIosRichPush` property to `true` in your `expo.plugins` object in `app.json`: ```json { "expo": { "plugins": [ [ "@braze/expo-plugin", { ... "enableBrazeIosRichPush": true } ] ] } } ``` Lastly, add the bundle identifier for this app extension to your project's credentials configuration: `.BrazeExpoRichPush`. For further details on this process, refer to [Using app extensions with Expo Application Services](https://www.braze.com/docs/ko/ko/developer_guide/push_notifications/?sdktab=react%20native#reactnative_app-extensions). # Braze SDK를 위한 푸시 스토리 설정 Source: /docs/ko/developer_guide/push_notifications/push_stories/index.md # 푸시 스토리 > Braze SDK용 푸시 스토리를 설정하는 방법을 알아보세요. ## Prerequisites Before you can use this feature, you'll need to [integrate the Swift Braze SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=swift). You'll also need to [set up push notifications](https://www.braze.com/docs/ko/ko/developer_guide/push_notifications/?sdktab=swift), which includes implementing the `UNNotification` framework. The following minimum SDK version is required to receive Push Stories: ## Setting up Push Stories ### Step 1: Adding the Notification Content Extension target {#notification-content-extension} In your app project, go to menu **File > New > Target** and add a new `Notification Content Extension` target and activate it. ![](https://www.braze.com/docs/ko/ko/assets/img/swift/push_story/add_content_extension.png?ad9e5d8cc83d88d9e26dbd2c4c8dba67) Xcode should generate a new target for you and create files automatically for you including: - `NotificationViewController.swift` - `MainInterface.storyboard` ### Step 2: Enable capabilities {#enable-capabilities} In Xcode, add the Background Modes capability using the **Signing & Capabilities** pane to the main app target. Select both the **Background fetch** and **Remote notifications** checkboxes. ![](https://www.braze.com/docs/ko/ko/assets/img/swift/push_story/enable_background_mode.png?37d0c9c4c59fb04aa930729a5539ed59) #### Adding an App Group Additionally, from the **Signing & Capabilities** pane in Xcode, add the App Groups capability to your main app target as well as the Notification Content Extension targets. Then, click the **+** button. Use your app's bundle ID to create the app group. For example, if your app's bundle ID is `com.company.appname`, you can name your app group `group.com.company.appname.xyz`. **Important:** App Groups in this context refer to Apple's [App Groups Entitlement](https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_security_application-groups) and not your Braze workspace (previously app group) ID. If you do not add your app to an App Group, your app may fail to populate certain fields from the push payload and will not work fully as expected. ### Step 3: Adding the Push Story framework to your app {#enable-capabilities} After following the [Swift Package Manager integration guide](https://www.braze.com/docs/ko/ko/developer_guide/platform_integration_guides/swift/sdk_integration/?tab=swift%20package%20manager/), add `BrazePushStory` to your `Notification Content Extension`: ![In Xcode, under frameworks and libraries, select the "+" icon to add a framework.](https://www.braze.com/docs/ko/ko/assets/img/swift/push_story/spm1.png?00b81a1ac272e7247a67cd7c176a79f8) ![](https://www.braze.com/docs/ko/ko/assets/img/swift/push_story/spm2.png?9df11322d50bd385f7151ba062c0319c) Add the following line to your Podfile: ```ruby target 'YourAppTarget' do pod 'BrazeKit' pod 'BrazeUI' pod 'BrazeLocation' end target 'YourNotificationContentExtensionTarget' do pod 'BrazePushStory' end # Only include the below if you want to also integrate Rich Push target 'YourNotificationServiceExtensionTarget' do pod 'BrazeNotificationService' end ``` **Note:** For instructions to implement Rich Push, see [Rich notifications](https://www.braze.com/docs/ko/ko/developer_guide/platform_integration_guides/swift/push_notifications/customization/rich_notifications/?tab=swift%20package%20manager). After updating the Podfile, navigate to the directory of your Xcode app project within your terminal and run `pod install`. Download the latest `BrazePushStory.zip` from the [GitHub release page](https://github.com/braze-inc/braze-swift-sdk/releases), extract it, and add the `BrazePushStory.xcframework` to your project's `Notification Content Extension`. ![](https://www.braze.com/docs/ko/ko/assets/img/swift/push_story/manual1.png?cdc5b6905a824611c983facc8b541026) **Important:** Make sure that **Do Not Embed** is selected for **BrazePushStory.xcframework** under the **Embed** column. ### Step 4: Updating your notification view controller {#enable-capabilities} In `NotificationViewController.swift`, add the following line to import the header files: ```swift import BrazePushStory ``` Next, replace the default implementation by inheriting [`BrazePushStory.NotificationViewController`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazepushstory/notificationviewcontroller/): ```swift class NotificationViewController: BrazePushStory.NotificationViewController {} ``` #### Custom handling push story events If you want to implement your own custom logic to handle push story notification events, inherit `BrazePushStory.NotificationViewController` as above and override the [`didReceive`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazepushstory/notificationviewcontroller/didreceive(_:)) methods as below. ```swift import BrazePushStory import UserNotifications import UserNotificationsUI class NotificationViewController: BrazePushStory.NotificationViewController { override func didReceive(_ notification: UNNotification) { super.didReceive(notification) // Custom handling logic } override func didReceive(_ response: UNNotificationResponse, completionHandler completion: @escaping (UNNotificationContentExtensionResponseOption) -> Void) { super.didReceive(response, completionHandler: completion) // Custom handling logic } } ``` ### Step 5: Setting the Notification Content Extension plist {#notification-content-extension} Open the `Info.plist` file of the `Notification Content Extension`, then add and change the following keys under `NSExtension \ NSExtensionAttributes`: | Key | Type | Value | |--------------------------------------------------|---------|------------------------| | `UNNotificationExtensionCategory` | String | `ab_cat_push_story_v2` | | `UNNotificationExtensionDefaultContentHidden` | Boolean | `YES` | | `UNNotificationExtensionInitialContentSizeRatio` | Number | `0.6` | | `UNNotificationExtensionUserInteractionEnabled` | Boolean | `YES` | {: .reset-td-br-1 .reset-td-br-2 .reset-td-br-3 aria-label="Step 5: Setting the Notification Content Extension plist #notification-content-extension" } Additionally, add the following top-level `Braze` dictionary to the same `Info.plist` file, replacing `REPLACE_WITH_APPGROUP` with the App Group you created in [Step 2](#enable-capabilities): | Key | Type | Value | |------------------|--------|--------------------------| | `Braze.AppGroup` | String | `REPLACE_WITH_APPGROUP` | {: .reset-td-br-1 .reset-td-br-2 .reset-td-br-3 aria-label="Step 5: Setting the Notification Content Extension plist #notification-content-extension" } Your `Info.plist` file should match the following image: ![](https://www.braze.com/docs/ko/ko/assets/img/swift/push_story/notificationcontentextension_plist.png?781099250e344b0bfbf448d47af7a25c) ### Step 6: Updating the Braze integration in your main app {#update-braze} Before initializing Braze, assign the name of your app group to your Braze configuration's [`push.appGroup`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/configuration-swift.class/push-swift.class/appgroup) property. ```swift let configuration = Braze.Configuration(apiKey: "", endpoint: "") configuration.push.appGroup = "REPLACE_WITH_APPGROUP" let braze = Braze(configuration: configuration) ``` ## Prerequisites Before you can use this feature, you'll need to [integrate the Cordova Braze SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=cordova). You'll also need to [set up push notifications](https://www.braze.com/docs/ko/ko/developer_guide/push_notifications/?sdktab=cordova). ## Setting up push stories ### Step 1: Create a notification content extension In your Xcode project, create a notification content extension. For a full walkthrough, see [iOS Push Stories Tutorial](https://braze-inc.github.io/braze-swift-sdk/tutorials/braze/b3-push-stories/). ### Step 2: Configure your push app group In your project's `config.xml` file, configure the push app group [you just created](#cordova_step-1-create-a-notification-content-extension). ```xml ``` Replace `PUSH_APP_GROUP` with the name of your push app group. Your `config.xml` should be similar to the following: ```xml ``` ### Step 3: Add a new target Open your Podfile and add `BrazePushStory` to the notification content extension target [you created previously](#cordova_step-1-create-a-notification-content-extension). To avoid duplicate symbol errors, use static linking. ```ruby target 'NOTIFICATION_CONTENT_EXTENSION' do use_frameworks! :linkage => :static pod 'BrazePushStory' end ``` Replace `NOTIFICATION_CONTENT_EXTENSION` with the name of your notification content extension. Your Podfile should be similar to the following: ```ruby target 'MyAppNotificationContentExtension' do use_frameworks! :linkage => :static pod 'BrazePushStory' end ``` ### Step 4: Reinstall your CocoaPods dependencies In the terminal, go to your iOS directory and reinstall your CocoaPod dependencies. ```bash cd PATH_TO_PROJECT/platform/ios pod install ``` ## Prerequisites Before you can use this feature, you'll need to [integrate the React Native Braze SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=react%20native). You'll also need to [set up push notifications](https://www.braze.com/docs/ko/ko/developer_guide/push_notifications/?sdktab=react%20native). ## Enabling push stories For the React Native SDK, **push stories are available for Android by default**. To enable Push Stories on iOS using Expo, ensure you have an app group defined for your application. For more information, see [Adding an App Group](https://www.braze.com/docs/ko/ko/developer_guide/platform_integration_guides/swift/push_notifications/push_story/#adding-an-app-group). Next, configure the `enableBrazeIosPushStories` property to `true` and assign your app group ID to `iosPushStoryAppGroup` in your `expo.plugins` object in `app.json`: ```json { "expo": { "plugins": [ [ "@braze/expo-plugin", { ... "enableBrazeIosPushStories": true, "iosPushStoryAppGroup": "group.com.company.myApp.PushStories" } ] ] } } ``` Lastly, add the bundle identifier for this app extension to your project's credentials configuration: `.BrazeExpoPushStories`. For further details on this process, refer to [Using app extensions with Expo Application Services](https://www.braze.com/docs/ko/ko/developer_guide/push_notifications/?sdktab=react%20native#reactnative_app-extensions). **Warning:** If you are using Push Stories with Expo Application Services, be sure to use the `EXPO_NO_CAPABILITY_SYNC=1` flag when running `eas build`. There is a known issue in the command line which removes the App Groups capability from your extension's provisioning profile. # Braze SDK를 위한 소프트 푸시 프롬프트 설정 Source: /docs/ko/developer_guide/push_notifications/soft_push_prompts/index.md # 웹용 소프트 푸시 프롬프트 > Braze SDK의 소프트 푸시 프롬프트를 설정하는 방법을 알아보세요. ## Prerequisites Before you can use this feature, you'll need to [integrate the Web Braze SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=web). You'll also need to [set up push notifications](https://www.braze.com/docs/ko/ko/developer_guide/push_notifications/?sdktab=web). If you're integrating Braze through mParticle's embedded kit on the web, see [Step 3 in mParticle's Braze Web event integration](https://docs.mparticle.com/integrations/braze/event/#web) for instructions to implement soft push prompts. ## About soft push prompts It's often a good idea for sites to implement a "soft" push prompt where you "prime" the user and make your case for sending them push notifications before requesting push permission. This is useful because the browser throttles how often you may prompt the user directly, and if the user denies permission you can never ask them again. Alternatively, if you would like to include special custom handling, instead of calling `requestPushPermission()` directly as described in the standard [Web push integration](https://www.braze.com/docs/ko/ko/developer_guide/platform_integration_guides/web/push_notifications/integration/#step-2-browser-registration), use our [triggered in-app messages](https://www.braze.com/docs/ko/ko/developer_guide/in_app_messages/triggering_messages/?tab=web). **Tip:** This can be done without SDK customization using our new [no code push primer](https://www.braze.com/docs/ko/ko/user_guide/message_building_by_channel/push/best_practices/push_primer_messages/). ## Setting up soft push prompts **Note:** This guide uses code samples from the Braze Web SDK 4.0.0+. To upgrade to the latest Web SDK version, see [SDK Upgrade Guide](https://github.com/braze-inc/braze-web-sdk/blob/master/UPGRADE_GUIDE.md). ### Step 1: Create a push primer campaign First, you must create a "Prime for Push" in-app messaging campaign in the Braze dashboard: 1. Create a **Modal** in-app message with the text and styling you want. 2. Next, set the on-click behavior to **Close Message**. This behavior will be customized later. 3. Add a key-value pair to the message where the key is `msg-id`, and the value is `push-primer`. 4. Assign a custom event trigger action (such as "prime-for-push") to the message. You can create the custom event manually from the dashboard if needed. ### Step 2: Remove calls In your Braze SDK integration, find and remove any calls to `automaticallyShowInAppMessages()` from within your loading snippet. ### Step 3: Update integration Finally, replace the removed call with the following snippet. Call `subscribeToInAppMessage()` before calling `openSession()`. This ensures your in-app message listener is registered in time to receive the push primer message. ```javascript import * as braze from "@braze/web-sdk"; // Be sure to remove any calls to braze.automaticallyShowInAppMessages() braze.subscribeToInAppMessage(function(inAppMessage) { // check if message is not a control variant if (inAppMessage instanceof braze.inAppMessage) { // access the key-value pairs, defined as `extras` const keyValuePairs = inAppMessage.extras || {}; // check the value of our key `msg-id` defined in the Braze dashboard if (keyValuePairs["msg-id"] === "push-primer") { // We don't want to display the soft push prompt to users on browsers // that don't support push, or if the user has already granted/blocked permission if ( braze.isPushSupported() === false || braze.isPushPermissionGranted() || braze.isPushBlocked() ) { // do not call `showInAppMessage` return; } // user is eligible to receive the native prompt // register a click handler on one of the two buttons if (inAppMessage.buttons[0]) { // Prompt the user when the first button is clicked inAppMessage.buttons[0].subscribeToClickedEvent(function() { braze.requestPushPermission( function() { // success! }, function() { // user declined } ); }); } } } // show the in-app message now braze.showInAppMessage(inAppMessage); }); ``` When you wish to display the soft push prompt to the user, call `braze.logCustomEvent` - with whatever event name triggers this in-app message. # 푸시 분석 및 커스텀 이벤트 로깅 Source: /docs/ko/developer_guide/push_notifications/logging_message_data/index.md # 푸시 분석 및 커스텀 이벤트 로깅 {#push-analytics-and-custom-event-logging} > 이 페이지에서는 네이티브 푸시 분석(열기, 영향받은 열기, Campaign 보고서)과 푸시 페이로드에서의 커스텀 데이터 로깅(커스텀 이벤트 및 속성) 워크플로를 다룹니다. 이 가이드를 통해 사용 사례에 해당하는 워크플로를 확인하고 플랫폼에 맞는 단계를 따르세요. ## 필수 조건 {#prerequisites} 시작하기 전에 플랫폼에 대한 초기 푸시 알림 통합을 완료하세요: - [Android 푸시 알림](https://www.braze.com/docs/ko/ko/developer_guide/push_notifications/?sdktab=android) - [Swift 푸시 알림](https://www.braze.com/docs/ko/ko/developer_guide/push_notifications/?sdktab=swift) - [웹 푸시 알림](https://www.braze.com/docs/ko/ko/developer_guide/push_notifications/?sdktab=web) ## 네이티브 푸시 분석 vs. 커스텀 이벤트 로깅 {#native-push-analytics-vs-custom-event-logging} 다음 워크플로는 각각 다른 보고 화면을 사용합니다. | 분석 카테고리 | 설명 | 표시 위치 | | --- | --- | --- | | 네이티브 푸시 분석 | Braze 푸시 Campaign에 연결된 열기 및 영향받은 열기와 같은 푸시 측정기준 | 푸시 Campaign 분석, Currents 메시지 참여 이벤트, 보고서 빌더 | | 커스텀 이벤트 및 속성 | SDK 메서드 또는 `/users/track` 엔드포인트를 통해 정의하고 기록하는 분석 | 고객 프로필, 세분화, 동작 기반 Campaign 및 Canvases, 커스텀 이벤트 분석 | {: .reset-td-br-1 .reset-td-br-2 .reset-td-br-3 aria-label="Native push analytics vs. custom event logging" } **Important:** 커스텀 이벤트(예: `push_notification_opened`)를 기록하는 것은 Braze의 네이티브 푸시 열기 추적과 동일하지 않습니다. 커스텀 이벤트는 네이티브 푸시 Campaign 열기 측정기준이나 푸시 기여도에 반영되지 않습니다. ## Braze가 자동으로 기록하는 항목 {#what-braze-logs-automatically} SDK 통합이 구성되면 Braze는 푸시 열기 및 영향받은 열기를 포함한 핵심 채널 상호작용 데이터를 자동으로 기록합니다. 표준 푸시 분석에는 추가 코드가 필요하지 않습니다. 자동으로 수집되는 데이터의 전체 목록은 [SDK 데이터 수집](https://www.braze.com/docs/ko/ko/user_guide/data/unification/user_data/sdk_data_collection/)을 참조하세요. 자세한 내용은 다음을 참조하세요: - [SDK 데이터 수집](https://www.braze.com/docs/ko/ko/user_guide/data/unification/user_data/sdk_data_collection/) - 자동으로 수집되는 데이터와 선택적 데이터의 전체 목록. - [영향받은 열기](https://www.braze.com/docs/ko/ko/user_guide/analytics/tracking/influenced_opens/) - Braze가 영향받은 열기를 계산하는 방법. - [메시지 참여 이벤트](https://www.braze.com/docs/ko/ko/user_guide/data/distribution/braze_currents/event_glossary/message_engagement_events/) - Currents의 다운스트림 이벤트 스키마. ## 커스텀 푸시 처리 시 네이티브 푸시 분석 유지 {#preserving-native-push-analytics-with-custom-push-handling} 여러 푸시 공급자를 통합하거나, 추가 페이로드 데이터를 처리하거나, 커스텀 알림 표시 로직을 구현해야 할 때 커스텀 푸시 핸들러를 사용할 수 있습니다. 커스텀 푸시 핸들러를 사용하는 경우에도 푸시 페이로드를 Braze SDK 메서드에 전달해야 합니다. 이를 통해 Braze가 내장된 추적 데이터를 추출하고 네이티브 푸시 분석(열기, 영향받은 열기, 전달 측정기준)을 기록할 수 있습니다. 커스텀 `FirebaseMessagingService`가 있는 경우, `onMessageReceived` 메서드 내에서 `BrazeFirebaseMessagingService.handleBrazeRemoteMessage(...)`를 호출하세요. `FirebaseMessagingService` 서브클래스는 Android 시스템에 의해 [플래그 지정되거나 종료](https://firebase.google.com/docs/cloud-messaging/android/receive)되지 않도록 호출 후 9초 이내에 실행을 완료해야 합니다. ```java public class MyFirebaseMessagingService extends FirebaseMessagingService { @Override public void onMessageReceived(RemoteMessage remoteMessage) { super.onMessageReceived(remoteMessage); if (BrazeFirebaseMessagingService.handleBrazeRemoteMessage(this, remoteMessage)) { // Braze processed a Braze push payload. } else { // Non-Braze payload: pass to your other handlers. } } } ``` 전체 구현 예제는 [Braze Android SDK Firebase 푸시 샘플 앱](https://github.com/braze-inc/braze-android-sdk/tree/master/samples/firebase-push)을 참조하세요. 수동 푸시 통합에서는 백그라운드 및 사용자 알림 콜백을 Braze에 전달합니다. **백그라운드 알림:** ```swift if let braze = AppDelegate.braze, braze.notifications.handleBackgroundNotification( userInfo: userInfo, fetchCompletionHandler: completionHandler ) { return } completionHandler(.noData) ``` **사용자 알림 응답:** ```swift if let braze = AppDelegate.braze, braze.notifications.handleUserNotification( response: response, withCompletionHandler: completionHandler ) { return } completionHandler() ``` **포그라운드 알림:** ```swift func userNotificationCenter( _ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void ) { if let braze = AppDelegate.braze { braze.notifications.handleForegroundNotification(notification: notification) } if #available(iOS 14.0, *) { completionHandler([.banner, .list, .sound]) } else { completionHandler([.alert, .sound]) } } ``` 전체 구현 예제는 [Braze Swift SDK 수동 푸시 샘플(`AppDelegate.swift`)](https://github.com/braze-inc/braze-swift-sdk/blob/main/Examples/Swift/Sources/PushNotifications-Manual/AppDelegate.swift)을 참조하세요. 웹 푸시의 경우, [웹 푸시 알림](https://www.braze.com/docs/ko/ko/developer_guide/push_notifications/?sdktab=web)에 설명된 대로 서비스 워커와 SDK 초기화를 구성하세요. 더 많은 코드 샘플은 [Braze Web SDK 리포지토리](https://github.com/braze-inc/braze-web-sdk)를 참조하세요. ## 푸시 페이로드에서 커스텀 데이터 로깅 {#logging-custom-data-from-push-payloads} 비즈니스 로직에 연결된 커스텀 이벤트나 속성과 같이 푸시 페이로드 키-값 페어에서 추가 데이터를 기록해야 할 때 이 섹션을 사용하세요. 커스텀 이벤트에 대한 자세한 내용은 [커스텀 이벤트](https://www.braze.com/docs/ko/ko/user_guide/data/activation/events/custom_events/)를 참조하세요. SDK 메서드를 통해 커스텀 이벤트를 기록하려면 [커스텀 이벤트 로깅](https://www.braze.com/docs/ko/ko/developer_guide/analytics/logging_events/)을 참조하세요. ### 옵션 A: `/users/track` 엔드포인트로 기록 {#option-a-log-with-the-userstrack-endpoint} [`/users/track`](https://www.braze.com/docs/ko/ko/api/endpoints/user_data/post_user_track/) 엔드포인트를 호출하여 실시간으로 분석을 기록할 수 있습니다. 고객 프로필을 식별하려면 푸시 페이로드 키-값 페어에 `braze_id`를 포함하세요. **Note:** `braze_id`를 전달하면 프로필만 식별됩니다. 페이로드 값을 읽고 기록하려는 이벤트 또는 속성과 함께 `/users/track` 요청을 보내는 구현 로직이 별도로 필요합니다. ### 옵션 B: 앱 실행 후 SDK 메서드로 기록 {#option-b-log-with-sdk-methods-after-app-launch} 페이로드 데이터를 로컬에 저장한 후 앱이 초기화된 후 SDK 메서드를 통해 커스텀 이벤트와 속성을 기록할 수도 있습니다. 이 접근 방식은 분석 데이터를 먼저 저장한 후 다음 앱 실행 시 전송하는 알림 콘텐츠 확장 플로우에서 일반적으로 사용됩니다. **Important:** 앱이 실행될 때까지 분석이 Braze로 전송되지 않습니다. 해제 설정에 따라 사용자가 알림을 해제하는 시점과 앱이 열리고 분석을 전송하는 시점 사이에 지연이 발생할 수 있습니다. ## 알림 콘텐츠 확장에서 로깅(Swift) {#logging-from-a-notification-content-extension-swift} 다음 단계에서는 Swift 알림 콘텐츠 확장에서 커스텀 이벤트, 커스텀 속성 및 사용자 속성을 저장하고 전송하는 방법을 다룹니다. ### 1단계: Xcode에서 앱 그룹 구성 {#step-1-configure-app-groups-in-xcode} Xcode에서 메인 앱 타겟에 `App Groups` 기능을 추가합니다. **App Groups**를 켜고 **+**를 클릭하여 새 그룹을 추가합니다. 앱의 번들 ID를 사용하여 그룹 식별자를 생성합니다(예: `group.com.company.appname.xyz`). 메인 앱 타겟과 콘텐츠 확장 타겟 모두에서 **App Groups**를 켭니다. ![메인 앱과 알림 확장에 대해 App Groups 기능이 활성화된 Xcode](https://www.braze.com/docs/ko/ko/assets/img/swift/push_story/add_app_groups.png?44e3d92af533e6323db33236364b99e1) ### 2단계: 기록할 항목 선택 {#step-2-choose-what-to-log} 스니펫을 구현하기 전에 기록할 분석 카테고리를 선택하세요: - **커스텀 이벤트:** 사용자가 수행하는 동작(예: 플로우 완료 또는 특정 UI 요소 탭). 동작 기반 트리거, 세분화 및 이벤트 분석에 커스텀 이벤트를 사용합니다. 자세한 내용은 [커스텀 이벤트](https://www.braze.com/docs/ko/ko/user_guide/data/activation/events/custom_events/) 및 [커스텀 이벤트 로깅](https://www.braze.com/docs/ko/ko/developer_guide/analytics/logging_events/)을 참조하세요. - **커스텀 속성:** 정의하고 시간이 지남에 따라 업데이트하는 프로필 필드(예: `plan_tier` 또는 `preferred_language`). 자세한 내용은 [커스텀 속성](https://www.braze.com/docs/ko/ko/user_guide/data/activation/custom_data/data_types/) 및 [사용자 속성 설정](https://www.braze.com/docs/ko/ko/developer_guide/analytics/setting_user_attributes/)을 참조하세요. - **사용자 속성:** 표준 프로필 필드(예: 이메일, 이름, 전화번호). 샘플 코드에서는 타입이 지정된 `UserAttribute` 모델로 표현된 후 Braze 사용자 필드에 매핑됩니다. 이 섹션의 헬퍼 파일(`RemoteStorage`, `UserAttribute`, `EventName Dictionary`)은 이 샘플 구현에서 사용하는 로컬 유틸리티 파일입니다. 내장 SDK 클래스가 아닙니다. 페이로드에서 파생된 데이터를 `UserDefaults`에 저장하고, 보류 중인 사용자 업데이트를 위한 타입 모델을 정의하며, 이벤트 페이로드 구성을 표준화합니다. 로컬 데이터 저장 동작에 대한 자세한 내용은 [스토리지](https://www.braze.com/docs/ko/ko/developer_guide/storage/?tab=swift)를 참조하세요. **Note:** 이 섹션의 헬퍼 파일 예제는 iOS 전용(Swift 및 Objective-C)입니다. Android 및 웹에서 커스텀 이벤트와 속성을 기록하는 방법은 [커스텀 이벤트 로깅](https://www.braze.com/docs/ko/ko/developer_guide/analytics/logging_events/)([Android](https://www.braze.com/docs/ko/ko/developer_guide/analytics/logging_events/?tab=android), [Web](https://www.braze.com/docs/ko/ko/developer_guide/analytics/logging_events/?tab=web)) 및 [사용자 속성 설정](https://www.braze.com/docs/ko/ko/developer_guide/analytics/setting_user_attributes/)([Android](https://www.braze.com/docs/ko/ko/developer_guide/analytics/setting_user_attributes/?tab=android), [Web](https://www.braze.com/docs/ko/ko/developer_guide/analytics/setting_user_attributes/?tab=web))을 참조하세요. #### 커스텀 이벤트 저장 {#saving-custom-events} 사전을 빌드하고, 메타데이터를 채우고, 헬퍼 파일로 저장하여 분석 페이로드를 생성합니다. 1. 이벤트 메타데이터로 사전을 초기화합니다. 2. 이벤트 데이터를 검색하고 저장하기 위해 `userDefaults`를 초기화합니다. 3. 기존 배열이 있으면 추가하고 저장합니다. 4. 배열이 없으면 새 배열을 저장합니다. ```swift func saveCustomEvent(with properties: [String: Any]? = nil) { // 1 let customEventDictionary = Dictionary(eventName: "YOUR-EVENT-NAME", properties: properties) // 2 let remoteStorage = RemoteStorage(storageType: .suite) // 3 if var pendingEvents = remoteStorage.retrieve(forKey: .pendingCustomEvents) as? [[String: Any]] { pendingEvents.append(contentsOf: [customEventDictionary]) remoteStorage.store(pendingEvents, forKey: .pendingCustomEvents) } else { // 4 remoteStorage.store([customEventDictionary], forKey: .pendingCustomEvents) } } ``` ```objc - (void)saveCustomEvent:(NSDictionary *)properties { // 1 NSDictionary *customEventDictionary = [[NSDictionary alloc] initWithEventName:@"YOUR-EVENT-NAME" properties:properties]; // 2 RemoteStorage *remoteStorage = [[RemoteStorage alloc] initWithStorageType:StorageTypeSuite]; NSMutableArray *pendingEvents = [[remoteStorage retrieveForKey:RemoteStorageKeyPendingCustomEvents] mutableCopy]; // 3 if (pendingEvents) { [pendingEvents addObject:customEventDictionary]; [remoteStorage store:pendingEvents forKey:RemoteStorageKeyPendingCustomEvents]; } else { // 4 [remoteStorage store:@[ customEventDictionary ] forKey:RemoteStorageKeyPendingCustomEvents]; } } ``` #### Braze에 커스텀 이벤트 전송 {#sending-custom-events-to-braze} SDK 초기화 직후에 저장된 분석을 기록합니다. 1. 보류 중인 이벤트를 순회합니다. 2. 각 이벤트의 키-값 페어를 순회합니다. 3. `event_name` 키를 확인합니다. 4. 나머지 키-값 페어를 `properties` 사전에 추가합니다. 5. 각 커스텀 이벤트를 기록합니다. 6. 스토리지에서 보류 중인 이벤트를 제거합니다. ```swift func logPendingCustomEventsIfNecessary() { let remoteStorage = RemoteStorage(storageType: .suite) guard let pendingEvents = remoteStorage.retrieve(forKey: .pendingCustomEvents) as? [[String: Any]] else { return } // 1 for event in pendingEvents { var eventName: String? var properties: [AnyHashable: Any] = [:] // 2 for (key, value) in event { if key == "event_name" { // 3 if let eventNameValue = value as? String { eventName = eventNameValue } else { print("Invalid type for event_name key") } } else { // 4 properties[key] = value } } // 5 if let eventName = eventName { AppDelegate.braze?.logCustomEvent(name: eventName, properties: properties) } } // 6 remoteStorage.removeObject(forKey: .pendingCustomEvents) } ``` ```objc - (void)logPendingEventsIfNecessary { RemoteStorage *remoteStorage = [[RemoteStorage alloc] initWithStorageType:StorageTypeSuite]; NSArray *pendingEvents = [remoteStorage retrieveForKey:RemoteStorageKeyPendingCustomEvents]; // 1 for (NSDictionary *event in pendingEvents) { NSString *eventName = nil; NSMutableDictionary *properties = [NSMutableDictionary dictionary]; // 2 for (NSString* key in event) { if ([key isEqualToString:@"event_name"]) { // 3 if ([[event objectForKey:key] isKindOfClass:[NSString class]]) { eventName = [event objectForKey:key]; } else { NSLog(@"Invalid type for event_name key"); } } else { // 4 properties[key] = event[key]; } } // 5 if (eventName != nil) { [AppDelegate.braze logCustomEvent:eventName properties:properties]; } } // 6 [remoteStorage removeObjectForKey:RemoteStorageKeyPendingCustomEvents]; } ``` #### 커스텀 속성 저장 {#saving-custom-attributes} 분석 사전을 처음부터 생성한 후 저장합니다. 1. 속성 메타데이터로 사전을 초기화합니다. 2. 속성 데이터를 검색하고 저장하기 위해 `userDefaults`를 초기화합니다. 3. 기존 배열이 있으면 추가하고 저장합니다. 4. 배열이 없으면 새 배열을 저장합니다. ```swift func saveCustomAttribute() { // 1 let customAttributeDictionary: [String: Any] = ["YOUR-CUSTOM-ATTRIBUTE-KEY": "YOUR-CUSTOM-ATTRIBUTE-VALUE"] // 2 let remoteStorage = RemoteStorage(storageType: .suite) // 3 if var pendingAttributes = remoteStorage.retrieve(forKey: .pendingCustomAttributes) as? [[String: Any]] { pendingAttributes.append(contentsOf: [customAttributeDictionary]) remoteStorage.store(pendingAttributes, forKey: .pendingCustomAttributes) } else { // 4 remoteStorage.store([customAttributeDictionary], forKey: .pendingCustomAttributes) } } ``` ```objc - (void)saveCustomAttribute { // 1 NSDictionary *customAttributeDictionary = @{ @"YOUR-CUSTOM-ATTRIBUTE-KEY": @"YOUR-CUSTOM-ATTRIBUTE-VALUE" }; // 2 RemoteStorage *remoteStorage = [[RemoteStorage alloc] initWithStorageType:StorageTypeSuite]; NSMutableArray *pendingAttributes = [[remoteStorage retrieveForKey:RemoteStorageKeyPendingCustomAttributes] mutableCopy]; // 3 if (pendingAttributes) { [pendingAttributes addObject:customAttributeDictionary]; [remoteStorage store:pendingAttributes forKey:RemoteStorageKeyPendingCustomAttributes]; } else { // 4 [remoteStorage store:@[ customAttributeDictionary ] forKey:RemoteStorageKeyPendingCustomAttributes]; } } ``` #### Braze에 커스텀 속성 전송 {#sending-custom-attributes-to-braze} SDK 초기화 직후에 저장된 분석을 기록합니다. 1. 보류 중인 속성을 순회합니다. 2. 각 키-값 페어를 순회합니다. 3. 각 커스텀 속성 키와 값을 기록합니다. 4. 스토리지에서 보류 중인 속성을 제거합니다. ```swift func logPendingCustomAttributesIfNecessary() { let remoteStorage = RemoteStorage(storageType: .suite) guard let pendingAttributes = remoteStorage.retrieve(forKey: .pendingCustomAttributes) as? [[String: Any]] else { return } // 1 pendingAttributes.forEach { setCustomAttributesWith(keysAndValues: $0) } // 4 remoteStorage.removeObject(forKey: .pendingCustomAttributes) } func setCustomAttributesWith(keysAndValues: [String: Any]) { // 2 for (key, value) in keysAndValues { // 3 if let value = value as? [String] { setCustomAttributeArrayWithKey(key, andValue: value) } else { setCustomAttributeWithKey(key, andValue: value) } } } ``` ```objc - (void)logPendingCustomAttributesIfNecessary { RemoteStorage *remoteStorage = [[RemoteStorage alloc] initWithStorageType:StorageTypeSuite]; NSArray *pendingAttributes = [remoteStorage retrieveForKey:RemoteStorageKeyPendingCustomAttributes]; // 1 for (NSDictionary *attribute in pendingAttributes) { [self setCustomAttributeWith:attribute]; } // 4 [remoteStorage removeObjectForKey:RemoteStorageKeyPendingCustomAttributes]; } - (void)setCustomAttributeWith:(NSDictionary *)keysAndValues { // 2 for (NSString *key in keysAndValues) { // 3 [self setCustomAttributeWith:key andValue:[keysAndValues objectForKey:key]]; } } ``` #### 사용자 속성 저장 {#saving-user-attributes} 사용자 속성을 저장할 때 업데이트되는 사용자 필드(`email`, `first_name`, `phone_number` 등)를 캡처하는 커스텀 오브젝트를 생성합니다. 이 오브젝트는 `UserDefaults`를 통해 저장 및 검색할 수 있어야 합니다. 예제는 **헬퍼 파일** 탭의 `UserAttribute` 헬퍼 파일을 참조하세요. 1. 해당 타입으로 인코딩된 `UserAttribute` 오브젝트를 초기화합니다. 2. 데이터를 검색하고 저장하기 위해 `userDefaults`를 초기화합니다. 3. 기존 배열이 있으면 추가하고 저장합니다. 4. 배열이 없으면 새 배열을 저장합니다. ```swift func saveUserAttribute() { // 1 guard let data = try? PropertyListEncoder().encode(UserAttribute.email("USER-ATTRIBUTE-VALUE")) else { return } // 2 let remoteStorage = RemoteStorage(storageType: .suite) // 3 if var pendingAttributes = remoteStorage.retrieve(forKey: .pendingUserAttributes) as? [Data] { pendingAttributes.append(contentsOf: [data]) remoteStorage.store(pendingAttributes, forKey: .pendingUserAttributes) } else { // 4 remoteStorage.store([data], forKey: .pendingUserAttributes) } } ``` ```objc - (void)saveUserAttribute { // 1 UserAttribute *userAttribute = [[UserAttribute alloc] initWithUserField:@"USER-ATTRIBUTE-VALUE" attributeType:UserAttributeTypeEmail]; NSError *error; NSData *data = [NSKeyedArchiver archivedDataWithRootObject:userAttribute requiringSecureCoding:YES error:&error]; if (error != nil) { // log error } // 2 RemoteStorage *remoteStorage = [[RemoteStorage alloc] initWithStorageType:StorageTypeSuite]; NSMutableArray *pendingAttributes = [[remoteStorage retrieveForKey:RemoteStorageKeyPendingUserAttributes] mutableCopy]; // 3 if (pendingAttributes) { [pendingAttributes addObject:data]; [remoteStorage store:pendingAttributes forKey:RemoteStorageKeyPendingUserAttributes]; } else { // 4 [remoteStorage store:@[data] forKey:RemoteStorageKeyPendingUserAttributes]; } } ``` #### Braze에 사용자 속성 전송 {#sending-user-attributes-to-braze} SDK 초기화 직후에 저장된 분석을 기록합니다. 1. `pendingAttributes` 데이터를 순회합니다. 2. 각 `UserAttribute`를 디코딩합니다. 3. 속성 타입에 따라 사용자 필드를 설정합니다. 4. 스토리지에서 보류 중인 사용자 속성을 제거합니다. ```swift func logPendingUserAttributesIfNecessary() { let remoteStorage = RemoteStorage(storageType: .suite) guard let pendingAttributes = remoteStorage.retrieve(forKey: .pendingUserAttributes) as? [Data] else { return } // 1 for attributeData in pendingAttributes { // 2 guard let userAttribute = try? PropertyListDecoder().decode(UserAttribute.self, from: attributeData) else { continue } // 3 switch userAttribute { case .email(let email): AppDelegate.braze?.user.set(email: email) } } // 4 remoteStorage.removeObject(forKey: .pendingUserAttributes) } ``` ```objc - (void)logPendingUserAttributesIfNecessary { RemoteStorage *remoteStorage = [[RemoteStorage alloc] initWithStorageType:StorageTypeSuite]; NSArray *pendingAttributes = [remoteStorage retrieveForKey:RemoteStorageKeyPendingUserAttributes]; // 1 for (NSData *attributeData in pendingAttributes) { NSError *error; // 2 UserAttribute *userAttribute = [NSKeyedUnarchiver unarchivedObjectOfClass:[UserAttribute class] fromData:attributeData error:&error]; if (error != nil) { // log error } // 3 if (userAttribute) { switch (userAttribute.attributeType) { case UserAttributeTypeEmail: [self user].email = userAttribute.userField; break; } } } // 4 [remoteStorage removeObjectForKey:RemoteStorageKeyPendingUserAttributes]; } ``` #### RemoteStorage 헬퍼 파일 {#remotestorage-helper-file} ```swift enum RemoteStorageKey: String, CaseIterable { // MARK: - Notification Content Extension Analytics case pendingCustomEvents = "pending_custom_events" case pendingCustomAttributes = "pending_custom_attributes" case pendingUserAttributes = "pending_user_attributes" } enum RemoteStorageType { case standard case suite } class RemoteStorage: NSObject { private var storageType: RemoteStorageType = .standard private lazy var defaults: UserDefaults = { switch storageType { case .standard: return .standard case .suite: // Use the App Group identifier you created in Step 1. return UserDefaults(suiteName: "group.com.company.appname.xyz")! } }() init(storageType: RemoteStorageType = .standard) { self.storageType = storageType } func store(_ value: Any, forKey key: RemoteStorageKey) { defaults.set(value, forKey: key.rawValue) } func retrieve(forKey key: RemoteStorageKey) -> Any? { return defaults.object(forKey: key.rawValue) } func removeObject(forKey key: RemoteStorageKey) { defaults.removeObject(forKey: key.rawValue) } func resetStorageKeys() { for key in RemoteStorageKey.allCases { defaults.removeObject(forKey: key.rawValue) } } } ``` ```objc @interface RemoteStorage () @property (nonatomic) StorageType storageType; @property (nonatomic, strong) NSUserDefaults *defaults; @end @implementation RemoteStorage - (id)initWithStorageType:(StorageType)storageType { if (self = [super init]) { self.storageType = storageType; } return self; } - (void)store:(id)value forKey:(RemoteStorageKey)key { [[self defaults] setValue:value forKey:[self rawValueForKey:key]]; } - (id)retrieveForKey:(RemoteStorageKey)key { return [[self defaults] objectForKey:[self rawValueForKey:key]]; } - (void)removeObjectForKey:(RemoteStorageKey)key { [[self defaults] removeObjectForKey:[self rawValueForKey:key]]; } - (void)resetStorageKeys { [[self defaults] removeObjectForKey:[self rawValueForKey:RemoteStorageKeyPendingCustomEvents]]; [[self defaults] removeObjectForKey:[self rawValueForKey:RemoteStorageKeyPendingCustomAttributes]]; [[self defaults] removeObjectForKey:[self rawValueForKey:RemoteStorageKeyPendingUserAttributes]]; } - (NSUserDefaults *)defaults { if (!_defaults) { switch (self.storageType) { case StorageTypeStandard: _defaults = [NSUserDefaults standardUserDefaults]; break; case StorageTypeSuite: _defaults = [[NSUserDefaults alloc] initWithSuiteName:@"group.com.company.appname.xyz"]; break; } } return _defaults; } - (NSString*)rawValueForKey:(RemoteStorageKey)remoteStorageKey { switch(remoteStorageKey) { case RemoteStorageKeyPendingCustomEvents: return @"pending_custom_events"; case RemoteStorageKeyPendingCustomAttributes: return @"pending_custom_attributes"; case RemoteStorageKeyPendingUserAttributes: return @"pending_user_attributes"; default: [NSException raise:NSGenericException format:@"Unexpected FormatType."]; } } ``` #### UserAttribute 헬퍼 파일 {#userattribute-helper-file} ```swift enum UserAttribute: Hashable { case email(String?) } // MARK: - Codable extension UserAttribute: Codable { private enum CodingKeys: String, CodingKey { case email } func encode(to encoder: Encoder) throws { var values = encoder.container(keyedBy: CodingKeys.self) switch self { case .email(let email): try values.encodeIfPresent(email, forKey: .email) } } init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) let email = try values.decodeIfPresent(String.self, forKey: .email) self = .email(email) } } ``` ```objc @implementation UserAttribute - (id)initWithUserField:(NSString *)userField attributeType:(UserAttributeType)attributeType { if (self = [super init]) { self.userField = userField; self.attributeType = attributeType; } return self; } - (void)encodeWithCoder:(NSCoder *)encoder { [encoder encodeObject:self.userField forKey:@"userField"]; [encoder encodeInteger:self.attributeType forKey:@"attributeType"]; } - (id)initWithCoder:(NSCoder *)decoder { if (self = [super init]) { self.userField = [decoder decodeObjectForKey:@"userField"]; NSInteger attributeRawValue = [decoder decodeIntegerForKey:@"attributeType"]; self.attributeType = (UserAttributeType) attributeRawValue; } return self; } @end ``` #### EventName 사전 헬퍼 파일 {#eventname-dictionary-helper-file} ```swift extension Dictionary where Key == String, Value == Any { init(eventName: String, properties: [String: Any]? = nil) { self.init() self[PushNotificationKey.eventName.rawValue] = eventName if let properties = properties { for (key, value) in properties { self[key] = value } } } } ``` ```objc @implementation NSMutableDictionary (Helper) + (instancetype)dictionaryWithEventName:(NSString *)eventName properties:(NSDictionary *)properties { NSMutableDictionary *dict = [NSMutableDictionary dictionary]; dict[@"event_name"] = eventName; if (properties) { for (id key in properties) { dict[key] = properties[key]; } } return dict; } @end ``` ## 결과 분석 {#analyzing-results} 분석 카테고리에 맞는 보고 화면을 사용하세요: | 분석 카테고리 | Braze에서 확인하는 위치 | | --- | --- | | 네이티브 푸시 분석 | Campaign 수준의 푸시 열기 측정기준을 보려면 푸시 Campaign의 **Campaign Analytics** 페이지로 이동합니다. 측정기준 정의는 [영향받은 열기](https://www.braze.com/docs/ko/ko/user_guide/analytics/tracking/influenced_opens/)를 참조하세요. 커스텀 분석 뷰를 구축하려면 **Analytics** > **Report Builder (New)**로 이동합니다. 탐색 단계는 [보고서 빌더](https://www.braze.com/docs/ko/ko/user_guide/analytics/reports/report_builder/)를 참조하세요. 웨어하우스 수준의 이벤트 스키마는 [메시지 참여 이벤트](https://www.braze.com/docs/ko/ko/user_guide/data/distribution/braze_currents/event_glossary/message_engagement_events/)를 참조하세요. | | 커스텀 이벤트 및 속성 | 커스텀 이벤트 트렌드를 보려면 **Analytics** > **Custom Events Report**로 이동합니다. 자세한 내용은 [커스텀 이벤트](https://www.braze.com/docs/ko/ko/user_guide/data/activation/events/custom_events/)를 참조하세요. 사용자 수준의 값을 검사하려면 **Search Users** 페이지로 이동하여 프로필을 엽니다. 단계는 [고객 프로필](https://www.braze.com/docs/ko/ko/user_guide/audience/manage_audience/user_profiles/)을 참조하세요. 이러한 값으로 오디언스를 필터링하려면 **Audience** > **Segments**로 이동합니다. 탐색 단계는 [Segment 생성](https://www.braze.com/docs/ko/ko/user_guide/audience/segments/creating_a_segment/) 및 [세분화 필터](https://www.braze.com/docs/ko/ko/user_guide/audience/segments/segmentation_filters/)의 필터 옵션을 참조하세요. | {: .reset-td-br-1 .reset-td-br-2 aria-label="Analyzing results" } 커스텀 보고서 생성에 대해서는 [보고서 빌더](https://www.braze.com/docs/ko/ko/user_guide/analytics/reports/report_builder/)를 참조하세요. ## 관련 참조 {#related-references} - [푸시 알림](https://www.braze.com/docs/ko/ko/developer_guide/push_notifications/) - [커스텀 이벤트 로깅](https://www.braze.com/docs/ko/ko/developer_guide/analytics/logging_events/) - [커스텀 이벤트](https://www.braze.com/docs/ko/ko/user_guide/data/activation/events/custom_events/) - [사용자 추적 엔드포인트(`/users/track`)](https://www.braze.com/docs/ko/ko/api/endpoints/user_data/post_user_track/) - [Braze Android SDK 리포지토리](https://github.com/braze-inc/braze-android-sdk) - [Braze Swift SDK 리포지토리](https://github.com/braze-inc/braze-swift-sdk) - [Braze Web SDK 리포지토리](https://github.com/braze-inc/braze-web-sdk) # 테스트 메시지 전송 Source: /docs/ko/developer_guide/push_notifications/sending_test_messages/index.md # Sending test messages > Before sending out a messaging campaign to your users, you may want to test it to make sure it looks right and operates in the intended manner. You can use the dashboard to create and send test messages with push notifications, in-app messages (IAM), or email. ## Sending a test message ### Step 1: Create a designated test segment After you set up a test segment, you can use it to test any of your Braze messaging channels. When set up correctly, this only needs to be done a single time. To set up a test segment, go to **Segments** and create a new segment. Select **Add Filter**, then choose a one of the test filters. ![A Braze test campaign displaying the filters available in the targeting step.](https://www.braze.com/docs/ko/ko/assets/img_archive/testmessages1.png?c440e858d187b30c92b316dfa12b9774) With test filters, you can ensure that only users with a specific email address or [external user ID](https://www.braze.com/docs/ko/ko/developer_guide/platform_integration_guides/swift/analytics/setting_user_ids/#setting-user-ids) are sent the test message. ![A dropdown menu displaying several filters listed under a heading that reads Testing](https://www.braze.com/docs/ko/ko/assets/img_archive/testmessages2.png?8c289defede0c6ba588c9b8ba8d0c9f5) Both email address and external user ID filters offer the following options: | Operator | Description | |------------------|--------------------------------------------------------------------------------------------------------------------------------| | `equals` | This will look for an exact match of the email or user ID that you provide. Use this if you only want to send the test campaigns to devices associated with a single email or user ID. | | `does not equal` | Use this if you want to exclude a particular email or user ID from test campaigns. | | `matches` | This will find users that have email addresses or user IDs that match part of the search term you provide. You could use this to find only the users that have an `@yourcompany.com` address, allowing you to send messages to everyone on your team. | {: .reset-td-br-1 .reset-td-br-2 aria-label="Step 1: Create a designated test segment a class="margin-fix" name="test-segment"/a" } You can select multiple specific emails using the "`matches`" option and separating the email addresses with a | character. For example: "`matches`" "`email1@braze.com` | `email2@braze.com`". You can also combine multiple operators together. For example, the test segment could include an email address filter that "`matches`" "`@braze.com`" and another filter that "`does not equal`" "`sales@braze.com`". After adding the testing filters to your test segment, you can verify it's working by selecting **Preview** or by selecting **Settings** > **CSV Export All User Data** to export that segment's user data to a CSV file. ![A section of a Braze campaign titled Segment Details](https://www.braze.com/docs/ko/ko/assets/img_archive/testmessages3.png?78e031a18aad06f510fd2ac4946bf7c5) **Note:** Exporting the segment's User Data to a CSV file is the most accurate verification method, as the preview will only show a sample of your users and may not include all users. ### Step 2: Send the message You can send a message using the Braze dashboard or the command line. To send test push notifications or in-app messages, you need to target your previously created test segment. Begin by creating your campaign and following the usual steps. When you reach the **Target Audiences** step, select your test segment from the dropdown menu. ![A Braze test campaign displaying the segments available in the targeting step.](https://www.braze.com/docs/ko/ko/assets/img_archive/test_segment.png?25ae32cc99d02e6dcdd9b66a0adf75e4) Confirm your campaign and launch it to test your push notification and in-app messages. **Note:** Be sure to select **Allow users to become re-eligible to receive campaign** under the **Schedule** portion of the campaign composer if you intend to use a single campaign to send a test message to yourself more than once. If you're only testing email messages, you do not necessarily have to set up a test segment. In the first step of the campaign composer where you compose your campaign's email message, click **Send Test** and enter the email address to which you wish to send a test email. ![A Braze campaign with the Test Send tab selected](https://www.braze.com/docs/ko/ko/assets/img_archive/testmessages45.png?883cb58cd3adf2e8315817db896b7914) **Tip:** You can also enable or disable [TEST (or SEED)](https://www.braze.com/docs/ko/ko/user_guide/administrative/app_settings/email_settings/#append-email-subject-lines) being appended on your test messages. Alternatively, you can send a single notification using cURL and the [Braze Messaging API](https://www.braze.com/docs/ko/ko/api/endpoints/messaging/). Note that these examples make a request using the `US-01` instance. To find out yours, refer to [API endpoints](https://www.braze.com/docs/ko/ko/api/basics/#endpoints). ```bash curl -X POST -H "Content-Type: application/json" -H "Authorization: Bearer {BRAZE_API_KEY}" -d '{ "external_user_ids":["EXTERNAL_USER_ID"], "messages": { "android_push": { "title":"Test push title", "alert":"Test push", "extra":{ "CUSTOM_KEY":"CUSTOM_VALUE" } } } }' https://rest.iad-01.braze.com/messages/send ``` ```bash curl -X POST -H "Content-Type: application/json" -H "Authorization: Bearer {BRAZE_API_KEY}" -d '{ "external_user_ids":["EXTERNAL_USER_ID"], "messages": { "apple_push": { "alert": "Test push", "extra": { "CUSTOM_KEY" :"CUSTOM_VALUE" } } } }' https://rest.iad-01.braze.com/messages/send ``` ```bash curl -X POST -H "Content-Type: application/json" -H "Authorization: Bearer {BRAZE_API_KEY}" -d '{ "external_user_ids":["EXTERNAL_USER_ID"], "messages": { "kindle_push": { "title":"Test push title", "alert":"Test push", "extra":{ "CUSTOM_KEY":"CUSTOM_VALUE" } } } }' https://rest.iad-01.braze.com/messages/send ``` Replace the following: | Placeholder | Description | |---------------------|-----------------------------------------------------------| | `BRAZE_API_KEY` | Your Braze API key used for authentication. In Braze, go to **Settings** > **API Keys** to locate your key. | | `EXTERNAL_USER_ID` | The external user ID used to send your message to a specific user. In Braze, go to **Audience** > **Search users**, then search for a user. | | `CUSTOM_KEY` | (Optional) A custom key for additional data. | | `CUSTOM_VALUE` | (Optional) A custom value assigned to your custom key. | {: .reset-td-br-1 .reset-td-br-2 aria-label="Step 2: Send the message" } ## Test limitations There are a few situations where test messages don't have complete feature parity with launching a campaign or Canvas to a real set of users. In these instances, to validate this behavior, you should launch the campaign or Canvas to a limited set of test users. - Viewing the Braze [preference center](https://www.braze.com/docs/ko/ko/user_guide/message_building_by_channel/email/managing_user_subscriptions/#subscription-groups) from **Test Messages** will cause the submit button to be grayed out. - The list-unsubscribe header is not included in emails sent by the test message functionality. - For in-app messages and Content Cards, the target user must have a push token for the target device. # Braze SDK의 고급 푸시 알림 예시 Source: /docs/ko/developer_guide/push_notifications/examples/index.md # 고급 푸시 알림 예시 {#advanced-push-notification-examples} > 다음 가이드에서는 Braze SDK의 고급 푸시 알림 예제를 다룹니다. ## Prerequisites Before you can use this feature, you'll need to [integrate the Android Braze SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=android). You'll also need to [set up push notifications](https://www.braze.com/docs/ko/ko/developer_guide/push_notifications/?sdktab=android). ## Custom notification layout Braze notifications are sent as [data messages](https://firebase.google.com/docs/cloud-messaging/concept-options), which means that your application will always have a chance to respond and perform behavior accordingly, even in the background (in contrast to notification messages, which can be handled automatically by the system when your app is in the background). As such, your application will have a chance to customize the experience by, for example displaying personalized UI elements within the notification delivered to the notification tray. While implementing push in this way may be unfamiliar to some, one of our well-known features at Braze, [Push Stories](https://www.braze.com/docs/ko/ko/user_guide/message_building_by_channel/push/advanced_push_options/push_stories/), are a prime example of using custom view components to create an engaging experience! **Important:** Android imposes some limitations on what components can be used to implement custom notification views. Notification view layouts must _only_ contain View objects compatible with the [RemoteViews](https://developer.android.com/reference/android/widget/RemoteViews) framework. You can use the [`IBrazeNotificationFactory`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze/-i-braze-notification-factory/index.html) interface to customize how Braze push notifications are displayed. By extending `BrazeNotificationFactory`, Braze will call your factory's `createNotification()` method before the notification is displayed to the user. It will then pass a payload containing custom key-value pairs sent through the Braze dashboard or REST API. In this section, you'll partner with Superb Owl, the host of a new game show where wildlife rescue teams compete to see who can save the most owls. They're looking to leverage live updating notifications in their Android app, so they can display the status of an on-going match and make dynamic updates to the notification in realtime. ![The Live Update that Superb Owl wants to show, displaying an on-going match between 'Wild Bird Fund' and 'Owl Rescue'. It's currently the fourth quarter and the score is 2-4 with OWL in the lead.](https://www.braze.com/docs/ko/ko/assets/img/android/android-live-activity-superb-owl-example.jpg?b7c1563bedc808b7a9aba3771231ddc4){: style="max-width:65%;"} ### Step 1: Add a custom layout You can add one or more custom notification RemoteView layouts to your project. These are helpful for handling how notifications are displayed when collapsed or expanded. Your directory structure should be similar to the following: ```plaintext . ├── app/ └── res/ └── layout/ ├── liveupdate_collapsed.xml └── liveupdate_expanded.xml ``` In each XML file, create a custom layout. Superb Owl created the following layouts for their collapsed and expanded RemoteView layouts: ```xml ``` **Show the sample code** ```xml ``` ### Step 2: Create a custom notification factory In your application, create a new file named `MyCustomNotificationFactory.kt` that extends [`BrazeNotificationFactory`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze/-i-braze-notification-factory/index.html) to handle how custom RemoteView layouts are displayed. In the following example, Superb Owl created a custom notification factory to display a RemoteView layout for on-going matches. In the [next step](#android_step-3-map-custom-data), they'll create a new method called `getTeamInfo` to map a team's data to the activity. **Show the sample code** ```kotlin import android.app.Notification import android.widget.RemoteViews import androidx.core.app.NotificationCompat import com.braze.models.push.BrazeNotificationPayload import com.braze.push.BrazeNotificationFactory import com.braze.push.BrazeNotificationUtils.getOrCreateNotificationChannelId import com.braze.support.BrazeLogger.brazelog class MyCustomNotificationFactory : BrazeNotificationFactory() { override fun createNotification(payload: BrazeNotificationPayload): Notification? { if (payload.extras.containsKey("live_update")) { val kvp = payload.extras val notificationChannelId = getOrCreateNotificationChannelId(payload) val context = payload.context if (context == null) { brazelog { "BrazeNotificationPayload has null context. Not creating notification" } return null } val team1 = kvp["team1"] val team2 = kvp["team2"] val score1 = kvp["score1"] val score2 = kvp["score2"] val time = kvp["time"] val quarter = kvp["quarter"] // Superb Owl will define the 'getTeamInfo' method in the next step. val (team1name, team1icon) = getTeamInfo(team1) val (team2name, team2icon) = getTeamInfo(team2) // Get the layouts to use in the custom notification. val notificationLayoutCollapsed = RemoteViews(BuildConfig.APPLICATION_ID, R.layout.liveupdate_collapsed) val notificationLayoutExpanded = RemoteViews(BuildConfig.APPLICATION_ID, R.layout.liveupdate_expanded) // Very simple notification for the small layout notificationLayoutCollapsed.setTextViewText( R.id.notification_title, "$team1 $score1 - $score2 $team2\n$time $quarter" ) notificationLayoutExpanded.setTextViewText(R.id.score, "$score1 - $score2") notificationLayoutExpanded.setTextViewText(R.id.team1name, team1name) notificationLayoutExpanded.setTextViewText(R.id.team2name, team2name) notificationLayoutExpanded.setTextViewText(R.id.timeInfo, "$time - $quarter") notificationLayoutExpanded.setImageViewResource(R.id.team1logo, team1icon) notificationLayoutExpanded.setImageViewResource(R.id.team2logo, team2icon) val customNotification = NotificationCompat.Builder(context, notificationChannelId) .setSmallIcon(R.drawable.notification_small_icon) .setStyle(NotificationCompat.DecoratedCustomViewStyle()) .setCustomContentView(notificationLayout) .setCustomBigContentView(notificationLayoutExpanded) .build() return customNotification } else { // Use the BrazeNotificationFactory for all other notifications return super.createNotification(payload) } } } ``` ### Step 3: Map custom data In `MyCustomNotificationFactory.kt`, create a new method for handling data when Live Updates are displayed. Superb Owl created the following method to map each team's name and logo to expanded Live Updates: ```kotlin class CustomNotificationFactory : BrazeNotificationFactory() { override fun createNotification(payload: BrazeNotificationPayload): Notification? { // Your existing code return super.createNotification(payload) } // Your new method private fun getTeamInfo(team: String?): Pair { return when (team) { "WBF" -> Pair("Wild Bird Fund", R.drawable.team_wbf) "OWL" -> Pair("Owl Rehab", R.drawable.team_owl) else -> Pair("Unknown", R.drawable.notification_small_icon) } } } ``` ### Step 4: Set the custom notification factory In your application class, use [`customBrazeNotificationFactory`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze/-braze/-companion/custom-braze-notification-factory.html?query=var%20customBrazeNotificationFactory:%20IBrazeNotificationFactory?)to set your custom notification factory. ```kotlin import com.braze.Braze class MyApplication : Application() { override fun onCreate() { super.onCreate() // Tell Braze to use your custom factory for notifications Braze.customBrazeNotificationFactory = MyCustomNotificationFactory() } } ``` ### Step 5: Send the activity You can use the [`/messages/send`](https://www.braze.com/docs/ko/ko/api/endpoints/messaging/send_messages/post_send_messages) REST API endpoint to send a push notification to a user's Android device. #### Example curl command Superb Owl sent their request using the following curl command: ``` curl -X POST "https://BRAZE_REST_ENDPOINT/messages/send" \ -H "Authorization: Bearer {REST_API_KEY}" \ -H "Content-Type: application/json" \ --data '{ "external_user_ids": ["USER_ID"], "messages": { "android_push": { "title": "WBF vs OWL", "alert": "2 to 4 1:33 Q4", "extra": { "live_update": "true", "team1": "WBF", "team2": "OWL", "score1": "2", "score2": "4", "time": "1:33", "quarter": "Q4" }, "notification_id": "ASSIGNED_NOTIFICATION_ID" } } }' ``` **Tip:** While curl commands are helpful for testing, we recommend handling this call in your backend where you're already handling your [iOS Live Activities](https://www.braze.com/docs/ko/ko/developer_guide/push_notifications/live_notifications/?sdktab=swift). #### Request parameters | Key | Description | | ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `REST_API_KEY` | A Braze REST API key with `messages.send` permissions.

This can be created in the Braze dashboard from **Settings** > **API Keys**. | | `BRAZE_REST_ENDPOINT` | Your REST endpoint URL. Your endpoint will depend on the [Braze URL for your instance](https://www.braze.com/docs/ko/ko/api/basics/#endpoints). | | `USER_ID` | The ID of the user you are sending the notification to. | | `messages.android_push.title` | The message's title. By default, this is not used for the custom notification factory's live notifications, but it may be used as a fallback. | | `messages.android_push.alert` | The message's body. By default, this is not used for the custom notification factory's live notifications, but it may be used as a fallback. | | `messages.extra` | Key-value pairs that the custom notification factory uses for live notifications. You can assign any string to this value—however, in the example above, `live_updates` is used to determine if it's a default or live push notification. | | `ASSIGNED_NOTIFICATION_ID` | The notification ID you want to assign to the chosen user's live notification. The ID must be unique to this game, and must be used in order to [update their existing notification](#android_step-4-update-data-with-the-braze-rest-api) later. | {: .reset-td-br-1 .reset-td-br-2 aria-label="Request parameters" } ### Step 6: Update the activity To update the existing RemoteView notification with new data, modify the relevant key-value pairs assigned to `messages.extra`, then use the same `notification_id` and call the `/messages/send` endpoint again. ## Personalized push notifications Push notifications can display user-specific information inside a custom view hierarchy. In the following example, an API-trigger is used to send personalized push notification to a user so they can track check their current progress after completing a specific task in the app. ![Personalized Push dashboard Example](https://www.braze.com/docs/ko/ko/assets/img/push_implementation_guide/android_push_custom_layout.png?42a0a3df3a4069479bfb1db1bd65bde1){: style="max-width:65%;border:0"} To set up a personalized push in the dashboard, register the specific category you want to be displayed, then set any relevant user attributes you'd like to display using Liquid. ![Personalized Push dashboard Example](https://www.braze.com/docs/ko/ko/assets/img/push_implementation_guide/push5.png?199277e2adf2d1ded48e5dba4c2d7b4a){: style="max-width:60%;"} ## Prerequisites Before you can use this feature, you'll need to [integrate the Swift Braze SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=swift). You'll also need to [set up push notifications](https://www.braze.com/docs/ko/ko/developer_guide/push_notifications/?sdktab=swift). **Note:** This implementation guide is centered around a Swift implementation, but Objective-C snippets are provided for those interested. ## Notification content app extensions ![Two push messages shown side-by side. The message on the left shows what a push looks like with the default UI. The message on the right shows a coffee punch card push made by implementing a custom push UI.](https://www.braze.com/docs/ko/ko/assets/img/push_implementation_guide/push1.png?d04035fb11637f7db51f24a1afab9e8f){: style="max-width:65%;border:0;margin-top:10px"} Notification content app extensions provide you a great option for push notification customization. Notification content app extensions display a custom interface for your app’s notifications when a push notification is expanded. Push notifications can be expanded in three different ways: - A long press on the push banner - Swiping down on the push banner - Swiping the banner to the left and selecting "View" These custom views offer smart ways to engage customers by displaying distinct types of content, including interactive notifications, notifications populated with user data, and even push messages that can capture information like phone numbers and email. One of our well-known features at Braze, [Push Stories](https://www.braze.com/docs/ko/ko/user_guide/message_building_by_channel/push/advanced_push_options/push_stories/), are a prime example of what a push notification content app extension can look like! ### Requirements ![Xcode's 'Choose a template for your new target' screen with 'Notification Content Extension' selected under Application Extension.](https://www.braze.com/docs/ko/ko/assets/img/push_implementation_guide/push15.png?64059ffe5c16313ee6377e0a79405812){: style="float:right;max-width:50%;margin-left:10px; border:0;margin-top:10px"} - [Push notifications](https://www.braze.com/docs/ko/ko/developer_guide/push_notifications/?sdktab=swift) successfully integrated in your app - The following files generated by Xcode based on your coding language: **Swift**
- `NotificationViewController.swift` - `MainInterface.storyboard` **Objective-C**
- `NotificationViewController.h` - `NotificationViewController.m` - `MainInterface.storyboard` ## Interactive push notification Push notifications can respond to user actions inside a content app extension. For users running iOS 12 or later, this means you can turn your push notifications into fully interactive messages! This provides an exciting option to introduce interactivity to your promotions and applications. For example, your push notification can include a game for users to play, a spin-to-win wheel for discounts, or a "like" button to save a listing or song. The following example shows a push notification where users are able to play a match game inside the expanded notification. ![A diagram of what the phases of a interactive push notification could look like. A sequence shows a user pressing into a push notification that displays an interactive matching game.](https://www.braze.com/docs/ko/ko/assets/img/push_implementation_guide/push12.png?e32579b6de7f5aec62265828724d6657){: style="border:0"} ### Dashboard configuration To create an interactive push notification, you must set a custom view in your dashboard. 1. From the **Campaigns** page, click **Create Campaign** to start a new push notification campaign. 2. On the **Compose** tab, toggle on **Notification Buttons**. 3. Enter a custom iOS category in the **iOS Notification Category** field. 4. In the `.plist` of your Notification Content Extension Target, set the `UNNotificationExtensionCategory` attribute to your custom iOS category. The value given here must match what is set in the Braze dashboard under **iOS Notification Category**. 5. Set the `UNNotificationExtensionInteractionEnabled` key to `true` to enable user interactions in a push notification. ![The notification button options found in the push message composer settings.](https://www.braze.com/docs/ko/ko/assets/img/push_implementation_guide/push16.png?be40aad198215645c3ef4ac2553267f4){: style="max-width:75%;border:0;margin-top:10px"} ![A plist showing NSExtension with UNNotificationExtensionCategory set to "your_custom_category", UNNotificationExtensionDefaultContentHidden set to 1, and UNNotificationExtensionInitialContentSizeRatio set to 1.](https://www.braze.com/docs/ko/ko/assets/img/push_implementation_guide/push17.png?42f5ff77b6402aade26b936b5c78dbc7){: style="max-width:75%;border:0;margin-top:10px"} ## Personalized push notifications ![Two iPhones displayed side-by-side. The first iPhone shows the unexpanded view of the push message. The second iPhone shows the expanded version of the push message displaying a "progress" shot of how far they are through a course, the name of the next session, and when the next session must be completed.](https://www.braze.com/docs/ko/ko/assets/img/push_implementation_guide/push6.png?438d9acc8285244397d14467a8a63d3a){: style="float:right;max-width:40%;margin-left:15px;border:0"} Push notifications can display user-specific information inside a content extension. This allows you to create user-focused push content, such as adding the option to share your progress across different platforms, show unlocked achievements, or display onboarding checklists. This example shows a push notification displayed to a user after they have completed a specific task in the Braze Learning course. By expanding the notification, the user can see their progress through their learning path. The information provided here is user-specific and can be fired off as a session is completed or a specific user action is taken by leveraging an API trigger. ### Dashboard configuration To create a personalized push notification, you must set a custom view in your dashboard. 1. From the **Campaigns** page, click **Create Campaign** to start a new push notification campaign. 2. On the **Compose** tab, toggle on **Notification Buttons**. 3. Enter a custom iOS category in the **iOS Notification Category** field. 4. In the **Settings** tab, create key-value pairs using standard Liquid. Set the appropriate user attributes you want the message to show. These views can be personalized based on specific user attributes of a specific user profile. 5. In the `.plist` of your Notification Content Extension Target, set the `UNNotificationExtensionCategory` attribute to your custom iOS category. The value given here must match what is set in the Braze dashboard under **iOS Notification Category**. ![Four sets of key-value pairs, where "next_session_name" and "next_session_complete_date" are set as an API trigger property using Liquid, and "completed_session count" and "total_session_count" are set as a custom user attribute using Liquid.](https://www.braze.com/docs/ko/ko/assets/img/push_implementation_guide/push5.png?199277e2adf2d1ded48e5dba4c2d7b4a){: style="max-width:60%;"} ### Handling key-value pairs The method `didReceive` is called when the notification content app extension has received a notification. This method can be found within the `NotificationViewController`. The key-value pairs provided in the dashboard are represented in the code through the use of a `userInfo` dictionary. #### Parsing Key-Value Pairs from Push Notifications ``` swift func didReceive(_ notification: UNNotification) { let userInfo = notification.request.content.userInfo guard let value = userInfo["YOUR-KEY-VALUE-PAIR"] as? String, let otherValue = userInfo["YOUR-OTHER-KEY-VALUE-PAIR"] as? String, else { fatalError("Key-Value Pairs are incorrect.")} ... } ``` ```objc - (void)didReceiveNotification:(nonnull UNNotification *)notification { NSDictionary *userInfo = notification.request.content.userInfo; if (userInfo[@"YOUR-KEY-VALUE-PAIR"] && userInfo[@"YOUR-OTHER-KEY-VALUE-PAIR"]) { ... } else { [NSException raise:NSGenericException format:@"Key-Value Pairs are incorrect"]; } } ``` ## Information capture push notification Push notifications can capture user information inside a content app extension, pushing the limits of what is possible with a push. Requesting user input through push notifications allows you to not only request basic information like name or email, but also prompt users to submit feedback or complete an unfinished user profile. **Tip:** For more information, see [Logging push notification data](https://www.braze.com/docs/ko/ko/developer_guide/analytics/logging_channel_data/push_notifications/). In the following flow, the custom view is able to respond to state changes. Those state change components are represented in each image. 1. User receives a push notification. 2. Push is opened. After expanded, the push prompts the user for information. In this example, the user's email address is requested, but you could request any sort of information. 3. Information is provided, and if in the expected format, the registration button is shown. 3. Confirmation view is displayed, and push gets dismissed. ### Dashboard configuration To create an information capture push notification, you must set a custom view in your dashboard. 1. From the **Campaigns** page, click **Create Campaign** to start a new push notification campaign. 2. On the **Compose** tab, toggle on **Notification Buttons**. 3. Enter a custom iOS category in the **iOS Notification Category** field. 4. In the **Settings** tab, create key-value pairs using standard Liquid. Set the appropriate user attributes you want the message to show. 5. In the `.plist` of your Notification Content Extension Target, set the `UNNotificationExtensionCategory` attribute to your custom iOS category. The value given here must match what is set in the Braze dashboard under **iOS Notification Category**. As seen in the example, you may also include an image in your push notification. To do this, you must integrate [rich notifications](https://www.braze.com/docs/ko/ko/developer_guide/push_notifications/rich/?sdktab=swift), set the notification style in your campaign to Rich Notification, and include a rich push image. ![A push message with three sets of key-value pairs. 1. "Braze_id" set as a Liquid call to retrieve Braze ID. 2. "cert_title" set as "Braze Marketer Certification". 3. "Cert_description" set as "Certified Braze marketers drive...".](https://www.braze.com/docs/ko/ko/assets/img/push_implementation_guide/push9.png?4f1d1fc129e7f564d006e649dc0ef582) ### Handling button actions Each action button is uniquely identified. The code checks if your response identifier is equal to the `actionIdentifier`, and if so, knows that the user clicked the action button. **Handling Push Notification Action Button Responses**
``` swift func didReceive(_ response: UNNotificationResponse, completionHandler completion: @escaping (UNNotificationContentExtensionResponseOption) -> Void) { if response.actionIdentifier == "YOUR-REGISTER-IDENTIFIER" { // do something } else { // do something else } } ``` ```objc - (void)didReceiveNotificationResponse:(UNNotificationResponse *)response completionHandler:(void (^)(UNNotificationContentExtensionResponseOption))completion { if ([response.actionIdentifier isEqualToString:@"YOUR-REGISTER-IDENTIFIER"]) { completion(UNNotificationContentExtensionResponseOptionDismiss); } else { completion(UNNotificationContentExtensionResponseOptionDoNotDismiss); } } ``` ### Dismissing pushes Push notifications can be automatically dismissed from an action button press. There are three pre-built push dismissal options that we recommend: 1. `completion(.dismiss)` - Dismisses the notification 2. `completion(.doNotDismiss)` - Notification stays open 3. `completion(.dismissAndForward)` - Push dismisses and the user gets forwarded into the application # 푸시 구독 상태에 대하여 Source: /docs/ko/developer_guide/push_notifications/subscription_states/index.md # 푸시 구독 상태에 대하여 ## Push subscription states {#push-sub-states} A "Push Subscription State" in Braze identifies a **user's** global preference for their desire to receive push notifications. Because the subscription state is user-based, it is not specific to any individual app. Subscription states become helpful flags when deciding which users to target for push notifications. **Note:** A user's push subscription state applies to their entire user profile, which includes all of the user's devices. The following subscription state options exist: `Subscribed`, `Opted-In`, and `Unsubscribed`. By default, for your user to receive your messages through push, their push subscription state must be either `Subscribed` or `Opted-In`, and they must have foreground push enabled. You can override this setting if needed when composing a message. |Opt-in State|Description| |---|---| |`Subscribed`| Default push subscription state when a user profile is created in Braze. | |`Opted-In`| A user has explicitly expressed a preference to receive push notifications. Braze automatically moves a user's opt-in state to `Opted-In` if the user accepts an OS-level push prompt.

This does not apply to Android 12 or below users.| |`Unsubscribed`| A user explicitly unsubscribed from push through your application or other methods your brand provides. By default, Braze push campaigns target only users that are `Subscribed` or `Opted-in` for push.| {: .reset-td-br-1 .reset-td-br-2 aria-label="Push subscription states #push-sub-states" } **Important:** Braze does not automatically change a user's push subscription state to `Unsubscribed`. Remember that if a user's push subscription state is `Unsubscribed`, then the user's `Foreground Push Enabled` filter in segmentation is `false`. ### Push registration and reachable users Push subscription state reflects a user's preference, but whether they count as **reachable** for push in the dashboard also depends on [push registration](https://www.braze.com/docs/ko/ko/user_guide/channels/push/push_setup/push_token_lifecycle/)—that is, a valid foreground push token on their profile. For how Braze calculates channel-level counts, see [Measure segment size](https://www.braze.com/docs/ko/ko/user_guide/audience/segments/measuring_segment_size/). - **Push campaigns and Canvases:** Users who aren't push registered aren't included in **Reachable users** for Android Push or iOS Push in audience statistics, even when their push subscription state is `Subscribed` or `Opted-In`. - **Other channels:** The same users can still count as reachable for other channels they qualify for (for example, email or in-app messages). - **Segments:** Segment membership follows your filters. Users without push registration remain in the segment unless a filter excludes them (for example, **Foreground Push Enabled**). Total segment membership can be higher than the sum of users shown in push-specific **Reachable users** rows. A user profile can show push subscription state `Subscribed` while no push token is assigned. Those users still don't count toward **Reachable users** for Android Push or iOS Push until Braze records a valid token. For filter definitions, see [Segmentation filters](https://www.braze.com/docs/ko/ko/user_guide/audience/segments/segmentation_filters/). ### Updating push subscription states {#update-push-subscription-state} Review the following ways to update a user's push subscription state: #### Automatic opt-in (default) By default, Braze sets a user's push subscription state to `Opted-In` when they first authorize push notifications for your app. Braze also does this when a user re-enables push permissions in their system settings after previously disabling them. To disable this default behavior, add the following property to your Android Studio project's `braze.xml` file: ```xml false ``` On iOS, a new install typically shows push subscription state **`Subscribed`** until the user allows notifications. After the user selects **Allow** on the OS prompt, Braze sets the state to **`Opted-In`** when automatic opt-in is enabled. If the user selects **Don't Allow** and later turns push on in iOS Settings, the state updates after the user logs a session—not at the moment they change Settings. Starting with [Braze Swift SDK version 7.5.0](https://github.com/braze-inc/braze-swift-sdk/releases/tag/7.5.0), you can disable or further customize this behavior by adding the `optInWhenPushAuthorized` configuration to your Xcode project's `AppDelegate.swift` file: ```swift configuration.optInWhenPushAuthorized = false // disables the default behavior let braze = Braze(configuration: configuration) AppDelegate.braze = braze ``` #### SDK integration You can update a user's subscription state with the Braze SDK using the `setPushNotificationSubscriptionType` method on [Web](https://js.appboycdn.com/web-sdk/latest/doc/classes/braze.user.html#setpushnotificationsubscriptiontype), [Android](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze/-braze-user/set-push-notification-subscription-type.html), or [iOS](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/user-swift.class/set(pushnotificationsubscriptionstate:)). For example, you can use this method to create a settings page in your app where users can manually enable or disable push notifications. #### REST API You can update a user's subscription state with the Braze REST API using the [`/users/track` endpoint](https://www.braze.com/docs/ko/ko/api/endpoints/user_data/post_user_track/) to update their [`push_subscribe`](https://www.braze.com/docs/ko/ko/api/objects_filters/user_attributes_object) attribute. ### Differences between push enablement and push subscription status Push enablement refers to whether a user has granted OS- or browser-level permission to receive notifications on a specific device. Push subscription state is a Braze-level setting that represents a user's global preference for receiving push across their profile. When automatic opt-in is enabled (the default), Braze updates a user's push subscription state to `Opted-In` when they authorize push notifications for your app or re-enable permissions in their system settings (for example, on iOS, Android 13+, and supported web browsers). Otherwise, the user's push subscription state remains `Subscribed` until you explicitly change it using an SDK method or REST API call. Braze does not automatically change a user's push subscription state to `Unsubscribed` when they opt out of notifications at the OS, browser, or app level. To update a user's push subscription state, you must update it in Braze. For example, if a user disables push from an in-app preference center, update the push subscription state to `Unsubscribed` in Braze. Braze does not update user profiles based on your preference center. To align subscription states with a user's in-app preferences, call the appropriate methods using the [SDK](https://www.braze.com/docs/ko/ko/user_guide/channels/push/push_setup/push_subscription_states/#sdk-integration) (iOS or Android) or [REST API](https://www.braze.com/docs/ko/ko/user_guide/channels/push/push_setup/push_subscription_states/#rest-api). ### Imported push tokens (iOS) When you [import iOS push tokens](https://www.braze.com/docs/ko/ko/api/endpoints/user_data/post_user_track/#push-token-import) with `push_token_import`, the user's push subscription state is typically **`Subscribed`** until they log a session in your Braze-integrated app. After the first session, Braze may update the state to **`Opted-In`** if [automatic opt-in](#automatic-opt-in-default) applies (for example, when the user authorizes push on iOS and `optInWhenPushAuthorized` is enabled). Review **Contact Settings** on the user's profile after import and again after the user's first in-app session to confirm the expected state. ### Checking push subscription state ![User profile for John Doe with their push subscription state set to Subscribed.](https://www.braze.com/docs/ko/ko/assets/img/push_example.png?35176b34da21057d058dc0b0f0e3d9f7){: style="float:right;max-width:35%;margin-left:15px;"} You can check a user's push subscription state with Braze in any of the following ways: * **User profile:** You can access individual user profiles through the Braze dashboard on the **[User Search](https://www.braze.com/docs/ko/ko/user_guide/engagement_tools/segments/user_profiles/)** page. After finding a user's profile (via email address, phone number, or external user ID), you can select the **Engagement** tab to view and manually adjust a user's subscription state. * **REST API export:** You can export individual user profiles in JSON format using the export [Users by segment](https://www.braze.com/docs/ko/ko/api/endpoints/export/user_data/post_users_segment/) or [Users by identifier](https://www.braze.com/docs/ko/ko/api/endpoints/export/user_data/post_users_identifier/) endpoints. Braze returns a push tokens object that contains push enablement information per device. # Braze SDK의 푸시 알림 문제 해결 Source: /docs/ko/developer_guide/push_notifications/troubleshooting/index.md # 푸시 알림 문제 해결 {#troubleshoot-push-notifications} > Braze SDK의 푸시 알림 문제를 해결하는 방법을 알아보세요. ## Troubleshooting If you're experiencing issues after setting up push notifications, consider the following: - Web push notifications require that your site be HTTPS. - Not all browsers can receive push messages. Ensure that `braze.isPushSupported()` returns `true` in the browser. - Some browsers, such as Firefox, do not display images in push notifications. For details on browser support, refer to the [MDN documentation for Notification images](https://developer.mozilla.org/en-US/docs/Web/API/Notification/image). - If a user has denied a site push access, they won't be prompted for permission again unless they remove the denied status from their browser preferences. ## Understanding the Braze push workflow The Firebase Cloud Messaging (FCM) service is Google's infrastructure for push notifications sent to Android applications. Here is the simplified structure of how push notifications are enabled for your users' devices and how Braze can send push notifications to them: ```mermaid --- config: theme: mc --- sequenceDiagram participant Device as User Device participant App as Android App participant BrazeSDK as Braze SDK participant BrazeAPI as Braze Server participant Firebase as Google Firebase Note over Device, Firebase: Register Option 1
Register Automatically using `com_braze_firebase_cloud_messaging_registration_enabled` in braze.xml App ->> Braze: App initializes Braze with the first Braze call
This could be automatic session handling BrazeSDK ->> App: Get push token from Firebase Manager BrazeSDK ->> BrazeAPI: Send push token to Braze Server Note right of BrazeAPI: Braze will remove push token from any
other user who may have previously
been logged in on the same device. Note over Device, Firebase: Register Option 2
Manual registration. App ->> BrazeSDK: App sets `Braze.registeredPushToken` BrazeSDK ->> BrazeAPI: Send push token to Braze Server Note right of BrazeAPI: Braze will remove push token from any
other user who may have previously
been logged in on the same device. Note over Device, Firebase: Push permission BrazeAPI ->> BrazeSDK: In-App Message containing push prompt BrazeSDK -> App: In-App Message is displayed App -> BrazeSDK: User requests permissions BrazeSDK -> App: Displays the Push Authorization prompt BrazeSDK -> BrazeAPI: If authorized and `com_braze_optin_when_push_authorized`, Opt-In value is sent. Note over Device, Firebase: Push Notification Is Sent BrazeAPI ->> Firebase: Sends push message Firebase ->> Device: Push message sent Device ->> App: Android will send the push to the App.
This could be blocked to Do Not Disturb, Power Saving Mode, etc. App ->> BrazeSDK: Message is sent to BrazeFirebaseMessagingService BrazeSDK ->> Device: SDK will check if the push is from Braze.
If so, push data is transformed into a Push Notification and displayed. ``` ### Step 1: Configuring your Google Cloud API key In developing your app, you'll need to provide the Braze Android SDK with your Firebase sender ID. Additionally, you'll need to provide an API Key for server applications to the Braze dashboard. Braze will use this API key to send messages to your devices. You will also need to check that FCM service is enabled in Google Developer's console. **Note:** A common mistake during this step is using the app identifier API key instead of the REST API key. ### Step 2: Devices register for FCM and provide Braze with push tokens In typical integrations, the Braze Android SDK will handle registering devices for FCM capability. This will usually happen immediately upon opening the app for the first time. After registration, Braze will be provided with an FCM Registration ID, which is used to send messages to that device specifically. We will store the Registration ID for that user, and that user will become "push registered" if they previously did not have a push token for any of your apps. ### Step 3: Launching a Braze push campaign When a push campaign is launched, Braze will make requests to FCM to deliver your message. Braze will use the API key copied in the dashboard to authenticate and verify that we can send push notifications to the push tokens provided. ### Step 4: Removing invalid tokens If FCM informs us that any of the push tokens we were attempting to send a message to are invalid, we remove those tokens from the user profiles they were associated with. If users have no other push tokens, they will no longer show up as "Push Registered" under the **Segments** page. For more details about FCM, visit [Cloud messaging](https://firebase.google.com/docs/cloud-messaging/). ## Utilizing the push error logs Braze provides push notification errors within the message activity log. This error log provides a variety of warnings which can be very helpful for identifying why your campaigns aren't working as expected. Clicking on an error message will redirect you to relevant documentation to help you troubleshoot a particular incident. ![](https://www.braze.com/docs/ko/ko/assets/img_archive/message_activity_log.png?6577302323ab3f2df3196a973320b8d3) ## Troubleshooting scenarios ### Push isn't sending Your push messages might not be sending because of the following situations: - Your credentials exist in the wrong Google Cloud Platform project ID (wrong sender ID). - Your credentials have the wrong permission scope. - You uploaded wrong credentials to the wrong Braze workspace (wrong sender ID). For other issues that may prevent you from sending a push message, refer to [User Guide: Troubleshooting Push Notifications](https://www.braze.com/docs/ko/ko/user_guide/message_building_by_channel/push/troubleshooting/). ### No "push registered" users showing in the Braze dashboard (prior to sending messages) Confirm that your app is correctly configured to allow push notifications. Common failure points to check include: #### Incorrect sender ID Check that the correct FCM sender ID is included in the `braze.xml` file. An incorrect sender ID will lead to `MismatchSenderID` errors reported in the dashboard's message activity log. #### Braze registration not occurring Since FCM registration is handled outside of Braze, failure to register can only occur in two places: 1. During registration with FCM 2. When passing the FCM-generated push token to Braze We recommend setting a breakpoint or logging to confirm that the FCM-generated push token is being sent to Braze. If a token is not generated correctly or at all, we recommend consulting the [FCM documentation](https://firebase.google.com/docs/cloud-messaging/android/client). #### Google Play Services not present For FCM push to work, Google Play Services must be present on the device. If Google Play Services isn't on a device, push registration will not occur. **Note:** Google Play Services is not installed on Android emulators without Google APIs installed. #### Device not connected to the internet Check that your device has good internet connectivity and isn't sending network traffic through a proxy. ### Tapping push notification doesn't open the app Check if `com_braze_handle_push_deep_links_automatically` is set to `true` or `false`. To enable Braze to automatically open the app and any deep links when a push notification is tapped, set `com_braze_handle_push_deep_links_automatically` to `true` in your `braze.xml` file. If `com_braze_handle_push_deep_links_automatically` is set to its default of `false`, you need to use a Braze Push Callback to listen for and handle the push received and opened intents. ### Push notifications bounced If a push notification isn't delivered, make sure it didn't bounce by looking in the [developer console](https://www.braze.com/docs/ko/ko/developer_guide/platforms/android/push_notifications/troubleshooting/#utilizing-the-push-error-logs). The following are descriptions of common errors that may be logged in the developer console: #### Error: MismatchSenderID `MismatchSenderID` indicates an authentication failure. Confirm your Firebase sender ID and FCM API key are correct. #### Error: InvalidRegistration `InvalidRegistration` can be caused by a malformed push token. 1. Make sure to pass a valid push token to Braze from [Firebase Cloud Messaging](https://firebase.google.com/docs/cloud-messaging/android/client#retrieve-the-current-registration-token). #### Error: NotRegistered 1. `NotRegistered` typically occurs when an app has been deleted from a device. Braze uses `NotRegistered` internally to signal that an app has been uninstalled from a device. 2. `NotRegistered` may also occur when multiple registrations occur and a second registration invalidates the first token. ### Push notifications sent but not displayed on users' devices There are a few reasons why this could be occurring: #### Application was force quit If you force-quit your application through your system settings, your push notifications will not be sent. Launching the app again will re-enable your device to receive push notifications. #### BrazeFirebaseMessagingService not registered The BrazeFirebaseMessagingService must be properly registered in `AndroidManifest.xml` for push notifications to appear: ```xml ``` #### Firewall is blocking push If you are testing push over Wi-Fi, your firewall may be blocking ports necessary for FCM to receive messages. Confirm that ports `5228`, `5229`, and `5230` are open. Additionally, since FCM doesn't specify its IPs, you must also allow your firewall to accept outgoing connections to all IP addresses contained in the IP blocks listed in Google's ASN of `15169`. #### Custom notification factory returning null If you have implemented a [custom notification factory](https://www.braze.com/docs/ko/ko/developer_guide/platform_integration_guides/android/push_notifications/android/integration/standard_integration/#custom-displaying-notifications), ensure that it is not returning `null`. This will cause notifications not to be displayed. ### "Push registered" users no longer enabled after sending messages There are a few reasons why this could be happening: #### Application was uninstalled Users have uninstalled the application. This will invalidate their FCM push token. #### Invalid Firebase Cloud Messaging server key The Firebase Cloud Messaging server key provided in the Braze dashboard is invalid. The sender ID provided should match the one referenced in your app's `braze.xml` file. The server key and sender ID are found here in your Firebase Console: ![The Firebase platform under "Settings" and then "Cloud Messaging" will display your server ID and server key.](https://www.braze.com/docs/ko/ko/assets/img_archive/finding_firebase_server_key.png?de34d7ce2b1ae4b9c4a9d543b5b40585 "FirebaseServerKey") ### Push clicks not logged Braze logs push clicks automatically, so this scenario should be comparatively rare. If push clicks are not being logged, it is possible that push click data has not been flushed to our servers yet. Braze throttles the frequency of its flushes based on the strength of the network connection. With a good network connection, push click-data should arrive at the server within a minute in most circumstances. ### Deep links not working #### Verify deep link configuration Deep links can be [tested with ADB](https://developer.android.com/training/app-indexing/deep-linking.html#testing-filters). We recommend testing your deep link with the following command: `adb shell am start -W -a android.intent.action.VIEW -d "THE_DEEP_LINK" THE_PACKAGE_NAME` If the deep link fails to work, the deep link may be misconfigured. A misconfigured deep link will not work when sent through Braze push. #### Verify custom handling logic If the deep link [works correctly with ADB](https://developer.android.com/training/app-indexing/deep-linking.html#testing-filters) but fails to work from Braze push, check whether any [custom push open handling](https://www.braze.com/docs/ko/ko/developer_guide/platform_integration_guides/android/push_notifications/android/integration/standard_integration/#android-push-listener-callback) has been implemented. If so, verify that the custom handling code properly handles the incoming deep link. #### Disable back stack behavior If the deep link [works correctly with ADB](https://developer.android.com/training/app-indexing/deep-linking.html#testing-filters) but fails to work from Braze push, try disabling [back stack](https://developer.android.com/guide/components/activities/tasks-and-back-stack). To do so, update your **braze.xml** file to include: ```xml false ``` ## Understanding the Braze/APNs workflow The Apple Push Notification service (APNs) is the infrastructure for sending push notifications to applications running on Apple's platforms. Here is the simplified structure of how push notifications are enabled for your users' devices and how Braze can send push notifications to them: 1. You configure the push certificate and provisioning profile 2. Devices register for APNs and provide Braze with push tokens 3. You launch a Braze push campaign 4. Braze removes invalid tokens ### Step 1: Configuring the push certificate and provisioning profile In developing your app, you'll need to create an SSL certificate to enable push notifications. This certificate will be included in the provisioning profile your app is built with and will also need to be uploaded to the Braze dashboard. The certificate allows Braze to tell APNs that we are allowed to send push notifications on your behalf. There are two types of [provisioning profiles](https://developer.apple.com/library/content/documentation/IDEs/Conceptual/AppDistributionGuide/MaintainingProfiles/MaintainingProfiles.html) and certificates: development and distribution. We recommend just using distribution profiles and certificates to avoid any confusion. If you choose to use different profiles and certificates for development and distribution, ensure that the certificate uploaded to the dashboard matches the provisioning profile you are currently using. **Warning:** Do not change the push certificate environment (development versus production). Changing the push certificate to the wrong environment can lead to your users having their push token accidentally removed, making them unreachable by push. ### Step 2: Devices register for APNs and provide Braze with push tokens When users open your app, they will be prompted to accept push notifications. If they accept this prompt, APNs will generate a push token for that particular device. The Swift SDK will immediately and asynchronously send up the push token for apps using the default [automatic flush policy](https://www.braze.com/docs/ko/ko/developer_guide/platform_integration_guides/swift/advanced_use_cases/fine_network_traffic_control/#automatic-request-processing). After we have a push token associated with a user, they will show as "Push Registered" in the dashboard on their user profile under the **Engagement** tab and will be eligible to receive push notifications from Braze campaigns. **Note:** Starting in macOS 13, on certain devices, you can test push notifications on an iOS 16 Simulator running on Xcode 14. For further details, refer to the [Xcode 14 Release Notes](https://developer.apple.com/documentation/xcode-release-notes/xcode-14-release-notes). #### Considerations for push token generation - If users install your app on another device, another token will be created and captured in the same way. - If users reinstall your app, a new token will be generated and passed to Braze. However, the original token may still be logged as valid by APNs and Braze. - If users uninstall your app, Braze doesn't get immediately notified of this and the token will still appear as valid until it is retired by APNs. - At some point, APNs will retire old tokens. Braze doesn't have control or visibility of this. ### Step 3: Launching a Braze push campaign When a push campaign is launched, Braze will make requests to APNs to deliver your message. Specifically, the requests are passed to APNs for each current valid push token unless **Send to a user's most recent device** is selected. After Braze receives a successful response from APNs, we will log a successful delivery on the user profile, though the user may not have received the actual message for reasons including: - Their device is powered off. - Their device isn't connected to the internet (Wi-Fi or cellular). - They recently uninstalled the app. Braze will use the SSL push certificate uploaded in the dashboard to authenticate and verify that we are allowed to send push notifications to the push tokens provided. If a device is online, the notification should be received shortly after the campaign has been sent. Note that Braze sets the default APNs [expiration date](https://developer.apple.com/documentation/usernotifications/setting_up_a_remote_notification_server/sending_notification_requests_to_apns#2947607) for notifications to 30 days. ### Step 4: Removing invalid tokens If [APNs](https://developer.apple.com/library/content/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/APNSOverview.html#//apple_ref/doc/uid/TP40008194-CH8-SW1) informs us that any of the push tokens we were attempting to send a message to are invalid, we remove those tokens from the user profiles they were associated with. **Note:** It's normal for APNs to initially return a success status even if a token becomes unregistered, as APNs doesn't immediately report token invalidation events. APNs intentionally delays returning a `410` status for invalid tokens on a randomized schedule, designed to protect user privacy and prevent tracking of app uninstalls. You can safely continue sending notifications to an unregistered token until APNs returns a `410` status. ## Using the push error logs The [Message Activity Log](https://www.braze.com/docs/ko/ko/user_guide/administrative/app_settings/message_activity_log_tab/) gives you the opportunity to see any messages (especially error messages) associated with your campaigns and sends, including push notification errors. This error log provides a variety of warnings which can be very helpful for identifying why your campaigns aren't working as expected. Clicking on an error message will redirect you to relevant documentation to help you troubleshoot a particular incident. ![Push error logs displaying the time the error occurred, the app name, the channel, error type, and error message.](https://www.braze.com/docs/ko/ko/assets/img_archive/message_activity_log.png?6577302323ab3f2df3196a973320b8d3) Common errors you might see here include user-specific notifications, such as ["Received Unregistered Sending to Push Token"](#swift_received-unregistered-sending). In addition, Braze also provides a push changelog on the user profile under the **Engagement** tab. This changelog provides insight into push registration behavior such as token invalidation, push registration errors, tokens being moved to new users, etc. ![](https://www.braze.com/docs/ko/ko/assets/img_archive/push_changelog.gif?36d10186d33121a195e943385dd0d02a){: style="max-width:50%;" } ### Message Activity Log errors #### Received unregistered sending to push token {#received-unregistered-sending} - Make sure that the push token being sent to Braze from the method `AppDelegate.braze?.notifications.register(deviceToken:)` is valid. You can look in the **Message Activity Log** to see the push token. It should look something like `6e407a9be8d07f0cdeb9e724733a89445f57a89ec890d63867c482a483506fa6`, a long string containing a mix of letters and numbers. If your push token looks different, check your [code](https://www.braze.com/docs/ko/ko/developer_guide/platform_integration_guides/swift/push_notifications/integration/#step-4-register-push-tokens-with-braze) for sending Braze the push tokens. - Ensure that your push provisioning profile matches the environment you're testing. Universal certificates may be configured in the Braze dashboard to send to either the development or production APNs environment. Using a development certificate for a production app or a production certificate for a development app will not work. - Check that the push token you have uploaded to Braze matches the provisioning profile you used to build the app you sent the push token from. #### Device token not for topic APNs returns `DeviceTokenNotForTopic` (HTTP status 400) when the push token doesn't match the topic (bundle ID) configured for your credentials. Braze may surface this in **Message Activity Log** or push delivery logs as `DeviceTokenNotForTopic`. To resolve the mismatch: 1. Confirm the app's **bundle ID** matches the **App Bundle ID** in Braze (**Settings** > **App Settings** > **Push Notification Settings**). 2. Verify the provisioning profile used to build the app includes push capability for that bundle ID. 3. Confirm the push credential uploaded to Braze matches the app's environment (development versus production). 4. For `.p8` keys, verify **Team ID** and **Key ID** in Braze match your Apple Developer account. 5. Re-upload a valid `.p8` key or `.p12` certificate if credentials were rotated or revoked. Prefer `.p8` authentication keys when possible. For credential types and dashboard status indicators, see [Migrate to a .p8 authentication key](https://www.braze.com/docs/ko/ko/user_guide/channels/push/troubleshooting/#migrate-to-a-p8-authentication-key). #### BadDeviceToken sending to push token The `BadDeviceToken` is an APNs error code and does not originate from Braze. There could be a number of reasons for this response being returned, including the following: - The app received a push token that was invalid for the credentials uploaded to the dashboard. - Push was disabled for this workspace. - The user has opted out of push. - The app was uninstalled. - Apple refreshed the push token, which invalidated the old token. - The app was built for a production environment, but the push credentials uploaded to Braze are set for a development environment (or the other way around). ## Push registration issues ### No push registration prompt If the application does not prompt you to register for push notifications, there is likely an issue with your push registration integration. Ensure you have followed our [documentation](https://www.braze.com/docs/ko/ko/developer_guide/push_notifications/?sdktab=swift) and correctly integrated our push registration. You can also set breakpoints in your code to ensure the push registration code is running. ### No "push registered" users showing in the dashboard (prior to sending messages) Ensure that your app is correctly configured to allow push notifications. Common failure points to check include: - Check that your app is prompting you to allow push notifications. Typically, this prompt will appear upon your first open of the app, but it can be programmed to appear elsewhere. If it does not appear where it should be, the problem is likely with the basic configuration of your app's push capabilities. - Verify the steps for [push integration](https://www.braze.com/docs/ko/ko/developer_guide/push_notifications/?sdktab=swift) were successfully completed. - Check that the provisioning profile your app was built with includes permissions for push. Make sure that you're pulling down all of the available provisioning profiles from your Apple developer account. To confirm this, perform the following steps: 1. In Xcode, navigate to **Preferences > Accounts** (or use the keyboard shortcut Command+,). 2. Select the Apple ID you use for your developer account and click **View Details**. 3. On the next page, click ** Refresh** and confirm that you're pulling all available provisioning profiles. - Check you have [properly enabled push capability](https://www.braze.com/docs/ko/ko/developer_guide/platform_integration_guides/swift/push_notifications/integration/#step-2-enable-push-capabilities) in your app. - Check your push provisioning profile matches the environment you're testing in. Universal certificates may be configured in the Braze dashboard to send to either the development or production APNs environment. Using a development certificate for a production app or a production certificate for a development app will not work. - Check that you are calling our `registerPushToken` method by setting a breakpoint in your code. - Make sure you're testing using a device (push will not work on a simulator) and have good network connectivity. ## Push notifications sent but not displayed on users’ devices ### "Push registered" users no longer enabled after sending messages This likely indicates that the user had an invalid push token. This can happen for several reasons: #### Dashboard and app certificate mismatch If the push certificate you uploaded in the dashboard is not the same one in the provisioning profile that your app was built with, APNs will reject the token. Verify that you have uploaded the correct certificate and completed another session in the app before attempting another test notification. #### Application was uninstalled If a user has uninstalled your application, their push token will be invalid and removed upon the next send. #### Regenerating your provisioning profile As a last resort, starting over fresh and creating a whole new provisioning profile can clear up configuration errors that come from working with multiple environments, profiles, and apps at the same time. There are many "moving parts" in setting up push notifications, so sometimes, it is best to retry from the beginning. This will also help isolate the problem if you need to continue troubleshooting. ### Messages not delivered to "push registered" users #### App is foregrounded On iOS versions that do not integrate push via the `UserNotifications` framework, if the app is in the foreground when the push message is received, it will not be displayed. You should background the app on your test devices before sending test messages. #### Test notification scheduled incorrectly Check the schedule you set for your test message. If it is set to local time zone delivery or [Intelligent Timing](https://www.braze.com/docs/ko/ko/user_guide/brazeai/intelligence/intelligent_timing/), you may have just not received the message yet (or had the app in the foreground when it was received). ### User not "push registered" for the app being tested Check the user profile of the user you are trying to send a test message to. Under the **Engagement** tab, there should be a list of "pushable apps." Verify the app you are trying to send test messages to is in this list. Users will show up as "Push Registered" if they have a push token for any app in your workspace, so this could be something of a false positive. The following would indicate a problem with push registration or that the user's token had been returned to Braze as invalid by APNs after being pushed: ![A user profile displaying the contact settings of a user. Under Push, "No Apps" are displayed.](https://www.braze.com/docs/ko/ko/assets/img_archive/registration_problem.png?b01abd4f8f8ddd58425f6ecc82c256ea){: style="max-width:50%"} ## Push clicks not logged {#push-clicks-not-logged} - Make sure you have followed the [push integration steps](https://www.braze.com/docs/ko/ko/developer_guide/platform_integration_guides/swift/push_notifications/integration/#step-5-enable-push-handling). - Braze does not handle push notifications received silently in the foreground (default foreground push behavior prior to the `UserNotifications` framework). This means that links will not be opened, and push clicks will not be logged. If your application has not yet integrated the `UserNotifications` framework, Braze will not handle push notifications when the application state is `UIApplicationStateActive`. Ensure that your app does not delay calls to [push handling methods](https://www.braze.com/docs/ko/ko/developer_guide/platform_integration_guides/swift/push_notifications/integration/#step-5-enable-push-handling); otherwise, the Swift SDK may treat push notifications as silent foreground push events and not handle them. ## Deep links not working For comprehensive troubleshooting across all channels—including universal links, custom schemes, email, and third-party providers like Branch—see [Deep linking troubleshooting](https://www.braze.com/docs/ko/ko/developer_guide/push_notifications/deep_linking_troubleshooting). ### Web links from push clicks not opening Links in push notifications need to be ATS compliant to be opened in web views. Ensure that your web links use HTTPS. For more information, refer to [ATS compliance](https://www.braze.com/docs/ko/ko/developer_guide/platform_integration_guides/swift/advanced_use_cases/linking/#app-transport-security-ats). ### Deep links from push clicks not opening Most of the code that handles deep links also handles push opens. First, ensure that push opens are being logged. If not, fix that issue (as the fix often fixes link handling). If opens are being logged, check whether it is an issue with the deep link in general or with the deep linking push click handling. To do this, test to see if a deep link from an in-app message click works. ## Understanding the Braze push workflow The Firebase Cloud Messaging (FCM) service is Google's infrastructure for push notifications sent to Android applications. Here is the simplified structure of how push notifications are enabled for your users' devices and how Braze can send push notifications to them: ```mermaid --- config: theme: mc --- sequenceDiagram participant Device as User Device participant App as Android App participant BrazeSDK as Braze SDK participant BrazeAPI as Braze Server participant Firebase as Google Firebase Note over Device, Firebase: Register Option 1
Register Automatically using `com_braze_firebase_cloud_messaging_registration_enabled` in braze.xml App ->> Braze: App initializes Braze with the first Braze call
This could be automatic session handling BrazeSDK ->> App: Get push token from Firebase Manager BrazeSDK ->> BrazeAPI: Send push token to Braze Server Note right of BrazeAPI: Braze will remove push token from any
other user who may have previously
been logged in on the same device. Note over Device, Firebase: Register Option 2
Manual registration. App ->> BrazeSDK: App sets `Braze.registeredPushToken` BrazeSDK ->> BrazeAPI: Send push token to Braze Server Note right of BrazeAPI: Braze will remove push token from any
other user who may have previously
been logged in on the same device. Note over Device, Firebase: Push permission BrazeAPI ->> BrazeSDK: In-App Message containing push prompt BrazeSDK -> App: In-App Message is displayed App -> BrazeSDK: User requests permissions BrazeSDK -> App: Displays the Push Authorization prompt BrazeSDK -> BrazeAPI: If authorized and `com_braze_optin_when_push_authorized`, Opt-In value is sent. Note over Device, Firebase: Push Notification Is Sent BrazeAPI ->> Firebase: Sends push message Firebase ->> Device: Push message sent Device ->> App: Android will send the push to the App.
This could be blocked to Do Not Disturb, Power Saving Mode, etc. App ->> BrazeSDK: Message is sent to BrazeFirebaseMessagingService BrazeSDK ->> Device: SDK will check if the push is from Braze.
If so, push data is transformed into a Push Notification and displayed. ``` ### Step 1: Configuring your Google Cloud API key In developing your app, you'll need to provide the Braze Android SDK with your Firebase sender ID. Additionally, you'll need to provide an API Key for server applications to the Braze dashboard. Braze will use this API key to send messages to your devices. You will also need to check that FCM service is enabled in Google Developer's console. **Note:** A common mistake during this step is using the app identifier API key instead of the REST API key. ### Step 2: Devices register for FCM and provide Braze with push tokens In typical integrations, the Braze Android SDK will handle registering devices for FCM capability. This will usually happen immediately upon opening the app for the first time. After registration, Braze will be provided with an FCM Registration ID, which is used to send messages to that device specifically. We will store the Registration ID for that user, and that user will become "push registered" if they previously did not have a push token for any of your apps. ### Step 3: Launching a Braze push campaign When a push campaign is launched, Braze will make requests to FCM to deliver your message. Braze will use the API key copied in the dashboard to authenticate and verify that we can send push notifications to the push tokens provided. ### Step 4: Removing invalid tokens If FCM informs us that any of the push tokens we were attempting to send a message to are invalid, we remove those tokens from the user profiles they were associated with. If users have no other push tokens, they will no longer show up as "Push Registered" under the **Segments** page. For more details about FCM, visit [Cloud messaging](https://firebase.google.com/docs/cloud-messaging/). ## Utilizing the push error logs Braze provides push notification errors within the message activity log. This error log provides a variety of warnings which can be very helpful for identifying why your campaigns aren't working as expected. Clicking on an error message will redirect you to relevant documentation to help you troubleshoot a particular incident. ![](https://www.braze.com/docs/ko/ko/assets/img_archive/message_activity_log.png?6577302323ab3f2df3196a973320b8d3) ## Troubleshooting scenarios ### Push isn't sending Your push messages might not be sending because of the following situations: - Your credentials exist in the wrong Google Cloud Platform project ID (wrong sender ID). - Your credentials have the wrong permission scope. - You uploaded wrong credentials to the wrong Braze workspace (wrong sender ID). For other issues that may prevent you from sending a push message, refer to [User Guide: Troubleshooting Push Notifications](https://www.braze.com/docs/ko/ko/user_guide/message_building_by_channel/push/troubleshooting/). ### No "push registered" users showing in the Braze dashboard (prior to sending messages) Confirm that your app is correctly configured to allow push notifications. Common failure points to check include: #### Incorrect sender ID Check that the correct FCM sender ID is included in the `braze.xml` file. An incorrect sender ID will lead to `MismatchSenderID` errors reported in the dashboard's message activity log. #### Braze registration not occurring Since FCM registration is handled outside of Braze, failure to register can only occur in two places: 1. During registration with FCM 2. When passing the FCM-generated push token to Braze We recommend setting a breakpoint or logging to confirm that the FCM-generated push token is being sent to Braze. If a token is not generated correctly or at all, we recommend consulting the [FCM documentation](https://firebase.google.com/docs/cloud-messaging/android/client). #### Google Play Services not present For FCM push to work, Google Play Services must be present on the device. If Google Play Services isn't on a device, push registration will not occur. **Note:** Google Play Services is not installed on Android emulators without Google APIs installed. #### Device not connected to the internet Check that your device has good internet connectivity and isn't sending network traffic through a proxy. ### Tapping push notification doesn't open the app Check if `com_braze_handle_push_deep_links_automatically` is set to `true` or `false`. To enable Braze to automatically open the app and any deep links when a push notification is tapped, set `com_braze_handle_push_deep_links_automatically` to `true` in your `braze.xml` file. If `com_braze_handle_push_deep_links_automatically` is set to its default of `false`, you need to use a Braze Push Callback to listen for and handle the push received and opened intents. ### Push notifications bounced If a push notification isn't delivered, make sure it didn't bounce by looking in the [developer console](https://www.braze.com/docs/ko/ko/developer_guide/platforms/android/push_notifications/troubleshooting/#utilizing-the-push-error-logs). The following are descriptions of common errors that may be logged in the developer console: #### Error: MismatchSenderID `MismatchSenderID` indicates an authentication failure. Confirm your Firebase sender ID and FCM API key are correct. #### Error: InvalidRegistration `InvalidRegistration` can be caused by a malformed push token. 1. Make sure to pass a valid push token to Braze from [Firebase Cloud Messaging](https://firebase.google.com/docs/cloud-messaging/android/client#retrieve-the-current-registration-token). #### Error: NotRegistered 1. `NotRegistered` typically occurs when an app has been deleted from a device. Braze uses `NotRegistered` internally to signal that an app has been uninstalled from a device. 2. `NotRegistered` may also occur when multiple registrations occur and a second registration invalidates the first token. ### Push notifications sent but not displayed on users' devices There are a few reasons why this could be occurring: #### Application was force quit If you force-quit your application through your system settings, your push notifications will not be sent. Launching the app again will re-enable your device to receive push notifications. #### BrazeFirebaseMessagingService not registered The BrazeFirebaseMessagingService must be properly registered in `AndroidManifest.xml` for push notifications to appear: ```xml ``` #### Firewall is blocking push If you are testing push over Wi-Fi, your firewall may be blocking ports necessary for FCM to receive messages. Confirm that ports `5228`, `5229`, and `5230` are open. Additionally, since FCM doesn't specify its IPs, you must also allow your firewall to accept outgoing connections to all IP addresses contained in the IP blocks listed in Google's ASN of `15169`. #### Custom notification factory returning null If you have implemented a [custom notification factory](https://www.braze.com/docs/ko/ko/developer_guide/platform_integration_guides/android/push_notifications/android/integration/standard_integration/#custom-displaying-notifications), ensure that it is not returning `null`. This will cause notifications not to be displayed. ### "Push registered" users no longer enabled after sending messages There are a few reasons why this could be happening: #### Application was uninstalled Users have uninstalled the application. This will invalidate their FCM push token. #### Invalid Firebase Cloud Messaging server key The Firebase Cloud Messaging server key provided in the Braze dashboard is invalid. The sender ID provided should match the one referenced in your app's `braze.xml` file. The server key and sender ID are found here in your Firebase Console: ![The Firebase platform under "Settings" and then "Cloud Messaging" will display your server ID and server key.](https://www.braze.com/docs/ko/ko/assets/img_archive/finding_firebase_server_key.png?de34d7ce2b1ae4b9c4a9d543b5b40585 "FirebaseServerKey") ### Push clicks not logged Braze logs push clicks automatically, so this scenario should be comparatively rare. If push clicks are not being logged, it is possible that push click data has not been flushed to our servers yet. Braze throttles the frequency of its flushes based on the strength of the network connection. With a good network connection, push click-data should arrive at the server within a minute in most circumstances. ### Deep links not working #### Verify deep link configuration Deep links can be [tested with ADB](https://developer.android.com/training/app-indexing/deep-linking.html#testing-filters). We recommend testing your deep link with the following command: `adb shell am start -W -a android.intent.action.VIEW -d "THE_DEEP_LINK" THE_PACKAGE_NAME` If the deep link fails to work, the deep link may be misconfigured. A misconfigured deep link will not work when sent through Braze push. #### Verify custom handling logic If the deep link [works correctly with ADB](https://developer.android.com/training/app-indexing/deep-linking.html#testing-filters) but fails to work from Braze push, check whether any [custom push open handling](https://www.braze.com/docs/ko/ko/developer_guide/platform_integration_guides/android/push_notifications/android/integration/standard_integration/#android-push-listener-callback) has been implemented. If so, verify that the custom handling code properly handles the incoming deep link. #### Disable back stack behavior If the deep link [works correctly with ADB](https://developer.android.com/training/app-indexing/deep-linking.html#testing-filters) but fails to work from Braze push, try disabling [back stack](https://developer.android.com/guide/components/activities/tasks-and-back-stack). To do so, update your **braze.xml** file to include: ```xml false ``` ## Troubleshooting ### Push doesn't appear after app is closed from task switcher If you observe that push notifications no longer appear after the app is closed from the task switcher, your app is likely in Debug mode. .NET MAUI adds scaffolding in Debug mode that prevents apps from receiving push after their process is killed. If you run your app in Release Mode, you should see push even after the app is closed from the task switcher. ### Custom notification factory not being set correctly Custom notification factories (and all delegates) must extend [`Java.Lang.Object`](https://developer.xamarin.com/api/type/Android.Runtime.IJavaObject/) to work properly across the C# and Java divide. See [Xamarin](https://developer.xamarin.com/guides/android/advanced_topics/java_integration_overview/working_with_jni/#Implementing_Interfaces) on implementing Java interfaces for more information. ## 푸시 알림의 줄 바꿈 {#push-linebreaks} Liquid 태그를 사용하여 푸시 알림을 작성할 때, Liquid 태그에 인접한 줄 바꿈은 메시지가 전송되기 전에 자동으로 제거됩니다. [푸시 알림 작성기](https://www.braze.com/docs/ko/ko/user_guide/message_building_by_channel/push/creating_a_push_message/)에서는 편집 중 메시지의 가독성을 유지하기 위해 이러한 줄 바꿈이 다시 추가됩니다. 메시지를 저장할 때 Liquid 태그 주변에 줄 바꿈이 표시되는 경우, 이는 정상적인 동작입니다. # Braze SDK를 위한 실시간 활동 Source: /docs/ko/developer_guide/live_notifications/index.md 라이브 활동 > 사용자의 잠금 화면에 직접 지속적이고 동적인 알림을 전송하여 사용자가 앱을 열지 않고도 실시간 업데이트를 받을 수 있도록 하는 방법을 알아보세요. Swift의 경우 이 기능이 기본적으로 지원됩니다. 사용자의 잠금 화면으로 직접 지속적이고 동적인 알림을 전송하여 앱을 열지 않고도 실시간 업데이트를 받을 수 있도록 하는 방법을 알아보세요. Featured: - Swift를 위한 라이브 활동 구현 # Android Braze SDK용 라이브 업데이트 Source: /docs/ko/developer_guide/live_notifications/live_updates/index.md # Android용 라이브 업데이트 {#live-updates-for-android} > Braze SDK에서 Android 라이브 업데이트를 사용하는 방법을 알아보세요. [진행률 중심 알림](https://developer.android.com/about/versions/16/features/progress-centric-notifications)이라고도 합니다. 이러한 알림은 대화형 잠금 화면 알림을 표시할 수 있는 [Swift Braze SDK의 라이브 활동](https://www.braze.com/docs/ko/ko/developer_guide/live_notifications/live_activities/)과 유사합니다. Android 16은 사용자가 시작한 시작부터 끝까지의 여정을 원활하게 추적할 수 있도록 진행률 중심 알림을 도입했습니다. ## 작동 방식 {#how-it-works} [`IBrazeNotificationFactory`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze/-i-braze-notification-factory/index.html) 인터페이스를 사용하여 Braze 푸시 알림이 표시되는 방식을 커스텀할 수 있습니다. `BrazeNotificationFactory`를 확장하면 사용자에게 알림이 표시되기 전에 Braze가 팩토리의 `createNotification()` 메서드를 호출합니다. 그런 다음 Braze 대시보드 또는 REST API를 통해 전송된 커스텀 키-값 페어가 포함된 페이로드를 전달합니다. ## 라이브 업데이트 표시 {#displaying-a-live-update} 이 섹션에서는 야생동물 구조팀이 누가 가장 많은 올빼미를 구할 수 있는지 경쟁하는 새로운 게임 쇼의 호스트인 Superb Owl과 파트너가 됩니다. Android 앱에서 라이브 업데이트를 활용하여 진행 중인 경기의 상태를 표시하고 실시간으로 알림을 동적으로 업데이트할 수 있도록 하려고 합니다. ![Android의 라이브 업데이트 예시](https://www.braze.com/docs/ko/ko/assets/img/android/android-live-update.png?fffb1e71ca0ee2c210b5fc515b609258){: style="max-width:40%;"} ### Prerequisites Before you can use this feature, you'll need to [integrate the Android Braze SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=android). ### 1단계: 커스텀 알림 팩토리 만들기 {#step-1-create-a-custom-notification-factory} 애플리케이션에서 [`BrazeNotificationFactory`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze/-i-braze-notification-factory/index.html)를 확장하는 `MyCustomNotificationFactory.kt`라는 새 파일을 생성하여 Braze 라이브 업데이트가 표시되는 방식을 처리합니다. 다음 예시에서 Superb Owl은 진행 중인 경기에 대한 라이브 업데이트를 표시하는 커스텀 알림 팩토리를 만들었습니다. 다음 단계에서는 `getTeamInfo`라는 새 메서드를 만들어 팀의 데이터를 활동에 매핑합니다. ```kotlin class MyCustomNotificationFactory : IBrazeNotificationFactory { override fun createNotification(payload: BrazeNotificationPayload): Notification? { val notificationBuilder = populateNotificationBuilder(payload) val context = payload.context ?: return null if (notificationBuilder == null) { brazelog { "Notification could not be built. Returning null as created notification." } return null } notificationBuilder.setContentTitle("Android Live Updates").setContentText("Ongoing updates below") setProgressStyle(notificationBuilder, context) return notificationBuilder.build() } private fun setProgressStyle(notificationBuilder: NotificationCompat.Builder, context: Context) { val style = NotificationCompat.ProgressStyle() .setStyledByProgress(false) .setProgress(200) .setProgressTrackerIcon(IconCompat.createWithResource(context, R.drawable.notification_small_icon)) .setProgressSegments( mutableListOf( NotificationCompat.ProgressStyle.Segment(1000).setColor(Color.GRAY), NotificationCompat.ProgressStyle.Segment(200).setColor(Color.BLUE), ) ) .setProgressPoints( mutableListOf( NotificationCompat.ProgressStyle.Point(60).setColor(Color.RED), NotificationCompat.ProgressStyle.Point(560).setColor(Color.GREEN) ) ) notificationBuilder.setStyle(style) } } ``` ### 2단계: 커스텀 데이터 매핑 {#step-2-map-custom-data} `MyCustomNotificationFactory.kt`에서 라이브 업데이트가 표시될 때 데이터를 처리하는 새로운 메서드를 만듭니다. Superb Owl은 각 팀의 이름과 로고를 확장된 라이브 업데이트에 매핑하기 위해 다음과 같은 메서드를 만들었습니다: ```kotlin class CustomNotificationFactory : BrazeNotificationFactory() { override fun createNotification(payload: BrazeNotificationPayload): Notification? { // Your existing code return super.createNotification(payload) } // Your new method private fun getTeamInfo(team: String?): Pair { return when (team) { "WBF" -> Pair("Wild Bird Fund", R.drawable.team_wbf) "OWL" -> Pair("Owl Rehab", R.drawable.team_owl) else -> Pair("Unknown", R.drawable.notification_small_icon) } } } ``` ### 3단계: 커스텀 알림 팩토리 설정 {#step-3-set-the-custom-notification-factory} 애플리케이션 클래스에서 [`customBrazeNotificationFactory`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze/-braze/-companion/custom-braze-notification-factory.html?query=var%20customBrazeNotificationFactory:%20IBrazeNotificationFactory?)를 사용하여 커스텀 알림 팩토리를 설정합니다. ```kotlin class MyApplication : Application() { override fun onCreate() { super.onCreate() // Tell Braze to use your custom factory for notifications Braze.customBrazeNotificationFactory = MyCustomNotificationFactory() } } ``` ### 4단계: 활동 보내기 {#step-4-send-the-activity} [`/messages/send`](https://www.braze.com/docs/ko/ko/api/endpoints/messaging/send_messages/post_send_messages/) REST API 엔드포인트를 사용하여 사용자의 Android 기기로 푸시 알림을 보낼 수 있습니다. #### curl 명령 예시 {#example-curl-command} Superb Owl은 다음 curl 명령을 사용하여 요청을 보냈습니다: ``` curl -X POST "https://BRAZE_REST_ENDPOINT/messages/send" \ -H "Authorization: Bearer {REST_API_KEY}" \ -H "Content-Type: application/json" \ --data '{ "external_user_ids": ["USER_ID"], "messages": { "android_push": { "title": "WBF vs OWL", "alert": "2 to 4 1:33 Q4", "extra": { "live_update": "true", "team1": "WBF", "team2": "OWL", "score1": "2", "score2": "4", "time": "1:33", "quarter": "Q4" }, "notification_id": "ASSIGNED_NOTIFICATION_ID" } } }' ``` **Tip:** curl 명령은 테스트에 유용하지만, 이미 [iOS 라이브 활동](https://www.braze.com/docs/ko/ko/developer_guide/push_notifications/live_notifications/?sdktab=swift)을 처리하고 있는 백엔드에서 이 호출을 처리하는 것이 좋습니다. #### 요청 매개변수 {#request-parameters} | 키 | 설명 | |------------------------------|------------| | `REST_API_KEY` | `messages.send` 권한이 있는 Braze REST API 키입니다.

Braze 대시보드의 **설정** > **API 키**에서 생성할 수 있습니다. | | `BRAZE_REST_ENDPOINT` | REST 엔드포인트 URL입니다. 엔드포인트는 [인스턴스의 Braze URL](https://www.braze.com/docs/ko/ko/api/basics/#endpoints)에 따라 달라집니다. | | `USER_ID` | 알림을 보낼 사용자의 ID입니다. | | `messages.android_push.title` | 메시지 제목입니다. 기본적으로 커스텀 알림 팩토리의 라이브 알림에는 사용되지 않지만, 대체 용도로 사용할 수 있습니다. | | `messages.android_push.alert` | 메시지 본문입니다. 기본적으로 커스텀 알림 팩토리의 라이브 알림에는 사용되지 않지만, 대체 용도로 사용할 수 있습니다. | | `messages.extra` | 커스텀 알림 팩토리에서 라이브 알림에 사용하는 키-값 페어입니다. 이 값에는 어떤 문자열이든 할당할 수 있지만, 위의 예시에서는 `live_updates`를 사용하여 기본 푸시 알림인지 라이브 푸시 알림인지를 결정합니다. | | `ASSIGNED_NOTIFICATION_ID` | 선택한 사용자의 라이브 알림에 할당할 알림 ID입니다. 이 ID는 해당 게임에 고유해야 하며, 나중에 [기존 알림을 업데이트](#android_step-4-update-data-with-the-braze-rest-api)할 때 사용해야 합니다. | {: .reset-td-br-1 .reset-td-br-2 role="presentation" } ### 5단계: 활동 업데이트 {#step-5-update-the-activity} 기존 라이브 업데이트를 새 데이터로 업데이트하려면 `messages.extra`에 할당된 관련 키-값 페어를 수정한 다음 동일한 `notification_id`를 사용하여 `/messages/send` 엔드포인트를 다시 호출합니다. # Swift Braze SDK의 라이브 활동 Source: /docs/ko/developer_guide/live_notifications/live_activities/index.md # Swift의 라이브 활동 {#live-activities-for-swift} > Swift Braze SDK의 라이브 활동을 구현하는 방법을 알아보세요. 라이브 활동은 잠금 화면에 바로 표시되는 지속적인 대화형 알림으로, 사용자는 기기를 잠금 해제하지 않고도 동적인 실시간 업데이트를 받을 수 있습니다. ## 작동 방식 {#how-it-works} ![iPhone 잠금 화면의 배송 추적기 라이브 활동. 자동차가 있는 상태 표시줄이 거의 반쯤 채워져 있습니다. 텍스트에 "픽업까지 2분"이라고 표시됩니다.](https://www.braze.com/docs/ko/ko/assets/img/swift/live_activities/example_2.png?3675f3043731a76345f8790b4417a5dd){: style="max-width:40%;float:right;margin-left:15px;"} 라이브 활동은 정적 정보와 업데이트하는 동적 정보의 조합으로 표시됩니다. 예를 들어 배송 상태 추적기를 제공하는 라이브 활동을 만들 수 있습니다. 이 라이브 활동에는 회사 이름이 정적 정보로 표시되며, 배송 기사가 목적지에 가까워질수록 업데이트되는 동적 "배송 시간"이 표시됩니다. 개발자는 Braze를 사용하여 라이브 활동 생애주기를 관리하고, Braze REST API를 호출하여 라이브 활동을 업데이트하며, 구독한 모든 기기가 가능한 한 빨리 업데이트를 받도록 할 수 있습니다. 또한 Braze를 통해 라이브 활동을 관리하기 때문에 다른 메시징 채널—푸시 알림, 인앱 메시지, Content Cards—과 함께 사용하여 채택을 유도할 수 있습니다. ## 시퀀스 다이어그램 {#sequence-diagram} **다이어그램 보기** ```mermaid --- config: theme: mc --- sequenceDiagram participant Server as Client Server participant Device as User Device participant App as iOS App / Braze SDK participant BrazeAPI as Braze API participant APNS as Apple Push Notification Service Note over Server, APNS: Launch Option 1
Locally Start Activities App ->> App: Register a Live Activity using
`launchActivity(pushTokenTag:activity:)` App ->> App: Get push token from iOS App ->> BrazeAPI: Activity ID & Push token
automatically sent to Braze Note over Server, APNS: Launch Option 2
Remotely Start Activities Device ->> App: Call `registerPushToStart`
to collect push tokens early App ->> BrazeAPI: Push-to-start tokens sent to Braze Server ->> BrazeAPI: POST /messages/live_activity/start Note right of BrazeAPI: Payload includes:
- push_token
- activity_id
- external_id
- event_name
- content_state (optional) BrazeAPI ->> APNS: Live activity start request APNS ->> Device: APNS sends activity to device App ->> App: Get push token from iOS App ->> BrazeAPI: Activity ID & Push token
automatically sent to Braze Note over Server, APNS: Resuming activities upon app launch App ->> App: Call `resumeActivities(ofType:)` on each app launch Note over Server, APNS: Updating a Live Activity loop update a live activity Server ->> BrazeAPI: POST /messages/live_activity/update Note right of BrazeAPI: Payload includes changes
to ContentState (dynamic variables) BrazeAPI ->> APNS: Update sent to APNS APNS ->> Device: APNS sends update to device end Note over Server, APNS: Ending a Live Activity Server ->> BrazeAPI: POST /messages/live_activity/update Note right of BrazeAPI: Activity can be ended via:
- User manually dismisses
- Times out after 12 hours
- Setting `end_activity: true` on `/messages/live_activity/update` APNS ->> Device: Live activity is dismissed ``` ## 라이브 활동 구현 {#implementing-a-live-activity} ### Prerequisites Before you can use this feature, you'll need to [integrate the Swift Braze SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=swift). 다음도 완료해야 합니다: - 프로젝트가 iOS 16.1 이상을 대상으로 하는지 확인하세요. - Xcode 프로젝트의 **Signing & Capabilities** 아래에 `Push Notification` 권한을 추가하세요. - `.p8` 키가 알림을 보내는 데 사용되는지 확인합니다. `.p12` 또는 `.pem` 같은 이전 파일은 지원되지 않습니다. - Braze Swift SDK 버전 8.2.0부터 [라이브 활동을 원격으로 등록](#swift_step-2-start-the-activity)할 수 있습니다. 이 기능을 사용하려면 iOS 17.2 이상이 필요합니다. **Note:** 라이브 활동과 푸시 알림은 비슷하지만 시스템 권한은 서로 다릅니다. 기본적으로 모든 라이브 활동 기능은 활성화되어 있지만, 사용자는 앱별로 이 기능을 비활성화할 수 있습니다. ### 1단계: 활동 만들기 {#create-an-activity} 먼저, iOS 애플리케이션에서 라이브 활동을 설정하려면 Apple 설명서의 [라이브 활동으로 라이브 데이터 표시](https://developer.apple.com/documentation/activitykit/displaying-live-data-with-live-activities)를 따라야 합니다. 이 작업의 일환으로 `Info.plist`에서 `NSSupportsLiveActivities`를 `YES`로 설정해야 합니다. 라이브 활동의 정확한 성격은 비즈니스 사례에 따라 다르므로 [Activity](https://developer.apple.com/documentation/activitykit/activityattributes) 오브젝트를 설정하고 초기화해야 합니다. 중요한 점은 다음을 정의해야 한다는 것입니다: * `ActivityAttributes`: 이 프로토콜은 라이브 활동에 표시되는 정적(변경되지 않음) 및 동적(변경됨) 콘텐츠를 정의합니다. * `ActivityAttributes.ContentState`: 이 유형은 활동이 진행되는 동안 업데이트될 동적 데이터를 정의합니다. 또한 SwiftUI를 사용하여 지원되는 기기에서 잠금 화면과 Dynamic Island의 UI 프레젠테이션을 생성할 수 있습니다. 이러한 제약 조건은 Braze와는 독립적이므로 라이브 활동에 대한 Apple의 [전제 조건 및 제한](https://developer.apple.com/documentation/activitykit/displaying-live-data-with-live-activities#Understand-constraints) 사항을 숙지하고 있어야 합니다. **Note:** 동일한 라이브 활동에 푸시를 자주 보낼 예정인 경우 `Info.plist` 파일에서 `NSSupportsLiveActivitiesFrequentUpdates`를 `YES`로 설정하면 Apple의 예산 한도에 의해 제한되는 것을 방지할 수 있습니다. 자세한 내용은 ActivityKit 설명서의 [`Determine the update frequency`](https://developer.apple.com/documentation/activitykit/updating-and-ending-your-live-activity-with-activitykit-push-notifications#Determine-the-update-frequency) 섹션을 참조하세요. #### 예시 {#example} Superb Owl 프로그램에 대한 사용자 업데이트를 제공하는 라이브 활동을 생성한다고 가정합니다. 이 프로그램에서는 서로 경쟁하는 두 야생동물 구조대에게 보호 중인 올빼미에 대한 점수를 부여합니다. 이 예제에서는 `SportsActivityAttributes`라는 구조체를 생성했지만 `ActivityAttributes`를 직접 구현하여 사용할 수 있습니다. ```swift #if canImport(ActivityKit) import ActivityKit #endif @available(iOS 16.1, *) struct SportsActivityAttributes: ActivityAttributes { public struct ContentState: Codable, Hashable { var teamOneScore: Int var teamTwoScore: Int } var gameName: String var gameNumber: String } ``` ### 2단계: 활동 시작 {#start-the-activity} 먼저, 활동을 등록하는 방법을 선택합니다: - **원격:** [`registerPushToStart`]() 메서드를 사용자 라이프사이클 초기에, 푸시 투 스타트 토큰이 필요하기 전에 호출한 다음 [`/messages/live_activity/start`](https://www.braze.com/docs/ko/ko/api/endpoints/messaging/live_activity/start/) 엔드포인트를 사용하여 활동을 시작합니다. - **로컬:** 라이브 활동의 인스턴스를 생성한 다음, [`launchActivity`]() 메서드를 사용하여 Braze가 관리할 푸시 토큰을 생성합니다. **Important:** 라이브 활동을 원격으로 등록하려면 iOS 17.2 이상이 필요합니다. #### 2.1단계: 위젯 확장에 BrazeKit 추가하기 {#step-21-add-brazekit-to-your-widget-extension} Xcode 프로젝트에서 앱 이름을 선택한 다음, **General**을 선택합니다. **Frameworks and Libraries**에서 `BrazeKit`가 나열되어 있는지 확인합니다. ![샘플 Xcode 프로젝트의 Frameworks and Libraries 아래에 있는 BrazeKit 프레임워크.](https://www.braze.com/docs/ko/ko/assets/img/swift/live_activities/xcode_frameworks_and_libraries.png?cec8b144f4f7f25ba4df574c272bd622) #### 2.2단계: BrazeLiveActivityAttributes 프로토콜 추가 {#brazeActivityAttributes} `ActivityAttributes` 구현에서 `BrazeLiveActivityAttributes` 프로토콜에 대한 준수를 추가한 다음 속성 모델에 `brazeActivityId` 속성을 추가합니다. **Important:** iOS는 `brazeActivityId` 속성을 라이브 활동 푸시 투 스타트 페이로드의 해당 필드에 매핑하므로 이름을 변경하거나 다른 값을 할당해서는 안 됩니다. ```swift import BrazeKit #if canImport(ActivityKit) import ActivityKit #endif @available(iOS 16.1, *) // 1. Add the `BrazeLiveActivityAttributes` conformance to your `ActivityAttributes` struct. struct SportsActivityAttributes: ActivityAttributes, BrazeLiveActivityAttributes { public struct ContentState: Codable, Hashable { var teamOneScore: Int var teamTwoScore: Int } var gameName: String var gameNumber: String // 2. Add the `String?` property to represent the activity ID. var brazeActivityId: String? } ``` #### 2.3단계: 푸시 투 스타트 등록 {#step-23-register-for-push-to-start} 다음으로, 라이브 활동 유형을 등록하여 Braze가 이 유형과 관련된 모든 푸시 투 스타트 토큰과 라이브 활동 인스턴스를 추적할 수 있도록 합니다. **Warning:** iOS 운영체제는 기기를 재시작한 후 처음 앱을 설치할 때만 푸시 투 스타트 토큰을 생성합니다. 토큰이 안정적으로 등록되도록 하려면 `didFinishLaunchingWithOptions` 메서드에서 `registerPushToStart`를 호출하세요. ###### 예시 다음 예제에서 `LiveActivityManager` 클래스는 라이브 활동 오브젝트를 처리합니다. 그런 다음, `registerPushToStart` 메서드에서 `SportsActivityAttributes`를 등록합니다: ```swift import BrazeKit #if canImport(ActivityKit) import ActivityKit #endif class LiveActivityManager { @available(iOS 17.2, *) func registerActivityType() { // This method returns a Swift background task. // You may keep a reference to this task if you need to cancel it wherever appropriate, or ignore the return value if you wish. let pushToStartObserver: Task = Self.braze?.liveActivities.registerPushToStart( forType: Activity.self, name: SportsActivityAttributes.name ) } } ``` #### 2.4단계: 푸시 투 스타트 알림 보내기 {#step-24-send-a-push-to-start-notification} [`/messages/live_activity/start`](https://www.braze.com/docs/ko/ko/api/endpoints/messaging/live_activity/start/) 엔드포인트를 사용하여 원격 푸시 투 스타트 알림을 보냅니다. [Apple의 ActivityKit 프레임워크](https://developer.apple.com/documentation/activitykit)를 사용하여 푸시 토큰을 받을 수 있으며, Braze SDK가 이를 관리할 수 있습니다. 이렇게 하면 Braze가 백엔드에서 푸시 토큰을 Apple 푸시 알림 서비스(APNs)로 전송하므로 Braze API를 통해 라이브 활동을 업데이트할 수 있습니다. 1. Apple의 ActivityKit API를 사용하여 라이브 활동 구현의 인스턴스를 생성합니다. 2. `pushType` 매개변수를 `.token`으로 설정합니다. 3. 정의한 라이브 활동 `ActivitiesAttributes` 및 `ContentState`를 전달합니다. 4. [`launchActivity(pushTokenTag:activity:)`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/liveactivities-swift.class)에 전달하여 Braze 인스턴스에 활동을 등록합니다. `pushTokenTag` 매개변수는 사용자가 정의하는 커스텀 문자열입니다. 생성하는 각 라이브 활동에서 고유해야 합니다. 라이브 활동을 등록한 후 Braze SDK가 푸시 토큰의 변경 사항을 추출하고 관찰합니다. #### 예시 이 예제에서는 라이브 활동 오브젝트에 대한 인터페이스로 `LiveActivityManager` 클래스를 생성합니다. 그런 다음, `pushTokenTag`를 `"sports-game-2024-03-15"`로 설정합니다. ```swift import BrazeKit #if canImport(ActivityKit) import ActivityKit #endif class LiveActivityManager { @available(iOS 16.2, *) func createActivity() { let activityAttributes = SportsActivityAttributes(gameName: "Superb Owl", gameNumber: "Game 1") let contentState = SportsActivityAttributes.ContentState(teamOneScore: "0", teamTwoScore: "0") let activityContent = ActivityContent(state: contentState, staleDate: nil) if let activity = try? Activity.request(attributes: activityAttributes, content: activityContent, // Setting your pushType as .token allows the Activity to generate push tokens for the server to watch. pushType: .token) { // Register your Live Activity with Braze using the pushTokenTag. // This method returns a Swift background task. // You may keep a reference to this task if you need to cancel it wherever appropriate, or ignore the return value if you wish. let liveActivityObserver: Task = AppDelegate.braze?.liveActivities.launchActivity(pushTokenTag: "sports-game-2024-03-15", activity: activity) } } } ``` 라이브 활동 위젯은 이 초기 콘텐츠를 사용자에게 표시합니다. ![두 팀의 점수가 표시된 iPhone 잠금 화면의 라이브 활동. Wild Bird Fund와 Owl Rehab 팀 모두 점수가 0입니다.](https://www.braze.com/docs/ko/ko/assets/img/swift/live_activities/example_1_1.png?b9615bf4d416a7865420f5364951ccd6){: style="max-width:40%;"} ### 3단계: 활동 추적 재개 {#resume-activity-tracking} 앱 실행 시 Braze가 라이브 활동을 추적하도록 하려면 다음을 수행합니다: 1. `AppDelegate` 파일을 엽니다. 2. `ActivityKit` 모듈을 사용할 수 있는 경우 가져옵니다. 3. 애플리케이션에 등록한 모든 `ActivityAttributes` 유형에 대해 `application(_:didFinishLaunchingWithOptions:)`에서 [`resumeActivities(ofType:)`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/liveactivities-swift.class/resumeactivities(oftype:))를 호출합니다. 이를 통해 Braze는 모든 활성 라이브 활동에 대한 푸시 토큰 업데이트를 추적하는 작업을 재개할 수 있습니다. 사용자가 기기에서 라이브 활동을 명시적으로 삭제한 경우, 해당 활동은 제거된 것으로 간주되며 Braze는 더 이상 해당 활동을 추적하지 않습니다. ###### 예시 ```swift import UIKit import BrazeKit #if canImport(ActivityKit) import ActivityKit #endif @main class AppDelegate: UIResponder, UIApplicationDelegate { static var braze: Braze? = nil func application( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? ) -> Bool { if #available(iOS 16.1, *) { Self.braze?.liveActivities.resumeActivities( ofType: Activity.self ) } return true } } ``` ### 4단계: 활동 업데이트 {#update-the-activity} ![두 팀의 점수가 표시된 iPhone 잠금 화면의 라이브 활동. Wild Bird Fund는 2점, Owl Rehab은 4점입니다.](https://www.braze.com/docs/ko/ko/assets/img/swift/live_activities/example_1_2.png?c9ac24cf3bad2d8d1cc815a44699160e){: style="max-width:40%;float:right;margin-left:15px;"} [`/messages/live_activity/update`](https://www.braze.com/docs/ko/ko/api/endpoints/messaging/live_activity/update/) 엔드포인트를 사용하면 Braze REST API를 통해 전달되는 푸시 알림으로 라이브 활동을 업데이트할 수 있습니다. 이 엔드포인트를 사용하여 라이브 활동의 `ContentState`를 업데이트합니다. `ContentState`를 업데이트하면 라이브 활동 위젯에 새 정보가 표시됩니다. 다음은 전반전이 끝난 후 Superb Owl 프로그램의 모습입니다. 자세한 내용은 [`/messages/live_activity/update` 엔드포인트](https://www.braze.com/docs/ko/ko/api/endpoints/messaging/live_activity/update/) 문서를 참조하세요. ### 5단계: 활동 종료 {#end-the-activity} 라이브 활동이 활성화되면 사용자의 잠금 화면과 Dynamic Island에 모두 표시됩니다. Braze를 통해 종료하려면 [`/messages/live_activity/update`](https://www.braze.com/docs/ko/ko/api/endpoints/messaging/live_activity/update/) 엔드포인트에서 `end_activity`를 `true`로 설정합니다. 라이브 활동 종료의 안정성을 높이려면 다음 선택적 단계를 수행하세요: 1. 동일한 `update` 요청에 `dismissal_date`를 선택적으로 포함하여 iOS가 라이브 활동 UI를 제거할 시점을 제안합니다. 2. [메시지 활동 로그](https://www.braze.com/docs/ko/ko/user_guide/administrative/app_settings/message_activity_log_tab/)에서 전달 결과를 확인합니다. #### 자동 해제 설정 {#arranging-automatic-dismissal} 자동 해제를 설정하려면 라이브 활동을 시작한 후 업데이트 엔드포인트에 대한 후속 요청을 예약합니다. 1. 추적할 수 있는 `activity_id`와 함께 `/messages/live_activity/start` 요청을 보냅니다. 2. 해당 `activity_id`와 목표 종료 시간을 백엔드 스케줄러에 저장합니다. 3. 목표 종료 시간에 `end_activity`를 `true`로 설정한 `/messages/live_activity/update` 요청을 보냅니다. 4. 동일한 업데이트 요청에서 해제 날짜를 구성합니다. 자세한 내용은 [`/messages/live_activity/update`](https://www.braze.com/docs/ko/ko/api/endpoints/messaging/live_activity/update/) 엔드포인트를 참조하세요. 해제 타이밍은 iOS에 의해 제어됩니다. 유효한 종료 요청을 보낸 후에도 잠금 화면이나 Dynamic Island에서의 제거가 OS 수준의 조건에 따라 지연되거나 다르게 동작할 수 있습니다. 라이브 활동은 Braze 외부에서도 종료될 수 있습니다: * **사용자 해제**: 사용자가 수동으로 라이브 활동을 해제할 수 있습니다. * **시간 초과**: 기본 시간인 8시간이 지나면 iOS는 사용자의 Dynamic Island에서 라이브 활동을 제거합니다. 기본 시간인 12시간이 지나면 iOS는 사용자의 잠금 화면에서 라이브 활동을 제거합니다. 자세한 내용은 [`/messages/live_activity/update` 엔드포인트](https://www.braze.com/docs/ko/ko/api/endpoints/messaging/live_activity/update/) 문서를 참조하세요. ## 라이브 활동 추적 {#tracking-live-activities} 라이브 활동 이벤트는 Currents, Snowflake 데이터 공유 및 쿼리 빌더에서 사용할 수 있습니다. 다음 이벤트는 라이브 활동의 생애주기를 이해하고 모니터링하며, 토큰 가용성을 추적하고, 문제를 독립적으로 진단하거나 전달 상태를 확인하는 데 도움이 됩니다. - [라이브 활동 푸시 투 스타트 토큰 변경](https://www.braze.com/docs/ko/ko/user_guide/data/braze_currents/event_glossary/customer_behavior_events/#live-activity-push-to-start-token-change-events): 푸시 투 스타트(PTS) 토큰이 Braze에 추가되거나 업데이트될 때 캡처하여 사용자별 토큰 등록 및 가용성을 추적할 수 있습니다. - [라이브 활동 업데이트 토큰 변경](https://www.braze.com/docs/ko/ko/user_guide/data/braze_currents/event_glossary/customer_behavior_events/#live-activity-update-token-change-events): 라이브 활동 업데이트(LAU) 토큰의 추가, 업데이트 또는 제거를 추적합니다. - [라이브 활동 전송](https://www.braze.com/docs/ko/ko/user_guide/data/braze_currents/event_glossary/message_engagement_events/#live-activity-send-events): Braze에 의해 라이브 활동이 시작, 업데이트 또는 종료될 때마다 로그를 기록합니다. - [라이브 활동 결과](https://www.braze.com/docs/ko/ko/user_guide/data/braze_currents/event_glossary/message_engagement_events/#live-activity-outcome-events): Braze에서 전송된 모든 라이브 활동에 대해 Apple 푸시 알림 서비스(APNs)로의 최종 전달 상태를 나타냅니다. ## 라이브 활동 이벤트 관찰(선택 사항) {#observe-live-activity-events} **Important:** Apple의 다음 ActivityKit 스트림을 직접 구독하지 마세요. Braze의 구독과 충돌하여 라이브 활동이 올바르게 작동하지 않을 수 있습니다: 1. [`pushTokenUpdates`](https://developer.apple.com/documentation/activitykit/activity/pushtokenupdates-swift.property) 2. [`activityStateUpdates`](https://developer.apple.com/documentation/activitykit/activity/activitystateupdates-swift.property) 3. [`contentUpdates`](https://developer.apple.com/documentation/activitykit/activity/contentupdates-swift.property) 4. [`pushToStartTokenUpdates`](https://developer.apple.com/documentation/activitykit/activity/pushtostarttokenupdates) 5. [`activityUpdates`](https://developer.apple.com/documentation/activitykit/activity/activityupdates-swift.type.property) 대신 아래에 설명된 구독을 사용하세요. Braze SDK는 `braze.liveActivities`에서 전체 라이브 활동 생애주기를 관찰할 수 있는 두 가지 구독 메서드를 제공합니다. 전체 단계별 안내는 [라이브 활동 튜토리얼](https://braze-inc.github.io/braze-swift-sdk/tutorials/brazekit/b4-live-activities)을 참조하세요. - [`subscribeToStateUpdates(_:)`](#subscribe-to-state-updates): 푸시 투 스타트 토큰 등록 및 실행 중인 활동 인스턴스에 대한 생애주기 이벤트를 전달합니다. - [`subscribeToErrors(_:)`](#subscribe-to-errors): 라이브 활동 추적 중 발생한 SDK 및 서버 측 오류를 전달합니다. **Note:** 두 메서드 모두 [`Braze.Cancellable`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/cancellable-swift.typealias)을 반환합니다. 반환된 값이 강한 참조를 통해 유지되는 한 구독이 활성 상태로 유지됩니다(예: `Braze` 인스턴스와 동일한 생애주기를 가진 속성에 저장). ### 구독 설정 {#set-up-subscriptions} `application(_:didFinishLaunchingWithOptions:)`에서 구독을 한 번 설정하고 앱의 수명 동안 유지합니다: ```swift class AppDelegate: UIResponder, UIApplicationDelegate { static var braze: Braze? var stateSubscription: Braze.Cancellable? var errorSubscription: Braze.Cancellable? func application( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? ) -> Bool { let braze = Braze(configuration: config) Self.braze = braze if #available(iOS 16.1, *) { stateSubscription = Self.braze?.liveActivities.subscribeToStateUpdates { event in self.handleStateUpdate(event) } errorSubscription = Self.braze?.liveActivities.subscribeToErrors { error in self.handleLiveActivityError(error) } } return true } } ``` **Note:** 콜백은 향후 라이브 활동 이벤트에 대해서만 트리거됩니다. 구독 시점의 현재 상태를 재생하지 않습니다. 현재 상태 스냅샷을 조회하려면 `Activity.activities`를 사용하세요. ### subscribeToStateUpdates {#subscribe-to-state-updates} `subscribeToStateUpdates(_:)`는 전체 라이브 활동 생애주기를 다루는 `UpdateEvent` 값을 전달합니다. 이벤트는 두 가지 범위로 나뉩니다: - `.activityType(ActivityType)`: 푸시 투 스타트 토큰 등록을 위한 유형 수준 이벤트(iOS 17.2+). 아직 활동 인스턴스가 존재하지 않습니다. - `.activityInstance(ActivityInstance)`: 특정 실행 중인 활동에 대한 인스턴스 수준 이벤트. 여러 구독자가 지원됩니다. 각 활성 구독은 모든 이벤트를 독립적으로 수신합니다. #### 유형 범위 이벤트 {#type-scoped-events} | 이벤트 | 발생 시점 | | ----- | ------------- | | `.pushToStartTokenRead(activityType:)` | OS에서 푸시 투 스타트 토큰을 읽었습니다. 이제 Braze가 이 유형의 새 활동을 원격으로 시작할 수 있습니다. | | `.pushToStartTokenFlushed(activityType:)` | 토큰이 Braze 서버로 전송되었습니다. Braze가 이 유형에 대해 푸시 투 스타트 알림을 보낼 수 있습니다. | | `.pushToStartOptedOut(activityType:)` | `optOutPushToStart(type:)`를 통해 사용자가 이 활동 유형의 푸시 투 스타트를 옵트아웃했습니다. | | `.pushToStartOptOutFlushed(activityType:)` | 옵트아웃이 Braze 서버로 전송되었습니다. | {: .reset-td-br-1 .reset-td-br-2 aria-label="유형 범위 이벤트" } #### 인스턴스 범위 이벤트 {#instance-scoped-events} | 이벤트 | 발생 시점 | | ----- | ------------- | | `.started(activityId:activityType:pushTokenTag:launchSource:)` | SDK가 `launchActivity(pushTokenTag:activity:)`를 통해 이 활동 추적을 시작했습니다. `launchSource` 값은 앱에서 시작된 활동의 경우 `.local`, 원격으로 시작된 활동의 경우 `.pushToStart`입니다. | | `.resumed(activityId:activityType:pushTokenTag:)` | SDK가 `resumeActivities(ofType:)`를 통해 이 활동 추적을 재개했습니다. | | `.pushTokenFlushed(activityId:activityType:pushTokenTag:)` | 활동의 푸시 토큰이 Braze 서버에 의해 수락되었습니다. 이제 활동이 원격 업데이트를 받을 수 있습니다. | | `.active(activityId:activityType:)` | 활동이 현재 활성 상태이며 사용자에게 표시됩니다. | | `.stale(activityId:activityType:staleDate:)` | 활동의 콘텐츠가 오래되었습니다. iOS 16.2 이상에서만 발생합니다. | | `.dismissed(activityId:activityType:)` | 사용자가 수동으로 활동을 해제했습니다. | | `.ended(activityId:activityType:)` | 활동이 종료되었습니다. | | `.contentUpdated(activityId:activityType:)` | 활동의 콘텐츠 상태가 업데이트되었습니다(iOS 16.2+). 커스텀 로직을 사용하여 `Activity.activities`에서 ID로 `Activity`를 조회하고 `activity.content.state`를 통해 타입이 지정된 상태에 접근하세요. | | `.pushTokenUpdated(activityId:activityType:)` | ActivityKit가 활동의 푸시 토큰을 교체했습니다. | {: .reset-td-br-1 .reset-td-br-2 aria-label="인스턴스 범위 이벤트" } ###### 예시 ```swift func handleStateUpdate(_ event: Braze.LiveActivities.UpdateEvent) { switch event { // Type-scoped: push-to-start token lifecycle (iOS 17.2+) case .activityType(.pushToStartTokenRead(let activityType)): print("[\(activityType)] Push-to-start token read by SDK") // ... // Instance-scoped: SDK tracking case .activityInstance(.started(let id, let type, let tag, let source)): print("[\(type)] Activity \(id) started via \(source), tag: \(tag)") // ... // Instance-scoped: ActivityKit lifecycle case .activityInstance(.active(let id, let type)): print("[\(type)] Activity \(id) is active") // ... case .activityInstance(.ended(let id, let type)): print("[\(type)] Activity \(id) ended") // Instance-scoped: content updates (iOS 16.2+) case .activityInstance(.contentUpdated(let id, let type)): // For more advanced use cases of `contentUpdated`, see the section below print("[\(type)] Content updated for activity \(id)") case .activityInstance(.pushTokenUpdated(let id, let type)): print("[\(type)] Activity \(id) push token rotated") } } ``` ### subscribeToErrors {#subscribe-to-errors} `subscribeToErrors(_:)`는 `UpdateEvent`와 동일한 두 가지 범위를 사용하여 `ErrorEvent` 값을 전달합니다: - `.activityType(ActivityType)`: 푸시 투 스타트 등록 실패에 대한 유형 수준 오류. - `.activityInstance(ActivityInstance)`: 실행 중인 활동에 대한 인스턴스 수준 오류. 재시도가 적절한지 판단하려면 `isTransient` 플래그를 사용하세요. SDK는 일시적 실패를 자동으로 재시도합니다. #### 유형 범위 오류 {#type-scoped-errors} | 오류 | 발생 시점 | | ----- | ------------- | | `.pushToStartRegistrationFailed(activityType:isTransient:reason:)` | 푸시 투 스타트 토큰이 Braze 서버에 도달하지 못했습니다. | {: .reset-td-br-1 .reset-td-br-2 aria-label="유형 범위 오류" } #### 인스턴스 범위 오류 {#instance-scoped-errors} | 오류 | 발생 시점 | | ----- | ------------- | | `.registrationFailed(activityId:activityType:pushTokenTag:isTransient:reason:)` | 활동의 푸시 토큰이 Braze에 등록되지 못했습니다. | | `.activityNotFound(activityId:activityType:)` | `resumeActivities(ofType:)`가 더 이상 실행되지 않는 활동에 대한 저장된 매핑을 발견했습니다. 앱이 종료된 동안 활동이 종료되었을 가능성이 높습니다. | | `.invalidPushTokenTag(activityId:activityType:tag:)` | `launchActivity(pushTokenTag:activity:)`가 유효하지 않은 태그로 호출되었습니다. 태그는 비어 있지 않아야 하며 256바이트 미만이어야 합니다. | {: .reset-td-br-1 .reset-td-br-2 aria-label="인스턴스 범위 오류" } ###### 예시 ```swift func handleLiveActivityError(_ error: Braze.LiveActivities.ErrorEvent) { switch error { // Type-scoped errors case .activityType(.pushToStartRegistrationFailed(let type, let isTransient, let reason)): if isTransient { print("[\(type)] Push-to-start registration failed (transient, will retry): \(reason)") } else { print("[\(type)] Push-to-start registration failed (permanent): \(reason)") } // Instance-scoped errors case .activityInstance(.registrationFailed(let id, let type, _, let isTransient, let reason)): if isTransient { print("[\(type)] Activity \(id) registration failed (transient, retrying): \(reason)") } else { print("[\(type)] Activity \(id) registration failed (permanent): \(reason)") } case .activityInstance(.activityNotFound(let id, let type)): print("[\(type)] Stored activity \(id) not found on resume") case .activityInstance(.invalidPushTokenTag(let id, let type, let tag)): print("[\(type)] Activity \(id) has invalid push token tag '\(tag)'") } } ``` ### 콘텐츠 상태 업데이트 처리(선택 사항) {#handle-content-state} 실제 라이브 활동 인스턴스의 콘텐츠 상태를 사용하려면 이 섹션을 따르세요. `.contentUpdated` 이벤트가 발생하면 커스텀 로직을 사용하여 `Activity.activities`에서 ID로 실행 중인 `Activity`를 조회한 다음 `activity.content.state`를 통해 타입이 지정된 `ContentState`에 접근합니다. #### 단일 속성 유형 {#single-attributes-type} ```swift case .activityInstance(.contentUpdated(let id, let type)): #if canImport(ActivityKit) // Add custom logic look up the Activity by ID and access your app's typed ContentState. // In this example, `SportsActivityAttributes` is the app's custom type. if #available(iOS 16.2, *), let activity = findActivityInstance(id: id, as: SportsActivityAttributes.self) { // `activityContent` is now strongly typed as a `SportsActivityAttributes` let activityContent = activity.content.state print("[\(type)] Game \(id) — score: \(activityContent.teamOneScore)–\(activityContent.teamTwoScore)") return } #endif print("[\(type)] Content updated for activity \(id)") // ... // - MARK: Helper methods @available(iOS 16.2, *) func findActivityInstance( id: String, as type: Attributes.Type ) -> Activity? { // Use Apple's API to find the matching Live Activity instance: // - https://developer.apple.com/documentation/activitykit/activity/activities Activity.activities.first(where: { $0.id == id }) } ``` #### 다중 속성 유형 {#multiple-attributes-types} 앱에서 여러 `ActivityAttributes` 유형을 사용하는 경우 `type` 문자열을 확인하여 적절한 `Activity`를 조회합니다: ```swift case .activityInstance(.contentUpdated(let id, let type)): #if canImport(ActivityKit) if #available(iOS 16.2, *) { if type == SportsActivityAttributes.name, let activity = findActivityInstance(id: id, as: SportsActivityAttributes.self) { let activityContent = activity.content.state print("[\(type)] Game \(id) — score: \(activityContent.teamOneScore)–\(activityContent.teamTwoScore)") return } else if type == OrderActivityAttributes.name, let activity = findActivityInstance(id: id, as: OrderActivityAttributes.self) { let activityContent = activity.content.state print("[\(type)] Order \(id) — status: \(activityContent.status), ETA: \(activityContent.eta)") return } } #endif print("[\(type)] Content updated for activity \(id)") // ... // - MARK: Helper methods @available(iOS 16.2, *) func findActivityInstance( id: String, as type: Attributes.Type ) -> Activity? { // Use Apple's API to find the matching Live Activity instance: // - https://developer.apple.com/documentation/activitykit/activity/activities Activity.activities.first(where: { $0.id == id }) } ``` ## 자주 묻는 질문(FAQ) {#faq} ### 기능 및 지원 {#functionality-and-support} #### 어떤 플랫폼에서 라이브 활동을 지원하나요? {#what-platforms-support-live-activities} 현재 라이브 활동은 iOS 및 iPadOS에 특정한 기능입니다. 기본적으로 iPhone 또는 iPad에서 시작된 활동은 연결된 watchOS 11+ 또는 macOS 26+ 기기에서도 추가로 표시됩니다. ![macOS 메뉴 바에 알림으로 표시된 라이브 활동의 스크린샷.](https://www.braze.com/docs/ko/ko/assets/img/live-activity-macos.png?ae4757435bb769d2eb1ea1d297477a1c){: style="max-width:60%;"} 라이브 활동 문서에서는 Braze Swift SDK를 통해 라이브 활동을 관리하기 위한 [필수 조건](https://www.braze.com/docs/ko/ko/developer_guide/platforms/swift/live_activities/#prerequisites)을 다룹니다. #### React Native 앱이 라이브 활동을 지원하나요? {#do-react-native-apps-support-live-activities} 예, React Native SDK 3.0.0 이상에서 Braze Swift SDK를 통해 라이브 활동을 지원합니다. 즉, Braze Swift SDK 위에 직접 React Native iOS 코드를 작성해야 합니다. Apple에서 제공하는 라이브 활동 기능은 JavaScript로 변환할 수 없는 언어(예: Swift 동시성, 제네릭, SwiftUI)를 사용하기 때문에 라이브 활동을 위한 React Native 전용 JavaScript 편의 API는 없습니다. #### Braze는 Campaign 또는 캔버스 단계로 라이브 활동을 지원하나요? {#does-braze-support-live-activities-as-a-campaign-or-canvas-step} 아니요, 현재 이 기능은 지원되지 않습니다. ### 푸시 알림 및 라이브 활동 {#push-notifications-and-live-activities} #### 라이브 활동이 활성화되어 있는 동안 푸시 알림이 전송되면 어떻게 되나요? {#what-happens-if-a-push-notification-is-sent-while-a-live-activity-is-active} ![화면 가운데 Bulls 대 Bears 스포츠 경기 라이브 활동이 표시된 휴대폰 화면. 화면 하단에는 푸시 알림 lorem ipsum 텍스트가 표시됩니다.](https://www.braze.com/docs/ko/ko/assets/img/push-vs-live-activities.png?e6bf39f47386b7741f04078c5b0f55fc){: style="max-width:30%;float:right;margin-left:15px;"} 라이브 활동과 푸시 알림은 서로 다른 화면 영역을 차지하며 사용자 화면에서 충돌하지 않습니다. #### 라이브 활동이 푸시 메시지 기능을 활용하는 경우, 라이브 활동을 수신하려면 푸시 알림을 활성화해야 하나요? {#if-live-activities-leverage-push-message-functionality-do-push-notifications-need-to-be-enabled-to-receive-live-activities} 라이브 활동은 업데이트를 위해 푸시 알림에 의존하지만, 서로 다른 사용자 설정에 의해 제어됩니다. 사용자는 라이브 활동에 옵트인하면서 푸시 알림은 옵트아웃할 수 있으며, 그 반대도 가능합니다. 라이브 활동 업데이트 토큰은 8시간 후에 만료됩니다. #### 라이브 활동에 푸시 프라이머가 필요한가요? {#do-live-activities-require-push-primers} [푸시 프라이머](https://www.braze.com/docs/ko/ko/user_guide/channels/push/best_practices/push_primer_messages/)는 사용자에게 앱의 푸시 알림 옵트인을 유도하는 모범 사례입니다. 그러나 라이브 활동에 옵트인하라는 시스템 프롬프트는 없습니다. 기본적으로 사용자는 iOS 16.1 이상에서 해당 앱을 설치할 때 개별 앱의 라이브 활동에 옵트인됩니다. 이 권한은 앱별로 기기 설정에서 비활성화하거나 다시 활성화할 수 있습니다. ### 기술 주제 및 문제 해결 {#technical-topics-and-troubleshooting} #### 라이브 활동에 오류가 있는지 어떻게 알 수 있나요? {#how-do-i-know-if-live-activities-has-errors} 모든 라이브 활동 오류는 Braze 대시보드의 [메시지 활동 로그](https://www.braze.com/docs/ko/ko/user_guide/administer/global/workspace_settings/logs_and_alerts/message_activity_log/)에 기록되며, "LiveActivity Errors"로 필터링할 수 있습니다. #### 푸시 투 스타트 알림을 보낸 후에도 라이브 활동을 받지 못한 이유는 무엇인가요? {#after-sending-a-push-to-start-notification-why-havent-i-received-my-live-activity} 먼저 페이로드에 [`messages/live_activity/start`](https://www.braze.com/docs/ko/ko/api/endpoints/messaging/live_activity/start/) 엔드포인트에 설명된 모든 필수 필드가 포함되어 있는지 확인합니다. `activity_attributes` 및 `content_state` 필드는 프로젝트 코드에 정의된 등록정보와 일치해야 합니다. 페이로드가 정확하다고 확신하는 경우, APNs에 의해 사용량 제한이 적용되었을 수 있습니다. 이 제한은 Braze가 아닌 Apple에서 부과합니다. 푸시 투 스타트 알림이 기기에 성공적으로 도착했지만 사용량 제한으로 인해 표시되지 않았는지 확인하려면 Mac의 콘솔 앱을 사용하여 프로젝트를 디버그할 수 있습니다. 원하는 기기의 기록 프로세스를 연결한 다음 검색창에서 `process:liveactivitiesd`로 로그를 필터링합니다. #### 푸시 투 스타트로 라이브 활동을 시작한 후 새 업데이트를 받지 못하는 이유는 무엇인가요? {#after-starting-my-live-activity-with-push-to-start-why-isnt-it-receiving-new-updates} [위에서](#swift_brazeActivityAttributes) 설명한 지침을 올바르게 구현했는지 확인합니다. `ActivityAttributes`에는 `BrazeLiveActivityAttributes` 프로토콜 준수와 `brazeActivityId` 속성이 모두 포함되어야 합니다. 라이브 활동 푸시 투 스타트 알림을 받은 후, Braze URL의 `/push_token_tag` 엔드포인트로 나가는 네트워크 요청이 표시되는지, `"tag"` 필드 아래에 올바른 활동 ID가 포함되어 있는지 다시 한번 확인합니다. 마지막으로, 업데이트 페이로드의 라이브 활동 속성 유형이 `registerPushToStart` SDK 메서드 호출에 사용된 정확한 문자열 및 클래스와 일치하는지 확인하세요. 오타를 방지하기 위해 상수를 사용하세요. #### `live_activity/update` 엔드포인트를 사용하려고 할 때 액세스 거부 응답을 받습니다. 왜인가요? {#i-am-receiving-an-access-denied-response-when-i-try-to-use-the-live_activityupdate-endpoint-why} 사용하는 API 키에 올바른 권한을 부여해야 다양한 Braze API 엔드포인트에 액세스할 수 있습니다. 이전에 생성한 API 키를 사용하는 경우 해당 권한을 업데이트하지 않았을 수 있습니다. [API 키 보안 개요](https://www.braze.com/docs/ko/ko/api/basics/#rest-api-key-security)를 다시 한번 살펴보세요. #### `messages/send` 엔드포인트는 `messages/live_activity/update` 엔드포인트와 사용량 제한을 공유하나요? {#does-the-messagessend-endpoint-share-rate-limits-with-the-messageslive_activityupdate-endpoint} 기본적으로 `messages/live_activity/update` 엔드포인트의 사용량 제한은 여러 엔드포인트에 걸쳐 워크스페이스당 시간당 250,000건의 요청입니다. 자세한 내용은 [API 사용량 제한](https://www.braze.com/docs/ko/ko/api/api_limits/)을 참조하세요. # Braze SDK의 기능 플래그 Source: /docs/ko/developer_guide/feature_flags/index.md # Feature flags > Feature flags allow you to remotely enable or disable functionality for a specific or random selection of users. Importantly, they let you turn a feature on and off in production without additional code deployment or app store updates. This allows you to safely roll out new features with confidence. **Tip:** When you're ready to create your own feature flags, check out [Creating feature flags](https://www.braze.com/docs/ko/ko/developer_guide/feature_flags/create/). ## Prerequisites These are the minimum SDK versions needed to start using feature flags: ## Use cases ### Gradual rollouts Use feature flags to gradually enable features to a sample population. For example, you can soft launch a new feature to your VIP users first. This strategy helps mitigate risks associated with shipping new features to everyone at once and helps catch bugs early. ![Moving image of rollout traffic slider going from 0% to 100%.](https://www.braze.com/docs/ko/ko/assets/img/feature_flags/feature-flags-rollout.gif?9a18c29013c714574f37682a1e7b0637) For example, let's say we've decided to add a new "Live Chat Support" link to our app for faster customer service. We could release this feature to all customers at once. However, a wide release carries risks, such as: * Our Support team is still in training, and customers can start support tickets after it's released. This doesn't give us any leeway in case the Support team needs more time. * We're unsure of the actual volume of new support cases we'll get, so we might not be staffed appropriately. * If our Support team is overwhelmed, we have no strategy to quickly turn this feature off again. * There might be bugs introduced in the chat widget, and we don't want customers to have a negative experience. With Braze feature flags, we can instead gradually roll out the feature and mitigate all of these risks: * We will turn on the "Live Chat Support" feature when the Support team says they're ready. * We will enable this new feature for only 10% of users to determine if we're staffed appropriately. * If there are any bugs, we can quickly disable the feature instead of rushing to ship a new release. To gradually roll out this feature, we can [create a feature flag](https://www.braze.com/docs/ko/ko/developer_guide/feature_flags/create/) named "Live Chat Widget." ![Feature flag details for an example named Live Chat Widget. The ID is enable_live_chat. This feature flag description reads that the live chat widget will show on the support page.](https://www.braze.com/docs/ko/ko/assets/img/feature_flags/feature-flags-use-case-livechat-1.png?f87ac91f3de136edd7784b806876d8c0) In our app code, we will only show the **Start Live Chat** button when the Braze feature flag is enabled: ```javascript import {useState} from "react"; import * as braze from "@braze/web-sdk"; // Get the initial value from the Braze SDK const featureFlag = braze.getFeatureFlag("enable_live_chat"); const [liveChatEnabled, setLiveChatEnabled] = useState(featureFlag.enabled); // Listen for updates from the Braze SDK braze.subscribeToFeatureFlagsUpdates(() => { const newValue = braze.getFeatureFlag("enable_live_chat").enabled; setLiveChatEnabled(newValue); }); // Only show the Live Chat if the Braze SDK determines it is enabled return (<> Need help? {liveChatEnabled && } ) ``` ```java // Get the initial value from the Braze SDK FeatureFlag featureFlag = braze.getFeatureFlag("enable_live_chat"); Boolean liveChatEnabled = featureFlag != null && featureFlag.getEnabled(); // Listen for updates from the Braze SDK braze.subscribeToFeatureFlagsUpdates(event -> { FeatureFlag newFeatureFlag = braze.getFeatureFlag("enable_live_chat"); Boolean newValue = newFeatureFlag != null && newFeatureFlag.getEnabled(); liveChatEnabled = newValue; }); // Only show the Live Chat view if the Braze SDK determines it is enabled if (liveChatEnabled) { liveChatView.setVisibility(View.VISIBLE); } else { liveChatView.setVisibility(View.GONE); } ``` ```kotlin // Get the initial value from the Braze SDK val featureFlag = braze.getFeatureFlag("enable_live_chat") var liveChatEnabled = featureFlag?.enabled // Listen for updates from the Braze SDK braze.subscribeToFeatureFlagsUpdates() { event -> val newValue = braze.getFeatureFlag("enable_live_chat")?.enabled liveChatEnabled = newValue } // Only show the Live Chat view if the Braze SDK determines it is enabled if (liveChatEnabled) { liveChatView.visibility = View.VISIBLE } else { liveChatView.visibility = View.GONE } ``` ```swift // Get the initial value from the Braze SDK let featureFlag = braze.featureFlags.featureFlag(id: "enable_live_chat") var liveChatEnabled = featureFlag?.enabled ?? false // Listen for updates from the Braze SDK braze.featureFlags.subscribeToUpdates() { _ in let newValue = braze.featureFlags.featureFlag(id: "enable_live_chat")?.enabled ?? false liveChatEnabled = newValue } // Only show the Live Chat view if the Braze SDK determines it is enabled liveChatView.isHidden = !liveChatEnabled ``` ### Remotely control app variables Use feature flags to modify your app's functionality in production. This can be particularly important for mobile apps, where app store approvals prevent rolling out changes quickly to all users. For example, let's say that our marketing team wants to list our current sales and promotions in our app's navigation. Normally, our engineers require one week's lead time for any changes and three days for an app store review. But with Thanksgiving, Black Friday, Cyber Monday, Hanukkah, Christmas, and New Year's Day all within two months, we won't be able to meet these tight deadlines. With feature flags, we can let Braze power the content of our app navigation link, letting our marketing manager make changes in minutes rather than days. To remotely configure this feature, we'll create a new feature flag called `navigation_promo_link` and define the following initial properties: ![Feature flag with link and text properties directing to a generic sales page.](https://www.braze.com/docs/ko/ko/assets/img/feature_flags/feature-flags-use-case-navigation-link-1.png?bb61c2aee4c725233b491bdb762a99f8) In our app, we'll use getter methods by Braze to retrieve this feature flag's properties and build the navigation links based on those values: ```javascript import * as braze from "@braze/web-sdk"; import {useState} from "react"; const featureFlag = braze.getFeatureFlag("navigation_promo_link"); // Check if the feature flag is enabled const [promoEnabled, setPromoEnabled] = useState(featureFlag.enabled); // Read the "link" property const [promoLink, setPromoLink] = useState(featureFlag.getStringProperty("link")); // Read the "text" property const [promoText, setPromoText] = useState(featureFlag.getStringProperty("text")); return (<>
Home { promoEnabled && {promoText} } Products Categories
) ``` ```java // liveChatView is the View container for the Live Chat UI FeatureFlag featureFlag = braze.getFeatureFlag("navigation_promo_link"); if (featureFlag != null && featureFlag.getEnabled()) { liveChatView.setVisibility(View.VISIBLE); } else { liveChatView.setVisibility(View.GONE); } liveChatView.setPromoLink(featureFlag.getStringProperty("link")); liveChatView.setPromoText(featureFlag.getStringProperty("text")); ``` ```kotlin // liveChatView is the View container for the Live Chat UI val featureFlag = braze.getFeatureFlag("navigation_promo_link") if (featureFlag?.enabled == true) { liveChatView.visibility = View.VISIBLE } else { liveChatView.visibility = View.GONE } liveChatView.promoLink = featureFlag?.getStringProperty("link") liveChatView.promoText = featureFlag?.getStringProperty("text") ``` ```swift let featureFlag = braze.featureFlags.featureFlag(id: "navigation_promo_link") if let featureFlag { liveChatView.isHidden = !featureFlag.enabled } else { liveChatView.isHidden = true } liveChatView.promoLink = featureFlag?.stringProperty("link") liveChatView.promoText = featureFlag?.stringProperty("text") ``` Now, the day before Thanksgiving, we only have to change those property values in the Braze dashboard. ![Feature flag with link and text properties directing to a Thanksgiving sales page.](https://www.braze.com/docs/ko/ko/assets/img/feature_flags/feature-flags-use-case-navigation-link-2.png?161a40a2366fc2441facbd206639b55c) As a result, the next time someone loads the app, they will see the new Thanksgiving deals. ### Message coordination Use feature flags to synchronize a feature's rollout and messaging and strengthen collaboration between product and marketing teams. By coordinating feature releases and messaging through feature flags, both teams can align their strategies and create consistent user experiences. For example, let's say that we're launching a new loyalty rewards program for our users. It can be difficult for marketing and product teams to perfectly coordinate the timing of promotional messaging with a feature's rollout. However, with feature flags in Canvas, our product team can apply sophisticated logic to enable a feature for a specific audience, while our marketing team controls the related messaging to those same users. To effectively coordinate feature rollout and messaging, we'll create a new feature flag called `show_loyalty_program`. For our initial phased release, we'll let Canvas control when and for whom the feature flag is enabled. For now, we'll leave the rollout percentage at 0% and not select any target segments. ![A feature flag with the name Loyalty Rewards Program. The ID is show_loyalty_program, and the description that this shows the new loyalty rewards program on the home screen and profile page.](https://www.braze.com/docs/ko/ko/assets/img/feature_flags/feature-flags-use-case-loyalty.png?8df52467dc60091b3ee90869d4c0c688) Then, in Canvas, we'll create a [Feature Flag step](https://www.braze.com/docs/ko/ko/user_guide/engagement_tools/canvas/canvas_components/feature_flags/) that enables the `show_loyalty_program` feature flag for our "High Value Customers" segment: ![An example of a Canvas with an Audience Split step where the high-value customers segment turns on the show_loyalty_program feature flag.](https://www.braze.com/docs/ko/ko/assets/img/feature_flags/feature-flags-use-case-canvas-flow.png?20b6eb4882f8f131848dff0c70948c92) Now, users in this segment start to see the new loyalty program, and after it's enabled, an email and survey are sent out automatically to help our teams gather feedback. ### Feature experimentation Use feature flags to experiment and confirm your hypotheses around your new feature. By splitting traffic into two or more groups, you can compare the impact of a feature flag across groups, and determine the best course of action based on the results. An [A/B test](https://www.braze.com/docs/ko/ko/user_guide/engagement_tools/testing/multivariant_testing/) is a powerful tool that compares users' responses to multiple versions of a variable. In this example, our team has built a new checkout flow for our eCommerce app. Even though we're confident it's improving the user experience, we want to run an A/B test to measure its impact on our app's revenue. To begin, we'll create a new feature flag called `enable_checkout_v2`. We won't add an audience or rollout percentage. Instead, we'll use a feature flag experiment to split traffic, enable the feature, and measure the outcome. In our app, we'll check if the feature flag is enabled or not and swap out the checkout flow based on the response: ```javascript import * as braze from "@braze/web-sdk"; const featureFlag = braze.getFeatureFlag("enable_checkout_v2"); braze.logFeatureFlagImpression("enable_checkout_v2"); if (featureFlag?.enabled) { return } else { return } ``` ```java FeatureFlag featureFlag = braze.getFeatureFlag("enable_checkout_v2"); braze.logFeatureFlagImpression("enable_checkout_v2"); if (featureFlag != null && featureFlag.getEnabled()) { return new NewCheckoutFlow(); } else { return new OldCheckoutFlow(); } ``` ```kotlin val featureFlag = braze.getFeatureFlag("enable_checkout_v2") braze.logFeatureFlagImpression("enable_checkout_v2") if (featureFlag?.enabled == true) { return NewCheckoutFlow() } else { return OldCheckoutFlow() } ``` ```swift let featureFlag = braze.featureFlags.featureFlag(id: "enable_checkout_v2") braze.featureFlags.logFeatureFlagImpression(id: "enable_checkout_v2") if let featureFlag, featureFlag.enabled { return NewCheckoutFlow() } else { return OldCheckoutFlow() } ``` We'll set up our A/B test in a [Feature Flag Experiment](https://www.braze.com/docs/ko/ko/developer_guide/feature_flags/experiments/). Now, 50% of users will see the old experience, while the other 50% will see the new experience. We can then analyze the two variants to determine which checkout flow resulted in a higher conversion rate. ![A feature flag experiment splitting traffic into two 50 percent groups.](https://www.braze.com/docs/ko/ko/assets/img/feature_flags/feature-flag-use-case-campaign-experiment.png?e8202a94961d079e7676f6d8345ab8cc) Once we determine our winner, we can stop this campaign and increase the rollout percentage on the feature flag to 100% for all users while our engineering team hard-codes this into our next app release. ### Segmentation Use the **Feature Flag** filter to create a segment or target messaging at users based on whether they have a feature flag enabled. For example, let's say we have a feature flag that controls premium content in our app. We could create a segment that filters for users who don't have the feature flag enabled, and then send that segment a message urging them to upgrade their account to view premium content. ![](https://www.braze.com/docs/ko/ko/assets/img/feature_flags/feature_flag_segmentation_filter.png?1e1d240bffb7e96c29d06a6fe26298aa) For more information about filtering on segments, see [Creating a segment](https://www.braze.com/docs/ko/ko/user_guide/engagement_tools/segments/creating_a_segment/). **Note:** To prevent recursive segments, it is not possible to create a segment that references other feature flags. ## Plan limitations These are the feature flag limitations for free and paid plans. | Feature | Free version | Paid version | | :---------------------------------------------------------------------------------------------------------------- | :--------------- | ----------------- | | [Active feature flags](#active-feature-flags) | 10 per workspace | 110 per workspace | | [Active campaign experiments](https://www.braze.com/docs/ko/ko/developer_guide/feature_flags/experiments/) | 1 per workspace | 100 per workspace | | [Feature Flag Canvas steps](https://www.braze.com/docs/ko/ko/user_guide/engagement_tools/canvas/canvas_components/feature_flags/) | Unlimited | Unlimited | {: .reset-td-br-1 .reset-td-br-2 aria-label="Plan limitations" } A feature flag is considered active and will count toward your limit if any of the following apply: - Rollout is more than 0% - Used in an active Canvas - Used in an active experiment Even if the same feature flag matches multiple criteria, such as if it's used in a Canvas and the rollout is 50%, it will only count as 1 active feature flag toward your limit. **Note:** To purchase the paid version of feature flags, contact your Braze account manager, or request an upgrade in the Braze dashboard. # 기능 플래그 생성 Source: /docs/ko/developer_guide/feature_flags/create/index.md # Create feature flags > Feature flags allow you to remotely enable or disable functionality for a selection of users. Create a new feature flag within the Braze dashboard. Provide a name and an `ID`, a target audience, and a percentage of users for whom to enable to this feature. Then, using that same `ID` in your app or website's code, you can conditionally run certain parts of your business logic. To learn more about feature flags and how you can use them in Braze, see [About feature flags](https://www.braze.com/docs/ko/ko/developer_guide/feature_flags/). ## Prerequisites ### SDK version To use feature flags, ensure your SDKs are up to date with at least these minimum versions: ### Braze permissions To manage feature flags in the dashboard, you'll either need to be an Administrator, or have the following [permissions](https://www.braze.com/docs/ko/ko/user_guide/administrative/app_settings/manage_your_braze_users/user_permissions/): | Permission | What you can do | |-------------------------------------------------------------------------------|-------------------------------------------| | **Manage Feature Flags** | View, create, and edit feature flags. | | **Access Campaigns, Canvases, Cards, Feature Flags, Segments, Media Library** | View the list of available feature flags. | {: .reset-td-br-1 .reset-td-br-2 .reset-td-br-3 aria-label="Braze permissions" } ## Creating a feature flag ### Step 1: Create a new feature flag Go to **Messaging** > **Feature Flags**, then select **Create Feature Flag**. ![A datatable showing an existing feature flag and how to create a new one.](https://www.braze.com/docs/ko/ko/assets/img/feature_flags/create_ff.png?c5a473da4c9497a16786b50273e89722){: style="max-width:75%"} ### Step 2: Fill out the details Under **Feature flag details**, enter a name, ID, and description for your feature flag. ![A form showing that you can add a name, ID, description and properties to a feature flag.](https://www.braze.com/docs/ko/ko/assets/img/feature_flags/create_ff_properties.png?08b4de8b7b9b76779e97203d614e6689){: style="max-width:75%"} | Field | Description | |--------------|----------------------------------------------------------------------------| | Name | A human-readable title for your marketers and administrators. | | ID | The unique ID you'll use in your code to check if this feature is [enabled for a user](#enabled). This ID cannot be changed later, so review the [ID naming best practices](#naming-conventions) before continuing. | | Description | An optional description that gives some context about your feature flag. | | Properties | Optional properties that remotely configure your feature flag. They can be overwritten in Canvas steps or feature flag experiments. | {: .reset-td-br-1 .reset-td-br-2 .reset-td-br-3 aria-label="Step 2: Fill out the details" } ### Step 2a: Create custom properties Under **Properties**, you can optionally create custom properties your app can access through the Braze SDK when your feature is enabled. You can assign a string, boolean, image, timestamp, JSON, or a number value to each variable, as well as set a default value. In the following example, the feature flag shows an out-of-stock banner for an eCommerce store using the custom properties listed: |Property Name|Type|Value| |--|--|--| |`banner_height`|`number`|`75`| |`banner_color`|`string`|`blue`| |`banner_text`|`string`|`Widgets are out of stock until July 1.`| |`dismissible`|`boolean`|`false`| |`homepage_icon`|`image`|`http://s3.amazonaws.com/[bucket_name]/`| |`account_start`|`timestamp`|`2011-01-01T12:00:00Z`| |`footer_settings`|`JSON`|`{ "colors": [ "red", "blue", "green" ], "placement": 123 }`| {: .reset-td-br-1 .reset-td-br-2 .reset-td-br-3 aria-label="Step 2a: Create custom properties" } **Tip:** There is no limit to the number of properties you can add. However, a feature flag's properties are limited to a total of 10,000 characters. ### Step 4: Choose segments to target Before rolling out a feature flag, you need to choose a [segment](https://www.braze.com/docs/ko/ko/user_guide/engagement_tools/segments/) of users to target. Select **Add Rule** on your newly created flag and then use the filter group and segment dropdown menus to filter users out of your target audience. Add multiple filters to further narrow your audience. ![A textbox labeled Rollout Traffic with the ability to add segments and filters.](https://www.braze.com/docs/ko/ko/assets/img/feature_flags/segmentation_ff.png?32d97ed8745dd0fa1a0e3be1b416dc65){: style="max-width:75%;"} ### Step 5: Set the rollout traffic {#rollout} By default, feature flags are always inactive, which allows you to separate your feature release's date from your total user activation. To begin your rollout, use the **Rollout Traffic** section to enter a percentage in the text box. This will choose the percentage of random users in your selected segment to receive this new feature. **Important:** Do not set your rollout traffic above 0% until you are ready for your new feature to go live. When you initially define your feature flag in the dashboard, leave this setting at 0%. **Important:** To roll out a flag with just one rule or to a singular audience, add your first rule with segmentation criteria and rollout percentages selected. Lastly, confirm the **Everyone Else** rule is toggled off, and save your flag. ## Multi-rule feature flag rollouts Use multi-rule feature flag rollouts to define a sequence of rules for evaluating users, which allows for precise segmentation and controlled feature releases. This method is ideal for deploying the same feature to diverse audiences. ### Evaluation order Feature flag rules are evaluated from top to bottom, in the order they're listed. A user qualifies for the first rule they meet. If a user doesn't meet any rules, their eligibility is determined by the default "Everyone Else" rule. ### User qualification - If a user meets the criteria for the first rule, they are immediately eligible to receive the feature flag. - If a user doesn't qualify for the first rule, they're evaluated against the second rule, and so on. The sequential evaluation continues until a user qualifies for a rule or reaches the "Everyone Else" rule at the bottom of the list. ### "Everyone Else" rule The "Everyone Else" rule acts as a default. If a user doesn't qualify for any preceding rules, their eligibility for the feature flag will be determined by the toggle setting of the "Everyone Else" rule. For example, if the "Everyone Else" rule is toggled "Off", in the default state, a user who doesn't meet the criteria for any other rules won't receive the feature flag upon their session start. ### Re-ordering rules By default, rules are ordered in the sequence that they're created, but you can reorder these rules by dragging and dropping them in the dashboard. ![An image showing that a user can add a rule to a feature flag.](https://www.braze.com/docs/ko/ko/assets/img/feature_flags/add_rule.png?5e7f228358ecd02cb9f215d94a96b050){: style="max-width:80%;"} ![An image showing a summary of a feature flag with multiple rules added and an everyone else rule.](https://www.braze.com/docs/ko/ko/assets/img/feature_flags/mr_rules_overview.png?6b3828dbafe8adf27aa654af59b30a3f){: style="max-width:80%;"} ### Multi-rule feature flag use cases #### Gradually release a checkout page Let's say you work for an eCommerce brand and have a new checkout page that you want to rollout across different geographies to ensure stability. Using multi-rule feature flags, you can set the following: - **Rule 1:** Your US segment is set to 100%. - **Rule 2:** Your segment is set to 50% of your Brazilian users, so not all of them receive the flow at one time. - **Rule 3 (Everyone Else):** For all other users, toggle on your "Everyone Else" rule and set it to 15%, so that a portion of all users can check out with the new flow. #### Reach internal testers first Let's say you're a product manager who wants to make sure your internal testers always receive the feature flag when you release a new product. You can add your internal testers segment to your first rule and set it to 100%, so that your internal testers are eligible during every feature rollout. ## Using the "enabled" field for your feature flags {#enabled} After you've defined your feature flag, configure your app or site to check whether or not it is enabled for a particular user. When it is enabled, you'll set some action or reference the feature flag's variable properties based on your use case. The Braze SDK provides getter methods to pull your feature flag's status and its properties into your app. Feature flags are refreshed automatically at session start so that you can display the most up-to-date version of your feature upon launch. The SDK caches these values so they can be used while offline. **Note:** Be sure to log [feature flag impressions](#impressions). Let's say you were to rolling out a new type of user profile for your app. You might set the `ID` as `expanded_user_profile`. Then, you would have your app check to see if it should display this new user profile to a particular user. For example: ```javascript const featureFlag = braze.getFeatureFlag("expanded_user_profile"); if (featureFlag?.enabled) { console.log(`expanded_user_profile is enabled`); } else { console.log(`expanded_user_profile is not enabled`); } ``` ```swift let featureFlag = braze.featureFlags.featureFlag(id: "expanded_user_profile") if featureFlag?.enabled == true { print("expanded_user_profile is enabled") } else { print("expanded_user_profile is not enabled") } ``` ```java FeatureFlag featureFlag = braze.getFeatureFlag("expanded_user_profile"); if (featureFlag != null && featureFlag.getEnabled()) { Log.i(TAG, "expanded_user_profile is enabled"); } else { Log.i(TAG, "expanded_user_profile is not enabled"); } ``` ```kotlin val featureFlag = braze.getFeatureFlag("expanded_user_profile") if (featureFlag?.enabled == true) { Log.i(TAG, "expanded_user_profile is enabled.") } else { Log.i(TAG, "expanded_user_profile is not enabled.") } ``` ```javascript const featureFlag = await Braze.getFeatureFlag("expanded_user_profile"); if (featureFlag?.enabled) { console.log(`expanded_user_profile is enabled`); } else { console.log(`expanded_user_profile is not enabled`); } ``` ```csharp var featureFlag = Appboy.AppboyBinding.GetFeatureFlag("expanded_user_profile"); if (featureFlag != null && featureFlag.Enabled) { Console.WriteLine("expanded_user_profile is enabled"); } else { Console.WriteLine("expanded_user_profile is not enabled"); } ``` ```javascript const featureFlag = await BrazePlugin.getFeatureFlag("expanded_user_profile"); if (featureFlag?.enabled) { console.log(`expanded_user_profile is enabled`); } else { console.log(`expanded_user_profile is not enabled`); } ``` ```dart BrazeFeatureFlag? featureFlag = await braze.getFeatureFlagByID("expanded_user_profile"); if (featureFlag?.enabled == true) { print("expanded_user_profile is enabled"); } else { print("expanded_user_profile is not enabled"); } ``` ```brightscript featureFlag = m.braze.getFeatureFlag("expanded_user_profile") if featureFlag <> invalid and featureFlag.enabled print "expanded_user_profile is enabled" else print "expanded_user_profile is not enabled" end if ``` ### Logging a feature flag impression {#impressions} Track a feature flag impression whenever a user has had an opportunity to interact with your new feature, or when they __could__ have interacted if the feature is disabled (in the case of a control group in an A/B test). Feature flag impressions are only logged once per session. Usually, you can put this line of code directly underneath where you reference your feature flag in your app: ```javascript braze.logFeatureFlagImpression("expanded_user_profile"); ``` ```swift braze.featureFlags.logFeatureFlagImpression(id: "expanded_user_profile") ``` ```java braze.logFeatureFlagImpression("expanded_user_profile"); ``` ```kotlin braze.logFeatureFlagImpression("expanded_user_profile") ``` ```javascript Braze.logFeatureFlagImpression("expanded_user_profile"); ``` ```csharp Appboy.AppboyBinding.LogFeatureFlagImpression("expanded_user_profile"); ``` ```javascript BrazePlugin.logFeatureFlagImpression("expanded_user_profile"); ``` ```dart braze.logFeatureFlagImpression("expanded_user_profile"); ``` ```brightscript m.Braze.logFeatureFlagImpression("expanded_user_profile"); ``` ### Accessing properties {#accessing-properties} To access the properties of a feature flag, use one of the following methods depending on the type you defined in the dashboard. If there is no such property of the corresponding type for the key you provided, these methods will return `null`. ```javascript // Returns the Feature Flag instance const featureFlag = braze.getFeatureFlag("expanded_user_profile"); // Returns the String property const stringProperty = featureFlag.getStringProperty("color"); // Returns the boolean property const booleanProperty = featureFlag.getBooleanProperty("expanded"); // Returns the number property const numberProperty = featureFlag.getNumberProperty("height"); // Returns the Unix UTC millisecond timestamp property as a number const timestampProperty = featureFlag.getTimestampProperty("account_start"); // Returns the image property as a String of the image URL const imageProperty = featureFlag.getImageProperty("homepage_icon"); // Returns the JSON object property as a FeatureFlagJsonPropertyValue const jsonProperty = featureFlag.getJsonProperty("footer_settings"); ``` ```swift // Returns the Feature Flag instance let featureFlag: FeatureFlag = braze.featureFlags.featureFlag(id: "expanded_user_profile") // Returns the string property let stringProperty: String? = featureFlag.stringProperty(key: "color") // Returns the boolean property let booleanProperty: Bool? = featureFlag.boolProperty(key: "expanded") // Returns the number property as a double let numberProperty: Double? = featureFlag.numberProperty(key: "height") // Returns the Unix UTC millisecond timestamp property as an integer let timestampProperty: Int? = featureFlag.timestampProperty(key: "account_start") // Returns the image property as a String of the image URL let imageProperty: String? = featureFlag.imageProperty(key: "homepage_icon") // Returns the JSON object property as a [String: Any] dictionary let jsonObjectProperty: [String: Any]? = featureFlag.jsonObjectProperty(key: "footer_settings") ``` ```java // Returns the Feature Flag instance FeatureFlag featureFlag = braze.getFeatureFlag("expanded_user_profile"); // Returns the String property String stringProperty = featureFlag.getStringProperty("color"); // Returns the boolean property Boolean booleanProperty = featureFlag.getBooleanProperty("expanded"); // Returns the number property Number numberProperty = featureFlag.getNumberProperty("height"); // Returns the Unix UTC millisecond timestamp property as a long Long timestampProperty = featureFlag.getTimestampProperty("account_start"); // Returns the image property as a String of the image URL String imageProperty = featureFlag.getImageProperty("homepage_icon"); // Returns the JSON object property as a JSONObject JSONObject jsonObjectProperty = featureFlag.getJSONProperty("footer_settings"); ``` ```kotlin // Returns the Feature Flag instance val featureFlag = braze.getFeatureFlag("expanded_user_profile") // Returns the String property val stringProperty: String? = featureFlag.getStringProperty("color") // Returns the boolean property val booleanProperty: Boolean? = featureFlag.getBooleanProperty("expanded") // Returns the number property val numberProperty: Number? = featureFlag.getNumberProperty("height") // Returns the Unix UTC millisecond timestamp property as a long val timestampProperty: Long? = featureFlag.getTimestampProperty("account_start") // Returns the image property as a String of the image URL val imageProperty: String? = featureFlag.getImageProperty("homepage_icon") // Returns the JSON object property as a JSONObject val jsonObjectProperty: JSONObject? = featureFlag.getJSONProperty("footer_settings") ``` ```javascript // Returns the String property const stringProperty = await Braze.getFeatureFlagStringProperty("expanded_user_profile", "color"); // Returns the boolean property const booleanProperty = await Braze.getFeatureFlagBooleanProperty("expanded_user_profile", "expanded"); // Returns the number property const numberProperty = await Braze.getFeatureFlagNumberProperty("expanded_user_profile", "height"); // Returns the Unix UTC millisecond timestamp property as a number const timestampProperty = await Braze.getFeatureFlagTimestampProperty("expanded_user_profile", "account_start"); // Returns the image property as a String of the image URL const imageProperty = await Braze.getFeatureFlagImageProperty("expanded_user_profile", "homepage_icon"); // Returns the JSON object property as an object const jsonObjectProperty = await Braze.getFeatureFlagJSONProperty("expanded_user_profile", "footer_settings"); ``` ```csharp // Returns the Feature Flag instance var featureFlag = Appboy.AppboyBinding.GetFeatureFlag("expanded_user_profile"); // Returns the String property var stringProperty = featureFlag.GetStringProperty("color"); // Returns the boolean property var booleanProperty = featureFlag.GetBooleanProperty("expanded"); // Returns the number property as an integer var integerProperty = featureFlag.GetIntegerProperty("height"); // Returns the number property as a double var doubleProperty = featureFlag.GetDoubleProperty("height"); // Returns the Unix UTC millisecond timestamp property as a long var timestampProperty = featureFlag.GetTimestampProperty("account_start"); // Returns the image property as a String of the image URL var imageProperty = featureFlag.GetImageProperty("homepage_icon"); // Returns the JSON object property as a JSONObject var jsonObjectProperty = featureFlag.GetJSONProperty("footer_settings"); ``` ```javascript // Returns the String property const stringProperty = await BrazePlugin.getFeatureFlagStringProperty("expanded_user_profile", "color"); // Returns the boolean property const booleanProperty = await BrazePlugin.getFeatureFlagBooleanProperty("expanded_user_profile", "expanded"); // Returns the number property const numberProperty = await BrazePlugin.getFeatureFlagNumberProperty("expanded_user_profile", "height"); // Returns the Unix UTC millisecond timestamp property as a number const timestampProperty = await BrazePlugin.getFeatureFlagTimestampProperty("expanded_user_profile", "account_start"); // Returns the image property as a String of the image URL const imageProperty = await BrazePlugin.getFeatureFlagImageProperty("expanded_user_profile", "homepage_icon"); // Returns the JSON object property as an object const jsonObjectProperty = await BrazePlugin.getFeatureFlagJSONProperty("expanded_user_profile", "footer_settings"); ``` ```dart // Returns the Feature Flag instance BrazeFeatureFlag featureFlag = await braze.getFeatureFlagByID("expanded_user_profile"); // Returns the String property var stringProperty = featureFlag.getStringProperty("color"); // Returns the boolean property var booleanProperty = featureFlag.getBooleanProperty("expanded"); // Returns the number property var numberProperty = featureFlag.getNumberProperty("height"); // Returns the Unix UTC millisecond timestamp property as an integer var timestampProperty = featureFlag.getTimestampProperty("account_start"); // Returns the image property as a String of the image URL var imageProperty = featureFlag.getImageProperty("homepage_icon"); // Returns the JSON object property as a Map collection var jsonObjectProperty = featureFlag.getJSONProperty("footer_settings"); ``` ```brightscript ' Returns the String property color = featureFlag.getStringProperty("color") ' Returns the boolean property expanded = featureFlag.getBooleanProperty("expanded") ' Returns the number property height = featureFlag.getNumberProperty("height") ' Returns the Unix UTC millisecond timestamp property account_start = featureFlag.getTimestampProperty("account_start") ' Returns the image property as a String of the image URL homepage_icon = featureFlag.getImageProperty("homepage_icon") ' Returns the JSON object property footer_settings = featureFlag.getJSONProperty("footer_settings") ``` ### Getting a list of all feature flags {#get-list-of-flags} ```javascript const features = getAllFeatureFlags(); for(const feature of features) { console.log(`Feature: ${feature.id}`, feature.enabled); } ``` ```swift let features = braze.featureFlags.featureFlags for let feature in features { print("Feature: \(feature.id)", feature.enabled) } ``` ```java List features = braze.getAllFeatureFlags(); for (FeatureFlag feature: features) { Log.i(TAG, "Feature: ", feature.getId(), feature.getEnabled()); } ``` ```kotlin val featureFlags = braze.getAllFeatureFlags() featureFlags.forEach { feature -> Log.i(TAG, "Feature: ${feature.id} ${feature.enabled}") } ``` ```javascript const features = await Braze.getAllFeatureFlags(); for(const feature of features) { console.log(`Feature: ${feature.id}`, feature.enabled); } ``` ```csharp List features = Appboy.AppboyBinding.GetAllFeatureFlags(); foreach (FeatureFlag feature in features) { Console.WriteLine("Feature: {0} - enabled: {1}", feature.ID, feature.Enabled); } ``` ```javascript const features = await BrazePlugin.getAllFeatureFlags(); for(const feature of features) { console.log(`Feature: ${feature.id}`, feature.enabled); } ``` ```dart List featureFlags = await braze.getAllFeatureFlags(); featureFlags.forEach((feature) { print("Feature: ${feature.id} ${feature.enabled}"); }); ``` ```brightscript features = m.braze.getAllFeatureFlags() for each feature in features print "Feature: " + feature.id + " enabled: " + feature.enabled.toStr() end for ``` ### Refreshing feature flags {#refreshing} You can refresh the current user's feature flags mid-session to pull the latest values from Braze. **Tip:** Refreshing happens automatically upon session start. Refreshing is only needed prior to important user actions, such as before loading a checkout page, or if you know a feature flag will be referenced. ```javascript braze.refreshFeatureFlags(() => { console.log(`Feature flags have been refreshed.`); }, () => { console.log(`Failed to refresh feature flags.`); }); ``` ```swift braze.featureFlags.requestRefresh { result in switch result { case .success(let features): print("Feature flags have been refreshed:", features) case .failure(let error): print("Failed to refresh feature flags:", error) } } ``` ```java braze.refreshFeatureFlags(); ``` ```kotlin braze.refreshFeatureFlags() ``` ```javascript Braze.refreshFeatureFlags(); ``` ```csharp Appboy.AppboyBinding.RefreshFeatureFlags(); ``` ```javascript BrazePlugin.refreshFeatureFlags(); ``` ```dart braze.refreshFeatureFlags(); ``` ```brightscript m.Braze.refreshFeatureFlags() ``` ### Listening for changes {#updates} You can configure the Braze SDK to listen and update your app when the SDK refreshes any feature flags. This is useful if you want to update your app if a user is no longer eligible for a feature. For example, setting some state in your app based on whether or not a feature is enabled, or one of its property values. ```javascript // Register an event listener const subscriptionId = braze.subscribeToFeatureFlagsUpdates((features) => { console.log(`Features were updated`, features); }); // Unregister this event listener braze.removeSubscription(subscriptionId); ``` ```swift // Create the feature flags subscription // - You must keep a strong reference to the subscription to keep it active let subscription = braze.featureFlags.subscribeToUpdates { features in print("Feature flags were updated:", features) } // Cancel the subscription subscription.cancel() ``` ```java braze.subscribeToFeatureFlagsUpdates(event -> { Log.i(TAG, "Feature flags were updated."); for (FeatureFlag feature: event.getFeatureFlags()) { Log.i(TAG, "Feature: ", feature.getId(), feature.getEnabled()); } }); ``` ```kotlin braze.subscribeToFeatureFlagsUpdates() { event -> Log.i(TAG, "Feature flags were updated.") event.featureFlags.forEach { feature -> Log.i(TAG, "Feature: ${feature.id}") } } ``` ```javascript // Register an event listener Braze.addListener(braze.Events.FEATURE_FLAGS_UPDATED, (featureFlags) => { console.log(`featureFlagUpdates`, JSON.stringify(featureFlags)); }); ``` To listen for changes, set the values for **Game Object Name** and **Callback Method Name** under **Braze Configuration** > **Feature Flags** to the corresponding values in your application. ```javascript // Register an event listener BrazePlugin.subscribeToFeatureFlagUpdates((featureFlags) => { console.log(`featureFlagUpdates`, JSON.stringify(featureFlags)); }); ``` In the Dart code in your app, use the following sample code: ```dart // Create stream subscription StreamSubscription featureFlagsStreamSubscription; featureFlagsStreamSubscription = braze.subscribeToFeatureFlags((featureFlags) { print("Feature flags were updated"); }); // Cancel stream subscription featureFlagsStreamSubscription.cancel(); ``` Feature flag data is automatically forwarded from both the Android and iOS native layers. No additional setup is required. If you're using Flutter SDK 17.1.0 or earlier, feature flag data forwarding from the iOS native layer requires manual setup. Your application likely contains a `featureFlags.subscribeToUpdates` callback that calls `BrazePlugin.processFeatureFlags(featureFlags)`. To migrate to Flutter SDK 18.0.0, remove the `BrazePlugin.processFeatureFlags(_:)` call—data forwarding is now handled automatically. For an example, see [AppDelegate.swift](https://github.com/braze-inc/braze-flutter-sdk/blob/master/example/ios/Runner/AppDelegate.swift) in the Braze Flutter SDK sample application. ```brightscript ' Define a function called `onFeatureFlagChanges` to be called when feature flags are refreshed m.BrazeTask.ObserveField("BrazeFeatureFlags", "onFeatureFlagChanges") ``` ```typescript import { useEffect, useState } from "react"; import { FeatureFlag, getFeatureFlag, removeSubscription, subscribeToFeatureFlagsUpdates, } from "@braze/web-sdk"; export const useFeatureFlag = (id: string): FeatureFlag => { const [featureFlag, setFeatureFlag] = useState( getFeatureFlag(id) ); useEffect(() => { const listener = subscribeToFeatureFlagsUpdates(() => { setFeatureFlag(getFeatureFlag(id)); }); return () => { removeSubscription(listener); }; }, [id]); return featureFlag; }; ``` ## Checking user eligibility To check which feature flags a user is eligible for in Braze, go to **Audience** > **Search Users**, then search for and select a user. In the **Feature Flags Eligibility** tab, you can filter the list of eligible feature flags by platform, application, or device. You can also preview the payload that will be returned to the user by selecting next to a feature flag. ![An image showing the table of feature flags a user is eligible for.](https://www.braze.com/docs/ko/ko/assets/img/feature_flags/eligibility.png?faa287e849be2508f0622972e0fb2ed9){: style="max-width:85%;"} ## Viewing the changelog To view a feature flag's changelog, open a feature flag and select **Changelog**. ![A feature flag's "Edit" page, with the "Changelog" button highlighted.](https://www.braze.com/docs/ko/ko/assets/img/feature_flags/changelog/open_changelog.png?45a939d4bb3aa60da211695f6b60a4f2){: style="max-width:60%;"} Here, you can review when a change happened, who made the change, which category it belongs to, and more. ![The changelog of the selected feature flag.](https://www.braze.com/docs/ko/ko/assets/img/feature_flags/changelog/changelog.png?eced90e7169fdea5c5a40287f59afb38){: style="max-width:90%;"} ## Segmenting with feature flags {#segmentation} Braze automatically keeps track of which users are currently enabled for a feature flag. You can create a segment or target messaging using the [**Feature Flag** filter](https://www.braze.com/docs/ko/ko/user_guide/engagement_tools/segments/segmentation_filters/#feature-flags). For more information about filtering on segments, see [Creating a segment](https://www.braze.com/docs/ko/ko/user_guide/engagement_tools/segments/creating_a_segment/). ![The "Filters" section with "Feature Flag" typed into the filter search bar.](https://www.braze.com/docs/ko/ko/assets/img/feature_flags/feature-flags-filter-name.png?ad7d0b8534a8f300591cbb11fd2d9f10){: style="max-width:75%;"} **Note:** To prevent recursive segments, it is not possible to create a segment that references other feature flags. ## Best practices ### Don't combine rollouts with Canvases or experiments To avoid users being enabled and disabled by different entry points, you should either set the rollouts slider to a value greater than zero OR enable the feature flag in a Canvas or experiment. As a best practice, if you plan to use a feature flag in a Canvas or experiment, keep the rollout percentage at zero. ### Naming conventions To keep your code clear and consistent, consider using the following format when naming your feature flag ID: ```plaintext BEHAVIOR_PRODUCT_FEATURE ``` Replace the following: | Placeholder | Description | |-------------|---------------------------------------------------------------------------------------------------------------------------| | `BEHAVIOR` | The behavior of the feature. In your code, be sure the behavior is disabled by default and avoid using phrases like `disabled` in the feature flag name. | | `PRODUCT` | The product the feature belongs to. | | `FEATURE` | The name of the feature. | {: .reset-td-br-1 .reset-td-br-2 .reset-td-br-3 aria-label="Naming conventions" } Here's an example feature flag where `show` is the behavior, `animation_profile` is the product, and `driver` is the feature: ```plaintext show_animation_profile_driver ``` ### Planning ahead Always play it safe. When considering new features that may require an off switch, it's better to release new code with a feature flag and not need it than it is to realize a new app update is required. ### Be descriptive Add a description to your feature flag. While this is an optional field in Braze, it can help answer questions others may have when browsing available feature flags. - Contact details for who is responsible for the enablement and behavior of this flag - When this flag should be disable - Links to documentation or notes about the new feature this flag controls - Any dependencies or notes on how to use the feature ### Clean up old feature flags We're all guilty of leaving features on at 100% rollout for longer than necessary. To help keep your code (and Braze dashboard) clean, remove permanent feature flags from your code base after all users have upgraded and you no longer need the option to disable the feature. This helps reduce the complexity of your development environment, but also keeps your list of feature flags tidy. # 피처 플래그 실험 Source: /docs/ko/developer_guide/feature_flags/experiments/index.md # Feature flag experiments > Feature flag experiments let you A/B test changes to your applications to optimize conversion rates. Marketers can use feature flags to determine whether a new feature positively or negatively impacts conversion rates, or which set of feature flag properties is most optimal. ## Prerequisites Before you can track user data in the experiment, your app needs to record when a user interacts with a feature flag. This is called a feature flag impression. Make sure to log a feature flag impression whenever a user sees or could have seen the feature you're testing, even if they're in the control group. To learn more about logging feature flag impressions, see [Creating feature flags](https://www.braze.com/docs/ko/ko/developer_guide/platform_wide/feature_flags/create/#impressions). ```javascript const featureFlag = braze.getFeatureFlag("my-new-feature"); braze.logFeatureFlagImpression("my-new-feature"); if (featureFlag?.enabled) { return } else { return } ``` ```java FeatureFlag featureFlag = braze.getFeatureFlag("my-new-feature"); braze.logFeatureFlagImpression("my-new-feature"); if (featureFlag != null && featureFlag.getEnabled()) { return new NewFeature(); } else { return new ExistingFeature(); } ``` ```kotlin val featureFlag = braze.getFeatureFlag("my-new-feature") braze.logFeatureFlagImpression("my-new-feature") if (featureFlag?.enabled == true) { return NewFeature() } else { return ExistingFeature() } ``` ## Creating a feature flag experiment ### Step 1: Create an experiment 1. Go to **Messaging** > **Campaigns**, then select **+ Create Campaign**. 2. Select **Feature Flag Experiment**. 3. Give your campaign a clear and meaningful name. ### Step 2: Add experiment variants Next, create variations. For each variant, choose the feature flag you want to turn on or off, then review its assigned properties. To test the impact of your feature, use variants to split traffic into two or more groups. Name one group "My control group" and turn its feature flags off. ### Step 3: Overwrite properties (optional) You can choose to overwrite the default properties you initially set up for users who receive a specific campaign variant. To edit, add, or remove additional default properties, edit the feature flag itself from **Messaging** > **Feature Flags**. When a variant is disabled, the SDK will return an empty properties object for the given feature flag. ![The 'Experiment Variants' section with the 'link' variable key overwritten with '/sales'.](https://www.braze.com/docs/ko/ko/assets/img/feature_flags/feature_flag_experiment_override.png?6831378a3d27f8755821b4d118771eff){: style="max-width:80%"} ### Step 4: Choose users to target Use one of your segments or filters to choose your [target users](https://www.braze.com/docs/ko/ko/user_guide/engagement_tools/messaging_fundamentals/targeting_users/). For example, you can use the **Received Feature Flag Variant** filter to retarget users who have already received an A/B test. ![The 'Target' page in a feature flag experiment with 'Received Feature Flag Variant' highlighted in the filter group search bar.](https://www.braze.com/docs/ko/ko/assets/img/feature_flags/variant-filter-dropdown.png?f937119488b7aa7ef186d1e82b125a96){: style="max-width:70%"} **Note:** Segment membership is calculated when feature flags are refreshed for a given user. Changes are made available after your app refreshes feature flags, or when a new session is started. ### Step 5: Distribute variants Choose the percentage distribution for your experiment. As a best practice, you should not change the distribution after your experiment has been launched. ### Step 6: Assign conversions Braze lets you to track how often users perform specific actions, [conversion events](https://www.braze.com/docs/ko/ko/user_guide/engagement_tools/messaging_fundamentals/conversion_events/), after receiving a campaign. Specify up to a 30-day window during which a conversion will be counted if the user takes the specified action. ### Step 7: Review and launch After you’ve finished building the last of your experiment, review its details, then select **Launch Experiment**. ## Reviewing the results After your feature flag experiment is finished, you can review impression data for your experiment. Go to **Messaging** > **Campaigns** and select the campaign with your feature flag experiment. ### Campaign analytics **Campaign Analytics** offers a high-level overview of your experiment's performance, such as: - The total number of impressions - The number of unique impressions - The primary conversion rate - The total revenue generated by the message - The estimated audience You can also view the experiment's settings for delivery, audience, and conversion. ### Feature flag experiment performance **Feature Flags Experiments Performance** shows how well your message performed across various dimensions. The specific metrics you see will vary depending on your chosen messaging channel, and whether you're running a multivariate test. To see the feature flag values associated with each variant, select **Preview**. # 자주 묻는 질문 Source: /docs/ko/developer_guide/feature_flags/faq/index.md # Frequently asked questions > This article provides answers to some frequently asked questions about feature flags. ## Functionality and support ### What platforms are Braze feature flags supported on? {#platforms} Braze supports feature flags on iOS, Android, and Web platforms with the following SDK version requirements: Do you need support on other platforms? Email our team: [feature-flags-feedback@braze.com](mailto:feature-flags-feedback@braze.com). ### What is the level of effort involved when implementing a feature flag? {#level-of-effort} A feature flag can be created and integrated in a few minutes. Most of the effort involved will be related to your engineering team building the new feature you plan to roll out. But when it comes to adding a feature flag, it's as simple as an `IF`/`ELSE` statement in your app or website's code: ```javascript import { getFeatureFlag } from "@braze/web-sdk"; if (getFeatureFlag("new_shopping_cart").enabled) { // Show the new homepage your team has built } else { // Show the old homepage } ``` ```java if (braze.getFeatureFlag("new_shopping_cart").getEnabled()) { // Show the new homepage your team has built } else { // Show the old homepage } ``` ```kotlin if (braze.getFeatureFlag("new_shopping_cart")?.enabled == true) { // Show the new homepage your team has built } else { // Show the old homepage } ``` ### How can feature flags benefit Marketing teams? {#marketing-teams} Marketing teams can use feature flags to coordinate product announcements (such as product launch emails) when a feature is only enabled for a small percentage of users. For example, with Braze feature flags, you can roll out a new Customer Loyalty program to 10% of users in your app, and send an email, push, or other messaging to that same 10% of enabled users using the Canvas Feature Flag step. ### How can feature flags benefit Product teams? {#product-teams} Product teams can use feature flags to perform gradual rollouts or soft launches of new features in order to monitor key performance indicators and customer feedback before making it available to all users. Product teams can use [feature flag properties](https://www.braze.com/docs/ko/ko/developer_guide/platform_wide/feature_flags/create/#properties) to remotely populate content in an app, such as deep links, text, imagery, or other dynamic content. Using the Canvas Feature Flag step, Product teams can also run an A/B split test to measure how a new feature impacts conversion rates compared to users with the feature disabled. ### How can feature flags benefit engineering teams? {#engineering-teams} Engineering teams can use feature flags to reduce the risk inherent in launching new features and avoid rushing to deploy code fixes in the middle of the night. By releasing new code hidden behind a feature flag, your team can turn the feature on or off remotely from the Braze dashboard, bypassing the delay of pushing out new code or waiting for an app store update approval. ## Feature rollouts and targeting ### Can a feature flag be rolled out to only a select group of users? {#target-users} Yes, create a segment in Braze that targets specific users—by email address, `user_id`, or any other attribute on your user profiles. Then, deploy the feature flag for 100% of that segment. ### How does adjusting the rollout percentage affect users who were previously bucketed into the enabled group? {#random-buckets} Feature flag rollouts remain consistent for users across devices and sessions. - When a feature flag is rolled out to 10% of random users, that 10% will remain enabled and persist for the lifetime of that feature flag. - If you increase the rollout from 10% to 20%, the same 10% will remain enabled, plus a new, additional 10% of users will be added to the enabled group. - If you lower the rollout from 20% to 10%, only the original 10% of users will remain enabled. This strategy helps ensure that users are shown a consistent experience in your app and don't flip-flop back and forth across sessions. Of course, disabling a feature down to 0% will remove all users from the feature flag, which is helpful if you discover a bug or need to disable the feature altogether. ## Technical topics ### Can feature flags be used to control when the Braze SDK is initialized? {#initialization} No, the SDK must be initialized to download and synchronize feature flags for the current user. This means you can't use feature flags to limit which users are created or tracked in Braze. ### How frequently does the SDK refresh feature flags? {#refresh-frequency} Feature flags are refreshed at session start and when changing active users. Feature flags can also be manually refreshed using the SDK's [refresh method](https://www.braze.com/docs/ko/ko/developer_guide/platform_wide/feature_flags/create/#refreshing). Feature flag refreshes are rate limited to once every five minutes (subject to change). Keep in mind that good data practices recommend not refreshing feature flags too quickly (with potential rate limiting if done so), so it's best only to refresh before a user interacts with new features or periodically in the app if necessary. ### Are feature flags available while a user is offline? {#offline} Yes, after feature flags are refreshed, they are stored locally on the user's device and can be accessed while offline. ### What happens if feature flags are refreshed mid-session? {#listen-for-updates} Feature flags may be refreshed mid-session. There are scenarios where you may want to update your app if certain variables or your configuration should change. There are other scenarios where you may not want to update your app, to avoid a shocking change in how your UI is rendered. To control this, [listen for updates](https://www.braze.com/docs/ko/ko/developer_guide/platform_wide/feature_flags/create/#updates) to feature flags and determine whether to re-render your app based on which feature flags have changed. ### Why aren't users in my Global Control Group receiving feature flags experiments? You can't enable feature flags for users in your [Global Control Group](https://www.braze.com/docs/ko/ko/user_guide/engagement_tools/testing/global_control_group/). This means users in your Global Control Group also can't be part of Feature Flag experiments. ## Additional questions? Have questions or feedback? Email our team: [feature-flags-feedback@braze.com](mailto:feature-flags-feedback@braze.com). # Braze SDK용 분석 정보 Source: /docs/ko/developer_guide/analytics/index.md # 분석 {#analytics} > Braze SDK의 분석에 대해 알아보고 Braze가 수집하는 데이터, 커스텀 이벤트와 커스텀 속성의 차이점, 분석 관리를 위한 모범 사례를 더 잘 이해할 수 있습니다. **Tip:** Braze를 구현하는 동안 팀과 마케팅 목표에 대해 논의하여 추적할 데이터와 Braze로 추적하는 방법을 가장 잘 결정할 수 있도록 하세요. 예시는 이 가이드 끝에 있는 [택시/차량 공유 앱](#example-case) 사례 연구를 참조하세요. ## 자동으로 수집되는 데이터 {#automatically-collected-data} 특정 사용자 데이터(예: 처음 사용한 앱, 마지막으로 사용한 앱, 총 세션 수, 기기 OS 등)는 SDK에서 자동으로 수집됩니다. 통합 가이드에 따라 SDK를 구현하면 이 [기본 데이터 수집](https://www.braze.com/docs/ko/ko/user_guide/data/unification/user_data/sdk_data_collection/) 기능을 활용할 수 있습니다. 이 목록을 확인하면 사용자에 대한 동일한 정보를 두 번 이상 저장하지 않도록 하는 데 도움이 됩니다. 세션 시작 및 종료를 제외하고, 자동으로 추적되는 모든 기타 데이터는 데이터 포인트 사용량에 포함되지 않습니다. 특정 데이터 항목의 기본 수집을 차단하는 프로세스를 허용 목록에 추가하려면 [SDK 프라이머](https://www.braze.com/docs/ko/ko/developer_guide/getting_started/sdk_overview/) 문서를 참조하세요. ## 커스텀 이벤트 {#custom-events} 커스텀 이벤트는 사용자가 수행하는 동작으로, 애플리케이션과의 가치 있는 사용자 상호 작용을 추적하는 데 가장 적합합니다. 커스텀 이벤트를 기록하면 구성 가능한 지연 시간으로 후속 Campaign을 얼마든지 트리거할 수 있으며, 해당 이벤트의 최신성 및 빈도에 따라 다음과 같은 세분화 필터를 사용할 수 있습니다. | 세분화 옵션 | 드롭다운 필터 | 입력 옵션 | | ---------------------| --------------- | ------------- | | 커스텀 이벤트가 **X회 이상** 발생했는지 확인합니다 | **MORE THAN** | **숫자** | | 커스텀 이벤트가 **X회 미만으로** 발생했는지 확인합니다 | **LESS THAN** | **숫자** | | 커스텀 이벤트가 **정확히 X회** 발생했는지 확인합니다 | **EXACTLY** | **숫자** | | 커스텀 이벤트가 **X 날짜 이후에** 마지막으로 발생했는지 확인합니다 | **AFTER** | **시간** | | 커스텀 이벤트가 **X 날짜 이전에** 마지막으로 발생했는지 확인합니다 | **BEFORE** | **시간** | | 커스텀 이벤트가 마지막으로 발생한 지 **X일이 넘었는지** 확인합니다 | **MORE THAN** | **이전 일수**(양수) | | 커스텀 이벤트가 마지막으로 발생한 **날짜가 X일 미만인지** 확인합니다 | **LESS THAN** | **이전 일수**(양수) | | 커스텀 이벤트가 **X회(최대 = 50) 초과**하여 발생했는지 확인합니다 | **MORE THAN** | 지난 **Y일 동안(Y = 1,3,7,14,21,30)** | | 커스텀 이벤트가 **X회(최대 = 50) 미만으로** 발생했는지 확인합니다 | **LESS THAN** | 지난 **Y일 동안(Y = 1,3,7,14,21,30)** | | 커스텀 이벤트가 **정확히 X회(최대 = 50)만큼** 발생했는지 확인합니다 | **EXACTLY** | 지난 **Y일 동안(Y = 1,3,7,14,21,30)** | {: .reset-td-br-1 .reset-td-br-2 .reset-td-br-3 aria-label="Custom events" } Braze는 세분화를 위해 이러한 이벤트가 발생한 횟수와 각 사용자가 마지막으로 수행한 시간을 기록합니다. **커스텀 이벤트** 분석 페이지에서 각 커스텀 이벤트의 발생 빈도를 집계하여 볼 수 있을 뿐만 아니라 시간 경과에 따른 Segment별로 더 자세히 분석할 수 있습니다. Campaign이 마지막으로 전송된 시간을 나타내기 위해 Braze에서 시계열에 겹쳐 표시하는 회색 선을 보고 Campaign이 커스텀 이벤트 활동에 어떤 영향을 미쳤는지 확인하는 데 특히 유용합니다. ![신용 카드를 추가하고 30일 동안 검색한 사용자에 대한 통계를 보여주는 커스텀 이벤트 분석 그래프.](https://www.braze.com/docs/ko/ko/assets/img_archive/custom_event_analytics_example.png?345ada8684baf98a23ceb1a80341f24b "custom_event_analytics_example.png") **Note:** [커스텀 속성 증분](https://www.braze.com/docs/ko/ko/api/endpoints/messaging/)을 사용하면 커스텀 이벤트와 유사한 사용자 동작에 대한 카운터를 유지할 수 있습니다. 그러나 시계열에서 커스텀 속성 데이터는 볼 수 없습니다. 시계열로 분석할 필요가 없는 사용자 동작은 이 방법을 통해 기록해야 합니다. ### 커스텀 이벤트 저장 {#custom-event-storage} 모든 고객 프로필 데이터(커스텀 이벤트, 커스텀 속성, 커스텀 데이터)는 해당 프로필이 활성 상태인 동안 저장됩니다. ### 커스텀 이벤트 속성정보 {#custom-event-properties} 커스텀 이벤트 속성정보를 사용하면 Braze에서 커스텀 이벤트 및 구매에 대한 등록정보를 설정할 수 있습니다. 그런 다음, 이러한 등록정보를 사용하여 트리거 조건을 추가로 검증하고, 메시징의 개인화를 강화하며, 원시 데이터 내보내기를 통해 보다 정교한 분석을 생성할 수 있습니다. 등록정보 값은 문자열, 숫자, 부울 또는 시간 오브젝트일 수 있습니다. 그러나 등록정보 값은 배열 오브젝트가 될 수 없습니다. 예를 들어, 이커머스 애플리케이션에서 사용자가 장바구니를 이탈할 때 메시지를 보내려는 경우, 사용자 장바구니의 `cart_value` 커스텀 이벤트 속성정보를 추가하여 타겟 오디언스를 추가로 개선하고 Campaign 개인화를 강화할 수 있습니다. ![장바구니를 포기하고 장바구니 값을 100 이상 200 미만으로 남겨둔 사용자에게 Campaign을 보내는 커스텀 이벤트 예제입니다.](https://www.braze.com/docs/ko/ko/assets/img_archive/customEventProperties.png?03200b17e56f8f8ad0c6ab439de76832 "customEventProperties.png") 커스텀 이벤트 속성정보를 사용하여 메시징 템플릿 내에서 개인화할 수도 있습니다. 트리거 이벤트와 함께 [실행 기반 전달](https://www.braze.com/docs/ko/ko/user_guide/messaging/campaigns/schedule_your_campaign/triggered_delivery/)을 사용하는 모든 Campaign은 해당 이벤트의 커스텀 이벤트 속성정보를 사용하여 메시징 개인화를 수행할 수 있습니다. 게임 애플리케이션이 레벨을 완료한 사용자에게 메시지를 보내려는 경우, 사용자가 해당 레벨을 완료하는 데 걸린 시간을 등록정보로 추가하여 메시지를 더욱 개인화할 수 있습니다. 이 예에서는 [조건 로직](https://www.braze.com/docs/ko/ko/user_guide/messaging/design_and_edit/personalize/liquid/conditional_logic/)을 사용하여 세 개의 서로 다른 Segment에 대해 메시지를 개인화합니다. ``time_spent``라는 커스텀 이벤트 속성정보는 `` } ``를 호출하여 메시지에 포함할 수 있습니다. ```liquid Congratulations on beating that level so fast! Check out our online portal where you can play against top players from around the world! Don't forget to visit the town store between levels to upgrade your tools. Talk to villagers for essential tips on how to beat levels! ``` 커스텀 이벤트 속성정보는 메시징을 개인화하거나 세분화된 실행 기반 전달 Campaign을 구축하는 데 도움이 되도록 설계되었습니다. 이벤트 속성정보의 최신성 및 빈도를 기준으로 Segment를 생성하려면 고객 성공 매니저 또는 고객지원팀에 문의하세요. ## 커스텀 속성 {#custom-attributes} 커스텀 속성은 표준 속성보다 훨씬 더 구체적으로 사용자를 타겟팅할 수 있는 매우 유연한 도구입니다. 커스텀 속성은 사용자에 대한 브랜드별 정보를 저장하는 데 유용합니다. 커스텀 속성에 대한 시계열 정보는 저장되지 않으므로 이전 커스텀 이벤트 예제처럼 이에 기반한 그래프는 얻을 수 없다는 점을 유의하세요. ### 커스텀 속성 저장 {#custom-attribute-storage} 모든 고객 프로필 데이터(커스텀 이벤트, 커스텀 속성, 커스텀 데이터)는 해당 프로필이 활성 상태인 동안 저장됩니다. ### 커스텀 속성 데이터 유형 {#custom-attribute-data-types} 커스텀 속성으로 저장할 수 있는 데이터 유형은 다음과 같습니다: #### 문자열(영숫자) {#strings-alphanumeric-characters} 문자열 속성은 즐겨 찾는 브랜드, 전화번호 또는 애플리케이션 내에서 마지막으로 검색한 문자열과 같은 사용자 입력을 저장하는 데 유용합니다. 문자열 속성은 커스텀 데이터에 대한 [길이 제약 조건](#length-constraints)(479바이트; 단일 바이트 문자 약 479자 또는 일본어와 같은 다중 바이트 스크립트의 경우 약 160자)을 따릅니다. 다음 표에서는 문자열 속성에 사용할 수 있는 세분화 옵션을 설명합니다. | 세분화 옵션 | 드롭다운 필터 | 입력 옵션 | | ---------------------| --------------- | ------------- | | 문자열 속성이 입력한 문자열과 **정확히 일치하는지** 확인합니다 | **EQUALS** | **문자열** | | 문자열 속성이 입력된 문자열 **또는** 정규표현식과 **부분 일치**하는지 확인합니다 | **MATCHES REGEX** | **문자열** **또는** **정규표현식** | | 문자열 속성이 입력된 문자열 **또는** 정규표현식과 **부분 일치하지 않는지** 확인합니다 | **DOES NOT MATCH REGEX** | **문자열** **또는** **정규표현식** | | 문자열 속성이 입력한 문자열과 **일치하지 않는지** 확인합니다 | **DOES NOT EQUAL** | **문자열** | | 사용자 프로필에 문자열 속성이 **존재하는지** 확인합니다 | **IS BLANK** | **N/A** | | 사용자 프로필에 문자열 속성이 **존재하지 않는지** 확인합니다 | **IS NOT BLANK** | **N/A** | {: .reset-td-br-1 .reset-td-br-2 .reset-td-br-3 aria-label="Strings (alphanumeric characters)" } **Important:** **DOES NOT MATCH REGEX** 필터를 사용하여 세분화할 때는 해당 고객 프로필에 값이 할당된 커스텀 속성이 이미 존재해야 합니다. Braze는 사용자를 적절하게 타겟팅하기 위해 "OR" 로직을 사용하여 커스텀 속성이 비어 있는지 확인할 것을 권장합니다. **Tip:** 정규표현식 필터를 사용하는 방법에 대한 자세한 내용은 [Perl 호환 정규표현식(PCRE)](http://www.regextester.com/pregsyntax.html) 설명서를 참조하세요.
정규식에 대한 추가 리소스: - [Braze를 사용한 정규식](https://www.braze.com/docs/ko/ko/user_guide/audience/segments/regex/) - [정규식 디버거 및 테스터](https://regex101.com/) - [정규식 튜토리얼](https://medium.com/factory-mind/regex-tutorial-a-simple-cheatsheet-by-examples-649dc1c3f285) #### 배열 {#arrays} 배열 속성은 사용자에 대한 관련 정보 목록을 저장하는 데 유용합니다. 예를 들어, 사용자가 마지막으로 시청한 100개의 콘텐츠를 배열에 저장하면 특정 관심사를 기반으로 세분화할 수 있습니다. 커스텀 속성 배열은 1차원 집합이며, 다차원 배열은 지원되지 않습니다. **커스텀 속성 배열에 요소를 추가하면 해당 요소가 이미 존재하지 않는 한 배열의 끝에 추가되며, 이미 존재하는 경우 현재 위치에서 배열의 끝으로 이동합니다.** 예를 들어 `['hotdog','hotdog','hotdog','pizza']` 배열을 가져온 경우 고유 값만 지원되기 때문에 배열 속성에 `['hotdog', 'pizza']`로 표시됩니다. 배열에 최대 요소 개수가 포함된 경우 첫 번째 요소는 삭제되고 새 요소가 끝에 추가됩니다. 다음은 웹 SDK의 배열 동작을 보여주는 예제 코드입니다: ```js var abUser = appboy.getUser(); // initialize array for this user, assuming max length of favorite_foods is set to 4. abUser.setCustomUserAttribute('favorite_foods', ['pizza', 'wings', 'pasta']); // => ['pizza', 'wings', 'pasta'] abUser.addToCustomAttributeArray('favorite_foods', 'fries'); // => ['pizza', 'wings', 'pasta', 'fries'] abUser.addToCustomAttributeArray('favorite_foods', 'pizza'); // => ['wings', 'pasta', 'fries', 'pizza'] abUser.addToCustomAttributeArray('favorite_foods', 'ice cream'); // => ['pasta', 'fries', 'pizza', 'ice cream'] ``` 배열의 기본 및 최대 요소 개수는 500개입니다. Braze 대시보드의 **데이터 설정** > **커스텀 속성**에서 최대 요소 개수를 업데이트할 수 있습니다. 최대 요소 개수를 초과하는 배열은 최대 요소 개수만큼 잘립니다. 다음 표에서는 배열 속성에 사용할 수 있는 세분화 옵션을 설명합니다. | 세분화 옵션 | 드롭다운 필터 | 입력 옵션 | | ---------------------| --------------- | ------------- | | 배열 속성에 입력한 값과 **정확히 일치하는 값이 포함**되었는지 확인합니다 | **INCLUDES VALUE** | **문자열** | | 배열 속성에 입력한 값과 **정확히 일치하는 값이 포함되지 않았는지** 확인합니다 | **DOESN'T INCLUDE VALUE** | **문자열** | | 배열 속성에 입력된 값 **또는** 정규표현식과 **부분 일치하는 값이 포함**되었는지 확인합니다 | **MATCHES REGEX** | **문자열** **또는** **정규표현식** | | 배열 속성에 **값이 있는지** 확인합니다 | **HAS A VALUE** | **N/A** | | 배열 속성이 **비어 있는지** 확인합니다 | **IS EMPTY** | **N/A** | {: .reset-td-br-1 .reset-td-br-2 .reset-td-br-3 aria-label="Arrays" } **Note:** [Perl 호환 정규표현식(PCRE)](http://www.regextester.com/pregsyntax.html)을 사용합니다. #### 날짜 {#dates} 시간 속성은 특정 동작이 마지막으로 수행된 시간을 저장하는 데 유용하므로 사용자에게 콘텐츠별 재참여 메시지를 제공할 수 있습니다. **Note:** 커스텀 이벤트 또는 구매 이벤트가 발생한 마지막 날짜는 자동으로 기록되며, 커스텀 시간 속성을 통해 중복으로 기록해서는 안 됩니다. 상대 날짜(예: 1일 이상 이전, 2일 미만 이전)를 사용하는 날짜 필터는 하루를 24시간으로 계산합니다. 이 필터를 사용하여 실행하는 모든 Campaign에는 24시간 단위로 모든 사용자가 포함됩니다. 예를 들어, 1일 이상 이전에 마지막으로 사용한 앱은 Campaign이 실행된 정확한 시간부터 "24시간 넘게 앱을 마지막으로 사용한" 모든 사용자를 캡처합니다. 더 긴 날짜 범위로 설정된 Campaign도 마찬가지입니다. 활성화 후 5일은 이전 120시간을 의미합니다. 다음 표에서는 시간 속성에 사용할 수 있는 세분화 옵션을 설명합니다. | 세분화 옵션 | 드롭다운 필터 | 입력 옵션 | | ---------------------| --------------- | ------------- | | 시간 속성이 **선택한 날짜** **이전**인지 확인합니다 | **BEFORE** | **캘린더 날짜 선택기** | | 시간 속성이 **선택한 날짜** **이후**인지 확인합니다 | **AFTER** | **캘린더 날짜 선택기** | | 시간 속성이 **X일을 초과**한 **이전** 시점인지 확인합니다 | **MORE THAN** | **이전 일수** | | 시간 속성이 **X일 미만**의 **이전** 시점인지 확인합니다 | **LESS THAN** | **이전 일수** | | 시간 속성이 **향후 X일을 초과**한 **향후** 시점인지 확인합니다 | **IN MORE THAN** | **향후 일수** | | 시간 속성이 **향후 X일 미만**의 **향후** 시점인지 확인합니다 | **IN LESS THAN** | **향후 일수** | | 사용자 프로필에 시간 속성이 **존재하는지** 확인합니다 | **BLANK** | **N/A** | | 사용자 프로필에 시간 속성이 **존재하지 않는지** 확인합니다 | **IS NOT BLANK** | **N/A** | {: .reset-td-br-1 .reset-td-br-2 .reset-td-br-3 aria-label="Dates" } #### 숫자 {#integers} 숫자 속성은 다양한 사용 사례가 있습니다. 증분 숫자 커스텀 속성은 특정 동작이나 이벤트가 발생한 횟수를 저장하는 데 유용합니다. 표준 숫자는 신발 사이즈, 허리 사이즈 또는 사용자가 특정 제품 기능이나 카테고리를 조회한 횟수를 기록하는 등 다양한 용도로 활용됩니다. **Note:** 지출 금액은 이 방법으로 기록해서는 안 됩니다. 대신 [구매 메서드](https://www.braze.com/docs/ko/ko/developer_guide/platform_wide/analytics_overview/#purchase-events--revenue-tracking)를 통해 기록해야 합니다. 다음 표에서는 숫자 속성에 사용할 수 있는 세분화 옵션을 설명합니다. | 세분화 옵션 | 드롭다운 필터 | 입력 옵션 | | ---------------------| --------------- | ------------- | | 숫자 속성이 **숫자**보다 **큰지** 확인합니다 | **MORE THAN** | **숫자** | | 숫자 속성이 **숫자**보다 **작은지** 확인합니다 | **LESS THAN** | **숫자** | | 숫자 속성이 **정확히** **숫자**인지 확인합니다 | **EXACTLY** | **숫자** | | 숫자 속성이 **숫자**와 **같지 않은지** 확인합니다 | **DOES NOT EQUAL** | **숫자** | | 사용자 프로필에 숫자 속성이 **존재하는지** 확인합니다 | **EXISTS** | **N/A** | | 사용자 프로필에 숫자 속성이 **존재하지 않는지** 확인합니다 | **DOES NOT EXIST** | **N/A** | {: .reset-td-br-1 .reset-td-br-2 .reset-td-br-3 aria-label="Numbers #integers" } #### 부울(참/거짓) {#booleans-truefalse} 부울 속성은 구독 상태 및 사용자에 대한 기타 간단한 이진 데이터를 저장하는 데 유용합니다. 제공되는 입력 옵션을 사용하면 해당 속성에 대한 기록이 아직 없는 사용자 외에도 변수를 부울로 명시적으로 설정한 사용자를 찾을 수 있습니다. 다음 표에서는 부울 속성에 사용할 수 있는 세분화 옵션을 설명합니다. | 세분화 옵션 | 드롭다운 필터 | 입력 옵션 | | ---------------------| --------------- | ------------- | | 부울 값이 다음과 **같은지** 확인합니다 | **IS** | **TRUE**, **FALSE**, **TRUE OR NOT SET** 또는 **FALSE OR NOT SET** | | 사용자 프로필에 부울 값이 **존재하는지** 확인합니다 | **EXISTS** | **N/A** | | 사용자 프로필에 부울 값이 **존재하지 않는지** 확인합니다 | **DOES NOT EXIST** | **N/A** | {: .reset-td-br-1 .reset-td-br-2 .reset-td-br-3 aria-label="Booleans (true/false)" } ## 구매 이벤트 / 매출 추적 {#purchase-events-revenue-tracking} 구매 메서드를 사용하여 인앱 구매를 기록하면 각 개별 고객 프로필에 대한 생애주기 가치(LTV)가 설정됩니다. 이 데이터는 매출 페이지에서 시계열 그래프로 확인할 수 있습니다. 다음 표에서는 구매 이벤트에 사용할 수 있는 세분화 옵션을 설명합니다. | 세분화 옵션 | 드롭다운 필터 | 입력 옵션 | | ---------------------| --------------- | ------------- | | 총 지출 금액이 **숫자**보다 **큰지** 확인합니다 | **GREATER THAN** | **숫자** | | 총 지출 금액이 **숫자**보다 **적은지** 확인합니다 | **LESS THAN** | **숫자** | | 총 지출 금액이 **정확히** **숫자**인지 확인합니다 | **EXACTLY** | **숫자** | | **X 날짜 이후에** 마지막으로 구매가 발생했는지 확인합니다 | **AFTER** | **시간** | | **X 날짜 이전에** 마지막으로 구매가 발생했는지 확인합니다 | **BEFORE** | **시간** | | 마지막 구매가 **X일 이상 전에** 발생했는지 확인합니다 | **MORE THAN** | **시간** | | 마지막 구매가 **X일 미만 전에** 발생했는지 확인합니다 | **LESS THAN** | **시간** | | 구매가 **X(최대 = 50)회 이상** 발생했는지 확인합니다 | **MORE THAN** | 지난 **Y일 동안(Y = 1,3,7,14,21,30)** | | 구매가 **X(최대 = 50)회 미만으로** 발생했는지 확인합니다 | **LESS THAN** | 지난 **Y일 동안(Y = 1,3,7,14,21,30)** | | 구매가 **정확히 X(최대 = 50)회** 발생했는지 확인합니다 | **EXACTLY** | 지난 **Y일 동안(Y = 1,3,7,14,21,30)** | {: .reset-td-br-1 .reset-td-br-2 .reset-td-br-3 aria-label="Purchase events / revenue tracking" } **Note:** 특정 구매가 발생한 횟수를 기준으로 세분화하려면 해당 구매를 [증분 커스텀 속성](#integers)으로 개별적으로 기록해야 합니다. ## 택시/차량 공유 앱 활용 사례 {#example-case} 이 예제에서는 어떤 사용자 데이터를 수집할지 결정하려는 차량 공유 앱을 생각해 보겠습니다. 다음 질문과 브레인스토밍 프로세스는 마케팅팀과 개발팀이 따라야 할 훌륭한 모델입니다. 이 연습이 끝나면 두 팀 모두 목표를 달성하기 위해 수집해야 할 커스텀 이벤트와 속성을 확실히 이해할 수 있습니다. **사례 질문 #1: 목표는 무엇인가요?** 목표는 간단합니다. 사용자가 앱을 통해 택시를 호출하는 것입니다. **사례 질문 #2: 앱 설치부터 목표까지의 중간 단계는 무엇인가요?** 1. 사용자는 등록 절차를 시작하고 개인 정보를 입력해야 합니다. 2. 사용자는 SMS를 통해 받은 코드를 앱에 입력하여 등록 절차를 완료하고 인증해야 합니다. 3. 택시를 호출해야 합니다. 4. 택시를 호출하려면 검색 시 택시를 이용할 수 있어야 합니다. 이러한 동작은 다음과 같은 커스텀 이벤트로 태그를 지정할 수 있습니다: - 등록 시작 - 등록 완료 - 성공적인 택시 호출 - 택시 호출 실패 이벤트를 구현한 후 이제 다음 Campaign을 실행할 수 있습니다: 1. 등록을 시작했지만 특정 시간 내에 등록 완료 이벤트가 트리거되지 않은 사용자에게 메시지를 보냅니다. 2. 등록을 완료한 사용자에게 축하 메시지를 보냅니다. 3. 택시 호출에 실패한 후 일정 시간 내에 성공적인 택시 호출이 없는 사용자에게 사과와 프로모션 크레딧을 보냅니다. 4. 성공적인 택시 호출 횟수가 많은 우수 고객에게 프로모션을 보내 로열티에 대한 감사의 마음을 전합니다. 이외에도 다양한 활용이 가능합니다! **사례 질문 #3: 메시징에 도움이 될 사용자에 대한 다른 정보로 무엇이 있나요?** - 프로모션 크레딧이 있는지 여부 - 드라이버에게 평균적으로 어떤 평점을 주는지 - 사용자를 위한 고유한 프로모션 코드가 있는지 그런 다음 이러한 특성에 다음과 같은 커스텀 속성으로 태그를 지정할 수 있습니다: - 프로모션 크레딧 잔액(십진수 유형) - 평균 운전자 평점(숫자 유형) - 고유 프로모션 코드(문자열 유형) 이러한 속성을 추가하면 사용자에게 다음과 같은 Campaign을 보낼 수 있습니다: 1. 7일 동안 로그인하지 않았지만 프로모션 크레딧이 있는 사용자에게 크레딧이 존재하며 앱으로 돌아와서 사용해야 한다는 사실을 알려주세요! 2. 낮은 운전자 평점을 준 사용자에게 메시지를 보내 라이딩이 만족스럽지 않은 이유에 대한 직접적인 고객 피드백을 받습니다. 3. [메시지 템플릿 및 개인화 기능](https://www.braze.com/docs/ko/ko/user_guide/messaging/design_and_edit/personalize/)을 사용하여 고유 프로모션 코드 속성을 사용자 대상 메시지에 포함할 수 있습니다. ## 모범 사례 {#best-practices} ### 일반적인 모범 사례 {#general-best-practices} #### 이벤트 속성정보 사용 {#use-event-properties} - 커스텀 이벤트 이름은 사용자가 수행하는 동작을 설명하는 내용으로 지정합니다. - 이벤트에 대한 중요한 데이터를 표현하기 위해 커스텀 이벤트 속성정보를 충분히 활용하세요. - 예를 들어, 50개의 서로 다른 영화를 각각 시청할 때마다 별도의 커스텀 이벤트를 캡처하는 것보다 단순히 영화 시청을 이벤트로 캡처하고 영화 이름이 포함된 이벤트 속성정보를 갖는 것이 더 효과적입니다. ### 개발 모범 사례 {#development-best-practices} #### 모든 사용자에 대한 사용자 ID 설정 {#set-user-ids-for-every-user} 각 사용자에 대해 사용자 ID를 설정해야 합니다. 사용자가 앱을 열었을 때 변경되지 않고 액세스할 수 있어야 합니다. 이 식별자를 제공하는 것을 **강력히 권장합니다**. 그러면 다음과 같은 기능을 수행할 수 있습니다: - 여러 기기와 플랫폼에서 사용자를 추적하여 행동 및 인구 통계 데이터의 품질을 개선합니다. - [사용자 데이터 API](https://www.braze.com/docs/ko/ko/api/endpoints/user_data/)를 사용하여 사용자에 대한 데이터를 가져옵니다. - 일반 메시지와 트랜잭션 메시지 모두에 대한 [메시징 API](https://www.braze.com/docs/ko/ko/api/endpoints/messaging/)로 특정 사용자를 타겟팅합니다. 사용자 ID는 512자 미만이어야 하며, 쉽게 알아낼 수 없는 비공개 항목이어야 합니다(예: 일반 이메일 주소나 사용자 이름이 아님). 이러한 식별자를 사용할 수 없는 경우, Braze는 사용자에게 고유 식별자를 할당하지만 사용자 ID에 나열된 기능은 사용할 수 없습니다. 개인과 연결된 고유 식별자가 없는 사용자에 대해서는 사용자 ID를 설정하지 않아야 합니다. 기기 식별자를 전달해도 Braze에서 기본적으로 제공하는 자동 익명 사용자 추적 기능과 비교했을 때 아무런 이점이 없습니다. 다음은 적합한 사용자 ID와 부적합한 사용자 ID의 몇 가지 예입니다. 다음은 사용자 ID로 적합한 옵션입니다: - 해시된 이메일 주소 또는 고유 사용자 이름 - 고유 데이터베이스 식별자 사용자 ID로 사용해서는 안 됩니다: - 기기 ID - 난수 또는 세션 ID - 고유하지 않은 모든 ID - 이메일 주소 - 다른 타사 공급업체의 사용자 ID #### 커스텀 이벤트 및 속성에 읽기 쉬운 이름 부여 {#give-custom-events-and-attributes-readable-names} 마케터로서 Braze 도입 1~2년 후에 사용을 시작한다고 상상해 보세요. 추가적인 맥락 없이 "usr_no_acct"와 같은 이름들로 가득 찬 드롭다운 목록을 읽는 것은 부담스러울 수 있습니다. 이벤트와 속성에 식별 가능하고 읽기 쉬운 이름을 부여하면 플랫폼의 모든 사용자가 더 쉽게 작업할 수 있습니다. 다음 모범 사례를 고려하세요: - 숫자 문자로 커스텀 이벤트를 시작하지 마세요. 드롭다운 목록은 알파벳순으로 정렬되며 숫자로 시작하면 원하는 필터로 세분화하기가 더 어려워집니다. - 가능한 한 모호한 약어나 전문 용어를 사용하지 마세요. - 예: 코드 내에서 사용자의 국가에 대한 변수 이름으로 `usr_ctry`를 사용해도 괜찮지만, 대시보드를 사용하는 마케터가 명확하게 이해할 수 있도록 커스텀 속성을 `user_country`와 같은 이름으로 Braze에 전송해야 합니다. #### 속성이 변경될 때만 로그 기록 {#only-log-attributes-when-they-change} 전달된 속성에 이전에 저장된 값과 동일한 값이 포함되어 있더라도 Braze에 전달된 모든 속성을 데이터 포인트로 계산합니다. 데이터가 변경될 때만 로깅하면 데이터 포인트의 중복 사용을 방지하고 불필요한 API 호출을 피해 보다 원활한 환경을 지원할 수 있습니다. #### 이벤트 이름을 프로그래밍 방식으로 생성하지 마세요 {#avoid-programmatically-generating-event-names} 이벤트 이름을 계속 새로 만들면 사용자를 의미 있게 세분화하는 것이 불가능해집니다. 일반적으로 "강남 스타일 시청" 또는 "기사 읽기: 미드타운 맨해튼의 점심 명소 10위"와 같이 매우 구체적인 이벤트 대신 "동영상 시청" 또는 "기사 읽기"와 같은 일반적인 이벤트를 캡처하는 것이 좋습니다. 이벤트에 대한 특정 데이터는 이벤트 이름의 일부가 아닌 이벤트 속성정보로 포함되어야 합니다. ### 기술적 한계 및 제약 {#technical-limitations-and-constraints} 커스텀 이벤트를 구현할 때는 다음과 같은 한계와 제약 조건에 유의하세요: #### 길이 제약 조건 {#length-constraints} Braze는 커스텀 이벤트 이름, 커스텀 속성 이름(키), 커스텀 이벤트 문자열 값에 대해 바이트 단위의 길이 제한(479바이트)을 적용합니다. 이 한도를 초과하는 값은 잘립니다. 문자로 표현할 경우, 이는 약 479개의 단일 바이트 문자(예: ASCII)에 해당하거나, 일본어와 같은 다중 바이트 스크립트의 경우 약 160자(UTF-8에서 문자당 약 3바이트 가정)에 해당합니다. 가능하다면 앱의 네트워크 및 배터리 성능 향상을 위해 이름과 값을 최대한 짧게 유지하세요. 가능하면 50자 이내로 제한하세요. #### 콘텐츠 제약 조건 {#content-constraints} 다음 콘텐츠는 속성 및 이벤트에서 프로그래밍 방식으로 트리밍됩니다. 다음을 사용하지 않도록 주의하세요: - 선행 및 후행 공백 - 줄바꿈 - 전화번호 내 숫자가 아닌 모든 문자 - 예: "(732) 178-1038"은 "7321781038"로 축약됩니다 - 공백이 아닌 문자는 공백으로 변환해야 합니다 - 커스텀 이벤트의 접두사로 $를 사용해서는 안 됩니다 - 잘못된 UTF-8 인코딩 값 - "My \x80 Field"는 "My Field"로 축약됩니다 #### 예약 키 {#reserved-keys} 다음 키는 예약되어 있으며 커스텀 이벤트 속성정보로 사용할 수 없습니다: - `time` - `product_id` - `quantity` - `event_name` - `price` - `currency` #### 값 정의 {#value-definitions} - 정수 값은 64비트입니다 - 소수점은 기본적으로 소수점 이하 15자리입니다 ### 일반 이름 필드 구문 분석 {#parsing-a-generic-name-field} 사용자에 대해 하나의 일반 이름 필드만 존재하는 경우(예: 'JohnDoe'), 이 전체 제목을 사용자의 이름 속성에 할당할 수 있습니다. 공백을 사용하여 사용자의 이름과 성을 모두 구문 분석할 수도 있지만, 후자의 방법은 일부 사용자의 이름이 잘못 지정될 수 있는 잠재적 위험이 있습니다. # Braze SDK를 통해 사용자 ID 설정 Source: /docs/ko/developer_guide/analytics/setting_user_ids/index.md # 사용자 ID 설정 {#set-user-ids} > Braze SDK를 통해 사용자 ID를 설정하는 방법을 알아보세요. 이는 여러 기기와 플랫폼에서 사용자를 추적하고, [사용자 데이터 API](https://www.braze.com/docs/ko/ko/developer_guide/rest_api/user_data/#user-data)를 통해 데이터를 가져오고, [메시징 API](https://www.braze.com/docs/ko/ko/api/endpoints/messaging/)를 통해 타겟팅된 메시지를 보낼 수 있는 고유 식별자입니다. 사용자에게 고유 ID를 할당하지 않으면 Braze에서 익명 ID를 대신 할당하지만, 할당할 때까지는 이러한 기능을 사용할 수 없습니다. **Note:** 목록에 없는 래퍼 SDK의 경우 관련 네이티브 Android 또는 Swift 메서드를 대신 사용하세요. ## 익명 사용자 정보 {#about-anonymous-users} After you [integrate the Braze SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/), users who launch your app for the first time will be considered "anonymous" until you call the `changeUser` method and assign them an `external_id`. Once assigned, you can't make them anonymous again. However, if they uninstall and reinstall your app, they will become anonymous again until `changeUser` is called. If a previously-identified user starts a session on a new device, all of their anonymous activity will automatically sync to their existing profile after you call `changeUser` on that device using their `external_id`. This includes any attributes, events, or history collected during the session on the new device. ### 익명 사용자 추적 방지 {#preventing-anonymous-user-tracking} 사용자가 식별되기 전에 데이터를 수집하지 않아야 하는 사용 사례의 경우, 사용자가 로그인하고 `external_id`를 사용할 수 있을 때까지 Braze SDK 초기화를 지연할 수 있습니다. 코드에서 사용자가 로그인하면 `true`로 전환되는 플래그를 설정하고, 해당 플래그가 설정된 경우에만 SDK를 초기화하세요. **Warning:** 사용자가 앱을 **처음 다운로드할 때**(`external_id`가 설정되기 전)에만 초기화를 지연하세요. 사용자가 로그아웃하거나 새 세션을 시작할 때마다 SDK 초기화를 방지하면 인앱 메시지 및 콘텐츠 카드 자산의 프리페칭에 간섭이 발생하여 해당 Campaign에 전달 가능성 오류가 발생할 수 있습니다. ## 사용자 ID 설정 {#setting-a-user-id} 사용자 ID를 설정하려면 사용자가 처음 로그인한 후 `changeUser()` 메서드를 호출합니다. ID는 고유해야 하며 [명명 모범 사례](#naming-best-practices)를 따라야 합니다. 고유 식별자를 해싱하는 경우 해싱 함수의 입력을 정규화해야 합니다. 예를 들어 이메일 주소를 해시할 때는 앞뒤 공백을 모두 제거하고 현지화를 고려하세요. 표준 웹 SDK 구현의 경우 다음 메서드를 사용할 수 있습니다: ```javascript braze.changeUser(YOUR_USER_ID_STRING); ``` 대신 Google Tag Manager를 사용하려면 **Change User** 태그 유형을 사용하여 [`changeUser` 메서드](https://js.appboycdn.com/web-sdk/latest/doc/modules/braze.html#changeuser)를 호출할 수 있습니다. 사용자가 로그인하거나 고유한 `external_id` 식별자로 식별될 때마다 사용합니다. 일반적으로 웹사이트에서 전송한 데이터 레이어 변수를 사용하여 채워지는 **External User ID** 필드에 현재 사용자의 고유 ID를 입력해야 합니다. ![Braze 액션 태그 구성 설정을 보여주는 대화상자. 포함된 설정은 "tag type" 및 "external user ID"입니다.](https://www.braze.com/docs/ko/ko/assets/img/web-gtm/gtm-change-user.png?a4edbf312c5ba1fa6d32ecdd559361b0) ```java Braze.getInstance(context).changeUser(YOUR_USER_ID_STRING); ``` ```kotlin Braze.getInstance(context).changeUser(YOUR_USER_ID_STRING) ``` ```swift AppDelegate.braze?.changeUser(userId: "YOUR_USER_ID") ``` ```objc [AppDelegate.braze changeUser:@"YOUR_USER_ID_STRING"]; ``` ```javascript BrazePlugin.changeUser("YOUR_USER_ID"); ``` ```brightscript m.Braze.setUserId(YOUR_USER_ID_STRING) ``` ```csharp AppboyBinding.ChangeUser("YOUR_USER_ID_STRING"); ``` ```javascript Braze.changeUser("YOUR_USER_ID_STRING"); ``` ### `changeUser()` 작동 방식 {#how-changeuser-works} `changeUser()`를 호출하면 다음과 같은 동작이 적용됩니다: - 이미 설정된 것과 **동일한** 사용자 ID로 `changeUser()`를 호출하면 세션 수에 영향을 미치지 않습니다. - **다른** 사용자 ID로 `changeUser()`를 호출하면 현재 세션이 자동으로 종료되고 새 세션이 시작됩니다. - 익명 사용자가 **새로운** 사용자 ID(Braze에 아직 존재하지 않는 ID)로 `changeUser()`를 호출하면 익명 프로필의 데이터가 새로 식별된 프로필에 병합됩니다. - 익명 사용자가 **기존** 사용자 ID로 `changeUser()`를 호출하면 익명 프로필의 데이터가 식별된 프로필에 병합되지 않습니다. **Note:** `changeUser()`를 호출하면 현재 사용자의 세션을 종료하는 과정에서 데이터 플러시가 발생합니다. SDK는 새 사용자로 전환하기 전에 이전 사용자의 보류 중인 데이터를 자동으로 플러시하므로 `changeUser()`를 호출하기 전에 수동으로 데이터 플러시를 요청할 필요가 없습니다. **Warning:** 단일 공유 사용자 ID(예: 정적 기본값 외부 ID)를 할당하거나 사용자가 로그아웃할 때 `changeUser()`를 호출하지 마세요. 이렇게 하면 공유 기기에서 이전에 로그인한 사용자를 다시 참여시킬 수 없으며, 모든 데이터가 단일 사용자 ID에 기록되어 다른 기능이 예상대로 작동하지 않을 수 있습니다. 대신 모든 사용자 ID를 개별적으로 추적하고 앱의 로그아웃 프로세스에서 이전에 로그인한 사용자로 다시 전환할 수 있도록 하세요. 새 세션이 시작되면 Braze는 새로 활성화된 프로필의 데이터를 자동으로 새로고침합니다. ## 사용자 별칭 {#user-aliases} ### 작동 방식 {#how-they-work} Although anonymous users don’t have `external_ids`, you can assign them a [user alias](https://www.braze.com/docs/ko/ko/user_guide/data/user_data_collection/user_profile_lifecycle/#user-aliases) instead. You should assign a user alias when you want to add other identifiers to the user but don't know what their `external_id` is (for example, they aren't logged in). With user aliases, you also can: - Use the Braze API to log events and attributes associated with anonymous users - Use the [External User ID is blank](https://www.braze.com/docs/ko/ko/user_guide/engagement_tools/segments/segmentation_filters#external-user-id) segmentation filter to target anonymous users in your messaging ### 사용자 별칭 설정 {#setting-a-user-alias} 사용자 별칭은 이름과 레이블의 두 부분으로 구성됩니다. 이름은 식별자 자체를 가리키고, 레이블은 식별자가 속한 유형을 가리킵니다. 예를 들어 타사 고객지원 플랫폼에 외부 ID `987654`를 가진 사용자가 있는 경우, Braze에서 이름 `987654`와 레이블 `support_id`로 별칭을 할당하면 여러 플랫폼에서 해당 사용자를 추적할 수 있습니다. ```javascript braze.getUser().addAlias(ALIAS_NAME, ALIAS_LABEL); ``` ```java Braze.getInstance(context).getCurrentUser().addAlias(ALIAS_NAME, ALIAS_LABEL); ``` ```kotlin Braze.getInstance(context).currentUser?.addAlias(ALIAS_NAME, ALIAS_LABEL) ``` ```swift Appboy.sharedInstance()?.user.addAlias(ALIAS_NAME, ALIAS_LABEL) ``` ```objc [[Appboy sharedInstance].user addAlias:ALIAS_NAME withLabel:ALIAS_LABEL]; ``` ```json { "alias_name" : (required, string), "alias_label" : (required, string) } ``` ```javascript Braze.addAlias("ALIAS_NAME", "ALIAS_LABEL"); ``` ## ID 명명 모범 사례 {#naming-best-practices} 무작위로 잘 분산된 128비트 문자열인 [UUID(범용 고유 식별자)](https://en.wikipedia.org/wiki/Universally_unique_identifier) 표준을 사용하여 사용자 ID를 생성하는 것이 좋습니다. 또는 기존 고유 식별자(예: 이름 또는 이메일 주소)를 해시하여 사용자 ID를 대신 생성할 수도 있습니다. 이 경우 사용자 가장을 방지할 수 있도록 [SDK 인증](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/authentication/)을 구현해야 합니다. **Warning:** 사용자 ID에 추측 가능한 값이나 증가하는 숫자를 사용하지 마세요. 이로 인해 조직이 악의적인 공격이나 데이터 유출에 노출될 수 있습니다. 추가 보안을 위해 [SDK 인증](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/authentication/)을 사용하세요. 처음부터 사용자 ID의 이름을 올바르게 지정하는 것이 중요하지만, 나중에 언제든지 [`/users/external_ids/rename`](https://www.braze.com/docs/ko/ko/api/endpoints/user_data/external_id_migration/) 엔드포인트를 사용하여 이름을 변경할 수 있습니다. | 권장되지 않는 ID 유형 | 권장되지 않는 예 | | ------------ | ----------- | | 사용자가 볼 수 있는 프로필 ID 또는 사용자 이름 | JonDoe829525552 | | 이메일 주소 | Anna@email.com | | 자동 증가하는 사용자 ID | 123 | {: .reset-td-br-1 .reset-td-br-2 aria-label="ID 명명 모범 사례" } **Warning:** 사용자 ID를 생성하는 방법에 대한 세부 정보를 공유하지 마세요. 이로 인해 조직이 악의적인 공격이나 데이터 유출에 노출될 수 있습니다. # Braze 소프트웨어 개발 키트를 통해 사용자 속성 설정 Source: /docs/ko/developer_guide/analytics/setting_user_attributes/index.md # Set user attributes > Braze SDK를 통해 사용자 속성을 설정하는 방법을 알아보세요. **Note:** 목록에 없는 래퍼 SDK의 경우 관련 네이티브 Android 또는 Swift 메서드를 대신 사용하세요. ## Prerequisites Before you can use this feature, you'll need to [integrate the Web Braze SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=web). ## Default user attributes ### Predefined methods Braze provides predefined methods for setting the following user attributes within the [`User` class](https://js.appboycdn.com/web-sdk/latest/doc/classes/braze.user.html): - First Name - Last Name - Language - Country - Date of Birth - Email - Gender - Home City - Phone Number ### Setting default attributes To set a default attribute for a user, call the `getUser()` method on your Braze instance to get a reference to the current user of your app. Then you can call methods to set a user attribute. ```javascript braze.getUser().setFirstName("SomeFirstName"); ``` ```javascript braze.getUser().setGender(braze.User.Genders.FEMALE); ``` ```javascript braze.getUser().setDateOfBirth(2000, 12, 25); ``` Using Google Tag Manager, standard user attributes (such as a user's first name), should be logged in the same manner as custom user attributes. Ensure the values you're passing in for standard attributes match the expected format specified in the [User class](https://js.appboycdn.com/web-sdk/latest/doc/classes/braze.user.html) documentation. For example, the gender attribute can accept any of the following as values: `"m" | "f" | "o" | "u" | "n" | "p"`. Therefore to set a user's gender as female, create a Custom HTML tag with the following content: ```html ``` ### Unsetting default attributes You can remove or unset a user attribute through your app code, a REST API request, or a [User Update](https://www.braze.com/docs/ko/ko/user_guide/messaging/canvas/canvas_components/user_update/) Canvas step. For array and boolean attributes, use `null`. For other data types, use an empty string (`""`). To unset a default user attribute with the Web SDK, pass `null` to the related method. For example: ```javascript braze.getUser().setFirstName(null); ``` ```javascript braze.getUser().setGender(null); ``` ```javascript braze.getUser().setDateOfBirth(null, null, null); ``` ## Custom user attributes ### Setting custom attributes In addition to the default user attribute methods, you can also set [custom attributes](https://www.braze.com/docs/ko/ko/user_guide/data_and_analytics/custom_data/custom_attributes/#custom-attribute-data-types) for your users. Full method specifications, see [our JSDocs](https://js.appboycdn.com/web-sdk/latest/doc/classes/braze.user.html). To set a custom attribute with a `string` value: ```javascript braze.getUser().setCustomUserAttribute( YOUR_ATTRIBUTE_KEY_STRING, YOUR_STRING_VALUE ); ``` To set a custom attribute with a `integer` value: ```javascript braze.getUser().setCustomUserAttribute( YOUR_ATTRIBUTE_KEY_STRING, YOUR_INT_VALUE ); // Integer attributes may also be incremented using code like the following braze.getUser().incrementCustomUserAttribute( YOUR_ATTRIBUTE_KEY_STRING, THE_INTEGER_VALUE_BY_WHICH_YOU_WANT_TO_INCREMENT_THE_ATTRIBUTE ); ``` To set a custom attribute with a `date` value: ```javascript braze.getUser().setCustomUserAttribute( YOUR_ATTRIBUTE_KEY_STRING, YOUR_DATE_VALUE ); // This method will assign the current time to a custom attribute at the time the method is called braze.getUser().setCustomUserAttribute( YOUR_ATTRIBUTE_KEY_STRING, new Date() ); // This method will assign the date specified by secondsFromEpoch to a custom attribute braze.getUser().setCustomUserAttribute( YOUR_ATTRIBUTE_KEY_STRING, new Date(secondsFromEpoch * 1000) ); ``` The default and maximum number of elements in an array is 500. You can update the maximum number of arrays in the Braze dashboard, under **Data Settings** > **Custom Attributes**. Arrays exceeding the maximum number of elements are truncated to contain the maximum number of elements. To set a custom attribute with an `array` value: ```javascript braze.getUser().setCustomUserAttribute(YOUR_ATTRIBUTE_KEY_STRING, YOUR_ARRAY_OF_STRINGS); // Adding a new element to a custom attribute with an array value braze.getUser().addToCustomAttributeArray(YOUR_ATTRIBUTE_KEY_STRING, "new string"); // Removing an element from a custom attribute with an array value braze.getUser().removeFromCustomAttributeArray(YOUR_ATTRIBUTE_KEY_STRING, "value to be removed"); ``` **Important:** Dates passed to Braze with this method must be JavaScript Date objects. **Important:** Custom attribute keys and values can only have a maximum of 255 characters. For more information about valid custom attribute values, refer to the [reference documentation](https://js.appboycdn.com/web-sdk/latest/doc/classes/braze.user.html). Custom user attributes are not available due to a limitation in Google Tag Manager's scripting language. To log custom attributes, create a Custom HTML tag with the following content: ```html ``` **Important:** The GTM template does not support nested properties on events or purchases. You can use the preceding HTML to log any events or purchases that require nested properties. ### Unsetting custom attributes To unset a custom attribute, pass `null` to the related method. ```javascript braze.getUser().setCustomUserAttribute(YOUR_ATTRIBUTE_KEY_STRING, null); ``` ### Nesting custom attributes You can also nest properties within custom attributes. In the following example, a `favorite_book` object with nested properties is set as a custom attribute on the user profile. For more details, refer to [Nested Custom Attributes](https://www.braze.com/docs/ko/ko/user_guide/data/custom_data/custom_attributes/nested_custom_attribute_support). ```javascript import * as braze from "@braze/web-sdk"; const favoriteBook = { title: "The Hobbit", author: "J.R.R. Tolkien", publishing_date: "1937" }; braze.getUser().setCustomUserAttribute("favorite_book", favoriteBook); ``` ### Using the REST API You can also use our REST API to set or unset user attributes. For more information, refer to [User Data Endpoints](https://www.braze.com/docs/ko/ko/developer_guide/rest_api/user_data/#user-data). ## Setting user subscriptions To set up a subscription for your users (either email or push), call the functions `setEmailNotificationSubscriptionType()` or `setPushNotificationSubscriptionType()`, respectively. Both functions take the `enum` type `braze.User.NotificationSubscriptionTypes` as arguments. This type has three different states: | Subscription Status | Definition | | ------------------- | ---------- | | `braze.User.NotificationSubscriptionTypes.OPTED_IN` | Subscribed, and explicitly opted in | | `braze.User.NotificationSubscriptionTypes.SUBSCRIBED` | Subscribed, but not explicitly opted in | | `braze.User.NotificationSubscriptionTypes.UNSUBSCRIBED` | Unsubscribed and/or explicitly opted out | {: .reset-td-br-1 .reset-td-br-2 aria-label="Setting user subscriptions" } When a user is registered for push, the browser forces them to choose to allow or block notifications, and if they choose to allow push, they are set `OPTED_IN` by default. Visit [Managing user subscriptions](https://www.braze.com/docs/ko/ko/user_guide/message_building_by_channel/email/managing_user_subscriptions/#managing-user-subscriptions) for more information on implementing subscriptions and explicit opt-ins. ### Unsubscribing a user from email ```javascript braze.getUser().setEmailNotificationSubscriptionType(braze.User.NotificationSubscriptionTypes.UNSUBSCRIBED); ``` ### Unsubscribing a user from push ```java braze.getUser().setPushNotificationSubscriptionType(braze.User.NotificationSubscriptionTypes.UNSUBSCRIBED); ``` ## Prerequisites Before you can use this feature, you'll need to [integrate the Android Braze SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=android). ## Default user attributes ### Predefined methods Braze provides predefined methods for setting the following user attributes within the [`BrazeUser`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze/-braze-user/index.html) class. For method specifications, refer to [our KDoc](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze/-braze-user/index.html). - First name - Last name - Country - Language - Date of birth - Email - Gender - Home city - Phone number **Note:** All string values such as first name, last name, country, and home city are limited to 255 characters. ### Setting default attributes To set a default attribute for a user, call the `getCurrentUser()` method on your Braze instance to get a reference to the current user of your app. Then you can call methods to set a user attribute. ```java Braze.getInstance(context).getCurrentUser(new IValueCallback() { @Override public void onSuccess(BrazeUser brazeUser) { brazeUser.setFirstName("first_name"); } } ``` ```kotlin Braze.getInstance(context).getCurrentUser { brazeUser -> brazeUser.setFirstName("first_name") } ``` ### Unsetting default attributes To unset a user attribute, pass `null` to the relevant method. ```java Braze.getInstance(context).getCurrentUser(new IValueCallback() { @Override public void onSuccess(BrazeUser brazeUser) { brazeUser.setFirstName(null); } } ``` ```kotlin Braze.getInstance(context).getCurrentUser { brazeUser -> brazeUser.setFirstName(null) } ``` ## Custom user attributes In addition to the default user attributes, Braze also allows you to define custom attributes using several different data types. For more information on each attribute's segmentation option, see [User data collection](https://www.braze.com/docs/ko/ko/developer_guide/analytics). ### Setting custom attributes To set a custom attribute with a `string` value: ```java Braze.getInstance(context).getCurrentUser(new IValueCallback() { @Override public void onSuccess(BrazeUser brazeUser) { brazeUser.setCustomUserAttribute("your_attribute_key", "your_attribute_value"); } } ``` ```kotlin Braze.getInstance(context).getCurrentUser { brazeUser -> brazeUser.setCustomUserAttribute("your_attribute_key", "your_attribute_value") } ``` To set a custom attribute with an `int` value: ```java Braze.getInstance(context).getCurrentUser(new IValueCallback() { @Override public void onSuccess(BrazeUser brazeUser) { brazeUser.setCustomUserAttribute("your_attribute_key", YOUR_INT_VALUE); // Integer attributes may also be incremented using code like the following: brazeUser.incrementCustomUserAttribute("your_attribute_key", YOUR_INCREMENT_VALUE); } } ``` ```kotlin Braze.getInstance(context).getCurrentUser { brazeUser -> brazeUser.setCustomUserAttribute("your_attribute_key", YOUR_INT_VALUE) // Integer attributes may also be incremented using code like the following: brazeUser.incrementCustomUserAttribute("your_attribute_key", YOUR_INCREMENT_VALUE) } ``` To set a custom attribute with a `long` integer value: ```java Braze.getInstance(context).getCurrentUser(new IValueCallback() { @Override public void onSuccess(BrazeUser brazeUser) { brazeUser.setCustomUserAttribute("your_attribute_key", YOUR_LONG_VALUE); } }); ``` ```kotlin Braze.getInstance(context).getCurrentUser { brazeUser -> brazeUser.setCustomUserAttribute("your_attribute_key", YOUR_LONG_VALUE) } ``` To set a custom attribute with a `float` value: ```java Braze.getInstance(context).getCurrentUser(new IValueCallback() { @Override public void onSuccess(BrazeUser brazeUser) { brazeUser.setCustomUserAttribute("your_attribute_key", YOUR_FLOAT_VALUE); } }); ``` ```kotlin Braze.getInstance(context).getCurrentUser { brazeUser -> brazeUser.setCustomUserAttribute("your_attribute_key", YOUR_FLOAT_VALUE) } ``` To set a custom attribute with a `double` value: ```java Braze.getInstance(context).getCurrentUser(new IValueCallback() { @Override public void onSuccess(BrazeUser brazeUser) { brazeUser.setCustomUserAttribute("your_attribute_key", YOUR_DOUBLE_VALUE); } }); ``` ```kotlin Braze.getInstance(context).getCurrentUser { brazeUser -> brazeUser.setCustomUserAttribute("your_attribute_key", YOUR_DOUBLE_VALUE) } ``` To set a custom attribute with a `boolean` value: ```java Braze.getInstance(context).getCurrentUser(new IValueCallback() { @Override public void onSuccess(BrazeUser brazeUser) { brazeUser.setCustomUserAttribute("your_attribute_key", YOUR_BOOLEAN_VALUE); } }); ``` ```kotlin Braze.getInstance(context).getCurrentUser { brazeUser -> brazeUser.setCustomUserAttribute("your_attribute_key", YOUR_BOOLEAN_VALUE) } ``` ```java Braze.getInstance(context).getCurrentUser(new IValueCallback() { @Override public void onSuccess(BrazeUser brazeUser) { brazeUser.setCustomUserAttribute("your_attribute_key", YOUR_DATE_VALUE); // This method will assign the current time to a custom attribute at the time the method is called: brazeUser.setCustomUserAttributeToNow("your_attribute_key"); // This method will assign the date specified by SECONDS_FROM_EPOCH to a custom attribute: brazeUser.setCustomUserAttributeToSecondsFromEpoch("your_attribute_key", SECONDS_FROM_EPOCH); } }); ``` ```kotlin Braze.getInstance(context).getCurrentUser { brazeUser -> brazeUser.setCustomUserAttribute("your_attribute_key", YOUR_DATE_VALUE) // This method will assign the current time to a custom attribute at the time the method is called: brazeUser.setCustomUserAttributeToNow("your_attribute_key") // This method will assign the date specified by SECONDS_FROM_EPOCH to a custom attribute: brazeUser.setCustomUserAttributeToSecondsFromEpoch("your_attribute_key", SECONDS_FROM_EPOCH) } ``` **Warning:** Dates passed to Braze with this method must either be in the [ISO 8601](http://en.wikipedia.org/wiki/ISO_8601) format (e.g `2013-07-16T19:20:30+01:00`) or in the `yyyy-MM-dd'T'HH:mm:ss:SSSZ` format (e.g `2016-12-14T13:32:31.601-0800`). The default and maximum number of elements in an array is 500. You can update the maximum number of arrays in the Braze dashboard, under **Data Settings** > **Custom Attributes**. Arrays exceeding the maximum number of elements are truncated to contain the maximum number of elements. For more information on custom attribute arrays and their behavior, see [Arrays](https://www.braze.com/docs/ko/ko/developer_guide/analytics/#arrays). ```java Braze.getInstance(context).getCurrentUser(new IValueCallback() { @Override public void onSuccess(BrazeUser brazeUser) { // Setting a custom attribute with an array value brazeUser.setCustomAttributeArray("your_attribute_key", testSetArray); // Adding to a custom attribute with an array value brazeUser.addToCustomAttributeArray("your_attribute_key", "value_to_add"); // Removing a value from an array type custom attribute brazeUser.removeFromCustomAttributeArray("your_attribute_key", "value_to_remove"); } }); ``` ```kotlin Braze.getInstance(context).getCurrentUser { brazeUser -> // Setting a custom attribute with an array value brazeUser.setCustomAttributeArray("your_attribute_key", testSetArray) // Adding to a custom attribute with an array value brazeUser.addToCustomAttributeArray("your_attribute_key", "value_to_add") // Removing a value from an array type custom attribute brazeUser.removeFromCustomAttributeArray("your_attribute_key", "value_to_remove") } ``` ### Unsetting custom attributes To unset a custom attribute, pass the relevant attribute key to the `unsetCustomUserAttribute` method. ```java Braze.getInstance(context).getCurrentUser(new IValueCallback() { @Override public void onSuccess(BrazeUser brazeUser) { brazeUser.unsetCustomUserAttribute("your_attribute_key"); } }); ``` ```kotlin Braze.getInstance(context).getCurrentUser { brazeUser -> brazeUser.unsetCustomUserAttribute("your_attribute_key") } ``` ### Nesting custom attributes You can also nest properties within custom attributes. In the following example, a `favorite_book` object with nested properties is set as a custom attribute on the user profile. For more details, refer to [Nested Custom Attributes](https://www.braze.com/docs/ko/ko/user_guide/data/custom_data/custom_attributes/nested_custom_attribute_support). ```java JSONObject favoriteBook = new JSONObject(); try { favoriteBook.put("title", "The Hobbit"); favoriteBook.put("author", "J.R.R. Tolkien"); favoriteBook.put("publishing_date", "1937"); } catch (JSONException e) { e.printStackTrace(); } braze.getCurrentUser(user -> { user.setCustomUserAttribute("favorite_book", favoriteBook); return null; }); ``` ```kotlin val favoriteBook = JSONObject() .put("title", "The Hobbit") .put("author", "J.R.R. Tolkien") .put("publishing_date", "1937") braze.getCurrentUser { user -> user.setCustomUserAttribute("favorite_book", favoriteBook) } ``` ### Using the REST API You can also use our REST API to set or unset user attributes. For more information, refer to [User Data Endpoints](https://www.braze.com/docs/ko/ko/developer_guide/rest_api/user_data/#user-data). ## Setting user subscriptions To set up a subscription for your users (either email or push), call the functions `setEmailNotificationSubscriptionType()` or `setPushNotificationSubscriptionType()`, respectively. Both of these functions take the enum type `NotificationSubscriptionType` as arguments. This type has three different states: | Subscription status | Definition | | ------------------- | ---------- | | `OPTED_IN` | Subscribed, and explicitly opted in | | `SUBSCRIBED` | Subscribed, but not explicitly opted in | | `UNSUBSCRIBED` | Unsubscribed and/or explicitly opted out | {: .reset-td-br-1 .reset-td-br-2 aria-label="Setting user subscriptions" } **Important:** No explicit opt-in is required by Android to send users push notifications. When a user is registered for push, they are set to `SUBSCRIBED` rather than `OPTED_IN` by default. Refer to [managing user subscriptions](https://www.braze.com/docs/ko/ko/user_guide/message_building_by_channel/email/managing_user_subscriptions/#managing-user-subscriptions) for more information on implementing subscriptions and explicit opt-ins. ### Setting email subscriptions ```java Braze.getInstance(context).getCurrentUser(new IValueCallback() { @Override public void onSuccess(BrazeUser brazeUser) { brazeUser.setEmailNotificationSubscriptionType(emailNotificationSubscriptionType); } }); ``` ```kotlin Braze.getInstance(context).getCurrentUser { brazeUser -> brazeUser.setEmailNotificationSubscriptionType(emailNotificationSubscriptionType) } ``` ### Setting push notification subscription ```java Braze.getInstance(context).getCurrentUser(new IValueCallback() { @Override public void onSuccess(BrazeUser brazeUser) { brazeUser.setPushNotificationSubscriptionType(pushNotificationSubscriptionType); } }); ``` ```kotlin Braze.getInstance(context).getCurrentUser { brazeUser -> brazeUser.setPushNotificationSubscriptionType(pushNotificationSubscriptionType) } ``` ## Prerequisites Before you can use this feature, you'll need to [integrate the Swift Braze SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=swift). ## Default user attributes ### Supported attributes The following attributes should be set on the `Braze.User` object: - `firstName` - `lastName` - `email` - `dateOfBirth` - `country` - `language` - `homeCity` - `phone` - `gender` ### Setting default attributes To set a default user attribute, set the appropriate field on the shared `Braze.User` object. The following is an example of setting the first name attribute: ```swift AppDelegate.braze?.user.set(firstName: "Alex") ``` ```objc [AppDelegate.braze.user setFirstName:@"Alex"]; ``` ### Unsetting default attributes To unset a default user attribute, pass `nil` to the relevant method. ```swift AppDelegate.braze?.user.set(firstName: nil) ``` ```objc [AppDelegate.braze.user setFirstName:nil]; ``` ## Custom user attributes In addition to the default user attributes, Braze also allows you to define custom attributes using several different data types. For more information on each attribute's segmentation option, see [User data collection](https://www.braze.com/docs/ko/ko/developer_guide/analytics/). **Important:** Custom attribute values have a maximum length of 255 characters; longer values will be truncated. For more information, refer to [`Braze.User`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/user-swift.class). ### Setting custom attributes To set a custom attribute with a `string` value: ```swift AppDelegate.braze?.user.setCustomAttribute(key: "your_attribute_key", value: "your_attribute_value") ``` ```objc [AppDelegate.braze.user setCustomAttributeWithKey:@"your_attribute_key" stringValue:"your_attribute_value"]; ``` To set a custom attribute with an `integer` value: ```swift AppDelegate.braze?.user.setCustomAttribute(key: "your_attribute_key", value: yourIntegerValue) ``` ```objc [AppDelegate.braze.user setCustomAttributeWithKey:@"your_attribute_key" andIntegerValue:yourIntegerValue]; ``` Braze treats `float` and `double` values the same within our database. To set a custom attribute with a double value: ```swift AppDelegate.braze?.user.setCustomAttribute(key: "your_attribute_key", value: yourDoubleValue) ``` ```objc [AppDelegate.braze.user setCustomAttributeWithKey:@"your_attribute_key" andDoubleValue:yourDoubleValue]; ``` To set a custom attribute with a `boolean` value: ```swift AppDelegate.braze?.user.setCustomAttribute("your_attribute_key", value: yourBoolValue) ``` ```objc [AppDelegate.braze.user setCustomAttributeWithKey:@"your_attribute_key" andBOOLValue:yourBOOLValue]; ``` To set a custom attribute with a `date` value: ```swift AppDelegate.braze?.user.setCustomAttribute("your_attribute_key", dateValue:yourDateValue) ``` ```objc [AppDelegate.braze.user setCustomAttributeWithKey:@"your_attribute_key" andDateValue:yourDateValue]; ``` The default and maximum number of elements in an array is 500. You can update the maximum number of arrays in the Braze dashboard, under **Data Settings** > **Custom Attributes**. Arrays exceeding the maximum number of elements are truncated to contain the maximum number of elements. To set a custom attribute with an `array` value: ```swift // Setting a custom attribute with an array value AppDelegate.braze?.user.setCustomAttributeArray(key: "array_name", array: ["value1", "value2"]) // Adding to a custom attribute with an array value AppDelegate.braze?.user.addToCustomAttributeArray(key: "array_name", value: "value3") // Removing a value from an array type custom attribute AppDelegate.braze?.user.removeFromCustomAttributeArray(key: "array_name", value: "value2") ``` ```objc // Setting a custom attribute with an array value [AppDelegate.braze.user setCustomAttributeArrayWithKey:@"array_name" array:@[@"value1", @"value2"]]; // Adding to a custom attribute with an array value [AppDelegate.braze.user addToCustomAttributeArrayWithKey:@"array_name" value:@"value3"]; // Removing a value from an array type custom attribute [AppDelegate.braze.user removeFromCustomAttributeArrayWithKey:@"array_name" value:@"value2"]; // Removing an entire array and key [AppDelegate.braze.user setCustomAttributeArrayWithKey:@"array_name" array:nil]; ``` ### Incrementing or decrementing custom attributes This code is an example of an incrementing custom attribute. You may increment the value of a custom attribute by any `integer` or `long` value: ```swift AppDelegate.braze?.user.incrementCustomUserAttribute(key: "your_attribute_key", by: incrementIntegerValue) ``` ```objc [AppDelegate.braze.user incrementCustomUserAttribute:@"your_attribute_key" by:incrementIntegerValue]; ``` ### Unsetting custom attributes To unset a custom attribute, pass the relevant attribute key to the `unsetCustomAttribute` method. ```swift AppDelegate.braze?.user.unsetCustomAttribute(key: "your_attribute_key") ``` To unset a custom attribute, pass the relevant attribute key to the `unsetCustomAttributeWithKey` method. ```objc [AppDelegate.braze.user unsetCustomAttributeWithKey:@"your_attribute_key"]; ``` ### Nesting custom attributes You can also nest properties within custom attributes. In the following example, a `favorite_book` object with nested properties is set as a custom attribute on the user profile. For more details, refer to [Nested Custom Attributes](https://www.braze.com/docs/ko/ko/user_guide/data/custom_data/custom_attributes/nested_custom_attribute_support). ```swift let favoriteBook: [String: Any?] = [ "title": "The Hobbit", "author": "J.R.R. Tolkien", "publishing_date": "1937" ] braze.user.setCustomAttribute(key: "favorite_book", dictionary: favoriteBook) ``` ```objc NSDictionary *favoriteBook = @{ @"title": @"The Hobbit", @"author": @"J.R.R. Tolkien", @"publishing_date": @"1937" }; [AppDelegate.braze.user setCustomAttributeWithKey:@"favorite_book" dictionary:favoriteBook]; ``` ### Using the REST API You can also use our REST API to set or unset user attributes. For more information, refer to [User Data Endpoints](https://www.braze.com/docs/ko/ko/developer_guide/rest_api/user_data/#user-data). ## Setting user subscriptions To set up a subscription for your users (either email or push), call the functions `set(emailSubscriptionState:)` or `set(pushNotificationSubscriptionState:)`, respectively. Both of these functions take the enum type `Braze.User.SubscriptionState` as arguments. This type has three different states: | Subscription Status | Definition | | ------------------- | ---------- | | `optedIn` | Subscribed, and explicitly opted in | | `subscribed` | Subscribed, but not explicitly opted in | | `unsubscribed` | Unsubscribed and/or explicitly opted out | {: .reset-td-br-1 .reset-td-br-2 aria-label="Setting user subscriptions" } Users who grant permission for an app to send them push notifications default to the status of `optedIn` as iOS requires an explicit opt-in. Users will be set to `subscribed` automatically upon receipt of a valid email address; however, we suggest that you establish an explicit opt-in process and set this value to `optedIn` upon receipt of explicit consent from your user. Refer to [Managing user subscriptions](https://www.braze.com/docs/ko/ko/user_guide/message_building_by_channel/email/managing_user_subscriptions/) for more details. ### Setting email subscriptions ```swift AppDelegate.braze?.user.set(emailSubscriptionState: Braze.User.SubscriptionState) ``` ```objc [AppDelegate.braze.user setEmailSubscriptionState: BRZUserSubscriptionState] ``` ### Setting push notification subscriptions ```swift AppDelegate.braze?.user.set(pushNotificationSubscriptionState: Braze.User.SubscriptionState) ``` ```objc [AppDelegate.braze.user setPushNotificationSubscriptionState: BRZUserSubscriptionState] ``` Refer to [Managing user subscriptions](https://www.braze.com/docs/ko/ko/user_guide/message_building_by_channel/email/managing_user_subscriptions/) for more details. ## Prerequisites Before you can use this feature, you'll need to [integrate the Flutter Braze SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=flutter). ## Default user attributes ### Supported attributes The following attributes are supported: - First Name - Last Name - Gender - Date of Birth - Home City - Country - Phone Number - Language - Email **Important:** All string values such as first name, last name, country, and home city are limited to 255 characters. ### Setting default attributes To set user attributes automatically collected by Braze, you can use the setter methods included with the SDK. ```dart braze.setFirstName('Name'); ``` ## Custom user attributes ### Setting custom attributes In addition to the default user attributes, Braze also allows you to define custom attributes using a number of different data types: To set a custom attribute with a `string` value: ```dart braze.setStringCustomUserAttribute("custom string attribute", "string custom attribute"); ``` To set a custom attribute with an `integer` value: ```dart // Set Integer Attribute braze.setIntCustomUserAttribute("custom int attribute key", integer); // Increment Integer Attribute braze.incrementCustomUserAttribute("key", integer); ``` To set a custom attribute with a `double` value: ```dart braze.setDoubleCustomUserAttribute("custom double attribute key", double); ``` To set a custom attribute with a `boolean` value: ```dart braze.setBoolCustomUserAttribute("custom boolean attribute key", boolean); ``` To set a custom attribute with a `date` value: ```dart braze.setDateCustomUserAttribute("custom date attribute key", date); ``` To set a custom attribute with an `array` value: ```dart // Adding to an Array braze.addToCustomAttributeArray("key", "attribute"); // Removing an item from an Array braze.removeFromCustomAttributeArray("key", "attribute"); ``` **Important:** Custom attribute values have a maximum length of 255 characters; longer values will be truncated. ### Unsetting custom attributes To unset a custom attribute, pass the relevant attribute key to the `unsetCustomUserAttribute` method. ```dart braze.unsetCustomUserAttribute('attribute_key'); ``` ## Prerequisites Before you can use this feature, you'll need to [integrate the Roku Braze SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=roku). ## Default user attributes ### Predefined methods Braze provides predefined methods for setting the following user attributes using the `m.Braze` object. - `FirstName` - `LastName` - `Email` - `Gender` - `DateOfBirth` - `Country` - `Language` - `HomeCity` - `PhoneNumber` ### Setting default attributes To set a default attribute, call the relevant method on the `m.Braze` object. ```brightscript m.Braze.setFirstName("Alex") ``` ```brightscript m.Braze.setLastName("Smith") ``` ```brightscript m.Braze.setEmail("alex@example.com") ``` ```brightscript m.Braze.setGender("m") ' Accepts: "m", "f", "o", "n", "u", "p" ``` ```brightscript m.Braze.setDateOfBirth(1990, 5, 15) ' Year, month, day ``` ```brightscript m.Braze.setCountry("United States") ``` ```brightscript m.Braze.setLanguage("en") ``` ```brightscript m.Braze.setHomeCity("New York") ``` ```brightscript m.Braze.setPhoneNumber("+1234567890") ``` ## Custom user attributes In addition to the default user attributes, Braze also allows you to define custom attributes using several different data types. ### Settings custom attributes To set a custom attribute a `string` value: ```brightscript m.Braze.setCustomAttribute("stringAttribute", "stringValue") ``` To set a custom attribute with an `integer` value: ```brightscript m.Braze.setCustomAttribute("intAttribute", 5) ``` Braze treats `float` and `double` values exactly the same. To set a custom attribute with either value: ```brightscript m.Braze.setCustomAttribute("floatAttribute", 3.5) ``` To set a custom attribute with a `boolean` value: ```brightscript m.Braze.setCustomAttribute("boolAttribute", true) ``` To set a custom attribute with a `date` value: ```brightscript dateAttribute = CreateObject("roDateTime") dateAttribute.fromISO8601String("1992-11-29 00:00:00.000") m.Braze.setCustomAttribute("dateAttribute", dateAttribute) ``` To set a custom attribute with an `array` value: ```brightscript stringArray = createObject("roArray", 3, true) stringArray.Push("string1") stringArray.Push("string2") stringArray.Push("string3") m.Braze.setCustomAttribute("arrayAttribute", stringArray) ``` **Important:** Custom attribute values have a maximum length of 255 characters; longer values will be truncated. ### Incrementing and decrementing custom attributes This code is an example of an incrementing custom attribute. You may increment the value of a custom attribute by any positive or negative integer value. ```brightscript m.Braze.incrementCustomUserAttribute("intAttribute", 3) ``` ### Unsetting custom attributes To unset a custom attribute, pass the relevant attribute key to the `unsetCustomAttribute` method. ```brightscript m.Braze.unsetCustomAttribute("attributeName") ``` ### Using the REST API You can also use our REST API to set or unset user attributes. For more information, refer to [User Data Endpoints](https://www.braze.com/docs/ko/ko/developer_guide/rest_api/user_data/#user-data). ## Setting email subscriptions You can set the following email subscription statuses for your users programmatically through the SDK. | Subscription Status | Definition | | ------------------- | ---------- | | `OptedIn` | Subscribed, and explicitly opted in | | `Subscribed` | Subscribed, but not explicitly opted in | | `UnSubscribed` | Unsubscribed and/or explicitly opted out | {: .reset-td-br-1 .reset-td-br-2 aria-label="Setting email subscriptions" } **Note:** These types fall under `BrazeConstants().SUBSCRIPTION_STATES`. The method for setting email subscription status is `setEmailSubscriptionState()`. Users will be set to `Subscribed` automatically upon receipt of a valid email address, however, we suggest that you establish an explicit opt-in process and set this value to `OptedIn` upon receipt of explicit consent from your user. For more details, visit [Managing user subscriptions](https://www.braze.com/docs/ko/ko/user_guide/message_building_by_channel/email/managing_user_subscriptions/#managing-user-subscriptions). ```brightscript m.Braze.setEmailSubscriptionState(BrazeConstants().SUBSCRIPTION_STATES.OPTED_IN) ``` ## Prerequisites Before you can use this feature, you'll need to [integrate the Unity Braze SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=unity). ## Default user attributes ### Predefined methods Braze provides predefined methods for setting the following user attributes using the `BrazeBinding` object. For more information, see [Braze Unity declaration file](https://github.com/braze-inc/braze-unity-sdk/blob/master/Assets/Plugins/Appboy/BrazePlatform.cs). - First name - Last name - User email - Gender - Birth date - User country - User home city - User email subscription - User push subscription - User phone number ### Setting default attributes To set a default attribute, call the relevant method on the `BrazeBinding` object. ```csharp BrazeBinding.SetUserFirstName("first name"); ``` ```csharp BrazeBinding.SetUserLastName("last name"); ``` ```csharp BrazeBinding.SetUserEmail("email@email.com"); ``` ```csharp BrazeBinding.SetUserGender(Appboy.Models.Gender); ``` ```csharp BrazeBinding.SetUserDateOfBirth("year(int)", "month(int)", "day(int)"); ``` ```csharp BrazeBinding.SetUserCountry("country name"); ``` ```csharp BrazeBinding.SetUserHomeCity("city name"); ``` ```csharp BrazeBinding.SetUserEmailNotificationSubscriptionType(AppboyNotificationSubscriptionType); ``` ```csharp BrazeBinding.SetUserPushNotificationSubscriptionType(AppboyNotificationSubscriptionType); ``` ```csharp BrazeBinding.SetUserPhoneNumber("phone number"); ``` ### Unsetting default attributes To unset a default user attribute, pass `null` to the relevant method. ```csharp BrazeBinding.SetUserFirstName(null); ``` ## Custom user attributes In addition to the default user attributes, Braze also allows you to define custom attributes using several different data types. For more information on each attribute's segmentation option, see [User data collection](https://www.braze.com/docs/ko/ko/developer_guide/analytics). ### Setting custom attributes To set a custom attribute, use the corresponding method for the attribute type: ```csharp AppboyBinding.SetCustomUserAttribute("custom string attribute key", "string custom attribute"); ``` ```csharp // Set Integer Attribute AppboyBinding.SetCustomUserAttribute("custom int attribute key", 'integer value'); // Increment Integer Attribute AppboyBinding.IncrementCustomUserAttribute("key", increment(int)) ``` ```csharp AppboyBinding.SetCustomUserAttribute("custom float attribute key", 'float value'); ``` ```csharp AppboyBinding.SetCustomUserAttribute("custom boolean attribute key", 'boolean value'); ``` ```csharp AppboyBinding.SetCustomUserAttributeToNow("custom date attribute key"); ``` ```csharp AppboyBinding.SetCustomUserAttributeToSecondsFromEpoch("custom date attribute key", 'integer value'); ``` **Note:** Dates passed to Braze must either be in the [ISO 8601](http://en.wikipedia.org/wiki/ISO_8601) format (such as `2013-07-16T19:20:30+01:00`) or in the `yyyy-MM-dd'T'HH:mm:ss:SSSZ` format (such as`2016-12-14T13:32:31.601-0800`). ```csharp // Setting An Array AppboyBinding.SetCustomUserAttributeArray("key", array(List), sizeOfTheArray(int)) // Adding to an Array AppboyBinding.AddToCustomUserAttributeArray("key", "Attribute") // Removing an item from an Array AppboyBinding.RemoveFromCustomUserAttributeArray("key", "Attribute") ``` **Important:** Custom attribute values have a maximum length of 255 characters; longer values will be truncated. ### Unsetting custom attributes To unset a custom attribute, pass the relevant attribute key to the `UnsetCustomUserAttribute` method. ```csharp AppboyBinding.UnsetCustomUserAttribute("custom attribute key"); ``` ### Using the REST API You can also use our REST API to set or unset user attributes. For more information, refer to [User Data Endpoints](https://www.braze.com/docs/ko/ko/developer_guide/rest_api/user_data/#user-data). ## Setting user subscriptions To set up an email or push subscription for your users, call one of the following functions. ```csharp // Email notifications AppboyBinding.SetUserEmailNotificationSubscriptionType() // Push notifications AppboyBinding.SetPushNotificationSubscriptionType()` ``` Both functions take `Appboy.Models.AppboyNotificationSubscriptionType` as arguments, which has three different states: | Subscription Status | Definition | | ------------------- | ---------- | | `OPTED_IN` | Subscribed, and explicitly opted in | | `SUBSCRIBED` | Subscribed, but not explicitly opted in | | `UNSUBSCRIBED` | Unsubscribed and/or explicitly opted out | {: .reset-td-br-1 .reset-td-br-2 aria-label="Setting user subscriptions" } **Note:** No explicit opt-in is required by Windows to send users push notifications. When a user is registered for push, they are set to `SUBSCRIBED` rather than `OPTED_IN` by default. To learn more, check out our documentation on [implementing subscriptions and explicit opt-ins](https://www.braze.com/docs/ko/ko/user_guide/message_building_by_channel/email/managing_user_subscriptions/#managing-user-subscriptions). | Subscription Type | Description | |------------------------------------------|-------------| | `EmailNotificationSubscriptionType` | Users will be set to `SUBSCRIBED` automatically upon receipt of a valid email address. However, we suggest that you establish an explicit opt-in process and set this value to `OPTED_IN` upon receipt of explicit consent from your user. Visit our [Changing User Subscriptions](https://www.braze.com/docs/ko/ko/user_guide/administrative/manage_your_users/managing_user_subscriptions/#changing-subscriptions) doc for more details. | | `PushNotificationSubscriptionType` | Users will be set to `SUBSCRIBED` automatically upon valid push registration. However, we suggest that you establish an explicit opt-in process and set this value to `OPTED_IN` upon receipt of explicit consent from your user. Visit our [Changing User Subscriptions](https://www.braze.com/docs/ko/ko/user_guide/administrative/manage_your_users/managing_user_subscriptions/#changing-subscriptions) doc for more details. | {: .reset-td-br-1 .reset-td-br-2 aria-label="Setting user subscriptions" } **Note:** These types fall under `Appboy.Models.AppboyNotificationSubscriptionType`. ### Setting email subscriptions ```csharp AppboyBinding.SetUserEmailNotificationSubscriptionType(AppboyNotificationSubscriptionType.OPTED_IN); ``` ### Setting push notification subscriptions ```csharp AppboyBinding.SetUserPushNotificationSubscriptionType(AppboyNotificationSubscriptionType.OPTED_IN); ``` ## Prerequisites Before you can use this feature, you'll need to [integrate the React Native Braze SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=react%20native). ## Logging custom attributes Braze provides methods for assigning attributes to users. You'll be able to filter and segment your users according to these attributes on the dashboard. ### Default user attributes To set user attributes automatically collected by Braze, you can use setter methods that come with the SDK. ```javascript Braze.setFirstName("Name"); ``` The following attributes are supported: - First Name - Last Name - Gender - Date of Birth - Home City - Country - Phone Number - Language - Email All string values such as first name, last name, country, and home city are limited to 255 characters. ### Custom user attributes In addition to our predefined user attribute methods, Braze also provides [custom attributes](https://www.braze.com/docs/ko/ko/user_guide/data_and_analytics/custom_data/custom_attributes/#custom-attribute-data-types) to track data from your applications. ```javascript Braze.setCustomUserAttribute("attribute_key", "attribute_value", function(){ // optional onResult callback }); ``` #### Unsetting custom attributes ```javascript Braze.unsetCustomUserAttribute("attribute_key", function(){ // optional onResult callback }); ``` #### Custom Attribute Arrays ```javascript // Adds a string to a custom attribute string array, or creates that array if one doesn't exist. Braze.addToCustomUserAttributeArray("my-attribute-array", "new or existing value", optionalCallback); // Removes a string from a custom attribute string array. Braze.removeFromCustomUserAttributeArray("my-attribute-array", "existing value", optionalCallback); ``` # Braze SDK를 통해 커스텀 이벤트 기록하기 Source: /docs/ko/developer_guide/analytics/logging_events/index.md # 커스텀 이벤트 기록 {#log-custom-events} > Braze SDK를 통해 커스텀 이벤트를 기록하는 방법을 알아보세요. **Note:** 목록에 없는 래퍼 SDK의 경우 관련 네이티브 Android 또는 Swift 메서드를 대신 사용하세요. 이커머스 권장 이벤트에 대해서는 [이커머스 이벤트 기록](https://www.braze.com/docs/ko/ko/developer_guide/analytics/logging_ecommerce_events/)을 참조하세요. ## 커스텀 이벤트 로깅하기 {#logging-a-custom-event} 커스텀 이벤트를 기록하려면 다음 이벤트 로깅 메서드를 사용하세요. 표준 웹 SDK 구현의 경우 다음 메서드를 사용할 수 있습니다: ```javascript braze.logCustomEvent("YOUR_EVENT_NAME"); ``` 대신 Google Tag Manager를 사용하려면 **커스텀 이벤트** 태그 유형을 사용하여 [`logCustomEvent` 메서드](https://js.appboycdn.com/web-sdk/latest/doc/modules/braze.html#logcustomevent)를 호출하고, 선택적으로 커스텀 이벤트 속성정보를 포함하여 커스텀 이벤트를 Braze에 전송할 수 있습니다. 이렇게 하려면: 1. 변수를 사용하거나 이벤트 이름을 입력하여 **이벤트 이름**을 입력합니다. 2. **행 추가** 버튼을 사용하여 이벤트 속성정보를 추가합니다. ![Braze 동작 태그 구성 설정을 보여주는 대화상자. 포함된 설정은 "태그 유형"(커스텀 이벤트), "이벤트 이름"(버튼 클릭), "이벤트 속성정보"입니다.](https://www.braze.com/docs/ko/ko/assets/img/web-gtm/gtm-custom-event.png?f5c76a9c9b8c1e0f2a24ddd36d3fffba) 네이티브 Android의 경우 다음 메서드를 사용할 수 있습니다: ```java Braze.getInstance(context).logCustomEvent(YOUR_EVENT_NAME); ``` ```kotlin Braze.getInstance(context).logCustomEvent(YOUR_EVENT_NAME) ``` ```swift AppDelegate.braze?.logCustomEvent(name: "YOUR_EVENT_NAME") ``` ```objc [AppDelegate.braze logCustomEvent:@"YOUR_EVENT_NAME"]; ``` ```dart braze.logCustomEvent('YOUR_EVENT_NAME'); ``` Braze Cordova 플러그인 메서드를 사용하세요: ```javascript BrazePlugin.logCustomEvent("YOUR_EVENT_NAME"); ``` `logCustomEvent` API는 다음을 수락합니다: - `eventName` (필수 문자열): 최대 255자까지 사용할 수 있습니다. 이름을 `$`로 시작하지 마세요. 영숫자 및 구두점을 사용하세요. - `eventProperties` (선택 사항 오브젝트): 이벤트 메타데이터에 대한 키-값 페어를 추가하세요. 키는 최대 255자까지 사용할 수 있으며, 키를 `$`로 시작하지 마세요. 등록정보 값의 경우, `string`(최대 255자), `numeric`, `boolean`, 배열 또는 중첩된 JSON 오브젝트를 사용하세요. 구현 세부 정보는 Braze Cordova SDK 소스를 참조하세요: - [`www/BrazePlugin.js` `logCustomEvent` 메서드 (138-140행)](https://github.com/braze-inc/braze-cordova-sdk/blob/86132bc7f0b6ddf1b598b0e612db70f11744801c/www/BrazePlugin.js#L138-L140) - [`www/BrazePlugin.js` JSDoc (128-140행)](https://github.com/braze-inc/braze-cordova-sdk/blob/86132bc7f0b6ddf1b598b0e612db70f11744801c/www/BrazePlugin.js#L128-L140) - [Android 핸들러 `src/android/BrazePlugin.kt` (108-115행)](https://github.com/braze-inc/braze-cordova-sdk/blob/86132bc7f0b6ddf1b598b0e612db70f11744801c/src/android/BrazePlugin.kt#L108-L115) - [iOS 핸들러 `src/ios/BrazePlugin.m` (308-313행)](https://github.com/braze-inc/braze-cordova-sdk/blob/86132bc7f0b6ddf1b598b0e612db70f11744801c/src/ios/BrazePlugin.m#L308-L313) - [iOS 메서드 선언 `src/ios/BrazePlugin.h` (24행)](https://github.com/braze-inc/braze-cordova-sdk/blob/86132bc7f0b6ddf1b598b0e612db70f11744801c/src/ios/BrazePlugin.h#L24) [Infillion 비콘](https://infillion.com/software/beacons/)을 Android 앱에 통합한 경우, 선택적으로 `visit.getPlace()`를 사용하여 위치별 이벤트를 기록할 수 있습니다. `requestImmediateDataFlush`는 앱이 백그라운드에 있는 경우에도 이벤트가 기록되도록 보장합니다. ```java Braze.getInstance(context).logCustomEvent("Entered " + visit.getPlace()); Braze.getInstance(context).requestImmediateDataFlush(); ``` ```kotlin Braze.getInstance(context).logCustomEvent("Entered " + visit.getPlace()) Braze.getInstance(context).requestImmediateDataFlush() ``` ```javascript Braze.logCustomEvent("YOUR_EVENT_NAME"); ``` ```brightscript m.Braze.logEvent("YOUR_EVENT_NAME") ``` ```csharp AppboyBinding.LogCustomEvent("YOUR_EVENT_NAME"); ``` ## 메타데이터 등록정보 추가하기 {#adding-metadata-properties} 커스텀 이벤트를 기록할 때, 이벤트와 함께 등록정보 오브젝트를 전달하여 해당 커스텀 이벤트에 대한 메타데이터를 추가할 수 있습니다. 등록정보는 키-값 페어로 정의됩니다. 키는 문자열이며 값은 `string`, `numeric`, `boolean`, [`Date`](http://www.w3schools.com/jsref/jsref_obj_date.asp) 오브젝트, 배열 또는 중첩된 JSON 오브젝트일 수 있습니다. 메타데이터 등록정보를 추가하려면 다음 이벤트 로깅 메서드를 사용하세요. ```javascript braze.logCustomEvent("YOUR-EVENT-NAME", { you: "can", pass: false, orNumbers: 42, orDates: new Date(), or: ["any", "array", "here"], andEven: { deeply: ["nested", "json"] } }); ``` ```java Braze.logCustomEvent("YOUR-EVENT-NAME", new BrazeProperties(new JSONObject() .put("you", "can") .put("pass", false) .put("orNumbers", 42) .put("orDates", new Date()) .put("or", new JSONArray() .put("any") .put("array") .put("here")) .put("andEven", new JSONObject() .put("deeply", new JSONArray() .put("nested") .put("json")) ) )); ``` ```kotlin Braze.logCustomEvent("YOUR-EVENT-NAME", BrazeProperties(JSONObject() .put("you", "can") .put("pass", false) .put("orNumbers", 42) .put("orDates", Date()) .put("or", JSONArray() .put("any") .put("array") .put("here")) .put("andEven", JSONObject() .put("deeply", JSONArray() .put("nested") .put("json")) ) )) ``` ```swift AppDelegate.braze?.logCustomEvent( name: "YOUR-EVENT-NAME", properties: [ "you": "can", "pass": false, "orNumbers": 42, "orDates": Date(), "or": ["any", "array", "here"], "andEven": [ "deeply": ["nested", "json"] ] ] ) ``` ```objc [AppDelegate.braze logCustomEvent:@"YOUR-EVENT-NAME" properties:@{ @"you": @"can", @"pass": @(NO), @"orNumbers": @42, @"orDates": [NSDate date], @"or": @[@"any", @"array", @"here"], @"andEven": @{ @"deeply": @[@"nested", @"json"] } }]; ``` ```dart braze.logCustomEvent('custom_event_with_properties', properties: { 'key1': 'value1', 'key2': ['value2', 'value3'], 'key3': false, }); ``` 등록정보 오브젝트로 커스텀 이벤트를 기록합니다: ```javascript var properties = {}; properties["key1"] = "value1"; properties["key2"] = ["value2", "value3"]; properties["key3"] = false; BrazePlugin.logCustomEvent("YOUR-EVENT-NAME", properties); ``` 등록정보를 인라인으로 전달할 수도 있습니다: ```javascript BrazePlugin.logCustomEvent("YOUR-EVENT-NAME", { "key": "value", "amount": 42, }); ``` 공식 Cordova 샘플 앱에는 문자열, 숫자, 부울, 배열 및 중첩 오브젝트 등록정보가 포함되어 있습니다: - [`sample-project/www/js/index.js` (230-251행)](https://github.com/braze-inc/braze-cordova-sdk/blob/86132bc7f0b6ddf1b598b0e612db70f11744801c/sample-project/www/js/index.js#L230-L251) 샘플 프로젝트 발췌: ```javascript var properties = {}; properties["One"] = "That's the Way of the World"; properties["Two"] = "After the Love Has Gone"; properties["Three"] = "Can't Hide Love"; BrazePlugin.logCustomEvent("cordovaCustomEventWithProperties", properties); BrazePlugin.logCustomEvent("cordovaCustomEventWithoutProperties"); BrazePlugin.logCustomEvent("cordovaCustomEventWithFloatProperties", { "Cart Value": 4.95, "Cart Item Name": "Spicy Chicken Bites 5 pack" }); BrazePlugin.logCustomEvent("cordovaCustomEventWithNestedProperties", { "array key": [1, "2", false], "object key": { "k1": "1", "k2": 2, "k3": false, }, "deep key": { "key": [1, "2", true] } }); ``` API 및 네이티브 브리지 세부 정보는 다음을 참조하세요: - [`www/BrazePlugin.js` JSDoc (128-140행)](https://github.com/braze-inc/braze-cordova-sdk/blob/86132bc7f0b6ddf1b598b0e612db70f11744801c/www/BrazePlugin.js#L128-L140) - [Android 핸들러 `src/android/BrazePlugin.kt` (108-115행)](https://github.com/braze-inc/braze-cordova-sdk/blob/86132bc7f0b6ddf1b598b0e612db70f11744801c/src/android/BrazePlugin.kt#L108-L115) - [iOS 핸들러 `src/ios/BrazePlugin.m` (308-313행)](https://github.com/braze-inc/braze-cordova-sdk/blob/86132bc7f0b6ddf1b598b0e612db70f11744801c/src/ios/BrazePlugin.m#L308-L313) ```javascript Braze.logCustomEvent("custom_event_with_properties", { key1: "value1", key2: ["value2", "value3"], key3: false, }); ``` ```brightscript m.Braze.logEvent("YOUR_EVENT_NAME", {"stringPropKey" : "stringPropValue", "intPropKey" : Integer intPropValue}) ``` ```csharp AppboyBinding.LogCustomEvent("event name", properties(Dictionary)); ``` **Important:** `time` 및 `event_name` 키는 예약되어 있으며 커스텀 이벤트 등록정보로 사용할 수 없습니다. ## 모범 사례 {#best-practices} 커스텀 이벤트 등록정보가 예상대로 기록되도록 하려면 세 가지 중요한 확인 사항을 점검해야 합니다: * [기록되는 이벤트 확인](#verify-events) * [로그 확인](#verify-log) * [값 확인](#verify-values) 커스텀 이벤트가 기록될 때마다 여러 등록정보가 함께 기록될 수 있습니다. ### 이벤트 확인 {#verify-events} 개발자에게 어떤 이벤트 등록정보가 추적되고 있는지 확인하세요. 모든 이벤트 등록정보는 대소문자를 구분한다는 점을 유의하세요. 커스텀 이벤트 추적에 대한 추가 정보는 플랫폼에 따라 다음 문서를 확인하세요: * [Android](https://www.braze.com/docs/ko/ko/developer_guide/analytics/logging_events/?tab=android) * [iOS](https://www.braze.com/docs/ko/ko/developer_guide/analytics/logging_events/?tab=swift) * [웹](https://www.braze.com/docs/ko/ko/developer_guide/analytics/logging_events/?tab=web) ### 로그 확인 {#verify-log} 이벤트 등록정보가 성공적으로 추적되었는지 확인하려면 **커스텀 이벤트** 페이지에서 모든 이벤트 등록정보를 볼 수 있습니다. 1. **데이터 설정** > **커스텀 이벤트**로 이동합니다. 2. 목록에서 커스텀 이벤트를 찾습니다. 3. 이벤트에 대해 **등록정보 관리**를 선택하여 이벤트와 관련된 등록정보의 이름을 확인합니다. ### 값 확인 {#verify-values} [테스트 사용자로 사용자를 추가](https://www.braze.com/docs/ko/ko/user_guide/administrative/app_settings/internal_groups_tab/#adding-test-users)한 후, 다음 단계에 따라 값을 확인하세요: 1. 앱 내에서 커스텀 이벤트를 수행합니다. 2. 데이터가 플러시될 때까지 약 10초 정도 기다립니다. 3. [이벤트 사용자 로그](https://www.braze.com/docs/ko/ko/user_guide/administer/global/workspace_settings/logs_and_alerts/event_user_log/)를 새로고침하여 커스텀 이벤트 및 함께 전달된 이벤트 등록정보 값을 확인합니다. ## 커스텀 이벤트 문제 해결 {#troubleshooting-custom-events} 다음 시나리오를 활용하여 SDK 전반에서 커스텀 이벤트 로깅 문제를 해결하세요. ### 커스텀 이벤트 트리거 확인 {#verifying-the-custom-event-trigger} 커스텀 이벤트가 나타나지 않는 경우, 앱에서 추적된 동작이 테스트 중인 동작과 일치하지 않을 수 있습니다. - 개발자 팀에 어떤 앱 동작이 커스텀 이벤트를 트리거하는지 확인하세요. - SDK 업그레이드 후 `braze` 대신 `appboy`를 참조하는 등 더 이상 사용되지 않는 코드 경로가 있는지 확인하세요. ### 커스텀 이벤트가 익명 프로필에 기록됨 {#custom-events-are-logged-to-an-anonymous-profile} 커스텀 이벤트를 기록하기 전에 사용자를 식별하지 않으면, Braze가 해당 이벤트를 익명 프로필에 연결할 수 있습니다. - 커스텀 이벤트를 수행하기 전에 `changeUser()`를 호출하여 Braze가 식별된 고객 프로필에 기록하도록 하세요. - 식별된 테스트 사용자로 테스트한 후 [이벤트 사용자 로그](https://www.braze.com/docs/ko/ko/user_guide/administer/global/workspace_settings/logs_and_alerts/event_user_log/)를 검토하세요. ### 커스텀 이벤트 로깅 설정 확인 {#verifying-custom-event-logging-setup} 커스텀 이벤트가 예상대로 나타나지 않는 경우, 개발자 팀이 올바른 앱 동작에 대해 커스텀 이벤트 로깅을 구현했는지 확인하세요. - 개발자 팀에 이벤트가 올바르게 기록되고 예상된 사용자 동작에서 트리거되는지 확인하도록 요청하세요. - 팀이 Braze 고객지원에 티켓을 열 때 [상세 로그](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/verbose_logging/) 및 관련 코드 스니펫을 포함하세요. - 앱이 Swift 또는 Android를 사용하는 경우, 개발자 팀은 [SDK 디버거 필수 조건](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/debugging/#prerequisites)을 사용하여 상세 로그를 생성할 수 있습니다. - 개발자 팀이 문제를 식별할 수 없는 경우, [Braze 고객지원 티켓](https://www.braze.com/docs/ko/ko/user_guide/administer/personal/braze_support/)을 열어주세요. # Braze SDK를 통한 구매 기록 Source: /docs/ko/developer_guide/analytics/logging_purchases/index.md # 구매 기록 > Braze SDK를 통해 인앱 구매를 기록하는 방법을 배우세요. 이를 통해 시간 경과에 따른 수익과 출처를 확인할 수 있습니다. 이를 통해 사용자 [생애주기 가치](https://www.braze.com/docs/ko/ko/developer_guide/analytics/#purchase-events--revenue-tracking)에 따라 세그먼트화할 수 있습니다. 커스텀 이벤트, 커스텀 속성 및 구매 이벤트를 사용하세요. **Note:** 목록에 없는 래퍼 SDK의 경우 관련 네이티브 Android 또는 Swift 메서드를 대신 사용하세요. 보고된 비-USD 통화는 보고된 날짜의 환율에 따라 Braze에서 USD로 표시됩니다. 통화 변환을 방지하려면 통화를 USD로 하드코딩하십시오. ## 구매 및 수익 기록하기 구매 및 수익을 기록하려면, 앱에서 성공적인 구매 후 `logPurchase()`을 호출하세요. 제품 식별자가 비어 있으면 구매가 Braze에 기록되지 않습니다. 표준 웹 SDK 구현의 경우 다음 방법을 사용할 수 있습니다: ```javascript braze.logPurchase(product_id, price, "USD", quantity); ``` 대신 Google Tag Manager를 사용하려면, **구매** 태그 유형을 사용하여 [`logPurchase` 메서드](https://js.appboycdn.com/web-sdk/latest/doc/modules/braze.html#logpurchase)를 호출할 수 있습니다. 이 태그를 사용하여 선택적으로 구매 속성정보를 포함한 Braze에 대한 구매를 추적합니다. 그렇게 하려면 1. **제품 ID** 및 **가격** 필드는 필수 입력 사항입니다. 2. **행 추가** 버튼을 사용하여 구매 속성을 추가합니다. ![Braze 작업 태그 구성 설정을 보여주는 대화상자. 포함된 설정은 "태그 유형", "외부 ID", "가격", "통화 코드", "수량", 및 "구매 속성정보"입니다.](https://www.braze.com/docs/ko/ko/assets/img/web-gtm/gtm-purchase.png?279d50ab49cb4e7f80e5fcd04cddf15e) ```java Braze.getInstance(context).logPurchase( String productId, String currencyCode, BigDecimal price, int quantity ); ``` ```kotlin Braze.getInstance(context).logPurchase( productId: String, currencyCode: String, price: BigDecimal, quantity: Int ) ``` ```swift AppDelegate.braze?.logPurchase(productID: "product_id", currency: "USD", price: price) ``` ```objc [AppDelegate.braze logPurchase:"product_id" currency:@"USD" price:price]; ``` ```javascript var properties = {}; properties["KEY"] = "VALUE"; BrazePlugin.logPurchase("PRODUCT_ID", 10, "USD", 5, properties); ``` ```dart braze.logPurchase(productId, currencyCode, price, quantity, properties: properties); ``` ```javascript Braze.logPurchase(productId, price, currencyCode, quantity, properties); ``` ```brightscript m.Braze.logPurchase("product_id", "currency_code", Double price, Integer quantity) ``` ```csharp AppboyBinding.LogPurchase("product_id", "currencyCode", price(decimal)); ``` **Warning:** `productID`는 최대 255자만 가질 수 있습니다. 또한, 제품 식별자가 비어 있으면 구매가 Braze에 기록되지 않습니다. ### 속성정보 추가 `Int`, `Double`, `String`, `Bool` 또는 `Date` 값으로 채워진 사전을 전달하여 구매에 대한 메타데이터를 추가할 수 있습니다. 표준 웹 SDK 구현의 경우 다음 방법을 사용할 수 있습니다: ```javascript braze.logPurchase(product_id, price, "USD", quantity, {key: "value"}); ``` 사이트가 표준 [이커머스 이벤트](https://developers.google.com/analytics/devguides/collection/ga4/ecommerce?client_type=gtm) 데이터 레이어 항목을 사용하여 Google Tag Manager에 구매를 기록하는 경우, **이커머스 구매** 태그 유형을 사용할 수 있습니다. 이 작업 유형은 `items`의 목록에 전송된 각 항목에 대한 별도의 '구매'를 Braze에 기록합니다. 구매 속성정보 목록에서 키를 지정하여 구매 속성정보로 포함할 추가 속성정보 이름을 지정할 수도 있습니다. Braze는 목록에 추가하는 모든 구매 자산에 대해 기록 중인 개별 `item` 내에서 확인합니다. 예를 들어, 다음 전자상거래 페이로드가 주어졌습니다: ``` items: [{ item_name: "5 L WIV ECO SAE 5W/30", item_id: "10801463", price: 24.65, item_brand: "EUROLUB", quantity: 1 }] ``` `item_brand` 및 `item_name`만 구매 속성정보로 전달하려면 구매 속성정보 테이블에 이 두 필드를 추가하면 됩니다. 속성을 제공하지 않으면 구매 속성이 전송되지 않습니다. [`logPurchase`](https://js.appboycdn.com/web-sdk/latest/doc/modules/braze.html#logpurchase) 호출로 전송되지 않습니다. ```java BrazeProperties purchaseProperties = new BrazeProperties(); purchaseProperties.addProperty("key", "value"); Braze.getInstance(context).logPurchase(..., purchaseProperties); ``` ```kotlin val purchaseProperties = BrazeProperties() purchaseProperties.addProperty("key", "value") Braze.getInstance(context).logPurchase(..., purchaseProperties) ``` ```swift let purchaseProperties = ["key": "value"] AppDelegate.braze?.logPurchase(productID: "product_id", currency: "USD", price: price, properties: purchaseProperties) ``` ```objc NSDictionary *purchaseProperties = @{@"key": @"value"}; [AppDelegate.braze logPurchase:@"product_id" currency:@"USD" price:price properties:purchaseProperties]; ``` ```javascript var properties = {}; properties["key"] = "value"; BrazePlugin.logPurchase("PRODUCT_ID", 10, "USD", 5, properties); ``` ```dart braze.logPurchase(productId, currencyCode, price, quantity, properties: {"key": "value"}); ``` ```javascript Braze.logPurchase(productId, price, currencyCode, quantity, { key: "value" }); ``` ```brightscript m.Braze.logPurchase("product_id", "currency_code", Double price, Integer quantity, {"stringPropKey" : "stringPropValue", "intPropKey" : Integer intPropValue}) ``` ```csharp Dictionary purchaseProperties = new Dictionary { { "key", "value" } }; AppboyBinding.LogPurchase("product_id", "currencyCode", price(decimal), purchaseProperties); ``` ### 수량 추가 기본값으로, `quantity`은 `1`로 설정됩니다. 그러나 고객이 단일 체크아웃에서 동일한 구매를 여러 번 하는 경우 구매에 수량을 추가할 수 있습니다. 수량을 추가하려면 `quantity`에 `Int` 값을 전달하십시오. ### REST API 사용 REST API를 사용하여 구매 내역을 기록할 수도 있습니다. 자세한 정보는 [사용자 데이터 엔드포인트](https://www.braze.com/docs/ko/ko/developer_guide/rest_api/user_data/#user-data)를 참조하십시오. ## 주문 기록 제품 수준 대신 주문 수준에서 구매를 기록하려면 주문 이름 또는 주문 카테고리를 `product_id` 으로 사용하면 됩니다. 자세한 내용은 [구매 개체 사양을](https://www.braze.com/docs/ko/ko/api/objects_filters/purchase_object/#product-id-naming-conventions) 참조하세요. ## 예약 키 다음 키는 예약되어 있으며 구매 속성으로 사용할 수 없습니다: - `time` - `product_id` - `quantity` - `event_name` - `price` - `currency` ## 지원되는 통화 Braze는 다음 통화 기호를 지원합니다. 제공하는 다른 통화 기호는 경고를 기록하고 구매가 Braze에 기록되지 않습니다. - `AED`, `AFN`, `ALL`, `AMD`, `ANG`, `AOA`, `ARS`, `AUD`, `AWG`, `AZN` - `BAM`, `BBD`, `BDT`, `BGN`, `BHD`, `BIF`, `BMD`, `BND`, `BOB`, `BRL` - `BSD`, `BTC`, `BTN`, `BWP`, `BYR`, `BZD` - `CAD`, `CDF`, `CHF`, `CLF`, `CLP`, `CNY`, `COP`, `CRC`, `CUC`, `CUP`, `CVE`, `CZK` - `DJF`, `DKK`, `DOP`, `DZD` - `EEK`, `EGP`, `ERN`, `ETB`, `EUR` - `FJD`, `FKP` - `GBP`, `GEL`, `GGP`, `GHS`, `GIP`, `GMD`, `GNF`, `GTQ`, `GYD` - `HKD`, `HNL`, `HRK`, `HTG`, `HUF` - `IDR`, `ILS`, `IMP`, `INR`, `IQD`, `IRR`, `ISK` - `JEP`, `JMD`, `JOD`, `JPY` - `KES`, `KGS`, `KHR`, `KMF`, `KPW`, `KRW`, `KWD`, `KYD`, `KZT` - `LAK`, `LBP`, `LKR`, `LRD`, `LSL`, `LTL`, `LVL`, `LYD` - `MAD`, `MDL`, `MGA`, `MKD`, `MMK`, `MNT`, `MOP`, `MRO`, `MTL`, `MUR`, `MVR`, `MWK`, `MXN`, `MYR`, `MZN` - `NAD`, `NGN`, `NIO`, `NOK`, `NPR`, `NZD` - `OMR` - `PAB`, `PEN`, `PGK`, `PHP`, `PKR`, `PLN`, `PYG` - `QAR` - `RON`, `RSD`, `RUB`, `RWF` - `SAR`, `SBD`, `SCR`, `SDG`, `SEK`, `SGD`, `SHP`, `SLL`, `SOS`, `SRD`, `STD`, `SVC`, `SYP`, `SZL` - `THB`, `TJS`, `TMT`, `TND`, `TOP`, `TRY`, `TTD`, `TWD`, `TZS` - `UAH`, `UGX`, `USD`, `UYU`, `UZS` - `VEF`, `VND`, `VUV` - `WST` - `XAF`, `XAG`, `XAU`, `XCD`, `XDR`, `XOF`, `XPD`, `XPF`, `XPT` - `YER` - `ZAR`, `ZMK`, `ZMW`, `ZWL` # Braze SDK를 통한 eCommerce 이벤트 기록 Source: /docs/ko/developer_guide/analytics/logging_ecommerce_events/index.md # eCommerce 이벤트 기록 {#log-ecommerce-events} > 타입이 지정된 이벤트 클래스와 `logEcommerceEvent`를 사용하여 Braze Android, Swift 및 Web SDK를 통해 [eCommerce 권장 이벤트](https://www.braze.com/docs/ko/ko/user_guide/data/activation/events/recommended_events/ecommerce_events/)를 기록하는 방법을 알아봅니다. 이벤트 속성정보 스키마, 플랫폼 기능 및 수집 유효성 검사에 대한 자세한 내용은 [권장 이벤트](https://www.braze.com/docs/ko/ko/user_guide/data/activation/events/recommended_events/) 및 [이벤트 유효성 검사 및 문제 해결](https://www.braze.com/docs/ko/ko/user_guide/data/activation/events/recommended_events/#event-validation-and-troubleshooting)을 참조하세요. **Note:** 목록에 없는 래퍼 SDK의 경우, 관련 네이티브 Android 또는 Swift 메서드를 대신 사용하세요. ## 이벤트 스키마 {#event-schemas} 6가지 eCommerce 권장 이벤트는 모든 플랫폼에서 주문 수준 스키마를 공유합니다. 각 이벤트 페이로드를 구축할 때 다음 속성정보 테이블을 사용하세요. 전체 유효성 검사 동작 및 REST API 예제가 포함된 정식 스키마는 [권장 이벤트](https://www.braze.com/docs/ko/ko/user_guide/data/activation/events/recommended_events/#event-schemas)를 참조하세요. 세분화, 캔버스 템플릿, 보고서 등 플랫폼 기능에 대한 자세한 내용은 [eCommerce 이벤트 사용 방법](https://www.braze.com/docs/ko/ko/user_guide/data/activation/events/recommended_events/ecommerce_events/)을 참조하세요. 사용자가 제품 상세 페이지를 볼 때 트리거합니다. **이벤트 속성정보** | 속성정보 이름 | 데이터 유형 | 필수 | 설명 | | ------------- | --------- | -------- | ----------- | | `product_id` | 문자열 | 예 | 고유 제품 식별자(예: SKU 또는 항목 ID). | | `product_name` | 문자열 | 예 | 제품 표시 이름. | | `variant_id` | 문자열 | 예 | 제품 배리언트 식별자(예: `shirt_medium_blue`). | | `image_url` | 문자열 | 아니요 | 제품 이미지 URL. | | `product_url` | 문자열 | 아니요 | 제품 페이지 URL(상세 정보 확인용). | | `price` | 플로트 | 예 | 조회 시점의 배리언트 단가. | | `currency` | 문자열 | 예 | 3자리 ISO 4217 코드(예: `USD` 또는 `EUR`). | | `source` | 문자열 | 예 | 이벤트가 발생한 소스(예: `web`, `ios` 또는 `android`). | | `type` | 문자열 배열 | 아니요 | 재입고 및 가격 인하 알림을 위한 Braze 카탈로그 트리거 기능을 사용하려면 필수입니다. 허용 값: `"price_drop"`, `"back_in_stock"`. | | `metadata` | 오브젝트 | 아니요 | 유연한 키-값 페어. 인식되는 하위 속성정보: `sku`(문자열). | {: .reset-td-br-1 .reset-td-br-2 .reset-td-br-3 .reset-td-br-4 aria-label="제품 조회 이벤트 속성정보" } 사용자의 장바구니 내용이 변경될 때마다 트리거합니다. 전체 장바구니 교체(`action`을 생략하거나 `replace`로 설정) 또는 증분 업데이트(`add` 또는 `remove`)를 사용합니다. **이벤트 속성정보** | 속성정보 | 데이터 유형 | 필수 | 설명 | | -------- | --------- | -------- | ----------- | | `cart_id` | 문자열 | 예 | 장바구니의 고유 식별자. 사용자의 장바구니 매핑을 위해 장바구니, 결제 및 주문 이벤트 간에 공유됩니다. | | `action` | 문자열 | 아니요 | `add`(수량 증가 또는 라인 추가), `remove`(수량 감소, `0`에서 라인 제거) 또는 `replace`(전체 장바구니 교체, `action` 생략과 동일). | | `total_value` | 플로트 | 조건부 | `action`이 생략되거나 `replace`일 때 필수. `action`이 `add` 또는 `remove`일 때 선택 사항. | | `subtotal_value` | 플로트 | 아니요 | 장바구니 소계 값(할인 적용 후, 세금/배송비 적용 전). | | `tax` | 플로트 | 아니요 | 장바구니에 적용된 총 세금. | | `shipping` | 플로트 | 아니요 | 장바구니의 총 배송비. | | `currency` | 문자열 | 예 | 3자리 ISO 4217 코드. | | `products` | 배열 | 예 | 이 업데이트의 라인 항목. 제품 속성정보 테이블을 참조하세요. | | `source` | 문자열 | 예 | 이벤트가 발생한 소스. | | `metadata` | 오브젝트 | 아니요 | 추가 이벤트 수준 데이터를 위한 유연한 키-값 페어. | {: .reset-td-br-1 .reset-td-br-2 .reset-td-br-3 .reset-td-br-4 aria-label="장바구니 업데이트 이벤트 속성정보" } **제품 속성정보(`products[]`)** | 속성정보 | 데이터 유형 | 필수 | 설명 | | -------- | --------- | -------- | ----------- | | `product_id` | 문자열 | 예 | 고유 제품 식별자. | | `product_name` | 문자열 | 예 | 제품 표시 이름. | | `variant_id` | 문자열 | 예 | 배리언트 식별자. | | `image_url` | 문자열 | 아니요 | 제품 이미지 URL. | | `product_url` | 문자열 | 아니요 | 제품 페이지 URL. | | `quantity` | 정수 | 예 | 전체 교체의 경우 이 라인의 장바구니 내 수량. `add` 또는 `remove`의 경우 추가하거나 제거할 수량. | | `price` | 플로트 | 예 | 배리언트 단가. | | `metadata` | 오브젝트 | 아니요 | 유연한 키-값 페어(예: `color` 또는 `size`). | {: .reset-td-br-1 .reset-td-br-2 .reset-td-br-3 .reset-td-br-4 aria-label="장바구니 업데이트 제품 속성정보" } 사용자가 결제 플로우를 시작할 때 트리거합니다. **이벤트 속성정보** | 속성정보 | 데이터 유형 | 필수 | 설명 | | -------- | --------- | -------- | ----------- | | `checkout_id` | 문자열 | 예 | 결제 세션의 고유 식별자. | | `cart_id` | 문자열 | 아니요 | 장바구니 식별자. 사용자의 장바구니 매핑을 위해 장바구니, 결제 및 주문 이벤트 간에 공유됩니다. | | `total_value` | 플로트 | 예 | 결제의 총 금액. | | `subtotal_value` | 플로트 | 아니요 | 소계 값(할인 적용 후, 세금/배송비 적용 전). | | `tax` | 플로트 | 아니요 | 결제에 적용된 총 세금. | | `shipping` | 플로트 | 아니요 | 총 배송비. | | `currency` | 문자열 | 예 | 3자리 ISO 4217 코드. | | `products` | 배열 | 예 | 결제 중인 항목. 제품 속성정보 테이블을 참조하세요. | | `source` | 문자열 | 예 | 이벤트가 발생한 소스. | | `metadata` | 오브젝트 | 아니요 | 유연한 키-값 페어. 인식되는 하위 속성정보: `checkout_url`(문자열). | {: .reset-td-br-1 .reset-td-br-2 .reset-td-br-3 .reset-td-br-4 aria-label="결제 시작 이벤트 속성정보" } **제품 속성정보(`products[]`)** | 속성정보 | 데이터 유형 | 필수 | 설명 | | -------- | --------- | -------- | ----------- | | `product_id` | 문자열 | 예 | 고유 제품 식별자. | | `product_name` | 문자열 | 예 | 제품 표시 이름. | | `variant_id` | 문자열 | 예 | 배리언트 식별자. | | `image_url` | 문자열 | 아니요 | 제품 이미지 URL. | | `product_url` | 문자열 | 아니요 | 제품 페이지 URL. | | `quantity` | 정수 | 예 | 장바구니 내 수량. | | `price` | 플로트 | 예 | 배리언트 단가. | | `metadata` | 오브젝트 | 아니요 | 유연한 키-값 페어(예: `color` 또는 `size`). | {: .reset-td-br-1 .reset-td-br-2 .reset-td-br-3 .reset-td-br-4 aria-label="결제 시작 제품 속성정보" } 주문이 성공적으로 완료되거나 결제가 확인될 때 트리거합니다. **이벤트 속성정보** | 속성정보 | 데이터 유형 | 필수 | 설명 | | -------- | --------- | -------- | ----------- | | `order_id` | 문자열 | 예 | 주문의 고유 식별자. | | `cart_id` | 문자열 | 아니요 | 장바구니 식별자. 사용자의 장바구니 매핑을 위해 장바구니, 결제 및 주문 이벤트 간에 공유됩니다. | | `total_value` | 플로트 | 예 | 주문의 총 금액. | | `subtotal_value` | 플로트 | 아니요 | 소계 값(할인 적용 후, 세금/배송비 적용 전). | | `tax` | 플로트 | 아니요 | 주문에 적용된 총 세금. | | `shipping` | 플로트 | 아니요 | 총 배송비. | | `currency` | 문자열 | 예 | 3자리 ISO 4217 코드. | | `total_discounts` | 플로트 | 아니요 | 주문에 적용된 총 할인 금액. | | `discounts` | 배열 | 아니요 | 적용된 할인의 상세 목록. 각 할인 오브젝트는 `code`(문자열), `amount`(플로트), `type`(문자열)을 지원합니다. | | `products` | 배열 | 예 | 주문 내 항목. 제품 속성정보 테이블을 참조하세요. | | `source` | 문자열 | 예 | 이벤트가 발생한 소스. | | `metadata` | 오브젝트 | 아니요 | 유연한 키-값 페어. 인식되는 하위 속성정보: `order_status_url`(문자열). | {: .reset-td-br-1 .reset-td-br-2 .reset-td-br-3 .reset-td-br-4 aria-label="주문 완료 이벤트 속성정보" } **제품 속성정보(`products[]`)** | 속성정보 | 데이터 유형 | 필수 | 설명 | | -------- | --------- | -------- | ----------- | | `product_id` | 문자열 | 예 | 고유 제품 식별자. | | `product_name` | 문자열 | 예 | 제품 표시 이름. | | `variant_id` | 문자열 | 예 | 배리언트 식별자. | | `image_url` | 문자열 | 아니요 | 제품 이미지 URL. | | `product_url` | 문자열 | 아니요 | 제품 페이지 URL. | | `quantity` | 정수 | 예 | 주문 내 수량. | | `price` | 플로트 | 예 | 배리언트 단가. | | `metadata` | 오브젝트 | 아니요 | 유연한 키-값 페어(예: `color` 또는 `size`). | {: .reset-td-br-1 .reset-td-br-2 .reset-td-br-3 .reset-td-br-4 aria-label="주문 완료 제품 속성정보" } 주문이 취소될 때 트리거합니다. **이벤트 속성정보** | 속성정보 | 데이터 유형 | 필수 | 설명 | | -------- | --------- | -------- | ----------- | | `order_id` | 문자열 | 예 | 주문의 고유 식별자. | | `total_value` | 플로트 | 예 | 취소되는 주문의 총 금액. 절대값(`0` 이상)을 전송하세요. Braze가 차감을 처리합니다. | | `subtotal_value` | 플로트 | 아니요 | 소계 값(할인 적용 후, 세금/배송비 적용 전). | | `tax` | 플로트 | 아니요 | 주문에 적용된 총 세금. | | `shipping` | 플로트 | 아니요 | 총 배송비. | | `currency` | 문자열 | 예 | 3자리 ISO 4217 코드. | | `total_discounts` | 플로트 | 아니요 | 주문에 적용된 총 할인 금액. | | `discounts` | 배열 | 아니요 | 적용된 할인의 상세 목록. | | `cancel_reason` | 문자열 | 예 | 주문이 취소된 사유. | | `products` | 배열 | 예 | 취소된 주문 내 항목. 제품 속성정보 테이블을 참조하세요. | | `source` | 문자열 | 예 | 이벤트가 발생한 소스. | | `metadata` | 오브젝트 | 아니요 | 유연한 키-값 페어. 인식되는 하위 속성정보: `order_status_url`(문자열). | {: .reset-td-br-1 .reset-td-br-2 .reset-td-br-3 .reset-td-br-4 aria-label="주문 취소 이벤트 속성정보" } **제품 속성정보(`products[]`)** | 속성정보 | 데이터 유형 | 필수 | 설명 | | -------- | --------- | -------- | ----------- | | `product_id` | 문자열 | 예 | 고유 제품 식별자. | | `product_name` | 문자열 | 예 | 제품 표시 이름. | | `variant_id` | 문자열 | 예 | 배리언트 식별자. | | `image_url` | 문자열 | 아니요 | 제품 이미지 URL. | | `product_url` | 문자열 | 아니요 | 제품 페이지 URL. | | `quantity` | 정수 | 예 | 주문 내 수량. | | `price` | 플로트 | 예 | 배리언트 단가. | | `metadata` | 오브젝트 | 아니요 | 유연한 키-값 페어(예: `color` 또는 `size`). | {: .reset-td-br-1 .reset-td-br-2 .reset-td-br-3 .reset-td-br-4 aria-label="주문 취소 제품 속성정보" } 전체 또는 부분 환불이 발생할 때 트리거합니다. 부분 환불의 경우 `total_value`를 원래 주문 총액이 아닌 환불 금액으로만 설정하세요. **이벤트 속성정보** | 속성정보 | 데이터 유형 | 필수 | 설명 | | -------- | --------- | -------- | ----------- | | `order_id` | 문자열 | 예 | 원래 주문의 고유 식별자. | | `total_value` | 플로트 | 예 | 환불의 총 금액. 절대값(`0` 이상)을 전송하세요. Braze가 매출 조정을 처리합니다. | | `currency` | 문자열 | 예 | 3자리 ISO 4217 코드. | | `total_discounts` | 플로트 | 아니요 | 원래 적용된 총 할인 금액. | | `discounts` | 배열 | 아니요 | 할인의 상세 목록. | | `products` | 배열 | 예 | 환불되는 항목. 제품 속성정보 테이블을 참조하세요. | | `source` | 문자열 | 예 | 이벤트가 발생한 소스. | | `metadata` | 오브젝트 | 아니요 | 유연한 키-값 페어. 인식되는 하위 속성정보: `order_status_url`(문자열). | {: .reset-td-br-1 .reset-td-br-2 .reset-td-br-3 .reset-td-br-4 aria-label="주문 환불 이벤트 속성정보" } **제품 속성정보(`products[]`)** | 속성정보 | 데이터 유형 | 필수 | 설명 | | -------- | --------- | -------- | ----------- | | `product_id` | 문자열 | 예 | 고유 제품 식별자. | | `product_name` | 문자열 | 예 | 제품 표시 이름. | | `variant_id` | 문자열 | 예 | 배리언트 식별자. | | `image_url` | 문자열 | 아니요 | 제품 이미지 URL. | | `product_url` | 문자열 | 아니요 | 제품 페이지 URL. | | `quantity` | 정수 | 예 | 환불된 수량. | | `price` | 플로트 | 예 | 배리언트 단가. | | `metadata` | 오브젝트 | 아니요 | 유연한 키-값 페어(예: `color` 또는 `size`). | {: .reset-td-br-1 .reset-td-br-2 .reset-td-br-3 .reset-td-br-4 aria-label="주문 환불 제품 속성정보" } ## Android Android SDK [42.3.0+](https://github.com/braze-inc/braze-android-sdk/releases/tag/v42.3.0)는 생성 시 클라이언트 측 유효성 검사와 `Braze.logEcommerceEvent` 호출 시 자동 `snake_case` 직렬화를 제공하는 타입이 지정된 eCommerce 이벤트 클래스를 제공합니다. | Android 클래스 | 이벤트 이름 | 참고 | | ------------- | ---------- | ----- | | `ProductViewedEvent` | `ecommerce.product_viewed` | 제품 필드를 `properties`의 최상위 수준으로 평탄화합니다(`products` 배열 없음). 이 클래스는 카탈로그 트리거를 위한 최상위 `type` 등록정보를 지원하지 않습니다. `type`이 필요한 경우 [`logCustomEvent`](#manual-logging-with-logcustomevent) 또는 REST API를 사용하세요. | | `CartUpdatedEvent` | `ecommerce.cart_updated` | `action` 등록정보에 `CartUpdatedAction`(`ADD`, `REMOVE`, `REPLACE`)을 사용합니다. | | `CheckoutStartedEvent` | `ecommerce.checkout_started` | | | `OrderPlacedEvent` | `ecommerce.order_placed` | 선택 사항인 `cartId`, `totalDiscounts`, `discounts`를 지원합니다. | {: .reset-td-br-1 .reset-td-br-2 .reset-td-br-3 aria-label="Android SDK eCommerce 이벤트 클래스" } **Important:** `ecommerce.order_cancelled` 및 `ecommerce.order_refunded`는 타입이 지정된 Android SDK 클래스로 제공되지 않습니다. [`logCustomEvent`](#manual-logging-with-logcustomevent) 또는 REST API를 사용하여 기록하세요. ### 공유 빌딩 블록 {#shared-building-blocks} - `EcommerceProduct`: 장바구니, 결제 및 주문 이벤트의 라인 항목입니다. - 필수: `productId`, `productName`, `variantId`, `price`, `quantity`(음수가 아닌 `Long`) - 선택 사항: `imageUrl`, `productUrl`, `metadata` - `BrazeProperties`: 이벤트 수준 또는 제품 수준의 `metadata`입니다. 키는 255자 이하의 비어 있지 않은 문자열이어야 하며 앞에 달러 기호($)가 올 수 없습니다. ### 클라이언트 측 유효성 검사 {#client-side-validation} 잘못된 페이로드는 이벤트 클래스를 생성할 때 `IllegalArgumentException`을 발생시키므로 이벤트가 대기줄에 추가되지 않습니다. 일반적인 규칙: | 필드 또는 규칙 | 유효성 검사 | | ------------ | ---------- | | 문자열 ID 및 이름(`product_id`, `product_name`, `variant_id`, `cart_id`, `checkout_id`, `order_id`, `source`, 선택 사항 URL) | 비어 있지 않으며 최대 255자 | | `price`, `total_value`, `total_discounts` | `0` 이상이어야 합니다 | | `currency` | 유효한 ISO 4217 코드(SDK에서 공백 제거 후 대문자로 변환) | | `products`(장바구니, 결제, 주문 이벤트) | 최소 하나의 `EcommerceProduct` | | `quantity`(제품당) | 음수가 아닌 정수 | {: .reset-td-br-1 .reset-td-br-2 aria-label="Android eCommerce 이벤트 클라이언트 측 유효성 검사 규칙" } 전송 시 직렬화된 등록정보가 SDK 크기 제한을 초과하면 `logEcommerceEvent`는 오류를 기록하고 이벤트를 전송하지 않습니다. ### 코드 예제 {#code-examples} ```kotlin import com.braze.Braze import com.braze.models.outgoing.BrazeProperties import com.braze.models.recommended.ecommerce.ProductViewedEvent val metadata = BrazeProperties() .addProperty("sku", "SS-R-101") .addProperty("category", "Apparel") val productViewedEvent = ProductViewedEvent( productId = "PROD101", productName = "Silk Scarf", variantId = "SCARF_RED_SILK", price = 150.00, currency = "EUR", source = "https://braze-fashion.eu", imageUrl = "https://braze-fashion.eu/images/scarf_red.jpg", productUrl = "https://braze-fashion.eu/products/scarf", metadata = metadata, ) Braze.getInstance(context).logEcommerceEvent(productViewedEvent) ``` `CartUpdatedAction`을 사용하여 `action`을 설정합니다: | 값 | 와이어 값 | 설명 | | ----- | ---------- | ----------- | | `CartUpdatedAction.ADD` | `add` | 수량을 늘리거나 라인을 추가합니다. | | `CartUpdatedAction.REMOVE` | `remove` | 수량을 줄이고, `0`에서 라인을 제거합니다. | | `CartUpdatedAction.REPLACE` | `replace` | 전체 장바구니를 교체합니다(기본값). | {: .reset-td-br-1 .reset-td-br-2 .reset-td-br-3 aria-label="ecommerce.cart_updated의 CartUpdatedAction 값" } ```kotlin import com.braze.Braze import com.braze.models.recommended.ecommerce.CartUpdatedAction import com.braze.models.recommended.ecommerce.CartUpdatedEvent import com.braze.models.recommended.ecommerce.EcommerceProduct val product = EcommerceProduct( productId = "SKU-RUN-4821", productName = "Ultraboost Running Shoe", variantId = "UB-BLK-11", price = 189.99, quantity = 1, ) val cartUpdatedEvent = CartUpdatedEvent( cartId = "cart_abc123", currency = "USD", source = "android", totalValue = 189.99, products = listOf(product), action = CartUpdatedAction.ADD, ) Braze.getInstance(context).logEcommerceEvent(cartUpdatedEvent) ``` ```kotlin import com.braze.Braze import com.braze.models.outgoing.BrazeProperties import com.braze.models.recommended.ecommerce.CheckoutStartedEvent import com.braze.models.recommended.ecommerce.EcommerceProduct val products = listOf( EcommerceProduct( productId = "SKU-RUN-4821", productName = "Ultraboost Running Shoe", variantId = "UB-BLK-11", price = 189.99, quantity = 1, ), ) val checkoutStartedEvent = CheckoutStartedEvent( checkoutId = "chk_88291", currency = "USD", source = "android", totalValue = 234.96, products = products, cartId = "cart_abc123", metadata = BrazeProperties().addProperty("checkout_url", "https://www.example.com/checkout/chk_88291"), ) Braze.getInstance(context).logEcommerceEvent(checkoutStartedEvent) ``` ```kotlin import com.braze.Braze import com.braze.models.outgoing.BrazeProperties import com.braze.models.recommended.ecommerce.EcommerceProduct import com.braze.models.recommended.ecommerce.OrderPlacedEvent val products = listOf( EcommerceProduct( productId = "SKU-RUN-4821", productName = "Ultraboost Running Shoe", variantId = "UB-BLK-11", price = 189.99, quantity = 1, ), ) val orderPlacedEvent = OrderPlacedEvent( orderId = "ord_77821", currency = "USD", source = "android", totalValue = 224.96, products = products, cartId = "cart_abc123", totalDiscounts = 10.0, discounts = listOf( mapOf("code" to "SPRING10", "amount" to 10.0, "type" to "percentage"), ), metadata = BrazeProperties().addProperty("order_status_url", "https://www.example.com/orders/ord_77821/status"), ) Braze.getInstance(context).logEcommerceEvent(orderPlacedEvent) ``` Braze는 이 이벤트에 대한 타입이 지정된 SDK 클래스를 제공하지 않습니다. `ecommerce.order_cancelled` 이벤트 스키마와 일치하는 페이로드로 `logCustomEvent`를 사용하세요. ```kotlin import com.braze.Braze import com.braze.models.outgoing.BrazeProperties import org.json.JSONArray import org.json.JSONObject val properties = BrazeProperties( JSONObject() .put("order_id", "ord_77821") .put("total_value", 224.96) .put("currency", "USD") .put("cancel_reason", "customer_request") .put("source", "android") .put( "products", JSONArray().put( JSONObject() .put("product_id", "SKU-RUN-4821") .put("product_name", "Ultraboost Running Shoe") .put("variant_id", "UB-BLK-11") .put("quantity", 1) .put("price", 189.99), ), ), ) Braze.getInstance(context).logCustomEvent("ecommerce.order_cancelled", properties) ``` Braze는 이 이벤트에 대한 타입이 지정된 SDK 클래스를 제공하지 않습니다. `ecommerce.order_refunded` 이벤트 스키마와 일치하는 페이로드로 `logCustomEvent`를 사용하세요. ```kotlin import com.braze.Braze import com.braze.models.outgoing.BrazeProperties import org.json.JSONArray import org.json.JSONObject val properties = BrazeProperties( JSONObject() .put("order_id", "ord_77821") .put("total_value", 189.99) .put("currency", "USD") .put("source", "android") .put( "products", JSONArray().put( JSONObject() .put("product_id", "SKU-RUN-4821") .put("product_name", "Ultraboost Running Shoe") .put("variant_id", "UB-BLK-11") .put("quantity", 1) .put("price", 189.99), ), ), ) Braze.getInstance(context).logCustomEvent("ecommerce.order_refunded", properties) ``` ```java import com.braze.Braze; import com.braze.models.outgoing.BrazeProperties; import com.braze.models.recommended.ecommerce.ProductViewedEvent; BrazeProperties metadata = new BrazeProperties() .addProperty("sku", "SS-R-101") .addProperty("category", "Apparel"); ProductViewedEvent productViewedEvent = new ProductViewedEvent( /* productId */ "PROD101", /* productName */ "Silk Scarf", /* variantId */ "SCARF_RED_SILK", /* price */ 150.00, /* currency */ "EUR", /* source */ "https://braze-fashion.eu", /* imageUrl */ "https://braze-fashion.eu/images/scarf_red.jpg", /* productUrl */ "https://braze-fashion.eu/products/scarf", /* metadata */ metadata ); Braze.getInstance(context).logEcommerceEvent(productViewedEvent); ``` `CartUpdatedAction`을 사용하여 `action`을 설정합니다: | 값 | 와이어 값 | 설명 | | ----- | ---------- | ----------- | | `CartUpdatedAction.ADD` | `add` | 수량을 늘리거나 라인을 추가합니다. | | `CartUpdatedAction.REMOVE` | `remove` | 수량을 줄이고, `0`에서 라인을 제거합니다. | | `CartUpdatedAction.REPLACE` | `replace` | 전체 장바구니를 교체합니다(기본값). | {: .reset-td-br-1 .reset-td-br-2 .reset-td-br-3 aria-label="ecommerce.cart_updated의 CartUpdatedAction 값" } ```java import com.braze.Braze; import com.braze.models.recommended.ecommerce.CartUpdatedAction; import com.braze.models.recommended.ecommerce.CartUpdatedEvent; import com.braze.models.recommended.ecommerce.EcommerceProduct; import java.util.Collections; EcommerceProduct product = new EcommerceProduct( /* productId */ "SKU-RUN-4821", /* productName */ "Ultraboost Running Shoe", /* variantId */ "UB-BLK-11", /* price */ 189.99, /* quantity */ 1 ); CartUpdatedEvent cartUpdatedEvent = new CartUpdatedEvent( /* cartId */ "cart_abc123", /* currency */ "USD", /* source */ "android", /* totalValue */ 189.99, /* products */ Collections.singletonList(product), /* metadata */ null, /* action */ CartUpdatedAction.ADD ); Braze.getInstance(context).logEcommerceEvent(cartUpdatedEvent); ``` ```java import com.braze.Braze; import com.braze.models.outgoing.BrazeProperties; import com.braze.models.recommended.ecommerce.CheckoutStartedEvent; import com.braze.models.recommended.ecommerce.EcommerceProduct; import java.util.Collections; EcommerceProduct product = new EcommerceProduct( /* productId */ "SKU-RUN-4821", /* productName */ "Ultraboost Running Shoe", /* variantId */ "UB-BLK-11", /* price */ 189.99, /* quantity */ 1 ); BrazeProperties metadata = new BrazeProperties() .addProperty("checkout_url", "https://www.example.com/checkout/chk_88291"); CheckoutStartedEvent checkoutStartedEvent = new CheckoutStartedEvent( /* checkoutId */ "chk_88291", /* currency */ "USD", /* source */ "android", /* totalValue */ 234.96, /* products */ Collections.singletonList(product), /* cartId */ "cart_abc123", /* metadata */ metadata ); Braze.getInstance(context).logEcommerceEvent(checkoutStartedEvent); ``` ```java import com.braze.Braze; import com.braze.models.outgoing.BrazeProperties; import com.braze.models.recommended.ecommerce.EcommerceProduct; import com.braze.models.recommended.ecommerce.OrderPlacedEvent; import java.util.Collections; EcommerceProduct product = new EcommerceProduct( /* productId */ "SKU-RUN-4821", /* productName */ "Ultraboost Running Shoe", /* variantId */ "UB-BLK-11", /* price */ 189.99, /* quantity */ 1 ); BrazeProperties metadata = new BrazeProperties() .addProperty("order_status_url", "https://www.example.com/orders/ord_77821/status"); OrderPlacedEvent orderPlacedEvent = new OrderPlacedEvent( /* orderId */ "ord_77821", /* currency */ "USD", /* source */ "android", /* totalValue */ 224.96, /* products */ Collections.singletonList(product), /* cartId */ "cart_abc123", /* totalDiscounts */ 10.0, /* discounts */ null, /* metadata */ metadata ); Braze.getInstance(context).logEcommerceEvent(orderPlacedEvent); ``` Braze는 이 이벤트에 대한 타입이 지정된 SDK 클래스를 제공하지 않습니다. `ecommerce.order_cancelled` 이벤트 스키마와 일치하는 페이로드로 `logCustomEvent`를 사용하세요. ```java import com.braze.Braze; import com.braze.models.outgoing.BrazeProperties; import org.json.JSONArray; import org.json.JSONObject; Braze.getInstance(context).logCustomEvent( "ecommerce.order_cancelled", new BrazeProperties(new JSONObject() .put("order_id", "ord_77821") .put("total_value", 224.96) .put("currency", "USD") .put("cancel_reason", "customer_request") .put("source", "android") .put("products", new JSONArray() .put(new JSONObject() .put("product_id", "SKU-RUN-4821") .put("product_name", "Ultraboost Running Shoe") .put("variant_id", "UB-BLK-11") .put("quantity", 1) .put("price", 189.99))))); ``` Braze는 이 이벤트에 대한 타입이 지정된 SDK 클래스를 제공하지 않습니다. `ecommerce.order_refunded` 이벤트 스키마와 일치하는 페이로드로 `logCustomEvent`를 사용하세요. ```java import com.braze.Braze; import com.braze.models.outgoing.BrazeProperties; import org.json.JSONArray; import org.json.JSONObject; Braze.getInstance(context).logCustomEvent( "ecommerce.order_refunded", new BrazeProperties(new JSONObject() .put("order_id", "ord_77821") .put("total_value", 189.99) .put("currency", "USD") .put("source", "android") .put("products", new JSONArray() .put(new JSONObject() .put("product_id", "SKU-RUN-4821") .put("product_name", "Ultraboost Running Shoe") .put("variant_id", "UB-BLK-11") .put("quantity", 1) .put("price", 189.99))))); ``` ## iOS Swift SDK는 타입이 지정된 eCommerce 이벤트 클래스(`ProductViewedEvent`, `CartUpdatedEvent`, `CheckoutStartedEvent`, `OrderPlacedEvent`)를 제공하며, 이를 빌드하여 `logEcommerceEvent`에 전달합니다. 장바구니, 결제 및 주문 이벤트의 제품에는 `ProductLineItem`을 사용합니다. 각 이니셜라이저는 throwing이므로 `try?`로 래핑하고 생성이 성공한 경우에만 이벤트를 기록합니다. 이 기능은 Swift SDK 버전 `15.0.0` 이상에서 사용할 수 있습니다. `ecommerce.order_cancelled` 및 `ecommerce.order_refunded`는 타입이 지정된 Swift SDK 클래스로 제공되지 않습니다. `logCustomEvent`를 사용하여 기록하세요. ### 코드 예제 ```swift if let productViewedEvent = try? Braze.Ecommerce.ProductViewedEvent( productId: "4111176", productName: "Torchie runners", variantId: "4111176700", imageUrl: "https://braze-apparel.com/images/products/large/torchie-runners.jpg", productUrl: "https://braze-apparel.com/footwear-categories/sneakers/braze-orange-torchie-runners/", price: 85, currency: "GBP", source: "https://braze-apparel.com/", metadata: [ "sku": "", "color": "ORANGE", "size": "6", "brand": "Braze" ], typeIdentifiers: ["price_drop", "back_in_stock"] ) { AppDelegate.braze?.logEcommerceEvent(productViewedEvent) } ``` ```swift if let productLine = try? Braze.Ecommerce.ProductLineItem( productId: "8266836345064", productName: "Classic T-Shirt", variantId: "44610569208040", imageUrl: "https://braze-apparel.com/images/tshirt-blue-medium.jpg", productUrl: "https://braze-apparel.com/products/classic-tshirt?variant=44610569208040", quantity: 2, price: 99.99, metadata: [ "sku": "TSH-BLU-M", "color": "BLUE", "size": "Medium", "brand": "Braze" ] ), let cartUpdatedEvent = try? Braze.Ecommerce.CartUpdatedEvent( cartId: "cart_12345", totalValue: 199.98, currency: "USD", products: [productLine], source: "https://braze-apparel.com", metadata: [:] ) { AppDelegate.braze?.logEcommerceEvent(cartUpdatedEvent) } ``` ```swift if let productLine = try? Braze.Ecommerce.ProductLineItem( productId: "632910392", productName: "Wireless Headphones", variantId: "808950810", quantity: 1, price: 199.98, metadata: [ "sku": "WH-BLK-PRO", "color": "Black", "brand": "BrazeAudio" ] ), let checkoutStartedEvent = try? Braze.Ecommerce.CheckoutStartedEvent( checkoutId: "checkout_abc123", cartId: "cart_12345", totalValue: 199.98, currency: "USD", products: [productLine], source: "https://braze-audio.com", metadata: [ "checkout_url": "https://checkout.braze-audio.com/abc123" ] ) { AppDelegate.braze?.logEcommerceEvent(checkoutStartedEvent) } ``` ```swift if let productLine = try? Braze.Ecommerce.ProductLineItem( productId: "632910392", productName: "Wireless Headphones", variantId: "808950810", quantity: 1, price: 199.98, metadata: [ "sku": "WH-BLK-PRO", "color": "Black", "brand": "BrazeAudio" ] ), let orderPlacedEvent = try? Braze.Ecommerce.OrderPlacedEvent( orderId: "order_67890", cartId: "cart_12345", totalValue: 189.98, currency: "USD", totalDiscounts: 10.00, discounts: [.structured(code: "SAVE10", amount: 10.00, type: "fixed")], products: [productLine], source: "https://braze-audio.com", metadata: [ "order_status_url": "https://braze-audio.com/orders/67890/status", "order_number": "ORD-2024-001234", "tags": ["electronics", "audio"], "referring_site": "https://www.e-referrals.com", "payment_gateway_names": ["tap2pay", "dotcash"] ] ) { AppDelegate.braze?.logEcommerceEvent(orderPlacedEvent) } ``` Braze는 이 이벤트에 대한 타입이 지정된 SDK 클래스를 제공하지 않습니다. `ecommerce.order_cancelled` 이벤트 스키마와 일치하는 페이로드로 `logCustomEvent`를 사용하세요. ```swift let discounts: [[String: Any]] = [ [ "code": "SAVE10", "amount": 10.00 ] ] let products: [[String: Any]] = [ [ "product_id": "632910392", "product_name": "Wireless Headphones", "variant_id": "808950810", "quantity": 1, "price": 199.98, "metadata": [ "sku": "WH-BLK-PRO", "color": "Black", "brand": "BrazeAudio" ] ] ] let properties: [String: Any] = [ "order_id": "order_67890", "cancel_reason": "customer changed mind", "total_value": 189.98, "subtotal_value": 169.98, "tax": 14.40, "shipping": 5.60, "currency": "USD", "total_discounts": 10.00, "discounts": discounts, "products": products, "source": "https://braze-audio.com", "metadata": [ "order_status_url": "https://braze-audio.com/orders/67890/status", "order_number": "ORD-2024-001234", "tags": ["cancelled", "customer_request"] ] ] AppDelegate.braze?.logCustomEvent(name: "ecommerce.order_cancelled", properties: properties) ``` Braze는 이 이벤트에 대한 타입이 지정된 SDK 클래스를 제공하지 않습니다. `ecommerce.order_refunded` 이벤트 스키마와 일치하는 페이로드로 `logCustomEvent`를 사용하세요. ```swift let discounts: [[String: Any]] = [ [ "code": "SAVE5", "amount": 5.00 ] ] let products: [[String: Any]] = [ [ "product_id": "632910392", "product_name": "Wireless Headphones", "variant_id": "808950810", "quantity": 1, "price": 99.99, "metadata": [ "sku": "WH-BLK-PRO", "color": "Black", "brand": "BrazeAudio" ] ] ] let properties: [String: Any] = [ "order_id": "order_67890", "total_value": 99.99, "currency": "USD", "total_discounts": 5.00, "discounts": discounts, "products": products, "source": "https://braze-audio.com", "metadata": [ "order_status_url": "https://braze-audio.com/orders/67890/status", "order_note": "Customer requested refund due to defective item", "order_number": "ORD-2024-001234", "tags": ["refund", "defective"] ] ] AppDelegate.braze?.logCustomEvent(name: "ecommerce.order_refunded", properties: properties) ``` ## Web Web SDK [6.8.0+](https://github.com/braze-inc/braze-web-sdk)에서는 이벤트 `name`과 `properties`를 사용하여 `logEcommerceEvent`를 호출합니다. 이전 SDK 버전에서는 이벤트 이름과 속성정보 오브젝트를 사용하여 `logCustomEvent`를 호출합니다. `ecommerce.order_cancelled` 및 `ecommerce.order_refunded`는 `logCustomEvent`를 사용합니다. ### 코드 예제 최신 SDK 버전에서는 `logEcommerceEvent()`를 호출합니다: ```javascript braze.logEcommerceEvent({ "name": "ecommerce.product_viewed", "properties": { "product_id": "4111176", "product_name": "Torchie runners", "variant_id": "4111176700", "image_url": "https://braze-apparel.com/images/products/large/torchie-runners.jpg", "product_url": "https://braze-apparel.com/footwear-categories/sneakers/braze-orange-torchie-runners/", "price": 85, "currency": "GBP", "source": "https://braze-apparel.com/", "metadata": { "sku": "", "color": "ORANGE", "size": "6", "brand": "Braze" } } }); ``` 이전 SDK 버전에서는 `logCustomEvent()`를 호출합니다: ```javascript braze.logCustomEvent("ecommerce.product_viewed", { "product_id": "4111176", "product_name": "Torchie runners", "variant_id": "4111176700", "image_url": "https://braze-apparel.com/images/products/large/torchie-runners.jpg", "product_url": "https://braze-apparel.com/footwear-categories/sneakers/braze-orange-torchie-runners/", "price": 85, "currency": "GBP", "source": "https://braze-apparel.com/", "metadata": { "sku": "", "color": "ORANGE", "size": "6", "brand": "Braze" } }); ``` 최신 SDK 버전에서는 `logEcommerceEvent()`를 호출합니다: ```javascript braze.logEcommerceEvent({ "name": "ecommerce.cart_updated", "properties": { "cart_id": "cart_12345", "currency": "USD", "total_value": 199.98, "products": [ { "product_id": "8266836345064", "product_name": "Classic T-Shirt", "variant_id": "44610569208040", "image_url": "https://braze-apparel.com/images/tshirt-blue-medium.jpg", "product_url": "https://braze-apparel.com/products/classic-tshirt?variant=44610569208040", "quantity": 2, "price": 99.99, "metadata": { "sku": "TSH-BLU-M", "color": "BLUE", "size": "Medium", "brand": "Braze" } } ], "source": "https://braze-apparel.com", "metadata": {} } }); ``` 이전 SDK 버전에서는 `logCustomEvent()`를 호출합니다: ```javascript braze.logCustomEvent("ecommerce.cart_updated", { "cart_id": "cart_12345", "currency": "USD", "total_value": 199.98, "subtotal_value": 179.98, "tax": 15.00, "shipping": 5.00, "products": [ { "product_id": "8266836345064", "product_name": "Classic T-Shirt", "variant_id": "44610569208040", "image_url": "https://braze-apparel.com/images/tshirt-blue-medium.jpg", "product_url": "https://braze-apparel.com/products/classic-tshirt?variant=44610569208040", "quantity": 2, "price": 99.99, "metadata": { "sku": "TSH-BLU-M", "color": "BLUE", "size": "Medium", "brand": "Braze" } } ], "source": "https://braze-apparel.com", "metadata": {} }); ``` 최신 SDK 버전에서는 `logEcommerceEvent()`를 호출합니다: ```javascript braze.logEcommerceEvent({ "name": "ecommerce.checkout_started", "properties": { "checkout_id": "checkout_abc123", "cart_id": "cart_12345", "total_value": 199.98, "currency": "USD", "products": [ { "product_id": "632910392", "product_name": "Wireless Headphones", "variant_id": "808950810", "quantity": 1, "price": 199.98, "metadata": { "sku": "WH-BLK-PRO", "color": "Black", "brand": "BrazeAudio" } } ], "source": "https://braze-audio.com", "metadata": { "checkout_url": "https://checkout.braze-audio.com/abc123" } } }); ``` 이전 SDK 버전에서는 `logCustomEvent()`를 호출합니다: ```javascript braze.logCustomEvent("ecommerce.checkout_started", { "checkout_id": "checkout_abc123", "cart_id": "cart_12345", "total_value": 199.98, "subtotal_value": 179.98, "tax": 15.00, "shipping": 5.00, "currency": "USD", "products": [ { "product_id": "632910392", "product_name": "Wireless Headphones", "variant_id": "808950810", "quantity": 1, "price": 199.98, "metadata": { "sku": "WH-BLK-PRO", "color": "Black", "brand": "BrazeAudio" } } ], "source": "https://braze-audio.com", "metadata": { "checkout_url": "https://checkout.braze-audio.com/abc123" } }); ``` 최신 SDK 버전에서는 `logEcommerceEvent()`를 호출합니다: ```javascript braze.logEcommerceEvent({ "name": "ecommerce.order_placed", "properties": { "order_id": "order_67890", "cart_id": "cart_12345", "total_value": 189.98, "currency": "USD", "total_discounts": 10.00, "discounts": [ { "code": "SAVE10", "amount": 10.00 } ], "products": [ { "product_id": "632910392", "product_name": "Wireless Headphones", "variant_id": "808950810", "quantity": 1, "price": 199.98, "metadata": { "sku": "WH-BLK-PRO", "color": "Black", "brand": "BrazeAudio" } } ], "source": "https://braze-audio.com", "metadata": { "order_status_url": "https://braze-audio.com/orders/67890/status", "order_number": "ORD-2024-001234", "tags": ["electronics", "audio"], "referring_site": "https://www.e-referrals.com", "payment_gateway_names": ["tap2pay", "dotcash"] } } }); ``` 이전 SDK 버전에서는 `logCustomEvent()`를 호출합니다: ```javascript braze.logCustomEvent("ecommerce.order_placed", { "order_id": "order_67890", "cart_id": "cart_12345", "total_value": 189.98, "subtotal_value": 169.98, "tax": 14.40, "shipping": 5.60, "currency": "USD", "total_discounts": 10.00, "discounts": [ { "code": "SAVE10", "amount": 10.00 } ], "products": [ { "product_id": "632910392", "product_name": "Wireless Headphones", "variant_id": "808950810", "quantity": 1, "price": 199.98, "metadata": { "sku": "WH-BLK-PRO", "color": "Black", "brand": "BrazeAudio" } } ], "source": "https://braze-audio.com", "metadata": { "order_status_url": "https://braze-audio.com/orders/67890/status", "order_number": "ORD-2024-001234", "tags": ["electronics", "audio"], "referring_site": "https://www.e-referrals.com", "payment_gateway_names": ["tap2pay", "dotcash"] } }); ``` ```javascript braze.logCustomEvent("ecommerce.order_cancelled", { "order_id": "order_67890", "cancel_reason": "customer changed mind", "total_value": 189.98, "subtotal_value": 169.98, "tax": 14.40, "shipping": 5.60, "currency": "USD", "total_discounts": 10.00, "discounts": [ { "code": "SAVE10", "amount": 10.00 } ], "products": [ { "product_id": "632910392", "product_name": "Wireless Headphones", "variant_id": "808950810", "quantity": 1, "price": 199.98, "metadata": { "sku": "WH-BLK-PRO", "color": "Black", "brand": "BrazeAudio" } } ], "source": "https://braze-audio.com", "metadata": { "order_status_url": "https://braze-audio.com/orders/67890/status", "order_number": "ORD-2024-001234", "tags": ["cancelled", "customer_request"] } }); ``` ```javascript braze.logCustomEvent("ecommerce.order_refunded", { "order_id": "order_67890", "total_value": 99.99, "currency": "USD", "total_discounts": 5.00, "discounts": [ { "code": "SAVE5", "amount": 5.00 } ], "products": [ { "product_id": "632910392", "product_name": "Wireless Headphones", "variant_id": "808950810", "quantity": 1, "price": 99.99, "metadata": { "sku": "WH-BLK-PRO", "color": "Black", "brand": "BrazeAudio" } } ], "source": "https://braze-audio.com", "metadata": { "order_status_url": "https://braze-audio.com/orders/67890/status", "order_note": "Customer requested refund due to defective item", "order_number": "ORD-2024-001234", "tags": ["refund", "defective"] } }); ``` ## `logCustomEvent`를 사용한 수동 기록 {#manual-logging-with-logcustomevent} 권장 이벤트를 수동으로 기록하려면 정확한 이벤트 이름(예: `ecommerce.product_viewed`)과 수동으로 구성한 `BrazeProperties` 또는 `JSONObject` 페이로드를 사용하여 `logCustomEvent`를 호출합니다. SDK는 수동 호출에 대해 권장 이벤트 스키마를 유효성 검사하지 않습니다. Braze는 수집 중에 이러한 페이로드를 유효성 검사합니다: - 유효한 페이로드는 전체 후처리가 적용된 권장 이벤트로 처리됩니다. - 잘못된 페이로드(필수 필드 누락, 잘못된 유형, 추가 최상위 등록정보)는 수집 후 삭제됩니다. 실패 내역은 워크스페이스 SDK 처리 로그와 [실패 요약 이메일](https://www.braze.com/docs/ko/ko/user_guide/data/activation/events/recommended_events/#find-failures)에 표시됩니다. 앱에서 잘못된 데이터가 전송되기 전에 포착할 수 있도록 가능하면 `logEcommerceEvent`를 사용하세요. 일반적인 `logCustomEvent` 사용법은 [커스텀 이벤트 기록](https://www.braze.com/docs/ko/ko/developer_guide/analytics/logging_events/?tab=android)을 참조하세요. # Braze SDK를 통해 콘텐츠 카드 데이터를 기록하십시오 Source: /docs/ko/developer_guide/analytics/logging_channel_data/content_cards/index.md # 콘텐츠 카드 데이터를 기록하십시오 > When building a custom UI for Content Cards, you must manually log analytics like impressions, clicks, and dismissals, as this is only handled automatically for default card models. Logging these events is a standard part of a Content Card integration and is essential for accurate campaign reporting and billing. To do this, populate your custom UI with data from the Braze data models and then manually log the events. Once you understand how to log analytics, you can see common ways Braze customers [create custom Content Cards](https://www.braze.com/docs/ko/ko/developer_guide/content_cards/creating_cards/). ## Logging analytics When implementing your custom Content Cards, you can parse the Content Card objects and extract their payload data such as `title`, `cardDescription`, and `imageUrl`. Then, you can use the resulting model data to populate your custom UI. To obtain the Content Card data models, subscribe to Content Card updates. There are two properties to pay particular attention to: * **`id`**: Represents the Content Card ID string. This is the unique identifier used to log analytics from custom Content Cards. * **`extras`**: Encompasses all the key-value pairs from the Braze dashboard. All properties outside of `id` and `extras` are optional to parse for custom Content Cards. For more information on the data model, see each platform's integration article: [Android](https://www.braze.com/docs/ko/ko/developer_guide/content_cards/?sdktab=android), [iOS](https://www.braze.com/docs/ko/ko/developer_guide/content_cards/?sdktab=swift), [Web](https://www.braze.com/docs/ko/ko/developer_guide/content_cards/?sdktab=web). Register a callback function to subscribe for updates when cards are refreshed. ```javascript import * as braze from "@braze/web-sdk"; braze.subscribeToContentCardsUpdates((updates) => { const cards = updates.cards; // For example: cards.forEach(card => { if (card.isControl) { // Do not display the control card, but remember to call `logContentCardImpressions([card])` } else if (card instanceof braze.ClassicCard || card instanceof braze.CaptionedImage) { // Use `card.title`, `card.imageUrl`, etc. } else if (card instanceof braze.ImageOnly) { // Use `card.imageUrl`, etc. } }) }); braze.openSession(); ``` **Note:** Content Cards will only refresh on session start if a subscribe request is called before `openSession()`. You can always choose to [manually refresh the feed](https://www.braze.com/docs/ko/ko/developer_guide/content_cards/customizing_cards/feed/) as well. ### Step 1: Create a private subscriber variable To subscribe to card updates, first declare a private variable in your custom class to hold your subscriber: ```java // subscriber variable private IEventSubscriber mContentCardsUpdatedSubscriber; ``` ### Step 2: Subscribe to updates Next, add the following code to subscribe to Content Card updates from Braze, typically inside of your custom Content Cards activity's `Activity.onCreate()`: ```java // Remove the previous subscriber before rebuilding a new one with our new activity. Braze.getInstance(context).removeSingleSubscription(mContentCardsUpdatedSubscriber, ContentCardsUpdatedEvent.class); mContentCardsUpdatedSubscriber = new IEventSubscriber() { @Override public void trigger(ContentCardsUpdatedEvent event) { // List of all Content Cards List allCards = event.getAllCards(); // Your logic below } }; Braze.getInstance(context).subscribeToContentCardsUpdates(mContentCardsUpdatedSubscriber); Braze.getInstance(context).requestContentCardsRefresh(); ``` ### Step 3: Unsubscribe We also recommend unsubscribing when your custom activity moves out of view. Add the following code to your activity's `onDestroy()` lifecycle method: ```java Braze.getInstance(context).removeSingleSubscription(mContentCardsUpdatedSubscriber, ContentCardsUpdatedEvent.class); ``` ### Step 1: Create a private subscriber variable To subscribe to card updates, first declare a private variable in your custom class to hold your subscriber: ```kotlin private var contentCardsUpdatedSubscriber: IEventSubscriber? = null ``` ### Step 2: Subscribe to updates Next, add the following code to subscribe to Content Card updates from Braze, typically inside of your custom Content Cards activity's `Activity.onCreate()`: ```kotlin // Remove the previous subscriber before rebuilding a new one with our new activity. Braze.getInstance(context).removeSingleSubscription(contentCardsUpdatedSubscriber, ContentCardsUpdatedEvent::class.java) contentCardsUpdatedSubscriber = IEventSubscriber { event -> // List of all Content Cards val allCards = event.allCards // Your logic below } Braze.getInstance(context).subscribeToContentCardsUpdates(contentCardsUpdatedSubscriber) Braze.getInstance(context).requestContentCardsRefresh(true) ``` ### Step 3: Unsubscribe We also recommend unsubscribing when your custom activity moves out of view. Add the following code to your activity's `onDestroy()` lifecycle method: ```kotlin Braze.getInstance(context).removeSingleSubscription(contentCardsUpdatedSubscriber, ContentCardsUpdatedEvent::class.java) ``` To access the Content Cards data model, call [`contentCards.cards`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/contentcards-swift.class/cards) on your `braze` instance. ```swift let cards: [Braze.ContentCard] = AppDelegate.braze?.contentCards.cards ``` Additionally, you can also maintain a subscription to observe for changes in your Content Cards. You can do so in one of two ways: 1. Maintaining a cancellable; or 2. Maintaining an `AsyncStream`. ### Cancellable ```swift // This subscription is maintained through a Braze cancellable, which will observe for changes until the subscription is cancelled. // You must keep a strong reference to the cancellable to keep the subscription active. // The subscription is canceled either when the cancellable is deinitialized or when you call its `.cancel()` method. let cancellable = AppDelegate.braze?.contentCards.subscribeToUpdates { [weak self] contentCards in // Implement your completion handler to respond to updates in `contentCards`. } ``` ### AsyncStream ```swift let stream: AsyncStream<[Braze.ContentCard]> = AppDelegate.braze?.contentCards.cardsStream ``` ```objc NSArray *contentCards = AppDelegate.braze.contentCards.cards; ``` Additionally, if you wish to maintain a subscription to your content cards, you can call [`subscribeToUpdates`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/contentcards-swift.class/subscribetoupdates(_:)): ```objc // This subscription is maintained through Braze cancellable, which will continue to observe for changes until the subscription is cancelled. BRZCancellable *cancellable = [self.braze.contentCards subscribeToUpdates:^(NSArray *contentCards) { // Implement your completion handler to respond to updates in `contentCards`. }]; ``` To get the Content Card data, use the `getContentCards` method: ```javascript import Braze from "@braze/react-native-sdk"; const cards = await Braze.getContentCards(); ``` To listen for updates, subscribe to Content Card update events: ```javascript const subscription = Braze.addListener(Braze.Events.CONTENT_CARDS_UPDATED, (update) => { const cards = update.cards; cards.forEach(card => { if (card.isControl) { // Do not display the control card, but remember to log an impression } else { // Use card.title, card.cardDescription, card.image, etc. } }); }); ``` To request a manual refresh of Content Cards from Braze servers: ```javascript Braze.requestContentCardsRefresh(); ``` To get cached Content Cards without a network request: ```javascript const cachedCards = await Braze.getCachedContentCards(); ``` ## Logging events Logging valuable metrics like impressions, clicks, and dismissals is quick and simple. Set a custom click listener to manually handle these analytics. Log impression events when cards are viewed by users using [`logContentCardImpressions`](https://js.appboycdn.com/web-sdk/latest/doc/modules/braze.html#logcontentcardimpressions): ```javascript import * as braze from "@braze/web-sdk"; braze.logContentCardImpressions([card1, card2, card3]); ``` Log card click events when users interact with a card using [`logContentCardClick`](https://js.appboycdn.com/web-sdk/latest/doc/modules/braze.html#logcontentcardclick): ```javascript import * as braze from "@braze/web-sdk"; braze.logContentCardClick(card); ``` The [`BrazeManager`](https://github.com/braze-inc/braze-growth-shares-android-demo-app/blob/main/app/src/main/java/com/braze/advancedsamples/BrazeManager.kt) can reference Braze SDK dependencies such as the Content Card objects array list to get the [`Card`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.cards/-card/index.html) to call the Braze logging methods. Use the `ContentCardable` base class to easily reference and provide data to the `BrazeManager`. To log an impression or click on a card, call [`Card.logClick()`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.cards/-card/log-click.html) or [`Card.logImpression()`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.cards/-card/log-impression.html) respectively. You can manually log or set a Content Card as "dismissed" to Braze for a particular card with [`isDismissed`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.cards/-card/is-dismissed.html). If a card is already marked as dismissed, it cannot be marked as dismissed again. To create a custom click listener, create a class that implements [`IContentCardsActionListener`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.ui.contentcards.listeners/-i-content-cards-action-listener/index.html) and register it with [`BrazeContentCardsManager`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.ui.contentcards.managers/-braze-content-cards-manager/index.html). Implement the [`onContentCardClicked()`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.ui.contentcards.listeners/-i-content-cards-action-listener/on-content-card-clicked.html) method, which will be called when the user clicks a Content Card. Then, instruct Braze to use your Content Card click listener. For example: ```java BrazeContentCardsManager.getInstance().setContentCardsActionListener(new IContentCardsActionListener() { @Override public boolean onContentCardClicked(Context context, Card card, IAction cardAction) { return false; } @Override public void onContentCardDismissed(Context context, Card card) { } }); ``` For example: ```kotlin BrazeContentCardsManager.getInstance().contentCardsActionListener = object : IContentCardsActionListener { override fun onContentCardClicked(context: Context, card: Card, cardAction: IAction): Boolean { return false } override fun onContentCardDismissed(context: Context, card: Card) { } } ``` **Important:** To handle control variant Content Cards in your custom UI, pass in your [`com.braze.models.cards.Card`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.cards/-card/index.html) object, then call the `logImpression` method as you would with any other Content Card type. The object will implicitly log a control impression to inform our analytics of when a user would have seen the control card. Implement the [`BrazeContentCardUIViewControllerDelegate`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazeui/brazecontentcarduiviewcontrollerdelegate) protocol and set your delegate object as the `delegate` property of your `BrazeContentCardUI.ViewController`. This delegate will handle passing the data of your custom object back to Braze to be logged. For an example, see [Content Cards UI tutorial](https://braze-inc.github.io/braze-swift-sdk/tutorials/braze/c2-contentcardsui/). ```swift // Set the delegate when creating the Content Cards controller contentCardsController.delegate = delegate // Method to implement in delegate func contentCard( _ controller: BrazeContentCardUI.ViewController, shouldProcess clickAction: Braze.ContentCard.ClickAction, card: Braze.ContentCard ) -> Bool { // Intercept the content card click action here. return true } ``` ```objc // Set the delegate when creating the Content Cards controller contentCardsController.delegate = delegate; // Method to implement in delegate - (BOOL)contentCardController:(BRZContentCardUIViewController *)controller shouldProcess:(NSURL *)url card:(BRZContentCardRaw *)card { // Intercept the content card click action here. return YES; } ``` **Important:** To handle control variant Content Cards in your custom UI, pass in your [`Braze.ContentCard.Control`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/contentcard/control(_:)) object, then call the `logImpression` method as you would with any other Content Card type. The object will implicitly log a control impression to inform our analytics of when a user would have seen the control card. Log impression events when cards are viewed by users: ```javascript Braze.logContentCardImpression(card.id); ``` Log card click events when users interact with a card: ```javascript Braze.logContentCardClicked(card.id); ``` Log dismissal events when a user dismisses a card: ```javascript Braze.logContentCardDismissed(card.id); ``` ## Handling on-click behavior When a user clicks a Content Card in a custom feed, the on-click behavior (such as navigating to a URL, deep linking, or logging a custom event) is not handled automatically. Use [`handleBrazeAction`](https://js.appboycdn.com/web-sdk/latest/doc/modules/braze.html#handlebrazeaction) to process the card's URL and execute the configured on-click action, including Braze actions (`brazeActions://` URLs). ```javascript import * as braze from "@braze/web-sdk"; // In your card click handler function onCardClick(card) { // Log the click braze.logContentCardClick(card); // Handle the on-click behavior if (card.url) { braze.handleBrazeAction(card.url); } } ``` | Parameter | Description | |---|---| | `url` | A valid URL, or a valid Braze action URL with the scheme `brazeActions://`. | | `openLinkInNewTab` | (Optional) Whether the URL should open in a new tab. Defaults to `false`. | {: .reset-td-br-1 .reset-td-br-2 aria-label="Handling on-click behavior" } **Important:** If you don't call `handleBrazeAction()`, on-click behaviors configured in the Braze dashboard (such as "Log Custom Event" or "Navigate to URL") won't execute for cards displayed in a custom feed. On-click behavior is handled automatically by the default Content Cards UI. For custom implementations, use the [`IContentCardsActionListener`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.ui.contentcards.listeners/-i-content-cards-action-listener/index.html) interface described in the [Logging analytics](#logging-analytics) section above. On-click behavior is handled automatically by the default Content Cards UI. For custom implementations, use the [`BrazeContentCardUIViewControllerDelegate`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazeui/brazecontentcarduiviewcontrollerdelegate) protocol described in the [Logging analytics](#logging-analytics) section above. # Braze SDK를 통해 인앱 메시지 데이터를 기록하십시오 Source: /docs/ko/developer_guide/analytics/logging_channel_data/in_app_messages/index.md # 인앱 메시지 데이터를 기록하십시오 > Braze SDK를 통해 인앱 메시지(IAM) 데이터를 로그하는 방법을 알아보세요. ## Prerequisites Before you can use this feature, you'll need to [integrate the Web Braze SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=web). ## Logging message data Logging in-app message [impressions](https://js.appboycdn.com/web-sdk/latest/doc/modules/braze.html#loginappmessageimpression) and [clicks](https://js.appboycdn.com/web-sdk/latest/doc/modules/braze.html#loginappmessagebuttonclick) is performed automatically when you use the `showInAppMessage` or `automaticallyShowInAppMessage` method. If you do not use either method and opt to manually display the message using your own UI code, use the following methods to log analytics: ```javascript // Registers that a user has viewed an in-app message with the Braze server. braze.logInAppMessageImpression(inAppMessage); // Registers that a user has clicked on the specified in-app message with the Braze server. braze.logInAppMessageClick(inAppMessage); // Registers that a user has clicked a specified in-app message button with the Braze server. braze.logInAppMessageButtonClick(button, inAppMessage); // Registers that a user has clicked on a link in an HTML in-app message with the Braze server. braze.logInAppMessageHtmlClick(inAppMessage, buttonId?, url?) ``` ## Prerequisites Before you can use this feature, you'll need to [integrate the Flutter Braze SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=flutter). ## Logging message data To log analytics using your `BrazeInAppMessage`, pass the instance into the desired analytics function: - `logInAppMessageClicked` - `logInAppMessageImpression` - `logInAppMessageButtonClicked` (along with the button index) For example: ```dart // Log a click braze.logInAppMessageClicked(inAppMessage); // Log an impression braze.logInAppMessageImpression(inAppMessage); // Log button index `0` being clicked braze.logInAppMessageButtonClicked(inAppMessage, 0); ``` ## Accessing message data To access in-app message data in your Flutter app, the `BrazePlugin` supports sending in-app message data using [Dart Streams](https://dart.dev/tutorials/language/streams). The `BrazeInAppMessage` object supports a subset of fields available in the native model objects, including `uri`, `message`, `header`, `buttons`, `extras`, and more. ### Listen for in-app message data in the Dart layer To receive in-app message data in the Dart layer, use the code below to create a `StreamSubscription` and call `braze.subscribeToInAppMessages()`. Remember to `cancel()` the stream subscription when it is no longer needed. ```dart // Create stream subscription StreamSubscription inAppMessageStreamSubscription; inAppMessageStreamSubscription = braze.subscribeToInAppMessages((BrazeInAppMessage inAppMessage) { // Handle in-app messages } // Cancel stream subscription inAppMessageStreamSubscription.cancel(); ``` For an example, see [main.dart](https://github.com/braze-inc/braze-flutter-sdk/blob/master/example/lib/main.dart) in the Braze Flutter SDK sample application. ### Forward in-app message data from the native layer In-app message data is automatically forwarded from both the Android and iOS native layers. No additional setup is required. If you're using Flutter SDK 17.1.0 or earlier, in-app message data forwarding from the iOS native layer requires manual setup. Your application likely contains one of the following. To migrate to Flutter SDK 18.0.0, remove the `BrazePlugin.processInAppMessage(_:)` call—data forwarding is now handled automatically. Remove the `BrazePlugin.processInAppMessage(_:)` call from your [`willPresent` delegate implementation](https://braze-inc.github.io/braze-swift-sdk/documentation/brazeui/brazeinappmessageuidelegate/inappmessage(_:willpresent:view:)-4pzvv). Remove the `BrazePlugin.processInAppMessage(message)` call from your custom presenter's [`present(message:)`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazeui/brazeinappmessageui/present(message:)-f2ra) implementation: ```swift class CustomInAppMessagePresenter: BrazeInAppMessageUI { override func present(message: Braze.InAppMessage) { // Pass in-app message data to the Dart layer. BrazePlugin.processInAppMessage(message) // If you want the default UI to display the in-app message. super.present(message: message) } } ``` ### Replaying the callback for in-app messages (optional) To store any in-app messages triggered before the callback is available and replay them after it is set, add the following entry to the `customConfigs` map when initializing the `BrazePlugin`: ```dart BrazePlugin braze = new BrazePlugin(customConfigs: {replayCallbacksConfigKey: true}); ``` ## Prerequisites Before you can use this feature, you'll need to [integrate the React Native Braze SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=react%20native). ## Methods for logging You can use these methods by passing your `BrazeInAppMessage` instance to log analytics and perform actions: | Method | Description | | --------------------------------------------------------- | ------------------------------------------------------------------------------------- | | `logInAppMessageClicked(inAppMessage)` | Logs a click for the provided in-app message data. | | `logInAppMessageImpression(inAppMessage)` | Logs an impression for the provided in-app message data. | | `logInAppMessageButtonClicked(inAppMessage, buttonId)` | Logs a button click for the provided in-app message data and button ID. | | `hideCurrentInAppMessage()` | Dismisses the currently displayed in-app message. | | `performInAppMessageAction(inAppMessage)` | Performs the action for an in-app message. | | `performInAppMessageButtonAction(inAppMessage, buttonId)` | Performs the action for an in-app message button. | {: .reset-td-br-1 .reset-td-br-2 aria-label="Methods for logging" } ## Handling message data In most cases, you can use the `Braze.addListener` method to register event listeners to handle data coming from in-app messages. Additionally, you can access the in-app message data in the JavaScript layer by calling the `Braze.subscribeToInAppMessage` method to have the SDKs publish an `inAppMessageReceived` event when an in-app message is triggered. Pass a callback to this method to execute your own code when the in-app message is triggered and received by the listener. To customize how message data is handled, refer to the following implementation examples: To enhance the default behavior, or if you don't have access to customize the native iOS or Android code, we recommend that you disable the default UI while still receiving in-app message events from Braze. To disable the default UI, pass `false` to the `Braze.subscribeToInAppMessage` method and use the in-app message data to construct your own message in JavaScript. Note that you will need to manually log analytics on your messages if you choose to disable the default UI. ```javascript import Braze from "@braze/react-native-sdk"; // Option 1: Listen for the event directly via `Braze.addListener`. // // You may use this method to accomplish the same thing if you don't // wish to make any changes to the default Braze UI. Braze.addListener(Braze.Events.IN_APP_MESSAGE_RECEIVED, (event) => { console.log(event.inAppMessage); }); // Option 2: Call `subscribeToInAppMessage`. // // Pass in `false` to disable the automatic display of in-app messages. Braze.subscribeToInAppMessage(false, (event) => { console.log(event.inAppMessage); // Use `event.inAppMessage` to construct your own custom message UI. }); ``` To include more advanced logic to determine whether or not to show an in-app message using the built-in UI, implement in-app messages through the native layer. **Warning:** Since this is an advanced customization option, note that overriding the default Braze implementation will also nullify the logic to emit in-app message events to your JavaScript listeners. If you wish to still use `Braze.subscribeToInAppMessage` or `Braze.addListener` as described in [Accessing in-app message data](#accessing-in-app-message-data), you will need to handle publishing the events yourself. Implement the `IInAppMessageManagerListener` as described in our Android article on [Custom Manager Listener](https://www.braze.com/docs/ko/ko/developer_guide/in_app_messages/customization/?sdktab=android#android_setting-custom-manager-listeners). In your `beforeInAppMessageDisplayed` implementation, you can access the `inAppMessage` data, send it to the JavaScript layer, and decide to show or not show the native message based on the return value. For more on these values, see our [Android documentation](https://www.braze.com/docs/ko/ko/developer_guide/in_app_messages/). ```java // In-app messaging @Override public InAppMessageOperation beforeInAppMessageDisplayed(IInAppMessage inAppMessage) { WritableMap parameters = new WritableNativeMap(); parameters.putString("inAppMessage", inAppMessage.forJsonPut().toString()); getReactNativeHost() .getReactInstanceManager() .getCurrentReactContext() .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class) .emit("inAppMessageReceived", parameters); // Note: return InAppMessageOperation.DISCARD if you would like // to prevent the Braze SDK from displaying the message natively. return InAppMessageOperation.DISPLAY_NOW; } ``` ### Overriding the default UI delegate By default, [`BrazeInAppMessageUI`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazeui/brazeinappmessageui/) is created and assigned when you initialize the `braze` instance. `BrazeInAppMessageUI` is an implementation of the [`BrazeInAppMessagePresenter`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/brazeinappmessagepresenter) protocol and comes with a `delegate` property that can be used to customize the handling of in-app messages that have been received. 1. Implement the `BrazeInAppMessageUIDelegate` delegate as described in [our iOS article here](https://braze-inc.github.io/braze-swift-sdk/tutorials/braze/c1-inappmessageui). 2. In the `inAppMessage(_:displayChoiceForMessage:)` delegate method, you can access the `inAppMessage` data, send it to the JavaScript layer, and decide to show or not show the native message based on the return value. For more details on these values, see our [iOS documentation](https://braze-inc.github.io/braze-swift-sdk/documentation/brazeui/brazeinappmessageuidelegate/). ```objc - (enum BRZInAppMessageUIDisplayChoice)inAppMessage:(BrazeInAppMessageUI *)ui displayChoiceForMessage:(BRZInAppMessageRaw *)message { // Convert the message to a JavaScript representation. NSData *inAppMessageData = [message json]; NSString *inAppMessageString = [[NSString alloc] initWithData:inAppMessageData encoding:NSUTF8StringEncoding]; NSDictionary *arguments = @{ @"inAppMessage" : inAppMessageString }; // Send to JavaScript. [self sendEventWithName:@"inAppMessageReceived" body:arguments]; // Note: Return `BRZInAppMessageUIDisplayChoiceDiscard` if you would like // to prevent the Braze SDK from displaying the message natively. return BRZInAppMessageUIDisplayChoiceNow; } ``` To use this delegate, assign it to `brazeInAppMessagePresenter.delegate` after initializing the `braze` instance. **Note:** `BrazeUI` can only be imported in Objective-C or Swift. If you are using Objective-C++, you will need to handle this in a separate file. ```objc @import BrazeUI; - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { BRZConfiguration *configuration = [[BRZConfiguration alloc] initWithApiKey:apiKey endpoint:endpoint]; Braze *braze = [BrazeReactBridge initBraze:configuration]; ((BrazeInAppMessageUI *)braze.inAppMessagePresenter).delegate = [[CustomDelegate alloc] init]; AppDelegate.braze = braze; } ``` ### Overriding the default native UI If you wish to fully customize the presentation of your in-app messages at the native iOS layer, conform to the [`BrazeInAppMessagePresenter`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/brazeinappmessagepresenter) protocol and assign your custom presenter following the sample below: ```objc BRZConfiguration *configuration = [[BRZConfiguration alloc] initWithApiKey:apiKey endpoint:endpoint]; Braze *braze = [BrazeReactBridge initBraze:configuration]; braze.inAppMessagePresenter = [[MyCustomPresenter alloc] init]; AppDelegate.braze = braze; ``` ## Prerequisites Before you can use this feature, you'll need to [integrate the Android Braze SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=android). ## Logging message data You will need to make sure certain functions are called to handle the analytics for your campaign. ### Displayed messages When a message is displayed or seen, log an impression: ```brightscript LogInAppMessageImpression(in_app_message.id, brazetask) ``` ### Clicked messages Once a user clicks on the message, log a click and then process `in_app_message.click_action`: ```brightscript LogInAppMessageClick(in_app_message.id, brazetask) ``` ### Clicked buttons If the user clicks on a button, log the button click and then process `inappmessage.buttons[selected].click_action`: ```brightscript LogInAppMessageButtonClick(inappmessage.id, inappmessage.buttons[selected].id, brazetask) ``` ### After processing a message After processing an in-app message, you should clear the field: ```brightscript m.BrazeTask.BrazeInAppMessage = invalid ``` ## Subscribing to in-app messages You may register Unity game objects to be notified of incoming in-app messages. We recommend setting game object listeners from the Braze configuration editor. In the configuration editor, listeners must be set separately for Android and iOS. If you need to configure your game object listener at runtime, use `AppboyBinding.ConfigureListener()` and specify `BrazeUnityMessageType.IN_APP_MESSAGE`. ## Parsing messages Incoming `string` messages received in your in-app message game object callback can be parsed into our pre-supplied model objects for convenience. Use `InAppMessageFactory.BuildInAppMessage()` to parse your in-app message. The resulting object will either be an instance of [`IInAppMessage.cs`](https://github.com/braze-inc/braze-unity-sdk/blob/18cb8ee89f1841c576eb954793edb6e06f9130b4/Assets/Plugins/Appboy/Models/InAppMessage/IInAppMessage.cs) or [`IInAppMessageImmersive.cs`](https://github.com/braze-inc/braze-unity-sdk/blob/18cb8ee89f1841c576eb954793edb6e06f9130b4/Assets/Plugins/Appboy/Models/InAppMessage/IInAppMessageImmersive.cs) depending on its type. ```csharp // Automatically logs a button click, if present. void InAppMessageReceivedCallback(string message) { IInAppMessage inApp = InAppMessageFactory.BuildInAppMessage(message); if (inApp is IInAppMessageImmersive) { IInAppMessageImmersive inAppImmersive = inApp as IInAppMessageImmersive; if (inAppImmersive.Buttons != null && inAppImmersive.Buttons.Count > 0) { inAppImmersive.LogButtonClicked(inAppImmersive.Buttons[0].ButtonID); } } } ``` ## Logging message data Clicks and impressions must be manually logged for in-app messages not displayed directly by Braze. Use `LogClicked()` and `LogImpression()` on [`IInAppMessage`](https://github.com/braze-inc/braze-unity-sdk/blob/18cb8ee89f1841c576eb954793edb6e06f9130b4/Assets/Plugins/Appboy/Models/InAppMessage/IInAppMessage.cs) to log clicks and impressions on your message. Use `LogButtonClicked(int buttonID)` on [`IInAppMessageImmersive`](https://github.com/braze-inc/braze-unity-sdk/blob/18cb8ee89f1841c576eb954793edb6e06f9130b4/Assets/Plugins/Appboy/Models/InAppMessage/IInAppMessageImmersive.cs) to log button clicks. Note that buttons are represented as lists of[`InAppMessageButton`](https://github.com/braze-inc/braze-unity-sdk/blob/18cb8ee89f1841c576eb954793edb6e06f9130b4/Assets/Plugins/Appboy/Models/InAppMessage/InAppMessageButton.cs) instances, each of which contains a `ButtonID`. # Braze SDK를 통해 푸시 알림 데이터를 기록하십시오 Source: /docs/ko/developer_guide/analytics/logging_channel_data/push_notifications/index.md # 푸시 알림 데이터를 기록하십시오 > Braze SDK를 통해 푸시 알림 데이터를 기록하는 방법을 알아보세요. ## Logging data with the Braze API (recommended) You can log analytics in real-time by making calls to the [`/users/track` endpoint](https://www.braze.com/docs/ko/ko/api/endpoints/user_data/post_user_track/). To log analytics, send the `braze_id` value from the Braze dashboard to identify which user profile to update. ![Personalized Push dashboard Example](https://www.braze.com/docs/ko/ko/assets/img/push_implementation_guide/android_braze_id_configuration.png?0cc22cde8cd194e7755f83b13d273806){: style="max-width:79%;"} ## Manually logging data Depending on the details of your payload, you can log analytics manually within your `FirebaseMessagingService.onMessageReceived` implementation or your startup activity. Keep in mind, your `FirebaseMessagingService` subclass must finish execution within 9 seconds of invocation to avoid being [flagged or terminated](https://firebase.google.com/docs/cloud-messaging/android/receive) by the Android system. ## Logging data with the Braze API (recommended) Logging analytics can be done in real-time with the help of the Braze API [`/users/track` endpoint](https://www.braze.com/docs/ko/ko/api/endpoints/user_data/post_user_track/). To log analytics, send down the `braze_id` value in the key-value pairs field (as seen in the following screenshot) to identify which user profile to update. ![A push message with three sets of key-value pairs. 1. "Braze_id" set as a Liquid call to retrieve Braze ID. 2. "cert_title" set as "Braze Marketer Certification". 3. "Cert_description" set as "Certified Braze marketers drive...".](https://www.braze.com/docs/ko/ko/assets/img/push_implementation_guide/push18.png?ae37ef2a75d3afb0525cc480263728d7){: style="max-width:80%;"} ## Logging data manually Logging manually will require you to first configure workspaces within Xcode, and then create, save, and retrieve analytics. This will require some custom developer work on your end. The following code snippets shown will help address this. It's important to note that analytics are not sent to Braze until the mobile application is subsequently launched. This means that, depending on your dismissal settings, there often exists an indeterminate period of time between when a push notification is dismissed and the mobile app is launched and the analytics are retrieved. While this time buffer may not affect all use cases, you should consider this impact adjust your user journey as necessary to include opening the application to address this concern. ![A graphic describing how analytics are processed in Braze. 1. Analytics data is created. 2. Analytics data is saved. 3. Push notification is dismissed. 4. Indeterminate period of time between when push notification is dismissed and mobile app is launched. 5. Mobile app is launched. 6. Analytics data is received. 7. Analytics data is sent to Braze.](https://www.braze.com/docs/ko/ko/assets/img/push_implementation_guide/push13.png?817f7603e474002aae9a3b25bccd81bb) ### Step 1: Configure app groups within Xcode In Xcode, add the `App Groups` capability. If you haven’t had any workspaces in your app, go to the capability of the main app target, turn on the `App Groups`, and click the **+** Add button. Then, use your app’s bundle ID to create the workspace. For example, if your app’s bundle ID is `com.company.appname`, you can name your workspace `group.com.company.appname.xyz`. Make sure the `App Groups` are turned on for both your main app target and the content extension target. ![](https://www.braze.com/docs/ko/ko/assets/img/swift/push_story/add_app_groups.png?44e3d92af533e6323db33236364b99e1) ### Step 2: Integrate code snippets The following code snippets are a helpful reference on how to save and send custom events, custom attributes, and user attributes. This guide will be speaking in terms of `UserDefaults`, but the code representation will be in the form of the helper file `RemoteStorage`. There are additional helper files, `UserAttributes` and `EventName Dictionary`, that are used when sending and saving user attributes. #### Saving custom events To save custom events, you must create the analytics from scratch. This is done by creating a dictionary, populating it with metadata, and saving the data through the use of a helper file. 1. Initialize a dictionary with event metadata 2. Initialize `userDefaults` to retrieve and store the event data 3. If there is an existing array, append new data to the existing array and save 4. If there is not an existing array, save the new array to `userDefaults` ``` swift func saveCustomEvent(with properties: [String: Any]? = nil) { // 1 let customEventDictionary = Dictionary(eventName: "YOUR-EVENT-NAME", properties: properties) // 2 let remoteStorage = RemoteStorage(storageType: .suite) // 3 if var pendingEvents = remoteStorage.retrieve(forKey: .pendingCustomEvents) as? [[String: Any]] { pendingEvents.append(contentsOf: [customEventDictionary]) remoteStorage.store(pendingEvents, forKey: .pendingCustomEvents) } else { // 4 remoteStorage.store([customEventDictionary], forKey: .pendingCustomEvents) } } ``` ```objc - (void)saveCustomEvent:(NSDictionary *)properties { // 1 NSDictionary *customEventDictionary = [[NSDictionary alloc] initWithEventName:@"YOUR-EVENT-NAME" properties:properties]; // 2 RemoteStorage *remoteStorage = [[RemoteStorage alloc] initWithStorageType:StorageTypeSuite]; NSMutableArray *pendingEvents = [[remoteStorage retrieveForKey:RemoteStorageKeyPendingCustomEvents] mutableCopy]; // 3 if (pendingEvents) { [pendingEvents addObject:customEventDictionary]; [remoteStorage store:pendingEvents forKey:RemoteStorageKeyPendingCustomAttributes]; } else { // 4 [remoteStorage store:@[ customEventDictionary ] forKey:RemoteStorageKeyPendingCustomAttributes]; } } ``` #### Sending custom events to Braze The best time to log any saved analytics from a notification content app extension is right after the SDK is initialized. This can be done by looping through any pending events, checking for the "Event Name" key, setting the appropriate values in Braze, and then clearing the storage for the next time this function is needed. 1. Loop through the array of pending events 2. Loop through each key-value pair in the `pendingEvents` dictionary 3. Explicitly check the key for “Event Name” to set the value accordingly 4. Every other key-value will be added to the `properties` dictionary 5. Log individual custom event 6. Remove all pending events from storage ``` swift func logPendingCustomEventsIfNecessary() { let remoteStorage = RemoteStorage(storageType: .suite) guard let pendingEvents = remoteStorage.retrieve(forKey: .pendingCustomEvents) as? [[String: Any]] else { return } // 1 for event in pendingEvents { var eventName: String? var properties: [AnyHashable: Any] = [:] // 2 for (key, value) in event { if key == PushNotificationKey.eventName.rawValue { // 3 if let eventNameValue = value as? String { eventName = eventNameValue } else { print("Invalid type for event_name key") } } else { // 4 properties[key] = value } } // 5 if let eventName = eventName { AppDelegate.braze?.logCustomEvent(eventName, properties: properties) } } // 6 remoteStorage.removeObject(forKey: .pendingCustomEvents) } ``` ```objc - (void)logPendingEventsIfNecessary { RemoteStorage *remoteStorage = [[RemoteStorage alloc] initWithStorageType:StorageTypeSuite]; NSArray *pendingEvents = [remoteStorage retrieveForKey:RemoteStorageKeyPendingCustomEvents]; // 1 for (NSDictionary *event in pendingEvents) { NSString *eventName = nil; NSMutableDictionary *properties = [NSMutableDictionary dictionary]; // 2 for (NSString* key in event) { if ([key isEqualToString:@"event_name"]) { // 3 if ([[event objectForKey:key] isKindOfClass:[NSString class]]) { eventName = [event objectForKey:key]; } else { NSLog(@"Invalid type for event_name key"); } } else { // 4 properties[key] = event[key]; } } // 5 if (eventName != nil) { [AppDelegate.braze logCustomEvent:eventName properties:properties]; } } // 6 [remoteStorage removeObjectForKey:RemoteStorageKeyPendingCustomEvents]; } ``` #### Saving custom attributes To save custom attributes, you must create the analytics from scratch. This is done by creating a dictionary, populating it with metadata, and saving the data through the use of a helper file. 1. Initialize a dictionary with attribute metadata 2. Initialize `userDefaults` to retrieve and store the attribute data 3. If there is an existing array, append new data to the existing array and save 4. If there is not an existing array, save the new array to `userDefaults` ``` swift func saveCustomAttribute() { // 1 let customAttributeDictionary: [String: Any] = ["YOUR-CUSTOM-ATTRIBUTE-KEY": "YOUR-CUSTOM-ATTRIBUTE-VALUE"] // 2 let remoteStorage = RemoteStorage(storageType: .suite) // 3 if var pendingAttributes = remoteStorage.retrieve(forKey: .pendingCustomAttributes) as? [[String: Any]] { pendingAttributes.append(contentsOf: [customAttributeDictionary]) remoteStorage.store(pendingAttributes, forKey: .pendingCustomAttributes) } else { // 4 remoteStorage.store([customAttributeDictionary], forKey: .pendingCustomAttributes) } } ``` ``` objc - (void)saveCustomAttribute { // 1 NSDictionary *customAttributeDictionary = @{ @"YOUR-CUSTOM-ATTRIBUTE-KEY": @"YOUR-CUSTOM-ATTRIBUTE-VALUE" }; // 2 RemoteStorage *remoteStorage = [[RemoteStorage alloc] initWithStorageType:StorageTypeSuite]; NSMutableArray *pendingAttributes = [[remoteStorage retrieveForKey:RemoteStorageKeyPendingCustomAttributes] mutableCopy]; // 3 if (pendingAttributes) { [pendingAttributes addObject:customAttributeDictionary]; [remoteStorage store:pendingAttributes forKey:RemoteStorageKeyPendingCustomAttributes]; } else { // 4 [remoteStorage store:@[ customAttributeDictionary ] forKey:RemoteStorageKeyPendingCustomAttributes]; } } ``` #### Sending custom attributes to Braze The best time to log any saved analytics from a notification content app extension is right after the SDK is initialized. This can be done by looping through the pending attributes, setting the appropriate custom attribute in Braze, and then clearing the storage for the next time this function is needed. 1. Loop through the array of pending attributes 2. Loop through each key-value pair in the `pendingAttributes` dictionary 3. Log individual custom attributes with corresponding key and value 4. Remove all pending attributes from storage ``` swift func logPendingCustomAttributesIfNecessary() { let remoteStorage = RemoteStorage(storageType: .suite) guard let pendingAttributes = remoteStorage.retrieve(forKey: .pendingCustomAttributes) as? [[String: Any]] else { return } // 1 pendingAttributes.forEach { setCustomAttributesWith(keysAndValues: $0) } // 4 remoteStorage.removeObject(forKey: .pendingCustomAttributes) } func setCustomAttributesWith(keysAndValues: [String: Any]) { // 2 for (key, value) in keysAndValues { // 3 if let value = value as? [String] { setCustomAttributeArrayWithKey(key, andValue: value) } else { setCustomAttributeWithKey(key, andValue: value) } } } ``` ```objc - (void)logPendingCustomAttributesIfNecessary { RemoteStorage *remoteStorage = [[RemoteStorage alloc] initWithStorageType:StorageTypeSuite]; NSArray *pendingAttributes = [remoteStorage retrieveForKey:RemoteStorageKeyPendingCustomAttributes]; // 1 for (NSDictionary *attribute in pendingAttributes) { [self setCustomAttributeWith:attribute]; } // 4 [remoteStorage removeObjectForKey:RemoteStorageKeyPendingCustomAttributes]; } - (void)setCustomAttributeWith:(NSDictionary *)keysAndValues { // 2 for (NSString *key in keysAndValues) { // 3 [self setCustomAttributeWith:key andValue:[keysAndValues objectForKey:key]]; } } ``` #### Saving user attributes When saving user attributes, we recommend creating a custom object to decipher what type of attribute is being updated (`email`, `first_name`, `phone_number`, etc.). The object should be compatible with being stored/retrieved from `UserDefaults`. See the `UserAttribute` helper file for one example of how to accomplish this. 1. Initialize an encoded `UserAttribute` object with the corresponding type 2. Initialize `userDefaults` to retrieve and store the event data 3. If there is an existing array, append new data to the existing array and save 4. If there is not an existing array, save the new array to `userDefaults` ``` swift func saveUserAttribute() { // 1 guard let data = try? PropertyListEncoder().encode(UserAttribute.userAttributeType("USER-ATTRIBUTE-VALUE")) else { return } // 2 let remoteStorage = RemoteStorage(storageType: .suite) // 3 if var pendingAttributes = remoteStorage.retrieve(forKey: .pendingUserAttributes) as? [Data] { pendingAttributes.append(contentsOf: [data]) remoteStorage.store(pendingAttributes, forKey: .pendingUserAttributes) } else { // 4 remoteStorage.store([data], forKey: .pendingUserAttributes) } } ``` ```objc - (void)saveUserAttribute { // 1 UserAttribute *userAttribute = [[UserAttribute alloc] initWithUserField:@"USER-ATTRIBUTE-VALUE" attributeType:UserAttributeTypeEmail]; NSError *error; NSData *data = [NSKeyedArchiver archivedDataWithRootObject:userAttribute requiringSecureCoding:YES error:&error]; if (error != nil) { // log error } // 2 RemoteStorage *remoteStorage = [[RemoteStorage alloc] initWithStorageType:StorageTypeSuite]; NSMutableArray *pendingAttributes = [[remoteStorage retrieveForKey:RemoteStorageKeyPendingUserAttributes] mutableCopy]; // 3 if (pendingAttributes) { [pendingAttributes addObject:data]; [remoteStorage store:pendingAttributes forKey:RemoteStorageKeyPendingUserAttributes]; } else { // 4 [remoteStorage store:@[data] forKey:RemoteStorageKeyPendingUserAttributes]; } } ``` #### Sending user attributes to Braze The best time to log any saved analytics from a notification content app extension is right after the SDK is initialized. This can be done by looping through the pending attributes, setting the appropriate custom attribute in Braze, and then clearing the storage for the next time this function is needed. 1. Loop through the array of `pendingAttributes` data 2. Initialize an encoded `UserAttribute` object from attribute data 3. Set specific user field based on the User Attribute type (email) 4. Remove all pending user attributes from storage ``` swift func logPendingUserAttributesIfNecessary() { let remoteStorage = RemoteStorage(storageType: .suite) guard let pendingAttributes = remoteStorage.retrieve(forKey: .pendingUserAttributes) as? [Data] else { return } // 1 for attributeData in pendingAttributes { // 2 guard let userAttribute = try? PropertyListDecoder().decode(UserAttribute.self, from: attributeData) else { continue } // 3 switch userAttribute { case .email(let email): user?.email = email } } // 4 remoteStorage.removeObject(forKey: .pendingUserAttributes) } ``` ```objc - (void)logPendingUserAttributesIfNecessary { RemoteStorage *remoteStorage = [[RemoteStorage alloc] initWithStorageType:StorageTypeSuite]; NSArray *pendingAttributes = [remoteStorage retrieveForKey:RemoteStorageKeyPendingUserAttributes]; // 1 for (NSData *attributeData in pendingAttributes) { NSError *error; // 2 UserAttribute *userAttribute = [NSKeyedUnarchiver unarchivedObjectOfClass:[UserAttribute class] fromData:attributeData error:&error]; if (error != nil) { // log error } // 3 if (userAttribute) { switch (userAttribute.attributeType) { case UserAttributeTypeEmail: [self user].email = userAttribute.userField; break; } } } // 4 [remoteStorage removeObjectForKey:RemoteStorageKeyPendingUserAttributes]; } ``` #### Helper files **RemoteStorage Helper File** ```swift enum RemoteStorageKey: String, CaseIterable { // MARK: - Notification Content Extension Analytics case pendingCustomEvents = "pending_custom_events" case pendingCustomAttributes = "pending_custom_attributes" case pendingUserAttributes = "pending_user_attributes" } enum RemoteStorageType { case standard case suite } class RemoteStorage: NSObject { private var storageType: RemoteStorageType = .standard private lazy var defaults: UserDefaults = { switch storageType { case .standard: return .standard case .suite: return UserDefaults(suiteName: "YOUR-DOMAIN-IDENTIFIER")! } }() init(storageType: RemoteStorageType = .standard) { self.storageType = storageType } func store(_ value: Any, forKey key: RemoteStorageKey) { defaults.set(value, forKey: key.rawValue) } func retrieve(forKey key: RemoteStorageKey) -> Any? { return defaults.object(forKey: key.rawValue) } func removeObject(forKey key: RemoteStorageKey) { defaults.removeObject(forKey: key.rawValue) } func resetStorageKeys() { for key in RemoteStorageKey.allCases { defaults.removeObject(forKey: key.rawValue) } } } ``` ```objc @interface RemoteStorage () @property (nonatomic) StorageType storageType; @property (nonatomic, strong) NSUserDefaults *defaults; @end @implementation RemoteStorage - (id)initWithStorageType:(StorageType)storageType { if (self = [super init]) { self.storageType = storageType; } return self; } - (void)store:(id)value forKey:(RemoteStorageKey)key { [[self defaults] setValue:value forKey:[self rawValueForKey:key]]; } - (id)retrieveForKey:(RemoteStorageKey)key { return [[self defaults] objectForKey:[self rawValueForKey:key]]; } - (void)removeObjectForKey:(RemoteStorageKey)key { [[self defaults] removeObjectForKey:[self rawValueForKey:key]]; } - (void)resetStorageKeys { [[self defaults] removeObjectForKey:[self rawValueForKey:RemoteStorageKeyPendingCustomEvents]]; [[self defaults] removeObjectForKey:[self rawValueForKey:RemoteStorageKeyPendingCustomAttributes]]; [[self defaults] removeObjectForKey:[self rawValueForKey:RemoteStorageKeyPendingUserAttributes]]; } - (NSUserDefaults *)defaults { if (!self.defaults) { switch (self.storageType) { case StorageTypeStandard: return [NSUserDefaults standardUserDefaults]; break; case StorageTypeSuite: return [[NSUserDefaults alloc] initWithSuiteName:@"YOUR-DOMAIN-IDENTIFIER"]; } } else { return self.defaults; } } - (NSString*)rawValueForKey:(RemoteStorageKey)remoteStorageKey { switch(remoteStorageKey) { case RemoteStorageKeyPendingCustomEvents: return @"pending_custom_events"; case RemoteStorageKeyPendingCustomAttributes: return @"pending_custom_attributes"; case RemoteStorageKeyPendingUserAttributes: return @"pending_user_attributes"; default: [NSException raise:NSGenericException format:@"Unexpected FormatType."]; } } ``` **UserAttribute Helper File** ```swift enum UserAttribute: Hashable { case email(String?) } // MARK: - Codable extension UserAttribute: Codable { private enum CodingKeys: String, CodingKey { case email } func encode(to encoder: Encoder) throws { var values = encoder.container(keyedBy: CodingKeys.self) switch self { case .email(let email): try values.encode(email, forKey: .email) } } init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) let email = try values.decode(String.self, forKey: .email) self = .email(email) } } ``` ```objc @implementation UserAttribute - (id)initWithUserField:(NSString *)userField attributeType:(UserAttributeType)attributeType { if (self = [super init]) { self.userField = userField; self.attributeType = attributeType; } return self; } - (void)encodeWithCoder:(NSCoder *)encoder { [encoder encodeObject:self.userField forKey:@"userField"]; [encoder encodeInteger:self.attributeType forKey:@"attributeType"]; } - (id)initWithCoder:(NSCoder *)decoder { if (self = [super init]) { self.userField = [decoder decodeObjectForKey:@"userField"]; NSInteger attributeRawValue = [decoder decodeIntegerForKey:@"attributeType"]; self.attributeType = (UserAttributeType) attributeRawValue; } return self; } @end ``` **EventName Dictionary Helper File** ```swift extension Dictionary where Key == String, Value == Any { init(eventName: String, properties: [String: Any]? = nil) { self.init() self[PushNotificationKey.eventName.rawValue] = eventName if let properties = properties { for (key, value) in properties { self[key] = value } } } } ``` ```objc @implementation NSDictionary (Helper) - (id)initWithEventName:(NSString *)eventName properties:(NSDictionary *)properties { self = [self init]; if (self) { dict[@"event_name"] = eventName; for(id key in properties) { dict[key] = properties[key]; } } return self; } @end ```
# Braze SDK를 통한 세션 추적 Source: /docs/ko/developer_guide/analytics/tracking_sessions/index.md # 세션 추적 {#track-sessions} > Braze SDK를 통해 세션을 추적하는 방법을 알아보세요. **Note:** 목록에 없는 래퍼 SDK의 경우 관련 네이티브 Android 또는 Swift 메서드를 대신 사용하세요. ## About the session lifecycle A session refers to the period of time the Braze SDK tracks user activity in your app after it's launched. You can also force a new session by [calling the `changeUser()` method](https://www.braze.com/docs/ko/ko/developer_guide/analytics/setting_user_ids/#setting-a-user-id). By default, a session starts when you first call `braze.openSession()`. The session will remain active for up to `30` minutes of inactivity (unless you [change the default session timeout](https://www.braze.com/docs/ko/ko/developer_guide/analytics/tracking_sessions/?tab=web#change-session-timeout) or the user closes the app. **Note:** If you've set up the [activity lifecycle callback](https://www.braze.com/docs/ko/ko/developer_guide/platform_integration_guides/android/initial_sdk_setup/android_sdk_integration/#step-4-tracking-user-sessions-in-android) for Android, Braze will automatically call [`openSession()`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze/-i-braze/open-session.html) and [`closeSession()`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze/-i-braze/close-session.html) for each activity in your app. By default, a session starts when `openSession()` is first called. If your app goes to the background and then returns to the foreground, the SDK will check if more than 10 seconds have passed since the session started (unless you [change the default session timeout](https://www.braze.com/docs/ko/ko/developer_guide/analytics/tracking_sessions/?tab=android#change-session-timeout)). If so, a new session will begin. Keep in mind that if the user closes your app while it's in the background, session data may not be sent to Braze until they reopen the app. Calling `closeSession()` will not immediately end the session. Instead, it will end the session after 10 seconds if `openSession()` isn't called again by the user starting another activity. By default, a session starts when you call `Braze.init(configuration:)`. This occurs when the `UIApplicationWillEnterForegroundNotification` notification is triggered, meaning the app has entered the foreground. If your app goes to the background, `UIApplicationDidEnterBackgroundNotification` is triggered. The app does not remain in an active session while in the background. When your app returns to the foreground, the SDK compares the time elapsed since the session started against the session timeout (unless you [change the default session timeout](https://www.braze.com/docs/ko/ko/developer_guide/analytics/tracking_sessions/?tab=swift#change-session-timeout)). If the time since the session started exceeds the timeout period, a new session begins. ## 비활성 정의 {#defining-inactivity} 비활성이 정의되고 측정되는 방식을 이해하는 것은 Web SDK에서 세션 수명 주기를 효과적으로 관리하는 데 핵심입니다. 비활성이란 Braze Web SDK가 사용자로부터 추적된 이벤트를 감지하지 못하는 기간을 의미합니다. ### 비활성 측정 방식 {#how-inactivity-is-measured} Web SDK는 [SDK 추적 이벤트](https://www.braze.com/docs/ko/ko/user_guide/data/activation/custom_data/events/#events)를 기반으로 비활성을 추적합니다. SDK는 추적된 이벤트가 전송될 때마다 재설정되는 내부 타이머를 유지합니다. 구성된 타임아웃 기간 내에 SDK 추적 이벤트가 발생하지 않으면 세션은 비활성으로 간주되어 종료됩니다. Web SDK에서 세션 수명 주기가 구현되는 방식에 대한 자세한 내용은 [Braze Web SDK GitHub 리포지토리](https://github.com/braze-inc/braze-web-sdk/blob/master/src/session.ts)의 세션 관리 소스 코드를 참조하세요. **기본적으로 활동으로 간주되는 항목:** - 웹 앱 열기 또는 새로고침 - Braze 기반 UI 요소와의 상호작용(예: [인앱 메시지](https://www.braze.com/docs/ko/ko/developer_guide/in_app_messages/) 또는 [Content Cards](https://www.braze.com/docs/ko/ko/developer_guide/content_cards/)) - 추적된 이벤트를 전송하는 SDK 메서드 호출(예: [커스텀 이벤트](https://www.braze.com/docs/ko/ko/developer_guide/analytics/logging_events/) 또는 [사용자 속성 업데이트](https://www.braze.com/docs/ko/ko/developer_guide/analytics/setting_user_attributes/)) **기본적으로 활동으로 간주되지 않는 항목:** - 다른 브라우저 탭으로 전환 - 브라우저 창 최소화 - 브라우저 포커스 또는 블러 이벤트 - 페이지에서의 스크롤 또는 마우스 움직임 **Note:** Web SDK는 브라우저 가시성 변경, 탭 전환 또는 사용자 포커스를 자동으로 추적하지 않습니다. 하지만 브라우저의 [Page Visibility API](https://developer.mozilla.org/en-US/docs/Web/API/Page_Visibility_API)를 사용하여 커스텀 이벤트 리스너를 구현하고 Braze에 [커스텀 이벤트](https://www.braze.com/docs/ko/ko/developer_guide/analytics/logging_events/?tab=web)를 전송하면 이러한 브라우저 수준의 상호작용을 추적할 수 있습니다. 예제 구현은 [커스텀 비활성 추적](#tracking-custom-inactivity)을 참조하세요. ### 세션 타임아웃 구성 {#session-timeout-configuration} 기본적으로 Web SDK는 추적된 이벤트가 30분 동안 없으면 세션을 비활성으로 간주합니다. SDK를 초기화할 때 `sessionTimeoutInSeconds` 매개변수를 사용하여 이 임계값을 커스텀할 수 있습니다. 코드 예제를 포함한 이 매개변수 구성에 대한 자세한 내용은 [기본 세션 타임아웃 변경하기](#changing-the-default-session-timeout)를 참조하세요. ### 예시: 비활성 시나리오 이해하기 {#example-understanding-inactivity-scenarios} 다음 시나리오를 살펴보겠습니다: 1. 사용자가 웹사이트를 열면 SDK가 [`braze.openSession()`](https://js.appboycdn.com/web-sdk/latest/doc/modules/braze.html#opensession)을 호출하여 세션을 시작합니다. 2. 사용자가 다른 웹사이트를 보기 위해 다른 브라우저 탭으로 30분 동안 전환합니다. 3. 이 시간 동안 웹사이트에서 SDK 추적 이벤트가 발생하지 않습니다. 4. 30분간의 비활성 후 세션이 자동으로 종료됩니다. 5. 사용자가 웹사이트 탭으로 다시 돌아와 SDK 이벤트(예: 페이지 보기 또는 콘텐츠와의 상호작용)를 트리거하면 새로운 세션이 시작됩니다. ### 커스텀 비활성 추적 {#tracking-custom-inactivity} 브라우저 가시성 또는 탭 전환을 기반으로 비활성을 추적해야 하는 경우 JavaScript 코드에서 커스텀 이벤트 리스너를 구현하세요. `visibilitychange`와 같은 브라우저 이벤트를 사용하여 사용자가 페이지를 떠나는 시점을 감지하고, 필요에 따라 Braze에 [커스텀 이벤트](https://www.braze.com/docs/ko/ko/developer_guide/analytics/logging_events/)를 수동으로 전송하거나 [`braze.openSession()`](https://js.appboycdn.com/web-sdk/latest/doc/modules/braze.html#opensession)을 호출하세요. ```javascript // Example: Track when user switches away from tab document.addEventListener('visibilitychange', function() { if (document.hidden) { // User switched away - optionally log a custom event braze.logCustomEvent('tab_hidden'); } else { // User returned - optionally start a new session and/or log an event // braze.openSession(); braze.logCustomEvent('tab_visible'); } }); ``` 커스텀 이벤트 로깅에 대한 자세한 내용은 [커스텀 이벤트 로깅](https://www.braze.com/docs/ko/ko/developer_guide/analytics/logging_events/)을 참조하세요. 세션 수명 주기 및 타임아웃 구성에 대한 자세한 내용은 [기본 세션 타임아웃 변경하기](#change-session-timeout)를 참조하세요. ## 세션 업데이트 구독하기 {#subscribing-to-session-updates} ### 1단계: 업데이트 구독 {#step-1-subscribe-to-updates} 세션 업데이트를 구독하려면 `subscribeToSessionUpdates()` 메서드를 사용하세요. 현재 Web Braze SDK에서는 세션 업데이트 구독이 지원되지 않습니다. ```java Braze.getInstance(this).subscribeToSessionUpdates(new IEventSubscriber() { @Override public void trigger(SessionStateChangedEvent message) { if (message.getEventType() == SessionStateChangedEvent.ChangeType.SESSION_STARTED) { // A session has just been started } } }); ``` ```kotlin Braze.getInstance(this).subscribeToSessionUpdates { message -> if (message.eventType == SessionStateChangedEvent.ChangeType.SESSION_STARTED) { // A session has just been started } } ``` 세션 종료 콜백을 등록하면 앱이 포그라운드로 돌아올 때 콜백이 실행됩니다. 세션 지속 시간은 앱이 열리거나 포그라운드로 전환되는 시점부터 닫히거나 백그라운드로 전환되는 시점까지 측정됩니다. ```swift // This subscription is maintained through a Braze cancellable, which will observe changes until the subscription is cancelled. // You must keep a strong reference to the cancellable to keep the subscription active. // The subscription is canceled either when the cancellable is deinitialized or when you call its `.cancel()` method. let cancellable = AppDelegate.braze?.subscribeToSessionUpdates { event in switch event { case .started(let id): print("Session \(id) has started") case .ended(let id): print("Session \(id) has ended") } } ``` 비동기 스트림을 구독하려면 [`sessionUpdatesStream`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/sessionupdatesstream)을 대신 사용할 수 있습니다. ```swift for await event in braze.sessionUpdatesStream { switch event { case .started(let id): print("Session \(id) has started") case .ended(let id): print("Session \(id) has ended") } } ``` ```objc // This subscription is maintained through a Braze cancellable, which will observe changes until the subscription is cancelled. // You must keep a strong reference to the cancellable to keep the subscription active. // The subscription is canceled either when the cancellable is deinitialized or when you call its `.cancel()` method. BRZCancellable *cancellable = [AppDelegate.braze subscribeToSessionUpdates:^(BRZSessionEvent * _Nonnull event) { switch (event.state) { case BRZSessionStateStarted: NSLog(@"Session %@ has started", event.sessionId); break; case BRZSessionStateEnded: NSLog(@"Session %@ has ended", event.sessionId); break; default: break; } }]; ``` React Native SDK는 세션 업데이트를 직접 구독하는 메서드를 제공하지 않습니다. 세션 수명 주기는 기본 네이티브 SDK에서 관리되므로, 업데이트를 구독하려면 **Android** 또는 **Swift** 탭의 네이티브 플랫폼 접근 방식을 사용하세요. ### 2단계: 세션 추적 테스트(선택 사항) {#step-2-test-session-tracking-optional} 세션 추적을 테스트하려면 기기에서 세션을 시작한 다음 Braze 대시보드를 열고 해당 사용자를 검색합니다. 고객 프로필에서 **Sessions Overview**를 선택합니다. 측정기준이 예상대로 업데이트되면 세션 추적이 올바르게 작동하고 있는 것입니다. ![사용자 프로필의 세션 개요 섹션으로, 세션 수, 마지막 사용 날짜 및 첫 사용 날짜를 보여줍니다.](https://www.braze.com/docs/ko/ko/assets/img_archive/test_session.png?0428888ea3bd01a486d8c674fb973747){: style="max-width:50%;"} **Note:** 앱별 세부 정보는 두 개 이상의 앱을 사용한 사용자에게만 표시됩니다. ## 기본 세션 타임아웃 변경하기 {#change-session-timeout} 세션이 자동으로 타임아웃되기까지의 시간을 변경할 수 있습니다. 기본적으로 세션 타임아웃은 `30`분으로 설정되어 있습니다. 이를 변경하려면 [`initialize`](https://js.appboycdn.com/web-sdk/latest/doc/modules/braze.html#initialize) 함수에 `sessionTimeoutInSeconds` 옵션을 전달하세요. `1` 이상의 정수로 설정할 수 있습니다. ```js // Sets the session timeout to 15 minutes instead of the default 30 braze.initialize('YOUR-API-KEY-HERE', { sessionTimeoutInSeconds: 900 }); ``` 기본적으로 세션 타임아웃은 `10`초로 설정되어 있습니다. 이를 변경하려면 `braze.xml` 파일을 열고 `com_braze_session_timeout` 매개변수를 추가하세요. `1` 이상의 정수로 설정할 수 있습니다. ```xml 60 ``` 기본적으로 세션 타임아웃은 `10`초로 설정되어 있습니다. 이를 변경하려면 [`init(configuration)`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/configuration-swift.class)에 전달되는 `configuration` 오브젝트에서 `sessionTimeout`을 설정하세요. `1` 이상의 정수로 설정할 수 있습니다. ```swift // Sets the session timeout to 60 seconds let configuration = Braze.Configuration( apiKey: "", endpoint: "" ) configuration.sessionTimeout = 60; let braze = Braze(configuration: configuration) AppDelegate.braze = braze ``` ```objc // Sets the session timeout to 60 seconds BRZConfiguration *configuration = [[BRZConfiguration alloc] initWithApiKey:brazeApiKey endpoint:brazeEndpoint]; configuration.sessionTimeout = 60; Braze *braze = [[Braze alloc] initWithConfiguration:configuration]; AppDelegate.braze = braze; ``` React Native SDK는 세션 관리를 네이티브 SDK에 의존합니다. 기본 세션 타임아웃을 변경하려면 네이티브 레이어에서 구성하세요: - **Android:** `braze.xml` 파일에서 `com_braze_session_timeout`을 설정하세요. 자세한 내용은 **Android** 탭을 선택하세요. - **iOS:** `Braze.Configuration` 오브젝트에서 `sessionTimeout`을 설정하세요. 자세한 내용은 **Swift** 탭을 선택하세요. **Note:** 세션 타임아웃을 설정하면 모든 세션 시맨틱이 설정된 타임아웃까지 자동으로 연장됩니다. ## 문제 해결 {#troubleshooting} ### 고객 프로필의 세션 수가 0인 경우 {#user-profile-has-0-sessions} SDK 외부에서 사용자가 생성된 경우 고객 프로필의 세션 수가 0일 수 있습니다: - **REST API로 생성된 경우:** [`/users/track`](https://www.braze.com/docs/ko/ko/api/endpoints/user_data/post_user_track/) 엔드포인트를 통해 요청에 `app_id`를 포함하여 사용자를 생성하면, 프로필은 해당 앱과 연결되어 표시되지만 해당 사용자에 대해 SDK가 초기화된 적이 없으므로 세션 데이터가 없습니다. - **CSV 가져오기로 생성된 경우:** 첫 번째 또는 마지막 세션 필드에 값 없이 [CSV](https://www.braze.com/docs/ko/ko/user_guide/audience/manage_audience/import_users/csv_import/)를 통해 사용자를 가져오면 프로필은 세션 수 0으로 존재합니다. ### 일부 사용자가 세션을 기록하지 않는 경우 {#some-users-are-not-logging-sessions} 세션은 SDK가 초기화된 후에만 추적되므로, SDK 초기화를 트리거하지 않는 사용자는 세션을 기록하지 않습니다. 이는 일반적으로 앱이 SDK를 초기화하기 전에 조건 로직을 사용하는 경우에 발생합니다. 예를 들어 로그인 플로우, 동의 프롬프트 또는 피처 플래그 뒤에 초기화를 지연시키는 경우입니다. 구현 가이드는 [지연 초기화](https://www.braze.com/docs/ko/ko/developer_guide/sdk_initalization/?sdktab=swift)를 참조하세요. 이러한 경우 조건을 충족하지 않는 사용자는 세션을 시작하지 않습니다. 일부 사용자는 세션을 기록하고 다른 사용자는 기록하지 않는 경우 다음을 확인하세요: - **초기화 로직을 확인하세요.** SDK가 일부가 아닌 모든 사용자와 앱 진입점에 대해 초기화되는지 확인하세요. - **최근 앱 변경 사항을 확인하세요.** SDK 초기화 관련 새로운 조건 로직이 세션 수의 급격한 감소를 유발할 수 있습니다. - **영향을 받는 사용자와 받지 않는 사용자를 비교하세요.** 특정 사용자에 대해 초기화가 건너뛰어지는 이유를 설명할 수 있는 앱 버전, 기기 유형 또는 사용자 플로우의 차이를 파악하세요. 구현을 확인한 후에도 문제가 지속되면 고객지원에 연락하기 전에 문제를 재현하고 다음 정보를 수집하세요: - 문제를 재현하는 단계 - 영향을 받는 앱 버전 - 문제가 발생하는 동안 캡처한 [상세 SDK 로그](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/verbose_logging/)(또는 플랫폼별: [Android](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=android#android_enabling-logs), [Swift](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=swift#swift_setting-the-log-level), [Web](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=web#web_logging)) - SDK 초기화 코드 스니펫 - 초기화 전에 적용된 조건 로직 요약 # Braze SDK를 통해 위치 추적 Source: /docs/ko/developer_guide/analytics/tracking_location/index.md # 위치 추적 > Braze SDK를 통해 위치를 추적하는 방법을 알아보세요. ## Logging the current location To get a user's current location, use the geolocation API's [`getCurrentPosition()`](https://developer.mozilla.org/en-US/docs/Web/API/Geolocation/getCurrentPosition) method. This will immediately prompt the user to allow or disallow tracking (unless they've already done so). ```javascript import * as braze from "@braze/web-sdk"; function success(position) { var coords = position.coords; braze.getUser().setLastKnownLocation( coords.latitude, coords.longitude, coords.accuracy, coords.altitude, coords.altitudeAccuracy ); } navigator.geolocation.getCurrentPosition(success); ``` Now when data is sent to Braze, the SDK can automatically detect the user's country using their IP address. For more information, see [setLastKnownLocation()](https://js.appboycdn.com/web-sdk/latest/doc/classes/braze.user.html#setlastknownlocation). ## Continuously tracking the location To continuously track a user's location during a page load, use the geolocation API's [`watchPosition()`](https://developer.mozilla.org/en-US/docs/Web/API/Geolocation/watchPosition) method. Calling this method will immediately prompt the user to allow or disallow tracking (unless they've already done so). If they opt-in, a success callback will now be invoked every time their location is updated. ```javascript function success(position) { var coords = position.coords; braze.getUser().setLastKnownLocation( coords.latitude, coords.longitude, coords.accuracy, coords.altitude, coords.altitudeAccuracy ); } navigator.geolocation.watchPosition(success); ``` **Important:** To learn how to disable continuous tracking, refer to the [Mozilla developer docs](https://developer.mozilla.org/en-US/docs/Web/API/Geolocation/watchPosition). ## Logging the current location Even if continuous tracking is disabled, you can manually log the user's current location using the [`setLastKnownLocation()`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze/-braze-user/set-last-known-location.html) method. ```java Braze.getInstance(context).getCurrentUser(new IValueCallback() { @Override public void onSuccess(BrazeUser brazeUser) { brazeUser.setLastKnownLocation(LATITUDE_DOUBLE_VALUE, LONGITUDE_DOUBLE_VALUE, ALTITUDE_DOUBLE_VALUE, ACCURACY_DOUBLE_VALUE); } } ``` ```kotlin Braze.getInstance(context).getCurrentUser { brazeUser -> brazeUser.setLastKnownLocation(LATITUDE_DOUBLE_VALUE, LONGITUDE_DOUBLE_VALUE, ALTITUDE_DOUBLE_VALUE, ACCURACY_DOUBLE_VALUE) } ``` ## Continuously tracking the location **Important:** [Starting with Android Marshmallow](https://developer.android.com/training/permissions/index.html), you must prompt your users to explicitly opt-in to location tracking. Once they do, Braze can start tracking their location at the beginning of the next session. This is unlike earlier versions of Android, where only declaring location permissions in your `AndroidManifest.xml` was required. To continuously track a user's location, you'll need to declare your app's intent to collect location data by adding at least one of the following permissions to your `AndroidManifest.xml` file. |Permission|Description| |---|---| | `ACCESS_COARSE_LOCATION` | Uses the most battery-efficient, non-GPS provider (such as a home network). Typically, this is sufficient for most location-data needs. Under the runtime permissions model, granting location permission implicitly authorizes the collection of fine location data. | | `ACCESS_FINE_LOCATION` | Includes GPS data for more precise location. Under the runtime permissions model, granting location permission also covers fine location access. | {: .reset-td-br-1 .reset-td-br-2 aria-label="Continuously tracking the location" } Your `AndroidManifest.xml` should be similar to the following: ```xml ... ``` ## Disabling continuous tracking You can disable continuous tracking at compile time or runtime. To disable continuous location tracking at compile time, set `com_braze_enable_location_collection` to `false` in `braze.xml`: ```xml false ``` To selectively disable continuous location tracking at runtime, use [`BrazeConfig`](https://www.braze.com/docs/ko/ko/developer_guide/platform_integration_guides/android/advanced_use_cases/runtime_configuration/#runtime-configuration): ```java BrazeConfig brazeConfig = new BrazeConfig.Builder() .setIsAutomaticLocationCollectionEnabled(false) .build(); Braze.configure(this, brazeConfig); ``` ```kotlin val brazeConfig = BrazeConfig.Builder() .setIsAutomaticLocationCollectionEnabled(false) .build() Braze.configure(this, brazeConfig) ``` ## Logging the current location ### Step 1: Configure your project **Important:** When using Braze location features, your application is responsible for requesting authorization for using location services. Be sure to review [Apple Developer: Requesting authorization to user location services](https://developer.apple.com/documentation/corelocation/requesting-authorization-to-use-location-services). To enable location tracking, open your Xcode project and select your app. In the **General** tab, add the `BrazeLocation` module. In your `AppDelegate.swift` file, import the `BrazeLocation` module at the top of the file. Add a `BrazeLocationProvider` instance to the Braze configuration, making sure all changes to the configuration are done prior to calling `Braze(configuration:)`. See `Braze.Configuration.Location` for the available configurations. ```swift import UIKit import BrazeKit import BrazeLocation @main class AppDelegate: UIResponder, UIApplicationDelegate { static var braze: Braze? = nil func application( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? ) -> Bool { // Setup Braze let configuration = Braze.Configuration(apiKey: brazeApiKey, endpoint: brazeEndpoint) configuration.logger.level = .info configuration.location.brazeLocationProvider = BrazeLocationProvider() configuration.location.automaticLocationCollection = true configuration.location.geofencesEnabled = true configuration.location.automaticGeofenceRequests = true let braze = Braze(configuration: configuration) AppDelegate.braze = braze return true } } ``` In your `AppDelegate.m` file, import the `BrazeLocation` module at the top of the file. Add a `BrazeLocationProvider` instance to the Braze configuration, making sure all changes to the configuration are done prior to calling `Braze(configuration:)`. See `BRZConfigurationLocation` for the available configurations. ```objc #import "AppDelegate.h" @import BrazeKit; @import BrazeLocation; @implementation AppDelegate #pragma mark - Lifecycle - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { // Setup Braze BRZConfiguration *configuration = [[BRZConfiguration alloc] initWithApiKey:brazeApiKey endpoint:brazeEndpoint]; configuration.logger.level = BRZLoggerLevelInfo; configuration.location.brazeLocationProvider = [[BrazeLocationProvider alloc] init]; configuration.location.automaticLocationCollection = YES; configuration.location.geofencesEnabled = YES; configuration.location.automaticGeofenceRequests = YES; Braze *braze = [[Braze alloc] initWithConfiguration:configuration]; AppDelegate.braze = braze; [self.window makeKeyAndVisible]; return YES; } #pragma mark - AppDelegate.braze static Braze *_braze = nil; + (Braze *)braze { return _braze; } + (void)setBraze:(Braze *)braze { _braze = braze; } @end ``` ### Step 2: Log the user's location Next, log the user's last-known location to Braze. The following examples assume you’ve assigned the Braze instance as a variable in your `AppDelegate`. ```swift AppDelegate.braze?.user.setLastKnownLocation(latitude:latitude, longitude:longitude) ``` ```swift AppDelegate.braze?.user.setLastKnownLocation(latitude:latitude, longitude:longitude, altitude:altitude, horizontalAccuracy:horizontalAccuracy, verticalAccuracy:verticalAccuracy) ``` ```objc [AppDelegate.braze.user setLastKnownLocationWithLatitude:latitude longitude:longitude horizontalAccuracy:horizontalAccuracy]; ``` ```objc [AppDelegate.braze.user setLastKnownLocationWithLatitude:latitude longitude:longitude horizontalAccuracy:horizontalAccuracy altitude:altitude verticalAccuracy:verticalAccuracy]; ``` **Tip:** For more information, see [`Braze.User.swift`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/user-swift.class/). ## Prerequisites Before you can use this feature, you'll need to [integrate the React Native Braze SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=react%20native). ## Setting the last known location To manually set the last known location for a user, use the `setLastKnownLocation` method. This is useful if you collect location data outside of the Braze SDK. ```javascript Braze.setLastKnownLocation(LATITUDE, LONGITUDE, ALTITUDE, HORIZONTAL_ACCURACY, VERTICAL_ACCURACY); ``` - On Android, `latitude` and `longitude` are required. `altitude`, `horizontalAccuracy`, and `verticalAccuracy` are optional. - On iOS, `latitude`, `longitude`, and `horizontalAccuracy` are required. `altitude` and `verticalAccuracy` are optional. For cross-platform compatibility, provide `latitude`, `longitude`, and `horizontalAccuracy` at a minimum. ## Setting a custom location attribute To set a custom location attribute on a user profile, use the `setLocationCustomAttribute` method. ```javascript Braze.setLocationCustomAttribute("favorite_restaurant", 40.7128, -74.0060, optionalCallback); ``` ## Requesting location initialization (Android only) Call `requestLocationInitialization` after a user grants location permissions to initialize Braze location features on Android. This method is not supported on iOS and is not required for iOS geofence or location features. ```javascript Braze.requestLocationInitialization(); ``` ## Geofences Geofences are supported on both iOS and Android. By default, the Braze SDK can automatically request and monitor geofences when location is available. You can rely on this automatic configuration for most integrations. ### Manually requesting geofences To manually request a geofence update for a specific GPS coordinate, use `requestGeofences`. This is available on both iOS and Android. If you use this method, disable automatic geofence requests in your native configuration so the SDK does not overwrite your manual requests. ```javascript Braze.requestGeofences(LATITUDE, LONGITUDE); ``` # Braze SDK를 통한 제거 추적 Source: /docs/ko/developer_guide/analytics/tracking_uninstalls/index.md # 제거 추적 > Braze SDK를 통해 제거 추적을 설정하는 방법을 알아보세요. 일반적인 정보는 [사용자 가이드를 참조하세요: 추적 제거](https://www.braze.com/docs/ko/ko/user_guide/analytics/tracking/uninstall_tracking). ## Setting up uninstall tracking ### Step 1: Set up FCM The Android Braze SDK uses Firebase Cloud Messaging (FCM) to send silent push notifications, which are used to collect uninstall tracking analytics. If you haven't already, [set up](https://www.braze.com/docs/ko/ko/developer_guide/platforms/android/push_notifications/#setting-up-push-notifications) or [migrate to](https://www.braze.com/docs/ko/ko/developer_guide/push_notifications/?sdktab=android) the Firebase Cloud Messaging API for push notifications. ### Step 2: Manually detect uninstall tracking (optional) By default, the Android Braze SDK will automatically detect and ignore silent push notifications related to uninstall tracking. However, you choose to manually detect uninstall tracking using the [`isUninstallTrackingPush()`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.models.push/-braze-notification-payload/is-uninstall-tracking-push.html) method. **Important:** Because silent notifications for uninstall tracking are not forwarded to any Braze push callbacks, you can only use this method before you pass a push notification to Braze. ### Step 3: Remove automatic server pings A silent push notification will wake your app and instantiate the `Application` component if it app isn't already running. So, if you have a custom [`Application`](https://developer.android.com/reference/android/app/Application) subclass, remove any logic that automatically pings your servers during your [`Application.onCreate()`](https://developer.android.com/reference/android/app/Application#onCreate()) lifecycle method. ### Step 4: Enable uninstall tracking Finally, enable uninstall tracking in Braze. For a full walkthrough, see [Enable uninstall tracking](https://www.braze.com/docs/ko/ko/user_guide/data_and_analytics/tracking/uninstall_tracking/#uninstall-tracking). **Important:** Tracking uninstalls can be imprecise. The metrics you see on Braze may be delayed or inaccurate. ## Setting up uninstall tracking ### Step 1: Enable background push In your Xcode project, go to **Capabilities** and ensure you have **Background Modes** enabled. For more information, see [silent push notification](https://www.braze.com/docs/ko/ko/developer_guide/push_notifications/silent/?sdktab=swift). ### Step 2: Ignore internal push notifications The Swift Braze SDK uses background push notifications to collect uninstall tracking analytics. To ensure your app doesn't make unwanted actions when these are sent, you'll need to ensure that [internal push notifications are ignored](https://www.braze.com/docs/ko/ko/developer_guide/push_notifications/silent/?sdktab=swift#swift_ignoring-internal-push-notifications). ### Step 3: Send a test push (optional) Next, send yourself a test push notification from the Braze dashboard (don't worry—it won't update your user profile). 1. Go to **Messaging** > **Campaigns** and create a push notification campaign using the relevant platform. 2. Go to **Settings** > **App Settings** and add the `appboy_uninstall_tracking` key with relevant `true` value, then check **Add Content-Available Flag**. 3. Use the **Preview** page to send yourself a test uninstall tracking push. 4. Check that your app does not make any unwanted automatic actions when it receives a push notification. **Note:** A badge number will be sent along with the test push notification—however a real uninstall tracking push won't send any badge numbers. ### Step 3: Enable uninstall tracking Finally, enable uninstall tracking in Braze. For a full walkthrough, see [Enable uninstall tracking](https://www.braze.com/docs/ko/ko/user_guide/data_and_analytics/tracking/uninstall_tracking/#uninstall-tracking). **Important:** Tracking uninstalls can be imprecise. The metrics you see on Braze may be delayed or inaccurate. # Braze SDK 데이터 수집 관리 Source: /docs/ko/developer_guide/analytics/managing_data_collection/index.md # 데이터 수집 관리 {#manage-data-collection} > 필요에 따라 데이터 개인정보 보호 규정을 준수할 수 있도록 Braze SDK의 데이터 수집을 관리하는 방법을 알아보세요. ## Disabling data tracking **Note:** This guide uses code samples from the Braze Web SDK 4.0.0+. To upgrade to the latest Web SDK version, see [SDK Upgrade Guide](https://github.com/braze-inc/braze-web-sdk/blob/master/UPGRADE_GUIDE.md). To disable data-tracking activity on the Web SDK, use the method [`disableSDK()`](https://js.appboycdn.com/web-sdk/latest/doc/modules/braze.html#disablesdk). This will sync any data logged before `disableSDK()` was called, and will cause all subsequent calls to the Braze Web SDK for this page and future page loads to be ignored. Use the **Disable Tracking** or **Resume Tracking** tag type to disable or re-enable web tracking, respectively. These two options call [`disableSDK`](https://js.appboycdn.com/web-sdk/latest/doc/modules/braze.html#disablesdk) and [`enableSDK`](https://js.appboycdn.com/web-sdk/latest/doc/modules/braze.html#enablesdk). ### Best practices To provide users with the option to stop tracking, we recommend building a simple page with two links or buttons: one that calls `disableSDK()` when clicked, and another that calls `enableSDK()` to allow users to opt back in. You can use these controls to start or stop tracking via other data sub-processors as well. **Note:** The Braze SDK does not need to be initialized to call `disableSDK()`, allowing you to disable tracking for fully anonymous users. Conversely,`enableSDK()` does not initialize the Braze SDK so you must also call `initialize()` afterward to enable tracking. ## Resuming data tracking To resume data collection, you can use the [`enableSDK()`](https://js.appboycdn.com/web-sdk/latest/doc/modules/braze.html#enablesdk) method. ## Google Play privacy questionnaire {#privacy-questionnaire} Starting in April 2022, Android developers must complete Google Play's [Data safety form](https://support.google.com/googleplay/android-developer/answer/10787469) to disclose privacy and security practices. This guide provides instructions on how to fill out this new form with information on how Braze handles your app data. As the app developer, you are in control of what data you send to Braze. Data received by Braze is processed according to your instructions. This is what Google classifies as a [service provider](https://support.google.com/googleplay/android-developer/answer/10787469?hl=en#zippy=%2Cwhat-kinds-of-activities-can-service-providers-perform). **Important:** This article provides information related to the data the Braze SDK processes as related to the Google safety section questionnaire. This article is not providing legal advice, so we recommend consulting with your legal team before submitting any information to Google. ### Questions |Questions|Answers for Braze SDK| |---|---| |Does your app collect or share any of the required user data types?|Yes, the Braze Android SDK collects data as configured by the app developer. | |Is all of the user data collected by your app encrypted in transit?|Yes.| |Do you provide a way for users to request that their data be deleted?|Yes.| {: .reset-td-br-1 .reset-td-br-2 aria-label="Questions" } For more information about handling user requests for their data and deletion, see [Braze Data Retention Information](https://www.braze.com/docs/ko/ko/api/data_retention/). ### Data collection The data collected by Braze is determined by your specific integration and the user data you choose to collect. To learn more about what data Braze collects by default and how to disable certain attributes, see our [SDK data collection options](https://www.braze.com/docs/ko/ko/user_guide/data_and_analytics/user_data_collection/sdk_data_collection/#minimum-integration).
Category Data type Braze Usage
Location Approximate location Not collected by default.
Precise location
Personal Info Name
Email address
User IDs
Address
Phone number
Race and ethnicity
Political or religious beliefs
Sexual orientation
Other info
Financial info User payment info
Purchase history
Credit score
Other financial info
Health and fitness Health info Not collected by default.
Fitness info
Messages Emails Not collected by default.
SMS or MMS
Other in-app messages If you send In-app messages or push notifications through Braze, we collect information on when users have opened or read these messages.
Photos and videos Photos Not collected.
Videos
Audio files Voice or sound recordings
Music files
Other audio files
Files and docs Files and docs
Calendar Calendar events
Contacts Contacts
App activity App interactions Braze collects session activity data by default. All other interactions and activity is determined by your app's custom integration.
In-app search history Not collected.
Installed apps Not collected.
Other user-generated content Not collected by default.
Other actions
Web browsing Web browsing history Not collected.
App information and performance Crash logs Braze collects crash logs for errors that occur within the SDK. This contains the user's phone model and OS level, along with a Braze specific user ID.
Diagnostics Not collected.
Other app performance data Not collected.
Device or other IDs Device or other IDs Braze generates a device ID to differentiate users' devices, and checks if messages are sent to the correct intended device.
To learn more about other device data that Braze collects which may fall outside the scope of Google Play's data safety guidelines, see our [Android storage overview](https://www.braze.com/docs/ko/ko/developer_guide/storage/?tab=android) and our [SDK data collection options](https://www.braze.com/docs/ko/ko/user_guide/data_and_analytics/user_data_collection/sdk_data_collection/#minimum-integration). ## Disabling data tracking To disable data-tracking activity on the Android SDK, use the method [`disableSDK()`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze/-braze/-companion/disable-sdk.html). This will cause all network connections to be canceled, meaning the Braze SDK will no longer pass any data to Braze servers. ## Wiping previously-stored data You can use the method [`wipeData()`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze/-braze/-companion/wipe-data.html) to fully clear all client-side data stored on the device. ## Resuming data tracking To resume data collection, you can use the [`enableSDK()`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze/-braze/-companion/enable-sdk.html) method. Keep in mind, this will not restore any previously wiped data. ## Apple's privacy manifest {#privacy-manifest} ### What is tracking data? Apple defines "tracking data" as data collected in your app about an end-user or device that's linked to third-party data (such as targeted advertising), or a data broker. For a complete definition with examples, see [Apple: Tracking](https://developer.apple.com/app-store/app-privacy-details/#user-tracking). By default, the Braze SDK does not collect tracking data. However, depending on your Braze SDK configuration, you may be required to list Braze-specific data in your app's privacy manifest. ### What is a privacy manifest? A privacy manifest is a file in your Xcode project that describes the reason your app and third-party SDKs collect data, along with their data-collection methods. Each of your third-party SDKs that track data require its own privacy manifest. When you [create your app's privacy report](https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_data_use_in_privacy_manifests#4239187), these privacy manifest files are automatically aggregated into a single report. ### API tracking-data domains Starting with iOS 17.2, Apple will block all declared tracking endpoints in your app until the end-user accepts an [Ad Tracking Transparency (ATT) prompt](https://support.apple.com/en-us/HT212025). Braze provides tracking endpoints to route your tracking data, while still allowing you to route non-tracking first-party data to the original endpoint. ## Declaring Braze tracking data **Tip:** For a full walkthrough, see the [Privacy Tracking Data tutorial](https://braze-inc.github.io/braze-swift-sdk/tutorials/braze/e1-privacy-tracking/). ### Prerequisites The following Braze SDK version is required to implement this feature: ### Step 1: Review your current policies Review your Braze SDK's current data-collection policies with your legal team to determine whether your app collects tracking data [as defined by Apple](#what-is-tracking-data). If you're not collecting any tracking data, you don't need to customize your privacy manifest for the Braze SDK at this time. For more information about the Braze SDK's data-collection policies, see [SDK data collection](https://www.braze.com/docs/ko/ko/user_guide/data/user_data_collection/sdk_data_collection/). **Important:** If any of your non-Braze SDKs collect tracking data, you'll need to review those policies separately. ### Step 2: Create a privacy manifest First, check if you already have a privacy manifest by searching for a `PrivacyInfo.xcprivacy` file in your Xcode project. If you already have this file, you can continue to the next step. Otherwise, see [Apple: Create a privacy manifest](sdk-tracking.iad-01.braze.com). ### Step 3: Add your endpoint to the privacy manifest In your Xcode project, open your app's `PrivacyInfo.xcprivacy` file, then right-click the table and check **Raw Keys and Values**. ![An Xcode project with the context menu open and "Raw Keys and Values" highlighted.](https://www.braze.com/docs/ko/ko/assets/img/apple/privacy_manifest/check_raw_keys_and_values.png?670eead9e29da0d52e7ae1a6d6205194) Under **App Privacy Configuration**, choose **NSPrivacyTracking** and set its value to **YES**. ![The 'PrivacyInfo.xcprivacy' file open with "NSPrivacyTracking" set to "YES".](https://www.braze.com/docs/ko/ko/assets/img/apple/privacy_manifest/add_nsprivacytracking.png?02325a36076d8716d2d1e340f7a8ecd7) Under **App Privacy Configuration**, choose **NSPrivacyTrackingDomains**. In the domains array, add a new element and set its value to the endpoint you [previously added to your `AppDelegate`](https://www.braze.com/docs/ko/ko/developer_guide/platform_integration_guides/swift/initial_sdk_setup/completing_integration/#update-your-app-delegate) prefixed with `sdk-tracking`. ![The 'PrivacyInfo.xcprivacy' file open with a Braze tracking endpoint listed under "NSPrivacyTrackingDomains".](https://www.braze.com/docs/ko/ko/assets/img/apple/privacy_manifest/add_nsprivacytrackingdomains.png?eb07fc3447c9380e0a20b912f9b22630) ### Step 4: Declare your tracking data Next, open `AppDelegate.swift` then list each [tracking property](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/configuration-swift.class/trackingproperty/) you want to declare by creating a static or dynamic tracking list. Keep in mind, Apple will block these properties until the end-user accepts their ATT prompt, so only list the properties you and your legal team consider tracking. For example: In the following example, `dateOfBirth`, `customEvent`, and `customAttribute` are declared as tracking data within a static list. ```swift import UIKit import BrazeKit @main class AppDelegate: UIResponder, UIApplicationDelegate { static var braze: Braze? = nil func application( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? ) -> Bool { let configuration = Braze.Configuration(apiKey: brazeApiKey, endpoint: brazeEndpoint) // Declare which types of data you wish to collect for user tracking. configuration.api.trackingPropertyAllowList = [ .dateOfBirth, .customEvent(["event-1"]), .customAttribute(["attribute-1", "attribute-2"]) ] let braze = Braze(configuration: configuration) AppDelegate.braze = braze return true } } ``` In the following example, the tracking list is automatically updated after the end-user accepts the ATT prompt. ```swift func applicationDidBecomeActive(_ application: UIApplication) { // Request and check your user's tracking authorization status. ATTrackingManager.requestTrackingAuthorization { status in // Let Braze know whether user data is allowed to be collected for tracking. let enableAdTracking = status == .authorized AppDelegate.braze?.set(adTrackingEnabled: enableAdTracking) // Add the `.firstName` and `.lastName` properties, while removing the `.everything` configuration. AppDelegate.braze.updateTrackingAllowList( adding: [.firstName, .lastName], removing: [.everything] ) } } ``` ### Step 5: Prevent infinite retry loops To prevent the SDK from entering an infinite retry loop, use the `set(adTrackingEnabled: enableAdTracking)` method to handle ATT permissions. The `adTrackingEnabled` property in your method should be handled similar to the following: ```swift func applicationDidBecomeActive(_ application: UIApplication) { // Request and check your user's tracking authorization status. ATTrackingManager.requestTrackingAuthorization { status in // Let Braze know whether user data is allowed to be collected for tracking. let enableAdTracking = status == .authorized AppDelegate.braze?.set(adTrackingEnabled: enableAdTracking) } } ``` ## Disabling data tracking To disable data-tracking activity on the Swift SDK, set the [`enabled`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/enabled) property to `false` on your Braze instance. When `enabled` is set to `false`, the Braze SDK ignores any calls to the public API. The SDK also cancels all in-flight actions, such as network requests, event processing, etc. ## Wiping previously-stored data You can use the [`wipeData()`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/wipedata()) method to fully clear locally-stored SDK data on a user's device. For Braze Swift versions 7.0.0 and later, the SDK and the `wipeData()` method randomly generates a UUID for their device ID. However, if your `useUUIDAsDeviceId` is set to `false` _or_ you're using Swift SDK version 5.7.0 or earlier, you'll also need to make a post request to [`/users/delete`](https://www.braze.com/docs/ko/ko/api/endpoints/user_data/post_user_delete/) since your Identifier for Vendors (IDFV) will automatically be used as that user's device ID. If you use manual push integration, and your app calls `wipeData()` and later re-enables the SDK in the same app run, call `registerForRemoteNotifications()` again so Braze can receive a refreshed device token. For more information, see [setting up push notifications](https://www.braze.com/docs/ko/ko/developer_guide/push_notifications/?sdktab=swift). ## Resuming data tracking To resume data collection, set [`enabled`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/enabled/) to `true`. Keep in mind, this will not restore any previously wiped data. ## IDFV collection In previous versions of the Braze iOS SDK, the IDFV (Identifier for Vendor) field was automatically collected as the user's device ID. Beginning in Swift SDK `v5.7.0`, the IDFV field was optionally disabled, and instead, Braze would set a random UUID as the device ID. Starting in Swift SDK `v7.0.0`, the IDFV field will not be collected by default, and a UUID will be set as the device ID instead. The `useUUIDAsDeviceId` feature configures the [Swift SDK](https://github.com/braze-inc/braze-swift-sdk) to set the device ID as a UUID. Traditionally, the iOS SDK would assign the device ID equal to the Apple-generated IDFV value. With this feature enabled by default on your iOS app, all new users created via the SDK would be assigned a device ID equal to a UUID. If you still want to collect IDFV separately, you can use [`set(identifierforvendor:)`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/set(identifierforvendor:)). ### Considerations #### SDK Version In Swift SDK `v7.0.0+`, when `useUUIDAsDeviceId` is enabled (default), all new users created will be assigned a random device ID. All previously existing users will maintain their same device ID value, which may have been IDFV. When this feature is not enabled, devices will continue to be assigned IDFV upon creation. #### Downstream **Technology partners**: When this feature is enabled, any technology partners that derive the IDFV value from the Braze device ID will no longer have access to this data. If the IDFV value derived from the device is needed for your partner integration, we recommend that you set this feature to `false`. **Currents**: `useUUIDAsDeviceId` set to true means the device ID sent in Currents will no longer equal the IDFV value. ### Frequently asked questions #### Will this change impact my existing users in Braze? No. When enabled, this feature will not overwrite any user data in Braze. New UUID device IDs will only be created for new devices or when `wipedata()` is called. #### Can I turn this feature off after turning it on? Yes, this feature can be toggled on and off at your discretion. Previously stored device IDs will never be overwritten. #### Can I still capture the IDFV value via Braze elsewhere? Yes, you can still optionally collect the IDFV via the Swift SDK (collection is disabled by default). ## Prerequisites Before you can use this feature, you'll need to [integrate the React Native Braze SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=react%20native). ## Disabling data tracking To disable data collection, use the `disableSDK` method. After calling this method, the Braze SDK stops sending data to Braze servers. ```javascript Braze.disableSDK(); ``` ## Resuming data tracking To resume data collection after disabling it, use the `enableSDK` method. ```javascript Braze.enableSDK(); ``` ## Wiping data To delete all locally stored Braze SDK data on the device, use the `wipeData` method. After calling this method, the SDK is disabled and must be re-enabled with `enableSDK`. ```javascript Braze.wipeData(); ``` ## Flushing data To request an immediate flush of any pending data to Braze servers, use `requestImmediateDataFlush`. ```javascript Braze.requestImmediateDataFlush(); ``` ## Setting ad-tracking enabled To inform Braze whether ad-tracking is enabled for this device, use the `setAdTrackingEnabled` method. The SDK does not automatically collect this data. ```javascript Braze.setAdTrackingEnabled(true, "GOOGLE_ADVERTISING_ID"); ``` The second parameter is the Google Advertising ID and is only used on Android. ## Updating the tracking property allow list (iOS only) To update the list of data types declared for tracking, use `updateTrackingPropertyAllowList`. This is a no-op on Android. ```javascript Braze.updateTrackingPropertyAllowList({ adding: [Braze.TrackingProperty.EMAIL, Braze.TrackingProperty.FIRST_NAME], removing: [], addingCustomEvents: ["my_custom_event"], removingCustomEvents: [], addingCustomAttributes: ["my_custom_attribute"], removingCustomAttributes: [] }); ``` For more information, refer to [Privacy Manifest](https://www.braze.com/docs/ko/ko/developer_guide/platform_integration_guides/swift/privacy_manifest/). ## Prerequisites Before you can use this feature, you'll need to [integrate the Roku Braze SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=roku). ## Wiping previously-stored data The Roku SDK doesn't include a `wipeData` method. To produce a clean-slate state functionally equivalent to `wipeData()` on other Braze SDKs, clear the four Braze registry sections, then re-initialize the SDK. The Braze Roku SDK persists data across the following registry sections: | Section | Contents | |---------|----------| | `braze.section.device_id` | The device UUID used to identify this device in Braze. | | `braze.section.user_id` | The external user ID, if one has been set. | | `braze.section.session` | The active session UUID, start time, and end time. | | `braze.section.config` | Cached SDK configuration and feature flag data. | {: .reset-td-br-1 .reset-td-br-2 aria-label="Wiping previously-stored data" } ### Step 1: Clear the registry sections Use [`roRegistry.Delete()`](https://developer.roku.com/docs/references/brightscript/components/roregistry.md) to delete each Braze section, then call `Flush()` to persist the changes: ```brightscript sub WipeBrazeData() registry = CreateObject("roRegistry") registry.Delete("braze.section.device_id") registry.Delete("braze.section.user_id") registry.Delete("braze.section.session") registry.Delete("braze.section.config") registry.Flush() end sub ``` ### Step 2: Re-initialize the Braze SDK When you [initialize the Braze SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=roku) again, the SDK handles the missing registry data gracefully: - The device ID section is empty, so the SDK generates a new UUID and treats the device as anonymous. - The user ID section is empty, so the SDK defaults to an anonymous user (an empty string `""`). - The session section is empty, so the SDK starts a new session. - The config section is empty, so the SDK re-fetches the configuration from the server. **Note:** The Roku SDK doesn't generate any server-side delete request when you clear the registry. If you also need to remove the user from Braze, send a request to [`/users/delete`](https://www.braze.com/docs/ko/ko/api/endpoints/user_data/post_user_delete/) using the user's `external_id` or `braze_id`. # Braze MCP 서버에 대하여 Source: /docs/ko/developer_guide/mcp_server/index.md # The Braze MCP server > Learn about the Braze MCP server, a secure connection that lets AI tools like Claude and Cursor access non-PII Braze data to answer questions, analyze trends, and provide insights. **Important:** The locally hosted Braze MCP server (beta) is sunsetting this summer. It will continue to work, but we're no longer adding endpoints or supporting the beta. A remote, Braze-hosted MCP server is coming to Early Access this summer. **Important:** ## Sunsetting the locally hosted Braze MCP server This summer, Braze is launching a remote, Braze-hosted MCP server in Early Access. It replaces the locally hosted beta server (`braze-mcp-server` on [PyPI](https://pypi.org/project/braze-mcp-server/) and the Claude Desktop extension directory). **What this means for you:** - The locally hosted server will continue to work, but it is no longer supported. We won't be adding new endpoints or fixing issues in the beta. - When the remote server is available in Early Access, you'll need to switch to it. The remote server requires no local installation, uses OAuth instead of static API keys, and works with MCP clients like Claude, Copilot, Gemini CLI, Codex, and Cursor. - Watch this page for Early Access availability, or contact your Braze account team to express interest. ## What is Model Context Protocol (MCP)? ​​Model Context Protocol, or MCP, is a standard that lets AI agents connect to and work with data from another platform. It has two main parts: - **MCP client:** The application where the AI agent runs, such as Cursor or Claude. - **MCP server:** A service provided by another platform, like Braze, that defines which tools the AI can use and what data it can access. ## About the Braze MCP server After [setting up the Braze MCP server], you can connect AI tools like agents, assistants, and chatbots directly to Braze, allowing them to read aggregated data such as Canvas and Campaign analytics, custom attributes, segments, and more. The Braze MCP server is great for: - Building AI-powered tools that need Braze context. - CRM engineers creating multi-step agent workflows. - Technical marketers experimenting with natural language queries. The Braze MCP server includes both read-only and write endpoints. They do not return data from Braze user profiles. You choose which endpoints to assign to your Braze API key, and that choice controls what an agent can read, create, or update. For the full list of available endpoints and their required permissions, see [Available API functions]. **Warning:** Only assign the API key permissions you want your agent to have. If you don't want your agent to make changes in Braze, leave any write permissions off when you create your API key. Agents may try to write data through any write permission you grant. ## Usage example You can interact with Braze through natural language using tools like Claude or Cursor. For other examples and best practices, see [Using the Braze MCP server]. **Example prompt:** `What are my available Braze functions?` **Example response:** Used `list_functions` and returned the available Braze MCP function categories. **Example prompt:** `What are my available Braze functions?` **Example response:** Queried `list_functions` and listed functions such as `get_canvas_list`. ## Frequently Asked Questions (FAQ) {#faq} ### Which MCP clients are supported? Only [Claude](https://claude.ai/) and [Cursor](https://cursor.com/) are officially supported. You must have an account for one of these clients to use the Braze MCP server. ### What Braze data can my MCP client access? MCP clients can access endpoints that don't return PII. You control which endpoints an agent can use through the permissions you assign to your API key. ### Can my MCP client change Braze data? Yes. The server exposes a focused set of write endpoints that let agents create or update content in your workspace, such as media library assets, email templates, and content blocks. Each write endpoint requires its own API key permission. If you don't want your agent to make a given change in Braze, leave that permission off when you create your API key. For the full list of write functions and their required permissions, see [Available API functions]. ### Can I use a third-party MCP server for Braze? Using a third-party MCP server for Braze data is not recommended. Only use the official Braze MCP server hosted on [PyPi](https://pypi.org/project/braze-mcp-server/). ### Why doesn't the Braze MCP server offer PII access? To protect user data while supporting valuable use cases, the server is limited to endpoints that don't typically return PII. This reduces risk for your workspace and the people in it. ### Can I reuse my API keys? No. You'll need to create a new API key for your MCP client. Remember to only give your AI tools access to what you’re comfortable with, and avoid elevated permissions. ### Is the Braze MCP server hosted locally or remotely? The currently available Braze MCP server is hosted locally. A remote, Braze-hosted MCP server is coming to Early Access this summer and will replace the locally hosted beta server. ### Why is Cursor only listing functions? Check if you're in ask mode or agent mode. To use the MCP server, you need to be in agent mode. ### What do I do when the agent returns an answer that looks incorrect? When working with tools like Cursor, you may want to try changing the model used. For example, if you have it set to auto, try changing it to a specific model and experiment to find which model performs best for your use case. You can also try starting a new chat and retrying the prompt. If issues persist, you can email us at [mcp-product@braze.com](mailto:mcp-product@braze.com) to let us know. If possible, include a video and expand the call functions so we can see what calls the agent attempted. ## Disclaimer The [Model Context Protocol (MCP)](https://modelcontextprotocol.io/docs/getting-started/intro) is a newly introduced open-source protocol that may be susceptible to security issues or vulnerabilities at this time. Braze MCP Server setup code and instructions are provided by Braze “as is” and without any warranties, and customers use it at their own risk. Braze shall not be responsible for any consequences arising from improper setup, misuse of the MCP, or any potential security issues that may arise. Braze strongly encourages customers to review their configurations carefully and to follow the outlined guidelines to reduce risks associated with the integrity and security of their Braze environment. For assistance or clarification, please contact [Braze Support](https://www.braze.com/docs/ko/ko/user_guide/administrative/access_braze/support). # Braze MCP 서버를 설정하세요 Source: /docs/ko/developer_guide/mcp_server/setup/index.md # Setting up the Braze MCP server > Learn how to set up the Braze MCP server, so you can interact with your Braze data through natural language using tools like Claude and Cursor. For more general information, see [Braze MCP server]. **Important:** The locally hosted Braze MCP server (beta) is sunsetting this summer. It will continue to work, but we're no longer adding endpoints or supporting the beta. A remote, Braze-hosted MCP server is coming to Early Access this summer. ## Prerequisites Before you start, you'll need the following: | Prerequisite | Description | |--------------|-------------| | Braze API Key | A Braze API key with the required permissions. You'll create a new key when you [set up your Braze MCP server](#create-api-key). | | MCP client | [Claude](https://claude.ai/), [Cursor](https://cursor.com/), and [Google Gemini CLI](https://docs.cloud.google.com/gemini/docs/codeassist/gemini-cli) are officially supported. You must have an account for one of these clients to use the Braze MCP server. | | Terminal | A terminal app so you can run commands and install tooling. Use your preferred terminal app or the one that's pre-installed on your computer. | {: .reset-td-br-1 .reset-td-br-2 aria-label="Prerequisites" } ## Setting up the Braze MCP server ### Step 1: Install `uv` First, install `uv`—a [command-line tool by Astral](https://docs.astral.sh/uv/getting-started/installation/) for dependency management and Python package handling. Open your terminal application, paste the following command, then press Enter. ```bash curl -LsSf https://astral.sh/uv/install.sh | sh ``` The output is similar to the following: ```bash $ curl -LsSf https://astral.sh/uv/install.sh | sh downloading uv 0.8.9 aarch64-apple-darwin no checksums to verify installing to /Users/Isaiah.Robinson/.local/bin uv uvx everything's installed! ``` Open Windows PowerShell, paste the following command, then press Enter. ```powershell irm https://astral.sh/uv/install.ps1 | iex ``` The output is similar to the following: ```powershell PS C:\Users\YourUser> irm https://astral.sh/uv/install.ps1 | iex Downloading uv 0.8.9 (x86_64-pc-windows-msvc) no checksums to verify installing to C:\Users\YourUser\.local\bin uv.exe uvx.exe everything's installed! ``` ### Step 2: Create an API key {#create-api-key} The Braze MCP server includes both read-only and write endpoints. They don't return data from Braze user profiles. Write endpoints let agents create or update content in your workspace. To create your API key: 1. Go to **Settings** > **APIs and Identifiers** > **API Keys**. 2. Create a new key. 3. Assign some or all of the following permissions to your key. **Important:** Only assign the permissions you want your agent to use. To prevent your agent from making changes in Braze, leave any write permissions off when you create your API key. **List of supported permissions** #### Campaigns | Endpoint | Required permission | |----------|---------------------| | [`/campaigns/data_series`](https://www.braze.com/docs/ko/ko/api/endpoints/export/campaigns/get_campaign_analytics) | `campaigns.data_series` | | [`/campaigns/details`](https://www.braze.com/docs/ko/ko/api/endpoints/export/campaigns/get_campaign_details) | `campaigns.details` | | [`/campaigns/list`](https://www.braze.com/docs/ko/ko/api/endpoints/export/campaigns/get_campaigns) | `campaigns.list` | | [`/sends/data_series`](https://www.braze.com/docs/ko/ko/api/endpoints/export/campaigns/get_send_analytics) | `sends.data_series` | {: .reset-td-br-1 .reset-td-br-2 aria-label="Campaigns" } #### Canvas | Endpoint | Required permission | |----------|---------------------| | [`/canvas/data_series`](https://www.braze.com/docs/ko/ko/api/endpoints/export/canvas/get_canvas_analytics) | `canvas.data_series` | | [`/canvas/data_summary`](https://www.braze.com/docs/ko/ko/api/endpoints/export/canvas/get_canvas_analytics_summary) | `canvas.data_summary` | | [`/canvas/details`](https://www.braze.com/docs/ko/ko/api/endpoints/export/canvas/get_canvas_details) | `canvas.details` | | [`/canvas/list`](https://www.braze.com/docs/ko/ko/api/endpoints/export/canvas/get_canvases) | `canvas.list` | {: .reset-td-br-1 .reset-td-br-2 aria-label="Canvas" } #### Catalogs | Endpoint | Required permission | |----------|---------------------| | [`/catalogs`](https://www.braze.com/docs/ko/ko/api/endpoints/catalogs/catalog_management/synchronous/get_list_catalogs) | `catalogs.get` | | [`/catalogs/{catalog_name}/items`](https://www.braze.com/docs/ko/ko/api/endpoints/catalogs/catalog_items/synchronous/get_catalog_items_details_bulk) | `catalogs.get_items` | | [`/catalogs/{catalog_name}/items/{item_id}`](https://www.braze.com/docs/ko/ko/api/endpoints/catalogs/catalog_items/synchronous/get_catalog_item_details) | `catalogs.get_item` | {: .reset-td-br-1 .reset-td-br-2 aria-label="Catalogs" } #### Cloud Data Ingestion | Endpoint | Required permission | |----------|---------------------| | [`/cdi/integrations`](https://www.braze.com/docs/ko/ko/api/endpoints/cdi/get_integration_list) | `cdi.integration_list` | | [`/cdi/integrations/{integration_id}/job_sync_status`](https://www.braze.com/docs/ko/ko/api/endpoints/cdi/get_job_sync_status) | `cdi.integration_job_status` | {: .reset-td-br-1 .reset-td-br-2 aria-label="Cloud Data Ingestion" } #### Content Blocks The `content_blocks.create` and `content_blocks.update` permissions are write permissions. Add them only if you want your agent to create or update content blocks in your workspace. | Endpoint | Required permission | |----------|---------------------| | [`/content_blocks/list`](https://www.braze.com/docs/ko/ko/api/endpoints/templates/content_blocks_templates/get_list_email_content_blocks) | `content_blocks.list` | | [`/content_blocks/info`](https://www.braze.com/docs/ko/ko/api/endpoints/templates/content_blocks_templates/get_see_email_content_blocks_information) | `content_blocks.info` | | [`/content_blocks/create`](https://www.braze.com/docs/ko/ko/api/endpoints/templates/content_blocks_templates/post_create_email_content_block) | `content_blocks.create` | | [`/content_blocks/update`](https://www.braze.com/docs/ko/ko/api/endpoints/templates/content_blocks_templates/post_update_content_block) | `content_blocks.update` | {: .reset-td-br-1 .reset-td-br-2 aria-label="Content Blocks" } #### Custom Attributes | Endpoint | Required permission | |----------|---------------------| | [`/custom_attributes`](https://www.braze.com/docs/ko/ko/api/endpoints/export/custom_attributes/get_custom_attributes) | `custom_attributes.get` | {: .reset-td-br-1 .reset-td-br-2 aria-label="Custom Attributes" } #### Events | Endpoint | Required permission | |----------|---------------------| | [`/events/list`](https://www.braze.com/docs/ko/ko/api/endpoints/export/custom_events/get_custom_events) | `events.list` | | [`/events/data_series`](https://www.braze.com/docs/ko/ko/api/endpoints/export/custom_events/get_custom_events_analytics) | `events.data_series` | | [`/events`](https://www.braze.com/docs/ko/ko/api/endpoints/export/custom_events/get_custom_events_data) | `events.get` | {: .reset-td-br-1 .reset-td-br-2 aria-label="Events" } #### KPIs | Endpoint | Required permission | |----------|---------------------| | [`/kpi/new_users/data_series`](https://www.braze.com/docs/ko/ko/api/endpoints/export/kpi/get_kpi_daily_new_users_date) | `kpi.new_users.data_series` | | [`/kpi/dau/data_series`](https://www.braze.com/docs/ko/ko/api/endpoints/export/kpi/get_kpi_dau_date) | `kpi.dau.data_series` | | [`/kpi/mau/data_series`](https://www.braze.com/docs/ko/ko/api/endpoints/export/kpi/get_kpi_mau_30_days) | `kpi.mau.data_series` | | [`/kpi/uninstalls/data_series`](https://www.braze.com/docs/ko/ko/api/endpoints/export/kpi/get_kpi_uninstalls_date) | `kpi.uninstalls.data_series` | {: .reset-td-br-1 .reset-td-br-2 aria-label="KPIs" } #### Media Library The `media_library.create` permission is a write permission. Add it only if you want your agent to upload assets to your media library. | Endpoint | Required permission | |----------|---------------------| | [`/media_library/create`](https://www.braze.com/docs/ko/ko/api/endpoints/media_library/manage_assets/create) | `media_library.create` | {: .reset-td-br-1 .reset-td-br-2 aria-label="Media Library" } #### Messages | Endpoint | Required permission | |----------|---------------------| | [`/messages/scheduled_broadcasts`](https://www.braze.com/docs/ko/ko/api/endpoints/messaging/schedule_messages/get_messages_scheduled) | `messages.schedule_broadcasts` | {: .reset-td-br-1 .reset-td-br-2 aria-label="Messages" } #### Preference Center | Endpoint | Required permission | |----------|---------------------| | [`/preference_center/v1/list`](https://www.braze.com/docs/ko/ko/api/endpoints/preference_center/get_list_preference_center) | `preference_center.list` | | [`/preference_center/v1/{preferenceCenterExternalID}`](https://www.braze.com/docs/ko/ko/api/endpoints/preference_center/get_view_details_preference_center) | `preference_center.get` | {: .reset-td-br-1 .reset-td-br-2 aria-label="Preference Center" } #### Purchases | Endpoint | Required permission | |----------|---------------------| | [`/purchases/product_list`](https://www.braze.com/docs/ko/ko/api/endpoints/export/purchases/get_list_product_id) | `purchases.product_list` | | [`/purchases/revenue_series`](https://www.braze.com/docs/ko/ko/api/endpoints/export/purchases/get_revenue_series) | `purchases.revenue_series` | | [`/purchases/quantity_series`](https://www.braze.com/docs/ko/ko/api/endpoints/export/purchases/get_number_of_purchases) | `purchases.quantity_series` | {: .reset-td-br-1 .reset-td-br-2 aria-label="Purchases" } #### Segments | Endpoint | Required permission | |----------|---------------------| | [`/segments/list`](https://www.braze.com/docs/ko/ko/api/endpoints/export/segments/get_segment) | `segments.list` | | [`/segments/data_series`](https://www.braze.com/docs/ko/ko/api/endpoints/export/segments/get_segment_analytics) | `segments.data_series` | | [`/segments/details`](https://www.braze.com/docs/ko/ko/api/endpoints/export/segments/get_segment_details) | `segments.details` | {: .reset-td-br-1 .reset-td-br-2 aria-label="Segments" } #### Sends | Endpoint | Required permission | |----------|---------------------| | [`/sends/data_series`](https://www.braze.com/docs/ko/ko/api/endpoints/export/campaigns/get_send_analytics) | `sends.data_series` | {: .reset-td-br-1 .reset-td-br-2 .reset-td-br-3 aria-label="Sends" } #### Sessions | Endpoint | Required permission | |----------|---------------------| | [`/sessions/data_series`](https://www.braze.com/docs/ko/ko/api/endpoints/export/sessions/get_sessions_analytics) | `sessions.data_series` | {: .reset-td-br-1 .reset-td-br-2 aria-label="Sessions" } #### SDK Authentication Keys | Endpoint | Required permission | |----------|---------------------| | [`/app_group/sdk_authentication/keys`](https://www.braze.com/docs/ko/ko/api/endpoints/sdk_authentication/get_sdk_authentication_keys) | `sdk_authentication.keys` | {: .reset-td-br-1 .reset-td-br-2 aria-label="SDK Authentication Keys" } #### Subscription | Endpoint | Required permission | |----------|---------------------| | [`/subscription/status/get`](https://www.braze.com/docs/ko/ko/api/endpoints/subscription_groups/get_list_user_subscription_group_status) | `subscription.status.get` | | [`/subscription/user/status`](https://www.braze.com/docs/ko/ko/api/endpoints/subscription_groups/get_list_user_subscription_groups) | `subscription.groups.get` | {: .reset-td-br-1 .reset-td-br-2 aria-label="Subscription" } #### Templates The `templates.email.create` and `templates.email.update` permissions are write permissions. Add them only if you want your agent to create or update email templates in your workspace. | Endpoint | Required permission | |----------|---------------------| | [`/templates/email/list`](https://www.braze.com/docs/ko/ko/api/endpoints/templates/email_templates/get_list_email_templates) | `templates.email.list` | | [`/templates/email/info`](https://www.braze.com/docs/ko/ko/api/endpoints/templates/email_templates/get_see_email_template_information) | `templates.email.info` | | [`/templates/email/create`](https://www.braze.com/docs/ko/ko/api/endpoints/templates/email_templates/post_create_email_template) | `templates.email.create` | | [`/templates/email/update`](https://www.braze.com/docs/ko/ko/api/endpoints/templates/email_templates/post_update_email_template) | `templates.email.update` | {: .reset-td-br-1 .reset-td-br-2 aria-label="Templates" } **Warning:** Don't reuse an existing API key. Create one specifically for your MCP client. Assign only the permissions your agent needs. Agents may try to use any permission you grant, so leave any write permissions off if you don't want your agent to make changes in Braze. ### Step 3: Get your identifier and endpoint When you configure your MCP client, you'll need your API key's identifier and your workspace's REST endpoint. To get these details, go back to the **API Keys** page in the dashboard—keep this page open, so you can reference it during [the next step](#configure-client). ![The 'API Keys' in Braze showing a newly created API key and the user's REST endpoint.](https://www.braze.com/docs/ko/ko/assets/img/mcp_server/get_indentifer_and_endpoint.png?e439a42a44d6fcaeb410fb209b2c39bd){: style="max-width:85%;"} ### Step 4: Configure your MCP client {#configure-client} Configure your MCP client using the pre-provided configuration file. Set up your MCP server using the [Claude Desktop](https://claude.ai/download) connector directory. 1. In Claude Desktop, go to **Settings** > **Connectors** > **Browse Connectors** > **Desktop Extensions** > **Braze MCP Server** > **Install**. 2. Enter your API key and base URL. 3. Save the configuration and restart Claude Desktop. In [Cursor](https://cursor.com/), go to **Settings** > **Tools and Integrations** > **MCP Tools** > **Add Custom MCP**, then add the following snippet: ```json { "mcpServers": { "braze": { "command": "uvx", "args": ["--native-tls", "braze-mcp-server@latest"], "env": { "BRAZE_API_KEY": "your-braze-api-key", "BRAZE_BASE_URL": "your-braze-endpoint-url" } } } } ``` Replace `key-identifier` and `rest-endpoint` with the corresponding values from the **API Keys** page in Braze. Your configuration should be similar to the following: ```json { "mcpServers": { "braze": { "command": "uvx", "args": ["--native-tls", "braze-mcp-server@latest"], "env": { "BRAZE_API_KEY": "2e8b-3c6c-d12e-bd75-4f0e2a8e5c71", "BRAZE_BASE_URL": "https://torchie.braze.com" } } } } ``` When you're finished, save the configuration and restart Cursor. Gemini CLI reads user settings from `~/.gemini/settings.json`. If this doesn't exist, you can create it by running the following in your terminal: ```powershell mkdir -p ~/.gemini nano ~/.gemini/settings.json ``` Next, replace `yourname` with the exact string before `@BZXXXXXXXX` in your terminal prompt. Then, replace `key-identifier` and `rest-endpoint` with the corresponding values from the **API Keys** page in Braze. Your configuration should be similar to the following: ```json { "mcpServers": { "braze": { "command": "/Users/yourname/.local/bin/uvx", "args": ["--native-tls", "braze-mcp-server@latest"], "env": { "BRAZE_API_KEY": "2e8b-3c6c-d12e-bd75-4f0e2a8e5c71", "BRAZE_BASE_URL": "https://torchie.braze.com" } } } } ``` When you're finished, save the configuration and restart Gemini CLI. Then, in Gemini, run the following commands to verify that the Braze MCP server is listed and that the tools and schema are available for use: ```powershell gemini /mcp /mcp desc /mcp schema ``` You should see the `braze` server listed with the tools and schema available for use. ### Step 5: Send a test prompt After you set up the Braze MCP server, try sending a test prompt to your MCP client. For other examples and best practices, see [Using the Braze MCP server]. **Example prompt:** `What are my available Braze functions?` **Example response:** Used `list_functions` and returned the available Braze MCP function categories. **Example prompt:** `What are my available Braze functions?` **Example response:** Queried `list_functions` and listed functions such as `get_canvas_list`. **Example prompt:** `What are my available Braze functions?` **Example response:** Queried `list_functions` in Gemini CLI and returned available Braze MCP function categories and sample functions. ## Troubleshooting ### Terminal errors #### `uvx` command not found If you receive an error that `uvx` command not found, reinstall `uv` and restart your terminal. ```bash curl -LsSf https://astral.sh/uv/install.sh | sh ``` #### `spawn uvx ENOENT` error If you receive a `spawn uvx ENOENT` errors, you may need to update the filepath in your client's config file. First, open your terminal and run the following command: ```bash which uvx ``` The command should return a message similar to the following: ```bash /Users/alex-lee/.local/bin/uvx ``` Copy the message to your clipboard and open [your client's config file](#configure-client). Replace `"command": "uvx"` with the path you copied, then restart your client. For example: ```json "command": "/Users/alex-lee/.local/bin/uvx" ``` #### Package installation fails If your package installation fails, try installing a specific Python version instead. ```bash uvx --python 3.12 braze-mcp-server@latest ``` ### Client configuration #### "This extension is not compatible with your device" If you see this error when installing the Braze MCP server extension, it may indicate one of the following: - **Your device doesn't meet the requirements**: Some MCP server extensions require specific operating system versions or hardware. - **Missing development tools (macOS only)**: On macOS, the extension installation requires command line developer tools to run Python commands. If these tools aren't installed, the installation will fail with this error. To install command line developer tools on macOS, run the following in your terminal: ```bash xcode-select --install ``` After installation completes, restart your MCP client and try installing the extension again. #### MCP client can't find the Braze server 1. Verify your MCP client configuration syntax is correct. 2. Restart your MCP client after configuration changes. 3. Check that `uvx` is in your system `PATH`. #### Authentication errors 1. Verify your `BRAZE_API_KEY` is correct and active. 2. Ensure your `BRAZE_BASE_URL` matches your Braze instance. 3. Check that your API key has the [correct permissions](#create-api-key). #### Connection timeouts or network errors 1. Verify your `BRAZE_BASE_URL` is correct for your instance. 2. Check your network connection and firewall settings. 3. Ensure you're using HTTPS in your base URL. ## Disclaimer The [Model Context Protocol (MCP)](https://modelcontextprotocol.io/docs/getting-started/intro) is a newly introduced open-source protocol that may be susceptible to security issues or vulnerabilities at this time. Braze MCP Server setup code and instructions are provided by Braze “as is” and without any warranties, and customers use it at their own risk. Braze shall not be responsible for any consequences arising from improper setup, misuse of the MCP, or any potential security issues that may arise. Braze strongly encourages customers to review their configurations carefully and to follow the outlined guidelines to reduce risks associated with the integrity and security of their Braze environment. For assistance or clarification, please contact [Braze Support](https://www.braze.com/docs/ko/ko/user_guide/administrative/access_braze/support). # Braze MCP 서버 사용하기 Source: /docs/ko/developer_guide/mcp_server/usage/index.md # Using the Braze MCP server > Learn how to interact with your Braze data through natural language using tools like Claude and Cursor. For more general information, see [Braze MCP server]. **Important:** The locally hosted Braze MCP server (beta) is sunsetting this summer. It will continue to work, but we're no longer adding endpoints or supporting the beta. A remote, Braze-hosted MCP server is coming to Early Access this summer. ## Prerequisites Before you can use this feature, you'll need to [set up the Braze MCP server]. ## Best practices When using the Braze MCP server through natural-language tools like Claude and Cursor, keep these tips in mind to get the best results: - LLMs can make mistakes, so always be sure to double-check their answers. - For data analysis, be clear about the time range you need. Shorter ranges often give more accurate results. - Use exact [Braze terminology](https://www.braze.com/resources/articles/glossary) so your LLM calls the right function. - If results seem incomplete, prompt your LLM to continue or dig deeper. - Try creative prompts! Depending on your MCP client, you may be able to export a CSV or other useful files. ## Usage examples After [setting up the Braze MCP server], you can interact with Braze through natural language using tools like Claude or Cursor. Here's some examples to get you started: ### What are my available Braze functions? **Example prompt** `What are my available Braze functions?` **Example response** I checked the Braze MCP server with `list_functions` and found 38 available API functions. Here are a few examples by category: - **Campaign analytics and management** - `get_campaign_dataseries` - `get_campaign_details` - `get_campaign_list` - **Canvas analytics and management** - `get_canvas_data_series` - `get_canvas_data_summary` - `get_canvas_list` - `get_canvas_details` - **Templates** - `create_email_template` - **Content blocks** - `create_content_block` - `get_content_blocks` - `get_content_block_info` - `update_content_block` **Example prompt** `What are my available Braze functions?` **Example response** I'll check your available functions using the Braze MCP server. - Called `list_functions` Based on that result, you have 38 Braze API functions organized into categories such as: - Campaign analytics and management - Canvas analytics and management - Templates - Content blocks Example functions include: - `get_campaign_list` - `get_canvas_list` - `get_canvas_details` - `create_email_template` - `update_content_block` For more information on the `list_functions` function, see [Available API functions]. ### Get details about a Canvas ID **Example prompt** `Get details about canvas ID YOUR-TEST-CANVAS-ID-123` **Example response** I retrieved details for `YOUR-TEST-CANVAS-ID-123` using `get_canvas_details`. - **Canvas name:** `YOUR-TEST-CANVAS-NAME` - **ID:** `YOUR-TEST-CANVAS-ID-123` - **Status:** Draft (not enabled) - **Created at:** Jan 15, 2026 10:30 AM UTC - **Last updated:** Jan 16, 2026 02:45 PM UTC - **Schedule type:** Time-based - **Primary channel:** Email Email message details: - **Subject:** `YOUR-TEST-SUBJECT-LINE` - **From:** `YOUR-TEST-SENDER@example.com` - **Message ID:** `YOUR-TEST-MESSAGE-ID-123` **Example prompt** `Get details about canvas ID YOUR-TEST-CANVAS-ID-123` **Example response** I pulled this Canvas using `get_canvas_details`: - **Canvas ID:** `YOUR-TEST-CANVAS-ID-123` - **Canvas name:** `YOUR-TEST-CANVAS-NAME` - **Status:** Draft - **Channel:** Email - **Message ID:** `YOUR-TEST-MESSAGE-ID-123` Email content summary: 1. Header section with brand image 2. Main headline section 3. Hero section with CTA button 4. Feature section with supporting content For more information on the `get_canvas_details` function, see [Available API functions]. ### Show me my recent Canvases **Example prompt** `Show me my recent Canvases` **Example response** I checked your available tools with `list_functions`, then used `get_canvas_list` to return your most recently edited Canvases. Most recent Canvases: 1. **YOUR-TEST-CANVAS-ALPHA** - ID: `YOUR-TEST-ID-123` - Last edited: Jan 16, 2026 02:45 PM UTC 2. **YOUR-TEST-CANVAS-BETA** - ID: `YOUR-TEST-ID-456` - Last edited: Jan 15, 2026 11:10 AM UTC 3. **YOUR-TEST-CANVAS-GAMMA** - ID: `YOUR-TEST-ID-789` - Last edited: Jan 14, 2026 08:25 PM UTC **Example prompt** `Show me my recent Canvases` **Example response** I'll retrieve your recent Canvas list from the Braze MCP server. - Called `list_functions` - Called `get_canvas_list` Recent Canvases (sorted by most recently edited): - **Canvas name:** `YOUR-TEST-CANVAS-ALPHA` - **Last edited:** Jan 16, 2026 02:45 PM UTC - **Canvas ID:** `YOUR-TEST-ID-123` - **Tags:** `lifecycle`, `email` - **Canvas name:** `YOUR-TEST-CANVAS-BETA` - **Last edited:** Jan 15, 2026 11:10 AM UTC - **Canvas ID:** `YOUR-TEST-ID-456` - **Tags:** `promo` For more information on the `get_canvas_list` function, see [Available API functions]. ### Create an email template **Example prompt** `Create an email template named "YOUR-TEST-TEMPLATE-NAME" for onboarding messages.` **Example response** I created your template with `create_email_template`. - **Template name:** `YOUR-TEST-TEMPLATE-NAME` - **Template ID:** `YOUR-TEST-TEMPLATE-ID-123` - **Subject line:** `YOUR-TEST-SUBJECT-LINE` - **Preheader:** `YOUR-TEST-PREHEADER` - **Created at:** Jan 16, 2026 03:10 PM UTC You can now reference `YOUR-TEST-TEMPLATE-ID-123` when creating or updating campaigns and Canvases. **Example prompt** `Create an email template named "YOUR-TEST-TEMPLATE-NAME" for onboarding messages.` **Example response** I used `create_email_template` and created a new template successfully. - **Template ID:** `YOUR-TEST-TEMPLATE-ID-123` - **Template name:** `YOUR-TEST-TEMPLATE-NAME` - **Subject:** `YOUR-TEST-SUBJECT-LINE` - **Description:** `Onboarding lifecycle template` If you'd like, I can also generate a starter HTML body and plain-text fallback for this template. For more information on the `create_email_template` function, see [Available API functions]. ### Update a content block **Example prompt** `Update content block "YOUR-TEST-CONTENT-BLOCK-ID-123" with new body copy for our spring promotion.` **Example response** I updated your content block using `update_content_block`. - **Content block ID:** `YOUR-TEST-CONTENT-BLOCK-ID-123` - **Name:** `YOUR-TEST-CONTENT-BLOCK-NAME` - **Status:** Updated - **Updated at:** Jan 16, 2026 03:25 PM UTC Preview: `YOUR-TEST-CONTENT-BLOCK-BODY-V2` **Example prompt** `Update content block "YOUR-TEST-CONTENT-BLOCK-ID-123" with new body copy for our spring promotion.` **Example response** I ran `update_content_block` and confirmed the update completed. - **Content block ID:** `YOUR-TEST-CONTENT-BLOCK-ID-123` - **Previous version:** `v1` - **Current version:** `v2` - **Last updated:** Jan 16, 2026 03:25 PM UTC Updated content preview: `YOUR-TEST-CONTENT-BLOCK-BODY-V2` For more information on the `update_content_block` function, see [Available API functions]. ## Disclaimer The [Model Context Protocol (MCP)](https://modelcontextprotocol.io/docs/getting-started/intro) is a newly introduced open-source protocol that may be susceptible to security issues or vulnerabilities at this time. Braze MCP Server setup code and instructions are provided by Braze “as is” and without any warranties, and customers use it at their own risk. Braze shall not be responsible for any consequences arising from improper setup, misuse of the MCP, or any potential security issues that may arise. Braze strongly encourages customers to review their configurations carefully and to follow the outlined guidelines to reduce risks associated with the integrity and security of their Braze environment. For assistance or clarification, please contact [Braze Support](https://www.braze.com/docs/ko/ko/user_guide/administrative/access_braze/support). # Braze MCP 서버에서 사용 가능한 API 기능 Source: /docs/ko/developer_guide/mcp_server/available_api_functions/index.md # Braze MCP server functions > The Braze MCP server exposes a set of API functions that map to specific Braze REST API endpoints. MCP clients like Claude and Cursor can call these functions to retrieve non-PII data and, with the right permissions, perform non-PII write actions. For more general information, see [Braze MCP server]. **Important:** The locally hosted Braze MCP server (beta) is sunsetting this summer. It will continue to work, but we're no longer adding endpoints or supporting the beta. A remote, Braze-hosted MCP server is coming to Early Access this summer. ## Prerequisites Before you can use this feature, you'll need to [set up the Braze MCP server]. ## Available Braze API functions Your MCP client references the following API functions to interact with the Braze MCP server. ### General functions These functions help your MCP client discover and run the available Braze API functions. | Function | Description | |----------|-------------| | `list_functions` | Lists all available Braze API functions with their descriptions and parameters. | | `call_function` | Calls a specific read-only Braze API function with the provided parameters. | | `call_write_function` | Calls a specific write-capable Braze API function with the provided parameters. | {: .reset-td-br-1 .reset-td-br-2 aria-label="General functions" } ### Campaigns | Function | Endpoint | Description | |----------|----------|-------------| | `get_campaign_list` | [`/campaigns/list`](https://www.braze.com/docs/ko/ko/api/endpoints/export/campaigns/get_campaigns) | Export a list of campaigns with metadata. | | `get_campaign_details` | [`/campaigns/details`](https://www.braze.com/docs/ko/ko/api/endpoints/export/campaigns/get_campaign_details) | Get detailed information about specific campaigns. | | `get_campaign_dataseries` | [`/campaigns/data_series`](https://www.braze.com/docs/ko/ko/api/endpoints/export/campaigns/get_campaign_analytics) | Retrieve time series analytics data for campaigns. | {: .reset-td-br-1 .reset-td-br-2 .reset-td-br-3 aria-label="Campaigns" } ### Canvases | Function | Endpoint | Description | |----------|----------|-------------| | `get_canvas_list` | [`/canvas/list`](https://www.braze.com/docs/ko/ko/api/endpoints/export/canvas/get_canvases) | Export a list of Canvases with metadata. | | `get_canvas_details` | [`/canvas/details`](https://www.braze.com/docs/ko/ko/api/endpoints/export/canvas/get_canvas_details) | Get detailed information about specific Canvases. | | `get_canvas_data_summary` | [`/canvas/data_summary`](https://www.braze.com/docs/ko/ko/api/endpoints/export/canvas/get_canvas_analytics_summary) | Get summary analytics for Canvas performance. | | `get_canvas_data_series` | [`/canvas/data_series`](https://www.braze.com/docs/ko/ko/api/endpoints/export/canvas/get_canvas_analytics) | Retrieve time series analytics data for Canvases. | {: .reset-td-br-1 .reset-td-br-2 .reset-td-br-3 aria-label="Canvases" } ### Catalogs | Function | Endpoint | Description | |----------|----------|-------------| | `get_catalogs` | [`/catalogs`](https://www.braze.com/docs/ko/ko/api/endpoints/catalogs/catalog_management/synchronous/get_list_catalogs) | Return a list of catalogs in a workspace. | | `get_catalog_items` | [`/catalogs/{catalog_name}/items`](https://www.braze.com/docs/ko/ko/api/endpoints/catalogs/catalog_items/synchronous/get_catalog_items_details_bulk) | Return multiple catalog items and their content with pagination support. | | `get_catalog_item` | [`/catalogs/{catalog_name}/items/{item_id}`](https://www.braze.com/docs/ko/ko/api/endpoints/catalogs/catalog_items/synchronous/get_catalog_item_details) | Return a specific catalog item and its content by ID. | {: .reset-td-br-1 .reset-td-br-2 .reset-td-br-3 aria-label="Catalogs" } ### Cloud Data Ingestion | Function | Endpoint | Description | |----------|----------|-------------| | `list_integrations` | [`/cdi/integrations`](https://www.braze.com/docs/ko/ko/api/endpoints/cdi/get_integration_list) | Return a list of existing CDI integrations. | | `get_integration_job_sync_status` | [`/cdi/integrations/{integration_id}/job_sync_status`](https://www.braze.com/docs/ko/ko/api/endpoints/cdi/get_job_sync_status) | Return past sync statuses for a given CDI integration. | {: .reset-td-br-1 .reset-td-br-2 .reset-td-br-3 aria-label="Cloud Data Ingestion" } ### Content Blocks The `create_content_block` and `update_content_block` functions are write functions. Your MCP client must call them with `call_write_function`, and your API key must have the matching `content_blocks.create` or `content_blocks.update` permission. | Function | Endpoint | Description | |----------|----------|-------------| | `get_content_blocks_list` | [`/content_blocks/list`](https://www.braze.com/docs/ko/ko/api/endpoints/templates/content_blocks_templates/get_list_email_content_blocks) | List your available content blocks. | | `get_content_blocks_info` | [`/content_blocks/info`](https://www.braze.com/docs/ko/ko/api/endpoints/templates/content_blocks_templates/get_see_email_content_blocks_information) | Get information on your content blocks. | | `create_content_block` | [`/content_blocks/create`](https://www.braze.com/docs/ko/ko/api/endpoints/templates/content_blocks_templates/post_create_email_content_block) | Create a content block. Requires `name` and `content`. Optional fields are `description`, `state` (must be `active` or `draft`), and `tags`. | | `update_content_block` | [`/content_blocks/update`](https://www.braze.com/docs/ko/ko/api/endpoints/templates/content_blocks_templates/post_update_content_block) | Update an existing content block. Requires `content_block_id` and at least one updatable field: `name`, `content`, `description`, `state` (must be `active` or `draft`), or `tags`. | {: .reset-td-br-1 .reset-td-br-2 .reset-td-br-3 aria-label="Content Blocks" } ### Custom Attributes | Function | Endpoint | Description | |----------|----------|-------------| | `get_custom_attributes` | [`/custom_attributes`](https://www.braze.com/docs/ko/ko/api/endpoints/export/custom_attributes/get_custom_attributes) | Export custom attributes recorded for your app. | {: .reset-td-br-1 .reset-td-br-2 .reset-td-br-3 aria-label="Custom Attributes" } ### Events | Function | Endpoint | Description | |----------|----------|-------------| | `get_events_list` | [`/events/list`](https://www.braze.com/docs/ko/ko/api/endpoints/export/custom_events/get_custom_events) | Export a list of custom events recorded for your app. | | `get_events_data_series` | [`/events/data_series`](https://www.braze.com/docs/ko/ko/api/endpoints/export/custom_events/get_custom_events_analytics) | Retrieve time series data for custom events. | | `get_events` | [`/events`](https://www.braze.com/docs/ko/ko/api/endpoints/export/custom_events/get_custom_events_data) | Get detailed event data with pagination support. | {: .reset-td-br-1 .reset-td-br-2 .reset-td-br-3 aria-label="Events" } ### KPIs | Function | Endpoint | Description | |----------|----------|-------------| | `get_new_users_data_series` | [`/kpi/new_users/data_series`](https://www.braze.com/docs/ko/ko/api/endpoints/export/kpi/get_kpi_daily_new_users_date) | Daily series of new user counts. | | `get_dau_data_series` | [`/kpi/dau/data_series`](https://www.braze.com/docs/ko/ko/api/endpoints/export/kpi/get_kpi_dau_date) | Daily Active Users time series data. | | `get_mau_data_series` | [`/kpi/mau/data_series`](https://www.braze.com/docs/ko/ko/api/endpoints/export/kpi/get_kpi_mau_30_days) | Monthly Active Users time series data. | | `get_uninstalls_data_series` | [`/kpi/uninstalls/data_series`](https://www.braze.com/docs/ko/ko/api/endpoints/export/kpi/get_kpi_uninstalls_date) | App uninstall time series data. | {: .reset-td-br-1 .reset-td-br-2 .reset-td-br-3 aria-label="KPIs" } ### Media Library The `create_media_library_asset` function is a write function. Your MCP client must call it with `call_write_function`, and your API key must have the `media_library.create` permission. | Function | Endpoint | Description | |----------|----------|-------------| | `create_media_library_asset` | [`/media_library/create`](https://www.braze.com/docs/ko/ko/api/endpoints/media_library/manage_assets/create) | Upload an asset to your Braze media library. You can provide either a publicly accessible URL (`asset_url`) or a base64-encoded file (`asset_file_base64`), but not both. Images have a 5 MB size limit. | {: .reset-td-br-1 .reset-td-br-2 .reset-td-br-3 aria-label="Media Library" } ### Messages | Function | Endpoint | Description | |----------|----------|-------------| | `get_scheduled_broadcasts` | [`/messages/scheduled_broadcasts`](https://www.braze.com/docs/ko/ko/api/endpoints/messaging/schedule_messages/get_messages_scheduled) | List upcoming scheduled campaigns and Canvases. | {: .reset-td-br-1 .reset-td-br-2 .reset-td-br-3 aria-label="Messages" } ### Preference Centers | Function | Endpoint | Description | |----------|----------|-------------| | `get_preference_centers` | [`/preference_center/v1/list`](https://www.braze.com/docs/ko/ko/api/endpoints/preference_center/get_list_preference_center) | List your available preference centers. | | `get_preference_center_details` | [`/preference_center/v1/{preferenceCenterExternalID}`](https://www.braze.com/docs/ko/ko/api/endpoints/preference_center/get_view_details_preference_center) | View details for a specific preference center including HTML content and options. | {: .reset-td-br-1 .reset-td-br-2 .reset-td-br-3 aria-label="Preference Centers" } ### Purchases | Function | Endpoint | Description | |----------|----------|-------------| | `get_product_list` | [`/purchases/product_list`](https://www.braze.com/docs/ko/ko/api/endpoints/export/purchases/get_list_product_id) | Export paginated list of product IDs. | | `get_revenue_series` | [`/purchases/revenue_series`](https://www.braze.com/docs/ko/ko/api/endpoints/export/purchases/get_revenue_series) | Revenue analytics time series data. | | `get_quantity_series` | [`/purchases/quantity_series`](https://www.braze.com/docs/ko/ko/api/endpoints/export/purchases/get_number_of_purchases) | Purchase quantity time series data. | {: .reset-td-br-1 .reset-td-br-2 .reset-td-br-3 aria-label="Purchases" } ### Segments | Function | Endpoint | Description | |----------|----------|-------------| | `get_segment_list` | [`/segments/list`](https://www.braze.com/docs/ko/ko/api/endpoints/export/segments/get_segment) | Export list of segments with analytics tracking status. | | `get_segment_data_series` | [`/segments/data_series`](https://www.braze.com/docs/ko/ko/api/endpoints/export/segments/get_segment_analytics) | Time series analytics data for segments. | | `get_segment_details` | [`/segments/details`](https://www.braze.com/docs/ko/ko/api/endpoints/export/segments/get_segment_details) | Detailed information about specific segments. | {: .reset-td-br-1 .reset-td-br-2 .reset-td-br-3 aria-label="Segments" } ### Sends | Function | Endpoint | Description | |----------|----------|-------------| | `get_send_data_series` | [`/sends/data_series`](https://www.braze.com/docs/ko/ko/api/endpoints/export/campaigns/get_send_analytics) | Daily analytics for tracked campaign sends. | {: .reset-td-br-1 .reset-td-br-2 .reset-td-br-3 aria-label="Sends" } ### Sessions | Function | Endpoint | Description | |----------|----------|-------------| | `get_session_data_series` | [`/sessions/data_series`](https://www.braze.com/docs/ko/ko/api/endpoints/export/sessions/get_sessions_analytics) | Time series data for app session counts. | {: .reset-td-br-1 .reset-td-br-2 .reset-td-br-3 aria-label="Sessions" } ### SDK Authentication Keys | Function | Endpoint | Description | |----------|----------|-------------| | `get_sdk_authentication_keys` | [`/app_group/sdk_authentication/keys`](https://www.braze.com/docs/ko/ko/api/endpoints/sdk_authentication/get_sdk_authentication_keys) | List all SDK Authentication keys for your app. | {: .reset-td-br-1 .reset-td-br-2 .reset-td-br-3 aria-label="SDK Authentication Keys" } ### Subscription | Function | Endpoint | Description | |----------|----------|-------------| | `get_user_subscription_groups` | [`/subscription/user/status`](https://www.braze.com/docs/ko/ko/api/endpoints/subscription_groups/get_list_user_subscription_groups) | List and get the subscription groups of a certain user. | | `get_subscription_group_status` | [`/subscription/status/get`](https://www.braze.com/docs/ko/ko/api/endpoints/subscription_groups/get_list_user_subscription_group_status) | Get the subscription state of a user in a subscription group. | {: .reset-td-br-1 .reset-td-br-2 .reset-td-br-3 aria-label="Subscription" } ### Templates The `create_email_template` and `update_email_template` functions are write functions. Your MCP client must call them with `call_write_function`, and your API key must have the matching `templates.email.create` or `templates.email.update` permission. | Function | Endpoint | Description | |----------|----------|-------------| | `get_email_templates_list` | [`/templates/email/list`](https://www.braze.com/docs/ko/ko/api/endpoints/templates/email_templates/get_list_email_templates) | List your available email templates. | | `get_email_template_info` | [`/templates/email/info`](https://www.braze.com/docs/ko/ko/api/endpoints/templates/email_templates/get_see_email_template_information) | Get information on your email templates. | | `create_email_template` | [`/templates/email/create`](https://www.braze.com/docs/ko/ko/api/endpoints/templates/email_templates/post_create_email_template) | Create an email template. Requires `template_name`, `subject`, and `body`. Optional fields are `plaintext_body`, `preheader`, `tags`, and `should_inline_css`. | | `update_email_template` | [`/templates/email/update`](https://www.braze.com/docs/ko/ko/api/endpoints/templates/email_templates/post_update_email_template) | Update an existing email template. Requires `email_template_id` and at least one updatable field: `template_name`, `subject`, `body`, `plaintext_body`, `preheader`, `tags`, or `should_inline_css`. | {: .reset-td-br-1 .reset-td-br-2 .reset-td-br-3 aria-label="Templates" } ## Disclaimer The [Model Context Protocol (MCP)](https://modelcontextprotocol.io/docs/getting-started/intro) is a newly introduced open-source protocol that may be susceptible to security issues or vulnerabilities at this time. Braze MCP Server setup code and instructions are provided by Braze “as is” and without any warranties, and customers use it at their own risk. Braze shall not be responsible for any consequences arising from improper setup, misuse of the MCP, or any potential security issues that may arise. Braze strongly encourages customers to review their configurations carefully and to follow the outlined guidelines to reduce risks associated with the integrity and security of their Braze environment. For assistance or clarification, please contact [Braze Support](https://www.braze.com/docs/ko/ko/user_guide/administrative/access_braze/support). # BrazeAI Decisioning Studio™ 시작하기 Source: /docs/ko/developer_guide/decisioning_studio/index.md # BrazeAI Decisioning Studio™ > Get started with BrazeAI Decisioning Studio™ (formerly OfferFit by Braze) to make 1:1 AI decisions that maximize your business metrics. ## What is BrazeAI Decisioning Studio™? [BrazeAI Decisioning Studio™](https://www.braze.com/product/brazeai-decisioning-studio/) replaces A/B testing with decisioning agents that personalize everything, and maximize any metric: drive dollars, not clicks—with Decisioning Studio, you can optimize any business metric. BrazeAI™ decisioning agents automatically discover the optimal action for every customer. Using your first-party data, BrazeAI™ can maximize any business KPI for a wide range of use cases, including cross-sell, upsell, repurchase, retention, renewal, referral, winback, and more. To learn more, or get started with Decisioning Studio, [book a call](https://www.braze.com/get-started/) with Braze. ![Overview of the Decisioning Studio feedback loop](https://www.braze.com/docs/ko/ko/assets/img/decisioning_studio/decisioniong_studio_feedback_loop.png?e6a6335904325ae3e2123e0e60cbac18) ## Key features - **Keep your tech stack, but add a brain:** BrazeAI™ plugs in as a decisioning layer between your data systems and your customer engagement platform. While Decisioning Studio works best with Braze, a variety of other platforms are supported. - **Pick winners for people, not segments:** Use all your first-party data to make the optimal 1:1 decision for each individual. - **Personalize everything:** AI decisioning agents find the best message, product, incentive, channel, timing, and frequency for each individual customer - **Maximize any metric:** Clicks aren’t dollars. Use BrazeAI™ to pick the offers or incentives that maximize revenue, profit, CLV, or any other business KPI. - **Open the black box:** See how AI decisioning agents personalize for deep insights into the drivers of customer behavior - **Expert support all the way:** Decisioning Studio Pro includes support from our AI Decisioning Services team, which will tailor your decisioning agents to the specific needs of your business. ## About Decisioning Studio ### How it works BrazeAI Decisioning Studio™ allows you to design and deploy decisioning agents that optimize any business metric. To set up Decisioning Studio, you will: - Connect data sources that tell the Agent how a customer reacts to its decisions - Configure orchestration to carry out the decisioning agent's actions - Design your decisioning agent to define what outcome you want to maximize, and what actions the agent can take to do so. - Launch your decisioning agent and let it continuously learn and optimize for your business outcomes. While Decisioning Studio Go is a self-service platform, Decisioning Studio Pro includes AI Decisioning Services support from Braze’s forward deployed data science team, which will help you design and configure your agent to maximize your business outcomes. See [Decisioning Studio Go vs. Decisioning Studio Pro](https://www.braze.com/docs/ko/ko/user_guide/brazeai/decisioning_studio/#decisioning-studio-go-vs-decisioning-studio-pro) for more details. For more details, see [Get Started with Decisioning Studio](https://www.braze.com/docs/ko/ko/user_guide/brazeai/decisioning_studio/get_started/). ### Decisioning Agents vs. BrazeAI Agents While both are powered by BrazeAI™, decisioning agents and Braze Agents serve different purposes in your marketing stack. **Decisioning agents** are the strategic orchestrators of your campaigns. They operate at the campaign level, continuously running experiments across dimensions like offer, channel, timing, and frequency to maximize a business metric such as revenue, conversions, or ARPU. A decisioning agent manages an entire use case—such as winback, cross-sell, or renewal—learning over time what combination of actions works best for each individual customer. **Braze Agents** are AI-powered helpers that live within individual Canvas steps or catalog fields. They use large language models (LLMs) to generate content (like personalized subject lines or message copy), make routing decisions based on customer context, or enrich your catalogs with dynamically generated values. Braze Agents excel at bringing creativity and personalization to specific touchpoints within your campaign. Think of it this way: a decisioning agent is the conductor orchestrating your entire campaign strategy, while Braze Agents are the musicians adding creativity and nuance to each individual moment in the customer journey. You can use them together—let a decisioning agent determine the optimal offer and channel for each customer, then use a Braze Agent to generate personalized copy for that specific message. ## Decisioning Studio Go vs. Decisioning Studio Pro Decisioning Studio offers two tiers: Go and Pro. Each tier is designed to meet different needs and use cases. ### Decisioning Studio Go Go is ideal for teams getting started with AI decisioning. It includes: - Self-serve creative configuration with a pre-designed decisioning agent - Success metric focused on clicks - Compatibility with three Customer Engagement Platforms (CEPs): Braze, Salesforce Marketing Cloud, and Klaviyo ### Decisioning Studio Pro Pro offers the full suite of Decisioning Studio capabilities for advanced use cases. Key features include: - A dedicated AI Decisioning Services team to support you from decisioning agent design through stable state - Success metric can be any business metric (not just clicks) - Ability to connect any customer data source for decisioning - Full suite of reporting and insights - Extended orchestration patterns ### About this guide In this guide, you first learn what decisioning agents are and how they work. Next, you set up the self-service Decisioning Studio Go, followed by the full-service Decisioning Studio Pro. Finally, you review reports and insights to understand the performance of your decisioning agents. ## Next steps 1. [Get Started with Decisioning Studio](https://www.braze.com/docs/ko/ko/user_guide/brazeai/decisioning_studio/get_started/) 2. [Setting up Decisioning Studio Go](https://www.braze.com/docs/ko/ko/user_guide/brazeai/decisioning_studio/decisioning_studio_go/) 3. [Get Started with Decisioning Studio](https://www.braze.com/docs/ko/ko/user_guide/brazeai/decisioning_studio/get_started/) 4. [Viewing reports and insights](https://www.braze.com/docs/ko/ko/user_guide/brazeai/decisioning_studio/reporting/) # BrazeAI 결정 스튜디오™ 통합 Source: /docs/ko/developer_guide/decisioning_studio/integration/index.md # Integrating BrazeAI Decisioning Studio™ > Learn how to integrate BrazeAI Decisioning Studio™ into Braze and partner with the AI Expert Services team to [build agents](https://www.braze.com/docs/ko/ko/user_guide/brazeai/decisioning_studio/building_agents) that apply AI for 1:1 decision-making to improve your key business metrics. **Important:** While BrazeAI Decisioning Studio™ works best with Braze, a variety of other platforms are already supported. We'll continue updating our documentation so you'll have everything you need—even if you're not using Braze. ## Prerequisites Before you can integrate, you'll need an active BrazeAI Decisioning Studio™ license. Interested in learning more? [Book a call](https://www.braze.com/get-started/). ## Integrating decision studio ### Step 1: Get your endpoint URL You'll need to get the endpoint URL associated with your specific Braze instance. For more information, see [Braze API endpoints](https://www.braze.com/docs/ko/ko/developer_guide/rest_api/basics/#endpoints). ### Step 2: Create an API key In Braze, go to **Settings** > **API Keys**, then create a new key with the following permissions: | Permission | Purpose | Required? | | :--- | ----- | :---: | | [`/users/track`](https://www.braze.com/docs/ko/ko/api/endpoints/user_data/post_user_track) | Updates custom attributes on user profiles, in addition to creating temporary user profiles when using test sends. | ✓ | | [`/users/delete`](https://www.braze.com/docs/ko/ko/api/endpoints/user_data/post_user_delete) | Deletes temporary user profiles that were created while using test sends. | Only for test sends | | [`/users/export/segment`](https://www.braze.com/docs/ko/ko/api/endpoints/export/user_data/post_users_segment) | Updates the available audience communications every morning by exporting the list of users from each selected segment. | ✓ | | [`/users/export/ids`](https://www.braze.com/docs/ko/ko/api/endpoints/export/user_data/post_users_identifier) | Retrieves a list of identifiers when targeting users using an `external_id` instead of a segment. Since Decisioning Studio doesn’t accept Personally Identifiable Information (PII), you'll need to ensure your `fields_to_export` parameter returns only non-PII fields. | Only if using `external_ids` | | [`/messages/send`](https://www.braze.com/docs/ko/ko/api/endpoints/messaging/send_messages/post_send_messages) | Sends recommended variants at the recommended time using API Campaigns that are configured for Decisioning Studio's experimenter. | ✓ | | [`/campaigns/list`](https://www.braze.com/docs/ko/ko/api/endpoints/export/campaigns/get_campaigns/#prerequisites) | Retrieves the list of active campaigns and extracts available email content for experimentation. | ✓ | | [`/campaigns/data_series`](https://www.braze.com/docs/ko/ko/api/endpoints/export/campaigns/get_campaign_analytics) | Exports aggregated campaign data to enable reporting, validation, and troubleshooting in Decisioning Studio, so you can compare reporting values and analyze baseline performance.

While not required, this permission is recommended. | | | [`/campaigns/details`](https://www.braze.com/docs/ko/ko/api/endpoints/export/campaigns/get_campaign_details) | Retrieves HTML content, subject line, and image resources from existing Campaigns for experimentation. | ✓ | | [`/canvas/list`](https://www.braze.com/docs/ko/ko/api/endpoints/export/canvas/get_canvases) | Retrieves the list of active Canvases to extract available email content for experimentation. | ✓ | | [`/canvas/data_series`](https://www.braze.com/docs/ko/ko/api/endpoints/export/canvas/get_canvas_analytics) | Exports aggregated canvas data for reporting and validation, especially when BAU is orchestrated via Canvas.

While not required, this permission is recommended. | | | [`/canvas/details`](https://www.braze.com/docs/ko/ko/api/endpoints/export/canvas/get_canvas_details/#prerequisites) | Retrieves HTML content, subject line, and image resources from existing Canvases for experimentation. | ✓ | | [`/segments/list`](https://www.braze.com/docs/ko/ko/api/endpoints/export/segments/get_segment) | Retrieves all existing segments as potential target audiences for the Decisioning Studio experimenter. | ✓ | | [`/segments/data_series`](https://www.braze.com/docs/ko/ko/api/endpoints/export/segments/get_segment_analytics) | Exports segment size information, which is shown in Decisioning Studio when selecting an audience. | ✓ | | [`/segments/details`](https://www.braze.com/docs/ko/ko/api/endpoints/export/segments/get_segment_details/#prerequisites) | Retrieves segment details such as entry and exit criteria to help understand changes in audience size or performance. | | | [`/templates/email/create`](https://www.braze.com/docs/ko/ko/api/endpoints/templates/email_templates/post_create_email_template) | Creates copies of selected base HTML templates with [dynamic placeholders](https://www.braze.com/docs/ko/ko/user_guide/personalization_and_dynamic_content/liquid) (Braze liquid tags) for experimentation, avoiding changes to the originals. | ✓ | | [`/templates/email/update`](https://www.braze.com/docs/ko/ko/api/endpoints/templates/email_templates/post_update_email_template) | Pushes updates to Decisioning Studio-created template copies when experimentation criteria change, such as call-to-actions. | ✓ | | [`/templates/email/info`](https://www.braze.com/docs/ko/ko/api/endpoints/templates/email_templates/get_see_email_template_information/#prerequisites) | Retrieves information about Decisioning Studio-created templates in your Braze instance. | ✓ | | [`/templates/email/list`](https://www.braze.com/docs/ko/ko/api/endpoints/templates/email_templates/get_list_email_templates) | Validates that templates were successfully copied over to your Braze instance. | ✓ | {: .reset-td-br-1 .reset-td-br-2 .reset-td-br-3 aria-label="Table" } ### Step 3: Contact your BrazeAI Decisioning Studio™ customer success manager Contact your BrazeAI Decisioning Studio™ customer success manager and ask them to enable BrazeAI Decisioning Studio™. They'll use your Braze API key and endpoint URL to finish setting up your integration. When it's complete, you'll work alongside the AI Expert Services team to [start building agents for your product](https://www.braze.com/docs/ko/ko/user_guide/brazeai/decisioning_studio/building_agents). Each agent is tailor-made to a specific business goal, so you'll work together to design an implementation that's right for you. # BrazeAI Decisioning Studio™를 위한 에이전트 구축 Source: /docs/ko/developer_guide/decisioning_studio/building_agents/index.md # Building AI decisioning agents > Learn how to build an agent for BrazeAI Decisioning Studio™, so you can automate personalized experimentation and optimize outcomes like conversions, retention, or revenue—without manual A/B testing. **Important:** While BrazeAI Decisioning Studio™ works best with Braze, a variety of other platforms are already supported. We'll continue updating our documentation so you'll have everything you need—even if you're not using Braze. ## About agents An AI decisioning agent is a custom configuration for the BrazeAI™ decisioning engine that's tailor-made to meet a specific business goal. For example, you could build a repeat purchase agent to increase follow-up conversions after an initial sale. You define the audience and message in Braze, while your decisioning agents runs daily experiments and automatically tests different combinations of product offers, message timing, and frequency for each customer. Over time, BrazeAI™ learns what works best and orchestrates personalized sends through Braze to maximize repurchase rates. To build a good agent, you'll: - Choose a success metric for BrazeAI™ to optimize for, such as revenue, conversions, or ARPU. - Define which dimensions to test, such as offer, subject line, creative, channel, or send time. - Select the options for each dimension, such as email versus SMS, or daily versus weekly frequency. ![Example diagram of a Decisioning Studio agent for referral emails.](https://www.braze.com/docs/ko/ko/assets/img/offerfit/example_use_cases_referral_email.png?5630af24b92ce66087a1fa741168a9e6) ## Sample agents Here are some examples of agents that you can build with BrazeAI Decisioning Studio™. Your AI decisioning agents will learn from every customer interaction and apply those insights to the next day's actions. | Agent use case | Business goal | Using typical methods | Using BrazeAI Decisioning Studio™ | |---------------------------------|----------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | **Cross-Sell or Upsell** | Maximize average revenue per user (ARPU) from internet subscriptions. | Run annual campaigns offering every customer the next-highest tier plan. | Empirically discover the best message, sending time, discount, and plan to offer for each customer, learning which customers are susceptible to leapfrog offers and which customers require discounts or other incentives to upgrade. | | **Renewal & Retention** | Secure contract renewals, maximizing both contract length and net present value (NPV). | A/B test manually, and offer significant discounts to secure renewals. | Use automated experimentation to find the best renewal offer for each customer, and identify customers who are less price sensitive and need less significant discounts to renew. | | **Repeat Purchase** | Maximize purchase and repurchase rates. | All customers receive the same journey after making a website account (such as the same email sequence with the same cadence). | Automate experimentation to find the best menu item to offer each customer, as well as the most effective subject line, sending time, and frequency of communication. | | **Winback** | Increase reactivation by encouraging past subscribers to resubscribe. | Sophisticated A/B testing and segmentation. | Leverage automated experimentation to test thousands of variables at once, discovering the best creative, message, channel and cadence for each individual. | | **Referral** | Maximize new accounts opened through business credit card referrals from existing customers. | Fixed email sequence for all customers, with extensive A/B testing to determine the best sending times, cadence, etc. for the customer population. | Automate experimentation to determine ideal email, creative, sending time, and credit card to offer specific customers. | | **Lead Nurturing & Conversion** | Drive incremental revenue and pay the right amount for each customer. | As privacy policies change at Facebook and other platforms, prior approaches to personalized paid ads become last effective. | Leverage robust first-party data to automatically experiment on customer segments, biding methodology, bid levels, and creative. | | **Loyalty & Engagement** | Maximize purchases by new enrollees in a customer loyalty program. | Customers received a fixed sequence of emails in response to their actions. For example, all new enrollees in the loyalty program receive the same journey. | Experiment automatically with different email offers, sending times, and frequencies to maximize purchase and repurchase for each customer. | {: .reset-td-br-1 .reset-td-br-2 .reset-td-br-3 .reset-td-br-4 aria-label="Table" } ## Building an agent ### Prerequisites Before you can build an agent, you'll need to [integrate BrazeAI Decisioning Studio™](https://www.braze.com/docs/ko/ko/user_guide/brazeai/decisioning_studio/integration). ### Step 1: Contact AI Expert Services The AI Expert Services team will work closely with you to scope, design, and build your decisioning agent. If you haven't already, [contact us](https://www.braze.com/get-started/) to get started. You'll complete the following steps together to build a custom agent that's right for you. ### Step 2: Design your agent Alongside the AI Expert Services team, you'll define: - a target audience, - the business metric to optimize, - the actions for BrazeAI™ decisioning agent, and - any first-party customer data the agent should leverage to drive your business outcomes. With the design in hand, the team will work with you to identify and complete any additional integration requirements. ### Step 3: Set up your delivery platform Next, the AI Expert Service team will help you set up your customer engagement platform. While the Decisioning Studio works best with Braze, a variety of other platforms are supported—contact your AI Expert Service team for additional resources. To set up Braze: 1. Create a [campaign](https://www.braze.com/docs/ko/ko/user_guide/engagement_tools/campaigns/building_campaigns/delivery_types/api_triggered_delivery/) or [Canvas](https://www.braze.com/docs/ko/ko/user_guide/engagement_tools/canvas/create_a_canvas/create_a_canvas/?tab=api-triggered%20delivery#step-2b-determine-your-canvas-entry-schedule). BrazeAI Decisioning Studio™ will use this delivery method to send 1:1 personalized activation events to the users in your defined audience. 2. Be sure you don't include a Braze [control group](https://www.braze.com/docs/ko/ko/user_guide/engagement_tools/testing/multivariant_testing/create_multivariate_campaign#including-a-control-group), so BrazeAI™ can be the dedicated control group instead. 3. Depending on your dimensions, you can configure Liquid tags in your creative content to dynamically populate your messaging with BrazeAI™ recommendations. BrazeAI™ will pass customer-specific content to the Liquid tags in your templates using the Braze API. ### Step 4: Launch and monitor After launching your agent, your AI Expert Services team will continue to monitor and tune it to your agreed-upon design. They'll also help you make any adjustments, expansions, or modifications to the agent, if needed. # REST API를 사용한 메시지 전송 Source: /docs/ko/developer_guide/rest_api/sending_messages/index.md # REST API를 사용한 메시지 전송 {#sending-messages-using-the-rest-api} > 두 가지 Braze 엔드포인트를 사용하여 백엔드에서 실시간으로 메시지를 보낼 수 있습니다. 각각 요청 형식이 다릅니다. 하나는 요청에 전체 메시지 내용을 포함해야 하며, 다른 하나는 Campaign ID를 요구하고 대시보드에서 정의된 콘텐츠를 전송합니다. 이 접근 방식은 API가 지원하는 모든 메시징 채널(WhatsApp, 이메일, SMS, 푸시, Content Cards, 웹훅 등)에서 작동합니다. ## 보내는 두 가지 방법 {#two-ways-to-send} | | [`/messages/send`](https://www.braze.com/docs/ko/ko/api/endpoints/messaging/send_messages/post_send_messages/) | [`/campaigns/trigger/send`](https://www.braze.com/docs/ko/ko/api/endpoints/messaging/send_messages/post_send_triggered_campaigns/) | | --- | --- | --- | | **Campaign ID** | 선택 사항. 대시보드 Campaign 추적 없이 발송하려면 생략하거나, 대시보드에서 추적하려면 API Campaign ID와 각 메시지에 `message_variation_id`를 함께 제공합니다. | 필수. | | **메시지 내용** | 요청에 `messages` 오브젝트를 포함해야 합니다(예: `messages.whats_app`, `messages.email`). | 허용되지 않음. 메시지 내용은 Braze 대시보드의 Campaign에서 정의됩니다. | | **사용 사례** | API 요청에 완전히 명시된 내용으로 메시지를 전송합니다. | API를 통해 사전 구축된 Campaign(대시보드 내 콘텐츠)을 특정 수신자에게 트리거합니다. | {: .reset-td-br-1 .reset-td-br-2 .reset-td-br-3 aria-label="Two ways to send" } 전체 요청 및 응답 세부 정보는 [즉시 메시지 보내기(API 전용)](https://www.braze.com/docs/ko/ko/api/endpoints/messaging/send_messages/post_send_messages/) 및 [API 트리거 전달을 사용한 Campaign 보내기](https://www.braze.com/docs/ko/ko/api/endpoints/messaging/send_messages/post_send_triggered_campaigns/) 엔드포인트 참조 문서를 확인하세요. --- ## 옵션 1: 요청에 메시지 내용을 포함하여 전송(`/messages/send`) {#option-1-send-with-message-content-in-the-request-messagessend} API 요청에서 전체 메시지 내용을 지정하려는 경우 이 엔드포인트를 사용합니다. `messages` 오브젝트(예: `messages.whats_app`, `messages.email` 또는 `messages.sms`)를 **반드시** 포함해야 합니다. Campaign 추적 없이 발송하려면 `campaign_id`를 생략하거나, 대시보드에서 발송 내역을 추적하려면 각 메시지에 API Campaign ID와 `message_variation_id`를 포함하세요(자세한 내용은 [엔드포인트 참조](https://www.braze.com/docs/ko/ko/api/endpoints/messaging/send_messages/post_send_messages/) 문서를 확인하세요). **필수:** `messages.send` 권한이 있는 API 키. **Important:** `external_user_ids`에 포함된 각 수신자는 Braze에 이미 존재해야 합니다. 발송과 함께 사용자를 생성하려면 먼저 [`/users/track`](https://www.braze.com/docs/ko/ko/api/endpoints/user_data/post_user_track/)를 사용하거나, [옵션 2](#option-2-trigger-a-campaign-with-content-in-the-dashboard-campaignstriggersend)(API 트리거 Campaign)를 대신 사용하세요. ### 예시: WhatsApp 템플릿 메시지 {#example-whatsapp-template-message} ``` POST YOUR_REST_ENDPOINT/messages/send Content-Type: application/json Authorization: Bearer YOUR_REST_API_KEY ``` ```json { "external_user_ids": ["user123"], "messages": { "whats_app": { "app_id": "YOUR_APP_ID", "subscription_group_id": "YOUR_WHATSAPP_SUBSCRIPTION_GROUP_ID", "message_type": "template_message", "message": { "template_name": "new_message_received", "template_language_code": "en_US" } } } } ``` 전체 WhatsApp 오브젝트 사양은 [WhatsApp 오브젝트](https://www.braze.com/docs/ko/ko/api/objects_filters/messaging/whats_app_object/)를 참조하세요. **Note:** `/messages/send` 엔드포인트는 TEXT 또는 IMAGE 헤더가 포함된 WhatsApp 템플릿만 지원합니다. DOCUMENT, VIDEO 또는 기타 미디어 헤더 유형의 경우 [API 트리거 Campaign 엔드포인트](https://www.braze.com/docs/ko/ko/api/endpoints/messaging/send_messages/post_send_triggered_campaigns/) 또는 Braze 대시보드를 대신 사용하세요. ### 예시: 이메일 {#example-email} ```json { "external_user_ids": ["user123"], "messages": { "email": { "app_id": "YOUR_APP_ID", "subject": "Your order has shipped", "from": "no-reply@example.com", "body": "

Your order #12345 is on its way.

" } } } ``` 다른 채널에 대해서는 [메시징 오브젝트](https://www.braze.com/docs/ko/ko/api/objects_filters/#messaging-objects)를 참조하세요. --- ## 옵션 2: 대시보드의 콘텐츠로 Campaign 트리거(`/campaigns/trigger/send`) {#option-2-trigger-a-campaign-with-content-in-the-dashboard-campaignstriggersend} Braze 대시보드에서 메시지 콘텐츠가 구축된 경우(API 트리거 Campaign) 이 엔드포인트를 사용합니다. **필수** 항목인 `campaign_id`와 수신자를 전송하며, `messages` 오브젝트는 전송하지 **않습니다**. **필수:** `campaigns.trigger.send` 권한이 있는 API 키. ### 1단계: API 트리거 Campaign 생성 {#step-1-create-an-api-triggered-campaign} 1. Braze 대시보드에서 **메시징** > **Campaigns**로 이동합니다. 2. **캠페인 생성**을 선택한 후, **API-Triggered Campaign**("API Campaign"이 아님)을 선택합니다. 3. 메시지 채널(WhatsApp, 이메일, SMS 등)을 추가하고 대시보드에서 메시지 내용을 구축합니다. 4. **Campaign ID**를 기록합니다(여러 메시지 배리언트를 사용하는 경우 **Send ID**도 함께 기록). API 요청에서 이 값들을 사용하게 됩니다. API 트리거 Campaign 구축에 대한 자세한 내용은 [API 트리거 전달](https://www.braze.com/docs/ko/ko/user_guide/messaging/campaigns/schedule_your_campaign/api_triggered_delivery/)을 참조하세요. ### 2단계: API를 통해 Campaign 트리거 {#step-2-trigger-the-campaign-via-the-api} `campaign_id`와 `recipients`(또는 `broadcast`/`audience`)를 포함하여 `/campaigns/trigger/send`로 POST 요청을 전송합니다. `messages` 오브젝트는 포함하지 마세요—콘텐츠는 Campaign에서 제공됩니다. ``` POST YOUR_REST_ENDPOINT/campaigns/trigger/send Content-Type: application/json Authorization: Bearer YOUR_REST_API_KEY ``` ```json { "campaign_id": "YOUR_CAMPAIGN_ID", "recipients": [ { "external_user_id": "user123" } ] } ``` 전체 요청 본문(`trigger_properties`, `send_to_existing_only`, `attributes` 등 포함)은 [API 트리거 전달을 사용한 Campaign 보내기](https://www.braze.com/docs/ko/ko/api/endpoints/messaging/send_messages/post_send_triggered_campaigns/#request-body) 엔드포인트 참조 문서를 확인하세요. --- ## 통합 확인 {#verify-your-integration} 1. 위의 옵션 중 하나를 사용하여 본인의 사용자 ID를 수신자로 지정하여 요청을 보냅니다. 2. 메시지가 전달되었는지 확인합니다. 3. 옵션 2를 사용하는 경우, Braze 대시보드에서 Campaign을 확인하여 발송이 기록되었는지 확인합니다. ## 고려 사항 {#considerations} - 지원되는 환경에서 Braze [개인화 기능](https://www.braze.com/docs/ko/ko/user_guide/messaging/design_and_edit/personalize/)을 활용하여 콘텐츠를 맞춤 설정하세요. - 관련 규정을 준수하고 필수적인 수신 거부 옵션 및 개인정보 처리방침을 포함하도록 메시징을 구성하세요. - 추가 엔드포인트(스케줄링, Canvas 트리거 등)에 대해서는 [메시징 엔드포인트](https://www.braze.com/docs/ko/ko/api/endpoints/messaging/)를 참조하세요. # REST API를 사용하여 SMS 메시지 전송 Source: /docs/ko/developer_guide/rest_api/sending_sms_messages/index.md # REST API를 사용하여 SMS 메시지 전송 {#sending-sms-messages-using-the-rest-api} > Braze REST API를 사용하여 백엔드에서 실시간으로 트랜잭션 SMS 메시지를 전송할 수 있습니다. 이 접근 방식을 사용하면 SMS 메시지를 프로그래밍 방식으로 전송하면서 Braze 대시보드의 다른 Campaigns 및 Canvases와 함께 전달 분석을 추적할 수 있는 서비스를 구축할 수 있습니다. 특히 백엔드 시스템에서 콘텐츠가 정의되는 대량의 트랜잭션 메시징에 유용합니다. 예를 들어, 다른 사용자로부터 메시지를 받았을 때 소비자에게 알림을 보내 웹사이트를 방문하여 받은편지함을 확인하도록 안내할 수 있습니다. 이 접근 방식을 사용하면 다음을 수행할 수 있습니다: - 백엔드에서 실시간으로 SMS 메시지를 트리거합니다. - 모든 마케팅 소유 Campaigns 및 Canvases와 함께 분석을 추적합니다. - 메시지 지연, 후속 리타겟팅, A/B 테스트와 같은 추가 Braze 기능으로 사용 사례를 확장합니다. - 선택적으로, [API 트리거 전달](https://www.braze.com/docs/ko/ko/user_guide/messaging/campaigns/schedule_your_campaign/api_triggered_delivery/)로 전환하여 Braze 대시보드에서 메시지 템플릿을 정의하면서도 백엔드에서 전송을 트리거할 수 있습니다. REST API를 통해 SMS 메시지를 전송하려면 Braze 대시보드에서 API 캠페인을 설정한 다음 [`/messages/send`](https://www.braze.com/docs/ko/ko/api/endpoints/messaging/send_messages/post_send_messages/) 엔드포인트를 사용하여 메시지를 전송해야 합니다. ## 필수 조건 {#prerequisites} 이 가이드를 완료하려면 다음이 필요합니다: | 요구 사항 | 설명 | | --- | --- | | Braze REST API 키 | `messages.send` 권한이 있는 키. 키를 생성하려면 **설정** > **API 키** > **API 키**로 이동합니다. | | SMS 구독 그룹 | Braze 워크스페이스에 구성된 SMS 구독 그룹. | | 백엔드 서비스 | Braze REST API에 HTTP POST 요청을 보낼 수 있는 백엔드 서비스 또는 스크립팅 환경. | {: .reset-td-br-1 .reset-td-br-2 aria-label="필수 조건" } ## 1단계: API 캠페인 생성 {#step-1-create-an-api-campaign} 1. Braze 대시보드에서 **메시징** > **Campaigns**로 이동합니다. 2. **캠페인 생성**을 선택한 다음 **API Campaigns**를 선택합니다. 3. "SMS 메시지 알림"과 같은 캠페인 이름과 설명을 입력합니다. 4. 식별 및 추적을 위한 관련 태그를 추가합니다. 5. **메시징 채널 추가**를 선택한 다음 **SMS**를 선택합니다. 6. 캠페인 페이지에 표시된 **Campaign ID**와 **Message Variation ID**를 기록해 두세요. API 요청을 구성할 때 두 값이 모두 필요합니다. ## 2단계: API를 사용하여 SMS 메시지 전송 {#step-2-send-an-sms-message-using-the-api} [`/messages/send`](https://www.braze.com/docs/ko/ko/api/endpoints/messaging/send_messages/post_send_messages/) 엔드포인트에 POST 요청을 구성합니다. 요청 페이로드에 캠페인 ID, 수신자의 외부 사용자 ID 및 SMS 콘텐츠를 포함합니다. **Important:** `external_user_ids`에 참조된 각 수신자는 Braze에 이미 존재해야 합니다. API 전용 전송은 새로운 고객 프로필을 생성하지 않습니다. 전송의 일환으로 사용자를 생성해야 하는 경우, 먼저 [`/users/track`](https://www.braze.com/docs/ko/ko/api/endpoints/user_data/post_user_track/)을 사용하거나 대신 [API 트리거 Campaign](https://www.braze.com/docs/ko/ko/api/endpoints/messaging/send_messages/post_send_triggered_campaigns/)을 사용하세요. ### 예시 요청 {#example-request} ``` POST YOUR_REST_ENDPOINT/messages/send Content-Type: application/json Authorization: Bearer YOUR_REST_API_KEY ``` `YOUR_REST_ENDPOINT`를 워크스페이스의 [REST 엔드포인트 URL](https://www.braze.com/docs/ko/ko/api/basics/#endpoints)로 교체하세요. ```json { "campaign_id": "YOUR_CAMPAIGN_ID", "external_user_ids": ["user123"], "messages": { "sms": { "app_id": "YOUR_APP_ID", "subscription_group_id": "YOUR_SMS_SUBSCRIPTION_GROUP_ID", "message_variation_id": "YOUR_MESSAGE_VARIATION_ID", "body": "Hi }, you have a new message in your inbox. Check it out at https://yourwebsite.com/messages. Text STOP to opt out." } } } ``` 플레이스홀더 값을 실제 ID로 교체하세요. `body` 필드는 [Liquid 개인화](https://www.braze.com/docs/ko/ko/user_guide/messaging/design_and_edit/personalize/liquid/)를 지원하므로 각 수신자에게 맞춤화된 메시지 콘텐츠를 작성할 수 있습니다. SMS 메시징 오브젝트에서 지원하는 매개변수의 전체 목록은 [SMS 오브젝트](https://www.braze.com/docs/ko/ko/api/objects_filters/messaging/sms_object/)를 참조하세요. 요청을 구성한 후, 백엔드 서비스에서 Braze REST API로 POST 요청을 전송합니다. ## 3단계: 통합 확인 {#step-3-verify-your-integration} 설정을 완료한 후, 통합을 확인합니다: 1. [2단계](#step-2-send-an-sms-message-using-the-api)에 설명된 대로 API 요청을 보내고, 수신자로 자신의 사용자 ID를 사용하세요. 2. SMS 메시지가 휴대폰으로 전달되었는지 확인하세요. 3. Braze 대시보드에서 캠페인 결과 페이지로 이동하여 전송이 기록되었는지 확인하세요. 4. 캠페인을 확장할 때 결과를 면밀히 모니터링하세요. ## 고려 사항 {#considerations} - SMS 캠페인이 관련 규정 및 통신사 요구 사항을 준수하는지 확인하세요. 모든 메시지에 옵트아웃 안내(예: "옵트아웃하려면 STOP을 문자로 보내세요")를 포함하세요. 자세한 내용은 [SMS 법률 및 규정](https://www.braze.com/docs/ko/ko/user_guide/channels/sms_mms_and_rcs/compliance_and_delivery/laws_and_regulations/) 및 [옵트인 및 옵트아웃 키워드](https://www.braze.com/docs/ko/ko/user_guide/channels/sms_mms_and_rcs/message_features_and_optimization/keyword_processing/optin_optout/)를 참조하세요. - Braze [개인화 기능](https://www.braze.com/docs/ko/ko/user_guide/messaging/design_and_edit/personalize/)을 사용하여 동적 콘텐츠 및 사용자별 데이터를 포함하여 SMS 콘텐츠를 개별 소비자에 맞게 조정하세요. - Braze REST API는 메시지 스케줄링, 캠페인 트리거 등을 위한 추가 [메시징 엔드포인트](https://www.braze.com/docs/ko/ko/api/endpoints/messaging/)를 제공합니다. # REST API를 사용하여 이메일 메시지 발송 Source: /docs/ko/developer_guide/rest_api/sending_email_messages/index.md # REST API를 사용하여 이메일 메시지 발송 {#sending-email-messages-using-the-rest-api} > Braze REST API를 사용하여 백엔드에서 실시간으로 트랜잭션 이메일을 발송할 수 있습니다. 이 방식을 사용하면 프로그래밍 방식으로 이메일을 발송하는 서비스를 구축하면서, Braze 대시보드에서 다른 Campaigns 및 Canvases와 함께 전달 분석을 추적할 수 있습니다. 이 방식은 콘텐츠가 백엔드 시스템에서 정의되는 트랜잭션 메시징에 특히 유용합니다. 예를 들어, 소비자가 다른 사용자로부터 메시지를 받았을 때 웹사이트를 방문하여 받은편지함을 확인하도록 알림을 보낼 수 있습니다. 이 방식을 사용하면 다음을 수행할 수 있습니다: - 백엔드에서 실시간으로 이메일을 트리거합니다. - 열기, 클릭 수, 반송을 포함하여 모든 마케팅 소유 Campaigns 및 Canvases와 함께 분석을 추적합니다. - 메시지 상호작용 데이터를 사용하여 후속 리타겟팅과 같은 후속 메시지를 트리거합니다. - 메시지 지연 및 A/B 테스트와 같은 추가 Braze 기능으로 사용 사례를 확장합니다. - 선택적으로 [API 트리거 전달](https://www.braze.com/docs/ko/ko/user_guide/messaging/campaigns/schedule_your_campaign/api_triggered_delivery/)로 전환하여 Braze 대시보드에서 이메일 템플릿을 정의하면서도 백엔드에서 발송을 트리거할 수 있습니다. REST API를 통해 이메일을 발송하려면 Braze 대시보드에서 API 캠페인을 설정한 다음, [`/messages/send`](https://www.braze.com/docs/ko/ko/api/endpoints/messaging/send_messages/post_send_messages/) 엔드포인트를 사용하여 메시지를 발송해야 합니다. ## 필수 조건 {#prerequisites} 이 가이드를 완료하려면 다음이 필요합니다: | 요구 사항 | 설명 | | --- | --- | | Braze REST API 키 | `messages.send` 권한이 있는 키입니다. 키를 생성하려면 **설정** > **API 키** > **API 키**로 이동합니다. | | Braze 앱 ID | 워크스페이스 내 앱의 식별자입니다. 확인하려면 **설정** > **API 키**로 이동하여 **앱 식별자** 섹션을 확인합니다. 이 값은 이메일 메시징 오브젝트의 `app_id` 필드에 필수입니다. 자세한 내용은 [앱 식별자](https://www.braze.com/docs/ko/ko/api/identifier_types/)를 참조하세요. | | HTML 이메일 콘텐츠 | 사전에 준비된 이메일 메시지의 HTML 본문입니다. | | 백엔드 서비스 | Braze REST API에 HTTP POST 요청을 보낼 수 있는 백엔드 서비스 또는 스크립팅 환경입니다. | {: .reset-td-br-1 .reset-td-br-2 aria-label="필수 조건" } ## 1단계: API 캠페인 생성 {#step-1-create-an-api-campaign} 1. Braze 대시보드에서 **메시징** > **Campaigns**로 이동합니다. 2. **캠페인 생성**을 선택한 다음 **API Campaign**을 선택합니다. 3. 캠페인의 이름과 설명을 입력합니다(예: "이메일 메시지 알림"). 4. 식별 및 추적을 위한 관련 태그를 추가합니다. 5. **메시징 채널 추가**를 선택한 다음 **이메일**을 선택합니다. 6. 캠페인 페이지에 표시된 **Campaign ID**를 기록합니다. API 요청을 구성할 때 이 값이 필요합니다. 선택적으로 **Message Variation ID**도 기록해 두세요. 발송 통계를 특정 메시지 변형에 귀속시키려면 요청에 이 값을 포함합니다. ## 2단계: API를 사용하여 이메일 발송 {#step-2-send-an-email-using-the-api} [`/messages/send`](https://www.braze.com/docs/ko/ko/api/endpoints/messaging/send_messages/post_send_messages/) 엔드포인트에 POST 요청을 구성합니다. 요청 페이로드에 캠페인 ID, 수신자의 외부 사용자 ID, 이메일 콘텐츠를 포함합니다. **Important:** `external_user_ids`에 참조된 각 수신자는 이미 Braze에 존재해야 합니다. API 전용 발송은 새 고객 프로필을 생성하지 않습니다. 발송의 일부로 사용자를 생성해야 하는 경우 먼저 [`/users/track`](https://www.braze.com/docs/ko/ko/api/endpoints/user_data/post_user_track/)을 사용하거나, [API 트리거 캠페인](https://www.braze.com/docs/ko/ko/api/endpoints/messaging/send_messages/post_send_triggered_campaigns/)을 대신 사용하세요. ### 요청 예시 {#example-request} ``` POST https://YOUR_REST_ENDPOINT/messages/send Content-Type: application/json Authorization: Bearer YOUR_REST_API_KEY ``` `YOUR_REST_ENDPOINT`를 워크스페이스의 [REST 엔드포인트 URL](https://www.braze.com/docs/ko/ko/api/basics/#endpoints)로 교체합니다. ```json { "campaign_id": "YOUR_CAMPAIGN_ID", "external_user_ids": ["user123"], "messages": { "email": { "app_id": "YOUR_APP_ID", "message_variation_id": "YOUR_MESSAGE_VARIATION_ID", "subject": "You have a new message!", "from": "Notifications ", "body": "

You have a new message!

Hi },

You received a new message in your inbox. Click the link below to read it:

View message

Thank you for using our service!

" } } } ``` 플레이스홀더 값을 실제 ID로 교체합니다. `from` 필드는 `"Display Name "` 형식을 사용해야 합니다. `body` 필드는 유효한 HTML을 허용하며 [Liquid 개인화](https://www.braze.com/docs/ko/ko/user_guide/messaging/design_and_edit/personalize/liquid/)를 지원하므로, 각 수신자에 맞게 이메일 콘텐츠를 맞춤 설정할 수 있습니다. 이메일 메시징 오브젝트에서 지원하는 전체 매개변수 목록은 [이메일 오브젝트](https://www.braze.com/docs/ko/ko/api/objects_filters/messaging/email_object/)를 참조하세요. 요청을 구성한 후 백엔드 서비스에서 Braze REST API로 POST 요청을 발송합니다. ## 3단계: 통합 확인 {#step-3-verify-your-integration} 설정을 완료한 후 통합을 확인합니다: 1. [2단계](#step-2-send-an-email-using-the-api)에 설명된 대로 자신의 사용자 ID를 수신자로 사용하여 API 요청을 발송합니다. 2. 이메일이 받은편지함에 전달되었는지 확인합니다. 3. Braze 대시보드에서 캠페인 결과 페이지로 이동하여 발송이 기록되었는지 확인합니다. 4. 캠페인을 확장하면서 결과를 면밀히 모니터링합니다. ## 고려 사항 {#considerations} - 필요한 수신 거부 옵션과 개인정보 보호 고지를 포함하여 이메일 캠페인이 GDPR 및 CAN-SPAM과 같은 관련 규정을 준수하는지 확인합니다. 자세한 내용은 [사용자 구독 관리](https://www.braze.com/docs/ko/ko/user_guide/channels/email/subscriptions/) 및 [이메일 모범 사례](https://www.braze.com/docs/ko/ko/user_guide/channels/email/best_practices/)를 참조하세요. - Braze [개인화 기능](https://www.braze.com/docs/ko/ko/user_guide/messaging/design_and_edit/personalize/)을 사용하여 동적 콘텐츠 및 사용자별 데이터를 포함하여 개별 소비자에게 맞춤화된 이메일 콘텐츠를 제공합니다. - Braze REST API는 메시지 예약, 캠페인 트리거 등을 위한 추가 [메시징 엔드포인트](https://www.braze.com/docs/ko/ko/api/endpoints/messaging/)를 제공합니다. # 사용자에게 제품 추천하기 Source: /docs/ko/developer_guide/rest_api/recommending_products/index.md # 사용자에게 제품 추천하기 {#recommending-products-to-users} > Braze REST API를 [카탈로그](https://www.braze.com/docs/ko/ko/user_guide/data/activation/catalogs/create/) 또는 [연결된 콘텐츠](https://www.braze.com/docs/ko/ko/user_guide/personalization_and_dynamic_content/connected_content/)와 함께 사용하여 메시지에 개인화된 제품 추천을 표시할 수 있습니다. 이 접근 방식을 사용하면 자체 추천 엔진을 Braze 메시징 생태계에 연결할 수 있으므로, 비기술 사용자도 각 추천과 관련된 콘텐츠와 메시징을 직접 관리할 수 있습니다. 이 접근 방식을 사용하면 다음을 수행할 수 있습니다: - REST API를 사용하여 백엔드에서 고객 프로필에 제품 추천을 저장합니다. - 카탈로그 또는 연결된 콘텐츠를 사용하여 발송 시점에 제품 메타데이터를 검색합니다. - 이메일, 푸시, 인앱 메시지 등 모든 메시징 채널에서 개인화된 추천을 표시합니다. ## 필수 조건 {#prerequisites} 이 가이드를 완료하려면 다음이 필요합니다: | 요구 사항 | 설명 | | --- | --- | | Braze REST API 키 | `users.track` 권한이 있는 키이며, API를 통해 카탈로그를 관리하는 경우 관련 카탈로그 권한도 필요합니다. 생성하려면 **설정** > **API 키**로 이동하세요. | | Braze 카탈로그 | 제품 메타데이터(이름, 카테고리, 가격, 이미지 URL 등)가 포함된 카탈로그입니다. 생성하려면 [카탈로그 생성](https://www.braze.com/docs/ko/ko/user_guide/data/activation/catalogs/create/)을 참조하세요. | | Liquid 지식 | 개인화된 변수를 템플릿화하고 연결된 콘텐츠를 사용하기 위한 [Liquid](https://www.braze.com/docs/ko/ko/user_guide/personalization_and_dynamic_content/liquid/)에 대한 중급 수준의 이해가 필요합니다. | {: .reset-td-br-1 .reset-td-br-2 role="presentation" } ## 1단계: 고객 프로필에 추천 저장 {#step-1-store-recommendations-on-user-profiles} 먼저, 추천 엔진에서 생성한 제품 추천을 Braze 고객 프로필에 커스텀 속성으로 저장합니다. 이렇게 하면 메시지 발송 시점에 각 사용자의 추천 제품을 참조할 수 있습니다. 1. 제품 ID나 선호 카테고리 등 저장할 추천 데이터를 결정합니다. 2. [`/users/track`](https://www.braze.com/docs/ko/ko/api/endpoints/user_data/post_user_track/) 엔드포인트를 사용하여 고객 프로필에 커스텀 속성으로 추천을 기록합니다. ### 요청 예시 {#example-request} ```http POST YOUR_REST_ENDPOINT/users/track Content-Type: application/json Authorization: Bearer YOUR_REST_API_KEY ``` `YOUR_REST_ENDPOINT`를 워크스페이스의 [REST 엔드포인트 URL](https://www.braze.com/docs/ko/ko/api/basics/#endpoints)로 바꾸세요. ```json { "attributes": [ { "external_id": "user123", "recommended_product_id": "1001" } ] } ``` 나중에 Liquid 템플릿에서 쉽게 참조할 수 있도록 의미 있는 속성 이름(예: `recommended_product_id`)을 사용하세요. 추천 엔진이 새로운 결과를 생성할 때마다 정기적으로 업데이트하여 추천의 정확성을 유지하세요. ## 2단계: 제품 메타데이터 검색 {#step-2-retrieve-product-metadata} 각 고객 프로필에 추천 식별자를 저장한 후, 메시지에 포함할 전체 제품 메타데이터(이름, 가격, 이미지 등)를 검색해야 합니다. 두 가지 옵션이 있습니다: - **옵션 A:** [Braze 카탈로그](#option-a-braze-catalogs) — 빠른 내장 조회를 위해 제품 정보를 Braze에 직접 저장합니다. - **옵션 B:** [연결된 콘텐츠](#option-b-connected-content) — 발송 시점에 외부 API에서 제품 정보를 가져옵니다. ### 옵션 A: Braze 카탈로그 {#option-a-braze-catalogs} 제품 인벤토리로 [카탈로그](https://www.braze.com/docs/ko/ko/user_guide/data/activation/catalogs/create/)를 생성한 경우, Liquid를 사용하여 메시지에서 직접 항목을 조회할 수 있습니다. 전체 안내는 [카탈로그 사용](https://www.braze.com/docs/ko/ko/user_guide/data/activation/catalogs/use/)을 참조하세요. #### 특정 카탈로그 항목 추천 {#recommend-a-specific-catalog-item} ID로 특정 제품을 참조하려면 `catalog_items` Liquid 태그를 사용합니다. 예를 들어, `retail_products`라는 카탈로그에서 제품 `1001`을 추천하려면: ```liquid We have a new item we think you'll like: Category: Name: Price: $ ``` #### 여러 카탈로그 항목 추천 {#recommend-multiple-catalog-items} 하나의 태그에서 여러 항목을 참조할 수도 있습니다. 예를 들어, 세 가지 제품을 소개하려면: ```liquid New items added in: - - - Visit our store to learn more! ``` #### 사용자의 추천을 사용하여 항목 템플릿화 {#template-items-using-a-users-recommendation} [1단계](#step-1-store-recommendations-on-user-profiles)의 커스텀 속성과 카탈로그 조회를 결합하여 각 사용자에게 맞는 추천을 개인화합니다: ```liquid Hi }, check out our pick for you: — $ ``` ### 옵션 B: 연결된 콘텐츠 {#option-b-connected-content} 제품 메타데이터가 Braze 카탈로그가 아닌 외부 서비스에 있는 경우, [연결된 콘텐츠](https://www.braze.com/docs/ko/ko/user_guide/personalization_and_dynamic_content/connected_content/making_an_api_call/)를 사용하여 발송 시점에 가져올 수 있습니다. 예를 들어, 내부 API가 ID로 제품 세부 정보를 반환하는 경우: ```liquid Hi }, we think you'll love: — $ ``` 메시지에서 API 호출을 수행하는 방법에 대한 자세한 내용은 [API 호출하기](https://www.braze.com/docs/ko/ko/user_guide/personalization_and_dynamic_content/connected_content/making_an_api_call/)를 참조하세요. **Warning:** 연결된 콘텐츠를 사용하여 대량의 제품 목록을 가져온 다음 발송 시점에 Liquid에서 해당 목록을 반복 처리하는 것은 피하세요. 대용량 응답 페이로드는 발송 지연 시간을 증가시키고, 대규모 발송 시 메시지 시간 초과 또는 전달 실패를 유발할 수 있습니다. 대신, 사용자에게 필요한 특정 제품 ID만 프로필에 저장하고([1단계](#step-1-store-recommendations-on-user-profiles) 참조), 해당 개별 항목의 메타데이터를 가져오거나 빠른 조회에 최적화된 [카탈로그](#option-a-braze-catalogs)를 사용하세요. ## 3단계: 통합 확인 {#step-3-verify-your-integration} 설정을 완료한 후 통합을 확인합니다: 1. [`/users/track`](https://www.braze.com/docs/ko/ko/api/endpoints/user_data/post_user_track/) 엔드포인트를 사용하여 자신의 고객 프로필에 테스트 추천을 기록합니다. 2. 카탈로그 또는 연결된 콘텐츠를 사용하여 추천 제품을 참조하는 테스트 메시지를 발송합니다. 3. 전달된 메시지에서 제품 세부 정보가 올바르게 렌더링되는지 확인합니다. 4. Braze 대시보드에서 Campaign 또는 Canvas 결과 페이지로 이동하여 발송이 기록되었는지 확인합니다. ## 고려 사항 {#considerations} - 추천 엔진이 새로운 결과를 생성할 때마다 커스텀 속성을 정기적으로 업데이트하여 추천 데이터의 정확성을 유지하세요. - Braze [개인화 기능](https://www.braze.com/docs/ko/ko/user_guide/personalization_and_dynamic_content/)을 사용하여 제품 세부 정보와 함께 사용자별 데이터를 포함하는 등 메시지를 더욱 맞춤화하세요. - Braze 대시보드에서 정의한 템플릿을 사용하여 백엔드에서 메시지를 트리거하려면 [API 트리거 전달](https://www.braze.com/docs/ko/ko/user_guide/engagement_tools/campaigns/building_campaigns/delivery_types/api_triggered_delivery/) 사용을 고려하세요. # 현지화 Source: /docs/ko/developer_guide/localization/index.md # 현지화 {#localization} > 전 세계 사용자와 소통할 수 있도록 Braze SDK의 현지화 및 지원 언어에 대해 알아보세요. 현지화된 메시지를 설정하는 방법에 대한 안내는 메시징 기본 사항 섹션의 [현지화](https://www.braze.com/docs/ko/ko/user_guide/messaging/messaging_fundamentals/localization/)를 참조하세요. ## 현지화 정보 {#about-localization} 영어 외에도 Braze는 앱에 표시되는 SDK 메시지를 위한 여러 언어를 지원합니다. 사용자의 휴대폰 언어가 지원되는 언어 중 하나로 설정되면, 메시징 채널에 기본적으로 포함된 SDK 메시지가 해당 언어로 번역됩니다. 예를 들어, 앱이 연결 문제에 대한 메시지를 표시하면 사용자가 선택한 언어로 번역됩니다. ## Supported language codes Braze supports most language codes in the [ISO-639-1](http://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) standard, with a few exceptions. Refer to the following table for the full list. | Language | Code | | -------- | ---- | | ENGLISH | `en` | | AFRIKAANS | `af` | | AGHEM | `agq` | | AKAN | `ak` | | ALBANIAN | `sq` | | AMHARIC | `am` | | ARABIC | `ar` | | ARMENIAN | `hy` | | ASSAMESE | `as` | | AYMARA | `ay` | | AZERBAIJANI | `az` | | BAFIA | `ksf` | | BASA | `bas` | | BASQUE | `eu` | | BELARUSIAN | `be` | | BEMBA | `bem` | | BENGALI | `bn` | | BENA | `bez` | | BOSNIAN | `bs` | | BRETON | `br` | | BULGARIAN | `bg` | | BURMESE | `my` | | CAMBODIAN | `km` | | CATALAN | `ca` | | CENTRAL ATLAS TAMAZIGHT | `tzm` | | CHEROKEE | `chr` | | CHIGA | `cgg` | | CHINESE | `zh` | | CONGO SWAHILI | `swc` | | CORNISH | `kw` | | CROATIAN | `hr` | | CZECH | `cs` | | DANISH | `da` | | DAWIDA | `dav` | | DOUALA | `dua` | | DUTCH | `nl` | | DZONGKHA | `dz` | | EKUGUSII | `guz` | | ESTONIAN | `et` | | ESPERANTO | `eo` | | EWONDO | `ewo` | | EWE | `ee` | | FAROESE | `fo` | | FARSI | `fa` | | FILIPINO | `fil` | | FINNISH | `fi` | | FRENCH | `fr` | | GALICIAN | `gl` | | GANDA | `lg` | | GEORGIAN | `ka` | | GERMAN | `de` | | GERMAN SWISS | `gsw` | | GREEK | `el` | | GREENLANDIC | `kl` | | GUARANI | `gn` | | GUJARATI | `gu` | | HAUSA | `ha` | | HAWAIIAN | `haw` | | HEBREW | `he` | | HINDI | `hi` | | HUNGARIAN | `hu` | | ICELANDIC | `is` | | IGBO | `ig` | | INDONESIAN | `id` | | INUKTITUT | `iu` | | IRISH | `ga` | | ITALIAN | `it` | | JAVANESE | `jv` | | JAPANESE | `ja` | | JOLA_FONYI | `dyo` | | KABYLE | `kab` | | KALENJIN | `kln` | | KAMBA | `kam` | | KANNADA | `kn` | | KASHMIRI | `ks` | | KAZAKH | `kk` | | KIEMBU | `ebu` | | KIKUYU | `ki` | | KINYARWANDA | `rw` | | KIRGHIZ | `ky` | | KOREAN | `ko` | | KURDISH | `ku` | | LAO | `lo` | | LATIN | `la` | | LATVIAN | `lv` | | LINGALA | `ln` | | LITHUANIAN | `lt` | | LUBA KATANGA | `lu` | | LUXEMBOURGISH | `lb` | | LUO | `luo` | | LUYIA | `luy` | | MACHAME | `jmc` | | MACEDONIAN | `mk` | | MALAGASY | `mg` | | MALAY | `ms` | | MALAYALAM | `ml` | | MALTESE | `mt` | | MANX | `gv` | | MARATHI | `mr` | | MASAI | `mas` | | MERU | `mer` | | MOLDAVIAN | `mo` | | MONGOLIAN | `mn` | | MORISYEN | `mfe` | | MUNDANG | `mua` | | NAM | `naq` | | NEPALI | `ne` | | NORTH NDEBELE | `nd` | | NORWEGIAN | `nb` | | NUER | `nus` | | NYANKOLE | `nyn` | | NYNORSK | `nn` | | OROMO | `om` | | PASHTO | `ps` | | PEUL | `ff` | | POLISH | `pl` | | PORTUGUESE | `pt` | | PUNJABI | `pa` | | QUECHUA | `qu` | | RAETO ROMANCE | `rm` | | ROMANIAN | `ro` | | ROMBO | `rof` | | RUSSIAN | `ru` | | RWA | `rwk` | | SAMBURU | `saq` | | SAMI | `se` | | SANGU | `sbp` | | SANSKRIT | `sa` | | SCOTTISH | `gd` | | SERBIAN | `sr` | | SENA | `seh` | | SHAMBALA | `ksb` | | SHONA | `sn` | | SICHUAN YI | `ii` | | SINDHI | `sd` | | SINHALESE | `si` | | SLOVAK | `sk` | | SLOVENIAN | `sl` | | SOMALI | `so` | | SPANISH | `es` | | SWAHILI | `sw` | | SWEDISH | `sv` | | TACHELHIT | `shi` | | TAGALOG | `tl` | | TAJIKI | `tg` | | TAMIL | `ta` | | TASAWAQ | `twq` | | TATAR | `tt` | | TELUGU | `te` | | TESO | `teo` | | THAI | `th` | | TIBETAN | `bo` | | TIGRINYA | `ti` | | TONGAN | `to` | | TURKISH | `tr` | | TURKMEN | `tk` | | UIGHUR | `ug` | | UKRAINIAN | `uk` | | URDU | `ur` | | UZBEK | `uz` | | VAI | `vai` | | VIETNAMESE | `vi` | | VUNJO | `vun` | | WELSH | `cy` | | XHOSA | `xh` | | YANGBEN | `yav` | | YIDDISH | `yi` | | YORUBA | `yo` | | ZARMA | `dje` | | ZULU | `zu` | {: .reset-td-br-1 .reset-td-br-2 aria-label="Supported language codes" } # 지오펜스 Source: /docs/ko/developer_guide/geofences/index.md # 지오펜스 {#geofences} > Braze SDK에 지오펜스를 설정하는 방법을 알아보세요. [지오펜스](https://www.braze.com/docs/ko/ko/user_guide/engagement_tools/locations_and_geofences/#about-locations-and-geofences)는 특정 글로벌 위치를 중심으로 원을 형성하는 가상의 지리적 영역으로, 위도, 경도, 반경을 결합하여 표현합니다. ## Prerequisites Before you can use this feature, you'll need to [integrate the Android Braze SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=android). ## Setting up geofences {#setting-up-geofences} ### Step 1: Enable in Braze You can enable geofences for your app in one of the following places: To enable geofences from the **Locations** page: 1. In Braze, go to **Audience** > **Locations**. 2. The number of apps in your workspace that have geofences enabled is listed under the map. For example, if geofences is only enabled for some of your apps, it may read: **2 of 5 Apps with Geofences enabled**. To enable additional apps, select the current count under the map. 3. Choose an app to enable geofences for, then select **Done.** ![The geofence options on the Braze locations page.](https://www.braze.com/docs/ko/ko/assets/img_archive/enable-geofences-locations-page.png?4bf8451a2e59f1723b529fa8ff43b7f7) To enable geofences from the **App Settings** page: 1. In Braze, go to **Settings** > **App Settings**. 2. Select the app you'd like to enable geofences for. 3. Check **Geofences Enabled**, then select **Save.** ![The geofence checkbox located on the Braze settings pages.](https://www.braze.com/docs/ko/ko/assets/img_archive/enable-geofences-app-settings-page.png?702b6b77bb33116e03d8ba576f4e62f9) ### Step 2: Update `build.gradle` Add `android-sdk-location` to your app-level `build.gradle`. Also, add the Google Play Services [location package](https://developers.google.com/android/reference/com/google/android/gms/location/package-summary) using the Google Play Services [setup guide](https://developers.google.com/android/guides/setup): ``` dependencies { implementation "com.braze:android-sdk-location:+" implementation "com.google.android.gms:play-services-location:${PLAY_SERVICES_VERSION}" } ``` ### Step 3: Update the manifest Add boot, fine location, and background location permissions to your `AndroidManifest.xml`: ```xml ``` **Important:** The background location access permission was added in Android 10 and is required for Geofences to work while the app is in the background for all Android 10+ devices. Add the Braze boot receiver to the `application` element of your `AndroidManifest.xml`: ```xml ``` ### Step 4: Enable Braze location collection If you have not yet enabled Braze location collection, update your `braze.xml` file to include `com_braze_enable_location_collection` and confirm its value is set to `true`: ```xml true ``` **Important:** Starting with Braze Android SDK version 3.6.0, Braze location collection is disabled by default. Braze geofences are enabled if Braze location collection is enabled. If you would like to opt-out of our default location collection but still want to use geofences, it can be enabled selectively by setting the value of key `com_braze_geofences_enabled` to `true` in `braze.xml`, independently of the value of `com_braze_enable_location_collection`: ```xml true ``` ### Step 5: Obtain location permissions from the end user For Android M and higher versions, you must request location permissions from the end user before gathering location information or registering geofences. Add the following call to notify Braze when a user grants the location permission to your app: ```java Braze.getInstance(context).requestLocationInitialization(); ``` ```kotlin Braze.getInstance(context).requestLocationInitialization() ``` This will cause the SDK to request geofences from Braze servers and initialize geofence tracking. See [`RuntimePermissionUtils.java`](https://github.com/braze-inc/braze-android-sdk/blob/master/droidboy/src/main/java/com/appboy/sample/util/RuntimePermissionUtils.kt) in our sample application for an example implementation. ```java public class RuntimePermissionUtils { private static final String TAG = BrazeLogger.getBrazeLogTag(RuntimePermissionUtils.class); public static final int DROIDBOY_PERMISSION_LOCATION = 40; public static void handleOnRequestPermissionsResult(Context context, int requestCode, int[] grantResults) { switch (requestCode) { case DROIDBOY_PERMISSION_LOCATION: // In Android Q, we require both FINE and BACKGROUND location permissions. Both // are requested simultaneously. if (areAllPermissionsGranted(grantResults)) { Log.i(TAG, "Required location permissions granted."); Toast.makeText(context, "Required location permissions granted.", Toast.LENGTH_SHORT).show(); Braze.getInstance(context).requestLocationInitialization(); } else { Log.i(TAG, "Required location permissions NOT granted."); Toast.makeText(context, "Required location permissions NOT granted.", Toast.LENGTH_SHORT).show(); } break; default: break; } } private static boolean areAllPermissionsGranted(int[] grantResults) { for (int grantResult : grantResults) { if (grantResult != PackageManager.PERMISSION_GRANTED) { return false; } } return true; } } ``` ```kotlin object RuntimePermissionUtils { private val TAG = BrazeLogger.getBrazeLogTag(RuntimePermissionUtils::class.java!!) val DROIDBOY_PERMISSION_LOCATION = 40 fun handleOnRequestPermissionsResult(context: Context, requestCode: Int, grantResults: IntArray) { when (requestCode) { DROIDBOY_PERMISSION_LOCATION -> // In Android Q, we require both FINE and BACKGROUND location permissions. Both // are requested simultaneously. if (areAllPermissionsGranted(grantResults)) { Log.i(TAG, "Required location permissions granted.") Toast.makeText(context, "Required location permissions granted.", Toast.LENGTH_SHORT).show() Braze.getInstance(context).requestLocationInitialization() } else { Log.i(TAG, "Required location permissions NOT granted.") Toast.makeText(context, "Required location permissions NOT granted.", Toast.LENGTH_SHORT).show() } else -> { } } } private fun areAllPermissionsGranted(grantResults: IntArray): Boolean { for (grantResult in grantResults) { if (grantResult != PackageManager.PERMISSION_GRANTED) { return false } } return true } } ``` Using the preceding sample code is done via: ```java if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { boolean hasAllPermissions = PermissionUtils.hasPermission(getApplicationContext(), Manifest.permission.ACCESS_BACKGROUND_LOCATION) && PermissionUtils.hasPermission(getApplicationContext(), Manifest.permission.ACCESS_FINE_LOCATION); if (!hasAllPermissions) { // Request both BACKGROUND and FINE location permissions requestPermissions(new String[]{android.Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_BACKGROUND_LOCATION}, RuntimePermissionUtils.DROIDBOY_PERMISSION_LOCATION); } } else { if (!PermissionUtils.hasPermission(getApplicationContext(), Manifest.permission.ACCESS_FINE_LOCATION)) { // Request only FINE location permission requestPermissions(new String[]{android.Manifest.permission.ACCESS_FINE_LOCATION}, RuntimePermissionUtils.DROIDBOY_PERMISSION_LOCATION); } } } ``` ```kotlin if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { val hasAllPermissions = PermissionUtils.hasPermission(applicationContext, Manifest.permission.ACCESS_BACKGROUND_LOCATION) && PermissionUtils.hasPermission(applicationContext, Manifest.permission.ACCESS_FINE_LOCATION) if (!hasAllPermissions) { // Request both BACKGROUND and FINE location permissions requestPermissions(arrayOf(android.Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_BACKGROUND_LOCATION), RuntimePermissionUtils.DROIDBOY_PERMISSION_LOCATION) } } else { if (!PermissionUtils.hasPermission(applicationContext, Manifest.permission.ACCESS_FINE_LOCATION)) { // Request only FINE location permission requestPermissions(arrayOf(android.Manifest.permission.ACCESS_FINE_LOCATION), RuntimePermissionUtils.DROIDBOY_PERMISSION_LOCATION) } } } ``` ### Step 6: Manually request geofence updates (optional) By default, Braze automatically retrieves the device's location and requests geofences based on that collected location. However, you can manually provide a GPS coordinate that will be used to retrieve proximal Braze geofences instead. To manually request Braze Geofences, you must disable automatic Braze geofence requests and provide a GPS coordinate for requests. #### Step 6.1: Disable automatic geofence requests Automatic Braze geofence requests can be disabled in your `braze.xml` file by setting `com_braze_automatic_geofence_requests_enabled` to `false`: ```xml false ``` This can additionally be done at runtime via: ```java BrazeConfig.Builder brazeConfigBuilder = new BrazeConfig.Builder() .setAutomaticGeofenceRequestsEnabled(false); Braze.configure(getApplicationContext(), brazeConfigBuilder.build()); ``` ```kotlin val brazeConfigBuilder = BrazeConfig.Builder() .setAutomaticGeofenceRequestsEnabled(false) Braze.configure(applicationContext, brazeConfigBuilder.build()) ``` #### Step 6.2: Manually request Braze geofence with GPS coordinate Braze Geofences are manually requested via the [`requestGeofences()`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze/-i-braze/request-geofences.html) method: ```java Braze.getInstance(getApplicationContext()).requestGeofences(latitude, longitude); ``` ```kotlin Braze.getInstance(applicationContext).requestGeofences(33.078947, -116.601356) ``` **Important:** Geofences can only be requested once per session, either automatically by the SDK or manually with this method. **Important:** As of iOS 14, geofences do not work reliably for users who choose to only give their approximate location permission. ## Prerequisites Before you can use this feature, you'll need to [integrate the Swift Braze SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=swift). ## Setting up geofences {#setting-up-geofences} ### Step 1: Enable in Braze You can enable geofences for your app in one of the following places: To enable geofences from the **Locations** page: 1. In Braze, go to **Audience** > **Locations**. 2. The number of apps in your workspace that have geofences enabled is listed under the map. For example, if geofences is only enabled for some of your apps, it may read: **2 of 5 Apps with Geofences enabled**. To enable additional apps, select the current count under the map. 3. Choose an app to enable geofences for, then select **Done.** ![The geofence options on the Braze locations page.](https://www.braze.com/docs/ko/ko/assets/img_archive/enable-geofences-locations-page.png?4bf8451a2e59f1723b529fa8ff43b7f7) To enable geofences from the **App Settings** page: 1. In Braze, go to **Settings** > **App Settings**. 2. Select the app you'd like to enable geofences for. 3. Check **Geofences Enabled**, then select **Save.** ![The geofence checkbox located on the Braze settings pages.](https://www.braze.com/docs/ko/ko/assets/img_archive/enable-geofences-app-settings-page.png?702b6b77bb33116e03d8ba576f4e62f9) ### Step 2: Enable your app's location services By default, Braze location services are not enabled. To enable them in your app, complete the following steps. For a step-by-step tutorial, see [Tutorial: Braze Locations and Geofences](https://braze-inc.github.io/braze-swift-sdk/tutorials/braze/d1-brazelocation/). #### Step 2.1: Add the `BrazeLocation` module In Xcode, open the **General** tab. Under **Frameworks, Libraries, and Embedded Content**, add the `BrazeLocation` module. ![Add the BrazeLocation module in your Xcode project](https://www.braze.com/docs/ko/ko/assets/img/sdk_geofences/add-brazeLocation-module-xcode.png?a635e73143b5dee799072b76b29ffd5b) #### Step 2.2: Update your `Info.plist` In your `info.plist`, assign a `String` value to one of the following keys that describes why your application needs to track location. This string will be shown when your users are prompted for location services, so be sure to clearly explain the value of enabling this feature for your app. - `NSLocationAlwaysAndWhenInUseUsageDescription` - `NSLocationWhenInUseUsageDescription` ![Info.plist location strings in Xcode](https://www.braze.com/docs/ko/ko/assets/img/sdk_geofences/info-plist-location-strings.png?2a8b87c6d26af9f0b44e2a273d016f8c) **Important:** Apple has deprecated `NSLocationAlwaysUsageDescription`. For more information, see [Apple's developer documentation](https://developer.apple.com/documentation/bundleresources/information-property-list/nslocationalwaysusagedescription). ### Step 3: Enable geofences in your code In your app's code, enable geofences by setting `location.geofencesEnabled` to `true` on the `configuration` object that initializes the [`Braze`](https://braze-inc.github.io/braze-swift-sdk/tutorials/braze/d1-brazelocation/) instance. For other `location` configuration options, see [Braze Swift SDK reference](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/configuration-swift.class/location-swift.class). ```swift let configuration = Braze.Configuration( apiKey: "", endpoint: "" ) configuration.location.brazeLocationProvider = BrazeLocationProvider() configuration.location.automaticLocationCollection = true configuration.location.geofencesEnabled = true configuration.location.automaticGeofenceRequests = true // Additional configuration customization... let braze = Braze(configuration: configuration) AppDelegate.braze = braze ``` ```objc BRZConfiguration *configuration = [[BRZConfiguration alloc] initWithApiKey:brazeApiKey endpoint:brazeEndpoint]; configuration.logger.level = BRZLoggerLevelInfo; configuration.location.brazeLocationProvider = [[BrazeLocationProvider alloc] init]; configuration.location.automaticLocationCollection = YES; configuration.location.geofencesEnabled = YES; configuration.location.automaticGeofenceRequests = YES; // Additional configuration customization... Braze *braze = [[Braze alloc] initWithConfiguration:configuration]; AppDelegate.braze = braze; ``` #### Step 3.1: Enable background reporting (optional) By default, geofence events are only monitored if your app is in the foreground or has `Always` authorization, which monitors all application states. However, you can choose to also monitor geofence events if your app is in the background or has [`When In Use` authorization](#swift_request-authorization). To monitor these additional geofence events, open your Xcode project, then go to **Signing & Capabilities**. Under **Background Modes**, check **Location updates**. ![In Xcode, Background Mode > Location Updates](https://www.braze.com/docs/ko/ko/assets/img/sdk_geofences/xcode-background-modes-location-updates.png?7bfb02d003c77dedd1af7bf706959671) Next, enable `allowBackgroundGeofenceUpdates` in your app's code. This lets Braze extend your app's "When In Use" status by continuously monitoring location updates. This setting only works when your app is in the background. When the app re-opens, all existing background processes are paused and foreground processes are prioritized instead. ```swift let configuration = Braze.Configuration( apiKey: "", endpoint: "" ) // Additional configuration customization... // Enable background geofence reporting with `When In Use` authorization. configuration.location.allowBackgroundGeofenceUpdates = true // Determines the number of meters required to trigger a new location update. configuration.location.distanceFilter = 8000 let braze = Braze(configuration: configuration) AppDelegate.braze = braze ``` ```objc BRZConfiguration *configuration = [[BRZConfiguration alloc] initWithApiKey:brazeApiKey endpoint:brazeEndpoint]; // Additional configuration customization... // Enable background geofence reporting with `When In Use` authorization. configuration.location.allowBackgroundGeofenceUpdates = YES; // Determines the number of meters required to trigger a new location update. configuration.location.distanceFilter = 8000; Braze *braze = [[Braze alloc] initWithConfiguration:configuration]; AppDelegate.braze = braze; ``` **Important:** To prevent battery drain and rate limiting, configure `distanceFilter` to a value that meets your app's specific needs. Setting `distanceFilter` to a higher value prevents your app from requesting your user's location too frequently. ### Step 4: Request authorization {#request-authorization} When requesting authorization from a user, request either `When In Use` or `Always` authorization. To request `When In Use` authorization, use the `requestWhenInUseAuthorization()` method: ```swift var locationManager = CLLocationManager() locationManager.requestWhenInUseAuthorization() ``` ```objc CLLocationManager *locationManager = [[CLLocationManager alloc] init]; [locationManager requestWhenInUseAuthorization]; ``` By default, `requestAlwaysAuthorization()` only grants your app `When In Use` authorization and will re-prompt your user for `Always` authorization after some time has passed. However, you can choose to immediately prompt your user by first calling `requestWhenInUseAuthorization()` and then calling `requestAlwaysAuthorization()` after receiving your initial `When In Use` authorization. **Important:** You can only immediately prompt for `Always` authorization a single time. ```swift var locationManager = CLLocationManager() locationManager.requestAlwaysAuthorization() ``` ```objc CLLocationManager *locationManager = [[CLLocationManager alloc] init]; [locationManager requestAlwaysAuthorization]; ``` ## Manually request geofences {#manually-request-geofences} When the Braze SDK requests geofences from the backend, it reports the user's current location and receives geofences that are determined to be optimally relevant based on the location reported. To control the location that the SDK reports for the purposes of receiving the most relevant geofences, you can manually request geofences by providing the desired coordinates. ### Step 1: Set `automaticGeofenceRequests` to `false` You can disable automatic geofence requests in your `configuration` object passed to [`init(configuration)`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/init(configuration:)). Set `automaticGeofenceRequests` to `false`. ```swift let configuration = Braze.Configuration( apiKey: "{BRAZE_API_KEY}", endpoint: "{BRAZE_ENDPOINT}" ) configuration.automaticGeofencesRequest = false let braze = Braze(configuration: configuration) AppDelegate.braze = braze ``` ```objc BRZConfiguration *configuration = [[BRZConfiguration alloc] initWithApiKey:{BRAZE_API_KEY} endpoint:{BRAZE_ENDPOINT}]; configuration.automaticGeofencesRequest = NO; Braze *braze = [[Braze alloc] initWithConfiguration:configuration]; AppDelegate.braze = braze; ``` ### Step 2: Call `requestGeofences` manually In your code, request geofences with the appropriate latitude and longitude. ```swift AppDelegate.braze?.requestGeofences(latitude: latitude, longitude: longitude) ``` ```objc [AppDelegate.braze requestGeofencesWithLatitude:latitude longitude:longitude]; ``` ## Frequently Asked Questions (FAQ) {#faq} #### Why am I not receiving geofences on my device? To confirm whether or not geofences are being received on your device, first use the [SDK Debugger tool](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/debugging#debugging-the-braze-sdk) to check SDK's logs. You will then be able to see if geofences are successfully being received from the server and if there are any notable errors. Below are other possible reasons geofences may not be received on your device: ##### iOS operating system limitations The iOS operating system only allows up to 20 geofences to be stored for a given app. With geofences enabled, Braze will use up some of these 20 available slots. To prevent accidental or unwanted disruption to other geofence-related functionality in your app, you must enable location geofences for individual apps on the dashboard. For our location services to work correctly, check that your app is not using all available geofence spots. ##### Rate limiting Braze has a limit of 1 geofence refresh per session to avoid unnecessary requests. #### How does it work if I am using both Braze and non-Braze geofence features? As mentioned above, iOS allows a single app to store a maximum of 20 geofences. This storage is shared by both Braze and non-Braze geofences and is managed by [CLLocationManager](https://developer.apple.com/documentation/corelocation/cllocationmanager). For instance, if your app contains 20 non-Braze geofences, there would be no storage to track any Braze geofences (or vice versa). In order to receive new geofences, you will need to use [Apple's location APIs](https://developer.apple.com/documentation/corelocation) to stop monitoring some of the existing geofences on the device. #### Can the Geofences feature be used while a device is offline? A device needs to be connected to the internet only when a refresh occurs. Once it has successfully received geofences from the server, it is possible to log a geofence entry or exit even if the device is offline. This is because a device's location operates separately from its internet connectivity. For example, say a device successfully received and registered geofences on session start and goes offline. If it then enters one of those registered geofences, it can trigger a Braze campaign. #### Why are geofences not monitored when my app is backgrounded/terminated? Without `Always` authorization, Apple restricts location services from running while an app is not in use. This is enforced by the operating system and is outside the control of the Braze SDK. While Braze offers separate configurations to run services while the app is in the background, there is no way to circumvent these restrictions for apps that are terminated without receiving explicit authorization from the user. ## Prerequisites Before you can use this feature, you'll need to [integrate the .NET MAUI Braze SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=.net%20maui%20(xamarin)). ## Prerequisites This is the minimum SDK versions needed to start using geofences: ## Setting up geofences {#setting-up-geofences} ### Step 1: Enable in Braze You can enable geofences for your app in one of the following places: To enable geofences from the **Locations** page: 1. In Braze, go to **Audience** > **Locations**. 2. The number of apps in your workspace that have geofences enabled is listed under the map. For example, if geofences is only enabled for some of your apps, it may read: **2 of 5 Apps with Geofences enabled**. To enable additional apps, select the current count under the map. 3. Choose an app to enable geofences for, then select **Done.** ![The geofence options on the Braze locations page.](https://www.braze.com/docs/ko/ko/assets/img_archive/enable-geofences-locations-page.png?4bf8451a2e59f1723b529fa8ff43b7f7) To enable geofences from the **App Settings** page: 1. In Braze, go to **Settings** > **App Settings**. 2. Select the app you'd like to enable geofences for. 3. Check **Geofences Enabled**, then select **Save.** ![The geofence checkbox located on the Braze settings pages.](https://www.braze.com/docs/ko/ko/assets/img_archive/enable-geofences-app-settings-page.png?702b6b77bb33116e03d8ba576f4e62f9) --- Next, follow the platform-specific instructions below for either Android or iOS: ### Step 2: Add dependencies Add the following NuGet package reference to your project: - `BrazePlatform.BrazeAndroidLocationBinding` ### Step 3: Update your AndroidManifest.xml Add the following permissions to your `AndroidManifest.xml`: ```xml ``` **Important:** The background location access permission is required for geofences to work while the app is in the background on Android 10+ devices. ### Step 4: Configure Braze location collection Ensure that location collection is enabled in your Braze configuration. If you want to enable geofences without automatic location collection, set the following in your `Braze.xml`: ```xml true true ``` ### Step 5: Request location permissions at runtime You must request location permissions from the user before registering geofences. In your C# code, use the following pattern: ```csharp using AndroidX.Core.App; using AndroidX.Core.Content; private void RequestLocationPermission() { // ...existing code for checking and requesting permissions... } public override void OnRequestPermissionsResult(int requestCode, string[] permissions, Permission[] grantResults) { // ...existing code for handling permission result... } ``` After permissions are granted, initialize Braze location collection: ```csharp Braze.GetInstance(this).RequestLocationInitialization(); ``` ### Step 6: Manually request geofence updates (optional) To manually request geofences for a specific location: ```csharp Braze.GetInstance(this).RequestGeofences(latitude, longitude); ``` **Important:** Geofences can only be requested once per session, either automatically by the SDK or manually with this method. ### Step 2: Add dependencies Add the following NuGet package reference to your project: - `Braze.iOS.BrazeLocation` ### Step 3: Configure location usage in Info.plist Add a usage description string for location services in your `Info.plist`: ```xml NSLocationAlwaysAndWhenInUseUsageDescription This app uses your location to enable geofences and location-based messaging. NSLocationWhenInUseUsageDescription This app uses your location to enable geofences and location-based messaging. ``` **Important:** Apple has deprecated `NSLocationAlwaysUsageDescription`. Use the keys above for iOS 14+. ### Step 4: Enable geofences in your Braze configuration In your app startup code (e.g., `App.xaml.cs`), configure Braze with geofences enabled: ```csharp using BrazeKit; using BrazeLocation; var configuration = new BRZConfiguration("", ""); configuration.Location.BrazeLocationProvider = new BrazeLocationProvider(); configuration.Location.AutomaticLocationCollection = true; configuration.Location.GeofencesEnabled = true; configuration.Location.AutomaticGeofenceRequests = true; // ...other configuration... var braze = new Braze(configuration); ``` ### Step 5: Enable background location updates (optional) To monitor geofences in the background, enable the **Location updates** background mode by adding the following configuration to your `Info.plist`: ```xml UIBackgroundModes location ``` Then, in your Braze configuration, set: ```csharp configuration.Location.AllowBackgroundGeofenceUpdates = true; configuration.Location.DistanceFilter = 8000; // meters ``` **Important:** Set `DistanceFilter` to a value that meets your app's needs to avoid battery drain. ### Step 6: Request location authorization Request either `When In Use` or `Always` authorization from the user: ```csharp using CoreLocation; var locationManager = new CLLocationManager(); locationManager.RequestWhenInUseAuthorization(); // or locationManager.RequestAlwaysAuthorization(); ``` **Important:** Without `Always` authorization, iOS restricts location services from running while the app is not in use. This is enforced by the operating system and cannot be bypassed by the Braze SDK. **Important:** Geofences are supported on **both iOS and Android** in the React Native SDK. The `requestLocationInitialization` method is Android-only and is not required for iOS. The `requestGeofences` method is available on both platforms. By default, the SDK can automatically request and monitor geofences when location is available; you can rely on this automatic configuration or call `requestGeofences` to request manually. ## Prerequisites Before you can use this feature, you'll need to [integrate the React Native Braze SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/?sdktab=react%20native). ## Setting up geofences {#setting-up-geofences} ### Step 1: Enable in Braze You can enable geofences for your app in one of the following places: To enable geofences from the **Locations** page: 1. In Braze, go to **Audience** > **Locations**. 2. The number of apps in your workspace that have geofences enabled is listed under the map. For example, if geofences is only enabled for some of your apps, it may read: **2 of 5 Apps with Geofences enabled**. To enable additional apps, select the current count under the map. 3. Choose an app to enable geofences for, then select **Done.** ![The geofence options on the Braze locations page.](https://www.braze.com/docs/ko/ko/assets/img_archive/enable-geofences-locations-page.png?4bf8451a2e59f1723b529fa8ff43b7f7) To enable geofences from the **App Settings** page: 1. In Braze, go to **Settings** > **App Settings**. 2. Select the app you'd like to enable geofences for. 3. Check **Geofences Enabled**, then select **Save.** ![The geofence checkbox located on the Braze settings pages.](https://www.braze.com/docs/ko/ko/assets/img_archive/enable-geofences-app-settings-page.png?702b6b77bb33116e03d8ba576f4e62f9) ### Step 2: Complete native Android setup Because the React Native SDK uses the native Braze Android SDK, complete the native Android geofence setup for your project. The iOS equivalent of these steps is covered in the native Swift SDK geofences guide ([steps 2.2 to 3.1](https://www.braze.com/docs/ko/ko/developer_guide/geofences/?sdktab=swift#swift_step-21-add-the-brazelocation-module)); step 2.1 (Add the BrazeLocation module) is not required for React Native because BrazeLocation is already included implicitly with the Braze React Native SDK. 1. **Update `build.gradle`:** Add `android-sdk-location` and Google Play Services location. See [Android geofences](https://www.braze.com/docs/ko/ko/developer_guide/geofences/?sdktab=android). 2. **Update the manifest:** Add location permissions and the Braze boot receiver. See [Android geofences](https://www.braze.com/docs/ko/ko/developer_guide/geofences/?sdktab=android). 3. **Enable Braze location collection:** Update your `braze.xml` file. See [Android geofences](https://www.braze.com/docs/ko/ko/developer_guide/geofences/?sdktab=android). ### Step 3: Complete native iOS setup Because the React Native SDK uses the native Braze iOS SDK, complete the native iOS geofence setup for your project by following the native Swift SDK instructions starting from step 2.2: update your `Info.plist` with location usage descriptions (step 2.2), and enable geofences in your Braze configuration including `automaticGeofenceRequests = true` (step 3); optionally enable background reporting (step 3.1). Step 2.1 (Add the BrazeLocation module) is not required—BrazeLocation is already included implicitly with the Braze React Native SDK. See [iOS geofences, steps 2.2 to 3.1](https://www.braze.com/docs/ko/ko/developer_guide/geofences/?sdktab=swift#swift_step-21-add-the-brazelocation-module). ### Step 4: Request geofences from JavaScript **On Android:** After the user grants location permissions, call `requestLocationInitialization()` to initialize Braze location features and request geofences from Braze servers. This method is not supported on iOS and is not required for iOS. **On iOS:** The equivalent is to enable the `automaticGeofenceRequests` configuration in your native Swift or Objective-C Braze configuration (see Step 3). With that enabled, the SDK automatically requests and monitors geofences when location is available; no JavaScript call equivalent to `requestLocationInitialization` is required. ```javascript import Braze from '@braze/react-native-sdk'; // Android only: call this after the user grants location permission Braze.requestLocationInitialization(); ``` ### Step 5: Manually request geofences (optional) On both iOS and Android, you can manually request a geofence update for a specific GPS coordinate using `requestGeofences`. By default, Braze automatically retrieves the device's location and requests geofences. To manually provide a coordinate instead: 1. Disable automatic geofence requests. On Android, set `com_braze_automatic_geofence_requests_enabled` to `false` in your `braze.xml`. On iOS, set `automaticGeofenceRequests` to `false` in your Braze configuration. 2. Call `requestGeofences` with the desired latitude and longitude: ```javascript import Braze from '@braze/react-native-sdk'; Braze.requestGeofences(33.078947, -116.601356); ``` **Important:** Geofences can only be requested once per session, either automatically by the SDK or manually with this method. # 저장 Source: /docs/ko/developer_guide/storage/index.md # 저장 {#storage} > Braze SDK에 저장되는 다양한 기기 수준의 등록정보에 대해 알아보세요. ## 기기 등록정보 {#device-properties} 기본적으로 Braze는 기기, 언어, 시간대를 기반으로 메시지를 개인화할 수 있도록 다음과 같은 기기 수준 등록정보를 수집합니다. - `BROWSER` - `BROWSER_VERSION` - `LANGUAGE` - `OS` - `RESOLUTION` - `TIME_ZONE` - `USER_AGENT` - `AD_TRACKING_ENABLED` - `ANDROID_VERSION` - `CARRIER` - `IS_BACKGROUND_RESTRICTED` - `LOCALE` - `MODEL` - `NOTIFICATION_ENABLED` - `RESOLUTION` - `TIMEZONE` **Note:** `AD_TRACKING_ENABLED` 및 `TIMEZONE`은 `null` 또는 공백인 경우 수집되지 않습니다. `GOOGLE_ADVERTISING_ID`는 SDK에서 자동으로 수집되지 않으며, [`setGoogleAdvertisingId`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze/-i-braze/set-google-advertising-id.html)를 통해 전달해야 합니다. - 기기 통신사([`CTCarrier` 지원 중단](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/configuration-swift.class/deviceproperty/carrier)에 대한 참고 사항 참조) - 기기 로케일 - 기기 모델 - 기기 OS 버전 - 푸시 승인 상태 - 푸시 표시 옵션 - 푸시 활성화됨 - 기기 해상도 - 기기 시간대 **Note:** Braze SDK는 IDFA를 자동으로 수집하지 않습니다. 앱은 바로 아래의 메서드를 구현하여 선택적으로 IDFA를 Braze에 전달할 수 있습니다. 앱은 앱 추적 투명성 프레임워크를 통해 최종 사용자로부터 추적에 대한 명시적인 옵트인을 확보한 후에 IDFA를 Braze에 전달해야 합니다. 1. 광고 추적 상태를 설정하려면 [`set(adTrackingEnabled:)`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/set(adtrackingenabled:)/)을 사용하세요. 2. 광고주 식별자(IDFA)를 설정하려면 [`set(identifierForAdvertiser:)`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/set(identifierforadvertiser:)/)를 사용하세요. 기본적으로 모든 등록정보가 활성화되어 있습니다. 그러나 수동으로 활성화 또는 비활성화할 수 있습니다. 일부 Braze SDK 기능에는 특정 등록정보(예: 현지 시간대 전달 및 시간대)가 필요하므로 프로덕션에 릴리스하기 전에 구성을 테스트해야 합니다. 예를 들어 허용 목록에 추가할 기기 언어를 지정할 수 있습니다. 자세한 내용은 [`InitializationOptions`](https://js.appboycdn.com/web-sdk/latest/doc/modules/braze.html#initializationoptions)의 `devicePropertyAllowlist` 옵션을 참조하세요. ```javascript import * as braze from"@braze/web-sdk"; braze.initialize("API-KEY", { baseUrl: "BASE-URL", devicePropertyAllowlist: [ braze.DeviceProperties.LANGUAGE ] // list of `DeviceProperties` you want to collect }); ``` 예를 들어 허용 목록에 추가할 Android OS 버전과 기기 로캘을 지정할 수 있습니다. 자세한 내용은 [`setDeviceObjectAllowlistEnabled()`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.configuration/-braze-config/-builder/set-device-object-allowlist-enabled.html) 및 [`setDeviceObjectAllowlist()`](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze.configuration/-braze-config/-builder/set-device-object-allowlist.html) 메서드를 참조하세요. ```java new BrazeConfig.Builder() .setDeviceObjectAllowlistEnabled(true) .setDeviceObjectAllowlist(EnumSet.of(DeviceKey.ANDROID_VERSION, DeviceKey.LOCALE)); ``` 예를 들어 허용 목록에 추가할 시간대 및 로캘 수집을 지정할 수 있습니다. 자세한 내용은 `configuration` 오브젝트의 [`devicePropertyAllowList`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/configuration-swift.class/devicepropertyallowlist) 등록정보를 참조하세요. ```swift configuration.devicePropertyAllowList = [.timeZone, .locale] ``` ```objc configuration.devicePropertyAllowList = @[ BRZDeviceProperty.timeZone, BRZDeviceProperty.locale ]; ``` **Tip:** 자동으로 수집되는 기기 등록정보에 대해 자세히 알아보려면 [SDK 데이터 수집](https://www.braze.com/docs/ko/ko/user_guide/data/unification/user_data/sdk_data_collection/)을 참조하세요. ## 쿠키 저장(웹 전용) {#cookies} [웹 Braze SDK를 초기화](https://js.appboycdn.com/web-sdk/latest/doc/modules/braze.html#initialize)하면 새 세션에서 자동으로 갱신되는 400일 만료 쿠키가 생성되어 저장됩니다. 다음과 같은 쿠키가 저장됩니다. | 쿠키 | 설명 | 크기 | |---|----|---| | `ab.storage.userId.[your-api-key]` | 현재 로그인한 사용자의 변경 여부를 확인하고 이벤트를 현재 사용자와 연결하는 데 사용됩니다. | `changeUser`에 전달된 값의 크기에 따라 다름 | | `ab.storage.sessionId.[your-api-key]` | 사용자가 새 세션을 시작하는지 기존 세션을 시작하는지 확인하여 메시지를 동기화하고 세션 분석을 계산하는 데 사용되는 무작위 생성 문자열입니다. | ~200바이트 | | `ab.storage.deviceId.[your-api-key]` | 익명 사용자를 식별하고 사용자의 기기를 구분하여 기기 기반 메시징을 활성화하는 데 사용되는 무작위 생성 문자열입니다. | ~200바이트 | | `ab.optOut` | `disableSDK` 호출 시 사용자의 옵트아웃 환경설정을 저장하는 데 사용됩니다. | ~40바이트 | | `ab._gd` | 루트 수준 쿠키 도메인을 결정하기 위해 임시로 생성(후 삭제)되며, 이를 통해 SDK가 하위 도메인에서 올바르게 작동할 수 있습니다. | 해당 없음 | {: .reset-td-br-1 .reset-td-br-2 .reset-td-br-3 aria-label="Store cookies (web only) #cookies" } ### 쿠키 만료 변경하기 {#cookie-expiry} 기본적으로 Braze 쿠키는 400일 후에 만료됩니다. 이를 변경하려면 웹 SDK를 초기화할 때 `cookieExpiryInDays` 옵션을 사용하세요. 값은 0보다 커야 하며, 이 옵션을 생략하거나 0 이하로 설정하면 400일 기본값이 적용됩니다. 이 옵션은 웹 SDK 6.6.0 이상이 필요합니다. ```javascript import * as braze from "@braze/web-sdk"; braze.initialize("API-KEY", { baseUrl: "BASE-URL", cookieExpiryInDays: 30 // expires after 30 days }); ``` ### 쿠키 비활성화하기 {#disable-cookies} 모든 쿠키를 비활성화하려면 웹 SDK를 초기화할 때 [`noCookies`](https://js.appboycdn.com/web-sdk/latest/doc/modules/braze.html#initializationoptions) 옵션을 사용하세요. 이렇게 하면 하위 도메인 간에 이동하는 익명 사용자를 연결할 수 없으며 각 하위 도메인에 새로운 사용자가 생성됩니다. ```javascript import * as braze from "@braze/web-sdk"; braze.initialize("API-KEY", { baseUrl: "BASE-URL", noCookies: true }); ``` 일반적으로 Braze 추적을 중지하거나 저장된 브라우저 데이터를 모두 지우려면 각각 [`disableSDK`](https://js.appboycdn.com/web-sdk/latest/doc/modules/braze.html#disableSDK) 및 [`wipeData`](https://js.appboycdn.com/web-sdk/latest/doc/modules/braze.html#wipedata) SDK 메서드를 참조하세요. 이 두 가지 메서드는 사용자가 동의를 철회하거나 SDK가 이미 초기화된 후 모든 Braze 기능을 중지하려는 경우에 유용할 수 있습니다. # Braze SDK의 네트워크 설정 Source: /docs/ko/developer_guide/network/index.md # 네트워크 설정 {#network-settings} > Braze SDK의 네트워크 설정을 구성하는 방법을 알아보세요. ## Network offline mode [Network offline mode](https://braze-inc.github.io/braze-android-sdk/kdoc/braze-android-sdk/com.braze/-braze/-companion/outbound-network-requests-offline.html?query=var%20outboundNetworkRequestsOffline:%20Boolean) is an optional feature that pauses or resumes outbound network requests from the Braze SDK at any point during runtime. Events are not lost during the offline state. This reference article covers how to integrate this mode. To enable network offline mode in the Braze SDK, see the following example: ```java Braze.setOutboundNetworkRequestsOffline(true); ``` ```kotlin Braze.setOutboundNetworkRequestsOffline(true) ``` ## Network traffic control ### Requesting processing policies Braze allows the user the option to control network traffic using the following protocols: By default, the `RequestPolicy` enum value is set to `automatic`. When set, immediate server requests are performed when user-facing data is required for Braze features, such as in-app messages. The Braze SDK will automatically handle all server communication, including: - Flushing custom events and attributes data to Braze servers - Updating Content Cards and geofences - Requesting new in-app messages To minimize server load, Braze performs periodic flushes of new user data every few seconds. When the `RequestPolicy` enum value is `manual`, it performs the same as automatic request processing, except: - Custom attributes and custom event data are not automatically flushed to the server throughout the user session. - Braze will still perform automatic network requests for internal features, such as requesting in-app messages, Liquid templating in in-app messages, geofences, and location tracking. For more details, see the `Braze.Configuration.Api.RequestPolicy.manual` [documentation](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/configuration-swift.class/api-swift.class/requestpolicy-swift.enum/manual). When these internal requests are made, Braze may flush locally stored custom attributes and custom event data to the Braze server, depending on the request type. ### Manually flushing user data Data can be manually flushed to Braze servers at any time using the following method: ```swift AppDelegate.braze?.requestImmediateDataFlush() ``` ```objc [AppDelegate.braze requestImmediateDataFlush]; ``` ### Setting the request processing policy These policies can be set at app startup time when you initialize the Braze configuration. In the `configuration` object, set the [`Braze.Configuration.Api.RequestPolicy`](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze/configuration-swift.class/api-swift.class/requestpolicy-swift.enum)) as shown in the following code snippet: ```swift configuration.api.requestPolicy = .automatic ``` ```objc configuration.api.requestPolicy = BRZRequestPolicyAutomatic; ``` # Braze SDK 참조, 리포지토리 및 샘플 앱 Source: /docs/ko/developer_guide/references/index.md # 참조, 리포지토리 및 샘플 앱 {#references-repositories-and-sample-apps} > 각 Braze SDK에 속한 참조 문서, GitHub 리포지토리 및 샘플 앱의 목록입니다. SDK의 참조 문서에는 사용 가능한 클래스, 유형, 함수 및 변수가 자세히 설명되어 있습니다. GitHub 리포지토리는 해당 SDK의 기능 및 속성 선언, 코드 변경 및 버전 관리에 대한 인사이트를 제공합니다. 각 리포지토리에는 Braze 기능을 테스트하거나 자체 애플리케이션과 함께 구현하는 데 사용할 수 있는 완전히 빌드 가능한 샘플 애플리케이션도 포함되어 있습니다. 문서 내 미러링된 리포지토리 README 콘텐츠는 [리포지토리 가이드](https://www.braze.com/docs/ko/ko/developer_guide/sdk_repository_guides/)를 참조하세요. ## 리소스 목록 {#list-of-resources} **Note:** 현재 일부 SDK에는 전용 참조 문서가 없지만 적극적으로 작업 중입니다. | 플랫폼 | 참조 | 리포지토리 | 샘플 앱 | | ----------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- | --------------------------------------------------------------------------------- | | Android SDK | [참조 문서](https://braze-inc.github.io/braze-android-sdk/kdoc/index.html) | [GitHub 리포지토리](https://github.com/braze-inc/braze-android-sdk) | [샘플 앱](https://github.com/braze-inc/braze-android-sdk/tree/master/samples) | | Swift SDK | [참조 문서](https://braze-inc.github.io/braze-swift-sdk/documentation/brazekit/braze) | [GitHub 리포지토리](https://github.com/braze-inc/braze-swift-sdk) | [샘플 앱](https://github.com/braze-inc/braze-swift-sdk/tree/main/Examples) | | 웹 SDK | [참조 문서](https://js.appboycdn.com/web-sdk/latest/doc/modules/braze.html#initialize) | [GitHub 리포지토리](https://github.com/braze-inc/braze-web-sdk) | [샘플 앱](https://github.com/braze-inc/braze-web-sdk/tree/master/sample-builds) | | Javascript SDK | [참조 문서](https://braze-inc.github.io/braze-javascript-sdk/) | [GitHub 리포지토리](https://github.com/braze-inc/braze-javascript-sdk/tree/main) | N/A | | Cordova SDK | [선언 파일](https://github.com/braze-inc/braze-cordova-sdk/blob/master/www/BrazePlugin.js) | [GitHub 리포지토리](https://github.com/braze-inc/braze-cordova-sdk) | [샘플 앱](https://github.com/braze-inc/braze-cordova-sdk/tree/master/sample-project) | | Flutter SDK | [참조 문서](https://pub.dev/documentation/braze_plugin/latest/braze_plugin/) | [GitHub 리포지토리](https://github.com/braze-inc/braze-flutter-sdk) | [샘플 앱](https://github.com/braze-inc/braze-flutter-sdk/tree/master/example) | | React Native SDK | [참조 문서](https://braze-inc.github.io/braze-react-native-sdk/) | [GitHub 리포지토리](https://github.com/braze-inc/braze-react-native-sdk) | [샘플 앱](https://github.com/braze-inc/braze-react-native-sdk/tree/master/BrazeProject) | | Vega SDK | [참조 문서](https://braze-inc.github.io/braze-vega-sdk/) | [GitHub 리포지토리](https://github.com/braze-inc/braze-vega-sdk) | N/A | | Roku SDK | N/A | [GitHub 리포지토리](https://github.com/braze-inc/braze-roku-sdk) | [샘플 앱](https://github.com/braze-inc/braze-roku-sdk/tree/main/torchietv) | | Unity SDK | [선언 파일](https://github.com/braze-inc/braze-unity-sdk/blob/master/Assets/Plugins/Appboy/BrazePlatform.cs) | [GitHub 리포지토리](https://github.com/braze-inc/braze-unity-sdk) | [샘플 앱](https://github.com/braze-inc/braze-unity-sdk/tree/master/unity-samples) | | .NET MAUI SDK (이전 Xamarin) | N/A | [GitHub 리포지토리](https://github.com/braze-inc/braze-xamarin-sdk) | [샘플 앱](https://github.com/braze-inc/braze-xamarin-sdk/tree/master/appboy-component/samples) | {: .reset-td-br-1 .reset-td-br-2 .reset-td-br-3 .reset-td-br-4 aria-label="리소스 목록" } ## 샘플 앱 빌드 {#building-a-sample-app} ### "Droidboy" 빌드 {#building-droidboy} [Android SDK GitHub 리포지토리](https://github.com/braze-inc/braze-android-sdk) 내의 테스트 애플리케이션은 Droidboy입니다. 다음 지침에 따라 프로젝트와 함께 모든 기능을 갖춘 사본을 빌드하세요. 1. 새 [워크스페이스](https://www.braze.com/docs/ko/ko/developer_guide/platform_wide/app_group_configuration/#app-group-configuration)를 생성하고 Braze API 식별자 키를 기록합니다.

2. `/droidboy/res/values/braze.xml` 내의 적절한 위치(각각 `com_braze_push_fcm_sender_id` 및 `com_braze_api_key`라는 문자열의 태그 사이)에 FCM 발신자 ID와 Braze API 식별자 키를 복사합니다.

3. **설정 관리** 아래 워크스페이스 설정에 FCM 서버 키와 서버 ID를 복사합니다.

4. Droidboy APK를 어셈블하려면 SDK 디렉토리 내에서 `./gradlew assemble`을 실행합니다. Windows에서는 `gradlew.bat`을 사용합니다.

5. 테스트 기기에 Droidboy APK를 자동으로 설치하려면 SDK 디렉토리 내에서 `./gradlew installDebug`를 실행합니다. ### "Hello Braze" 빌드 {#building-hello-braze} Hello Braze 테스트 애플리케이션은 Braze SDK의 최소 사용 사례를 보여주며, Braze SDK를 Gradle 프로젝트에 쉽게 통합하는 방법도 함께 보여줍니다. 1. **설정 관리** 페이지에서 API 식별자 키를 `res/values` 폴더의 `braze.xml` 파일에 복사합니다. ![](https://www.braze.com/docs/ko/ko/assets/img_archive/hello_appboy.png?6a24a92e98dc23be7df4f1b6ce39eef5)

2. 기기 또는 에뮬레이터에 샘플 앱을 설치하려면 SDK 디렉토리 내에서 다음 명령을 실행합니다. ``` ./gradlew installDebug ``` `ANDROID_HOME` 변수가 제대로 설정되어 있지 않거나 `local.properties` 폴더에 유효한 `sdk.dir` 폴더가 없는 경우, 이 플러그인이 기본 SDK도 설치해 줍니다. 자세한 내용은 [플러그인 리포지토리](https://github.com/JakeWharton/sdk-manager-plugin)를 참조하세요. Android SDK 빌드 시스템에 대한 자세한 내용은 [GitHub 리포지토리 README](https://github.com/braze-inc/braze-android-sdk/blob/master/README.md)를 참조하세요. ### Swift 테스트 앱 빌드 {#building-swift-test-apps} 다음 지침에 따라 테스트 애플리케이션을 빌드하고 실행하세요. 1. 새 [워크스페이스](https://www.braze.com/docs/ko/ko/developer_guide/platform_wide/app_group_configuration/#creating-your-app-group-in-my-apps)를 생성하고 앱 식별자 API 키와 엔드포인트를 기록합니다. 2. 통합 방법(스위프트 패키지 매니저, CocoaPods, 수동)에 따라 적절한 `xcodeproj` 파일을 선택하여 엽니다. 3. `Credentials` 파일의 해당 필드에 API 키와 엔드포인트를 입력합니다. **Note:** SDK 통합에 대한 QA를 수행할 때 [SDK 디버거](https://www.braze.com/docs/ko/ko/developer_guide/sdk_integration/debugging/)를 사용하면 앱의 상세 로깅을 켜지 않고도 문제를 해결할 수 있습니다. # 리포지토리 가이드 Source: /docs/ko/developer_guide/sdk_repository_guides/index.md # 리포지토리 가이드 {#repository-guides} > 이 페이지들은 Braze SDK 리포지토리의 공개 README 파일을 미러링합니다. 자동화를 통해 매주 동기화되며, 각 페이지에서 샘플 프로젝트와 추가 컨텍스트를 위한 소스 리포지토리로 연결됩니다. ## 사용 가능한 리포지토리 가이드 {#available-repository-guides} 플랫폼별로 다음 리포지토리 가이드를 선택하세요: - [Web SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_repository_guides/web/) - [Android SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_repository_guides/android/) - [Swift SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_repository_guides/swift/) - [JavaScript SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_repository_guides/javascript/) - [Cordova SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_repository_guides/cordova/) - [Flutter SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_repository_guides/flutter/) - [React Native SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_repository_guides/react_native/) - [Roku SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_repository_guides/roku/) - [Unity SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_repository_guides/unity/) - [.NET MAUI (Xamarin) SDK](https://www.braze.com/docs/ko/ko/developer_guide/sdk_repository_guides/xamarin/) # Web SDK 리포지토리 가이드 Source: /docs/ko/developer_guide/sdk_repository_guides/web/index.md ## Braze Web SDK 소개 {#about-the-braze-web-sdk} Braze Web SDK를 사용하면 Braze의 고객 참여 플랫폼을 웹 애플리케이션에 직접 통합할 수 있습니다. TypeScript로 구축되고 최신 웹 개발을 위해 설계된 이 SDK는 사용자 관리, 메시징, 분석 및 기능 플래그를 위한 포괄적인 도구를 제공합니다. ### 주요 기능 {#what-you-can-do} - **사용자 관리**: 웹 애플리케이션 전반에서 사용자 ID, 속성 및 동작을 추적하고 관리합니다 - **인앱 메시지**: 사용자가 사이트를 활발히 사용하는 동안 타겟팅된 메시지와 알림을 표시합니다 - **Content Cards**: 실시간으로 업데이트되는 개인화된 콘텐츠 피드와 프로모션 카드를 표시합니다 - **배너**: 사이트 내 특정 위치에 배너 메시지를 표시합니다 - **푸시 알림**: 사용자가 사이트에 없을 때도 웹 푸시 알림을 전송하여 참여를 유도합니다 - **기능 플래그**: 서버 측 기능 플래그 관리를 통해 기능 출시 및 A/B 테스트를 제어합니다 - **분석**: 커스텀 이벤트, 사용자 상호작용 및 전환 측정기준을 추적합니다 - **세션 관리**: 사용자 세션 및 참여 패턴을 모니터링합니다 싱글 페이지 애플리케이션, 이커머스 사이트 또는 콘텐츠 플랫폼을 구축하든, Braze Web SDK는 성장과 리텐션을 촉진하는 개인화되고 매력적인 사용자 경험을 만드는 데 필요한 도구를 제공합니다. ## 필수 조건 {#prerequisites} Braze Web SDK를 통합하기 전에 다음이 필요합니다: - **Braze 계정**: API 접근 권한이 있는 Braze 계정 - **API 키**: Braze 대시보드에서 확인할 수 있는 앱의 API 키 - **SDK 엔드포인트**: Braze SDK 엔드포인트 URL (예: `sdk.iad-01.braze.com`) ### 자격 증명 확인하기 {#getting-your-credentials} 1. **API 키**: Braze 대시보드에서 **설정** > **API 키**에서 확인할 수 있습니다 2. **SDK 엔드포인트**: **설정** > **SDK 인증** > **엔드포인트**에서 확인할 수 있습니다 3. **서비스 워커**: 푸시 알림에 필요합니다 (푸시 알림 섹션 참조) ## 설치 {#installation} ``` bash npm install --save @braze/web-sdk # or, using yarn: # yarn add @braze/web-sdk ``` ## 빠른 시작 {#quick-start} ``` typescript import * as braze from "@braze/web-sdk"; // Initialize the SDK braze.initialize('YOUR-API-KEY-HERE', { baseUrl: "YOUR-SDK-ENDPOINT-HERE", }); braze.changeUser('Jane Doe'); ``` ## 구성 참조 {#configuration-reference} ### 초기화 옵션 {#initialization-options} `initialize` 함수는 다음 속성을 가진 옵션 오브젝트를 받습니다: | 옵션 | 유형 | 기본값 | 설명 | |--------|------|---------|-------------| | `baseUrl` | `string` | **필수** | 이 옵션은 통합에 적합한 엔드포인트를 사용하도록 Braze Web SDK를 구성하는 데 필수입니다. 예: `braze.initialize('YOUR-API-KEY-HERE', { baseUrl: 'sdk.iad-03.braze.com' })` | | `enableLogging` | `boolean` | `false` | 기본적으로 로깅을 활성화하려면 true로 설정합니다. 이렇게 하면 Braze가 모든 사용자에게 표시되는 JavaScript 콘솔에 로그를 기록합니다! 프로덕션에 페이지를 릴리스하기 전에 이 옵션을 제거하거나 setLogger를 사용하여 대체 로거를 제공해야 합니다. | | `allowUserSuppliedJavascript` | `boolean` | `false` | 기본적으로 Braze Web SDK는 사용자 제공 JavaScript 클릭 동작을 허용하지 않으며, HTML 인앱 메시지 및 배너를 활성화하지 않습니다. 이는 Braze 대시보드 사용자가 사이트에서 JavaScript를 실행할 수 있기 때문입니다. Braze 대시보드 사용자가 악의적이지 않은 JavaScript 클릭 동작을 작성한다고 신뢰하는 경우 이 속성을 true로 설정합니다. | | `doNotLoadFontAwesome` | `boolean` | `false` | Braze는 인앱 메시지 아이콘에 Font Awesome을 사용합니다. 기본적으로 Braze는 FontAwesome CDN에서 FontAwesome 4.7.0을 자동으로 로드합니다. 이 동작을 비활성화하려면(예: 사이트에서 사용자 정의 버전의 FontAwesome을 사용하는 경우) 이 옵션을 `true`로 설정합니다. 이 경우 사이트에 FontAwesome이 로드되어 있는지 확인해야 합니다. 그렇지 않으면 인앱 메시지가 올바르게 렌더링되지 않을 수 있습니다. | | `inAppMessageZIndex` | `number` | `999999` | 기본적으로 Braze SDK는 z-index 999999로 In-App Messages를 표시합니다. 이 기본값을 재정의하려면 이 옵션에 값을 제공합니다. | | `sessionTimeoutInSeconds` | `number` | `30` | 기본적으로 세션은 30초 동안 비활성 상태이면 타임아웃됩니다. 이 기본값을 재정의하려면 이 옵션에 값을 제공합니다. | | `deviceId` | `string` | 자동 생성 | 기본적으로 Braze는 기기 ID로 임의의 GUID를 할당합니다. 사용자 정의 값으로 이 기본값을 재정의하려면 이 구성 옵션에 값을 제공합니다. | | `appVersion` | `string` | `undefined` | 이 옵션에 값을 제공하면 Braze로 전송되는 사용자 이벤트가 지정된 버전과 연결되며, 이를 사용자 세분화에 사용할 수 있습니다. | | `appVersionNumber` | `string` | `undefined` | 사용자 세분화에 사용할 수 있는 숫자 앱 버전 값입니다. 이 값은 "1.2.3.4"와 같이 네 개의 필드로 전송해야 하며, 그렇지 않으면 무시됩니다. 참고: `appVersion`도 동일한 값 또는 이 버전의 고유한 이름으로 설정해야 합니다. | | `contentSecurityNonce` | `string` | `undefined` | 이 옵션에 값을 제공하면 Braze SDK가 SDK에서 생성하는 모든 `