Skip to content

botforge-pro/swift-embed

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

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

Repository files navigation

Tests

SwiftEmbed

A lightweight Swift library for embedding JSON, YAML and text resources with automatic caching and Swift 6 concurrency support.

Features

  • 🎯 Simple API - Clean property wrapper and computed property syntax
  • πŸ“¦ JSON Support - Built-in JSON decoding
  • πŸ“ YAML Support - Full YAML decoding via Yams
  • πŸ“„ Text Support - Load plain text files as strings
  • πŸ”’ Type Safe - Compile-time type checking with Decodable
  • ⚑ Automatic Caching - Resources cached after first load
  • πŸ”„ Swift 6 Ready - No concurrency warnings with static properties
  • πŸ“± Cross-Platform - Works on iOS, macOS, tvOS, watchOS

Installation

Swift Package Manager

Add to your Package.swift:

dependencies: [
    .package(url: "https://github.com/botforge-pro/swift-embed.git", from: "1.5.0")
]

Usage

SwiftEmbed provides three ways to load resources:

1. Computed Properties (Recommended for Swift 6)

Best for static configuration and avoids Swift 6 concurrency warnings:

import SwiftEmbed

struct AppConfig {
    // Clean syntax, no Swift 6 warnings, automatic caching
    static var config: Config {
        Embedded.getJSON(Bundle.main, path: "Resources/config.json")
    }
    
    static var users: [User] {
        Embedded.getJSON(Bundle.main, path: "Resources/users.json")
    }
    
    static var template: String {
        Embedded.getText(Bundle.main, path: "Resources/template.html")
    }
}

// Usage
let apiURL = AppConfig.config.apiURL  // Loaded and cached on first access
let userCount = AppConfig.users.count // Retrieved from cache

2. Property Wrappers

For instance properties in classes/structs:

import SwiftEmbed

struct MyApp {
    @Embedded.JSON(Bundle.main, path: "Resources/users.json")
    var users: [User]
    
    @Embedded.YAML(Bundle.main, path: "Config/settings.yaml")
    var config: Config
    
    @Embedded.Text(Bundle.main, path: "Resources/template.html")
    var htmlTemplate: String
}

3. Direct Loading

For immediate use or dynamic loading:

import SwiftEmbed

// Load and decode JSON
let users = Embedded.getJSON(Bundle.main, path: "users.json", as: [User].self)

// Load and decode YAML
let config = Embedded.getYAML(Bundle.main, path: "config.yaml", as: Config.self)

// Load plain text
let template = Embedded.getText(Bundle.main, path: "template.html")

Caching

All methods use an internal cache powered by NSCache:

  • Resources are loaded from disk only once
  • Subsequent accesses return cached values
  • Memory-safe with automatic purging under pressure
  • Thread-safe for concurrent access

Testing

Perfect for loading test data:

import Testing
import SwiftEmbed

@Suite("API Tests")
struct APITests {
    struct TestCase: Decodable {
        let input: String
        let expected: String
    }
    
    // Load test data once, cached for all test runs
    static var testCases: [TestCase] {
        Embedded.getJSON(Bundle.module, path: "TestData/url_tests.json")
    }
    
    @Test("URL Validation", arguments: testCases)
    func testURLs(testCase: TestCase) {
        // Test implementation
    }
}

File Organization

Place your resource files in your target:

MyApp/
β”œβ”€β”€ Sources/
β”‚   └── MyApp/
β”‚       └── MyClass.swift
└── Resources/
    β”œβ”€β”€ data.json
    └── config.yaml

For Swift packages, ensure resources are declared in Package.swift:

.target(
    name: "MyApp",
    resources: [
        .copy("Resources")  // Use .copy() to preserve directory structure
        // .process() will flatten the directory structure
    ]
)

Important: Always use .copy() instead of .process() to preserve your directory structure. The .process() rule will flatten directories and may cause resource loading to fail.

Swift 6 Concurrency

SwiftEmbed is fully compatible with Swift 6's strict concurrency checking:

// βœ… No warnings - computed property approach
struct MyConfig {
    static var settings: Settings {
        Embedded.getJSON(Bundle.module, path: "config.json")
    }
}

// βœ… No warnings - static let with direct call
struct MyData {
    static let data = Embedded.getJSON(Bundle.module, path: "data.json", as: DataModel.self)
}

// ⚠️ Warning with property wrapper on static var
struct BadExample {
    @Embedded.JSON(Bundle.module, path: "config.json")
    static var config: Config  // Swift 6 warning: static var not concurrency-safe
}

Requirements

  • Swift 6.0+
  • iOS 15.0+ / macOS 12.0+ / tvOS 15.0+ / watchOS 8.0+

About

Property wrappers for embedding JSON/YAML resources in Swift projects

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Contributors 3

  •  
  •  
  •