SDK Swift de conversion de fichiers#
Enconvert est le client EnConvert officiel pour Swift, distribué via Swift Package Manager. Il est bâti sur URLSession avec async/await, ne porte aucune dépendance externe, et cible Swift 5.9 et plus récent sur macOS 12, iOS 15, tvOS 15 et watchOS 8. Douze méthodes du client couvrent la conversion de fichiers et le rendu d'URL (DOCX vers PDF, HEIC vers WebP, URL vers PDF, URL vers Markdown, lots à l'échelle d'un site entier), et l'espace de noms client.v2 ajoute vingt-trois méthodes d'intelligence web pour percevoir, découvrir, rechercher, distiller, ingérer et surveiller des pages.
Enconvert · Source : conversionapi/swift-sdk · Swift : 5.9+ · Plateformes : macOS 12+, iOS 15+, tvOS 15+, watchOS 8+ · Dépendances : aucune
Installation#
Ajoutez le paquet, puis listez le produit dans la cible qui l'utilise :
dependencies: [
.package(url: "https://github.com/conversionapi/swift-sdk.git", from: "0.0.1")
],
targets: [
.target(name: "MyApp", dependencies: [
.product(name: "Enconvert", package: "swift-sdk")
])
]
Dans Xcode, utilisez File > Add Package Dependencies et collez https://github.com/conversionapi/swift-sdk.git. Sous Linux, le SDK importe FoundationNetworking de manière conditionnelle : rien de plus n'est nécessaire de votre côté.
Démarrage rapide#
import Enconvert
let apiKey = ProcessInfo.processInfo.environment["ENCONVERT_API_KEY"] ?? ""
let client = try Enconvert(apiKey: apiKey)
let result = try await client.convertUrlToPdf("https://example.com", options: UrlToPdfOptions(saveTo: "page.pdf"))
print(result.filename, result.presignedUrl)
Enconvert.init est throwing, pas failable : un apiKey vide lève EnconvertError.invalidArgument avant que quoi que ce soit ne touche au réseau. Chaque méthode de requête est async throws, et les méthodes de conversion sont marquées @discardableResult pour qu'un appel effectué uniquement pour son effet de bord saveTo ne produise pas d'avertissement.
Ce que le client expose#
Douze méthodes sont accrochées à Enconvert et correspondent 1:1 à des endpoints REST :
| Méthode | Endpoint | Renvoie |
|---|---|---|
convertUrlToPdf(_:options:) |
POST /v1/convert/url-to-pdf |
ConversionResult |
convertUrlToScreenshot(_:options:) |
POST /v1/convert/url-to-screenshot |
ConversionResult |
convertUrlToMarkdown(_:options:) |
POST /v1/convert/url-to-markdown |
ConversionResult |
convertImage(_:options:) |
POST /v1/convert/{from}-to-{to} |
ConversionResult |
convertDocument(_:options:) |
POST /v1/convert/{from}-to-{to} |
ConversionResult |
convertToMarkdown(_:options:) |
POST /v1/convert/anything-to-markdown |
ConversionResult |
convertToPdf(_:options:) |
POST /v1/convert/anything-to-pdf |
ConversionResult |
convertWebsiteToPdf(_:options:) |
POST /v1/convert/website-to-pdf |
BatchSubmission |
convertWebsiteToScreenshot(_:options:) |
POST /v1/convert/website-to-screenshot |
BatchSubmission |
getJobStatus(_:) |
GET /v1/convert/status/{jobId} |
JobStatus |
getBatchStatus(_:) |
GET /v1/convert/batch/{batchId} |
BatchStatus |
waitForBatch(_:options:) |
GET /v1/convert/batch/{batchId} (interrogé en boucle) |
BatchStatus |
client.v2 est un espace de noms EnconvertV2 qui contient vingt-trois méthodes supplémentaires réparties sur six groupes de capacités :
| Groupe | Méthodes | Chemin de base |
|---|---|---|
| Perceive | perceive, perceiveDirect, getPerceiveOperation, downloadPerceiveArtifact, perceiveBatch, getPerceiveBatch |
/v2/perceive |
| Discover | discover |
/v2/discover |
| Lookup | lookup |
/v2/lookup |
| Distill | distill |
/v2/distill |
| Ingest | ingest, ingestFiles, getIngestJob, listIngestJobs, cancelIngestJob, retryIngestWebhook, getWebhookSecret, rotateWebhookSecret |
/v2/ingest |
| Watch | createWatcher, listWatchers, getWatcher, getWatcherSnapshots, updateWatcher, deleteWatcher |
/v2/watch |
Les options sont passées sous forme de struct dont les paramètres d'initialisation ont des valeurs par défaut : UrlToPdfOptions() signifie donc « tout par défaut » et vous ne nommez que les champs qui vous intéressent. Swift exige des arguments étiquetés dans l'ordre de déclaration : gardez donc saveTo: avant singlePage: et pdfOptions: lorsque vous en définissez plusieurs à la fois.
Conversion de fichiers#
Les envois acceptent un FileInput :
| Cas | À utiliser pour |
|---|---|
.path("report.docx") |
Un fichier sur le disque. Le nom de base détermine le format d'entrée et le type MIME. |
.data(bytes) |
Des octets bruts sans nom. Envoyés en tant que upload.bin, application/octet-stream. |
.wrapped(data: bytes, filename: "report.docx", contentType: nil) |
Des octets bruts plus un nom de fichier explicite. Un contentType à nil est déduit de l'extension. |
convertUrlToPdf#
Effectue le rendu de n'importe quelle URL publique en PDF.
let result = try await client.convertUrlToPdf("https://example.com", options: UrlToPdfOptions(
viewportWidth: 1440,
saveTo: "report.pdf",
singlePage: false,
pdfOptions: PdfOptions(pageSize: "A4", orientation: .landscape)
))
| Option | Type | Défaut | Description |
|---|---|---|---|
viewportWidth, viewportHeight |
Int? |
1920, 1080 |
Taille de la fenêtre d'affichage du navigateur, en pixels. |
loadMedia, enableScroll |
Bool? |
true |
Attend les images et les vidéos ; fait défiler la page de haut en bas pour déclencher le chargement paresseux. |
outputFilename |
String? |
auto | Remplace le nom de fichier généré. |
auth, cookies, headers |
HttpBasicAuth?, [BrowserCookie]?, [String: String]? |
aucun | Identifiants HTTP Basic, cookies injectés (50 au maximum), en-têtes de requête supplémentaires (20 au maximum, hop-by-hop rejetés). |
saveTo |
String? |
aucun | Chemin local où écrire le PDF. Les répertoires parents sont créés. |
singlePage |
Bool? |
true |
true produit une seule page continue. false pagine en utilisant pdfOptions.pageSize. |
pdfOptions |
PdfOptions? |
aucun | Géométrie de page. Voir Options PDF. |
Les pages derrière une authentification acceptent des identifiants, des cookies ou des en-têtes :
_ = try await client.convertUrlToPdf("https://internal.example.com/report", options: UrlToPdfOptions(
auth: HttpBasicAuth(username: "user", password: "pass"),
cookies: [BrowserCookie(name: "session", value: "abc123", domain: "internal.example.com")],
headers: ["X-Tenant": "acme"],
saveTo: "report.pdf"
))
Ne combinez pas auth avec une entrée Authorization dans headers. L'API rejette le conflit.
convertUrlToScreenshot#
Capture un PNG de n'importe quelle URL.
let shot = try await client.convertUrlToScreenshot(
"https://example.com",
options: UrlToScreenshotOptions(viewportWidth: 1440, saveTo: "shot.png")
)
UrlToScreenshotOptions accepte les mêmes champs de fenêtre d'affichage, de médias, de défilement, de nom de fichier et d'accès navigateur que UrlToPdfOptions, sans singlePage ni pdfOptions.
convertUrlToMarkdown#
Extrait un Markdown GitHub-Flavored propre à partir d'une URL. Le convertisseur supprime la navigation, les pieds de page, les publicités et les scripts, conserve le corps principal de l'article, et ajoute en tête un frontmatter YAML avec le titre, la description, l'url, les liens et les images. Utile pour les pipelines RAG, pour importer du contenu tiers dans un CMS, ou pour générer des données d'entraînement.
_ = try await client.convertUrlToMarkdown("https://example.com/article", options: UrlToMarkdownOptions(saveTo: "article.md"))
convertImage#
Convertit entre jpeg, png, svg, heic et webp, ou rastérise un PDF en JPEG.
let result = try await client.convertImage(.path("photo.heic"), options: ConvertImageOptions(outputFormat: "webp", saveTo: "photo.webp"))
Le format d'entrée provient de l'extension du nom de fichier (.jpg, .jpeg, .png, .svg, .heic, .webp, et .pdf pour la rastérisation). outputFormat est obligatoire et accepte les alias jpg, yml, htm et md.
| Option | Type | Obligatoire | Description |
|---|---|---|---|
outputFormat |
String |
Oui | Format cible, par exemple "webp". |
saveTo |
String? |
non | Chemin local où écrire le résultat. |
outputFilename |
String? |
non | Remplace le nom de fichier généré. |
convertDocument#
Convertit des documents et des formats texte structurés. outputFormat vaut "pdf" par défaut.
// docx vers pdf
_ = try await client.convertDocument(.path("report.docx"), options: ConvertDocumentOptions(saveTo: "report.pdf"))
// json vers yaml
_ = try await client.convertDocument(.path("data.json"), options: ConvertDocumentOptions(outputFormat: "yaml", saveTo: "data.yaml"))
// markdown vers pdf avec une mise en page personnalisée
_ = try await client.convertDocument(.path("README.md"), options: ConvertDocumentOptions(
saveTo: "readme.pdf",
pdfOptions: PdfOptions(pageSize: "A4", margins: PdfMargins(top: 20, bottom: 20))
))
Entrées prises en charge : .doc, .docx, .xls, .xlsx, .ppt, .pptx, .html, .htm, .odt, .ods, .odp, .ots, .pages, .numbers, .md, .markdown, .csv, .json, .xml, .yaml, .yml, .toml. EPUB n'a pas de paire documentaire dédiée : faites plutôt passer les fichiers .epub par convertToPdf ou convertToMarkdown.
Le SDK embarque la table de conversion complète de la passerelle et valide chaque paire {input}-to-{output} en local : une paire non prise en charge lève donc EnconvertError.invalidArgument avec la liste des sorties valides, plutôt que de payer un aller-retour voué à l'échec. Il y a 43 paires implémentées :
| Entrée | Sorties |
|---|---|
json |
csv, toml, xml, yaml |
xml |
csv, json |
yaml |
json |
csv |
json, xml |
toml |
json |
markdown |
html, pdf |
html |
pdf |
doc, excel, ppt, odt, ods, odp, ots, pages, numbers |
pdf |
jpeg, png, svg, heic, webp |
chacun vers les quatre autres (20 paires ordonnées) |
pdf |
jpeg |
Vous pouvez interroger cette table vous-même sans effectuer de requête :
validOutputsFor("json") // ["csv", "toml", "xml", "yaml"]
IMPLEMENTED_CONVERSIONS.contains("heic-to-webp") // true
convertToMarkdown#
Convertit un document envoyé, de presque n'importe quel format, en Markdown propre, le format étant détecté automatiquement côté serveur. Une bonne première étape pour un pipeline RAG.
_ = try await client.convertToMarkdown(.path("handbook.docx"), options: ConvertToMarkdownOptions(saveTo: "handbook.md"))
PDF, DOCX, PPTX, XLSX, CSV, HTML, EPUB, TXT et MD, ainsi que les fichiers bureautiques hérités ou ODF sont acceptés. Les images ne le sont pas. Cet endpoint n'accepte aucune option PDF.
convertToPdf#
Convertit un fichier envoyé, de presque n'importe quel format, en PDF : bureautique, ODF, Pages, Numbers, RTF, CSV, HTML, Markdown, texte brut, images matricielles, SVG, EPUB, ou un PDF existant transmis tel quel et normalisé.
_ = try await client.convertToPdf(.path("slides.pptx"), options: ConvertToPdfOptions(saveTo: "slides.pdf"))
// PDF en passthrough, converti en niveaux de gris
_ = try await client.convertToPdf(
.path("scan.pdf"),
options: ConvertToPdfOptions(saveTo: "scan-gray.pdf", pdfOptions: PdfOptions(grayscale: true))
)
grayscale est pris en compte ici. convertToPdf transmet pdfOptions, mais l'endpoint anything-to-pdf lit grayscale et ignore le reste. Utilisez convertDocument ou convertUrlToPdf lorsque vous avez besoin de la taille de page, de l'orientation, des marges, de l'échelle, des en-têtes ou des pieds de page.
convertWebsiteToPdf et convertWebsiteToScreenshot#
Découvrez toutes les pages d'un site, convertissez-les en arrière-plan et récupérez une archive ZIP unique. Les deux méthodes sont exclusivement asynchrones et exigent une clé API privée disposant de l'accès à l'exploration.
let batch = try await client.convertWebsiteToPdf("https://example.com", options: WebsiteToPdfOptions(
crawlMode: .sitemap,
excludePatterns: ["/blog/tag/"]
))
print(batch.batchId, batch.urlCount, batch.discoveryMethod ?? "")
// Bloque jusqu'à ce que le lot se stabilise, puis enregistre le ZIP
let status = try await client.waitForBatch(batch.batchId, options: WaitForBatchOptions(saveTo: "site.zip"))
print("\(status.completed) of \(status.total) pages converted")
// Ou interrogez-le vous-même
let snapshot = try await client.getBatchStatus(batch.batchId)
if snapshot.status != .processing {
print(snapshot.zipDownloadUrl ?? "")
}
| Option | Type | Défaut | Description |
|---|---|---|---|
crawlMode |
CrawlMode? |
.auto |
.auto, .sitemap (sitemap.xml uniquement) ou .full (sitemap plus exploration en largeur). |
includePatterns, excludePatterns |
[String]? |
aucun | Liste d'autorisation puis liste d'exclusion pour les URL découvertes. Mode d'exploration complète. |
notificationEmail |
String? |
propriétaire du projet | Adresse notifiée à la fin du lot. |
callbackUrl |
String? |
aucun | Webhook appelé en POST à la fin du lot. |
singlePage, pdfOptions |
Bool?, PdfOptions? |
voir ci-dessus | Lots PDF uniquement. |
Les deux méthodes acceptent également les champs de fenêtre d'affichage, de médias, de défilement et d'accès navigateur listés sous convertUrlToPdf, appliqués à chaque page. waitForBatch interroge toutes les 5 secondes avec un délai de 30 minutes par défaut ; remplacez-les via WaitForBatchOptions(intervalMs:timeoutMs:saveTo:). Dépasser le délai lève EnconvertError.api(statusCode: 504, ...). convertWebsiteToScreenshot se comporte à l'identique et produit un ZIP de PNG.
Intelligence web (V2)#
Chaque lecture V2 porte un score renderQuality de 0.0 à 1.0, exposé sous forme de Double? sur PerceiveResult, PerceiveDirectResult, DistillItem et WatcherSnapshot. Un score faible signifie que la page n'a pas été rendue honnêtement : un défi anti-bot, un mur de connexion, un bandeau de cookies posé sur une coquille SPA vide, un statut d'erreur HTTP. Le contenu revient quand même, signalé, à côté d'un dictionnaire deductions qui nomme chaque pénalité déclenchée et d'un tableau warnings : une mauvaise lecture n'entre donc jamais discrètement dans le contexte de votre agent. Conditionnez-y votre confiance avant de vous fier à quoi que ce soit :
if let quality = op.renderQuality, quality < 0.6 {
print("low quality read of \(op.url): \(op.deductions)")
}
Perceive#
Effectue le rendu d'une URL dans les artefacts que vous demandez. Synchrone, avec des URL d'artefacts signées valables 15 minutes. Chaque méthode V2 nécessite une clé API privée ; les clés publiques sont rejetées.
let op = try await client.v2.perceive("https://example.com", options: PerceiveOptions(
outputs: [.markdown, .screenshot, .structured],
extract: [.tables, .metadata],
viewport: PerceiveViewport(width: 1440)
))
print(op.operationId, op.renderQuality ?? 0, op.outputs["markdown"]?.url ?? "")
print(op.structured ?? [:], op.extractionTier ?? .heuristic)
// Resigner les URL d'artefacts plus tard
let again = try await client.v2.getPerceiveOperation(op.operationId)
| Option | Type | Défaut | Description |
|---|---|---|---|
outputs |
[PerceiveOutputName]? |
[.markdown, .structured] |
.markdown, .htmlCleaned, .htmlRaw, .screenshot, .screenshotFullPage, .pdf, .links, .images, .structured. |
extract |
[PerceiveExtractName]? |
aucun | .tables, .prices, .contacts, .metadata, .mainContent, .headings, .structuredData, .technologies, .all. |
schema |
JSONObject? |
aucun | Schéma JSON pour l'extraction structurée via le niveau LLM. |
waitFor, waitTimeoutMs |
String?, Int? |
aucun, 30000 |
Un sélecteur CSS (éventuellement préfixé par css:) ou js:<expr> à attendre, et son budget en ms (0 à 60000). |
jsCode |
String? |
aucun | JavaScript exécuté après la navigation, 20000 caractères maximum. |
viewport |
PerceiveViewport? |
1920 par 1080 | width de 320 à 3840, height de 240 à 2160. |
headers, cookies, auth |
[String: String]?, [BrowserCookie]?, HttpBasicAuth? |
aucun | En-têtes de requête, cookies injectés, identifiants HTTP Basic. |
cacheMode |
PerceiveCacheMode? |
.enabled |
.enabled (cache d'1 heure), .bypass, .refresh. |
pdfOptions |
PdfOptions? |
aucun | N'a de sens que lorsque outputs inclut .pdf. |
blockResources |
[PerceiveResourceType]? |
aucun | .image, .media, .font, .stylesheet, .script, .xhr, .fetch, .websocket, .manifest, .other. |
respectRobots, mobile |
Bool? |
valeur serveur par défaut | Respecter robots.txt ; émuler un appareil mobile. |
onlyMainContent |
Bool? |
true |
Supprime la navigation, l'en-tête, le pied de page et les bandeaux de cookies de l'artefact markdown et de l'extrait main_content. Mettez false pour la page entière. |
directDownload |
Bool? |
false |
Renvoie les octets bruts au lieu d'une enveloppe JSON. Préférez perceiveDirect. |
proxyUrl, geolocation et actionChain existent sur PerceiveOptions et sont sérialisées sur le réseau, mais le serveur répond actuellement 422 pour les trois. Laissez-les à nil.
Diffuser un artefact unique directement sur le disque évite l'enveloppe JSON et l'aller-retour par URL signée. perceiveDirect vérifie en local que vous avez demandé exactement une sortie produisant un artefact : une erreur ne coûte donc rien.
let direct = try await client.v2.perceiveDirect("https://example.com", options: PerceiveOptions(outputs: [.pdf]))
try direct.content.write(to: URL(fileURLWithPath: direct.filename ?? "page.pdf"))
// Retélécharge un artefact stocké plus tard. Passez nil si l'opération n'en a produit qu'un.
let saved = try await client.v2.downloadPerceiveArtifact(direct.operationId, output: .pdf)
PerceiveDirectResult porte content, contentType, filename, operationId, objectKey, cacheHit, renderQuality, sourceStatusCode, contentHash et warningsCount, tous lus depuis les en-têtes de réponse. Un 410 renvoyé par downloadPerceiveArtifact signifie que l'artefact est sorti de sa fenêtre de rétention.
Les lots acceptent jusqu'à 1000 URL avec un bloc d'options partagé. Les petits lots se terminent en ligne ; les plus gros reviennent en file d'attente et vous les interrogez :
let batch = try await client.v2.perceiveBatch(
["https://a.example", "https://b.example"],
options: PerceiveBatchOptions(outputs: [.markdown], outputMode: .zip)
)
let done = try await client.v2.getPerceiveBatch(batch.jobId)
if done.status == .completed, let zip = done.zip {
print(zip.url ?? "")
}
outputMode vaut .manifest (par défaut) ou .zip. directDownload est rejeté avec un 422 sur les lots.
Discover#
Énumère les URL d'un site sans rendu navigateur. Rapide, et il n'effectue jamais de rendu.
let opts = DiscoverOptions(mode: .hybrid, maxUrls: 200, excludePatterns: ["/tag/"])
let found = try await client.v2.discover("https://example.com", options: opts)
print(found.total, found.truncated, found.sources, found.urls)
| Option | Type | Défaut | Description |
|---|---|---|---|
mode |
DiscoverMode? |
.hybrid |
.sitemap, .crawl ou .hybrid (sitemap plus exploration HTTP). |
maxUrls, maxDepth |
Int? |
100, 2 |
De 1 à 1000 URL ; profondeur d'exploration de 1 à 5. |
includePatterns, excludePatterns |
[String]? |
aucun | Liste d'autorisation en expressions régulières, puis liste d'exclusion appliquée ensuite. 50 motifs maximum chacune. |
sameDomainOnly |
Bool? |
true |
Rester sur le domaine de l'URL de départ. |
respectRobots |
Bool? |
valeur serveur par défaut | Respecter robots.txt. |
DiscoverResult indique également pagesCrawled, robotsRespected et warnings, et sources contient les comptages bruts par source relevés avant déduplication.
Lookup#
Lance une recherche web catégorisée, en effectuant éventuellement le rendu des premiers résultats dans le même appel.
let search = try await client.v2.lookup(
"best static site generators",
options: LookupOptions(category: .web, numResults: 10, perceiveTop: 3)
)
for hit in search.results {
print(hit.position ?? 0, hit.title ?? "", hit.url ?? "", hit.perceive?.renderQuality ?? 0)
}
| Option | Type | Défaut | Description |
|---|---|---|---|
category |
LookupCategory? |
.web |
.web, .news, .images, .scholar, .patents, .maps. |
country, locale |
String? |
aucun | Code pays Google gl ("us", "in") et langue d'interface hl ("en"). |
timeFilter |
LookupTimeFilter? |
aucun | .hour, .day, .week, .month, .year. |
numResults, page |
Int? |
10, 1 |
De 1 à 100 résultats ; page de 1 à 10. |
location, autocorrect |
String?, Bool? |
aucun, true |
Localisation en texte libre comme "Austin, Texas" ; laisser le fournisseur corriger la requête. |
perceiveTop |
Int? |
0 |
Effectuer automatiquement le rendu des N premières URL de résultat, de 0 à 10. Chacune déclenche un rendu navigateur complet. |
LookupResult expose aussi answerBox, knowledgeGraph, perceiveOperationIds et credits.
Distill#
Extrayez des données structurées de pages web selon un schéma que vous définissez.
let extraction = try await client.v2.distill(DistillOptions(
urls: ["https://example.com/pricing"],
schema: ["plans": .string("list of plan names with monthly prices")],
cssSchema: CssSchema(baseSelector: ".plan-card", fields: [
CssField(name: "name", type: .text, selector: "h3"),
CssField(name: "price", type: .text, selector: ".price")
])
))
let first = extraction.results[0]
print(first.data ?? [:], first.extractionTier, first.fieldsFromCss, first.fieldsFromLlm)
schema est un JSONObject, c'est-à-dire un [String: JSONValue] : une table plate {field: description} comme un objet JSON Schema complet fonctionnent donc tous les deux. Le cssSchema facultatif s'exécute en premier et répond à tout ce que de simples sélecteurs peuvent atteindre ; seuls les champs qu'il manque sont escaladés vers le niveau LLM, et extractionTier indique quels niveaux ont réellement répondu (.css, .llm, .mixed ou .none). CssField.type vaut l'un de .text, .attribute, .html, .regex, .nested, .list ou .nestedList, avec une imbrication jusqu'à 5 niveaux.
Remplacez urls par discoverFrom pour découvrir puis distiller en un seul appel. DistillDiscoverFrom prend url, mode (.hybrid par défaut) et maxPages (de 1 à 50, 10 par défaut, plafonnant à la fois la découverte et la distillation) :
_ = try await client.v2.distill(DistillOptions(
discoverFrom: DistillDiscoverFrom(url: "https://example.com", mode: .sitemap, maxPages: 10),
schema: ["title": .string("page title"), "summary": .string("one-line summary")]
))
Passer à la fois urls et discoverFrom, ou aucun des deux, lève EnconvertError.invalidArgument avant l'envoi de toute requête.
Ingest#
Transforme un site, une liste d'URL ou une pile de documents envoyés en JSONL découpé et prêt pour le RAG. Toujours asynchrone.
let job = try await client.v2.ingest(IngestOptions(
mode: .sitemap,
url: "https://docs.example.com",
maxPages: 100,
chunk: IngestChunkOptions(maxWords: 512, sentenceOverlap: 1),
webhookUrl: "https://my.app/hooks/enconvert"
))
let status = try await client.v2.getIngestJob(job.jobId)
if status.status == .completed {
print(status.totalChunks, status.outputUrl ?? "")
}
| Option | Type | Défaut | Description |
|---|---|---|---|
mode |
IngestMode? |
.urls |
.urls, .sitemap ou .crawl. Le quatrième cas, .files, est celui que ingestFiles rapporte sur son job ; ne le passez pas ici. |
url |
String? |
aucun | URL de départ. Obligatoire pour .sitemap et .crawl, interdite pour .urls. |
urls |
[String]? |
aucun | URL explicites, 1000 au maximum. Obligatoires pour .urls, interdites sinon. |
maxPages, maxDepth |
Int? |
50, 2 |
Plafond de découverte pour .sitemap et .crawl, de 1 à 1000 ; profondeur de 1 à 5. |
sameDomainOnly |
Bool? |
true |
Rester sur le domaine de l'URL de départ. |
includePatterns, excludePatterns |
[String]? |
aucun | Liste d'autorisation en expressions régulières, puis liste d'exclusion. |
respectRobots |
Bool? |
valeur serveur par défaut | Respecter robots.txt. |
waitFor, waitTimeoutMs |
String?, Int? |
30000 ms |
Sélecteur ou expression js: attendu sur chaque page, et son budget (0 à 60000). |
chunk |
IngestChunkOptions? |
aucun | maxWords de 32 à 4000, 512 par défaut. sentenceOverlap de 0 à 10, 1 par défaut. |
webhookUrl |
String? |
aucun | Webhook de fin, signé en HMAC. |
Les règles de mode et d'URL ci-dessus sont appliquées côté client : ingest lève EnconvertError.invalidArgument plutôt que d'émettre une requête vouée à l'échec si vous passez urls avec mode: .sitemap. Les fichiers envoyés passent par le même pipeline et le même cycle de vie de job :
let files: [FileInput] = [.path("handbook.pdf"), .path("notes.docx")]
let fileJob = try await client.v2.ingestFiles(files, options: IngestFilesOptions(chunk: IngestChunkOptions(maxWords: 512)))
PDF, DOCX, PPTX, XLSX, CSV, HTML, EPUB, TXT et MD, ainsi que les fichiers bureautiques hérités ou ODF sont acceptés, et au moins un fichier est requis. Gestion des jobs et plomberie des webhooks :
let list = try await client.v2.listIngestJobs(V2ListOptions(limit: 20))
let canceled = try await client.v2.cancelIngestJob(job.jobId) // idempotent
let secret = try await client.v2.getWebhookSecret()
print(secret.signatureHeader, secret.signatureScheme, secret.replayToleranceSeconds)
_ = try await client.v2.rotateWebhookSecret() // les anciennes signatures cessent aussitôt d'être valides
let retry = try await client.v2.retryIngestWebhook(job.jobId)
print(retry.delivered, retry.attempts, retry.detail)
retryIngestWebhook répond 409 lorsque le job n'est pas terminé et 400 lorsqu'aucun webhook n'est configuré. V2ListOptions prend skip et limit (de 1 à 100, 20 par défaut).
Watch#
Refait le rendu d'une page à intervalle fixe et vous notifie quand elle change.
let watcher = try await client.v2.createWatcher("https://example.com/pricing", options: WatchCreateOptions(
frequencyMinutes: 60,
diffMode: .auto,
webhookUrl: "https://my.app/hooks/changes",
notifyEmail: true
))
let history = try await client.v2.getWatcherSnapshots(watcher.watcherId, options: SnapshotListOptions(limit: 10))
for snapshot in history.snapshots where snapshot.hasChanges {
print(snapshot.checkedAt, snapshot.changeCount, snapshot.similarity ?? 0)
}
| Option | Type | Défaut | Description |
|---|---|---|---|
frequencyMinutes |
Int? |
60 |
De 60 à 43200. Le plancher horaire est strict. |
diffMode |
WatchDiffMode? |
.auto |
.auto, .text, .structured, .tables, .metadata. |
trackFields |
JSONObject? |
aucun | Sous-ensemble de champs ou de sélecteurs transmis au moteur de diff. |
webhookUrl |
String? |
aucun | Webhook de notification de changement, signé en HMAC. |
notifyEmail |
Bool? |
true |
Notifier le propriétaire du projet par e-mail en cas de changement. |
// Une chaîne vide efface le webhook ; nil le laisse tel quel.
_ = try await client.v2.updateWatcher(watcher.watcherId, updates: WatcherUpdate(webhookUrl: "", status: .paused))
_ = try await client.v2.listWatchers()
_ = try await client.v2.getWatcher(watcher.watcherId)
_ = try await client.v2.deleteWatcher(watcher.watcherId) // suppression logique, idempotente
updateWatcher exige au moins un champ et lève EnconvertError.invalidArgument sur un WatcherUpdate vide. WatchUpdateStatus n'accepte que .active ou .paused ; la suppression passe par deleteWatcher, qui renvoie le watcher marqué supprimé avec le statut .deleted.
WatcherSnapshot.changes est un tableau d'objets JSON bruts extraits de la page surveillée. Échappez les valeurs avant de les afficher où que ce soit.
Options PDF#
PdfOptions est partagé par convertUrlToPdf, convertDocument, convertWebsiteToPdf, PerceiveOptions et (pour grayscale uniquement) convertToPdf. Seuls les champs que vous définissez sont envoyés.
let pdf = PdfOptions(
pageSize: "A4",
orientation: .landscape,
margins: PdfMargins(top: 10, bottom: 10, left: 15, right: 15),
scale: 0.9,
grayscale: false,
header: PdfHeaderFooter(content: "Quarterly Report", height: 15),
footer: PdfHeaderFooter(content: "Confidential", height: 12)
)
| Champ | Type | Description |
|---|---|---|
pageSize |
String? |
"A4", "A3", "Letter", "Legal", et compagnie. |
pageWidth, pageHeight |
Double? |
Remplacent pageSize lorsque les deux sont définis ensemble. |
orientation |
PdfOrientation? |
.portrait ou .landscape. Portrait par défaut. |
margins |
PdfMargins? |
top, bottom, left, right, chacun un Double?. Les quatre sont facultatifs. |
scale |
Double? |
Échelle de rendu, par exemple 0.9 pour 90 %. |
grayscale |
Bool? |
Post-traite le PDF en niveaux de gris. |
header |
PdfHeaderFooter? |
content (2000 caractères maximum) et height. |
footer |
PdfHeaderFooter? |
Même forme que header. |
Gestion des erreurs#
Swift reçoit un seul type d'erreur, EnconvertError, modélisé comme une énumération plutôt que comme une hiérarchie de classes. Filtrez-le avec des motifs catch :
do {
_ = try await client.v2.perceive("https://example.com")
} catch EnconvertError.authentication(let message) {
print("invalid or missing API key: \(message)")
} catch EnconvertError.rateLimit(let message) {
print("too many requests, back off and retry: \(message)")
} catch let error as EnconvertError {
print("api error: \(error)") // s'affiche sous la forme "[<status>] <message>"
}
| Cas | Levé sur | Code de statut |
|---|---|---|
.authentication(message:) |
Clé invalide, absente ou révoquée | 401, 403 (les deux rapportent 401) |
.quota(message:) |
Toute réponse à laquelle l'API répond 402 |
402 |
.rateLimit(message:) |
Limite de débit dépassée | 429 |
.api(statusCode:message:) |
Toute autre 4xx ou 5xx | le code réel |
.invalidArgument(_:) |
Validation côté client, avant toute requête | aucun |
EnconvertError se conforme à CustomStringConvertible et LocalizedError : String(describing:), localizedDescription et l'interpolation de chaînes s'affichent donc tous sous la forme "[<status>] <message>". Deux propriétés pratiques lisent les mêmes valeurs sans filtrage de motif : error.statusCode (Int?, nil pour .invalidArgument) et error.message (le texte sans le préfixe entre crochets). Une réponse 2xx bien formée à laquelle manque un champ requis par le SDK remonte sous la forme .api(statusCode: 0, ...), ce qui distingue une charge utile mal formée d'un véritable échec HTTP.
Les paires de conversion non prises en charge, un appel distill avec à la fois urls et discoverFrom, un appel perceiveDirect demandant deux artefacts, et un WatcherUpdate vide lèvent tous .invalidArgument avant que le réseau ne soit sollicité. Les codes de réponse sont catalogués dans la référence des codes d'erreur.
Récupération après timeout#
Les rendus d'URL longs et les conversions de gros documents peuvent dépasser le plafond de 60 à 120 secondes d'un reverse proxy même lorsque le job se termine correctement côté serveur. Le SDK s'en sort par interrogation, sans une ligne de code de votre part :
- Avant chaque conversion de fichier unique et d'URL unique, le client génère un UUIDv4, retire les tirets et l'envoie comme
job_id. - Si cette requête revient avec un statut de 500 ou plus, le client bascule silencieusement sur
GET /v1/convert/status/{job_id}, interrogé toutes les 3 secondes. Un404y signifie « pas encore enregistré » et maintient la boucle. - Sur
success, il renvoie le résultat. Surfailed, il lève.api(statusCode: 500, message:)portant le message du serveur. Le délai d'interrogation est de 5 minutes, après quoi vous obtenez.api(statusCode: 504, message: "Conversion timed out").
ConversionResult.jobId est renseigné par le client même lorsque le chemin synchrone a réussi et que la réponse l'a omis : vous pouvez donc le passer vous-même à getJobStatus :
let status = try await client.getJobStatus(result.jobId ?? "")
if status.status == .success {
print(status.presignedUrl ?? "")
} else if status.status == .failed {
print(status.error ?? "conversion failed")
}
convertWebsiteToPdf et convertWebsiteToScreenshot n'ont pas de ligne de job par unité à interroger : une 5xx y remonte donc immédiatement au lieu d'être réessayée. Les méthodes V2 n'utilisent pas non plus le repli par job.
Configuration#
let client = try Enconvert(
apiKey: ProcessInfo.processInfo.environment["ENCONVERT_API_KEY"] ?? "",
baseURL: "https://api.enconvert.com",
timeout: 300
)
| Paramètre | Type | Défaut | Description |
|---|---|---|---|
apiKey |
String |
obligatoire | Clé API privée. Une chaîne vide lève EnconvertError.invalidArgument. |
baseURL |
String |
https://api.enconvert.com |
À remplacer pour une passerelle auto-hébergée. Les barres obliques finales sont supprimées. |
timeout |
TimeInterval |
300 |
En secondes. Définit à la fois timeoutIntervalForRequest et timeoutIntervalForResource sur l'URLSession interne. |
La clé voyage dans un en-tête X-API-Key à chaque appel d'API. Les téléchargements présignés partent délibérément sans elle, puisqu'une URL de stockage signée s'authentifie d'elle-même et que transmettre la clé à un hôte de stockage la ferait fuiter. Pour annuler un appel individuel, enveloppez-le dans une Task et annulez-la : chaque méthode est une simple fonction async throws. Enconvert ne stocke que des propriétés let sur une seule URLSession : construisez donc un client unique au démarrage et réutilisez-le ; client.v2 est un mince espace de noms au-dessus du même transport.
Forme du résultat#
Les conversions de fichier unique et d'URL unique renvoient un ConversionResult :
public struct ConversionResult: Codable, Equatable, Sendable {
public let presignedUrl: String
public let objectKey: String
public let filename: String
public let fileSize: Int?
public let conversionTimeSeconds: Double?
public let jobId: String?
}
Les URL présignées sont de courte durée. Passez saveTo pour que le SDK écrive les octets sur le disque à votre place, en créant les répertoires parents au besoin, ou récupérez l'URL vous-même et rangez le fichier dans votre propre bucket pour un accès à long terme.
Les artefacts V2 arrivent sous forme de valeurs V2OutputArtifact indexées par nom de sortie, chacune portant url (String?, présignée pour 15 minutes et resignée à chaque GET de statut), objectKey, sizeBytes, contentType et expiresIn (en secondes, 900 par défaut). PerceiveResult les enveloppe avec les métadonnées de fiabilité : renderQuality, statusCode, deductions, cacheHit, warnings, contentHash, urlFinal, structured, extractionTier, tokens, costCents, durationMs et optionsEcho, qui renvoie en écho les options que le serveur a réellement appliquées, les secrets étant réduits à des booléens. Les charges utiles définies par l'appelant (schémas d'extraction, data distillées, trackFields de watcher, changes de diff, extra de recherche) font l'aller-retour via JSONValue, une énumération avec les cas .null, .bool, .number, .string, .array et .object, plus l'alias JSONObject pour [String: JSONValue]. Chaque type de résultat est Codable, Equatable et Sendable : mettre en cache un résultat parsé sur le disque et le recharger plus tard fonctionne sans rien de plus.
Sources et signalements#
- Paquet :
Enconvert, via Swift Package Manager. Version exposée à l'exécution par la constante de moduleVERSION - GitHub : conversionapi/swift-sdk
- Licence : MIT. Dépendances : aucune, uniquement
URLSessionet Foundation
À lire également : tous les SDK, vue d'ensemble V2, perceive, discover, lookup, distill, ingest, watch, vue d'ensemble des endpoints, paramètres et options, et votre tableau de bord pour les clés.
Questions fréquentes#
Comment convertir des fichiers en Swift ?#
Ajoutez https://github.com/conversionapi/swift-sdk.git aux dépendances de votre Package.swift, construisez un client avec try Enconvert(apiKey:), puis appelez une méthode typée comme convertDocument, convertImage ou convertUrlToPdf. Passez saveTo dans la struct d'options et le SDK écrit le fichier terminé directement à ce chemin, en créant les répertoires parents au besoin.
Comment convertir une URL en PDF en Swift ?#
Appelez try await client.convertUrlToPdf("https://example.com", options: UrlToPdfOptions(saveTo: "page.pdf")). Mettez singlePage: false pour paginer au lieu de produire une seule page continue, et passez pdfOptions: pour la taille de page, l'orientation, les marges, l'échelle, les niveaux de gris, les en-têtes et les pieds de page. N'oubliez pas que Swift veut les étiquettes dans l'ordre de déclaration : saveTo: vient donc avant singlePage: et pdfOptions:.
Comment convertir un DOCX en PDF en Swift ?#
try await client.convertDocument(.path("report.docx"), options: ConvertDocumentOptions(saveTo: "report.pdf")). Le format de sortie vaut "pdf" par défaut, outputFormat peut donc être omis. La même méthode gère les entrées XLSX, PPTX, ODT, ODS, ODP, OTS, Pages, Numbers, HTML, Markdown, CSV, JSON, XML, YAML et TOML.
Comment convertir du HEIC en WebP en Swift ?#
try await client.convertImage(.path("photo.heic"), options: ConvertImageOptions(outputFormat: "webp", saveTo: "photo.webp")). Le format d'entrée est lu depuis l'extension du nom de fichier, et les 20 paires ordonnées entre jpeg, png, svg, heic et webp fonctionnent de la même manière. Les paires non prises en charge lèvent EnconvertError.invalidArgument en local, avant l'envoi de toute requête.
Le SDK Swift embarque-t-il des dépendances tierces ?#
Non. Package.swift déclare un tableau dependencies vide. Tout repose sur URLSession, JSONSerialization et Foundation, avec FoundationNetworking importé de manière conditionnelle pour que le paquet se compile sous Linux comme sur les plateformes Apple.
Comment récupérer une page web en Markdown propre en Swift ?#
Deux possibilités. client.convertUrlToMarkdown renvoie du Markdown GitHub-Flavored avec un frontmatter YAML et constitue le chemin le plus simple. client.v2.perceive avec outputs: [.markdown] vous donne le même Markdown plus un score renderQuality, une table deductions, des warnings, et la possibilité d'ajouter des captures d'écran, des liens ou une extraction structurée issus du même rendu.
Que signifie la qualité de rendu et pourquoi faut-il la vérifier ?#
renderQuality est un Double? de 0.0 à 1.0 attaché à chaque lecture V2. Il baisse lorsque la page n'a pas été rendue honnêtement : défi anti-bot, mur de connexion, bandeau de cookies posé sur une coquille vide, ou statut d'erreur HTTP. Le contenu est quand même renvoyé plutôt qu'avalé : vérifiez donc le score et le dictionnaire deductions qui nomme chaque pénalité avant de transmettre le texte à un modèle.
Puis-je utiliser le SDK Swift dans une application iOS ou macOS ?#
Uniquement derrière votre propre backend. Le paquet se compile pour iOS 15, tvOS 15, watchOS 8 et macOS 12 afin que vous puissiez partager du code de modèle entre cibles, mais il s'authentifie avec une clé API privée et les endpoints V2 rejettent purement et simplement les clés publiques. Livrer cette clé dans un binaire d'application revient à la donner à quiconque décompresse le bundle. Faites appeler votre propre serveur depuis l'application, et appelez EnConvert depuis le serveur.
Que se passe-t-il quand une conversion longue atteint le timeout du proxy ?#
Le SDK envoie un job_id généré par le client avec chaque conversion de fichier unique et d'URL unique. Si la requête renvoie 500 ou plus, il interroge GET /v1/convert/status/{job_id} toutes les 3 secondes pendant 5 minutes au maximum, renvoyant le résultat sur success et levant .api(statusCode: 500, ...) sur failed. Dépasser le délai donne .api(statusCode: 504, message: "Conversion timed out"). Les soumissions de lots de sites sautent délibérément ce repli.
Comment savoir quelles conversions sont prises en charge avant d'envoyer une requête ?#
Appelez validOutputsFor("json") pour connaître les sorties acceptées par un format d'entrée donné, ou vérifiez l'appartenance à IMPLEMENTED_CONVERSIONS, l'ensemble des 43 endpoints {input}-to-{output} implémentés. convertImage et convertDocument effectuent la même vérification en interne et lèvent EnconvertError.invalidArgument avec la liste des sorties valides pour cette entrée, avant l'envoi de toute requête.