דלג לתוכן הראשי

שילוב Unity SDK

ה-SDK ל-Unity עוטף את ה-SDK הנייטיב של Joryio ל-iOS ול-Android. הוא לא מוסיף לוגיקת מעקב משלו: כל קריאה מועברת ל-SDK הנייטיב, ולכן ההתנהגות, האצווה והטיפול במצב לא מקוון זהים לשילוב נייטיב.

דרישות​

  • Unity 2022.3 LTS ומעלה. Unity 2021.3 ו-2022.1 בונים ל-Android עם JDK 8 ואינם יכולים לקמפל מול ה-SDK הנייטיבי של Android (bytecode של Java 11).
  • iOS 14.0+ / Android 6.0+ (API 23)
  • Android: Target API Level 34 ומעלה (ב-Player Settings). ספריות AndroidX של ה-SDK הנייטיבי דורשות compileSdk 34, ובניית ה-Android של Unity נכשלת בערך נמוך יותר.
  • Android: External Dependency Manager for Unity (EDM4U). ב-iOS אין בו צורך (ראו התקנה).
  • iOS: Xcode 15 ומעלה, עם גישת רשת ל-github.com בבנייה הראשונה של הפרויקט המיוצא - Xcode מוריד את ה-SDK הנייטיבי כחבילת Swift.
  • לפוש ב-Android: Firebase Cloud Messaging במשחק - חבילת ה-Messaging של Firebase Unity SDK וקובץ google-services.json שלכם. ה-SDK הנייטיב של Android מקומפל מול firebase-messaging אבל לא מביא אותו; בלעדיו האנליטיקה וההודעות בתוך המשחק עובדות והפוש כבוי.
תמיכה בפלטפורמות

iOS ו-Android בלבד. בפלטפורמות אחרות - העורך, מחשב שולחני, קונסולות - כל המתודות קיימות ולא עושות דבר, וה-SDK רושם אזהרה אחת לכל הפעלה. קוד המשחק שלכם לעולם לא צריך #if UNITY_IOS || UNITY_ANDROID סביב קריאה ל-Joryio.

התקנה​

הוסיפו את החבילה (com.joryio.sdk) מ-Git: ב-Package Manager בחרו + → Add package from git URL והזינו את הכתובת עם תג הגרסה:

https://github.com/joryio/joryio-unity-sdk.git#1.3.3

כדי לעדכן, שנו את התג לגרסה החדשה. תמיד נעלו תג: בלעדיו ה-Package Manager עוקב אחרי ה-commit האחרון.

לחלופין, הורידו את joryio-unity-<version>.unitypackage מ עמוד ה-releases ב-GitHub וייבאו אותו דרך Assets → Import Package → Custom Package. ה-SDK מותקן תחת Assets/JoryioSDK; כדי לעדכן, מחקו את התיקייה וייבאו את הגרסה החדשה.

השתמשו בדרך התקנה אחת בלבד, לא בשתיהן. החבילה מ-Git URL והתיקייה Assets/JoryioSDK מכילות את אותן assemblies (Joryio.Runtime, Joryio.Editor) ואת אותם GUID של assets, ולכן פרויקט עם שתיהן לא מתקמפל. כדי לעבור, הסירו קודם את זו שמותקנת: מחקו את Assets/JoryioSDK, או הסירו את חבילת Joryio ב-Package Manager. ה-.unitypackage מביא גם את תנאי הרישיון (LICENSE), את README.md ואת CHANGELOG.md אל Assets/JoryioSDK.

החבילה אינה ב-OpenUPM, שמפרסם חבילות קוד פתוח בלבד; ה-SDK של Joryio מופצים ברישיון מסחרי.

החבילה לא כוללת קבצים בינאריים נייטיביים מוכנים, כך שספריות משותפות מתיישבות עם מה שכבר קיים בפרויקט שלכם. היא מביאה את ה-SDK הנייטיביים ללא ממשק משתמש - מעקב, זהות ופוש, אבל לא תצוגות ההודעות הנייטיביות (ראו הודעות בתוך המשחק):

  • Android: תנו ל-EDM4U לפתור את io.joryio:joryio-android דרך Gradle, מקובץ הצהרת התלויות של החבילה (Editor/JoryioDependencies.xml).
  • iOS: אין מה לפתור ב-Unity. כשאתם בונים ל-iOS, החבילה מוסיפה את ה-SDK הנייטיבי לפרויקט ה-Xcode המיוצא כחבילת Swift (https://github.com/joryio/joryio-ios-sdk, בדיוק בגרסה של החבילה) ומקשרת את ה-product Joryio ל-UnityFramework. Xcode מוריד ופותר אותה בפעם הראשונה שהוא פותח או בונה את הפרויקט. לא CocoaPods ולא EDM4U מעורבים ב-iOS.
שדרוג מגרסה שהשתמשה ב-CocoaPods

גרסאות קודמות קיבלו את ה-SDK של iOS מ-CocoaPods (ה-pod Joryio/Core), ו-CocoaPods trunk מפסיק לקבל גרסאות חדשות ב-2 בדצמבר 2026. אחרי השדרוג, בנו את נגן ה-iOS פעם אחת עם Replace ולא Append, כדי שהפרויקט המיוצא לא ישא עוד את ה-pod הישן, ומחקו כל שורת <iosPod name="Joryio/..."> מקובצי התלויות של EDM4U שלכם.

התחלה מהירה​

לכל פלטפורמה מפתח SDK משלה (jry_sdk_ios_..., jry_sdk_android_...), מ-Settings → Apps בדשבורד. השתמשו בכתובת ה-API שמוצגת עבור סביבת העבודה שלכם.

using Joryio.Runtime;

void Awake()
{
#if UNITY_IOS
const string sdkKey = "jry_sdk_ios_YOUR_IOS_KEY";
#else
const string sdkKey = "jry_sdk_android_YOUR_ANDROID_KEY";
#endif
JoryioSDK.Initialize(sdkKey, "api-eu1.joryio.com");
}

ואז עקבו אחרי מה שהשחקנים עושים:

JoryioSDK.Track("level_complete", new Dictionary<string, object> {
{ "level", 7 },
{ "duration_seconds", 143.5 },
});

JoryioSDK.Identify("player-1234");
JoryioSDK.SetAttribute("favourite_class", "mage");
JoryioSDK.IncrementAttribute("games_played");

// Another identifier for the same player - works before Identify too
JoryioSDK.AddAlias("steam_id", "76561198000000000");

ערכים שומרים על הטיפוס שלהם במעבר: { "level", 7 } נשמר כמספר 7, לא כ-"7". פילוחים משווים מספרית, ולכן מספר שנשמר כמחרוזת לא יתאים לאף אחד. כל טיפוס מספרי נשמר, מילונים ורשימות מקוננים נשארים אובייקטים ומערכים, DateTime נשלח כ-ISO-8601 ב-UTC, ו-NaN או אינסוף הופכים ל-null.

כערך של תכונה (SetAttribute, SetAttributes, IncrementAttribute), NaN או אינסוף נדחים עם אזהרה והתכונה נשארת ללא שינוי - היא לא נמחקת. ההמרה ל-null חלה רק בתוך מאפייני אירוע.

אף קריאה לא זורקת חריגה לתוך המשחק: קריאה לפני Initialize, ארגומנט חובה שהוא null או כשל נייטיבי נרשמים ביומן ומתעלמים מהם.

החריגה היא הסכמה: OptOut(), OptIn() ו-UpdateConsent() פועלות גם לפני Initialize. ההחלטה מוחלת כשה-SDK מתחיל, לפני שנשלח משהו. גם IsUserOptedOut() עונה לפני Initialize - ראו הסכמה. גם WipeData() ו-GetTrackingConsent() פועלות לפני Initialize, וכך גם SetSdkAuthenticationToken ו-SetSdkAuthenticationErrorHandler - ראו אימות SDK. כך גם שינויי מנוי ו-UnregisterPush(): SetSubscription, AddToSubscriptionGroup, RemoveFromSubscriptionGroup ו-UnregisterPush() שנקראו לפני Initialize נשמרים במכשיר, גם אחרי הפעלה מחדש, ונשלחים כשה-SDK מתחיל. שינוי חדש יותר של אותו ערוץ או אותה קבוצה מחליף שינוי ישן יותר, כך שנשלחת הבחירה האחרונה של השחקן. ביטול (ביטול הרשמה, יציאה מקבוצה, UnregisterPush()) נשלח תמיד: OptOut(), הסכמה Denied או WipeData() לפני Initialize מוחקים הרשמות שנשמרו, לעולם לא ביטולים, וביטול שנעשה לפני WipeData() עדיין נשלח בשם השחקן שביקש אותו, ולא בשם הזהות החדשה שהמחיקה יוצרת. RegisterPushToken לפני Initialize לא נשלח, והוא לא מבטל UnregisterPush() שנשמר. שינוי מנוי לכיוון הרשמה (subscribe, opt-in או הצטרפות לקבוצה) שנעשה לפני Initialize בזמן שהשחקן ביטל הסכמה למעקב (opt-out) או שההסכמה היא Denied (שמורה מהפעלה קודמת או נקבעה בהפעלה הזו) אינו נשמר כלל: הוא נזרק עם אזהרה, ו-OptIn() או Granted מאוחרים יותר לא שולחים אותו.

ערך null לתכונה אינו שגיאה: SetAttribute("clan", null) מוחק את התכונה, בדיוק כמו UnsetAttribute("clan"), ב-iOS וב-Android כאחד.

הגדרות​

כל הגדרה אופציונלית ומשקפת את האפשרות בעלת אותו השם ב-SDK הנייטיבי. משכי זמן הם בשניות והשם אומר זאת, כי ה-SDK הנייטיביים אינם מסכימים (iOS מקבל שניות, Android מילישניות) - הגשרים ממירים.

JoryioSDK.Initialize(sdkKey, "api-eu1.joryio.com", new JoryioConfig {
Debug = UnityEngine.Debug.isDebugBuild,
FlushIntervalSeconds = 10,
AnonymousId = existingDeviceId, // only if you already have one
});
מאפייןסוגברירת מחדלתיאור
Debugboolfalseיומן נייטיב מפורט. רמת הלוג הנייטיבית עוקבת אחריו: debug כאשר true, שגיאות בלבד אחרת.
BatchSizeint50אירועים לאצווה לפני שליחה אוטומטית.
FlushIntervalSecondsdouble5שניות בין שליחות אוטומטיות.
SendImmediatelyboolfalseשליחת כל אירוע מיד עם המעקב, ללא אצווה (לא מומלץ לייצור).
MaxQueueSizeint1000מספר האירועים המרבי שהתור המקומי מחזיק בזמן שהשליחות נכשלות; מעבר לו הישנים ביותר נזרקים.
RequestTimeoutSecondsdouble10פסק זמן HTTP לכל בקשה.
SessionTimeoutSecondsdouble1800שניות של חוסר פעילות לפני שסשן חדש מתחיל.
TrackSessionStartbooltrueשליחת אירוע תחילת סשן באופן אוטומטי.
AnonymousIdstringנוצר אוטומטיתמזהה אנונימי משלכם למכשיר זה. משמש להרצה זו ונשמר רק כאשר עדיין לא נשמר דבר, כך שמזהה שהוגדר פעם אחת שורד גם אם המשחק יפסיק להעביר את האפשרות.
RequestPushPermissionAtLaunchboolfalseבקשת הרשאת פוש במהלך Initialize. ראו בהמשך מדוע ברירת המחדל כבויה.
MaxRetriesint3מספר הניסיונות החוזרים לכל בקשה לפני שהיא ממתינה לשליחה הבאה.
RetryBackoffMsint1000השהיית הבסיס בין ניסיונות חוזרים, במילישניות (השם של ה-SDK הנייטיביים).
TrackingConsentTrackingConsent?null (לא הוגדר)התשובה של מסך ההסכמה שלכם בזמן ההפעלה. כשלא הוגדר נשמרת התשובה שה-SDK זוכר (Denied שמור נשאר דחייה), ובלי תשובה שמורה המעקב רגיל. Granted עוקב ומחליף דחייה שמורה; Pending לא שולח דבר עד UpdateConsent(TrackingConsent.Granted), להפעלה הנוכחית בלבד; Denied לא אוסף דבר ונזכר. ראו הסכמה.
OptOutboolfalseהתחלה במצב opt-out. ה-opt-out נשמר, ולכן false לא מבטל OptOut() קודם.
NotificationChannelNamestringnull ("Notifications")Android בלבד. שם ערוץ ההתראות בהגדרות ההתראות של המערכת (Android 8 ומעלה); העבירו אותו מתורגם. ל-iOS אין ערוצים והוא מתעלם ממנו.
EnableSdkAuthenticationboolfalseשליחת טוקן חתום עם כל בקשה. ראו אימות SDK.
SdkAuthenticationTokenstringאיןהטוקן להתחלה, אם כבר יש לכם אחד.
AllowHtmlJsInAppMessagesboolfalseמאפשר לממשק הנייטיבי לצייר הודעות HTML. סקריפטים שבהן לא רצים; הגשר של ה-SDK הנייטיבי מטפל בכפתורים ובטפסים. אין לזה השפעה על הודעות שה-handler שלכם מצייר.
HoldInAppMessagesForHandlerboolfalseהחל מגרסה 1.3.2. המשחק שלכם מצייר הודעות In-App עם handler משלו: הודעות שמוכנות לפני הקריאה ל-SetInAppMessageHandler מוחזקות. ראו לפני שה-handler מוגדר.

Debug מגדיר גם את רמת הלוג הנייטיבית וגם את מצב ה-debug הנייטיבי ב-iOS וב-Android, וזה גם מאפשר את קריאות הבדיקה ResetDisplayedCampaigns ו-EvaluateInAppCampaigns.

התראות פוש​

אם למשחק שלכם עדיין אין מנגנון פוש, ה-SDK רושם את המכשיר בעצמו - ב-iOS תמיד, וב-Android כש-Firebase Cloud Messaging נמצא במשחק (ראו דרישות). בלי Firebase ב-Android אין טוקן FCM, ולכן שום דבר לא נרשם והפוש כבוי; האנליטיקה וההודעות בתוך המשחק לא מושפעות. בקשו את ההרשאה כשלשחקן יש סיבה להסכים - בקשה בשניות הראשונות של המשחק היא זו שנדחית הכי הרבה, וסירוב הרבה יותר קשה לשינוי מאשר עיכוב.

JoryioSDK.RequestPushPermission(granted => {
Debug.Log(granted ? "Push enabled" : "Player declined");
});

ה-callback רץ ב-main thread, פעם אחת בדיוק. ב-iOS ה-SDK מקבל אחר כך את טוקן ה-APNs בעצמו - אין app delegate לערוך.

ב-Android הבקשה מוצגת מה-activity של המשחק: GameActivity (ברירת המחדל ב-Unity 6) או UnityPlayerActivity הקלאסי (גרסאות קודמות, או פרויקט Unity 6 שבחר בו). בשניהם התשובה של השחקן מדווחת אחרי שחלון המערכת נסגר. מתחת ל-Android 13 אין בקשת הרשאה בזמן ריצה, וה-callback עונה מיד אם ההתראות מופעלות.

אם אתם מעדיפים שה-SDK יבקש כבר ב-Initialize - גרסת soft-launch, משחק שבו ההתראות הן העיקר, או פשוט כדי לראות את הזרימה - אפשר להפעיל את זה בהגדרות:

JoryioSDK.Initialize(sdkKey, "api-eu1.joryio.com", new JoryioConfig {
RequestPushPermissionAtLaunch = true,
});

זה כבוי כברירת מחדל, לא חסר. הבקשה היא חד-פעמית בשתי הפלטפורמות, ולכן ברירת המחדל מגינה על המקרה הנפוץ; האפשרות קיימת למי שהחליט אחרת.

אם אתם כבר משתמשים ב-Firebase או ב-Unity Mobile Notifications​

רוב המשחקים כן, ושני מנגנוני פוש שמתחרים על אותו טוקן הם הדרך הרגילה שבה שילוב נשבר. השאירו את המנגנון הקיים ומסרו לנו את הטוקן - טוקן המכשיר של APNs ב-iOS, וטוקן הרישום של FCM ב-Android. ב-Android הטוקן הזה מגיע מ-Firebase Messaging (או מעטיפת FCM משלכם): Unity Mobile Notifications לא מפיק טוקן FCM ב-Android - שם הוא מתזמן התראות מקומיות - ולכן הוא יכול לספק את הטוקן ב-iOS בלבד:

JoryioSDK.RegisterPushToken(tokenFromYourExistingStack);

הוסיפו callback כדי לדעת מה קרה - isRetryable פירושו תקלה זמנית (אופליין, DNS, 5xx) שכדאי לרשום שוב אחריה, isUnauthorized פירושו שמפתח ה-SDK שגוי:

JoryioSDK.RegisterPushToken(tokenFromYourExistingStack, result =>
{
if (!result.success && result.isRetryable)
{
// תקלה זמנית (אופליין, DNS, 5xx): רשמו שוב כשהרשת חוזרת
}
});

ב-Android ה-SDK לא נוגע אז בשירות ה-FCM. ב-iOS שני דברים עדיין קורים, כדי שלא תופתעו:

  • Initialize הופך את ה-SDK ל-delegate של UNUserNotificationCenter ומעביר כל callback ל-delegate שהיה מותקן לפניו. אתחלו את Joryio אחרי שתוסף הפוש האחר שלכם הגדיר את ה-delegate שלו (Unity Mobile Notifications עושה זאת בהפעלה, כך ש-Awake מתאים). תוסף שמגדיר את ה-delegate מאוחר יותר מחליף את זה של Joryio, ואז מעקב הפתיחות תלוי בקריאה שלכם ל-TrackPushClick.
  • ה-SDK מקבל את טוקן ה-APNs מה-app controller של Unity בעצמו. העברת אותו טוקן גם ל-RegisterPushToken לא מזיקה - אלא אם השחקן כיבה פוש, כי RegisterPushToken מבטל UnregisterPush() (ראו בהמשך).

RegisterPushToken מפעיל מחדש פוש למכשיר הזה: הוא מבטל UnregisterPush() קודם. OnTokenReceived של Firebase נקרא מעצמו בכל הפעלה, לכן אם אתם מעבירים אותו, דלגו עליו כל עוד השחקן כיבה פוש בהגדרות שלכם, וקראו ל-RegisterPushToken כשהוא מפעיל אותו מחדש. אחרת רענון טוקן, ולא השחקן, מבטל את הבחירה שלו.

UpdateBadgeCount(count) ו-ClearBadge() מגדירים את התג על אייקון האפליקציה ב-iOS. ב-Android הן לא עושות דבר, כי אין שם API לתגים.

הודעות בתוך המשחק​

קובץ התלויות מושך את ה-SDK הנייטיביים ללא ממשק משתמש, ולכן כברירת מחדל אף אחד לא מצייר הודעות בתוך המשחק: עד שתתקינו handler או תוסיפו את הממשק הנייטיבי, קמפיינים מגיעים למכשיר ולא מוצגים. בחרו אחת משתי הדרכים.

לצייר אותן בתוך המנוע עם העיצוב שלכם (מומלץ למשחקים) - התקינו handler:

JoryioSDK.SetInAppMessageHandler(message => {
// Title / Body / ImageUrl / Buttons are the authored content.
// Name is the campaign's dashboard label - never copy to show a player.
myCanvas.Show(message.Title, message.Body, message.ImageUrl);
foreach (var button in message.Buttons)
myCanvas.AddButton(button.Text, () => {
message.MarkClicked(button);
if (button.Action == "url" || button.Action == "deep_link")
Application.OpenURL(button.Url);
else if (button.Action == "request_push_permission")
JoryioSDK.RequestPushPermission(granted => { });
myCanvas.Hide();
message.MarkDismissed(); // the tap closed the message
});
message.MarkDisplayed();
}, canRenderHtml: false);
מעכשיו הדיווח באחריותכם

התקנת handler עוצרת את הציור של ה-SDK, ומאותו רגע הוא לא יכול לראות מה קרה. קראו ל-MarkDisplayed() כשההודעה מוצגת, ול-MarkClicked(button) / MarkDismissed() כשהשחקן פועל. MarkDisplayed() מדווח פעם אחת לכל הודעה גם אם נקרא כמה פעמים, ו-MarkClicked(button) שולח את ה-Id של הכפתור כ-elementId של ההקשה, כדי שמפת ההקשות תבחין בין הכפתורים. כפתור בלי מזהה לא שולח elementId - ולעולם לא את התווית שלו, שהיא טקסט תצוגה.

הקשה שסוגרת את ההודעה מדווחת כשתיהן: MarkClicked(button) ואחריו MarkDismissed(), כפי שה-SDK הנייטיב שולח כשהוא מצייר את ההודעה בעצמו.

הודעה שלא דווחה נחשבת כאילו לא הוצגה מעולם: אין תצוגה בדוחות, לשיוך ההכנסות אין תצוגה להתחבר אליה, ומגבלות התדירות לא סופרות אותה. הקמפיין ייקרא אז כאילו לא הגיע לאיש, וזה נראה כמו בעיית פילוח ולא כמו בעיית שילוב. ה-SDK רושם אזהרה אם הודעה נמחקת בלי שדווחה.

כדי להחזיר את הרינדור ל-SDK הנייטיב - כשהרנדרר שלכם נהרס, או בסצנה שאין בה ממשק לצייר עליו - הסירו את ה-handler:

JoryioSDK.ClearInAppMessageHandler(); // זהה ל-SetInAppMessageHandler(null)

ה-SDK הנייטיב מצייר אז שוב את ההודעות בתצוגות שלו (אם רכיבי ה-UI שלו כלולים ב-build - ראו בהמשך) ומדווח לשרת על היכולות שלו. זהו clearInAppMessageCallback של ה-SDK הנייטיב.

מרכיב שאולי אינו הרנדרר היחיד - רנדרר לכל סצנה, למשל - יעביר את ה-handler שהתקין. הוא מוסר רק אם הוא עדיין זה שמותקן, ולעולם לא handler שסקריפט אחר התקין אחריו:

void OnDestroy() => JoryioSDK.ClearInAppMessageHandler(myHandler);

canRenderHtml מתאר את הרנדרר שלכם. קנבס רגיל של Unity לא יכול להציג תוכן HTML, שדורש web view. זה לא עניין קוסמטי: קמפיינים מכוונים לפי מה שהמכשיר מדווח, ויכולת שלא הוצהרה נקראת כ"לא יכול", לא כ"לא ידוע". הצהרה על HTML שאתם לא יכולים לצייר תשלח קמפיינים לשחקנים שלעולם לא יראו אותם; השמטת HTML שאתם כן יכולים לצייר תמנע מכם קמפיינים שיכולתם להציג.

אם ה-web view שלכם מציג הודעת HTML עם <form data-jry-form>, שלחו אותו דרך ה-SDK כדי שהערכים ייכתבו לפרופיל של השחקן. ערכים הם מחרוזות, או רשימת מחרוזות לקבוצת תיבות סימון:

JoryioSDK.SubmitInAppForm(message.Id, new Dictionary<string, object> {
{ "email", "[email protected]" },
{ "topics", new List<string> { "events", "offers" } },
}, result => {
if (!result.ok) Debug.Log(result.message);
});

ה-callback תמיד רץ פעם אחת; כל כשל, כולל היעדר רשת, הוא ok = false.

לפני שה-handler מוגדר​

החל מגרסה 1.3.2. אפשר לקרוא ל-SetInAppMessageHandler רק אחרי Initialize, והסנכרון הראשון יכול להסתיים לפני כן - ואז ה-SDK הנייטיב, אם ממשק המשתמש שלו כלול ב-build, מצייר את ההודעה הזו בעצמו. הגדירו HoldInAppMessagesForHandler = true ב-JoryioConfig כדי למנוע זאת: הודעה שמוכנה לפני שה-handler הוגדר מוחזקת - לא מצוירת, לא מדווחת, ולא נספרת במגבלת התדירות - ומועברת ל-handler שלכם ברגע שתגדירו אותו. ClearInAppMessageHandler() במקום זאת מסיים את ההחזקה, וה-SDK הנייטיב מצייר את ההודעה המוחזקת.

או לתת ל-SDK הנייטיב לצייר אותן​

כדי שה-SDK הנייטיב יצייר את ההודעות בעצמו - שכבה בסגנון מערכת ההפעלה מעל המשחק, עם דיווח אוטומטי וללא קוד - הוסיפו את רכיבי הממשק שלו.

Android: הוסיפו את רכיב הממשק בקובץ תלויות EDM4U משלכם, למשל Assets/Editor/JoryioUIDependencies.xml, באותה גרסה כמו של החבילה:

<dependencies>
<androidPackages>
<androidPackage spec="io.joryio:joryio-android-ui:1.3.3" />
</androidPackages>
</dependencies>

iOS: הוסיפו את סמל ההגדרה (scripting define symbol) JORYIO_IOS_UI תחת Player Settings → iOS → Other Settings → Scripting Define Symbols. בניית ה-iOS תקשר אז גם את ה-product JoryioUI של חבילת ה-Swift, בגרסה של החבילה.

handler שהותקן עם SetInAppMessageHandler עדיין גובר על התצוגות הנייטיביות.

מה ההודעה נושאת​

ה-handler מקבל את כל מה שהשרת שלח, תחת אותם שמות שדות שבהם משתמש ה-SDK של React Native (בעבר הוא קיבל רק Id ו-Name, מה שהשאיר למשחק רק את התווית הפנימית של הקמפיין להצגה):

שדהקיים כאשרמשמעות
Id, Name, Typeתמידמזהה הקמפיין, שם הדשבורד (לא טקסט להצגה), ו-modal / banner / slideup / fullscreen / custom.
Kindתמידnative או html - איזו קבוצה מהשדות שלמטה מלאה. IsNative / IsHtml הם קיצורים.
Title, Body, ImageUrlnativeהטקסט והתמונה שנכתבו בקמפיין. Title ו-ImageUrl עשויים להיות null; Body תמיד קיים.
Buttonsnativeלעולם לא null. לכל כפתור Id, Text, Action (dismiss, url, deep_link, request_push_permission) ו-Url לשתי פעולות הקישור. ה-SDK אינו יכול לפעול על כפתור שאתם מציירים: פתחו את ה-URL בעצמכם, קראו ל-RequestPushPermission לפעולת ה-primer, ודווחו על ההקשה עם MarkClicked(button).
CloseButton, BackdropDismissiblenativeהאם המחבר ביקש כפתור סגירה והאם הקשה מחוץ להודעה סוגרת אותה.
Stylenativeדריסות העיצוב של המחבר, או null כאשר הקמפיין לא הגדיר כאלה. BackgroundColor, TextColor, PrimaryButtonColor, PrimaryButtonTextColor (מחרוזות צבע CSS), CornerRadius, FontSize (גודל גוף הטקסט - גזרו ממנו את הכותרת), TitleWeight (regular / medium / semibold / bold), TextAlign (auto / start / center / end - auto עוקב אחר שפת ההודעה עצמה, לא אחר אזור המכשיר) ו-FontFamily. כל שדה אופציונלי, ושדה חסר פירושו לרשת את העיצוב של המשחק שלכם; החילו את הקיימים ואל תחליפו ברירות מחדל משלכם לשאר. FontFamily הוא מאמץ מיטבי - החילו אותו רק אם המשחק מצרף את הגופן. אותם שדות ואותם שמות כמו message.style ב-SDK של React Native.
Html, Csshtmlהמסמך. קנבס uGUI אינו יכול לרנדר אותו - הכריזו canRenderHtml: true רק אם המשחק מארח web view.

מה Unity אינו יכול לרנדר, בפירוש. תוכן HTML דורש web view שהמנוע אינו מספק. תמונות הן כתובות URL: הורידו אותן בעצמכם (UnityWebRequestTexture). דריסות העיצוב של המחבר מגיעות ב-Style, אבל דבר אינו מחיל אותן עבורכם - לקנבס אין ערכת נושא לרשת ממנה, ולכן הרנדרר שלכם קורא את השדות שהוא תומך בהם ושומר על העיצוב שלו לשאר.

להתחיל מהדוגמה​

החבילה כוללת דוגמת In-app renderer: סצנה, פריפאב שניתן לעצב מחדש, והסקריפט שמחבר ביניהם. ייבאו אותה מ-Package Manager (בוחרים את חבילת Joryio, פותחים Samples ולוחצים Import), הזינו את מפתחות ה-SDK שלכם באובייקט JoryioInApp שב-JoryioInAppSample.unity, ובנו למכשיר iOS או Android. הודעות מגיעות רק ב-build למכשיר: ב-Editor ה-SDK לא עושה דבר, ולכן לחיצה על Play לא מציגה כלום.

התקנתם מ-.unitypackage? אין רשומה ב-Package Manager לייבא ממנה: הורידו את joryio-unity-<version>-sample.unitypackage מאותו עמוד releases וייבאו אותו באותה דרך. הוא מותקן ב-Assets/Samples/Joryio/<version>/In-app renderer, המקום שבו Package Manager שם את הדוגמה, וגם הוא דורש com.unity.ugui.

הפריפאב הוא מה שהופכים לשלכם - מחליפים צבעים, גופן, ומכניסים גרפיקה משלכם. הרנדרר מצייר את מה שנמצא בשדה Panel Prefab שלו (JoryioInAppPanel.prefab בסצנת הדוגמה) וממלא ארבעה ילדים שהוא מוצא לפי שם: Title ו-Body (Text), PrimaryButton (מועתק פעם אחת לכל כפתור בקמפיין) ו-SecondaryButton (כפתור הסגירה). דריסות העיצוב של המחבר מוחלות מעליו היכן שהקמפיין הגדיר אותן. כשהשדה ריק, הרנדרר בונה פאנל פשוט בקוד.

כפתורי uGUI צריכים EventSystem, ולכן הרנדרר יוצר אחד כשאין כזה בסצנה - עם מודול ה-UI של ה-Input System כשזו שיטת הקלט הפעילה שלכם, ואחרת עם המודול הישן. הסקריפט מדגים את הדבר האחד שחייבים לשמור: דיווח על מחזור החיים של כל הודעה. הדוגמה דורשת את com.unity.ugui, שה-SDK עצמו לא דורש.

מתי הודעה נחשבת כמוצגת​

הודעה נחשבת כמוצגת - כלומר in_app.displayed מדווח ומגבלת התדירות של הקמפיין נצרכת - רק כשהאדם יכול היה לראות אותה. משך הזמן שעליה להיות על המסך הוא IN_APP_DISPLAY_DWELL_MS (1000 מילישניות), אותו קבוע בכל ה-SDK של Joryio.

  • הודעות שה-SDK הנייטיב מצייר פועלות לפי הכלל של iOS ו-Android: מוצגות רק במסך יציב שבחזית, ונספרות אחרי שנייה אחת על המסך כשהאפליקציה בחזית. הקשה או סגירה לפני כן נחשבות כצפייה; הודעה שיורדת מהמסך מוקדם יותר לא נספרת ונשארת זכאית.
  • אם המשחק מצייר את ההודעות בעצמו, קראו ל-MarkDisplayed() לפי אותו כלל - JoryioSDK.IN_APP_DISPLAY_DWELL_MS: אחרי שההודעה הייתה גלויה כל הזמן הזה, או ממש לפני MarkClicked / MarkDismissed כשהשחקן פועל מוקדם יותר. אל תקראו לו עבור הודעה שירדה מהמסך קודם (החלפת סצנה, השהיית האפליקציה).

שני מתגים בלתי תלויים, זהים בכל ה-SDK של Joryio:

  • הסכמה למעקב - התשובה של מסך ההסכמה שלכם: Granted, Denied או Pending.
  • Opt-out - הבחירה של השחקן עצמו: OptOut() / OptIn().

נתונים נאספים רק כשההסכמה ניתנה וגם השחקן לא ביצע opt-out; כל "לא" גובר. אף מתג לא משנה את השני: UpdateConsent(TrackingConsent.Granted) לא מבטל opt-out (רק OptIn() מבטל), ו-OptOut() לא משנה את ההסכמה.

אם השחקן עוד לא ענה על מסך ההסכמה שלכם, התחילו עם הסכמה Pending: שום דבר לא נשלח - אין תחילת סשן, אין סנכרון in-app ואין רישום פוש - והאירועים ממתינים במכשיר (עד MaxQueueSize) עד שהתשובה מגיעה.

JoryioSDK.Initialize(sdkKey, apiHost, new JoryioConfig {
TrackingConsent = TrackingConsent.Pending,
});
// כשהשחקן עונה
JoryioSDK.UpdateConsent(TrackingConsent.Granted); // שולח את מה שהמתין בתור
// או
JoryioSDK.UpdateConsent(TrackingConsent.Denied); // מוחק אותו ולא אוסף דבר
  • Denied נזכר בין הפעלות. הוא מוסר רק על ידי UpdateConsent(TrackingConsent.Granted) או על ידי TrackingConsent = TrackingConsent.Granted מפורש בהגדרות.
  • השארת JoryioConfig.TrackingConsent ללא הגדרה (ברירת המחדל) שומרת דחייה שמורה; בלי תשובה שמורה ה-SDK עוקב כרגיל, כך שמשחק בלי מסך הסכמה לא צריך שום הגדרה.
  • Pending חל על ההפעלה הנוכחית בלבד ולעולם לא גובר על Denied שמור.
  • ערך מחוץ ל-enum (המרה שגויה) נדחה עם אזהרה; בהגדרות הוא נחשב כלא מוגדר.
JoryioSDK.OptOut(); // הפסקת איסוף; אירועים בתור וכתיבות מאפיינים ממתינות נמחקים
JoryioSDK.OptIn(); // חידוש
JoryioSDK.WipeData(); // מחיקת כל מה שנשמר במכשיר (ה-opt-out נשמר)

ה-opt-out נשמר ושורד הפעלה מחדש. כדי להתחיל סשן כבר במצב opt-out, הגדירו JoryioConfig.OptOut = true.

IsUserOptedOut() משקף את מתג ה-opt-out בלבד, לא את ההסכמה. GetTrackingConsent() מחזיר את מתג ההסכמה: Granted, Denied או Pending. שניהם עונים גם לפני Initialize, לפי מה שנשמר במכשיר: opt-out או Denied שנשמרו בהפעלה קודמת מדווחים ככאלה, לא כערך ברירת מחדל, ו-OptOut() / OptIn() שנקראו קודם באותה הפעלה גוברים על ה-opt-out השמור. GetTrackingConsent() מחזיר Granted רק כשלא הוחלט דבר, וכך גם ה-SDK מתנהג במצב הזה.

כשה-opt-out משתנה, ה-SDK רושם אותו בפרופיל השחקן כמאפיין הבוליאני tracking_opted_out, כדי שסגמנטים יוכלו להחריג שחקנים שביצעו opt-out. GetTrackingConsent() מדווח את ההסכמה שה-SDK המקורי שומר, כולל Denied שנשמר מהפעלה קודמת.

אימות SDK​

הטוקן מוכיח מי המשתמש המזוהה: בקשות שמציינות מזהה משתמש חייבות לשאת טוקן תקף. למבקרים אנונימיים אין טוקן להציג, ולכן הבקשות שלהם מתקבלות בלעדיו, אלא אם סביבת העבודה הפעילה גם זהות מחמירה (strict identity).

בסביבות עבודה שבהן אימות SDK מופעל, כל בקשה נושאת טוקן שהשרת שלכם חותם:

JoryioSDK.Initialize(sdkKey, apiHost, new JoryioConfig { EnableSdkAuthentication = true });
JoryioSDK.SetSdkAuthenticationToken(tokenFromYourServer); // אחרי התחברות

JoryioSDK.SetSdkAuthenticationErrorHandler(error => {
// error.reason: missing, invalid, expired, sub_mismatch או unknown
// error.refreshExhausted: ה-SDK הפסיק לנסות שוב את הבקשה הזו
FetchFreshToken(token => JoryioSDK.SetSdkAuthenticationToken(token));
});

SetSdkAuthenticationToken ו-SetSdkAuthenticationErrorHandler עובדים גם לפני Initialize. ה-SDK הנייטיב מתחיל עם הטוקן הזה, לפני הבקשה הראשונה שלו, ושומר אותו גם אחרי WipeData() שנקרא לפני Initialize, כך שביטול מנוי שנשמר עבור השחקן שנמחק עדיין נשלח עם הטוקן שלו. אפשר גם להעביר את הטוקן הראשון ב-JoryioConfig.SdkAuthenticationToken.

ה-handler רץ ב-main thread. הוא נקרא על 401 של אימות בבקשות של השחקן הנוכחי, כולל לפני שהוגדר טוקן כלשהו (reason missing) - לעולם לא על שאריות של שחקן שהתנתק, כך שהוא לא יגרום לכם לנתק את השחקן שמשחק עכשיו. אירועים וכתיבות של השחקן הנוכחי שנדחו נשמרים ונשלחים שוב אחרי שתגדירו טוקן חדש. אירועים נשלחים באצוות של שחקן אחד בכל פעם, ואירועים וכתיבות של שחקן שהתנתק נמחקים בכל 401 של אימות (missing, invalid, expired או sub_mismatch), כדי שלא יעכבו את אלה של השחקן הבא. Reset() מוציא את הטוקן מהסשן; הוא נשאר עם השאריות של השחקן היוצא, והבקשות של השחקן הבא לעולם לא נושאות אותו - הגדירו את הטוקן שלו עם SetSdkAuthenticationToken כשהוא מתחבר. אירועים וכתיבות שהשחקן היוצא כבר הכניס לתור עדיין נשלחים עם הטוקן שלו, כך שהתנתקות רגילה לא מאבדת דבר.

מסחר אלקטרוני​

לאירועי מסחר השתמשו ב-tracker המוקלד ולא ב-Track. הוא משתמש בשמות האירועים הקנוניים שדוחות ההכנסות והייחוס קוראים; שם שהוקלד ידנית, כמו "Added to Cart", נשמר כאירוע אחר ולא מתאים לאף דוח.

var item = new EcommerceCartItem { ProductId = "gems_500", Name = "500 gems", Price = 4.99, Quantity = 1 };
JoryioSDK.Ecommerce.AddToCart(item, cartValue: 4.99);
JoryioSDK.Ecommerce.Purchase(new EcommerceOrder {
OrderId = "order-1042",
Items = new List<EcommerceCartItem> { item },
Value = 4.99,
Currency = "USD",
});

ל-tracker יש אותם עשרים אירועים כמו ב-SDK של React Native, מ-ViewProduct ועד SubmitReview.

קריאה שחסר בה ארגומנט נדרש כותבת אזהרה ולא שולחת דבר - לעולם לא אירוע ריק או אירוע בסכום אפס: מוצר בלי ProductId, הזמנה בלי OrderId או Items, מזהה הזמנה או קופון ריק, קריאת עגלה בלי רשימת הפריטים. סכום שהוא NaN או אינסופי (Value, Price, cartValue, Shipping, Tax, Discount, refundAmount, discountAmount) נדחה באותו אופן, במקום להירשם כ-0.

זהות וסשן​

שלחו מספרי טלפון עם + וקידומת מדינה

שלחו תמיד מספרי טלפון בפורמט בינלאומי מלא (E.164): +, קידומת המדינה ואז המספר - למשל +972501234567. מספר בלי + נקרא לפי מדינת ברירת המחדל למספרי טלפון של סביבת העבודה; אם לא הוגדרה, או שאפשר לקרוא את הספרות בשתי דרכים, הוא נשמר כלא מאומת ולא משמש להודעות. איך מטופלים מספרי טלפון

var (userId, anonymousId) = JoryioSDK.GetIdentity(); // userId הוא null לשחקן אנונימי
var (sessionId, sessionStart, duration) = JoryioSDK.GetSessionInfo(); // duration בשניות

Reset() מנתק את השחקן: מזהה אנונימי חדש, ללא מזהה משתמש.

מדריך API​

כל חבר ציבורי של JoryioSDK, עם שמות הפרמטרים שלו. כולם סטטיים ובטוחים לקריאה לפני Initialize (הם רושמים ביומן ולא עושים דבר, ו-callback עדיין עונה פעם אחת).

חריגה: קריאות ההסכמה, WipeData() וקריאות אימות ה-SDK כן נכנסות לתוקף לפני Initialize, וקריאות המנוי ו-UnregisterPush() נשמרות עד אליה.

מתודהמה היא עושה
Initialize(sdkKey, apiHost, config = null)מפעיל את ה-SDK. קריאה שנייה נדחית. מפתח של פלטפורמה אחרת (מפתחות iOS מתחילים ב-jry_sdk_ios_, מפתחות Android ב-jry_sdk_android_) נדחה עם שגיאה וה-SDK נשאר כבוי. ראו הגדרות.
IsInitializedהאם Initialize הצליח. true גם בהפעלת iOS לפני פתיחת הנעילה הראשונה של המכשיר: ה-SDK המקורי מסיים לעלות בפתיחת הנעילה, וקריאות בינתיים ממתינות לו. אם קריאות שנשמרו בהפעלה קודמת עדיין במכשיר, iOS משאיר גם אותן בלתי קריאות עד אותה פתיחת נעילה: ה-SDK לא אוסף דבר וכל קריאה ממתינה עד שאפשר לקרוא אותן, ואז הן נשלחות ראשונות, והקריאות שנעשו מאז אחריהן, לפי הסדר. בינתיים GetTrackingConsent() ו-IsUserOptedOut() מחזירות את ההחלטות שההפעלה הזו קיבלה (קריאות ו-JoryioConfig). בלי החלטה כזו, GetTrackingConsent() מחזירה Pending, כי עדיין אי אפשר לקרוא את ההסכמה השמורה, ו-IsUserOptedOut() עונה מה-SDK המקומי, שאולי לא יראה עד פתיחת הנעילה ביטול הסכמה (opt-out) ששמרה הפעלה קודמת. כשהקריאות נשלחות, ההסכמה שמוחלת היא זו שההפעלה הזו קבעה במפורש (UpdateConsent לפני Initialize, אחרת JoryioConfig.TrackingConsent); בלעדיה חלה ההחלטה השמורה במכשיר, שנקראת אחרי פתיחת הנעילה, כמו ב-SDK המקוריים: Denied שמור נשאר Denied, ואם לא נשמרה החלטה ה-SDK ב-Granted, כמו בכל הפעלה אחרת. פונקציות קריאה אחרות, כמו GetUserId(), GetAnonymousId() ו-GetSessionId(), עדיין עונות מה-SDK המקומי ואינן משקפות עדיין את הקריאות הממתינות.
SdkVersionגרסת ה-SDK, "1.3.3".
Track(eventName, properties = null)מעקב אחר אירוע.
TrackScreen(screenName, properties = null)מעקב אחר צפייה במסך.
Flush()שולח עכשיו את האירועים שבתור.
Identify(userId)מזהה את השחקן. כשהשחקן ביטל את המעקב (opt-out) או שההסכמה היא Denied, המזהה נשמר רק במכשיר (שום דבר לא נשלח), כדי שביטול הרשמה שנעשה בינתיים יגיע לשחקן הנכון.
Reset()ניתוק: מזהה אנונימי חדש, ללא מזהה משתמש.
AddAlias(label, name)מצמיד מזהה נוסף, למשל ("steam_id", "7656..."). עובד גם לשחקן אנונימי.
GetUserId() / GetAnonymousId()המזהים הנוכחיים ("" כשאין).
GetIdentity()(userId, anonymousId); userId הוא null לשחקן אנונימי.
SetAttributes(attributes)מגדיר כמה תכונות. ערך null מוחק את אותה תכונה.
SetAttribute(key, value)מגדיר תכונה אחת ושומר על הטיפוס שלה. ערך null מוחק אותה; NaN או אינסוף נדחים.
IncrementAttribute(key, by = 1)מוסיף לתכונה מספרית. NaN או אינסוף נדחים.
UnsetAttribute(key)מוחק תכונה.
UpdateConsent(consent)TrackingConsent.Granted, Denied או Pending. לעולם לא משנה את ה-opt-out. פועל גם לפני Initialize. ראו הסכמה.
OptOut() / OptIn()עוצר או מחדש את האיסוף. נשמר בין הפעלות; לעולם לא משנה את ההסכמה. פועלים גם לפני Initialize.
IsUserOptedOut()האם השחקן ביצע opt-out (מתג ה-opt-out בלבד). עונה גם לפני Initialize, לפי ההחלטה השמורה.
GetTrackingConsent()ההסכמה הנוכחית: TrackingConsent.Granted, Denied או Pending (מתג ההסכמה בלבד). עונה גם לפני Initialize, לפי ההחלטה השמורה.
WipeData()מוחק את כל מה שנשמר במכשיר. ה-opt-out נשמר.
SetSubscription(channel, status)מנוי שיווקי לערוץ אחד. ראו מנוי שיווקי.
AddToSubscriptionGroup(groupId, channel) / RemoveFromSubscriptionGroup(groupId, channel)הצטרפות לקבוצת מנויים או יציאה ממנה.
RequestPushPermission(onResult)מבקש הרשאת התראות; onResult(bool granted).
GetPushPermissionStatus(onResult)"granted", "denied", "notDetermined" או "unavailable".
IsPushEnabled(onResult)האם ההתראות מופעלות; onResult(bool).
RegisterPushToken(token, onResult = null)מוסר טוקן ממערך הפוש שלכם; onResult(PushRegistrationResult).
ReportPushDelivered(trackingId)מדווח שפוש הגיע (במערך פוש משלכם).
TrackPushClick(trackingId)מדווח שהתראה נפתחה.
UnregisterPush()מפסיק פוש למכשיר הזה. נשאר כבוי גם אחרי הפעלה מחדש, עד RegisterPushToken (אחרי Initialize) או בקשת הרשאה. נשמר אם נקרא לפני Initialize.
GetDeviceToken()טוקן הפוש שה-SDK מחזיק ("" כשאין).
UpdateBadgeCount(count) / ClearBadge()תג סמל האפליקציה ב-iOS; לא עושה דבר ב-Android.
GetSessionId()מזהה הסשן הנוכחי.
GetSessionInfo()(sessionId, sessionStart, duration); duration בשניות.
SetSdkAuthenticationToken(token)מוסר טוקן חתום. ראו אימות SDK.
SetSdkAuthenticationErrorHandler(handler)מודיע כשבקשה נדחית תחת אימות SDK; null מפסיק להאזין.
Ecommerceמעקב המסחר האלקטרוני המוקלד. ראו מסחר אלקטרוני.
SetInAppMessageHandler(handler, canRenderHtml = false)מרנדרים הודעות in-app בעצמכם. null מחזיר את הרינדור ל-SDK הנייטיב.
ClearInAppMessageHandler()מחזיר את רינדור הודעות ה-in-app ל-SDK הנייטיב.
ClearInAppMessageHandler(handler)אותו דבר, רק אם handler הוא עדיין זה שמותקן. handler שהוא null מנקה את מה שמותקן.
TrackInAppImpression(campaignId, action, elementId = null)מדווח displayed, clicked או dismissed; elementId הוא מזהה הכפתור שנלחץ. עדיף להשתמש במתודות Mark... של ההודעה.
SubmitInAppForm(campaignId, values, onResult)שולח טופס מהודעת HTML שאתם מרנדרים; onResult(InAppFormSubmitResponse).
SyncInAppCampaigns()מושך עכשיו את הקמפיינים העדכניים.
EvaluateInAppCampaigns() / ResetDisplayedCampaigns()כלי בדיקה; נדחים אלא אם Debug מופעל.

בהודעה שה-handler שלכם מקבל: MarkDisplayed(), MarkClicked(button = null) ו-MarkDismissed().

בנייה​

ה-SDK מגדיר את הפרויקטים המיוצאים אוטומטית. אין מה לעשות ידנית:

  • Xcode: ה-SDK הנייטיבי כחבילת Swift (joryio-ios-sdk, מקושר ל-UnityFramework), גרסת Swift, הטמעת סביבת הריצה של Swift, יכולת הפוש, מצב הרקע להתראות מרוחקות, ו-UNITY_USES_REMOTE_NOTIFICATIONS ב-Classes/Preprocessor.h - בלעדיו Unity מסיר מהקוד את ה-callback שמוסר את טוקן ה-APNs. יכולת הפוש נוספת לקובץ ה-entitlements הקיים של ה-target כשיש כזה, כך שה-entitlements של תוספים אחרים נשמרים.
  • Android: כללי keep עבור גשר ה-Java מתווספים ל-proguard-unity.txt שנוצר, כך שבנייה ממוזערת לשחרור שומרת את המתודות ש-C# קורא בשמן.

אם התראות לא מגיעות בבנייה שאחרת עובדת, בדקו שיכולת Push Notifications קיימת ב-target: זו ההגדרה היחידה שהיעדרה לא מייצר שגיאה בשום מקום.

מנוי שיווקי​

האם מותר לשלוח הודעות לאדם הזה בערוץ מסוים. זו הסכמה שונה מ-OptOut(), שעניינה האם מותר לעקוב אחריו - אחת אינה גוררת את השנייה, ואפשר לסרב למעקב ועדיין לרצות את הניוזלטר.

זה מה שמרכז העדפות בתוך האפליקציה כותב.

JoryioSDK.SetSubscription("email", "unsubscribed");
JoryioSDK.AddToSubscriptionGroup("list_abc123", "email");

ערוצים: email, sms, whatsapp, push, viber. סטטוסים: optedIn, subscribed, unsubscribed.

נדרש מזהה - הקריאה מדולגת, עם שגיאה ביומן, אם אין מזהה משתמש ואין מזהה אנונימי.