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
}
}
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
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
onFaildelegate 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
- 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 Case | Recommended Approach |
|---|---|
| Just loading widgets | No extra code needed - widgets auto-wait |
| Custom UI before widgets load (loading spinner, etc.) | whenReady method |
| Non-widget operations need SDK ready | Completion handler |
| AppDelegate-based architecture | StorifyMeInitializationDelegate |
| Multiple components need to know | Notifications |
| Account switching / logout | shutdown() then re-initialize |
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
| Level | Description | Use Case |
|---|---|---|
.verbose | Most detailed output | Deep debugging, tracing execution |
.debug | Debug information | Development debugging |
.info | Notable events | General operational info |
.warning | Potential issues (Default) | Recoverable problems |
.error | Failures | Critical errors only |
.none | Silent mode | Production, 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"
)
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:
| Category | Description |
|---|---|
| Lifecycle | SDK initialization and shutdown |
| Network | API requests and responses |
| Widget | Widget loading and display |
| StoryViewer | Story presentation |
| WebView | WKWebView operations |
| Cache | Data persistence |
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
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
| Scenario | Recommended Action |
|---|---|
| Memory warning | SDK handles automatically |
| User logout | Call clearAllGIFCaches() |
| App entering background | SDK cleans expired cache automatically |
| Storage management UI | Show 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()
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 beginningrestartStoriesWhenOpen- On every stories preview launch, all stories will start from beginningalwaysResumeStoryWhereStopped- 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)?)
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:
- Provide meaningful alternative text for images and other non-text content.
- Ensure that all interactive elements are accessible via touch or keyboard navigation.
- Use appropriate color contrast to ensure readability for users with visual impairments.
- Provide captions or transcripts for audio and video content.
- 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.