SwiftUI API Integration

Calling the SnapIt REST API directly inside SwiftUI using native URLSession and Swift Concurrency.

If you prefer not to include the binary size of the native SDK, you can issue direct API POST requests to our servers from SwiftUI views:

ContentView.swift
import SwiftUI

// 1. Define JSON Model Schemas
struct VTONRequest: Codable {
    let model_name: String
    let inputClothesImageUrls: [String]
}

struct VTONResponse: Codable {
    let status: String
    let outputImageUrl: String?
    let message: String?
}

// 2. Direct API Service Layer
class VTONService {
    static let shared = VTONService()
    
    func generateTryOn(garmentURL: String) async throws -> String {
        guard let url = URL(string: "https://apisdk.snapmydesign.com/api/v1/vton/generate") else {
            throw URLError(.badURL)
        }
        
        var request = URLRequest(url: url)
        request.httpMethod = "POST"
        request.setValue("smd_live_your_api_key_here", forHTTPHeaderField: "X-API-Key")
        request.setValue("application/json", forHTTPHeaderField: "Content-Type")
        
        let payload = VTONRequest(
            model_name: "medium",
            inputClothesImageUrls: [garmentURL]
        )
        request.httpBody = try JSONEncoder().encode(payload)
        
        let (data, response) = try await URLSession.shared.data(for: request)
        
        guard let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 else {
            throw URLError(.badServerResponse)
        }
        
        let result = try JSONDecoder().decode(VTONResponse::self, from: data)
        guard let outputURL = result.outputImageUrl else {
            throw NSError(domain: "VTON", code: 0, userInfo: [
                NSLocalizedDescriptionKey: result.message ?? "Generation failed"
            ])
        }
        return outputURL
    }
}

// 3. SwiftUI Component View
struct ContentView: View {
    @State private var resultURL: String?
    @State private var isLoading = false
    @State private var errorText: String?
    
    let garmentImageURL = "https://assets.url/dress.jpg"
    
    var body: some View {
        VStack(spacing: 24) {
            Text("SnapIt Direct API Try-On")
                .font(.headline)
            
            if isLoading {
                ProgressView("Running AI workers...")
            } else if let output = resultURL, let url = URL(string: output) {
                AsyncImage(url: url) { phase in
                    switch phase {
                    case .success(let image):
                        image.resizable().scaledToFit()
                    case .failure:
                        Text("Failed to render output image")
                    default:
                        ProgressView()
                    }
                }
                .frame(maxHeight: 350)
                .cornerRadius(12)
            } else {
                Text("Ready to generate try-on model")
                    .foregroundColor(.secondary)
            }
            
            if let error = errorText {
                Text(error).foregroundColor(.red).font(.caption)
            }
            
            Button(action: triggerTryOn) {
                Text("Generate Try-On")
                    .bold()
                    .frame(maxWidth: .infinity)
                    .padding()
                    .background(Color.blue)
                    .foregroundColor(.white)
                    .cornerRadius(10)
            }
            .disabled(isLoading)
        }
        .padding()
    }
    
    private func triggerTryOn() {
        isLoading = true
        errorText = nil
        
        Task {
            do {
                let url = try await VTONService.shared.generateTryOn(garmentURL: garmentImageURL)
                await MainActor.run {
                    self.resultURL = url
                    self.isLoading = false
                }
            } catch {
                await MainActor.run {
                    self.errorText = error.localizedDescription
                    self.isLoading = false
                }
            }
        }
    }
}