iOS SDK
iOS 앱에 ZeroTalk 채팅 위젯을 추가하는 방법을 안내합니다. 설치를 마치면 앱 위에 채팅 오버레이가 표시되며, 사용자는 네이티브 앱 경험을 유지하면서 상담을 시작할 수 있습니다.
사전 요구사항
| 항목 | 요구사항 |
|---|---|
| iOS | 15.0 이상 |
| Swift | 5.7 이상 |
| Plugin Key | 대시보드 → 채팅 설정 → 연동 / 개발 → 연동 설정에서 발급 |
현재 공식 제공 SDK 는 iOS(Swift) 와 Android(Kotlin) 두 가지입니다. React Native · Flutter 용 패키지는 배포하지 않습니다.
크로스플랫폼 앱이라면 각 플랫폼의 네이티브 모듈에서 위 두 SDK 를 감싸 연동해야 합니다. 필요하시면 문의해 주세요.
대시보드 → 채팅 설정 → 연동 / 개발 → 연동 설정 → 웹사이트 카드 → Plugin Key 관리 → + 키 생성 에서 Plugin Key (pk_live_...) 를 발급받습니다.
설치
- CocoaPods
- Swift Package Manager
Podfile 에 다음을 추가합니다.
pod 'ZeroTalkSDK', '0.1.18'
pod install
이후 .xcworkspace 파일로 프로젝트를 엽니다.
CocoaPods — ZeroTalkSDK 에서 최신 버전을 확인할 수 있습니다.
Xcode → File → Add Package Dependencies 에서 다음 URL을 입력합니다.
https://github.com/generativelab-develop/zerotalk-ios-sdk
버전 규칙은 Exact Version (0.1.18) 으로 설정합니다.
GitHub Releases — zerotalk-ios-sdk 에서 최신 버전을 확인할 수 있습니다.
위젯 시작 (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 는 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 이 완료된 뒤에만 동작합니다(부팅 전 호출은 무시됩니다).
고급 기능
이벤트 콜백
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 인증 키가 등록돼 있지 않으면 푸시가 발송되지 않습니다. 워크스페이스 관리자가 먼저 한 번 등록해야 합니다.
대시보드 → 설정(⚙) → 연동 / 개발 → 모바일 SDK 푸시 페이지의 iOS APNs 카드에서 다음을 입력합니다.
| 입력 항목 | 무엇을 / 어디서 |
|---|---|
| .p8 키 파일 | Apple Developer → Certificates, Identifiers & Profiles → Keys 에서 Apple Push Notifications service (APNs) 를 체크해 키를 생성하고 .p8 파일을 다운로드합니다 (생성 시 1회만 다운로드 가능). |
| Key ID | 키 생성 시 표시되는 10자리 식별자 (Keys 목록에서도 확인). |
| Team ID | Apple 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단계의 콜백이 한 번도 호출되지 않아 토큰이 등록되지 않습니다.
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 에 전달합니다.
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 로 오해되기 쉬운 지점이니 두 가지를 함께 넣어 주세요.
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 알림 설정에서 앱 알림을 꺼야 합니다. 앱 내 토글은 향후 위젯 UI / 대시보드 설정에서 제공될 예정입니다.
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
)
| 값 | 설명 |
|---|---|
.system | OS 다크모드 설정을 따름 (기본값) |
.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
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
pluginKey | String | 필수 | 대시보드에서 발급한 Plugin Key (pk_live_ 또는 pk_test_ 로 시작) |
apiBaseUrl | String | 필수 | https://api.talk.zeroworks.ai/api/v1 |
wsUrl | String | 필수 | wss://api.talk.zeroworks.ai/ws/sdk |
cdnBaseUrl | String? | 선택 | 위젯 리소스 CDN. 기본값 https://cdn.talk.zeroworks.ai/latest — 운영 환경이면 생략합니다 |
memberId | String? | 선택 | 로그인 사용자 ID. 비회원이면 생략 |
memberHash | String? | 선택 | HMAC-SHA256 서명. 회원 인증 시 필수 |
language | ZeroTalkLanguage? | 선택 | 위젯 표시 언어. 생략 시 ko |
bootTimeoutSeconds | Double? | 선택 | boot 완료 대기 한도(초). 기본값 15. 0 이하·3600 초과는 설정 오류 |
profile | [String: Any]? | 선택 | 사용자 이름·이메일 등 프로필 정보 |
customFields | [String: Any]? | 선택 | 커스텀 속성 (플랜, 회사 ID 등) |
theme | [String: String]? | 선택 | CSS 변수 토큰 (글꼴·모서리 등. 색상은 대시보드에서 관리) |
appearance | ZeroTalkAppearance | 선택 | 위젯 외관 모드. 기본값 .system |
ZeroTalkLanguage
| 값 | 설명 |
|---|---|
.auto | 기기 언어 자동 감지 (인식 못 하면 ko) |
.ko · .en · .zhCN · .zhTW · .ja | 해당 언어로 고정 |
ZeroTalkAppearance
| 값 | 설명 |
|---|---|
.system | OS 다크모드 설정을 따름 (기본값) |
.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
| 케이스 | 설명 |
|---|---|
.connected | WebSocket 연결됨 |
.disconnected | WebSocket 연결 끊김 |
.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(_:) | 런타임 테마 변경 (색상 토큰 제외) |
ZeroTalkError
| 케이스 | 설명 |
|---|---|
notBootedYet | boot 호출 전에 다른 메서드를 호출했을 때 |
bootInProgress | boot 가 아직 진행 중일 때 중복 호출 |
invalidConfig | pluginKey 등 필수 설정값이 유효하지 않을 때 |
loadFailed | 위젯 리소스 로드 실패 |
noActiveScene | 활성 UIWindowScene 이 없을 때 |
bootTimeout | boot 완료 타임아웃 |
bridgeError(code: Int, message: String) | 위젯 내부 오류 |
다음 단계
- Android SDK — Kotlin / Maven Central 설치 가이드
- 회원 인증 — HMAC-SHA256 memberHash 계산 방법
- 웹 위젯 커스터마이징 — 색상, 위치, 인사 메시지 설정