# iOS Push-Stories

Push-Stories verwandeln eine erweiterte Push-Benachrichtigung in ein bildschirmfüllendes Story-Erlebnis im Instagram-Stil: randabfallende Bilder, Fortschrittsbalken oben, automatisch fortschreitende Seiten, Tippen zum Navigieren und eine Schaltfläche mit einem Deep Link. Sie werden von einer Notification Content Extension unter Verwendung des eigenständigen `PushwooshNotificationUI`-Moduls gerendert – Sie erstellen eine Unterklasse eines View Controllers und das SDK kümmert sich um das Parsen, das Laden von Bildern, den Fortschritt, das Timing, die Navigation und die Deep Links.

Verfügbar seit Version 7.0.46.

<img src="/ios-push-stories-demo.webp" alt="Push-Stories, die in einer erweiterten Benachrichtigung abgespielt werden"/>

## 1. Fügen Sie eine Notification Content Extension hinzu

Wählen Sie in Xcode **File > New > Target…**, wählen Sie **Notification Content Extension** aus und benennen Sie sie (zum Beispiel **StoriesContentExtension**).

<img src="/ios-push-stories-1.webp" alt="Hinzufügen eines Notification Content Extension-Ziels in Xcode"/>

## 2. Fügen Sie das PushwooshNotificationUI-Modul hinzu

`PushwooshNotificationUI` ist ein eigenständiges Modul ohne weitere Pushwoosh-Abhängigkeiten, sodass es innerhalb des Erweiterungsprozesses klein bleibt. Fügen Sie es zum **Ziel der Content Extension** hinzu (nicht zum Ziel der App).

**Swift Package Manager**

<img src="/ios-push-stories-spm.webp" alt="Hinzufügen des PushwooshNotificationUI-Pakets zum Erweiterungsziel"/>

**CocoaPods**

```ruby
target 'StoriesContentExtension' do
  use_frameworks!

  pod 'PushwooshXCFramework/PushwooshNotificationUI'
end
```

## 3. Erstellen Sie eine Unterklasse des Stories View Controllers

Ersetzen Sie den generierten `NotificationViewController`-Body durch eine Unterklasse von `PushwooshStoriesViewController`. Das ist die gesamte Integration.

<Tabs>
<TabItem label="Swift">
```swift
import PushwooshNotificationUI

class NotificationViewController: PushwooshStoriesViewController {}
```
</TabItem>
</Tabs>

## 4. Konfigurieren Sie die Info.plist der Erweiterung

Setzen Sie in der **Info.plist** der Content Extension die folgenden Schlüssel unter `NSExtension > NSExtensionAttributes`:

```xml
<key>UNNotificationExtensionCategory</key>
<string>PW_STORIES</string>
<key>UNNotificationExtensionUserInteractionEnabled</key>
<true/>
<key>UNNotificationExtensionDefaultContentHidden</key>
<true/>
<key>UNNotificationExtensionInitialContentSizeRatio</key>
<real>1.5</real>
```

<Aside type="note">
`UNNotificationExtensionCategory` muss mit der Kategorie übereinstimmen, die Sie in der Push-Benachrichtigung senden (`PW_STORIES`). `UNNotificationExtensionInitialContentSizeRatio` legt das anfängliche Höhenverhältnis der erweiterten Ansicht fest.
</Aside>

## 5. Senden Sie eine Push-Stories-Benachrichtigung

Senden Sie eine Benachrichtigung, deren Kategorie `PW_STORIES` ist und deren benutzerdefinierte Daten einen `pw_stories`-Block enthalten. Verwenden Sie das dedizierte `ios_category_custom`-Feld für die Kategorie und das `data`-Feld für die Stories-Payload.

```json
{
  "request": {
    "application": "APPLICATION_CODE",
    "auth": "API_ACCESS_TOKEN",
    "notifications": [
      {
        "send_date": "now",
        "content": "Tap to explore",
        "ios_title": "Push Stories",
        "ios_category_custom": "PW_STORIES",
        "ios_root_params": {
          "aps": {
            "mutable-content": 1
          }
        },
        "data": {
          "pw_stories": {
            "pages": [
              {
                "image": "https://example.com/story-1.jpg",
                "duration": 5.0,
                "link": "yourapp://page1",
                "button_title": "Get started",
                "title": "Welcome",
                "subtitle": "Swipe to explore what's new"
              },
              {
                "image": "https://example.com/story-2.jpg",
                "duration": 4.0,
                "link": "yourapp://page2",
                "button_title": "Learn more",
                "title": "Stay in the loop",
                "subtitle": "Updates, tips and more"
              }
            ]
          }
        }
      }
    ]
  }
}
```

Jede Seite unterstützt die folgenden Felder. Nur `image` ist erforderlich; der Rest ist optional.

| Feld | Beschreibung |
|-------|-------------|
| `image` | URL des bildschirmfüllenden Bildes für die Seite. |
| `duration` | Sekunden, die die Seite auf dem Bildschirm bleibt, bevor sie automatisch weitergeht. Standardmäßig etwa 5 Sekunden. |
| `link` | Deep Link, der geöffnet wird, wenn die Schaltfläche der Seite angetippt wird. |
| `button_title` | Titel der Schaltfläche der Seite. |
| `title` | Titeltext, der über die Seite gelegt wird. |
| `subtitle` | Untertiteltext, der über die Seite gelegt wird. |

<Aside type="note">
Ein fehlender, leerer oder fehlerhafter `pw_stories`-Block führt nicht zum Absturz der Erweiterung – sie greift auf den Standard-Benachrichtigungsinhalt zurück. `mutable-content: 1` ist nur für den optionalen Medien-Pre-Cache-Pfad erforderlich, der unten beschrieben wird.
</Aside>

## Anpassung

Überschreiben Sie Eigenschaften in Ihrer Unterklasse, um das Erlebnis anzupassen. Alle haben sinnvolle Standardwerte.

<Tabs>
<TabItem label="Swift">
```swift
import PushwooshNotificationUI

class NotificationViewController: PushwooshStoriesViewController {
    override var storyAspectRatio: CGFloat { 1.5 }     // keep in sync with InitialContentSizeRatio
    override var hapticsEnabled: Bool { true }
    override var longPressToPauseEnabled: Bool { true }
    override var crossfadesBetweenPages: Bool { true }
    override var loopsAfterLastPage: Bool { false }
}
```
</TabItem>
</Tabs>

| Eigenschaft | Standard | Beschreibung |
|----------|---------|-------------|
| `storyAspectRatio` | `1.5` | Seitenverhältnis (Höhe ÷ Breite) des Stories-Bereichs. Halten Sie es synchron mit `UNNotificationExtensionInitialContentSizeRatio`. |
| `hapticsEnabled` | `false` | Spielt ein leichtes taktiles Tippen bei der Navigation in der Tippzone ab. |
| `longPressToPauseEnabled` | `false` | Drücken und Halten pausiert die aktuelle Seite; Loslassen setzt fort. |
| `crossfadesBetweenPages` | `false` | Überblendet zwischen den Seiten anstatt hart zu schneiden. Fällt auf einen sofortigen Wechsel zurück, wenn „Bewegung reduzieren“ aktiviert ist. |
| `loopsAfterLastPage` | `false` | Startet nach dem Ende der letzten Seite wieder von der ersten Seite. |
| `appGroupIdentifier` | `nil` | App Group, die mit einer Notification Service Extension für den Medien-Pre-Cache geteilt wird (siehe unten). |

Sie können auch `showDefaultContent(for:)` überschreiben, um den Fallback anzupassen, der angezeigt wird, wenn die Payload fehlt oder fehlerhaft ist (standardmäßig wird der Alert-Body angezeigt).

### Medien-Pre-Cache

Für einen sofortigen, offline verfügbaren ersten Frame teilen Sie eine App Group zwischen Ihrer Content Extension und einer Notification Service Extension. Überschreiben Sie `appGroupIdentifier` auf dem Stories-Controller und laden Sie dann die Medien aus der `didReceive(_:withContentHandler:)` Ihrer Service Extension vor:

<Tabs>
<TabItem label="Swift">
```swift
import PushwooshNotificationUI

PushwooshStoriesMediaPrefetcher.prefetch(
    userInfo: request.content.userInfo,
    appGroupIdentifier: "group.com.example.app"
) {
    contentHandler(bestAttemptContent)
}
```
</TabItem>
</Tabs>

Aktivieren Sie die **App Groups**-Fähigkeit auf beiden Erweiterungen mit derselben Gruppenkennung und senden Sie `mutable-content: 1`, damit die Service Extension ausgeführt wird. Ohne eine App Group werden Medien stattdessen im `tmp`-Verzeichnis der Erweiterung zwischengespeichert.

### Lebenszyklus- und Analyse-Callbacks

Setzen Sie `storiesDelegate`, um Story-Ereignisse zu beobachten – Seitenimpressionen, Schaltflächen-Tipps, Abschluss und Fallback. Konform zu `PushwooshStoriesDelegate`; jede Methode ist optional, implementieren Sie also nur die, die Sie benötigen.

<Tabs>
<TabItem label="Swift">
```swift
import PushwooshNotificationUI

class NotificationViewController: PushwooshStoriesViewController, PushwooshStoriesDelegate {
    override func viewDidLoad() {
        super.viewDidLoad()
        storiesDelegate = self
    }

    func storiesViewController(_ controller: PushwooshStoriesViewController, didStartWithPageCount pageCount: Int) {}
    func storiesViewController(_ controller: PushwooshStoriesViewController, didShow page: StoryPage, at index: Int) {}
    func storiesViewController(_ controller: PushwooshStoriesViewController, didTapActionFor page: StoryPage, at index: Int) {}
    func storiesViewControllerDidFinish(_ controller: PushwooshStoriesViewController) {}
    func storiesViewControllerDidShowFallback(_ controller: PushwooshStoriesViewController) {}
}
```
</TabItem>
</Tabs>

| Callback | Wann er ausgelöst wird |
|----------|----------------|
| `didStartWithPageCount:` | Eine gültige Stories-Payload wurde geparst und die Wiedergabe beginnt in Kürze. |
| `didShow:at:` | Eine Seite wurde sichtbar. Verwenden Sie dies für Impressionen pro Seite. |
| `didTapActionFor:at:` | Der Benutzer hat auf die Call-to-Action-Schaltfläche getippt. |
| `storiesViewControllerDidFinish:` | Die letzte Seite wurde zu Ende abgespielt. |
| `storiesViewControllerDidShowFallback:` | Die Payload fehlte oder war fehlerhaft und der Fallback-Inhalt wurde angezeigt. |

Die an die Callbacks übergebene `StoryPage` legt die `imageURL`, `duration`, `link`, `buttonTitle`, `title` und `subtitle` der Seite offen.

### Navigation

Tippen Sie auf das rechte Drittel des Bildschirms, um zur nächsten Seite zu gelangen, und auf das linke Drittel, um zurückzugehen. Horizontales Wischen wird absichtlich nicht verwendet, da es mit der Systemgeste zum Schließen von Benachrichtigungen in Konflikt steht. Das Tippen auf die Seitenschaltfläche öffnet den Deep Link und schließt die Benachrichtigung.

## Referenzen

<CardGrid>
  <LinkCard
    title="iOS SDK API-Referenz"
    description="Vollständige technische Dokumentation, die alle öffentlichen Klassen, Methoden und Eigenschaften abdeckt."
    href="https://pushwoosh.github.io/pushwoosh-ios-sdk/"
  />
  <LinkCard
    title="Messages API"
    description="Senden Sie Benachrichtigungen, einschließlich der Felder data und ios_category_custom, über die Pushwoosh API."
    href="/developer/api-reference/messages-api/"
  />
</CardGrid>