Skip to content

Latest commit

 

History

History
284 lines (208 loc) · 7.18 KB

File metadata and controls

284 lines (208 loc) · 7.18 KB

BloggerAPI

A modern Swift package for interacting with the Google Blogger API v3, built with Swift Concurrency (async/await) and actors for thread-safe operations.

Features

  • Modern Swift Concurrency - Built with async/await and actors
  • 🔒 Thread-Safe - All types are Sendable and use actor isolation
  • 📅 Date Filtering - Filter posts by published date (before/after)
  • 🔍 Type-Safe - Leverages Swift's type system with Codable models
  • 🪵 Debug Logging - Integrated OSLog for network request debugging
  • 🧪 Well Tested - Comprehensive test coverage
  • 🎯 Simple API - Clean, intuitive interface

Requirements

  • macOS 13.0+
  • Swift 6.2+
  • Xcode 16.0+

Installation

Swift Package Manager

Add the following to your Package.swift:

dependencies: [
    .package(url: "https://github.com/coodly/BloggerAPI.git", from: "0.2.0")
]

Or add it via Xcode:

  1. File → Add Package Dependencies...
  2. Enter the repository URL
  3. Select version requirements

Quick Start

import BloggerAPI

// Create a Blogger instance
let blogger = Blogger(
    blogURL: "https://your-blog.blogspot.com",
    key: "YOUR_GOOGLE_API_KEY"
)

// Fetch posts
let posts = try await blogger.fetchPosts()

for post in posts {
    print("\(post.title ?? "Untitled")")
    print("\(post.published)")
}

Usage

Basic Post Fetching

Fetch all posts from your blog:

let blogger = Blogger(blogURL: "https://your-blog.blogspot.com", key: "YOUR_API_KEY")
let posts = try await blogger.fetchPosts()

Date Filtering

Fetch posts published after a specific date:

let calendar = Calendar.current
let afterDate = calendar.date(from: DateComponents(year: 2024, month: 1, day: 1))!

let posts = try await blogger.fetchPosts(publishedAfter: afterDate)

Fetch posts published before a specific date:

let beforeDate = calendar.date(from: DateComponents(year: 2024, month: 12, day: 31))!

let posts = try await blogger.fetchPosts(publishedBefore: beforeDate)

Fetch posts within a date range:

let afterDate = calendar.date(from: DateComponents(year: 2024, month: 6, day: 1))!
let beforeDate = calendar.date(from: DateComponents(year: 2024, month: 6, day: 30))!

let posts = try await blogger.fetchPosts(
    publishedAfter: afterDate,
    publishedBefore: beforeDate
)

Custom Network Handling

Provide your own network implementation for custom behavior (e.g., caching, request modification):

let customFetch = NetworkFetch { request in
    // Add custom headers, logging, etc.
    var modifiedRequest = request
    modifiedRequest.addValue("MyApp/1.0", forHTTPHeaderField: "User-Agent")

    return try await URLSession.shared.data(for: modifiedRequest)
}

let blogger = Blogger(
    blogURL: "https://your-blog.blogspot.com",
    key: "YOUR_API_KEY",
    fetch: customFetch
)

API Reference

Blogger

The main actor for interacting with the Blogger API.

public actor Blogger {
    public init(blogURL: String, key: String, fetch: NetworkFetch = .default)

    public func fetchPosts(
        publishedAfter: Date? = nil,
        publishedBefore: Date? = nil
    ) async throws -> [Post]
}

Post

Represents a blog post.

public struct Post: Codable, Sendable {
    public let id: String
    public let blog: Blog?
    public let published: Date
    public let title: String?
    public let content: String?
    public let images: [Image]?
    public let url: URL?
}

NetworkFetch

Struct for handling network requests. Use .default for standard URLSession behavior or provide a custom implementation.

public struct NetworkFetch: Sendable {
    public init(fetch: @escaping @Sendable (URLRequest) async throws -> (Data, URLResponse))
    public static var `default`: NetworkFetch
    public func fetch(request: URLRequest) async throws -> (Data, URLResponse)
}

BloggerError

Errors that can occur during API operations.

public enum BloggerError: Error {
    case noData
    case invalidJSON(Error)
}

Debug Logging

The library uses OSLog for debug logging. To view network request logs:

Via Console.app:

  1. Open Console.app
  2. Filter by subsystem: com.coodly.BloggerAPI

Via Terminal:

log stream --predicate 'subsystem == "com.coodly.BloggerAPI"' --level debug

Log levels:

  • Info: High-level operations (fetching posts, blog ID resolution)
  • Debug: Network requests, responses, date filters
  • Error: JSON decode errors and network failures

Getting a Google API Key

  1. Go to the Google Cloud Console
  2. Create a new project or select an existing one
  3. Enable the Blogger API v3
  4. Go to "Credentials" and create an API key
  5. Restrict the key to the Blogger API for security

Architecture

The library uses modern Swift Concurrency features:

  • Actors - Blogger and APIClient are actors for thread-safe state management
  • Sendable Types - All public types conform to Sendable for safe concurrent access
  • Async/Await - All API calls use async/await for clean, sequential code
  • Structured Concurrency - Proper error handling and cancellation support

Internal Structure

BloggerAPI/
├── Blogger.swift          # Main public interface (actor)
├── APIClient.swift        # API communication layer (actor)
├── NetworkFetch.swift     # Network abstraction
├── Models/
│   ├── Post.swift        # Post data model
│   ├── Blog.swift        # Blog data model
│   ├── Image.swift       # Image data model
│   └── PostsPage.swift   # Pagination response
├── BloggerError.swift    # Error types
└── Logger.swift          # OSLog configuration

Testing

Run tests with Swift Package Manager:

swift test

The library includes comprehensive tests covering:

  • Blog ID resolution
  • Post fetching
  • Date filtering (after, before, ranges)
  • RFC3339 date format compliance
  • Network request construction

Migration from v0.1.x

Version 0.2.0+ uses async/await instead of callbacks:

Before (v0.1.x):

let blogger = Blogger(blogURL: url, key: key, fetch: fetch)
blogger.fetchUpdates(after: date) { result in
    // Handle result
}

After (v0.2.0+):

let blogger = Blogger(blogURL: url, key: key)
let posts = try await blogger.fetchPosts(publishedAfter: date)

License

This project is licensed under the Apache License 2.0. See the LICENSE file for details.

Copyright 2016 Coodly LLC

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

Acknowledgments

  • Built with Swift Concurrency best practices
  • Uses the Google Blogger API v3
  • Follows RFC3339 date formatting standards