README.md 7232 octets

PicLead — iOS

Portage natif Swift/SwiftUI de l'application Android (voir le README racine pour la description fonctionnelle : scan de carte de visite → OCR local → brouillon éditable → carnet local → export .vcf, sync PicLead optionnelle).

Rebranding au même niveau qu'Android : nom affiché PicLead et bundle id fr.ebii.piclead (miroir de l'applicationId) ; le module, la cible Xcode et les identifiants internes restent Card2vcf/ card2vcf (miroir du namespace Android — clés, schéma, préfixe card2vcf:serverId= partagés avec l'existant).

Comme sur Android, l'OCR et le carnet restent 100 % sur l'appareil : aucune dépendance tierce, aucun service cloud — uniquement les frameworks Apple embarqués. Le réseau ne sert qu'à la sync optionnelle.

Build

Prérequis : Xcode 16+ (testé Xcode 26.6), XcodeGen.

cd ios
xcodegen generate
open Card2vcf.xcodeproj          # ou :
xcodebuild -project Card2vcf.xcodeproj -scheme Card2vcf \
  -destination 'platform=iOS Simulator,name=iPhone 17 Pro' build

Tests (mêmes suites que les tests JUnit Android, portées en XCTest) :

xcodebuild -project Card2vcf.xcodeproj -scheme Card2vcf \
  -destination 'platform=iOS Simulator,name=iPhone 17 Pro' test

Cible : iOS 16+, iPhone et iPad. Le .xcodeproj est généré (non versionné) ; la vérité est project.yml.

Correspondances Android ↔ iOS

FonctionAndroidiOS
Capture + correction perspectiveCameraX + OpenCVVisionKit VNDocumentCameraViewController (import photo : VNDetectRectanglesRequest + CIPerspectiveCorrection)
OCR (7 langues, on-device)Tesseract4AndroidVision VNRecognizeTextRequest (boucle d'orientation identique)
Structuration heuristiqueContactHeuristicParser (Kotlin)port Swift 1:1 (mêmes regex)
Carnet local + FTSRoom v3 + FTS4SQLite3 natif (acteur Swift) + FTS5 external-content, mêmes tables/colonnes
Import/Export VCFVCardParser/VCardSerializerports Swift 1:1 (vCard 3.0/4.0 en lecture, 3.0 en écriture)
Contact systèmeACTION_INSERTCNContactViewController (Contacts)
Stockage clé API chiffréeEncryptedSharedPreferences + Argon2id/XChaCha20Keychain + port Swift byte-compatible (Argon2id RFC 9106 + XChaCha20-Poly1305 via CryptoKit)
Client syncOkHttp + kotlinx-serializationURLSession + Codable (mêmes endpoints, mêmes JSON)
Pont AgendaCalendarProvider (calendriers locaux)EventKit (calendriers EKSource locaux)
Planification des tâches (jours ouvrés, teintes d'étiquette)planning/ (Kotlin, java.time.LocalDate)Planning/ (port Swift 1:1 + DateCivile, Foundation n'offrant pas de date sans fuseau)
UIJetpack ComposeSwiftUI (mêmes écrans, chaîne graphique design.md)

Structure

ios/
├── project.yml            # définition XcodeGen (2 cibles : app + tests)
├── Card2vcf/
│   ├── App/               # point d'entrée SwiftUI
│   ├── Contact/           # ContactCard, parseur heuristique, vCard
│   ├── CRM/               # normalisation, doublons, fusion, tri, abcédaire
│   ├── OCR/               # OcrResult, post-traitement, moteur Vision
│   ├── Planning/          # calendrier ouvré, teintes d'étiquette, aperçu de tâche
│   ├── Scan/              # caméra document, recadrage, pipeline scan→brouillon
│   ├── Data/              # entités, protocoles DAO, dépôt, images
│   │   └── Sqlite/        # implémentation SQLite3 + FTS5 (schéma Room v3)
│   ├── Sync/              # client Ailiance, moteur LWW, crypto, agenda EventKit
│   ├── Kanban/            # filtres kanban
│   └── UI/                # écrans SwiftUI (carnet, fiche, scan, projets, réglages)
└── Card2vcfTests/         # suites XCTest portées des tests JUnit

Écarts connus vs Android

  • Modèles hors-ligne téléchargeables (Android modeles/, saveurs fdroid/direct) : sans objet sur iOS, et c'est volontaire. L'OCR passe par Vision et la transcription par SFSpeechRecognizer : les modèles sont fournis et mis à jour par le système, il n'y a rien à télécharger, donc ni catalogue, ni dépôt de modèles, ni section « Modèles hors-ligne » dans les Réglages. Le chantier F-Droid répondait à une contrainte de taille d'APK (Tesseract + Vosk embarqués) qui n'existe pas ici.
  • Planification des tâches : portée (dates, durée en jours ouvrés, étiquettes colorées, sous-tâches, repère de retard, édition depuis la carte kanban). Restent hors périmètre des deux plateformes : diagramme de Gantt sur téléphone, édition des dépendances et du regroupement, réglage de l'échéance et du rythme du projet — tous réservés au web. Comme sur Android, la date de début se saisit en texte ISO, pas par un sélecteur graphique.
  • Langues OCR : Vision ne propose pas de correction linguistique pol/nld sur iOS 16 ; le texte latin reste reconnu (recognizer latin), sans correction spécifique à ces deux langues.
  • Note du contact système : le champ note de CNContact exige l'entitlement restreint com.apple.developer.contacts.notes — non renseigné à la création du contact système (la note reste dans le carnet Card2vcf).
  • Rotation manuelle de l'image de carte (bouton 90° Android) : non portée (le recadrage VisionKit redresse déjà la capture).
  • Migrations : additives depuis v3 (v3→v4 : colonne lastError sur sync_ops, miroir de Room MIGRATION_3_4) ; toute version plus ancienne reste réinitialisée (filet destructif, comme sur Android).
  • Prétraitement contraste (CLAHE) : non porté — spécifique Tesseract ; Vision gère nativement les cartes colorées. L'extraction spatiale (BlockGrouper, champs douteux) est portée ; la confiance Vision (0-1, ramenée à 0-100) est plus grossière que celle de Tesseract, le seuil « à vérifier » (70) peut donc marquer un peu plus large.
  • Refonte cosmétique Material 3 (coins arrondis, ChampTexte, chevrons de menus) : non répliquée à l'identique — iOS conserve son système éditorial carré ; la palette PicLead (menthe, rouge erreur, gris froids) et les signaux fonctionnels (pastilles de synchro, textes d'erreur, « À vérifier ») sont repris.
  • HTTP clair : refusé par défaut (ServerUrlPolicy), la case « Autoriser HTTP » reste nécessaire ; l'exception ATS globale du Info.plist n'est effective qu'une fois cette case cochée.
  • Source locale masquée par iCloud (sans équivalent Android, où le compte agenda local existe toujours) : quand iCloud Agenda est actif, iOS n'expose plus le stockage local. À l'activation d'une liaison, un dialogue laisse le choix : créer le calendrier dans le compte iCloud de l'utilisateur (synchronisé sur ses appareils), ou annuler (calendrier 100 % local possible en désactivant iCloud Agenda). findCalendar retrouve les calendriers des deux sources pour éviter les doublons au ré-appairage.