설치
Swift Package Manager 또는 CocoaPods로 Apple SDK를 추가하고 앱에서 시작하는 방법을 설명합니다.
설치는 패키지를 추가하고 앱 시작 시 Sophonz.start(options:)를 한 번 호출하는 것으로 끝납니다. 계측은 SDK가 설치하는 캡처 서비스가 담당하므로, 화면이나 네트워크 호출마다 코드를 넣을 필요가 없습니다.
빠른 시작
아래에서 수집기 URL, 프로젝트, 앱 키를 입력하고 탭(Swift · Objective-C)을 선택하면 그 값으로 초기화 코드가 생성됩니다. 고급 옵션은 "고급 옵션" 토글 뒤에 있습니다.
// AppDelegate.swift
import SophonzCore
let option = Sophonz.Options(
project: "sophonz",
appKey: "sk_tYtJUa4WweXXedj6lxjSqoPW5O0DnbXz",
endpoints: Sophonz.Endpoints(collectorURL: "https://in.sophonz.ai"))
Sophonz.setup(options: option)?.start()NOTE — 앱 키가 없으면 조용히 버려집니다
수집기는 service_key_mode: enforce로 동작합니다. 앱 키가 없거나 등록되지 않은 키로 보낸 텔레메트리는 오류 없이 버려지므로, 키를 넣지 않은 빌드는 정상으로 보이면서 아무것도 남기지 않습니다.
요구 사항
| 항목 | 요구 사항 |
|---|---|
| iOS | 13.0 이상 |
| tvOS · macOS | 13.0 이상 |
| watchOS | 6.0 이상 |
| Swift | 5.9 이상 |
| Xcode | 26 이상 (SDK 빌드 기준) |
Swift Package Manager
Xcode의 File > Add Package Dependencies에 저장소 주소를 넣습니다.
https://github.com/sophonz-labs/sophonz-apple-sdkPackage.swift로 관리하는 프로젝트라면 다음과 같습니다.
dependencies: [
.package(url: "https://github.com/sophonz-labs/sophonz-apple-sdk", from: "1.0.0")
],
targets: [
.target(
name: "YourApp",
dependencies: [
.product(name: "SophonzIO", package: "sophonz-apple-sdk")
]
)
]앱이 링크할 것은 SophonzIO 하나입니다. 공개 API인 Sophonz 클래스와 그 옵션이 여기에 있고, 구현은 그 뒤에 있습니다.
NOTE — 나머지 라이브러리 제품
패키지는 SophonzCore, SophonzSemantics, SophonzMacros, SophonzKSCrashBacktraceSupport도 제품으로 내보냅니다. 일반적인 앱은 필요하지 않으며, SophonzIO가 필요한 것을 가져옵니다. 이름이 *Internal인 모듈은 지원 대상 API가 아닙니다.
미리 빌드된 XCFramework
소스에서 빌드하지 않으려면 릴리스에 첨부된 XCFramework를 씁니다. 각 릴리스는
SophonzIO-<버전>.xcframework.zip과 .sha256을 함께 올립니다.
.binaryTarget(
name: "SophonzIO",
url: "https://github.com/sophonz-labs/sophonz-apple-sdk/releases/download/v1.0.0/SophonzIO-1.0.0.xcframework.zip",
checksum: "<.sha256 파일의 내용>"
)Xcode 프로젝트라면 압축을 풀어 타깃의 Frameworks, Libraries, and Embedded Content에 끌어다 놓고 Embed & Sign으로 둡니다.
CocoaPods
use_frameworks!
target 'YourApp' do
pod 'SophonzIO'
endpod installpodspec은 v 접두사가 붙은 태그(v1.0.0)를 가리킵니다. 아직 트렁크에 게시되지 않았으므로 저장소를 직접 지정합니다.
pod 'SophonzIO', :git => 'https://github.com/sophonz-labs/sophonz-apple-sdk.git', :tag => 'v1.0.0'SDK 시작
Sophonz.start(options:)는 정적 메서드이며 던집니다. 앱 시작 지점에서 메인 스레드에서 한 번 호출합니다.
설정은 타깃의 Sophonz-Info.plist에서 읽는 것을 권장합니다. 앱 키를 소스에 커밋하지 않고 빌드 구성마다 바꿀 수 있습니다.
<key>CollectorURL</key>
<string>https://in.sophonz.ai</string>
<key>AppKey</key>
<string>$(SOPHONZ_APP_KEY)</string>
<key>AppName</key>
<string>내 앱</string>
<key>Project</key>
<string>my-project</string>
<key>DeploymentEnvironment</key>
<string>production</string>| 키 | 보내는 값 | 설명 |
|---|---|---|
CollectorURL | — | 수집기 기본 주소. /v1/traces 같은 경로는 붙이지 않습니다 |
AppKey | service.key | 포털이 발급한 sk_… |
AppName | service.name | 포털이 앱을 찾는 키. 등록한 이름과 같아야 합니다 |
Project | service.namespace | 앱을 묶는 논리 단위 |
DeploymentEnvironment | deployment.environment.name | production, staging 등 |
CAUTION — AppName은 포털의 조인 키입니다
app.sophonz.ai는 (프로젝트, service.name)으로 앱을 찾습니다. 이 값을 바꾸면 수집기는 계속 데이터를 받는데 포털의 앱 화면만 비어 보입니다 — 전송이 끊긴 것처럼 보이지만 전송은 멀쩡합니다. 포털에 등록한 이름을 그대로 넣으세요.
NOTE — $(…)는 타깃 Info.plist에서만 치환됩니다
Xcode는 빌드 설정을 타깃의 Info.plist에서만 확장합니다. 리소스로 복사되는 설정 plist에 $(SOPHONZ_APP_KEY)를 적으면 그 문자열이 그대로 들어갑니다. 빌드 스크립트에서 치환해 생성하세요. SDK는 치환되지 않은 $(…)와 빈 문자열을 모두 "값 없음"으로 취급합니다.
import SwiftUI
import SophonzIO
@main
struct YourApp: App {
init() {
guard let options = Sophonz.Options.fromPlist() else {
// 수집기나 키가 없는 빌드입니다. 시작하지 않는 편이 낫습니다.
return
}
do {
try Sophonz.start(options: options)
} catch {
// 시작 실패가 앱을 막지 않도록 합니다. 계측이 없는 것보다
// 앱이 뜨지 않는 것이 나쁩니다.
print("[sophonz] 시작 실패: \(error)")
}
}
var body: some Scene {
WindowGroup { ContentView() }
}
}UIKit 앱이라면 application(_:didFinishLaunchingWithOptions:)에 같은 코드를 둡니다.
값을 코드로 넘기기
plist 대신 인자로 넘길 수도 있습니다.
try Sophonz.start(options: .withCollector(
url: "https://in.sophonz.ai",
appKey: "sk_…",
appName: "내 앱",
project: "my-project",
deploymentEnvironment: "production"
))시작 이후에는 Sophonz.shared로 접근합니다.
if Sophonz.shared.isSDKEnabled {
// 수집 중
}SwiftUI 화면 이름
UIKit 앱은 화면 이름이 자동으로 붙습니다. 뷰 컨트롤러가 곧 화면이기 때문입니다. SwiftUI는 그렇지 않습니다 — NavigationStack 전체가 뷰 컨트롤러 하나(UIKitNavigationController)라서, 이름을 붙이지 않으면 앱의 모든 스팬이 그 이름 하나로 기록되고 화면별 분석이 불가능해집니다.
화면이 스스로 이름을 밝힙니다.
GalleryView()
.sophonzScreen("Gallery")sophonzScreen은 화면 이름을 설정하면서 렌더 스팬도 함께 남깁니다. 화면의 일부인 뷰에는 sophonzTrace(_:)를 씁니다. 스팬은 시작 시점의 화면을 기록하므로, 화면을 떠난 뒤 끝나는 요청도 시작한 화면에 남습니다.
텔레메트리가 언제 나가는가
스팬과 로그는 끝나는 즉시 디스크에 쌓이고, 세션 파트가 끝날 때 업로드됩니다 — 앱이 백그라운드로 갈 때, 또는 강제 종료 뒤 다음 실행 때입니다. 주기적인 타이머가 아닙니다.
포그라운드에 10분 동안 떠 있던 앱은 10분치 텔레메트리를 디스크에 갖고 있고 아직 아무것도 보내지 않은 상태입니다. "간헐적으로 들어온다"는 관찰은 대부분 이것이며, 전송 계층의 문제가 아닙니다.
자체 익스포터로 시작
Sophonz 수집기 대신 직접 익스포터를 붙이는 모드입니다. 이 모드에서는 업로드가 아예 만들어지지 않으므로, 데이터가 나가는 유일한 통로가 익스포터입니다. 그래서 otel:이 선택이 아니라 필수 인자입니다.
import SophonzIO
import OpenTelemetrySdk
let otel = Sophonz.OTelOptions(
spanExporters: [myOtlpSpanExporter],
logExporters: [myOtlpLogExporter]
)
try Sophonz.start(options: .withLocalConfiguration(otel: otel))OTelOptions는 resource, spanProcessors, spanExporters, logProcessors, logExporters를 받습니다. service.name, service.version, telemetry.sdk.language는 SDK가 항상 설정하며 resource로 덮어쓸 수 없습니다.
Objective-C 프로젝트
Swift 모듈이므로 Objective-C에서 쓰려면 브리징 헤더 대신 생성된 인터페이스를 임포트합니다. 시작 코드는 Swift 파일 하나에 두고 Objective-C에서 그 함수를 호출하는 편이 단순합니다.