Screenshot API for Swift: URLSession, async/await & Combine
Capture screenshots, scrape web pages, and generate PDFs from Swift apps. Works with iOS, macOS, watchOS, and Swift on the server with Vapor or Hummingbird — no headless browser required.
Screenshot API for Swift — URLSession Quickstart
Swift's URLSession with async/await makes calling SnapAPI clean and readable. The following function returns screenshot data as Data and throws on API errors:
import Foundation
struct SnapAPI {
let apiKey: String
let session: URLSession
init(apiKey: String, session: URLSession = .shared) {
self.apiKey = apiKey
self.session = session
}
func screenshot(url: String, fullPage: Bool = true) async throws -> Data {
var components = URLComponents(string: "https://api.snapapi.pics/v1/screenshot")!
components.queryItems = [
URLQueryItem(name: "url", value: url),
URLQueryItem(name: "full_page", value: fullPage ? "true" : "false"),
URLQueryItem(name: "format", value: "jpeg"),
URLQueryItem(name: "block_ads", value: "true")
]
var request = URLRequest(url: components.url!)
request.setValue(apiKey, forHTTPHeaderField: "X-Api-Key")
request.timeoutInterval = 30
let (data, response) = try await session.data(for: request)
guard let http = response as? HTTPURLResponse, http.statusCode == 200 else {
throw URLError(.badServerResponse)
}
return data
}
}
// Usage
let snap = SnapAPI(apiKey: ProcessInfo.processInfo.environment["SNAPAPI_KEY"]!)
let imageData = try await snap.screenshot(url: "https://example.com")
// imageData is a JPEG you can display with UIImage(data:) or NSImage(data:)
The async/await pattern integrates naturally with SwiftUI's .task modifier and UIKit's async contexts. The returned Data can be passed directly to UIImage(data:) on iOS or NSImage(data:) on macOS.
Combine Integration
For codebases using Combine, wrap the URLSession dataTaskPublisher:
import Combine
func screenshotPublisher(url: String, apiKey: String) -> AnyPublisher<Data, Error> {
var components = URLComponents(string: "https://api.snapapi.pics/v1/screenshot")!
components.queryItems = [URLQueryItem(name: "url", value: url)]
var request = URLRequest(url: components.url!)
request.setValue(apiKey, forHTTPHeaderField: "X-Api-Key")
return URLSession.shared.dataTaskPublisher(for: request)
.tryMap { data, response in
guard (response as? HTTPURLResponse)?.statusCode == 200 else {
throw URLError(.badServerResponse)
}
return data
}
.eraseToAnyPublisher()
}
// Usage with SwiftUI ObservableObject
screenshotPublisher(url: "https://example.com", apiKey: key)
.receive(on: DispatchQueue.main)
.sink(receiveCompletion: { _ in }, receiveValue: { data in
self.screenshotImage = UIImage(data: data)
})
.store(in: &cancellables)
The publisher completes with the raw JPEG data. Chain .map { UIImage(data: $0) } to convert it for display. The receive(on: DispatchQueue.main) call ensures UI updates happen on the main thread.
Swift Server-Side Integration with Vapor
On the server, Vapor's async route handlers call SnapAPI naturally. Register a route that accepts a URL parameter and returns a screenshot:
import Vapor
func routes(_ app: Application) throws {
app.get("capture") { req async throws -> Response in
guard let target = req.query[String.self, at: "url"] else {
throw Abort(.badRequest, reason: "Missing url parameter")
}
let snap = SnapAPI(apiKey: Environment.get("SNAPAPI_KEY")!)
let imageData = try await snap.screenshot(url: target)
var headers = HTTPHeaders()
headers.add(name: .contentType, value: "image/jpeg")
return Response(status: .ok, headers: headers, body: .init(data: imageData))
}
}
This route proxies screenshot requests, adding your API key server-side so it is never exposed to clients. The Vapor route streams the response directly, keeping memory usage low even for large full-page captures.
SwiftUI Preview Image Component
Building a link preview feature in your iOS app is straightforward with SnapAPI. Create a SwiftUI view that loads a screenshot asynchronously and caches it with NSCache:
struct WebPreview: View {
let url: String
@State private var image: UIImage?
var body: some View {
Group {
if let image {
Image(uiImage: image)
.resizable()
.aspectRatio(contentMode: .fit)
} else {
ProgressView()
.frame(width: 300, height: 200)
}
}
.task {
let snap = SnapAPI(apiKey: Secrets.snapAPIKey)
if let data = try? await snap.screenshot(url: url, fullPage: false) {
image = UIImage(data: data)
}
}
}
}
The .task modifier cancels the async call automatically when the view disappears, preventing dangling network requests. Add a cache layer with NSCache<NSString, UIImage> to avoid re-fetching screenshots for URLs that have already been loaded in the current session.
Data Extraction and Scraping in Swift
SnapAPI's extract endpoint returns structured JSON, which decodes cleanly with Swift's Codable protocol. The response includes the matched text, element attributes, and an array of all matched elements for multi-element selectors:
struct ExtractRequest: Encodable {
let url: String
let selector: String
let waitFor: String?
enum CodingKeys: String, CodingKey {
case url, selector
case waitFor = "wait_for"
}
}
struct ExtractResponse: Decodable {
let text: String
let elements: [String]?
}
func extractText(from url: String, selector: String, apiKey: String) async throws -> String {
var request = URLRequest(url: URL(string: "https://api.snapapi.pics/v1/extract")!)
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.setValue(apiKey, forHTTPHeaderField: "X-Api-Key")
request.httpBody = try JSONEncoder().encode(ExtractRequest(url: url, selector: selector, waitFor: selector))
let (data, _) = try await URLSession.shared.data(for: request)
let result = try JSONDecoder().decode(ExtractResponse.self, from: data)
return result.text
}
Swift's Codable with CodingKeys handles the snake_case to camelCase conversion automatically. The waitFor parameter tells the API to wait until the selector is present in the DOM, handling JavaScript-rendered content that would otherwise return empty results.
Register at snapapi.pics for 200 free captures per month — no credit card required. The official SnapAPI Swift SDK is available on GitHub at github.com/Sleywill/snapapi-swift and supports all API endpoints with a clean Swift interface.