Skip to main content
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:
  1. Go to File → Add Package Dependencies
  2. Enter the repository URL: https://github.com/shortkit/shortkit-ios.git
  3. Select version 1.0.0 or later
Or add it to your Package.swift:
Package.swift
dependencies: [
    .package(url: "https://github.com/shortkit/shortkit-ios.git", from: "1.0.0")
]

CocoaPods

Podfile
pod 'shortkit', '~> 1.0'
Then run:
pod install

Initialization

Initialize the SDK as early as possible in your app lifecycle, typically in AppDelegate or your app’s entry point.
AppDelegate.swift
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
    }
}

Displaying the feed

SwiftUI

Use ShortkitFeedView directly in your view hierarchy:
VideoFeedView.swift
import shortkit
import SwiftUI

struct VideoFeedView: View {
    var body: some View {
        ShortkitFeedView()
            .edgesIgnoringSafeArea(.all)
    }
}
With filters:
FilteredFeedView.swift
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:
SceneDelegate.swift
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:
  1. Select your target
  2. Go to Signing & Capabilities
  3. Add Background Modes
  4. 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:
HomeViewController.swift
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