The shortkit iOS SDK provides a native video feed experience built on low-level AVFoundation APIs for optimal performance.
Requirements
- iOS 14.0 or later
- Xcode 14.0 or later
- Swift 5.7 or later
Installation
Swift Package Manager
Add shortkit to your project in Xcode:
- Go to File → Add Package Dependencies
- Enter the repository URL:
https://github.com/shortkit/shortkit-ios.git
- Select version
1.0.0 or later
Or add it to your Package.swift:
dependencies: [
.package(url: "https://github.com/shortkit/shortkit-ios.git", from: "1.0.0")
]
CocoaPods
Then run:
Initialization
Initialize the SDK as early as possible in your app lifecycle, typically in AppDelegate or your app’s entry point.
import shortkit
import UIKit
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
let config = ShortkitConfig(
feedOrientation: .vertical,
feedHeight: .fullscreen,
controls: ControlsConfig(
playPause: true,
scrubber: true,
playbackSpeed: PlaybackSpeedConfig(
enabled: true,
options: [1.0, 1.25, 1.5, 2.0]
),
captions: CaptionConfig(enabled: true, defaultOn: false),
volume: true,
share: ShareConfig(enabled: true)
),
theme: ThemeConfig(
primaryColor: "#FF6600",
controlsTint: "#FFFFFF",
backgroundColor: "#000000"
)
)
shortkit.initialize(
apiKey: "pk_live_your_publishable_key",
config: config,
environment: .production
)
return true
}
}
import shortkit
import SwiftUI
@main
struct MyApp: App {
init() {
let config = ShortkitConfig(
feedOrientation: .vertical,
feedHeight: .fullscreen,
controls: ControlsConfig(
playPause: true,
scrubber: true,
captions: CaptionConfig(enabled: true, defaultOn: false),
share: ShareConfig(enabled: true)
),
theme: ThemeConfig(
primaryColor: "#FF6600",
controlsTint: "#FFFFFF"
)
)
shortkit.initialize(
apiKey: "pk_live_your_publishable_key",
config: config
)
}
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
Displaying the feed
SwiftUI
Use ShortkitFeedView directly in your view hierarchy:
import shortkit
import SwiftUI
struct VideoFeedView: View {
var body: some View {
ShortkitFeedView()
.edgesIgnoringSafeArea(.all)
}
}
With filters:
import shortkit
import SwiftUI
struct FilteredFeedView: View {
var body: some View {
ShortkitFeedView(
filter: FeedFilter(tags: ["sports", "highlights"])
)
.edgesIgnoringSafeArea(.all)
}
}
UIKit
Create and add the feed view to your view controller:
VideoFeedViewController.swift
import shortkit
import UIKit
class VideoFeedViewController: UIViewController {
private var feedView: ShortkitFeedUIView?
override func viewDidLoad() {
super.viewDidLoad()
feedView = shortkit.createFeedView()
feedView?.frame = view.bounds
feedView?.autoresizingMask = [.flexibleWidth, .flexibleHeight]
if let feedView = feedView {
view.addSubview(feedView)
}
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
feedView?.resume()
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
feedView?.pause()
}
}
User identity
Associate user identity for personalized feeds and cross-device engagement:
// When user logs in
shortkit.setUserId("user_12345")
// When user logs out
shortkit.clearUserId()
Call setUserId as soon as your user authenticates. This triggers identity resolution, merging any anonymous engagement data with the identified profile.
Delegate methods
Implement ShortkitDelegate to receive callbacks:
import shortkit
class VideoFeedCoordinator: ShortkitDelegate {
init() {
shortkit.delegate = self
}
// Called when user taps share
func shortKit(_ shortKit: shortkit, didRequestShare content: Content) {
let url = content.shareUrl
let activityVC = UIActivityViewController(
activityItems: [url],
applicationActivities: nil
)
// Present share sheet
}
// Called when user taps a custom action
func shortKit(_ shortKit: shortkit, didTapAction action: String, on content: Content) {
switch action {
case "bookmark":
// Handle bookmark
break
case "readMore":
// Navigate to article
break
default:
break
}
}
// Called on playback errors
func shortKit(_ shortKit: shortkit, didEncounterError error: ShortkitError) {
print("Playback error: \(error.localizedDescription)")
}
// Called when content metadata is available
func shortKit(_ shortKit: shortkit, didLoadMetadata metadata: [String: Any], for content: Content) {
// Access custom metadata passed from your CMS
if let articleUrl = metadata["articleUrl"] as? String {
// Store for "Read More" action
}
}
}
Deep linking
Handle deep links to open specific content:
import shortkit
import UIKit
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
func scene(
_ scene: UIScene,
continue userActivity: NSUserActivity
) {
guard userActivity.activityType == NSUserActivityTypeBrowsingWeb,
let url = userActivity.webpageURL else {
return
}
// Extract content ID from your URL scheme
if let contentId = extractContentId(from: url) {
shortkit.openContent(contentId)
}
}
private func extractContentId(from url: URL) -> String? {
// Parse your deep link format
// e.g., yourapp.com/watch/cnt_abc123
let components = url.pathComponents
if components.contains("watch"), let id = components.last {
return id
}
return nil
}
}
Picture-in-Picture
Enable PiP for background playback:
let config = ShortkitConfig(
// ... other config
controls: ControlsConfig(
pictureInPicture: true
)
)
Add the background modes capability in your project settings:
- Select your target
- Go to Signing & Capabilities
- Add Background Modes
- Enable Audio, AirPlay, and Picture in Picture
AirPlay
Enable AirPlay casting:
let config = ShortkitConfig(
controls: ControlsConfig(
airplay: true
)
)
Entry widget
Create a mini-player widget for embedding in other screens:
import shortkit
import UIKit
class HomeViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let widgetConfig = EntryWidgetConfig(
style: .thumbnailRow,
itemCount: 5,
autoPlay: true // Play preview on hover/focus
)
let widget = shortkit.createEntryWidget(config: widgetConfig)
widget.frame = CGRect(x: 0, y: 100, width: view.bounds.width, height: 200)
widget.delegate = self
view.addSubview(widget)
}
}
extension HomeViewController: EntryWidgetDelegate {
func entryWidget(_ widget: EntryWidget, didSelectContent content: Content) {
// Present full feed starting at selected content
let feedVC = ShortkitFeedViewController(startingAt: content.id)
present(feedVC, animated: true)
}
}
Custom overlays
Register custom views to render on specific content:
// Register a custom overlay component
shortkit.registerOverlay(
identifier: "articleCard",
viewProvider: { content, metadata in
let card = ArticleCardView()
card.title = metadata["headline"] as? String
card.source = metadata["source"] as? String
return card
}
)
The overlay renders when content has matching metadata:
{
"customMetadata": {
"overlayType": "articleCard",
"headline": "Breaking News Story",
"source": "CNN"
}
}
Error handling
shortkit.delegate = self
func shortKit(_ shortKit: shortkit, didEncounterError error: ShortkitError) {
switch error {
case .networkError(let underlying):
// Handle network issues
break
case .playbackError(let code, let message):
// Handle playback failures
break
case .configurationError(let message):
// Handle config issues
break
}
}
Debug logging
Enable verbose logging during development:
shortkit.initialize(
apiKey: "pk_live_your_publishable_key",
config: config,
environment: .development // Enables debug logging
)
// Or explicitly set log level
shortkit.logLevel = .verbose
Next steps