Skip to content

About

Swift SDK for the put.io API

Topics

Resources

Contributing

Security policy

Stars

19 stars

Watchers

5 watching

Forks

Repository files navigation

put.io boncuk

putio-sdk-swift

Swift SDK for the put.io API

Swift Package: PutioSDK

CI Latest release license

Installation

Requires Xcode 26 or newer. The Swift Package targets iOS, macOS, Mac Catalyst, tvOS, and watchOS 26.

Install with Swift Package Manager in Xcode using:

https://github.com/putdotio/putio-sdk-swift.git

Or add it to Package.swift and depend on the PutioSDK product:

dependencies: [
    .package(url: "https://github.com/putdotio/putio-sdk-swift.git", from: "4.0.0")
]

CocoaPods is no longer supported: trunk goes read-only in December 2026, and 3.8.1 is the last PutioSDK pod. Switch to Swift Package Manager for 4.0.0 and later.

Quick Start

import PutioSDK

let sdk = PutioSDK(
    config: PutioSDKConfig(
        clientID: "<your-client-id>",
        token: "<your-access-token>"
    )
)

Task {
    do {
        let account = try await sdk.getAccountInfo()
        print(account.username)
    } catch let error as PutioSDKError {
        print(error.message)
        print(error.recoverySuggestion ?? "")
    }
}

Every network call is async throws over native URLSession; there is no third-party networking dependency. URL builders and callback parsing are synchronous.

Apps that need a custom transport for tests, fixtures, or specialized session configuration can pass their own URLSession:

let configuration = URLSessionConfiguration.ephemeral
configuration.protocolClasses = [MockURLProtocol.self]

let sdk = PutioSDK(
    config: PutioSDKConfig(clientID: "<your-client-id>"),
    urlSession: URLSession(configuration: configuration)
)

Error Handling

Thrown SDK errors are PutioSDKError values that conform to LocalizedError and expose small classification helpers for app code:

do {
    _ = try await sdk.getFile(fileID: 42)
} catch let error as PutioSDKError {
    if error.isAuthenticationFailure {
        // refresh credentials or send the user through sign-in
    } else if error.isRetryable {
        // schedule a retry with backoff
    } else if error.matches(statusCode: 404) {
        // refresh stale local state
    }
}

Video Playback

Media URLs carry the account's download token, never the OAuth token. The download token only authorizes file media endpoints such as streams, HLS, downloads, and subtitles, so it is the credential to hand to players, cast receivers, and external apps. Fetch it once from account info:

let account = try await sdk.getAccountInfo(query: PutioAccountInfoQuery(downloadToken: true))
let downloadToken = account.downloadToken

The SDK resolves video metadata with the configured OAuth token and builds the HLS URL with the download token:

switch try await sdk.resolveVideoPlaybackSource(fileID: 42, downloadToken: downloadToken) {
case .ready(let source):
    play(url: source.url, startingAt: source.startFrom)
case .conversionRequired:
    showConversionRequired()
}

Passing a non-video file throws PutioVideoPlaybackResolutionError.unsupportedFileType with a localized recovery suggestion.

Audio files resolve the same way into a direct stream source, with startFrom carrying the saved position:

let source = try await sdk.resolveAudioPlaybackSource(fileID: 50, downloadToken: downloadToken)
play(url: source.url, startingAt: source.startFrom)

Passing a non-audio file throws PutioAudioPlaybackResolutionError.unsupportedFileType.

PutioFile and PutioNextFile build the same URLs directly with getStreamURL(downloadToken:), getHlsStreamURL(downloadToken:), getAudioStreamURL(downloadToken:), getDownloadURL(downloadToken:), and getMp4DownloadURL(downloadToken:).

Media URLs are still bearer credentials for the account's files. Use them for playback and downloads; do not log or persist them.

App Config

/config stores whatever keys an app writes. Declare the shape in the app and let the SDK carry it:

// The SDK decodes off the caller's actor; `nonisolated` keeps the conformance
// usable there in targets with `MainActor` default isolation.
nonisolated struct AppConfig: Decodable {
  var autoplayNextVideo: Bool

  enum CodingKeys: String, CodingKey {
    case autoplayNextVideo = "autoplay_next_video"
  }

  // The document only holds keys some client has written; missing keys are
  // the app's defaults, not decoding failures.
  init(from decoder: Decoder) throws {
    let container = try decoder.container(keyedBy: CodingKeys.self)
    autoplayNextVideo = try container.decodeIfPresent(Bool.self, forKey: .autoplayNextVideo) ?? false
  }
}

let config = try await sdk.getConfig(as: AppConfig.self)
_ = try await sdk.setConfigValue(key: "autoplay_next_video", true)

Authentication Example

The example app shows a minimal ASWebAuthenticationSession flow with your own client ID and redirect URI, followed by an account fetch:

Generate a state with try PutioSDK.generateOAuthState(), pass it to getAuthURL(redirectURI:state:), then extract the token with accessToken(fromOAuthCallback:expectedScheme:expectedHost:expectedState:), which rejects callbacks whose state does not match.

Docs

  • Contributing for setup, make verify, live API checks, and releases
  • Architecture for the transport, concurrency posture, and covered API surface
  • Security policy for private vulnerability reports; fixes target the latest published release and main
  • Agent guide

License

This project is available under the MIT License

About

Swift SDK for the put.io API

Topics

Resources

Contributing

Security policy

Stars

19 stars

Watchers

5 watching

Forks

Releases

Used by

Contributors

Languages