본문으로 건너뛰기

iOS SDK

iOS 앱에 ZeroTalk 채팅 위젯을 추가하는 방법을 안내합니다. 설치를 마치면 앱 위에 채팅 오버레이가 표시되며, 사용자는 네이티브 앱 경험을 유지하면서 상담을 시작할 수 있습니다.

사전 요구사항​

항목요구사항
iOS15.0 이상
Swift5.9 이상
Plugin Key대시보드 → 채팅 설정 → 연동 / 개발 → 연동 설정에서 발급
React Native · Flutter 는 아직 제공하지 않습니다

현재 공식 제공 SDK 는 iOS(Swift) 와 Android(Kotlin) 두 가지입니다. React Native · Flutter 용 패키지는 배포하지 않습니다.

크로스플랫폼 앱이라면 각 플랫폼의 네이티브 모듈에서 위 두 SDK 를 감싸 연동해야 합니다. 필요하시면 문의해 주세요.

Plugin Key 위치

대시보드 → 채팅 설정 → 연동 / 개발 → 연동 설정 → 웹사이트 카드 → Plugin Key 관리 → + 키 생성 에서 Plugin Key (pk_live_...) 를 발급받습니다.

설치​

Podfile 에 다음을 추가합니다.

Podfile
pod 'ZeroTalkSDK', '0.1.19'
pod install

이후 .xcworkspace 파일로 프로젝트를 엽니다.

최신 버전 확인

CocoaPods — ZeroTalkSDK 에서 최신 버전을 확인할 수 있습니다.

위젯 시작 (boot)​

boot 는 앱 실행 시 한 번 호출합니다. 로그인을 마친 뒤나 홈 화면에 진입할 때 호출하는 것을 권장합니다.

import ZeroTalkSDK

ZeroTalk.boot(ZeroTalkConfig(
pluginKey: "YOUR_PLUGIN_KEY",
apiBaseUrl: "https://api.talk.zeroworks.ai/api/v1",
wsUrl: "wss://api.talk.zeroworks.ai/ws/sdk"
)) { status in
switch status {
case .success:
ZeroTalk.showMessenger()
case .failed(let error):
print("[ZeroTalk] boot 실패: \(error)")
}
}

boot 완료 전에 showMessenger() 를 호출해도 완료 후 자동으로 표시됩니다.

boot 중복 호출

boot 는 한 번만 호출합니다. 위젯을 재시작해야 하는 경우 shutdown() 호출 후 새 config 로 boot 를 재호출합니다. 단, 로그인·로그아웃에 따른 사용자 전환은 재부팅이 필요 없습니다 — identify() / logout() 을 사용하세요.

위젯 표시·숨김​

아래 메서드로 위젯을 열고 닫습니다.

ZeroTalk.showMessenger() // 위젯 열기
ZeroTalk.hideMessenger() // 위젯 닫기
ZeroTalk.toggle() // 열림 ↔ 닫힘 전환
ZeroTalk.shutdown() // 위젯 완전 종료 및 WebView 해제

사용자 인증​

로그인 사용자를 연동하면 상담 이력이 유지되고 사용자 정보가 대시보드에 표시됩니다.

ZeroTalk.boot(ZeroTalkConfig(
pluginKey: "YOUR_PLUGIN_KEY",
apiBaseUrl: "https://api.talk.zeroworks.ai/api/v1",
wsUrl: "wss://api.talk.zeroworks.ai/ws/sdk",
memberId: currentUser.id,
memberHash: computedHmac
)) { _ in }
memberHash 는 서버에서 계산합니다

memberHash 는 Secret Key 로 계산한 HMAC-SHA256 서명입니다. 계산식: HMAC-SHA256(secretKey, memberId) — hex lowercase 결과값. secretKey 와 memberId 는 모두 UTF-8 인코딩된 바이트를 사용합니다. Secret Key 를 앱 코드에 포함하면 누구든 임의의 사용자로 위장할 수 있습니다. 반드시 자사 백엔드 API 를 통해 계산하여 앱에 전달합니다.

Secret Key 는 대시보드 → 채팅 설정 → 연동 / 개발 → 연동 설정 → 웹사이트 카드 → Plugin Key 관리 에서 확인합니다. 생성 시 한 번만 표시되므로 반드시 기록해 두세요.

실행 중 사용자 전환​

앱 실행 중 사용자가 로그인하거나 로그아웃하는 경우, 위젯을 재부팅하지 않고 전환합니다.

// 로그인 완료 시점
ZeroTalk.identify(
memberId: currentUser.id,
hash: computedHmac,
profile: ["name": currentUser.name],
customFields: ["plan": "premium"]
)

// 로그아웃 시점 — 익명 사용자로 되돌아갑니다
ZeroTalk.logout()

profile 과 customFields 는 선택 인자입니다. 두 메서드 모두 boot 가 완료된 뒤에만 동작합니다(부팅 전 호출은 무시됩니다).

0.1.18 이하

0.1.18 까지는 logout() 이 기기에 저장한 방문자 ID 를 지우지 않았습니다. 그래서 로그아웃한 뒤 앱을 다시 실행하면 비회원 상태인데도 직전 회원의 상담 내역이 보이고, 그 회원의 답장 알림을 받을 수 있었습니다.

0.1.19 부터 logout() 이 방문자 ID 를 지웁니다. boot 전에 호출해도 지웁니다. 앱 코드는 바꿀 것이 없고, SDK 버전만 0.1.19 이상으로 올리면 됩니다.

고급 기능​

이벤트 콜백​

ZeroTalkDelegate 를 구현하면 위젯 상태 변화를 수신할 수 있습니다.

class MyViewController: UIViewController {

override func viewDidLoad() {
super.viewDidLoad()
ZeroTalk.setDelegate(self)
}
}

extension MyViewController: ZeroTalkDelegate {
func onMessengerShow() { }
func onMessengerHide() { }
func onBadgeChanged(unread: Int) { }
func onLinkClicked(url: String) -> Bool { return false }
func onRouteRequested(path: String) { }
func onConnectionStatusChanged(_ status: ZeroTalkConnectionStatus) { }
func onError(_ error: ZeroTalkError) { }
func onPermissionRequested(type: String) { }
}

파일 첨부​

위젯 안에서 사진 보관함·파일 선택이 동작하며, 호스트 앱이 따로 배선할 코드는 없습니다.

업로드 상한

첨부 파일은 300MB 까지 허용됩니다. 상한은 서버가 내려주는 값이라 워크스페이스 설정에 따라 달라질 수 있고, SDK 는 파일을 읽어들이기 전에 이 값으로 먼저 걸러냅니다. 대용량 파일은 앱이 백그라운드로 내려가도 전송이 이어집니다.

푸시 알림 (APNs)​

APNs 토큰을 등록하면 앱이 백그라운드 상태일 때 새 메시지 알림을 받을 수 있습니다.

사전 준비 — 대시보드에 APNs 인증 키 등록 (관리자)

아래 앱 설정으로 디바이스 토큰을 등록해도, 대시보드에 APNs 인증 키가 등록돼 있지 않으면 푸시가 발송되지 않습니다. 워크스페이스 관리자가 먼저 한 번 등록해야 합니다.

대시보드 → 설정(⚙) → 연동 / 개발 → 모바일 SDK 푸시 페이지의 iOS APNs 카드에서 다음을 입력합니다.

입력 항목무엇을 / 어디서
.p8 키 파일Apple Developer → Certificates, Identifiers & Profiles → Keys 에서 Apple Push Notifications service (APNs) 를 체크해 키를 생성하고 .p8 파일을 다운로드합니다 (생성 시 1회만 다운로드 가능).
Key ID키 생성 시 표시되는 10자리 식별자 (Keys 목록에서도 확인).
Team IDApple Developer → Membership 탭의 10자리 Team ID.
Bundle ID앱 번들 식별자 (Xcode → Target → General → Bundle Identifier). 예: com.company.app
환경Production (App Store·TestFlight 빌드) 또는 Sandbox (개발용) (Xcode 로 직접 설치한 개발 빌드). 앱이 디바이스 토큰을 발급받은 환경과 일치해야 푸시가 도착합니다.

1단계: Xcode 에서 Push Notifications Capability 를 추가합니다.

Xcode → 프로젝트 선택 → Target → Signing & Capabilities → + Capability → Push Notifications

2단계: 알림 권한을 받고 APNs 에 등록합니다.

이 단계를 건너뛰면 아래 3단계의 콜백이 한 번도 호출되지 않아 토큰이 등록되지 않습니다.

AppDelegate.swift
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
UNUserNotificationCenter.current()
.requestAuthorization(options: [.alert, .badge, .sound]) { granted, _ in
guard granted else { return }
DispatchQueue.main.async {
application.registerForRemoteNotifications()
}
}
return true
}

3단계: AppDelegate 에서 토큰을 SDK 에 전달합니다.

AppDelegate.swift
func application(
_ application: UIApplication,
didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data
) {
ZeroTalk.initPushToken(deviceToken)
}

func application(
_ application: UIApplication,
didFailToRegisterForRemoteNotificationsWithError error: Error
) {
NSLog("[ZeroTalk] APNs 등록 실패: %@", error.localizedDescription)
}

4단계: 알림을 탭하면 그 대화가 열리도록 연결합니다 (0.1.18 이상).

알림 탭은 UNUserNotificationCenter 의 delegate 로 전달됩니다. delegate 를 연결하지 않으면 탭 이벤트가 앱에 아예 오지 않아, 알림을 눌러도 아무 일이 일어나지 않습니다. 원인이 SDK 로 오해되기 쉬운 지점이니 두 가지를 함께 넣어 주세요.

AppDelegate.swift
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
UNUserNotificationCenter.current().delegate = self // 빠뜨리면 탭이 앱에 오지 않습니다
UNUserNotificationCenter.current()
.requestAuthorization(options: [.alert, .badge, .sound]) { granted, _ in
guard granted else { return }
DispatchQueue.main.async { application.registerForRemoteNotifications() }
}
return true
}

extension AppDelegate: UNUserNotificationCenterDelegate {
// 사용자가 알림을 탭했을 때. 앱이 꺼져 있었든 떠 있었든 이 한 곳으로 옵니다.
func userNotificationCenter(
_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse,
withCompletionHandler completionHandler: @escaping () -> Void
) {
ZeroTalk.handlePushNotification(response.notification.request.content.userInfo)
completionHandler()
}

// 앱이 떠 있는 동안 온 알림도 배너로 띄웁니다. 없으면 포그라운드에서는
// 알림이 보이지 않아 탭할 기회 자체가 없습니다.
func userNotificationCenter(
_ center: UNUserNotificationCenter,
willPresent notification: UNNotification,
withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void
) {
completionHandler([.banner, .sound])
}
}

제로톡 알림이 아니면 false 를 돌려주므로, 앱 자체 알림 처리와 함께 써도 안전합니다.

앱이 완전히 종료돼 있었다면

알림 탭이 위젯 준비보다 먼저 도착합니다. SDK 가 그 요청을 들고 있다가 준비되는 즉시 상담창을 띄우고 해당 대화를 엽니다 — 호스트가 순서를 신경 쓰거나 showMessenger() 를 따로 부를 필요가 없습니다.

로그아웃하거나 다른 계정으로 로그인하면 들고 있던 요청은 버립니다. 이전 사용자 앞으로 온 알림이 새 사용자 화면에서 열리지 않게 하기 위해서입니다.

0.1.17 에서는 앱이 꺼진 상태의 탭이 동작하지 않습니다

0.1.17 은 판별(isZeroTalkPush)과 앱이 떠 있을 때의 탭까지만 동작합니다. 앱이 완전히 종료된 상태에서 알림을 탭하면 앱만 뜨고 대화는 열리지 않습니다. 0.1.18 에서 해소됐습니다.

앱 내 푸시 알림 ON/OFF 토글 API 는 SDK 에 없습니다. 사용자가 알림을 받지 않으려면 OS 알림 설정에서 앱 알림을 꺼야 합니다.

비회원 푸시는 0.1.17 부터 동작합니다

푸시 토큰은 대화 상대(contact)에 묶여 저장되는데, 로그인하지 않은 방문자는 첫 문의를 보내기 전까지 contact 이 만들어지지 않습니다.

0.1.16 이하는 이 시점을 넘기면 토큰 등록을 다시 시도하지 않아 비회원 사용자에게 푸시가 도착하지 않았습니다. 앱에도 서버에도 오류가 남지 않아 증상만으로는 알아채기 어려웠습니다.

0.1.17 부터는 세션이 만들어질 때까지 토큰을 들고 있다가 자동으로 등록합니다. 로그인·로그아웃으로 사용자가 바뀌면 기기를 새 사용자에게 다시 묶는 것도 함께 처리합니다. 호스트 앱이 추가로 할 일은 없습니다.

테마​

CSS 변수 토큰으로 위젯의 글꼴·모서리 등을 커스터마이징합니다.

// boot 시점에 적용
ZeroTalkConfig(
pluginKey: "YOUR_PLUGIN_KEY",
// ...
theme: [
"--zerotalk-font-family": "'Pretendard', sans-serif",
"--zerotalk-radius": "12px"
]
)

// 런타임에 변경
ZeroTalk.setTheme(["--zerotalk-radius": "12px"])
색상(--zerotalk-primary 계열)은 여기서 지정하지 않습니다

브랜드 색상은 대시보드 → 채팅 설정 → 연동 / 개발 의 위젯 색상 설정으로 관리됩니다. SDK 는 라이트/다크 모드를 적용할 때 색상 토큰을 자체 값으로 다시 쓰기 때문에, theme 이나 setTheme 으로 넘긴 --zerotalk-primary 계열 값은 유지되지 않습니다.

다크모드 (Appearance)​

기기 OS 다크모드 설정에 따라 위젯 외관을 자동 전환합니다. 기본값은 .system 으로 OS 설정을 따릅니다.

ZeroTalkConfig(
pluginKey: "YOUR_PLUGIN_KEY",
// ...
appearance: .system // .system | .light | .dark
)
값설명
.systemOS 다크모드 설정을 따름 (기본값)
.light항상 라이트 모드
.dark항상 다크 모드

사용자 프로필·커스텀 필드​

대시보드에서 사용자 정보를 확인할 수 있도록 프로필 정보와 커스텀 속성을 전달합니다.

ZeroTalkConfig(
pluginKey: "YOUR_PLUGIN_KEY",
// ...
profile: [
"name": "홍길동",
"email": "hong@example.com",
"phone": "010-1234-5678"
],
customFields: [
"plan": "premium",
"company_id": "company_123"
]
)

:::info customFields 키 명명 규칙

대시보드에 미리 등록된 커스텀 필드만 저장됩니다. 키는 정규식 ^[a-z][a-z0-9_]{0,99}$ 을 만족해야 합니다 — 소문자로 시작하고, 영문 소문자·숫자·_ 만 허용, 길이 1–100자.

  • ✅ plan, company_id, nested_plan
  • ❌ Plan (대문자 시작), companyId (camelCase), company.id (점 포함), 1plan (숫자 시작)

규칙 위반 키 또는 대시보드 미등록 키는 무시되거나 422 VALIDATION_FAILED 응답을 받을 수 있습니다.

:::

:::tip profile.phone 형식

profile.phone 은 E.164 정규화(예: +821012345678) 후 저장됩니다. 010-1234-5678 처럼 한국식으로 보내도 무방합니다. 이전 키 mobileNumber 는 서버에서 인식하지 않으므로 phone 으로 사용하세요.

:::

API 레퍼런스​

ZeroTalkConfig​

파라미터타입필수설명
pluginKeyString필수대시보드에서 발급한 Plugin Key (pk_live_ 또는 pk_test_ 로 시작)
apiBaseUrlString필수https://api.talk.zeroworks.ai/api/v1
wsUrlString필수wss://api.talk.zeroworks.ai/ws/sdk
cdnBaseUrlString?선택위젯 리소스 CDN. 기본값 https://cdn.talk.zeroworks.ai/latest — 운영 환경이면 생략합니다
memberIdString?선택로그인 사용자 ID. 비회원이면 생략
memberHashString?선택HMAC-SHA256 서명. 회원 인증 시 필수
languageZeroTalkLanguage?선택위젯 표시 언어. 생략 시 ko
bootTimeoutSecondsDouble?선택boot 완료 대기 한도(초). 기본값 15. 0 이하·3600 초과는 설정 오류
profile[String: Any]?선택사용자 이름·이메일 등 프로필 정보
customFields[String: Any]?선택커스텀 속성 (플랜, 회사 ID 등)
theme[String: String]?선택CSS 변수 토큰 (글꼴·모서리 등. 색상은 대시보드에서 관리)
appearanceZeroTalkAppearance선택위젯 외관 모드. 기본값 .system

ZeroTalkLanguage​

값설명
.auto기기 언어 자동 감지 (인식 못 하면 ko)
.ko · .en · .zhCN · .zhTW · .ja해당 언어로 고정

ZeroTalkAppearance​

값설명
.systemOS 다크모드 설정을 따름 (기본값)
.light항상 라이트 모드
.dark항상 다크 모드

ZeroTalkDelegate​

메서드설명
onMessengerShow()위젯이 열렸을 때
onMessengerHide()위젯이 닫혔을 때
onBadgeChanged(unread:)읽지 않은 메시지 수 변경
onLinkClicked(url: String) -> Bool링크 클릭. false (기본) — SDK 가 확인 다이얼로그 후 앱 내 브라우저로 열기. true — SDK 는 열지 않고 호스트 앱이 자체 처리
onRouteRequested(path: String)앱 내 화면 이동 요청
onConnectionStatusChanged(_ status:)WebSocket 연결 상태 변경
onError(_ error: ZeroTalkError)오류 발생
onPermissionRequested(type: String)위젯이 OS 권한을 필요로 할 때 알림용으로 호출. iOS 는 SDK 가 이어서 권한 요청까지 직접 수행하므로 호스트가 따로 처리할 필요는 없습니다

ZeroTalkConnectionStatus​

케이스설명
.connectedWebSocket 연결됨
.disconnectedWebSocket 연결 끊김
.connecting연결 시도 중

정적 메서드​

메서드설명
boot(_:completion:)위젯 초기화
showMessenger()위젯 열기
hideMessenger()위젯 닫기
toggle()열림 ↔ 닫힘 전환
shutdown()위젯 종료 및 WebView 해제
setDelegate(_:)이벤트 콜백 수신 객체 설정
identify(memberId:hash:profile:customFields:)실행 중 로그인 사용자로 전환 (재부팅 불필요)
logout()익명 사용자로 되돌리기
initPushToken(_:)APNs 디바이스 토큰 등록
isZeroTalkPush(_:)받은 알림이 제로톡 상담 알림인지 판별 (0.1.17 이상)
handlePushNotification(_:)알림 탭을 넘겨 해당 대화를 엶. 제로톡 알림이 아니면 false (0.1.18 이상)
notifyPermission(type:granted:)호스트 앱이 OS 권한을 직접 처리한 경우에만 결과를 통보. iOS 는 SDK 가 권한 요청과 결과 전달을 자동으로 처리하므로 보통 호출할 일이 없습니다. "push" 토글 용도로는 사용하지 않습니다.
setTheme(_:)런타임 테마 변경 (색상 토큰 제외)
setLanguage(_:)위젯 표시 언어 변경

ZeroTalkError​

케이스설명
notBootedYetboot 호출 전에 다른 메서드를 호출했을 때
bootInProgressboot 가 아직 진행 중일 때 중복 호출
invalidConfigpluginKey 등 필수 설정값이 유효하지 않을 때
loadFailed위젯 리소스 로드 실패
noActiveScene활성 UIWindowScene 이 없을 때
bootTimeoutboot 완료 타임아웃
bridgeError(code: Int, message: String)위젯 내부 오류

연동이 안 될 때​

증상원인해결
boot 콜백이 .failed(.invalidConfig) 입니다pluginKey 등 필수 설정값이 유효하지 않습니다Plugin Key 가 pk_live_ 또는 pk_test_ 로 시작하는지, bootTimeoutSeconds 를 지정했다면 0 보다 크고 3600 이하인지 확인하세요
.failed(.noActiveScene) 입니다활성 UIWindowScene 이 없는 시점에 boot 를 호출했습니다앱 화면이 뜬 뒤에 boot 를 호출하세요
.failed(.bootTimeout) 입니다bootTimeoutSeconds(기본 15초) 안에 boot 가 끝나지 않았습니다기기 네트워크를 확인하고, 느린 환경이면 bootTimeoutSeconds 를 늘리세요
identify() · logout() 을 불렀는데 반영되지 않습니다boot 가 완료되기 전의 호출은 무시됩니다.success 를 받은 뒤에 호출하세요
디바이스 토큰이 등록되지 않습니다2단계의 registerForRemoteNotifications() 를 호출하지 않아 3단계 콜백이 오지 않습니다Push Notifications Capability 와 2단계 코드를 확인하세요
푸시가 오지 않습니다대시보드에 APNs 인증 키가 없거나, 등록한 환경(Production·Sandbox)이 토큰을 발급받은 환경과 다르거나, SDK 가 0.1.16 이하입니다위 「푸시 알림 (APNs)」 절의 사전 준비 표를 확인하세요
알림을 탭해도 아무 일이 없습니다UNUserNotificationCenter.current().delegate 를 연결하지 않았거나, SDK 가 0.1.17 이하입니다4단계 배선을 확인하고 0.1.18 이상으로 올리세요

다음 단계​