# إعداد Pushwoosh InboxKit لنظام iOS

*متاح منذ iOS SDK [7.0.40](https://github.com/Pushwoosh/pushwoosh-ios-sdk/releases/tag/7.0.40).*

يقدم Pushwoosh InboxKit شاشة صندوق وارد حديثة لـ UIKit فوق الواجهة الخلفية الحالية لصندوق الوارد. تغطي ستة تخطيطات خلايا افتراضية أشكال بطاقات المحتوى الشائعة — من البانرات البسيطة إلى دوارات الصور والفيديو المضمن وبطاقات Apple Wallet — وتتعامل أزرار CTA المضمنة مع التفاعلات الأكثر شيوعًا، والسطح بأكمله مفتوح للتصنيف الفرعي إذا كنت بحاجة إلى مظهر مخصص.

<img src="/setting-up-pushwoosh-inboxkit-ios-feed.webp" alt="موجز InboxKit يعرض بطاقات البانر، والتعليقات، والكلاسيكية، والدائرية، والفيديو، وApple Wallet" width="300" style="display: block; margin: 0 auto;"/>

<p style="text-align: center; opacity: 0.7; font-size: 0.875rem; margin-top: 0.5rem;">موجز InboxKit الافتراضي مع بطاقات البانر، والتعليقات، والكلاسيكية، والدائرية، والفيديو، وApple Wallet.</p>

## متى تستخدم InboxKit

استخدم InboxKit لأي تكامل جديد لنظام iOS. إنه البديل الموصى به لوحدة Objective-C الأقدم [PushwooshInboxUI](/ar/developer/pushwoosh-sdk/ios-sdk/setting-up-pushwoosh-inboxui-ios/).

يمنحك InboxKit:

- ستة أنواع خلايا مدمجة — بانر، مع تعليق، كلاسيكي، دائري، فيديو، و Apple Wallet — يتم تحديدها لكل رسالة عبر `displayType` في الحمولة، أو فرضها من الكود عبر `attributes.forceCellKind`. انظر [أنواع البطاقات](#card-types) للحصول على القائمة الكاملة. (بطاقة Apple Wallet مخصصة لنظام iOS فقط.)
- أزرار CTA مضمنة مع تعداد `PushwooshInboxButtonAction` محدد النوع (`openURL`, `dismiss`, `markRead`, `custom`). يتعامل SDK مع الثلاثة الأولى تلقائيًا؛ يقوم المفوّض الخاص بك بتوجيه `custom` إلى منطقك الخاص.
- دعم التثبيت: الرسائل التي تحتوي على `actionParams["pinned"] == true` تطفو إلى أعلى الموجز وتعرض رمز دبوس.
- السحب للحذف، السحب للتحديث، التحديد التلقائي كمقروء عند الاختفاء — كلها قابلة للتبديل عبر `PushwooshInboxKitAttributes`.
- تخزين دائم: عمليات الحذف وحالة القراءة تظل قائمة بعد إعادة تشغيل العملية حتى لو لم يتم تأكيد استدعاء الشبكة بعد.
- فئة أساسية مفتوحة `PushwooshInboxCell` لتخطيطات مخصصة بالكامل.

عقد الخادم لم يتغير — تعمل نفس الواجهة الخلفية لصندوق الوارد في Pushwoosh، والحمولات، وأدوات لوحة التحكم كما كانت من قبل.

## اختر طريقة التكامل الخاصة بك

- [**إعداد InboxKit باستخدام Swift Package Manager**](/ar/developer/pushwoosh-sdk/ios-sdk/setting-up-pushwoosh-inboxkit-ios/set-up-inboxkit-with-swift-package-manager/) — موصى به للمشاريع الجديدة.
- [**إعداد InboxKit باستخدام CocoaPods**](/ar/developer/pushwoosh-sdk/ios-sdk/setting-up-pushwoosh-inboxkit-ios/set-up-inboxkit-with-cocoapods/) — للمشاريع التي تستخدم CocoaPods بالفعل.

## أنواع البطاقات

يختار InboxKit تخطيط خلية لكل رسالة. يقرأ المحلل الافتراضي `displayType` من حمولة الدفع — ضعه داخل كائن `data`، الذي يقدمه SDK تحت `actionParams`. عندما يكون `displayType` مفقودًا، يلجأ المحلل إلى طريقة استدلالية: صورة + بدون عنوان ← بانر، صورة + عنوان ← مع تعليق، وإلا كلاسيكي. لفرض تخطيط واحد للموجز بأكمله من الكود، قم بتعيين `attributes.forceCellKind`.

كل تخطيط غني يتدهور برشاقة: إذا كانت الحمولة المطلوبة غائبة أو مشوهة، تعود البطاقة إلى `classic` بدلاً من عرض عنصر نائب فارغ (ويتم تسجيل `WARN`).

| `displayType` | التخطيط | حقل الحمولة المطلوب | يتدهور إلى |
|---|---|---|---|
| `banner` | صورة بملء الشاشة، بدون نص | صورة (`inbox_image` أو `data.image`) | `classic` عند عدم وجود صورة |
| `captioned` | صورة في الأعلى، عنوان + نص أدناه | صورة (`inbox_image` أو `data.image`) | `classic` عند عدم وجود صورة |
| `classic` | صورة رمزية أولية ملونة + عنوان + نص | — | — |
| `carousel` | معرض صور متعددة قابل للسحب | `data.carousel` (مصفوفة من الشرائح) | `classic` عند عدم وجود شرائح |
| `video` | ملصق مع شارة تشغيل، مشغل بملء الشاشة عند النقر | `data.video` (`url` + `poster` اختياري) | `classic` عند عدم وجود واصف |
| `wallet` | زر "إضافة إلى Apple Wallet" (iOS فقط) | `data.wallet` (عنوان URL لـ `.pkpass`) | `classic` عند عدم وجود عنوان URL للبطاقة |

{/* TODO(screenshot): replace each placeholder below with a per-card screenshot. Suggested filenames: setting-up-pushwoosh-inboxkit-ios-card-banner.webp, -captioned.webp, -classic.webp, -carousel.webp, -video.webp, -wallet.webp — then swap each <div> for an <img src="/<filename>" width="240" .../>. */}
<div style="display: flex; flex-wrap: wrap; gap: 1rem; justify-content: center; margin: 1.5rem 0;">
  <figure style="width: 200px; margin: 0;"><img src="/setting-up-pushwoosh-inboxkit-ios-card-banner.webp" alt="بطاقة بانر InboxKit" style="width: 100%; border-radius: 12px;"/><figcaption style="text-align: center; font-size: 0.85rem; color: #94a3b8; margin-top: 0.4rem;"><strong>بطاقة بانر</strong></figcaption></figure>
  <figure style="width: 200px; margin: 0;"><img src="/setting-up-pushwoosh-inboxkit-ios-card-captioned.webp" alt="بطاقة مع تعليق من InboxKit" style="width: 100%; border-radius: 12px;"/><figcaption style="text-align: center; font-size: 0.85rem; color: #94a3b8; margin-top: 0.4rem;"><strong>بطاقة مع تعليق</strong></figcaption></figure>
  <figure style="width: 200px; margin: 0;"><img src="/setting-up-pushwoosh-inboxkit-ios-card-classic.webp" alt="بطاقة كلاسيكية من InboxKit" style="width: 100%; border-radius: 12px;"/><figcaption style="text-align: center; font-size: 0.85rem; color: #94a3b8; margin-top: 0.4rem;"><strong>بطاقة كلاسيكية</strong></figcaption></figure>
  <figure style="width: 200px; margin: 0;"><img src="/setting-up-pushwoosh-inboxkit-ios-card-carousel.webp" alt="بطاقة دائرية من InboxKit" style="width: 100%; border-radius: 12px;"/><figcaption style="text-align: center; font-size: 0.85rem; color: #94a3b8; margin-top: 0.4rem;"><strong>بطاقة دائرية</strong></figcaption></figure>
  <figure style="width: 200px; margin: 0;"><img src="/setting-up-pushwoosh-inboxkit-ios-card-video.webp" alt="بطاقة فيديو من InboxKit" style="width: 100%; border-radius: 12px;"/><figcaption style="text-align: center; font-size: 0.85rem; color: #94a3b8; margin-top: 0.4rem;"><strong>بطاقة فيديو</strong></figcaption></figure>
  <figure style="width: 200px; margin: 0;"><img src="/setting-up-pushwoosh-inboxkit-ios-card-wallet.webp" alt="بطاقة Apple Wallet من InboxKit" style="width: 100%; border-radius: 12px;"/><figcaption style="text-align: center; font-size: 0.85rem; color: #94a3b8; margin-top: 0.4rem;"><strong>بطاقة Apple Wallet</strong></figcaption></figure>
</div>

تعتمد بطاقات البانر، والتعليقات، والكلاسيكية على حقول الرسائل القياسية (الصورة، العنوان، النص) بالإضافة إلى مصفوفة `buttons` الاختيارية — انظر [إضافة أزرار CTA مضمنة](#add-inline-cta-buttons). تحمل بطاقات الدوارة والفيديو و Apple Wallet بيانات منظمة إضافية داخل `data`، موثقة أدناه.

### بطاقة دائرية

تعرض البطاقة الدائرية عدة صور من رسالة واحدة — معرض قابل للسحب مع تعليقات اختيارية لكل شريحة ووجهات للنقر. توجد الشرائح في `data.carousel`. تحتاج كل شريحة إلى `image`؛ `title` (تراكب التعليق) و `url` (رابط عميق يتم فتحه عند النقر) اختيارية. يتم إسقاط الشريحة التي لا تحتوي على صورة؛ والنقر على شريحة بدون `url` ينتقل إلى إجراء الصف الافتراضي للرسالة.

```json title="POST https://api.pushwoosh.com/json/1.3/createMessage"
{
  "request": {
    "application": "XXXXX-XXXXX",
    "auth": "API_TOKEN",
    "notifications": [{
      "send_date": "now",
      "ios_title": "New arrivals",
      "content": "Swipe through this week's drops",
      "inbox_days": 7,
      "data": {
        "displayType": "carousel",
        "carousel": [
          { "image": "https://cdn.example.com/inbox/1.jpg", "title": "New in", "url": "myapp://product/1" },
          { "image": "https://cdn.example.com/inbox/2.jpg", "title": "On sale", "url": "myapp://product/2" },
          { "image": "https://cdn.example.com/inbox/3.jpg" }
        ]
      },
      "platforms": [1]
    }]
  }
}
```

### بطاقة فيديو

تعرض بطاقة الفيديو صورة ملصق مع شارة تشغيل؛ يؤدي النقر عليها إلى فتح مشغل بملء الشاشة (مع تشغيل الصوت، حتى مع تفعيل مفتاح الصامت). يوجد الواصف في `data.video`: `url` مطلوب ويجب أن يكون بثًا أو ملفًا `http`/`https`؛ `poster` هي صورة معاينة اختيارية.

```json title="POST https://api.pushwoosh.com/json/1.3/createMessage"
{
  "request": {
    "application": "XXXXX-XXXXX",
    "auth": "API_TOKEN",
    "notifications": [{
      "send_date": "now",
      "ios_title": "Watch the reveal",
      "content": "Tap to play",
      "inbox_days": 7,
      "data": {
        "displayType": "video",
        "video": {
          "url": "https://cdn.example.com/inbox/clip.mp4",
          "poster": "https://cdn.example.com/inbox/poster.jpg"
        }
      },
      "platforms": [1]
    }]
  }
}
```

### بطاقة Apple Wallet

تعرض بطاقة Apple Wallet صورة رئيسية اختيارية وعنوانًا ونصًا فوق زر **إضافة إلى Apple Wallet** الرسمي. يؤدي النقر على الزر إلى تنزيل `.pkpass` وتقديم ورقة إضافة البطاقات النظامية. استخدمها لتقديم القسائم أو بطاقات الولاء أو التذاكر أو بطاقات الصعود إلى الطائرة مباشرة من صندوق الوارد. البطاقة مخصصة لنظام iOS / Mac Catalyst فقط — على المنصات الأخرى، يتم عرض الرسالة كبطاقة كلاسيكية.

يوجد عنوان URL للبطاقة في `data.wallet`، إما كسلسلة نصية مجردة أو ككائن يحتوي على حقل `pass`. تضيف `data.image` الاختيارية الصورة الرئيسية. يخفي الزر نفسه تلقائيًا عندما لا يكون هناك عنوان URL للبطاقة أو عندما لا يتمكن الجهاز من إضافة بطاقات.

```json title="POST https://api.pushwoosh.com/json/1.3/createMessage"
{
  "request": {
    "application": "XXXXX-XXXXX",
    "auth": "API_TOKEN",
    "notifications": [{
      "send_date": "now",
      "ios_title": "Your loyalty card is ready",
      "content": "Add it to Apple Wallet in one tap",
      "inbox_days": 7,
      "data": {
        "displayType": "wallet",
        "image": "https://cdn.example.com/inbox/loyalty.png",
        "wallet": "https://passes.example.com/v1/passes/pass.com.example.loyalty/abc123?token=…"
      },
      "platforms": [1]
    }]
  }
}
```

يتم إبلاغ النتيجة إلى المفوّض الخاص بك:

```swift
extension MyInboxHost: PushwooshInboxKitDelegate {

    func inboxKit(_ vc: PushwooshInboxKitViewController,
                  didAddWalletPassFor message: PWInboxMessageProtocol) {
        // The pass is now in the user's Wallet — show a confirmation if you like.
    }

    func inboxKit(_ vc: PushwooshInboxKitViewController,
                  didFailToAddWalletPassFor message: PWInboxMessageProtocol,
                  error: Error?) {
        // Download failed — surface a retry, log, etc.
    }
}
```

كلا الاستدعاءين اختياريان (يحملان تطبيقات فارغة افتراضية). إلغاء المستخدم لورقة النظام ليس نجاحًا ولا فشلًا، لذلك لا يتم تشغيل أي استدعاء في هذه الحالة.

## إمكانية الوصول

خلايا InboxKit جاهزة لـ VoiceOver بشكل افتراضي. تعرض بطاقات البانر والتعليقات والكلاسيكية عنوانها ونصها وتاريخها من خلال التسميات الأساسية، وتقرأ أزرار CTA المضمنة عناوينها الخاصة. تضيف البطاقات الغنية دلالات صريحة:

- **فيديو** — يتم عرض الملصق كعنصر زر واحد يسمى "تشغيل الفيديو" (السمات `.button` + `.startsMediaSession`)، لذلك يعلن VoiceOver عنه كعنصر تحكم في الوسائط بدلاً من صورة عادية.
- **دوارة** — كل شريحة هي عنصر زر يكون تسمية إمكانية الوصول الخاصة به هي تعليق الشريحة، أو "شريحة" عندما لا يكون لها تعليق. يعلن مؤشر الصفحة عن الموضع الحالي كـ "*n* من *الإجمالي*".
- **Apple Wallet** — زر **إضافة إلى Apple Wallet** هو زر `PKAddPassButton` القياسي من Apple، والذي يحمل تسمية VoiceOver مترجمة خاصة به.

لاختبار واجهة المستخدم والأتمتة، يتم تعيين معرفي `accessibilityIdentifier` ثابتين: `inboxkit.video.play` على ملصق الفيديو و `inboxkit.wallet.add` على زر Wallet.

## قراءة البيانات المخصصة من رسالة

لجعل إشعار الدفع يظهر في صندوق الوارد، يجب أن يتضمن طلب `createMessage` في [Messages API](/ar/developer/api-reference/messages-api/) `inbox_image` أو `inbox_date` أو `inbox_days` — بدون أحد هذه الحقول، يتم تسليم الدفع كإشعار عادي ولا يصل أبدًا إلى موجز صندوق الوارد. توضع البيانات المخصصة الحرة تحت مفتاح `data`، الذي يسلمه SDK إلى العميل كمعلمة `u`:

```json title="POST https://api.pushwoosh.com/json/1.3/createMessage"
{
  "request": {
    "application": "XXXXX-XXXXX",
    "auth": "API_TOKEN",
    "notifications": [{
      "send_date": "now",
      "ios_title": "Summer sale",
      "content": "30% off everything — limited time only",
      "inbox_image": "https://cdn.example.com/inbox/summer.png",
      "inbox_days": 7,
      "data": {
        "displayType": "captioned",
        "promo_id": "SUMMER2026",
        "screen": "promo_details"
      },
      "platforms": [1]
    }]
  }
}
```

يعرض SDK هذا الكائن على رسالة صندوق الوارد من خلال `actionParams`. اقرأه من المفوّض عندما ينقر المستخدم على الصف أو على زر CTA مضمن:

```swift
extension MyInboxHost: PushwooshInboxKitDelegate {

    func inboxKit(_ vc: PushwooshInboxKitViewController,
                  didSelect message: PWInboxMessageProtocol) -> Bool {
        guard let params = message.actionParams as? [String: Any] else { return true }

        // The custom `data` object arrives under the "u" key —
        // either as a nested dictionary or as a JSON-encoded string,
        // depending on how the payload was built upstream.
        let custom: [String: Any]? = {
            if let dict = params["u"] as? [String: Any] { return dict }
            if let raw = params["u"] as? String,
               let bytes = raw.data(using: .utf8),
               let parsed = try? JSONSerialization.jsonObject(with: bytes) as? [String: Any] {
                return parsed
            }
            return nil
        }()

        if let promoId = custom?["promo_id"] as? String {
            navigateToPromo(promoId)
            return false   // we handled the tap; SDK should not run the default action
        }
        return true
    }
}
```

يعمل نفس البحث `actionParams["u"]` داخل `inboxKit(_:didTapButton:onMessage:)` لأزرار CTA المضمنة. بالنسبة لحالات CTA المحددة النوع (`openURL`, `dismiss`, `markRead`)، يقوم SDK بالفعل بتنفيذ الإجراء الافتراضي — أرجع `true` للحفاظ على هذا السلوك، أو `false` لقمعه وتشغيل منطقك الخاص.

## إضافة أزرار CTA مضمنة

يمكن أن تحمل الرسالة ما يصل إلى ثلاثة أزرار دعوة إلى اتخاذ إجراء (CTA) مضمنة. توجد الأزرار جنبًا إلى جنب مع البيانات المخصصة الأخرى داخل `data` كمصفوفة `buttons`. يقوم SDK بعرضها تلقائيًا داخل الخلايا ذات التعليقات والكلاسيكية:

```json title="POST https://api.pushwoosh.com/json/1.3/createMessage"
{
  "request": {
    "application": "XXXXX-XXXXX",
    "auth": "API_TOKEN",
    "notifications": [{
      "send_date": "now",
      "ios_title": "New promo card",
      "content": "Tap a button to claim or save",
      "inbox_image": "https://cdn.example.com/inbox/promo.png",
      "inbox_days": 7,
      "data": {
        "displayType": "captioned",
        "promo_id": "SUMMER2026",
        "buttons": [
          { "title": "Claim", "url": "https://example.com/promo/SUMMER2026" },
          { "title": "Read",  "action": "markRead" },
          { "title": "Save",  "action": "custom", "tag": "save_promo" }
        ]
      },
      "platforms": [1]
    }]
  }
}
```

يحتوي كل كائن زر على هذه الحقول:

| الحقل | النوع | متى |
|---|---|---|
| `title` | string | مطلوب. تسمية الزر المرئية. |
| `url` | string | ينتج عنوان URL غير فارغ وقابل للتحليل إجراء `openURL`. يفتحه SDK عبر `UIApplication.shared.open` ما لم يقم المفوّض الخاص بك بقمعه. |
| `action` | string | رمز إجراء صريح: `dismiss` (يزيل الرسالة من الموجز)، `markRead` (يحدد الرسالة كمقروءة)، أو `custom` (يتم التعامل معه من قبل المضيف). غير حساس لحالة الأحرف. |
| أي شيء آخر | any | عندما يكون `action` هو `custom`، يتم إعادة توجيه كل مفتاح على كائن الزر باستثناء `title` و `action` إلى المفوّض الخاص بك كحمولة مخصصة — اتفق على مفتاح مع المسوق (مثل `tag`) وقم بالتوجيه بناءً عليه. |

أولوية التحليل: رمز `action` الصريح أولاً، ثم `url` إذا كان غير فارغ، وإلا فإن الزر يقع في `custom` حاملاً الحمولة الكاملة (ناقص `title` و `action`).

اعترض النقرات من المفوّض الخاص بك. خاصية `button.action` هي تعداد `PushwooshInboxButtonAction` المحدد النوع:

```swift
extension MyInboxHost: PushwooshInboxKitDelegate {

    func inboxKit(_ vc: PushwooshInboxKitViewController,
                  didTapButton button: PushwooshInboxButton,
                  onMessage message: PWInboxMessageProtocol) -> Bool {
        switch button.action {
        case .openURL(let url):
            // Default behavior is fine — let SDK open the URL.
            return true

        case .dismiss, .markRead:
            // SDK handles both. Return false if you want to override.
            return true

        case .custom(let payload):
            // Marketer-defined custom button. Dispatch on a key you agreed on.
            if let tag = payload["tag"] as? String {
                switch tag {
                case "save_promo":
                    saveCurrentPromoLocally(message: message)
                default:
                    break
                }
            }
            return true   // ignored for custom — SDK never runs a default action here
        }
    }
}
```