iOS 27 introduces Product Page Headers and a centralized Asset Library in App Store Connect, letting developers display custom marketing images and videos at the top of their product page and in Search Results โ separate from screenshots and previews.
โข Product Page Headers give your app a branded, cinematic first impression before users even scroll to screenshots, directly influencing conversion.
โข The new Asset Library lets you pre-approve creative assets and swap them in real-time (e.g. seasonal campaigns) without a new app submission or App Review cycle.
โข Search Result visuals can now be a custom image or video instead of a default screenshot, making your listing stand out in competitive keyword searches and Apple Ads.
Demonstrates how to upload a creative asset (marketing image) to the App Store Connect Asset Library using the App Store Connect REST API via URLSession in Swift, which is the programmatic path for automating header and Search Result visuals.
import Foundation
// MARK: - App Store Connect API: Upload Creative Asset to Asset Library
// Requires a valid App Store Connect API JWT token and an existing App (appId)
struct AppStoreConnectConfig {
let baseURL = URL(string: "https://api.appstoreconnect.apple.com/v1")!
let jwt: String // Generated from your private key, key ID, and issuer ID
let appId: String
}
// Step 1: Reserve a creative asset upload slot
func reserveCreativeAsset(
config: AppStoreConnectConfig,
fileName: String,
fileSize: Int,
assetType: String = "MARKETING_IMAGE"
) async throws -> (reservationId: String, uploadOperations: [[String: Any]]) {
let url = config.baseURL.appendingPathComponent("creativeAssets")
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue("Bearer \(config.jwt)", forHTTPHeaderField: "Authorization")
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
let body: [String: Any] = [
"data": [
"type": "creativeAssets",
"attributes": [
"fileName": fileName,
"fileSize": fileSize,
"assetType": assetType,
"assetDeliveryState": "AWAITING_UPLOAD"
],
"relationships": [
"app": [
"data": ["type": "apps", "id": config.appId]
]
]
]
]
request.httpBody = try JSONSerialization.data(withJSONObject: body)
let (data, _) = try await URLSession.shared.data(for: request)
let json = try JSONSerialization.jsonObject(with: data) as! [String: Any]
let responseData = json["data"] as! [String: Any]
let id = responseData["id"] as! String
let attributes = responseData["attributes"] as! [String: Any]
let operations = attributes["uploadOperations"] as! [[String: Any]]
return (id, operations)
}
// Step 2: Upload bytes to the pre-signed URL returned in uploadOperations
func uploadAssetChunk(operation: [String: Any], imageData: Data) async throws {
guard let urlString = operation["url"] as? String,
let uploadURL = URL(string: urlString) else { return }
var request = URLRequest(url: uploadURL)
request.httpMethod = operation["method"] as? String ?? "PUT"
if let headers = operation["requestHeaders"] as? [[String: String]] {
for header in headers {
if let name = header["name"], let value = header["value"] {
request.setValue(value, forHTTPHeaderField: name)
}
}
}
let offset = operation["offset"] as? Int ?? 0
let length = operation["length"] as? Int ?? imageData.count
let chunk = imageData.subdata(in: offset..<(offset + length))
_ = try await URLSession.shared.upload(for: request, from: chunk)
}
// Step 3: Confirm upload complete and submit for review
func confirmAndSubmitAsset(config: AppStoreConnectConfig, reservationId: String) async throws {
let url = config.baseURL.appendingPathComponent("creativeAssets/\(reservationId)")
var request = URLRequest(url: url)
request.httpMethod = "PATCH"
request.setValue("Bearer \(config.jwt)", forHTTPHeaderField: "Authorization")
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
let body: [String: Any] = [
"data": [
"type": "creativeAssets",
"id": reservationId,
"attributes": ["assetDeliveryState": "COMPLETE"]
]
]
request.httpBody = try JSONSerialization.data(withJSONObject: body)
_ = try await URLSession.shared.data(for: request)
print("Creative asset \(reservationId) confirmed and queued for App Review.")
}
Creative assets submitted through the Asset Library flow are reviewed independently of your app binary, so approval timelines may differ. Real-time header swaps (without a version submission) are only possible once assets are pre-approved in the Asset Library. Video assets for headers and Search Results have specific dimension and duration requirements set by App Store Connect โ check the latest guidelines before encoding.
Product Page Header and Search Result creative assets are configured via App Store Connect and the App Store Connect API โ no device hardware constraints, but assets must pass App Review.
More iOS 27 APIs land every week.
Get notified when new capabilities are published โ no noise, just signal.