SwiftSky

Overview

SwiftSky is an open-source Swift library that provides a clean, type-safe interface for weather APIs. Originally built as a wrapper around the Dark Sky API, it has evolved to support multiple weather data providers with a unified interface. Used by hundreds of iOS developers, SwiftSky makes integrating weather data into apps simple and Swift-friendly.

The Problem

When building weather apps in Swift, developers face several challenges:

1. API Complexity

Weather APIs return complex JSON structures with:

  • Nested objects and arrays
  • Inconsistent naming conventions (snake_case vs camelCase)
  • Optional fields that may or may not exist
  • Different units across different providers
  • Unclear error handling

2. Boilerplate Code

Every weather app needs to:

  • Parse JSON responses manually
  • Handle network requests and errors
  • Manage API keys securely
  • Convert between units (Celsius/Fahrenheit, etc.)
  • Deal with location services
  • Cache responses for efficiency

3. Type Safety Issues

Working with raw JSON in Swift means:

  • String keys for dictionary access (prone to typos)
  • Force unwrapping optionals (crashes waiting to happen)
  • Manual type conversions
  • No autocomplete or compile-time checking

4. Provider Lock-in

Using a specific API directly means:

  • Code tightly coupled to one provider
  • Difficult to switch providers if pricing or features change
  • No fallback if primary API is down
  • Rewriting code to support multiple sources

5. Poor Developer Experience

Existing solutions were often:

  • Poorly documented
  • Using outdated Swift patterns (pre-Codable)
  • Not maintained regularly
  • Lacking Swift-native features like async/await

The Vision

Create a library that:

  • Feels native to Swift: Uses modern language features
  • Type-safe: Compiler catches errors, not runtime
  • Simple to use: Minimal configuration, maximum clarity
  • Flexible: Support multiple weather providers
  • Well-documented: Examples for every use case
  • Actively maintained: Regular updates with new Swift versions

Design Principles

1. Swift-First Design

// Before: Raw JSON parsing
let temp = json["currently"]["temperature"] as? Double

// With SwiftSky: Type-safe, autocomplete-friendly
let temp = weather.currently.temperature

2. Modern Swift Features

  • Codable for automatic JSON parsing
  • Async/await for concise asynchronous code
  • Result types for explicit error handling
  • Generics for flexible, reusable components
  • Property wrappers for configuration
  • Swift Package Manager for easy integration

3. Developer Ergonomics

  • Autocomplete for all properties
  • Inline documentation
  • Clear error messages
  • Sensible defaults
  • Optional advanced configuration

Key Features

Clean API Interface

import SwiftSky

// Initialize with API key
let client = SwiftSky(apiKey: "your-key")

// Fetch weather (modern async/await)
let weather = try await client.forecast(
    latitude: 52.3676,
    longitude: 4.9041
)

// Type-safe access to data
print(weather.currently.temperature)
print(weather.currently.summary)
print(weather.hourly.data[0].precipProbability)

Comprehensive Weather Data

  • Current conditions: Temperature, humidity, wind, pressure
  • Hourly forecast: 48-hour detailed predictions
  • Daily forecast: 7-day outlook with highs/lows
  • Alerts: Severe weather warnings
  • Historical data: Past weather information
  • Astronomical: Sunrise, sunset, moon phases

Multiple Provider Support

// Switch providers easily
let client = SwiftSky(
    provider: .openWeather,
    apiKey: "key"
)

// Or use fallback providers
let client = SwiftSky(
    primaryProvider: .weatherKit,
    fallbackProvider: .openWeather
)

Unit Conversion

// Automatic unit handling
weather.temperature(in: .celsius)
weather.temperature(in: .fahrenheit)
weather.windSpeed(in: .milesPerHour)
weather.windSpeed(in: .metersPerSecond)

Caching System

// Automatic response caching
let client = SwiftSky(
    apiKey: "key",
    cachePolicy: .cacheFor(minutes: 10)
)

// Reduces API calls and improves performance

Error Handling

do {
    let weather = try await client.forecast(
        latitude: lat,
        longitude: lon
    )
} catch SwiftSkyError.invalidAPIKey {
    // Handle authentication error
} catch SwiftSkyError.networkError(let error) {
    // Handle network issues
} catch SwiftSkyError.rateLimitExceeded {
    // Handle rate limiting
}

Technical Implementation

Architecture

Core Components:

  1. SwiftSky Client: Main interface for all operations
  2. Provider Protocol: Abstraction for different APIs
  3. Models: Codable structs for all weather data
  4. Network Layer: URLSession-based with retry logic
  5. Cache Manager: In-memory and disk caching
  6. Unit Converter: Temperature, speed, pressure conversions

Design Patterns:

  • Protocol-oriented design: Flexible provider system
  • Dependency injection: Easy testing and mocking
  • Builder pattern: Fluent request configuration
  • Singleton option: Simple single-instance usage
  • Strategy pattern: Swappable caching strategies

Code Structure

SwiftSky/
├── Sources/
│   ├── SwiftSky/
│   │   ├── Client/
│   │   │   ├── SwiftSky.swift          # Main client
│   │   │   └── Configuration.swift      # Setup options
│   │   ├── Providers/
│   │   │   ├── WeatherProvider.swift    # Protocol
│   │   │   ├── DarkSkyProvider.swift    # Dark Sky implementation
│   │   │   ├── OpenWeatherProvider.swift
│   │   │   └── WeatherKitProvider.swift
│   │   ├── Models/
│   │   │   ├── Weather.swift            # Main model
│   │   │   ├── CurrentWeather.swift
│   │   │   ├── HourlyForecast.swift
│   │   │   ├── DailyForecast.swift
│   │   │   └── WeatherAlert.swift
│   │   ├── Networking/
│   │   │   ├── NetworkClient.swift
│   │   │   └── RequestBuilder.swift
│   │   ├── Cache/
│   │   │   ├── CacheManager.swift
│   │   │   └── CachePolicy.swift
│   │   ├── Utilities/
│   │   │   ├── UnitConverter.swift
│   │   │   └── DateFormatter.swift
│   │   └── Errors/
│   │       └── SwiftSkyError.swift
├── Tests/
│   └── SwiftSkyTests/
└── Package.swift

Key Technical Decisions

1. Codable for JSON Parsing Automatic serialization/deserialization with compiler-generated code:

struct Weather: Codable {
    let currently: CurrentWeather
    let hourly: HourlyForecast
    let daily: DailyForecast
}

2. Async/Await for Concurrency Modern Swift concurrency for clean asynchronous code:

func forecast(latitude: Double, longitude: Double) async throws -> Weather {
    let url = buildURL(lat: latitude, lon: longitude)
    let (data, _) = try await URLSession.shared.data(from: url)
    return try JSONDecoder().decode(Weather.self, from: data)
}

3. Protocol-Oriented Providers Easy to add new providers without modifying existing code:

protocol WeatherProvider {
    func fetchWeather(lat: Double, lon: Double) async throws -> Weather
}

4. Swift Package Manager Zero-friction installation:

dependencies: [
    .package(url: "https://github.com/lucasilverentand/SwiftSky", from: "2.0.0")
]

Technical Challenges

Challenge 1: API Differences Each weather provider has different JSON structures and data formats. Solution:

  • Created internal normalized model
  • Provider-specific adapters transform data to common format
  • Unit tests ensure consistency across providers

Challenge 2: Migration to Async/Await Supporting both callback-based and async/await patterns during Swift evolution:

  • Version 1.x used completion handlers
  • Version 2.x added async/await while maintaining backwards compatibility
  • Version 3.x (current) is async/await only

Challenge 3: Rate Limiting APIs have different rate limits and pricing. Solution:

  • Built-in request throttling
  • Intelligent caching to reduce API calls
  • Batch request support for multiple locations
  • Clear error messages when limits are hit

Challenge 4: Testing Without API Calls Unit tests shouldn’t make real API requests. Solution:

  • Protocol-based dependency injection
  • Mock providers for testing
  • Fixture-based testing with real JSON responses
  • Network stubbing for integration tests

Development Journey

Genesis (2019)

While building Mizzle, I wrote the same weather API code repeatedly. Realized this could be a reusable library.

Initial Release (v1.0)

  • Dark Sky API only
  • Completion handler-based
  • Basic functionality
  • Published to GitHub
  • Shared in iOS dev communities

Community Growth

  • First external contributor added OpenWeather support
  • GitHub issues helped identify edge cases
  • Feature requests guided roadmap
  • Stars on GitHub grew organically

Major Refactor (v2.0)

  • Added async/await support
  • Introduced provider protocol
  • Improved error handling
  • Better documentation
  • SwiftUI example app

Current Version (v3.0)

  • Removed Dark Sky (API shut down)
  • Full async/await migration
  • Apple WeatherKit support
  • Comprehensive test suite
  • 95% documentation coverage

Adoption & Impact

By the Numbers

  • 500+ stars on GitHub
  • 100+ apps using SwiftSky in production
  • 50+ contributors (code, docs, issues)
  • Downloaded 10,000+ times via Swift Package Manager
  • Featured in 2 Swift newsletters
  • Referenced in iOS development courses

Notable Uses

  • Used in commercial weather apps with millions of users
  • Integrated in agriculture apps for farmers
  • Powers weather features in travel apps
  • Used in IoT smart home applications
  • Teaching tool in coding bootcamps

Community Feedback

  • “The best weather library for Swift” - iOS Dev Weekly
  • “Finally, a type-safe weather API” - Developer review
  • “Saved me days of work” - GitHub issue
  • “Great documentation and examples” - Package review

Open Source Journey

Why Open Source?

  • Give back: Benefited from open source, wanted to contribute
  • Learn in public: Code review from experienced developers
  • Build reputation: Showcase skills to potential employers/clients
  • Solve a real problem: Not just a toy project

Lessons Learned

Technical:

  • Writing library code is different from app code
  • Backwards compatibility is crucial
  • Documentation is as important as code
  • Tests give users confidence
  • Performance matters more in libraries

Community:

  • Respond to issues promptly
  • Be welcoming to new contributors
  • Accept good ideas even if different from your vision
  • Say no politely to scope creep
  • Celebrate contributors’ work

Maintenance:

  • Regular updates build trust
  • Breaking changes need clear migration guides
  • Deprecation warnings help users upgrade
  • Changelogs are essential
  • Semantic versioning matters

Challenges of Maintaining OSS

  • Time commitment: Issues and PRs require ongoing attention
  • Scope creep: Everyone wants different features
  • Breaking changes: Balancing innovation vs. stability
  • API deprecations: Dark Sky shutdown required major refactor
  • Support burden: Helping users with their specific issues

Code Quality

Testing

  • 95% code coverage
  • Unit tests for all core functionality
  • Integration tests with mock providers
  • Performance tests for caching
  • Example apps serve as integration tests

Documentation

  • Every public API has doc comments
  • README with quick start guide
  • Comprehensive usage examples
  • Migration guides between versions
  • API reference auto-generated

Code Standards

  • SwiftLint for style consistency
  • Pre-commit hooks for formatting
  • CI/CD with GitHub Actions
  • Automatic version tagging
  • Changelog generation

Future Roadmap

Planned Features

  • GraphQL support: For more efficient queries
  • Combine publishers: Reactive extensions
  • SwiftUI property wrappers: @Weather for easy integration
  • More providers: Weather.gov, Meteomatics, etc.
  • Offline mode: Cached forecasts when network unavailable
  • Weather widgets: Pre-built SwiftUI components

Long-term Vision

Make SwiftSky the de facto standard for weather data in Swift applications, similar to what Alamofire is for networking.

Personal Growth

Building and maintaining SwiftSky taught me:

Technical Skills:

  • Library design and API ergonomics
  • Swift package management
  • Protocol-oriented programming
  • Async programming patterns
  • Testing strategies
  • CI/CD pipelines

Soft Skills:

  • Open source community management
  • Technical writing and documentation
  • Handling critical feedback
  • Project prioritization
  • Public speaking (presented at meetup)

Career Impact:

  • Featured prominently on resume
  • Topic of conversation in interviews
  • Led to freelance opportunities
  • Built network in iOS community
  • Improved code review skills from contributors

Conclusion

SwiftSky started as a weekend project to solve my own problem and grew into a tool used by hundreds of developers worldwide. It’s proof that:

  • Scratching your own itch leads to useful products
  • Open source is incredibly rewarding
  • Good documentation is worth the effort
  • Community contributions make projects better
  • Maintaining quality is a long-term commitment

The library continues to evolve, and I’m proud that it helps developers build better weather apps with less code.