Skip to content

rryam/GhostingKit

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

23 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

GhostingKit

A modern, type-safe Swift library for interacting with the Ghost Content API. GhostingKit provides a comprehensive set of tools for building Ghost-powered applications with features like caching, retry logic, request cancellation, and more.

Features

  • βœ… Complete Ghost Content API Coverage: Posts, pages, authors, tags, tiers, and settings
  • βœ… Type-Safe: Strongly typed models with proper Swift types (Date, optionals, etc.)
  • βœ… Modern Swift: Built with async/await, actors, and Swift 5.9+ features
  • βœ… Caching: Built-in caching with configurable TTL and LRU eviction
  • βœ… Retry Logic: Configurable exponential backoff for failed requests
  • βœ… Request Cancellation: Cancel individual requests or all active requests
  • βœ… Pagination: Helper utilities for paginated content and streaming
  • βœ… Error Handling: Comprehensive error types with detailed messages
  • βœ… Security: Secure configuration loading from Bundle, environment, or JSON
  • βœ… SwiftUI Ready: Complete example app with navigation and detail views

Requirements

  • iOS 15.0+ / macOS 12.0+ / tvOS 15.0+ / watchOS 8.0+
  • Swift 5.9+
  • Xcode 15.0+

Installation

Swift Package Manager

Add GhostingKit to your project using Swift Package Manager:

dependencies: [
    .package(url: "https://github.com/your-username/GhostingKit.git", from: "1.0.0")
]

Quick Start

Basic Usage

import GhostingKit

// Initialize with your Ghost site credentials
let ghostingKit = try GhostingKit(
    adminDomain: "your-site.ghost.io",
    apiKey: "your-content-api-key"
)

// Fetch posts
let posts = try await ghostingKit.getPosts(limit: 10)
print("Found \(posts.posts.count) posts")

// Fetch a specific post
let post = try await ghostingKit.getPost(id: "post-id")
print("Post title: \(post.title)")

Secure Configuration

From Info.plist

Add these keys to your Info.plist:

<key>GhostAdminDomain</key>
<string>your-site.ghost.io</string>
<key>GhostAPIKey</key>
<string>your-content-api-key</string>
let configuration = try GhostingKitConfiguration.fromBundle()
let ghostingKit = try GhostingKit(configuration: configuration)

From Environment Variables

export GHOST_ADMIN_DOMAIN="your-site.ghost.io"
export GHOST_API_KEY="your-content-api-key"
let configuration = try GhostingKitConfiguration.fromEnvironment()
let ghostingKit = try GhostingKit(configuration: configuration)

API Reference

Posts

// Get all posts
let posts = try await ghostingKit.getPosts(limit: 15, page: 1)

// Get posts with authors and tags included
let postsWithAuthors = try await ghostingKit.getPosts(
    limit: 10,
    include: "authors,tags"
)

// Filter posts by tag
let swiftPosts = try await ghostingKit.getPosts(
    filter: "tag:swift",
    include: "authors,tags"
)

// Get a specific post
let post = try await ghostingKit.getPost(id: "post-id")
let postBySlug = try await ghostingKit.getPostBySlug(slug: "post-slug")

Authors

// Get all authors
let authors = try await ghostingKit.getAuthors()

// Get a specific author
let author = try await ghostingKit.getAuthor(id: "author-id")
let authorBySlug = try await ghostingKit.getAuthorBySlug(slug: "author-slug")

Tags

// Get all tags
let tags = try await ghostingKit.getTags()

// Get public tags only
let publicTags = try await ghostingKit.getTags(filter: "visibility:public")

// Get a specific tag
let tag = try await ghostingKit.getTag(id: "tag-id")
let tagBySlug = try await ghostingKit.getTagBySlug(slug: "tag-slug")

Pages

// Get all pages
let pages = try await ghostingKit.getPages()

// Get a specific page
let page = try await ghostingKit.getPage(id: "page-id")
let pageBySlug = try await ghostingKit.getPageBySlug(slug: "page-slug")

Settings

// Get site settings
let settings = try await ghostingKit.getSettings()
print("Site title: \(settings.title)")
print("Site description: \(settings.description)")

Tiers (Membership)

// Get membership tiers
let tiers = try await ghostingKit.getTiers()

// Get a specific tier
let tier = try await ghostingKit.getTier(id: "tier-id")

Advanced Features

Caching

// Configure caching
let cacheConfig = GhostingKitCacheConfiguration(
    ttl: 300,        // 5 minutes
    maxItems: 100,   // Maximum 100 cached items
    isEnabled: true
)

let config = GhostingKitConfiguration(
    adminDomain: "your-site.ghost.io",
    apiKey: "your-api-key",
    cacheConfiguration: cacheConfig
)

let ghostingKit = try GhostingKit(configuration: config)

// Clear cache when needed
await ghostingKit.clearCache()

Retry Logic

// Configure retry behavior
let retryConfig = GhostingKitRetryConfiguration(
    maxAttempts: 3,
    baseDelay: 1.0,
    useExponentialBackoff: true,
    maxDelay: 60.0,
    retryableStatusCodes: [408, 429, 500, 502, 503, 504]
)

let config = GhostingKitConfiguration(
    adminDomain: "your-site.ghost.io",
    apiKey: "your-api-key",
    retryConfiguration: retryConfig
)

Request Cancellation

// Cancellable requests
let cancellableRequest = await ghostingKit.getPostsCancellable(limit: 10)

// Cancel the specific request
cancellableRequest.cancel()

// Or cancel all active requests
await ghostingKit.cancelAllRequests()

// Get the result
do {
    let posts = try await cancellableRequest.task.value
    print("Got \(posts.posts.count) posts")
} catch {
    print("Request failed or was cancelled: \(error)")
}

Pagination

// Use pagination helpers
let posts = try await ghostingKit.getPosts(limit: 10, page: 1)
if let pagination = posts.pagination {
    print("Page \(pagination.currentPage) of \(pagination.totalPages)")
    print("Total posts: \(pagination.totalCount)")
    
    if pagination.hasNextPage {
        let nextPage = try await ghostingKit.getPosts(limit: 10, page: pagination.nextPage!)
    }
}

// Get all posts across multiple pages
let allPosts = try await ghostingKit.getAllPosts(pageSize: 15, maxPages: 5)

// Stream posts for memory efficiency
for try await post in ghostingKit.postsStream(pageSize: 10) {
    print("Processing post: \(post.title)")
}

Error Handling

GhostingKit provides comprehensive error handling with detailed error types:

do {
    let posts = try await ghostingKit.getPosts()
} catch GhostingKitError.invalidURL(let url) {
    print("Invalid URL: \(url)")
} catch GhostingKitError.httpError(let statusCode, let message) {
    print("HTTP \(statusCode): \(message ?? "Unknown error")")
} catch GhostingKitError.resourceNotFound(let type, let identifier) {
    print("\(type) with ID \(identifier) not found")
} catch GhostingKitError.networkError(let error) {
    print("Network error: \(error)")
} catch GhostingKitError.cancelled {
    print("Request was cancelled")
} catch GhostingKitError.timeout {
    print("Request timed out")
} catch {
    print("Unknown error: \(error)")
}

SwiftUI Integration

GhostingKit works seamlessly with SwiftUI. Check out the included example app for complete implementations of:

  • πŸ“± Posts list with navigation to detail views
  • πŸ‘€ Authors list with author detail views
  • 🏷️ Tags list with tag detail views
  • πŸ“„ Pages list with page detail views
  • πŸ” Full-text search and filtering
  • ⚑ Async image loading
  • 🎨 Modern SwiftUI design patterns

Example Views

Posts List

struct PostsView: View {
    @State private var posts: [GhostPost] = []
    let ghostingKit: GhostingKit
    
    var body: some View {
        List(posts, id: \.id) { post in
            NavigationLink(destination: PostDetailView(post: post)) {
                VStack(alignment: .leading) {
                    Text(post.title)
                        .font(.headline)
                    Text(post.excerpt ?? "")
                        .font(.subheadline)
                        .foregroundColor(.secondary)
                }
            }
        }
        .task {
            do {
                let response = try await ghostingKit.getPosts(include: "authors,tags")
                posts = response.posts
            } catch {
                print("Error loading posts: \(error)")
            }
        }
    }
}

Contributing

Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.

Development Setup

  1. Clone the repository
  2. Open Package.swift in Xcode
  3. Run tests with ⌘+U
  4. Check out the example app in the Ghosting folder

Running Tests

# Run all tests
swift test

# Run only unit tests (no network required)
swift test --filter MockGhostingKitTests

# Run integration tests (requires network)
swift test --filter integration

License

GhostingKit is available under the MIT license. See the LICENSE file for more info.

Ghost Content API

This library interacts with the Ghost Content API. For more information about Ghost and its API, visit:

Changelog

1.0.0

  • Initial release
  • Complete Ghost Content API coverage
  • SwiftUI example app
  • Comprehensive test suite
  • Modern Swift async/await support
  • Caching, retry logic, and request cancellation
  • Type-safe models with proper Date handling
  • Secure configuration management

About

Unofficial Swift SDK for Ghost API

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Contributors 2

  •  
  •  

Languages