שילוב iOS SDK
iOS SDK נייטיבי למעקב אירועים, ניהול סשנים, שליחת התראות פוש והצגת הודעות בתוך האפליקציה.
יכולות
- קל משקל - טביעת רגל מינימלית
- מהיר - מותאם לביצועים
- תמיכה במצב לא מקוון - תור אירועים מבוסס SQLite
- ניסיונות חוזרים - השהיה מעריכית בין ניסיונות לאחר כישלון
- שליחה באצוות - שליחה יעילה באצוות (50 אירועים / 5 שניות)
- In-App Messaging - הודעות Native ו־HTML, 5 סוגים, עם הגבלת תדירות
- Push Notifications - Apple Push Notification Service (APNS)
- גישה ממוקדת פרטיות - תואם GDPR ומכבד הסכמת משתמש
- iOS 14+ - תמיכה בגרסאות iOS מודרניות
דרישות
- iOS 14.0+
- Xcode 15.0+
- Swift 5.9+
התקנה
Swift Package Manager
ה־SDK מופץ כחבילת Swift. הוסיפו את השורה הבאה ל־Package.swift:
dependencies: [
.package(url: "https://github.com/joryio/joryio-ios.git", from: "1.0.0")
]
החבילה מספקת שני products. קשרו את מה שהאפליקציה צריכה:
.target(
name: "YourApp",
dependencies: [
.product(name: "Joryio", package: "joryio-ios"), // מעקב, זהות, פוש
.product(name: "JoryioUI", package: "joryio-ios"), // + הצגת הודעות In-App
]
)
CocoaPods
pod 'Joryio/UI' # הכול, כולל הצגת In-App
# pod 'Joryio' # מעקב, זהות ופוש בלבד - ללא WebKit מקושר
JoryioUI תלוי ב־Joryio, כך שקישור ה־product של ה־UI מביא את שניהם.
קחו את Joryio לבדו כשלאפליקציה אין שימוש בהודעות In-App, או כשסקירת אבטחה
מתנגדת לקישור WebView. שימו לב שברירת המחדל ב־CocoaPods היא Joryio נטול ה־UI -
הפקודה pod 'Joryio' לא תציג הודעות In-App עד שתחליפו אותה ב־pod 'Joryio/UI'.
או ב־Xcode:
- File → Add Package Dependencies
- הזינו:
https://github.com/joryio/joryio-ios.git - בחרו גרסה והוסיפו ל־target
התחלה מהירה
1. אתחול ה־SDK
ב־AppDelegate.swift:
import Joryio
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
// Initialize with your SDK key
Joryio.shared.initialize(
sdkKey: "jry_sdk_ios_YOUR_SDK_KEY",
apiHost: "api-eu1.joryio.com"
)
return true
}
מצאו את מפתח ה־SDK בלוח הבקרה של Joryio תחת Settings → Apps → [האפליקציה שלכם] → SDK Keys.
2. מעקב אירועים
// Basic event
Joryio.shared.track("Button Tapped")
// Event with properties
Joryio.shared.track("Product Viewed", properties: [
"product_id": "abc123",
"product_name": "Wireless Headphones",
"price": 99.99,
"category": "Electronics"
])
// Screen view
Joryio.shared.trackScreen("ProductDetail", properties: [
"product_id": "abc123"
])
3. זיהוי משתמשים
// Identify a user
Joryio.shared.identify("user-123")
Joryio.shared.setAttributes([
"email": "user@example.com",
"name": "John Doe",
"plan": "premium"
])
// Set attributes later
Joryio.shared.setAttribute("last_purchase", value: Date())
Joryio.shared.incrementAttribute("lifetime_value", by: 99.99)
// On logout
Joryio.shared.reset()
נתוני סשן אוטומטיים
iOS SDK מעשיר אירועי Session Start בנתוני מכשיר וסביבה:
$device_id$platform(ios)$model$os_name/$os_version$app_version/$build_number$bundle_id$screen_width/$screen_height$locale$language/$languages$timezonecountry(ISO-3166-1 alpha-2, נגזר מה־IP בתחילת סשן)
התראות פוש
הפעילו התראות פוש כדי לשלוח הודעות ממוקדות דרך Apple Push Notification Service (APNS).
1. הגדרה ב־AppDelegate
import Joryio
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
// Initialize SDK
Joryio.shared.initialize(
sdkKey: "jry_sdk_ios_YOUR_KEY",
apiHost: "api-eu1.joryio.com"
)
// Request push permissions
Task {
let granted = await Joryio.shared.requestPushPermissions()
if granted {
print("Push notifications enabled")
}
}
return true
}
// Handle device token registration
func application(
_ application: UIApplication,
didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data
) {
Joryio.shared.didRegisterForRemoteNotifications(deviceToken: deviceToken)
}
// Handle registration failure
func application(
_ application: UIApplication,
didFailToRegisterForRemoteNotificationsWithError error: Error
) {
Joryio.shared.didFailToRegisterForRemoteNotifications(error: error)
}
// Handle received push notification
func application(
_ application: UIApplication,
didReceiveRemoteNotification userInfo: [AnyHashable: Any],
fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void
) {
Joryio.shared.didReceiveRemoteNotification(userInfo, completionHandler: completionHandler)
}
}
2. הגדרת APNS בלוח הבקרה
כדי לשלוח התראות פוש, הגדירו את פרטי ה־APNS:
- עברו אל Settings → Apps → [האפליקציה שלכם]
- עברו ללשונית Push Notifications
- העלו תעודת APNS (.p12) או מפתח הרשאה (.p8)
- הזינו Team ID ו־Key ID (עבור .p8)
- בחרו סביבה (Development/Production)
3. יכולות פוש
// Check if push is enabled
let isEnabled = await Joryio.shared.isPushEnabled()
// Get device token
if let token = Joryio.shared.getDeviceToken() {
print("Device token: \(token)")
}
// Badge management
Joryio.shared.updateBadgeCount(5)
Joryio.shared.clearBadge()
// Unregister from push
Joryio.shared.unregisterFromPushNotifications()
הודעות בתוך האפליקציה
הציגו הודעות בתוך האפליקציה למשתמשים לפי התנהגות ומאפיינים.
אסימוני שליחה (delivery tokens)
כאשר השרת מגיש קמפיין מתאים, הוא מנפיק עבורו גם אסימון שליחה חתום וקצר-מועד. ה-SDK מחזיר את האסימון כשהוא מדווח על הצגה, קליק או סגירה, והשרת מאמת את החתימה לפני שהוא רושם משהו.
אינך צריך לעשות דבר - ה-SDK מטפל בזה עבורך. התיעוד כאן נועד להסביר מה קורה ללקוח שאינו שולח אסימון:
POST /v1/in-app/track (no deliveryToken)
{ "success": false, "error": "A delivery token is required" }
האסימון הוא מה שהופך דיווח הצגה לאמין: בלעדיו, כל מי שמחזיק במפתח ה-SDK - שנשלח בתוך כל אפליקציה ובכל עמוד - היה יכול לדווח על הצגות וקליקים לקמפיין שמעולם לא הוצג, והדוחות שלך היו סופרים אותם.
אם הצגות in-app מפסיקות להירשם, ודא שהאפליקציה נבנתה מול גרסה עדכנית של ה-SDK: בנייה שנעשתה לפני שאסימוני השליחה נוספו אינה שולחת אסימון, והשרת ידחה את ההצגות שלה.
סנכרון קמפיינים אוטומטי
ה־SDK מסנכרן קמפיינים מהשרת אוטומטית כאשר:
- האפליקציה עוברת לחזית (foreground)
- מתקבלת הודעת פוש
הסנכרונים מוגבלים לכל היותר לאחד בכל מרווח סנכרון; קראו ל־syncInAppCampaigns() כדי לסנכרן ברגעים אחרים (למשל אחרי זיהוי משתמש או עדכון מאפיינים).
שליטה ידנית בקמפיינים
// Manually sync campaigns from server
await Joryio.shared.syncInAppCampaigns()
// Manually trigger campaign evaluation
await Joryio.shared.evaluateInAppCampaigns()
// Reset displayed campaigns (for testing)
Joryio.shared.resetDisplayedCampaigns()
שני סוגי תוכן
כל קמפיין מגיע באחת משתי צורות תוכן, וה־SDK מציג כל אחת מהן אחרת:
| תוכן | מה זה | איך זה מוצג |
|---|---|---|
| Native | נתונים מובנים - כותרת, טקסט, תמונה, כפתורים | View־ים אמיתיים של UIKit, עם צבע ההדגשה, Dynamic Type, המצב הכהה ו־VoiceOver של האפליקציה. ללא WebView. |
| HTML | קוד HTML, CSS ו־JavaScript שנכתב על ידי המחבר | WKWebView בתוך ההודעה. |
הפעלת הודעות HTML
הודעות HTML כבויות כברירת מחדל. הודעת HTML מריצה JavaScript שנכתב על ידי המחבר בתוך האפליקציה שלכם, ולכן ההפעלה היא החלטה של צוות האפליקציה - לא משהו שמפעילים ממערכת השיווק:
let config = JoryioConfig(
inApp: InAppConfig(allowHtmlJsInAppMessages: true) // ברירת מחדל: false
)
Joryio.shared.initialize(
sdkKey: "jry_sdk_ios_YOUR_KEY",
apiHost: "api-eu1.joryio.com",
config: config
)
השארת ההגדרה כבויה אינה מבטלת הודעות In-App. הודעות Native ימשיכו להופיע, כי הן נתונים שה־SDK מצייר עם ה־View־ים שלו - ללא מפרש כלשהו. קמפייני HTML מדולגים ונרשמים ללוג.
אם מדיניות האבטחה שלכם אוסרת הרצת HTML שנכתב חיצונית בתוך התהליך, השאירו את ההגדרה כבויה וכתבו את הקמפיינים כהודעות Native.
ל־tvOS אין WebView כלל, ולכן הודעות HTML לעולם לא יוצגו שם, ללא קשר להגדרה. כתבו הודעות Native עבור יעדי tvOS.
רינדור ההודעות בעצמכם
ה-SDK מצייר עבורכם הודעות Native, אבל אפשר לקחת שליטה מלאה - אותו פתח מילוט
שספקים אחרים קוראים לו custom view factory. ממשו את InAppMessagePresenter
והציבו אותו:
Joryio.shared.inAppPresenter = MyPresenter()
הגילוי האוטומטי רץ רק כאשר inAppPresenter הוא nil, ולכן שלכם מחליף את
הרנדרר המובנה במקום להתנגש בו. אפשר להציב לפני או אחרי initialize.
ה-presenter מקבל את הקמפיין כש-content כבר מוכן - ה-Liquid מרונדר בצד השרת,
ושדות Native מגיעים כטקסט רגיל. דווחו על מה שהצגתם באמצעות
Joryio.shared.trackInAppImpression(campaignId, action:) כדי שהאנליטיקס ימשיך
לראות חשיפות והקלקות.
זה לא משהו שמגדירים, אבל כדאי להכיר אותו כשמשהו לא מוצג. ה-SDK מאתר את הרנדרר המובנה שלו לפי שם בזמן ריצה, והמודול שבו השם הזה נמצא תלוי בדרך השילוב:
| אופן האריזה | המחלקה שה-SDK מחפש | |
|---|---|---|
| SwiftPM | JoryioUI הוא target נפרד | JoryioUI.DefaultInAppMessagePresenter |
| CocoaPods | Joryio/UI הוא subspec, ו-subspecs חולקים את המודול של ה-pod | Joryio.DefaultInAppMessagePresenter |
שני השמות נבדקים. אם שום הודעת In-App לא מופיעה, חפשו את האזהרה של ה-SDK
"No in-app presenter found" - משמעה שמוצר ה-UI לא מקושר (pod 'Joryio/UI',
או המוצר JoryioUI ב-SwiftPM), ולא שהקמפיין לא הגיע. מבחוץ שני המצבים נראים זהים.
ל-presenter משלכם אין צורך בכל זה: הציבו אותו, והגילוי האוטומטי לא ירוץ כלל.
סוגי הודעות
ה־SDK תומך ב־5 סוגי הודעות:
- Modal - במרכז המסך עם רקע מוצל
- Banner - בראש המסך
- Slide-Up - התראה קטנה מלמטה
- Full-Screen - הודעה שתופסת את כל המסך
- Custom - האפליקציה קובעת את המיקום
הגבלת תדירות
הודעות מכבדות את כללי המגע ברמת סביבת העבודה ואת מגבלות התדירות ברמת הקמפיין:
- מקסימום חשיפות בפרק זמן
- מינימום זמן בין הודעות
- מגבלות תדירות לכל קמפיין
שליחת מאפיינים
setAttributes עמיד. כתיבה נשמרת בתור עד שהשרת מאשר אותה, כך שמאפיין שנקבע
כשהמכשיר לא מחובר נשלח כשהחיבור חוזר במקום להימחק.
- ניסיון חוזר בקריאת
setAttributesהבאה, בחזרה לחזית, ולפני סנכרון in-app. - מקובץ - רצף קריאות הופך לבקשה אחת (חלון של 800 מילישניות). כתיבה בודדת עדיין נשלחת מיד.
- נשמר באחסון המוצפן של הפלטפורמה (iOS Keychain, Android EncryptedSharedPreferences), כך שכתיבה שורדת סגירה של התהליך. נמחק ברגע שהשרת מאשר, ומנוקה על ידי
optOut()ו-wipeData().
מאפיינים משמשים גם למיקוד in-app. פרופיל השרת הוא המקור הקובע: רק כתיבות שהשרת עוד לא אישר יכולות לגבור עליו - וזה מה שמונע משני מכשירים של אותו איש קשר לחלוק על מי הוא.
אפשרויות תצורה
התאימו את התנהגות ה־SDK עם JoryioConfig:
JoryioConfig(
// User Identification
userId: String?, // Initialize with known user ID
anonymousId: String?, // Custom anonymous ID
// Batching & Performance
batchSize: Int, // Default: 50
flushInterval: TimeInterval, // Default: 5.0 seconds
sendImmediately: Bool, // Default: false
maxQueueSize: Int, // Default: 1000
// Session Management
sessionTimeout: TimeInterval, // Default: 1800 (30 minutes)
trackSessionStart: Bool, // Default: true
// Storage
persistQueue: Bool, // Default: true
// Network & Retry
maxRetries: Int, // Default: 3
retryBackoffMs: Double, // Default: 1000.0
requestTimeout: TimeInterval, // Default: 10.0
// Privacy & GDPR
respectDoNotTrack: Bool, // Default: true
optOut: Bool, // Default: false
trackingConsent: TrackingConsent, // Default: .granted
// Debugging
enableDebug: Bool, // Default: false
logLevel: LogLevel // Default: .error
)
יכולות מתקדמות
ניהול מאפייני משתמש
// Set multiple attributes
Joryio.shared.setAttributes([
"age": 28,
"city": "San Francisco",
"premium": true
])
// Set single attribute
Joryio.shared.setAttribute("language", value: "en")
// Increment numeric attribute
Joryio.shared.incrementAttribute("page_views", by: 1)
Joryio.shared.incrementAttribute("total_spent", by: 29.99)
// Remove attribute
Joryio.shared.unsetAttribute("temporary_flag")
בקרות פרטיות
// הפסקת איסוף. נשמר במכשיר - שורד הפעלה מחדש של האפליקציה.
Joryio.shared.optOut()
// Opt back in
Joryio.shared.optIn()
// Check opt-out status
if Joryio.shared.isUserOptedOut() {
print("User has opted out")
}
// מחיקת כל מה שה-SDK שמר במכשיר הזה.
// נפרד מ-optOut(): "להפסיק לאסוף" ו"למחוק את מה שכבר נאסף" הן
// בקשות שונות. זו הפעולה שנדרשת לבקשת מחיקה. היא אינה מבטלת
// הסכמה - יש לקרוא גם ל-optOut() אם זו הכוונה.
Joryio.shared.wipeData()
// Get identity info
let (userId, anonymousId) = Joryio.shared.getIdentity()
print("User: \(userId ?? "anonymous"), Anonymous ID: \(anonymousId)")
מה בדיוק עושה optOut():
| מפסיק איסוף | כן - track, identify ו-setAttributes הופכים ללא-פעולה |
| שורד הפעלה מחדש | כן - הדגל נשמר במכשיר ונקרא לפני כל איסוף |
| מוחק את מה שכבר בתור | כן - אירועים בתור וכתיבות מאפיינים שלא אושרו נמחקים, לא נשלחים מאוחר יותר |
| מעדכן את השרת | כן - מאפיין פרופיל אחד אחרון $tracking_opted_out, מאמץ מיטבי, נשלח בזמן שעוד מותר לשלוח |
| מוחק מידע שמור | לא - יש להשתמש ב-wipeData() |
המאפיין $tracking_opted_out הוא תיעוד, לא אכיפה: הוא נשמר בפרופיל כדי
שקמפיינים יוכלו להחריג לפיו. חסימה בצד השרת היא הגדרה נפרדת.
ריקון תור ידני
// Flush immediately (e.g., before app termination)
Joryio.shared.flush()
// Check queue size
let queueSize = Joryio.shared.getQueueSize()
print("Pending events: \(queueSize)")
שיטות עבודה מומלצות
1. מעקב אחר אירועים משמעותיים
התמקדו באירועים חשובים לעסק:
// Good: Specific, actionable events
Joryio.shared.track("Trial Started", properties: ["plan": "premium"])
Joryio.shared.track("Feature Used", properties: ["feature": "export"])
// Avoid: Overly generic events
Joryio.shared.track("Button Tapped") // Too generic
2. ניהול מחזור חיי משתמש
// On login
func handleLogin(userId: String, userInfo: UserInfo) {
Joryio.shared.identify(userId)
Joryio.shared.setAttributes([
"email": userInfo.email,
"name": userInfo.name
])
}
// On logout
func handleLogout() {
Joryio.shared.reset()
}
// On signup
func handleSignup(userId: String, userInfo: UserInfo) {
Joryio.shared.alias(userId)
Joryio.shared.identify(userId)
Joryio.shared.setAttributes(userInfo.attributes)
}
3. ריקון באירועים קריטיים
override func applicationWillTerminate(_ application: UIApplication) {
Joryio.shared.flush()
}
פתרון תקלות
אירועים לא מופיעים
- ודאו שמפתח ה־SDK נכון:
jry_sdk_ios_* - הפעילו יומני ניפוי שגיאות:
enableDebug: true - בדקו שגיאות בקונסול
- ודאו קישוריות רשת
- קראו
flush()לשליחה מיידית
פוש לא עובד
- ודאו שתעודת APNS הועלתה ללוח הבקרה
- בדקו שמזהה המכשיר נרשם
- ודאו entitlements מתאימים ב־Xcode
- בדקו בסביבה הנכונה (dev מול production)
שגיאות בנייה
- ודאו יעד פריסה iOS 14.0+
- נקו תיקיית build: Cmd+Shift+K
- עדכנו תלויות Swift Package
מעקב מסחר אלקטרוני
iOS SDK כולל מעקב מובנה אחר מסחר אלקטרוני לאירועי מוצרים, עגלה, תשלום והזמנות. ראו את מדריך מעקב המסחר האלקטרוני המשותף לכל ה-SDKs לתיעוד המלא עם דוגמאות Swift.
צעדים הבאים
- Android SDK - שילוב Android SDK
- מדריך Push Notifications - למידה על קמפיינים של פוש
- מדריך In-App Campaigns - יצירת הודעות בתוך האפליקציה
- אירועים מותאמים - מעקב אחרי אירועים מותאמים
- E-Commerce API
רישום לפוש והסכמה
ה-SDK נרשם להתראות מרוחקות בין אם המשתמש אישר התראות ובין אם לא. ב-iOS אלו שני דברים נפרדים: הרישום מפיק אסימון מכשיר ללא הסכמה, והאסימון הזה יכול להעביר רק פוש ברקע (שקט) - הוא לא יכול להציג דבר שהמשתמש לא אישר.
זה מה שמאפשר להודעת in-app להגיע גם למשתמש שסירב להתראות, ומשמעו שאסימון כבר קיים אם ידליק התראות בהגדרות בהמשך. Airship ו-OneSignal פועלים באותו אופן. יש לציין זאת במדיניות הפרטיות.