Developing a Mobile App for Photo Printing
Photo printing app is one of few mobile products where physical and digital intersect directly: user selects photo on smartphone, and in 2–5 days holds finished print. Technically means working with full-size files (HEIC, RAW conversion, color proof), complex editor UI, and typography API integration.
Working with Images: HEIC and Color Proof
iPhone defaults to HEIC (High Efficiency Image Container). Most print shops accept only JPEG. Convert on device or server:
import Photos
import ImageIO
func convertHEICtoJPEG(asset: PHAsset, completion: @escaping (Data?) -> Void) {
let options = PHImageRequestOptions()
options.deliveryMode = .highQualityFormat
options.isNetworkAccessAllowed = true
options.isSynchronous = false
PHImageManager.default().requestImage(
for: asset,
targetSize: PHImageManagerMaximumSize,
contentMode: .aspectFit,
options: options
) { image, info in
guard let cgImage = image?.cgImage else {
completion(nil)
return
}
let mutableData = NSMutableData()
guard let destination = CGImageDestinationCreateWithData(
mutableData, kUTTypeJPEG as CFString, 1, nil
) else {
completion(nil)
return
}
let jpegOptions: [CFString: Any] = [
kCGImageDestinationLossyCompressionQuality: 0.95,
// Keep sRGB for predictable color proof in print shop
kCGImagePropertyColorModel: kCGImagePropertyColorModelRGB
]
CGImageDestinationAddImage(destination, cgImage, jpegOptions as CFDictionary)
CGImageDestinationFinalize(destination)
completion(mutableData as Data)
}
}
Minimum resolution for quality 10×15 cm print—1200×900 px at 300 DPI. App should warn if photo too small:
func checkPrintQuality(image: UIImage, printSizeInches: CGSize) -> PrintQuality {
let requiredWidth = printSizeInches.width * 300
let requiredHeight = printSizeInches.height * 300
return image.size.width >= requiredWidth && image.size.height >= requiredHeight
? .good
: image.size.width >= requiredWidth * 0.7 ? .acceptable : .poor
}
Editor: Crop and Correction
Editor—central screen. For basic ops (crop, rotate, brightness, contrast) use iOS Core Image + CIFilter:
func applyBrightnessContrast(
to image: CIImage,
brightness: Float,
contrast: Float
) -> CIImage {
guard let filter = CIFilter(name: "CIColorControls") else { return image }
filter.setValue(image, forKey: kCIInputImageKey)
filter.setValue(brightness, forKey: kCIInputBrightnessKey)
filter.setValue(contrast, forKey: kCIInputContrastKey)
return filter.outputImage ?? image
}
On Android—Coil + android.graphics.ColorMatrix for basics, GPUImage library for complex.
Crop to print formats (10×15, 15×20, 20×30, square)—must support fixed aspect ratio with smooth crop animation.
File Upload to Print Shop
Photos 3–8 MB each. Order 30+ shots—100–250 MB. Upload with pause/resume capability:
// Android: WorkManager for reliable background upload
class PhotoUploadWorker(
context: Context,
params: WorkerParameters
) : CoroutineWorker(context, params) {
override suspend fun doWork(): Result {
val photoIds = inputData.getStringArray("photoIds") ?: return Result.failure()
val orderId = inputData.getString("orderId") ?: return Result.failure()
photoIds.forEachIndexed { index, photoId ->
val photo = photoRepository.getById(photoId)
val success = uploadPhoto(photo, orderId)
if (!success) return Result.retry()
setProgress(workDataOf(
"uploaded" to index + 1,
"total" to photoIds.size
))
}
return Result.success()
}
}
// Run with Constraints—only on Wi-Fi for large orders
val constraints = Constraints.Builder()
.setRequiredNetworkType(NetworkType.UNMETERED)
.build()
Print Shop Integration
Print shop APIs—usually REST with multipart/form-data for file uploads and separate endpoints for order creation. Common providers: Cewe API, Fujifilm API, private printers with custom REST.
After all files uploaded and order created—app shows status: "Processing" → "Sent to Print" → "Shipped" (with tracking number).
Timeline Estimates
Basic version (photo selection, crop, upload, payment, order history): 5–8 weeks. Advanced editor (filters, text, collages)—another 3–4 weeks. Pricing is calculated individually.







