Swift SDK
Publish articles, manage series, and run AI tools with the MisarBlog Swift SDK, plus post-embed helpers.
The Swift SDK ships a full client for the developer API (https://api.misar.io/blog/v1) plus the iframe embed helpers, in the MisarBlog package (v1.0.0).
Installation
Add the MisarBlog package in Package.swift:
dependencies: [
.package(url: "https://github.com/misar-ai/misarblog-swift.git", from: "1.0.0"),
],
targets: [
.target(name: "MyApp", dependencies: [
.product(name: "MisarBlog", package: "misarblog-swift"),
]),
]API Client
MisarBlogClient authenticates with an mbk_ key (or an OAuth 2.1 access token) and defaults to https://api.misar.io/blog/v1 (the gateway strips /api). Every method is async throws and returns the decoded [String: Any] payload. Retryable statuses (429, 500, 502, 503, 504) are retried with exponential back-off.
import MisarBlog
let blog = MisarBlogClient(apiKey: "mbk_YOUR_KEY")
// Override the base URL if needed
let staging = MisarBlogClient(
apiKey: "mbk_YOUR_KEY",
baseURL: "https://api.misar.io/blog/v1"
)Articles
// List your articles
let result = try await blog.articles.list(status: "published", limit: 20)
if let articles = result["articles"] as? [[String: Any]] {
for a in articles { print(a["title"] ?? "", a["url"] ?? "") }
}
// Get one by slug (or UUID)
let article = try await blog.articles.get(slug: "my-first-article")
// Publish
let created = try await blog.articles.publish(data: [
"title": "Getting Started with the MisarBlog API",
"body_markdown": "## Introduction\n\nThis guide covers…",
"tags": ["swift", "api"],
"cover_image_url": "https://example.com/cover.jpg",
"visibility": "public",
])
// Save a draft, then update / publish it
let draft = try await blog.articles.createDraft(data: [
"title": "Work in progress", "body_markdown": "## Soon…",
])
_ = try await blog.articles.update(slug: draft["slug"] as! String, data: ["publish": true])Decode any payload into a typed value with Article.from(_:) / Series.from(_:):
let typed = try Article.from(article)
print(typed.title ?? "", typed.slug ?? "")Other resources
try await blog.series.list()
try await blog.series.create(title: "A Series", description: "Optional")
try await blog.ai.titles(action: "seo", prompt: "best AI writing tools 2026")
try await blog.ai.complete(prompt: "Tighten this intro: …", system: "You are an editor.", maxTokens: 300)
try await blog.images.generate(prompt: "A minimalist cover", size: "1792x1024")
try await blog.search.query(q: "react", type: "articles", sort: "newest")
try await blog.recommendations.get(articleId: articleId, limit: 5)
try await blog.analytics.get(days: 30)
try await blog.profile.get() // GET /me
try await blog.plan.get()
try await blog.trial.status()
try await blog.reactions.get(articleId: articleId)
try await blog.reactions.add(articleId: articleId, type: "like") // "like" | "clap" | "bookmark"
try await blog.reactions.remove(articleId: articleId, type: "like")Resources
| Resource | Methods |
|---|---|
blog.articles | list · get · publish · update · createDraft |
blog.series | list · create · addArticle |
blog.ai | titles · complete |
blog.images | generate · upload |
blog.search | query |
blog.recommendations | get |
blog.analytics | get |
blog.profile | get — GET /me |
blog.plan | get |
blog.trial | status · start |
blog.reactions | get · add · remove |
blog.upsell | funnel — platform-admin only |
Error handling
do {
_ = try await blog.articles.get(slug: "missing-slug")
} catch let error as MisarBlogError {
print(error) // .apiError(status:message:requiredScope:grantedScopes:) or .networkError
}Embed helpers
The no-key iframe helpers live on the MisarBlog type:
import MisarBlog
let client = MisarBlog()
// Profile embed
let url = client.embedURL(username: "johndoe")
// Single-post embed with a theme
let postURL = client.embedURL(username: "johndoe", slug: "my-first-post", theme: "dark")
// For gated (paywalled) embeds, refresh the session token
let result = try await client.refreshToken(token: "current-session-token")
print(result.token, result.expiresAt)embedURL(username:slug:theme:) -> URL returns a public embed-viewer URL (theme defaults to "auto"). refreshToken(token:baseURL:) async throws -> TokenResult returns a TokenResult(token, expiresAt); baseURL defaults to "https://misar.blog".