Am construit o aplicație iOS în o săptămână și jumătate cu ajutorul lui Claude - de la concept la App Store Review

Aceasta este povestea modului în care am transformat o problemă reală de business într-o aplicație iOS completă, publicată în App Store, în mai puțin de două săptămâni. Cu puțin cod scris de mână, mult dialog cu un AI și o grămadă de cafea.

Problema

Dacă ai o firmă în România și ai angajați care folosesc mașini de serviciu, știi prea bine despre ce e vorba: foaia de parcurs. Acel document pe care șoferii trebuie să îl completeze pentru fiecare deplasare - dată, rută, kilometri, scopul deplasării, consum de carburant - și care la finalul lunii trebuie să ajungă semnat pe biroul contabilului.

Ani de zile, foile de parcurs au însemnat hârtie, pixuri uitate în torpedou, rânduri scrise de mână la 40 de grade în mașina parcată în soare. Sau, în cel mai bun caz, un fișier Excel shared pe WhatsApp care era mereu out of sync și pe care nimeni nu știa care e versiunea finală.

Eu am vrut să rezolv asta. Nu cu o soluție Enterprise cu licențe pe utilizator și module de implementat. Ci cu o aplicație simplă, modernă, pentru iPhone, pe care orice angajat o poate instala în 30 de secunde și care generează automat raportul lunar în format PDF - gata de semnat.

Ideea se numește Rolog.

De ce să construiesc singur, și de ce cu AI?

Sunt developer de 20 de ani, dar nu am experiență în iOS nativ. Am mai scris Swift, am mai pipăit SwiftUI, dar să construiesc o aplicație completă, cu autentificare, abonamente prin App Store, backup în iCloud, generare de PDF-uri și o experiență de utilizare coerentă - singur, de la zero - ar fi durat luni de zile.

Alternativa: să încerc cu Claude.

Știam de Claude Code - tool-ul de coding al Anthropic, disponibil direct în Xcode prin integrarea nativă. Am decis să dau o șansă. Nu ca să scrie cod pe care să îl copiez. Ci ca partener de dezvoltare - cineva (ceva?) cu care să vorbesc despre arhitectură, să generez componente, să depanez probleme și să evoluez aplicația iterativ.

Ce a urmat a depășit orice așteptare.

Cum a început colaborarea

Primele zile au funcționat în modelul clasic de chat din Xcode - scriam o cerință, Claude genera cod, eu îl analizam, îl ajustam, puneam întrebări. Era util, dar procesul era relativ lent: trebuia să copiez manual cod in unele situatii, să explic contextul de fiecare dată datorita limitarii de token-uri per conversatie, să gestionez eu coerența dintre fișiere.

Totul s-a schimbat când Apple a lansat suportul pentru agenți în integrarea Claude din Xcode.

Dintr-o dată, Claude nu mai era un chatbot care generează text. Era un agent care putea să:

citească fișierele din proiect direct, fără să îi explic contextul

modifice mai multe fișiere simultan, menținând coerența între ele

ruleze diagnostice și să vadă erorile de compilare în timp real

navigheze structura proiectului ca un developer care cunoaște codebase-ul

Saltul în calitate, precizie și viteză a fost imediat și dramatic. Nu mai repetam contextul. Claude știa ce am construit, cum am construit, ce convenții am folosit. Conversațiile au devenit mai dense, mai tehnice, mai productive.

Ce am construit: Rolog în detaliu

Rolog este o aplicație iOS pentru managementul foilor de parcurs ale vehiculelor de companie. Iată ce face:

Călătorii

Utilizatorul înregistrează fiecare deplasare: locație de start, locație de final, dată, scopul deplasării. Aplicația calculează automat distanța pe baza rutei reale (prin MapKit), calculează consumul de carburant și costul aferent. Fiecare călătorie e stocată local, instant disponibilă.

Flotă de vehicule

Fiecare mașină e configurată cu: marcă, număr de înmatriculare, tip de motor (combustie internă, electric, hibrid, LPG), consum mediu la suta de kilometri (sau kWh/100km pentru electrice). Aplicația suportă mai multe mașini, fiecare cu propriul jurnal de călătorii.

Șoferi

Profiluri individuale pentru șoferi, cu statistici automate: total călătorii, total kilometri, cost total, medie lunară, mașina preferată. Statisticile se actualizează automat la fiecare călătorie salvată.

Locații salvate

Un sistem de bookmarks pentru locațiile folosite frecvent. Adresa e validată prin geocoding, coordonatele GPS sunt stocate, și locațiile pot fi marcate ca favorite. Când schimbi numele unei locații, toate călătoriile existente care o referențiază se actualizează automat.

Rapoarte PDF

Probabil cea mai valoroasă funcționalitate. La finalul lunii, selectezi mașina și luna, apeși un buton, și aplicația generează un PDF profesional - "Foaia de Parcurs" - cu:

- Antetul companiei (nume, CUI, adresă, logo)

- Tabelul tuturor deplasărilor lunare (dată, rută, scop, km, cost)

- Sumarul: total km, consum total, cost total

- Câmpuri pentru semnături (șofer și responsabil)

Raportul poate fi generat în română sau engleză, independent de limba aplicației. Îl distribui direct din aplicatie prin Share Sheet - email, AirDrop, WhatsApp, orice.

Autentificare și conturi

Aplicația are un sistem complet de autentificare: înregistrare cu email, verificare prin cod de 5 cifre trimis pe email, resetare parolă prin coduri de recuperare. Totul construit custom, cu stocare securizată în Keychain și hashing al parolelor.

Backup în iCloud

Datele sunt salvate intr-un backup automat în iCloud. Există un mecanism sofisticat de restore care include o zonă de staging - fișierele sunt descărcate din iCloud înainte ca baza de date SwiftData să fie deschisă, garantând integritatea datelor la restaurare.

Abonamente

Modelul de business: 30 de zile trial gratuit cu limite (5 călătorii, 1 mașină, 1 șofer, 5 locații), apoi abonament lunar sau anual prin App Store. Gestionat prin StoreKit 2 - API-ul modern al Apple pentru subscriptions, cu suport pentru grace periods și transaction verification.

Stack-ul tehnic

Toată aplicația e scrisă în Swift, exclusiv cu framework-uri Apple:

Componentă

Tehnologie

UI SwiftUI
Bază de date SwiftData
Hărți și rute MapKit + CoreLocation
Abonamente StoreKit 2
Generare PDF UIGraphics (Core Graphics)
Vizualizare PDF PDFKit
Backup iCloud / CloudKit
Email Resend API
Stocare sigură Keychain

 

Am ales SwiftData în loc de CoreData pentru că e mai modern și se integrează natural cu SwiftUI prin macro-uri (`@Query`, `@Model`). A fost o decizie corectă - codul de acces la date e curat și declarativ.

StoreKit 2 (API-ul async/await pentru subscriptions) a fost la fel o alegere bună. Față de vechiul StoreKit, e dramatic mai simplu de implementat și de înțeles.

Generarea PDF-ului a fost cel mai complex punct tehnic. Am evitat librăriile terțe și am folosit direct UIGraphics - context Core Graphics, text layout manual, clipping circular pentru logo-uri, paginare automată pentru luni cu multe deplasări. Rezultatul e un PDF cu aspect profesional, generat nativ, fără dependențe externe.

Cum a arătat colaborarea cu Claude în practică

Lăsați-mă să fiu cât mai explicit despre cum am lucrat împreună, pentru că cred că e cea mai interesantă parte a acestei povești.

Arhitectura, de la zero

La început, nu știam exact cum să structurez aplicația. Am discutat cu Claude despre modele de date - cum să relațonez `User`, `Trip`, `Car`, `Driver`, `Location`. Claude a sugerat să folosesc relații bilaterale în SwiftData cu reguli de delete cascade, să separ statisticile șoferilor într-un model dedicat (`DriverStatistics`) care se recalculează automat, să folosesc pattern-ul `@Observable` (nou în Swift 5.9) în loc de `@StateObject` pentru servicii.

Nu am acceptat toate sugestiile orbit. Am avut dezbateri reale. De exemplu, am discutat mult despre cum să gestionez calculul distanței - dacă să îl fac la salvarea călătoriei sau live. Am ajuns împreună la concluzia că distanța calculată prin MapKit trebuie stocată, nu recalculată, pentru că rutele se pot schimba în timp.

Cerinte complexe implementate rapid

Unele funcționalități pe care le-aș fi evitat sau le-aș fi simplificat dacă lucram singur, cu Claude au fost implementate cu usurinta:

Sistemul de backup iCloud cu staging - Claude a propus și implementat mecanismul de a descărca fișierele de backup într-o zonă temporară înainte de a le aplica, tocmai pentru a evita coruperea bazei de date.

Statisticile șoferilor în timp real - `DriverStatisticsService` recalculează automat toate metricele la fiecare modificare a călătoriilor. Implementarea e corectă din punct de vedere al thread-safety (`@MainActor`), ceva ce aș fi probabil greșit singur.

Sistemul de email cu template-uri HTML - emailurile de verificare a contului și de resetare a parolei au template-uri HTML responsive cu branding-ul aplicației, generate dinamic, în română sau engleză în funcție de preferințele utilizatorului.

Debug eficient

Câteva exemple de sesiuni de debugging cu Claude:

La un moment dat, tab bar-ul aplicației apărea cu un fundal întunecat în loc să fie transparent, dar numai pe anumite ecrane. Era un comportament legat de `scrollEdgeAppearance` în iOS - un detaliu subtil pe care l-am depanat împreună, ajungând la soluția corectă: orice view care nu e un `ScrollView` sau `List` trebuie să fie învelit într-un `ScrollView` pentru ca iOS să aplice aspectul corect al tab bar-ului.

Un alt exemplu: localizarea în română nu se aplica în anumite componente dropdown, deși toate string-urile existau în fișierele `.strings`. Problema era că un `PickerCard` avea două overload-uri - unul cu `String` și unul cu `LocalizedStringKey` - și codul transmitea `String` explicit, ocolind localizarea. Fix simplu odată identificat, dar greu de văzut fără cineva care cunoaște tot codul.

Iterație rapidă pe UI/UX

Designul aplicației a evoluat iterativ. Am ales o temă intunecata cu gradienturi subtile și componente "glass" (efect de sticlă mat). Claude a implementat componentele de bază (`GlassCard`, `DarkThemeBackground`, `ThemeColors`) pe care le-am rafinat împreună de-a lungul mai multor sesiuni.

Când ceva nu arăta cum voiam, descriam problema, Claude propunea o soluție, o vedeam pe telefon, dădeam feedback. Ciclul dura minute, nu ore.

Momentele dificile

Nu a fost totul perfect. Câteva momente care m-au pus la încercare:

Înălțimea calculată greșit pentru empty states

Aplicația are ecrane de "empty state" - ce se afișează când nu ai nicio mașină, niciun șofer, etc. Acestea foloseau o valoare `screenHeight` capturată printr-un `GeometryReader` pe `.background` al `NavigationStack`, citită în `onAppear`. Problema: la prima afișare, valoarea era `600` (hardcodat implicit), iar la a doua afișare era valoarea reală. Rezultat: înălțimea elementelor era variabila între prima și a doua vizită pe un tab.

Soluția: mutatul `GeometryReader` în interiorul `NavigationStack`, ca prim child direct, în loc de pe `.background`. Astfel, `geo.size.height` e disponibil sincron, din primul render.

Sincronizarea numelor de locații în călătorii

Când schimbai numele unei locații salvate, schimbarea nu se reflecta în călătoriile care o foloseau. Asta pentru că `Trip` stochează `startLocationName` și `endLocationName` ca string-uri copiate la momentul creării - o decizie de arhitectură care are sens (nu vrei ca rapoartele istorice să se schimbe retroactiv), dar care înseamnă că redenumirea explicită trebuie propagată manual. Am adăugat un `.onChange(of: location.name)` în `EditLocationView` care actualizează toate călătoriile asociate.

StoreKit în sandbox

Testarea abonamentelor prin StoreKit e notorie pentru că e complicată în development. Am creat o funcție `setDemoUserSubscriptionState(modelContext:forceActive:)` care manipulează direct starea abonamentului în baza de date pentru testare rapidă.

Timeline: De la concept la App Store

Iată cum a arătat calendarul real:

Ziua 1: Concept, modele de date, arhitectură de bază cu Claude

Ziua 2: Autentificare, înregistrare, verificare email

Ziua 3: Trip management, calculul distanței cu MapKit

Ziua 4: Fleet și driver management, statistici

Ziua 5: Locații cu geocoding și autocomplete de adrese

Ziua 6: Generarea rapoartelor PDF - cea mai complexă zi

Ziua 7: Abonamente cu StoreKit 2

Ziua 8: iCloud backup și restore

Ziua 9: Rafinare UI, localizare completă în română și engleză, bugfixing

Ziua 10: Pregătire pentru App Store (screenshots, descrieri, review guidelines) și submit în App Store.

O săptămână și jumătate de la o pagină goală la o aplicație completă, cu toate funcționalitățile descrise mai sus, supusă review-ului Apple.

Ce am învățat despre lucrul cu un AI agent

Câteva observații după această experiență, pentru cine vrea să reproducă ceva similar:

Contextul e totul. Diferența dintre Claude ca chatbot și Claude ca agent a fost uriașă tocmai pentru că agentul are acces la context - știe codul, vede erorile, înțelege structura proiectului. Nu mai repeți. Nu mai explici de la zero la fiecare sesiune.

Trebuie să știi ce vrei. Claude e extrem de bun la cum, dar ce trebuie să stii tu. Nu l-am lăsat niciodată să decidă singur ce funcționalitate să implementeze. Îi spuneam clar ce trebuie făcut, de ce, și care sunt constrângerile. Cu cât promptul e mai precis, cu atât rezultatul e mai bun.

Verifică tot. Nu am acceptat niciodată cod fără să îl înțeleg. Dacă ceva părea ciudat, întrebam. Dacă o soluție mi se părea prea complexă, ceream una mai simplă. Agentul greșește, propune soluții suboptimale, uneori îmbunătățește lucruri nesolocitate. Trebuie să fii prezent.

Iterația e o super putere. Nu am construit nimic mare dintr-o dată. Cerințele complexe au apărut în 3-4 iterații, fiecare mai rafinată decât precedenta. Generarea PDF-ului a trecut prin cel puțin 5 versiuni până a arătat cum voiam.

Înregistrează deciziile de arhitectură. Câteva dintre problemele pe care le-am rezolvat ulterior veneau din decizii de arhitectură luate la început pe care le uitasem. Acum încerc să documentez mai bine de ce s-a ales o anumită abordare, nu doar cum.

Unde e Rolog acum

La momentul scrierii acestui articol, Rolog e în starea Waiting for Review la Apple. Aștept să fie aprobat în zilele următoare.

Va fi disponibil în App Store România cu un free trial gratuit de 30 de zile. Dacă ai o firmă, dacă ai angajați cu mașini de serviciu, dacă ești obosit de foile de parcurs pe hârtie sau de Excel-uri haotice - Rolog e pentru tine.

Concluzie

Am construit o aplicație iOS complexă, cu zeci de funcționalități, în o săptămână și jumătate. Nu aș fi putut face asta singur - nu în acest timp, și probabil nu la această calitate.

Colaborarea cu Claude, mai ales în forma de agent cu acces direct la proiect, a schimbat fundamental modul în care mă raportez la construirea de software. Nu e vorba despre a lăsa AI-ul să scrie cod în locul tău. E vorba despre a lucra cu un partener care nu obosește, nu uită contextul, și poate executa rapid - în timp ce tu rămâi cel care decide, validează și înțelege.

Rolog e construit cu SwiftUI, SwiftData, MapKit, StoreKit 2 și mult ajutor de la Claude. Dacă ai întrebări despre orice aspect tehnic din acest articol, scrie-mi la contact@lorinbute.com.