π± Complete App Clips development toolkit for iOS with instant experiences
Features β’ Installation β’ Quick Start β’ Components β’ Documentation
- Why AppClipsStudio?
- Features
- Requirements
- Installation
- Quick Start
- Components
- Documentation
- Contributing
- License
- Star History
Building App Clips requires managing invocation URLs, handling deep links, creating lightweight experiences, and ensuring <10MB size. AppClipsStudio handles all the complexity.
// Define your App Clip in seconds
@main
struct MyAppClip: AppClip {
var body: some Scene {
AppClipScene { invocation in
switch invocation.experience {
case .orderFood(let restaurantId):
OrderView(restaurant: restaurantId)
case .rentBike(let stationId):
BikeRentalView(station: stationId)
}
}
}
}| Feature | Description |
|---|---|
| π Quick Setup | App Clip target in minutes |
| π Smart Invocations | NFC, QR, Safari, Maps, Messages |
| π Location Verification | Confirm user is at location |
| π³ Apple Pay Ready | One-tap payments |
| π Sign in with Apple | Seamless authentication |
| π¦ Size Optimizer | Keep under 10MB limit |
| π§ͺ Testing Tools | Local & TestFlight testing |
| π Analytics | Clip performance metrics |
dependencies: [
.package(url: "https://github.com/muhittincamdali/AppClipsStudio.git", from: "1.0.0")
]# Using CLI
appclipstudio init --name "MyAppClip" --bundleId "com.myapp.clip"Or in Xcode: File β New β Target β App Clip
import AppClipsStudio
enum ClipExperience: AppClipExperience {
case orderFood(restaurantId: String)
case viewMenu(restaurantId: String)
case payBill(tableId: String)
static func parse(from url: URL) -> ClipExperience? {
guard let components = URLComponents(url: url, resolvingAgainstBaseURL: true) else {
return nil
}
switch components.path {
case "/order":
return .orderFood(restaurantId: components.queryValue("id") ?? "")
case "/menu":
return .viewMenu(restaurantId: components.queryValue("id") ?? "")
case "/pay":
return .payBill(tableId: components.queryValue("table") ?? "")
default:
return nil
}
}
}struct OrderView: View {
let restaurantId: String
@StateObject private var viewModel = OrderViewModel()
var body: some View {
VStack {
RestaurantHeader(id: restaurantId)
MenuList(items: viewModel.menuItems)
AppClipPayButton(amount: viewModel.total) {
await viewModel.checkout()
}
}
.appClipOverlay() // Shows "Get the Full App" banner
}
}// Generate NFC payload
let nfcData = AppClipNFC.generate(
url: "https://myapp.com/order?id=123",
experience: .orderFood
)
// Write to NFC tag
try await NFCWriter.write(nfcData, to: tag)// Generate App Clip QR code
let qrCode = AppClipQR.generate(
url: "https://myapp.com/menu?id=456",
size: CGSize(width: 200, height: 200),
style: .appClipCode // Special Apple design
)
Image(uiImage: qrCode)// Generate official App Clip Code
let clipCode = try await AppClipCode.request(
url: "https://myapp.com/experience",
style: .camera, // or .nfc
color: .blue
)<meta name="apple-itunes-app"
content="app-id=123456789,
app-clip-bundle-id=com.myapp.clip,
app-clip-display=card">// Register location-based experience
AppClipLocation.register(
experience: .orderFood,
coordinate: CLLocationCoordinate2D(latitude: 37.33, longitude: -122.03),
radius: 100 // meters
)One-tap Apple Pay:
AppClipPayButton(
amount: 29.99,
currency: .usd,
label: "Order Coffee"
) { result in
switch result {
case .success(let payment):
await processPayment(payment)
case .failure(let error):
showError(error)
}
}Seamless authentication:
AppClipSignInButton { result in
switch result {
case .success(let credential):
await createAccount(credential)
case .failure(let error):
showError(error)
}
}Confirm user location:
LocationVerification(
coordinate: restaurantLocation,
radius: 50
) { verified in
if verified {
showOrderOptions()
} else {
showLocationError()
}
}Promote full app:
ContentView()
.appClipOverlay(
title: "Get the Full Experience",
subtitle: "Download MyApp for rewards & history",
action: .openAppStore
)App Clips must be under 10MB. AppClipsStudio helps:
// Analyze bundle size
let analysis = try await BundleAnalyzer.analyze()
print("Current size: \(analysis.totalSize)")
print("Limit: 10MB")
for item in analysis.largestAssets {
print("\(item.name): \(item.size)")
}
// Recommendations
for suggestion in analysis.optimizations {
print(suggestion)
}AppClipOptimizer.configure {
$0.compressImages = true
$0.stripUnusedCode = true
$0.minimizeAssets = true
$0.useThinBinary = true
}// Test invocation locally
AppClipTester.simulate(
url: "https://myapp.com/order?id=test123",
location: .mock(latitude: 37.33, longitude: -122.03)
)// Configure for TestFlight
AppClipConfig.testFlight {
$0.invocationURL = "https://myapp.com/test"
$0.mockLocation = true
}# Run with invocation URL
appclipstudio run --url "https://myapp.com/order?id=123"// Track clip performance
AppClipAnalytics.track(.clipLaunched(experience: .orderFood))
AppClipAnalytics.track(.paymentCompleted(amount: 29.99))
AppClipAnalytics.track(.fullAppPromoted)
// Get insights
let metrics = await AppClipAnalytics.getMetrics()
print("Launches: \(metrics.launches)")
print("Conversions: \(metrics.fullAppInstalls)")
print("Revenue: \(metrics.totalRevenue)")Transfer data to full app:
// In App Clip
AppClipDataTransfer.save(
key: "orderHistory",
value: orderData
)
// In Full App
if let data = AppClipDataTransfer.retrieve(key: "orderHistory") {
importOrderHistory(data)
}// β
Good: Single focused task
struct BikeRentalClip: AppClip {
var body: some Scene {
AppClipScene { _ in
RentBikeFlow() // Just rental, nothing else
}
}
}
// β Avoid: Too many features// β
Good: Immediate content
struct QuickOrderClip: View {
var body: some View {
MenuView() // Shows immediately
.task {
await loadDetails() // Background
}
}
}// Show value proposition immediately
SplashView(
title: "Order in 30 Seconds",
subtitle: "No app download required",
action: "Start Order"
)# Initialize App Clip target
appclipstudio init
# Analyze bundle size
appclipstudio analyze
# Generate QR codes
appclipstudio qr --url "https://myapp.com/exp" --output qr.png
# Test invocation
appclipstudio test --url "https://myapp.com/order"
# Validate configuration
appclipstudio validateSee Examples:
- CoffeeShop - Order & pay
- BikeRental - Rent bikes
- Restaurant - View menu & pay bill
- Parking - Pay for parking
| Platform | Version |
|---|---|
| iOS | 17.0+ |
| Xcode | 16.0+ |
See CONTRIBUTING.md.
MIT License - see LICENSE.
Build instant experiences β‘