Tutorial

Screenshot API in Swift & iOS — URLSession, SwiftUI & Combine

Capture web screenshots from Swift iOS and macOS apps using SnapAPI. Full URLSession examples, SwiftUI Image display, and async/await integration.

Screenshot API in Swift with URLSession

iOS and macOS applications can capture screenshots of web pages through the SnapAPI REST endpoint using URLSession, Swift's built-in networking layer. No third-party HTTP libraries are required. Construct a URLRequest by building a URLComponents object with the base URL set to https://snapapi.pics/api/screenshot and adding URLQueryItem values for access_key, target, format, and any optional parameters like width or delay. Convert the URLComponents to a URL and create a URLRequest from it with a GET HTTP method. Call URLSession.shared.dataTask(with: request) in completion handler style or use URLSession.shared.data(for: request) with Swift's async/await to receive the response data as a Data object containing the raw PNG bytes.

Displaying the screenshot in a UIKit view controller requires converting the Data response into a UIImage. Unwrap the UIImage initializer result safely — pass the response data to UIImage(data: imageData) and check that the result is non-nil before assigning it to an image view. Set the contentMode of the UIImageView to .scaleAspectFit to display the screenshot at the correct aspect ratio within the available view bounds. For SwiftUI applications, convert the Data to a UIImage and then wrap it in a SwiftUI Image with Image(uiImage: uiImage), or on macOS use NSImage and Image(nsImage: nsImage). The conversion is straightforward and requires no external image processing libraries.

Screenshot API in SwiftUI with async/await

Modern SwiftUI applications use async/await for all network calls, keeping view code clean and readable. Define a ScreenshotService class or actor with an async function fetchScreenshot(url: String) throws -> Data that builds the SnapAPI request URL and calls URLSession.shared.data(for: request). The function throws for non-200 status codes using a custom ScreenshotAPIError enum with cases for invalidURL, apiError(statusCode: Int), networkError(underlying: Error), and invalidImageData. In the SwiftUI view, call the screenshot service from within a task modifier on the view: .task { do { screenshotData = try await screenshotService.fetchScreenshot(url: targetURL) } catch { error = localizedError } }. The task modifier automatically cancels the in-flight request when the view disappears.

Caching screenshot data in SwiftUI applications prevents redundant API calls when a user navigates away from and back to a view displaying a screenshot. Use NSCache to store UIImage objects keyed by the target URL string. NSCache automatically evicts entries under memory pressure, making it appropriate for screenshot thumbnails that can always be regenerated from the API. Define the cache as a singleton or inject it through the SwiftUI environment as an EnvironmentObject to share it across views. The image loading logic becomes: check the cache, return the cached image if present, otherwise fetch from SnapAPI and store the result in the cache before returning it to the caller. This pattern reduces API usage and improves perceived performance in applications that display many screenshot thumbnails simultaneously.

Screenshot API with Swift Combine

iOS applications using the Combine framework for reactive programming can wrap the SnapAPI URLSession call in a publisher that emits a UIImage on success or an error on failure. Use URLSession.shared.dataTaskPublisher(for: request) as the base publisher and chain operators to transform the raw (Data, URLResponse) tuple into a UIImage. Apply .tryMap to check the HTTP status code and throw an error for non-200 responses, then apply a second .tryMap to convert the Data into a UIImage. Apply .receive(on: DispatchQueue.main) before the sink subscriber to ensure the UIImage result is delivered on the main thread, where UIKit and SwiftUI expect image updates. Store the returned AnyCancellable in a Set or property to keep the subscription alive until the screenshot loads.

Combine pipelines that generate screenshots for a dynamic list of URLs use the flatMap operator to transform each URL in a publisher into a screenshot request and merge the results. Use Publishers.MergeMany or Publishers.Sequence combined with flatMap(maxPublishers: .max(5)) to limit concurrent SnapAPI requests to five at a time, matching the API rate limit without overwhelming the network layer. This pattern works well in collection views where visible cells need screenshot thumbnails — as cells scroll into view, they subscribe to the screenshot publisher, and the flatMap concurrency limit prevents the ten most recently visible cells from all firing simultaneous API requests.

Screenshot API Error Handling in Swift

Robust Swift integrations handle all SnapAPI error cases and provide clear feedback to users when screenshot generation fails. The API returns structured HTTP status codes: 400 for invalid parameters or malformed target URLs, 401 for missing or invalid API keys, 422 for target URLs that return error status codes, 429 for rate limit or monthly quota exhaustion, and 500 for unexpected server errors. Define a Swift enum ScreenshotAPIError: LocalizedError with cases matching each status code category and implement errorDescription to return user-facing strings. In your URLSession completion handler or async function, map the HTTP status code to the appropriate error case and rethrow it so calling code can handle it appropriately.

iOS applications that display screenshot thumbnails in a collection view need loading state management to handle the time between requesting a screenshot and receiving the result. Define a view model with a @Published var screenshotState: ScreenshotState enum with cases for idle, loading, loaded(UIImage), and failed(Error). The collection view cell observes the state publisher and updates its display accordingly: showing a placeholder skeleton during the loading state, displaying the UIImage on a successful load, and showing a retry button or error icon on failure. This pattern keeps the loading state tightly bound to the cell view model rather than scattered across the view layer.

Screenshot API on macOS with SwiftUI

macOS SwiftUI applications use the same SnapAPI Swift integration pattern as iOS, with minor adjustments for the AppKit-based display layer. Replace UIImage with NSImage in the image conversion step and use Image(nsImage:) in SwiftUI views. macOS applications have more lenient memory constraints than iOS, so the NSCache eviction policy can be configured with a higher countLimit to retain more screenshot thumbnails in memory. macOS applications running as menu bar extras or in the background can use URLSession background download tasks to fetch screenshots without requiring the app to be in the foreground, delivering the result through the URLSessionDownloadDelegate callback when the download completes.

Swift Package Manager projects can include the SnapAPI Swift SDK as a package dependency by adding the repository URL to the project's Package.swift file or using the Xcode package dependency UI. The SDK wraps the URLSession networking code described in this guide into a clean, tested client class with a full Swift async/await interface, automatic retry logic for transient network errors, and comprehensive error types. Using the SDK removes the need to maintain the networking boilerplate in your own codebase and ensures compatibility with future SnapAPI API changes. The SDK is open source at github.com/Sleywill/snapapi-swift and welcomes contributions from the Swift community.

Screenshot API in Swift with Background App Refresh

iOS applications that use background app refresh to update screenshot thumbnails while the app is not in the foreground can call SnapAPI through URLSession background download tasks. Background URLSession tasks continue executing even after the user switches away from the app, delivering results through the URLSessionDelegate methods when downloads complete. Configure a background URLSessionConfiguration with an identifier string, create a URLSession with that configuration, and submit download tasks using the background session rather than the shared session. When a background download completes, the system relaunches the app in the background if needed and calls the URLSession delegate's urlSession(_:downloadTask:didFinishDownloadingTo:) method with a temporary file URL containing the downloaded PNG screenshot. Move the file to a permanent location in the application's documents directory before the delegate method returns.

Screenshot API Swift Package Integration

Swift Package Manager projects that need screenshot functionality can add the SnapAPI Swift SDK as a remote package dependency. In Xcode, navigate to File, Add Packages, and enter the repository URL https://github.com/Sleywill/snapapi-swift. Select the version rule appropriate for your project — Up to Next Major Version is recommended to receive bug fixes and minor improvements automatically while avoiding breaking API changes. After the package resolves, import SnapAPISwift in any Swift source file that needs screenshot functionality and initialize a SnapAPIClient with your API key. The SDK provides a clean async throws function interface for screenshot, scrape, and extract operations with Swift-native error types that integrate cleanly with Swift's structured concurrency and error propagation mechanisms.

Screenshot API in Swift Widget Extension

iOS and macOS widget extensions written in SwiftUI can display screenshot thumbnails generated by SnapAPI. Widget extensions run in a separate process from the main app with limited network access, but URLSession data tasks are available in widget timeline providers. Implement a TimelineProvider that fetches a screenshot from SnapAPI in the getTimeline function using URLSession, converts the response data to a SwiftUI-displayable format, and creates timeline entries containing the image data. Store the fetched image data in the app group shared container using UserDefaults or a shared file, so the widget can access screenshots fetched by the main app without making redundant API calls from the widget process. Widgets refresh on a schedule set by the TimelineReloadPolicy, so batch the screenshot fetches during each refresh cycle to stay within the free plan's monthly quota.