ShipinKit is an unofficial Swift SDK for prototyping typed video-generation flows with the Runway and Luma APIs.
- Typed, provider-specific requests and responses; no public
Anyreturn values - A lazily resolved, always-redacted
ShipinCredential - An injectable
ShipinTransportfor deterministic tests and app-owned networking - Runway API version
2024-11-06with Gen-4.5 and Gen-4 Turbo contracts - Luma Ray 2 and Ray 2 Flash video-generation contracts
- Structured task states, failure receipts, polling timeouts, and cancellation handling
- Finite per-request timeouts and path-safe provider identifiers
- Offline fixture tests that never require or call a live provider account
ShipinKit never logs credentials, authorization headers, prompt bodies, or provider responses.
- Swift 6.0+
- iOS 16+, macOS 14+, tvOS 16+, watchOS 9+, or visionOS 1+
dependencies: [
.package(url: "https://github.com/rryam/ShipinKit.git", from: "2.0.0")
]Then add ShipinKit to your target dependencies.
Do not commit API keys or put them in a shared Xcode scheme. Resolve them from an app-owned secrets service instead:
let credential = ShipinCredential {
try await secrets.runwayAPIKey()
}The closure is called at request time, so an application can rotate a credential without recreating its client. String(describing:) and String(reflecting:) always produce <redacted>.
An API key embedded in a distributed client app cannot be kept fully secret. For production apps, keep provider credentials on a server you control and expose a narrow authenticated endpoint to the app. Direct provider access is best limited to local tools and prototypes.
Create a typed request, start it, and wait for the complete task receipt:
import ShipinKit
let image = try RunwayMLImageSource(
url: URL(string: "https://example.com/input.jpg")!
)
let request = try RunwayMLImageToVideoRequest(
model: .gen4Turbo,
promptImage: image,
promptText: "A slow camera push through morning fog",
duration: .fiveSeconds,
ratio: .landscape
)
let runway = RunwayML(credential: credential)
let result = try await runway.generateVideo(request)
switch result.task.state {
case .succeeded(let output):
// Store provider output promptly; provider URLs are temporary.
use(output)
default:
break
}For manual task control:
let created = try await runway.createImageToVideoTask(request)
let latest = try await runway.task(id: created.id)
try await runway.cancelOrDeleteTask(id: created.id)The request type deliberately supports the shared Gen-4.5 and Gen-4 Turbo image-to-video fields. Other Runway models have different parameter contracts and are not represented as if they were interchangeable.
import ShipinKit
let credential = ShipinCredential {
try await secrets.lumaAPIKey()
}
let request = try LumaAIGenerationRequest(
prompt: "A paper boat moving across a calm pond",
model: .ray2,
resolution: .p720,
duration: .fiveSeconds,
aspectRatio: .landscape
)
let luma = LumaAI(credential: credential)
let result = try await luma.generateVideo(request)
use(result.videoURL)Image-to-video uses typed keyframes:
let keyframes = try LumaAIKeyframes(
frame0: .image(URL(string: "https://example.com/start.jpg")!)
)
let request = try LumaAIGenerationRequest(
prompt: "A tiger walks forward through falling snow",
keyframes: keyframes
)Current Luma camera concepts are typed in requests and discoverable from the provider:
let concept = try LumaAIConcept(key: "dolly_zoom")
let request = try LumaAIGenerationRequest(
prompt: "A car on a mountain road",
concepts: [concept]
)
let availableConcepts = try await luma.listConcepts()Known resolution and duration cases have named values. Their .custom(...) cases preserve newer provider values without requiring a ShipinKit release first.
If the application chooses a provider dynamically, ShipinClient returns a typed enum:
let client = ShipinClient(
service: .runwayML(credential: credential)
)
switch try await client.generate(.runwayML(request)) {
case .runwayML(let task):
use(task.id)
case .lumaAI:
break
}All clients accept any ShipinTransport. Tests can return fixture data and inspect the generated URLRequest without making a network call:
struct FixtureTransport: ShipinTransport {
let response: ShipinHTTPResponse
func send(_ request: URLRequest) async throws -> ShipinHTTPResponse {
response
}
}Each client also accepts a requestTimeout (60 seconds by default). Polling has a separate overall timeout, so one stalled request and one long-running generation have independent limits.
- Import and link the
ShipinKitproduct. The published1.0.0product was namedRunveyKit. - Replace the old multi-parameter
generate(...) -> Anycall with aLumaAIGenerationRequestorRunwayMLImageToVideoRequest. - Replace
gen3a_turbo,16:9/9:16Runway values, and the old version header with the typed current model, resolution, and duration values. - Remove the obsolete Runway
watermarkparameter; it is not part of the current image-to-video contract. - Handle
ShipinErrorfor validation, transport, HTTP, terminal generation, and timeout failures. - Replace live placeholder tests with an injected
ShipinTransportand fixture responses.
Provider contracts are based on the official Runway API reference, Runway model catalog, and Luma video-generation documentation.
ShipinKit is available under the MIT license. It is not affiliated with or endorsed by Runway or Luma AI.