Skip to main content

Configuration

You can configure the StorifyMe SDK to suit your specific needs by customizing various options and settings.

SDK Lifecycle Management

The StorifyMe SDK provides comprehensive lifecycle management APIs that give you full control over SDK initialization, state monitoring, and resource cleanup.

SDK State

The SDK exposes its current state through the state property:

// Check current SDK state
let currentState = StorifyMeInstance.shared.state

switch currentState {
case .inactive:
print("SDK is not initialized or has been shut down")
case .initializing:
print("SDK is currently initializing")
case .ready:
print("SDK is ready to use")
case .failed(let error):
print("SDK initialization failed: \(error.message)")
}

// Quick check if SDK is ready
if StorifyMeInstance.shared.isReady {
// Safe to use SDK features
}

Initialization with Completion Handler

Use the completion handler to be notified when initialization completes:

StorifyMeInstance.shared.initialize(
accountId: "YOUR_ACCOUNT_ID",
apiKey: "YOUR_API_KEY",
env: .EU
) { result in
switch result {
case .success:
print("SDK initialized successfully")
// Safe to use SDK features
case .failure(let error):
print("Initialization failed: \(error.message)")
// Handle error appropriately
}
}
Thread Safety

The completion handler is always called on the main thread, making it safe to update UI directly.

Initialization Delegate

For apps that prefer the delegate pattern, implement StorifyMeInitializationDelegate:

class AppDelegate: UIResponder, UIApplicationDelegate, StorifyMeInitializationDelegate {

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
StorifyMeInstance.shared.initializationDelegate = self
StorifyMeInstance.shared.initialize(accountId: "ID", apiKey: "KEY", env: .EU)
return true
}

// MARK: - StorifyMeInitializationDelegate

func storifyMeDidBecomeReady() {
print("SDK is ready")
// Perform actions that require SDK to be ready
}

func storifyMeDidFailToInitialize(error: StorifyMeError) {
print("SDK failed: \(error.message)")
// Handle initialization failure
}
}

whenReady Method

The whenReady method provides a convenient way to execute code when the SDK becomes ready:

StorifyMeInstance.shared.whenReady { result in
switch result {
case .success:
// SDK is ready, safe to proceed
self.loadWidgets()
case .failure(let error):
// SDK failed to initialize
self.showError(error)
}
}

Behavior:

  • If SDK is already ready: handler called immediately with .success
  • If SDK has already failed: handler called immediately with .failure(error)
  • If SDK is initializing or inactive: handler called when initialization completes
Use Case

whenReady is particularly useful in view controllers or SwiftUI views where you want to ensure the SDK is ready before performing operations, without needing to track the initialization state yourself.

Notification-Based Observation

For reactive architectures or when you need to observe SDK state changes from multiple locations:

// Observe SDK becoming ready
NotificationCenter.default.addObserver(
self,
selector: #selector(handleSDKReady),
name: .storifyMeSDKDidBecomeReady,
object: nil
)

// Observe SDK initialization failure
NotificationCenter.default.addObserver(
self,
selector: #selector(handleSDKFailed(_:)),
name: .storifyMeSDKDidFailToInitialize,
object: nil
)

// Observe SDK shutdown
NotificationCenter.default.addObserver(
self,
selector: #selector(handleSDKShutdown),
name: .storifyMeSDKDidShutdown,
object: nil
)

@objc private func handleSDKReady() {
print("SDK is ready")
}

@objc private func handleSDKFailed(_ notification: Notification) {
if let error = notification.userInfo?[StorifyMeSDKErrorKey] as? StorifyMeError {
print("SDK failed: \(error.message)")
}
}

@objc private func handleSDKShutdown() {
print("SDK has been shut down")
}

Widget Auto-Wait

Widgets automatically wait for SDK initialization before loading. This eliminates race conditions and simplifies your code:

// This is safe to call immediately - widget will wait for SDK if needed
let widget = StorifyMeWidget()
widget.setWidgetId(widgetId: 123)
widget.eventDelegate = self
widget.load() // Automatically waits for SDK to be ready

Behavior:

  • If SDK is ready: widget loads immediately
  • If SDK is initializing: widget waits, then loads when SDK becomes ready
  • If SDK fails to initialize: widget's onFail delegate method is called with the error

shutdown Method

The shutdown method properly releases all SDK resources and resets the SDK to its initial state:

StorifyMeInstance.shared.shutdown {
print("SDK resources have been released")
// SDK is now in inactive state
}

What shutdown does:

  • Resets SDK state to .inactive
  • Clears all WebView resources
  • Removes all notification observers
  • Clears URL caches
  • Releases account credentials
When to Use shutdown
  • Account switching: Call shutdown() before initializing with different credentials
  • Memory pressure: Release resources when your app receives memory warnings
  • User logout: Clean up SDK state when user logs out of your app
  • Testing: Reset SDK state between test cases

Example: Account Switching

func switchAccount(to newAccountId: String, apiKey: String) {
StorifyMeInstance.shared.shutdown { [weak self] in
// Re-initialize with new credentials
StorifyMeInstance.shared.initialize(
accountId: newAccountId,
apiKey: apiKey,
env: .EU
) { result in
switch result {
case .success:
self?.reloadAllWidgets()
case .failure(let error):
self?.showError(error)
}
}
}
}

Choosing the Right Approach

Use CaseRecommended Approach
Just loading widgetsNo extra code needed - widgets auto-wait
Custom UI before widgets load (loading spinner, etc.)whenReady method
Non-widget operations need SDK readyCompletion handler
AppDelegate-based architectureStorifyMeInitializationDelegate
Multiple components need to knowNotifications
Account switching / logoutshutdown() then re-initialize
Most Apps Don't Need Extra Code

Since widgets automatically wait for SDK initialization, most apps can simply call widget.load() without any additional lifecycle handling. The other approaches are only needed for advanced use cases like custom loading UI or non-widget SDK operations.

Logging

The StorifyMe SDK includes a built-in logging system that integrates with Apple's unified logging (OSLog). You can control the verbosity of SDK logs to aid debugging during development or silence them in production.

Log Levels

LevelDescriptionUse Case
.verboseMost detailed outputDeep debugging, tracing execution
.debugDebug informationDevelopment debugging
.infoNotable eventsGeneral operational info
.warningPotential issues (Default)Recoverable problems
.errorFailuresCritical errors only
.noneSilent modeProduction, disable all logs

Configuring Log Level

Set the log level before or after SDK initialization:

// Enable debug logging for development
StorifyMeInstance.shared.logLevel = .debug

// Initialize SDK
StorifyMeInstance.shared.initialize(
accountId: "YOUR_ACCOUNT_ID",
apiKey: "YOUR_API_KEY"
)
Recommended Setup

Configure logging before initialize() to capture initialization logs.

Environment-Based Configuration

Use conditional compilation to set different log levels for debug and release builds:

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {

#if DEBUG
// Development: Show debug logs
StorifyMeInstance.shared.logLevel = .debug
#else
// Production: Silent or warnings only
StorifyMeInstance.shared.logLevel = .none
#endif

StorifyMeInstance.shared.initialize(
accountId: "YOUR_ACCOUNT_ID",
apiKey: "YOUR_API_KEY"
)
return true
}

Viewing Logs

SDK logs appear in:

  • Xcode Console: During debugging
  • Console.app: Filter by subsystem com.storifyme.sdk

Log messages follow this format:

[StorifyMe] [LEVEL] [Category] Message

Example output:

[StorifyMe] [DEBUG] [Widget] Loading widget configuration
[StorifyMe] [ERROR] [Network] Request failed: timeout

Log Categories

Logs are organized by functional area for easy filtering:

CategoryDescription
LifecycleSDK initialization and shutdown
NetworkAPI requests and responses
WidgetWidget loading and display
StoryViewerStory presentation
WebViewWKWebView operations
CacheData persistence
Console.app Filtering

In Console.app, filter by subsystem com.storifyme.sdk and category (e.g., Network) to see specific log types.

GIF Cache Management

The StorifyMe SDK includes automatic GIF caching for improved performance. You can configure cache behavior and manually manage caches when needed.

Cache Configuration

Configure cache settings before or after SDK initialization:

// Maximum age for disk cache (default: 7 days)
StorifyMeInstance.shared.diskCacheMaxAge = 7 * 24 * 60 * 60

// Enable/disable automatic cleanup of expired cache (default: true)
StorifyMeInstance.shared.autoCleanExpiredCache = true
tip

Configure cache settings before initialize() to ensure they apply from the start.

Manual Cache Management

For advanced use cases, you can manually manage GIF caches:

// Clear all GIF caches (memory + disk)
StorifyMeInstance.shared.clearAllGIFCaches()

// Get current disk cache size in bytes
if let cacheSize = StorifyMeInstance.shared.getGIFDiskCacheSize() {
let cacheSizeMB = Double(cacheSize) / 1024 / 1024
print("GIF cache size: \(String(format: "%.2f", cacheSizeMB)) MB")
}

When to Clear Caches

ScenarioRecommended Action
Memory warningSDK handles automatically
User logoutCall clearAllGIFCaches()
App entering backgroundSDK cleans expired cache automatically
Storage management UIShow cache size, offer clear option

SwiftUI-Specific Cache Management

When using SwiftUI, use the static methods on StorifyMeWidgetView:

// Clear GIF memory cache
StorifyMeWidgetView.clearGifCache()

// Clear all caches (memory + disk)
StorifyMeWidgetView.clearAllCaches()
info

These methods are particularly important when navigating away from screens with widgets, as SwiftUI may not properly release UIViewRepresentable resources. See the SwiftUI Integration Guide for details.

Configuration

Widget Properties

Retrieve story thumbnail size and other UI properties by calling this method.

storifyMeWidget.getWidgetProperties()

Configure Stories playback

Playback of stories can be customized.

The default behavior is for stories to continue where the user left off, anytime in the past. This can be easily adjusted by setting the PlaybackBehaviour behavior.

  • restartStoriesOnAppLaunch - On every app launch, all stories will start from beginning
  • restartStoriesWhenOpen - On every stories preview launch, all stories will start from beginning
  • alwaysResumeStoryWhereStopped - Stories will resume where user previously stopped watching them, no matter if app was earlier terminated or stories closed
storifyMeWidget.setPlaybackOptions(
options: StorifyMeStoryPlaybackOptions(
behaviour: .restartStoriesOnAppLaunch
)
)

Configure Audio control

Audio control of stories can be customized. It can be easily adjusted by setting StorifyMeStoryAudioOptions.

  • applyLastUserChangeForAllFutureStories - Default state of audio will be used every time, until user changes manually state of audio. After that last user change will be applied to every story shown.
  • applyChangeForSingleStory - The same behaviour will be applied on every story launch, no matter what user done in past. When user mutes/unmutes one story, that change will be applied for that story only.
  • applyChangeForPresentedStories - The same behaviour will be applied on every story launch, no matter what user done in past. When a user mutes/unmutes a story, that change will be applied for all stories that are currently listed in widget. After user dismisses stories, and presents them again, the default behaviour will be applied.
storifyMeWidget.setWidgetAudioOptions(
options: StorifyMeStoryAudioOptions(
behaviour: .applyLastUserChangeForAllFutureStories,
defaultState: .unmuted))

Configure Widget Posters

The widget supports both GIF posters and video posters. Each type has a different default state, and you can control them in code depending on your requirements. If both are disabled, the widget will display a static image by default.

GIF Posters
  • Default: Enabled (automatically play in the widget)
  • Usage: You can disable or enable them programmatically
// Disable GIF posters
storifyMeWidget.setGifPosterEnabled(false)

// Enable GIF posters
storifyMeWidget.setGifPosterEnabled(true)
Video Posters
  • Default: Disabled (do not play automatically)
  • Usage: You can enable or disable them programmatically
// Enable video posters
storifyMeWidget.setVideoPosterEnabled(true)

// Disable video posters
storifyMeWidget.setVideoPosterEnabled(false)

Both poster types can be enabled or disabled depending on your use case. For example, you might enable them to provide richer previews and a more engaging user experience, or disable them to reduce data usage and improve performance on lower-end devices.

Configure URL Presentation Control

The StorifyMeURLPresentationOptions provides flexible options for controlling the presentation of URLs within the application. By configuring this setting, you can define how URLs are displayed when presenting stories.

PresentationMode

  • inAppBrowser: URLs are presented within an in-app browser. User interactions with URLs are maintained across successive story presentations, ensuring consistent behavior.
  • externalBrowser (Default): URLs consistently open in an external browser application. User interactions with URLs remain isolated to individual story presentations and do not affect other content.

The default behavior is to open URLs in an external browser. To set a different default behavior, adjust the StorifyMeURLPresentationOptions accordingly.

Example usage:

storifyMeWidget.setURLPresentationOptions(
options: StorifyMeURLPresentationOptions(presentationMode: .inAppBrowser))

This allows you to tailor the URL presentation experience to match your users' preferences and ensure a seamless browsing experience.

Configure Widget Scroll Navigation Options

The StorifyMeWidgetScrollNavigationOptions provides options for controlling where to place navigation icons.

Example usage:

storifyMeWidget.setWidgetScrollNavigationOptions(
options: StorifyMeWidgetScrollNavigationOptions(
storiesNavigation: .verticalCenteredControls))

Configure Direction for Widget

The desired direction for the StorifyMe widget can be configured using the setDirection method.

Enum: StorifyMeContentDirection

  • ltr: Displays from left to right.
  • rtl: Displays from right to left.
var config = StorifyMeWidgetConfig()
// Set direction to Right-to-Left (RTL)
config.setDirection(direction: StorifyMeContentDirection.rtl)

let storifyMeWidget = StorifyMeWidget()
storifyMeWidget.setWidgetConfig(config: config)
storifyMeWidget.setWidgetId(widgetId: WIDGET_ID)
storifyMeWidget.load()

Configure Language for Widget

The desired language for the StorifyMe widget can be specified using the setLanguage method by providing a valid language code.

var config = StorifyMeWidgetConfig()
// Set the language to English
config.setLanguage(languageCode: "en")

let storifyMeWidget = StorifyMeWidget()
storifyMeWidget.setWidgetConfig(config: config)
storifyMeWidget.setWidgetId(widgetId: WIDGET_ID)
storifyMeWidget.load()

Disable initial onboarding screen

You can hide the onboarding screen with a simple call method:

  StorifyMe.instance.disableInitialOnboarding()

Disable Tutorial

Use this method to permanently disable the initial onboarding tutorial, ensuring users won't encounter it again after it's triggered.

StorifyMeInstance.shared.disableInitialOnboarding()

Methods

Show StorifyMe Notification

This feature enables the display of notifications while presenting stories, enhancing user engagement and interaction within the platform. Users can now receive notifications that provide additional context or updates related to the ongoing story, enhancing the overall storytelling experience.

To implement this feature, use the following code:

StorifyMeInstance.shared.showNotification(
title: "Notification title example",
message: "Message example")

Replace the parameters with the desired title, message and optional duration for the notification display.

Pause and Resume Playback

This feature allows you to pause and resume the playback of stories and reels with the StorifyMe playback controller. You can control the visibility of playback controls during this process.

To Pause Playback:

StorifyMeInstance.shared.playbackController.pausePlayback(controlsVisibility: .visible)    

Use this method to pause the current story or reel with the option to keep playback controls visible.

To Resume Playback:

StorifyMeInstance.shared.playbackController.resumePlayback(mode: .fromBeginning)    

Use this method to resume the paused story or reel, specifying whether playback should start from the beginning.

Replace the parameters and options as needed for your specific use case.

Close Stories

Terminate and close all playing stories or reels using the StorifyMe playback controller.

StorifyMeInstance.shared.playbackController.closePlayback()

Make sure to use this feature judiciously, as it abruptly ends the playback without any specific user interaction. It is recommended to provide appropriate cues or user prompts before invoking this method to ensure a seamless and user-friendly experience.

Open a single story by handle

It's possible to open a single specific StorifyMe story by handle programmatically, here is the example code:

import StorifyMe

Then, in order to launch the specific story with handle some-example-handle all that needs to be done is to run this line of code:

  StorifyMeInstance.shared.openStoryByHandle(handle: "some-example-handle")

In case you want to define the button (or similar) via code, you can do this:

override func viewDidLoad() {
super.viewDidLoad()
...
setupOpenByHandleButton()
}

private func setupOpenByHandleButton() {
let button = UIButton()
...
button.addTarget(self, action: #selector(openByHandleButtonTapped), for: .touchUpInside)
...
}

@objc private func openByHandleButtonTapped() {
StorifyMeInstance.shared.openStoryByHandle(handle: "some-example-handle")
}

Open stories manually

You can also load a story right away after the page is loaded. This way, the first thing a user sees is the story. To do so, you can have a loading animation while stories load, and then once stories are loaded you can just open a story by ID, handle or position.

And then using the code you can open a specific story by:

  • position
  • handle
  • id as explained in the following sections.

Before opening a story, you can customize its playback and audio behavior using the StorifyMeStoryBehaviour configuration. This allows you to control how the story behaves when it’s opened. The class has the following properties:

  • storyAudioOptions (StorifyMeStoryAudioOptions?): Controls the audio behavior of the story.
  • storyPlaybackOptions (StorifyMeStoryPlaybackOptions?): Defines the playback behavior.

openWidgetStoryByPosition()

You can open a story from the widget directly from the code. To open the first story in the Widget list of stories, please use:

// Old way: Position and completion handler.
storifyMeWidget.openWidgetStoryByPosition(_ position: Int, completion: ((StorifyMeWidgetStoryNavigatorExecutionResult) -> Void)?)

// New way (from version 2.4.8): Position, optional story behavior, and completion handler.
storifyMeWidget.openWidgetStoryByPosition(_ position: Int, storyBehaviour: StorifyMeStoryBehaviour?, completion: ((StorifyMeWidgetStoryNavigatorExecutionResult) -> Void)?)

How to get list of stories?

You can get length of stories in widget, as well as their IDs and handles from the onLoad callback.

openWidgetStoryByHandle()

You can open a story by handle from the widget directly from the code. To open the story with the handle story-handle from the Widget list of stories, please use:

// Old way: Handle and completion handler.
storifyMeWidget.openWidgetStoryByHandle(_ handle: String, completion: ((StorifyMeWidgetStoryNavigatorExecutionResult) -> Void)?)

// New way (from version 2.4.8): Handle, optional story behavior, and completion handler.
storifyMeWidget.openWidgetStoryByHandle(_ handle: String, storyBehaviour: StorifyMeStoryBehaviour?, completion: ((StorifyMeWidgetStoryNavigatorExecutionResult) -> Void)?)

openWidgetStoryById()

You can open a story by ID from the widget directly from the code. To open the story with the ID 1 from the Widget list of stories, please use:

// Old way: ID and completion handler.
storifyMeWidget.openWidgetStoryById(_ id: Int, completion: ((StorifyMeWidgetStoryNavigatorExecutionResult) -> Void)?)

// New way (from version 2.4.8): ID, optional story behavior, and completion handler.
storifyMeWidget.openWidgetStoryById(_ id: Int, storyBehaviour: StorifyMeStoryBehaviour?, completion: ((StorifyMeWidgetStoryNavigatorExecutionResult) -> Void)?)

Accessibility

Accessibility support

The StorifyMe SDK provides accessibility support to ensure that your app is usable by people with disabilities. To enhance the accessibility of your app, you can follow these guidelines:

  1. Provide meaningful alternative text for images and other non-text content.
  2. Ensure that all interactive elements are accessible via touch or keyboard navigation.
  3. Use appropriate color contrast to ensure readability for users with visual impairments.
  4. Provide captions or transcripts for audio and video content.
  5. Use semantic markup to structure your content and make it easier to navigate with assistive technologies.

Set focus on a story in a widget

To set focus on a specific item in the StoriesView later, you can use the following code:

storifyMeWidget.setFocusOnStoryCell(story)

Replace story with the desired item to set focus on. This can help users with visual impairments navigate through the stories more easily.

Focus a story cell when using an external keyboard

If you need to focus a story cell while an external keyboard is used, check the approach below:

Click to view the full code
let storifyMeWidget = StorifyMeWidget()
var cellToFocus: UICollectionViewCell? = nil // Variable to store the cell to focus on

// Function to focus on a specific story cell
private func focusCell(story: story) {
// Set focus on the specified story
storifyMeWidget.setFocusOnStoryCell(story: story) { cell in
// Completion handler: once focus is set, check if the cell is returned
if let cell = cell {
// Store the focused cell if available
self.cellToFocus = cell
} else {
// If no cell is returned, reset the focus variable
self.cellToFocus = nil
}

// Request a focus update and ensure the focus is updated immediately
self.setNeedsFocusUpdate()
self.updateFocusIfNeeded()
}
}

// Override to return the custom cell for focus if available
// If no custom cell is set, fall back to the default preferred focus environments
override var preferredFocusEnvironments: [UIFocusEnvironment] {
// Check if a custom cell is set for focus
if let cell = cellToFocus {
// Reset the focus variable after using the custom cell
cellToFocus = nil
// Return the custom cell as the preferred focus environment
return [cell]
} else {
// Fallback to the default preferred focus environments if no custom cell is set
return super.preferredFocusEnvironments
}
}

focusCell(story:)

focusCell(story:): This function sets focus on a specific story by passing the story object. It uses a completion handler to ensure that the focus update is performed only after the story's focus action has completed. If the focus is set successfully, it stores the corresponding UICollectionViewCell in the cellToFocus variable.

preferredFocusEnvironments

preferredFocusEnvironments: This override ensures that the custom UICollectionViewCell is used as the preferred focus environment. If no custom focus is set (i.e., cellToFocus is nil), it defaults to the standard behavior.

By following these accessibility guidelines, you can ensure that your app is inclusive and accessible to all users, regardless of their abilities.