{"id":33899982,"url":"https://github.com/grdsdev/swift-vcr","last_synced_at":"2026-06-06T00:31:24.590Z","repository":{"id":320355960,"uuid":"1081776405","full_name":"grdsdev/swift-vcr","owner":"grdsdev","description":"Record your test suite's HTTP interactions and replay them during future test runs for fast, deterministic, accurate tests.","archived":false,"fork":false,"pushed_at":"2025-10-30T12:38:29.000Z","size":35,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-14T23:05:00.321Z","etag":null,"topics":["http","swift","swift-package","testing","urlsession","vcr"],"latest_commit_sha":null,"homepage":null,"language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/grdsdev.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-10-23T09:17:24.000Z","updated_at":"2025-10-23T10:57:16.000Z","dependencies_parsed_at":"2025-12-12T02:06:24.308Z","dependency_job_id":null,"html_url":"https://github.com/grdsdev/swift-vcr","commit_stats":null,"previous_names":["grdsdev/swift-vcr"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/grdsdev/swift-vcr","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/grdsdev%2Fswift-vcr","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/grdsdev%2Fswift-vcr/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/grdsdev%2Fswift-vcr/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/grdsdev%2Fswift-vcr/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/grdsdev","download_url":"https://codeload.github.com/grdsdev/swift-vcr/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/grdsdev%2Fswift-vcr/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33965591,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-05T02:00:06.157Z","response_time":120,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["http","swift","swift-package","testing","urlsession","vcr"],"created_at":"2025-12-11T22:52:27.620Z","updated_at":"2026-06-06T00:31:24.584Z","avatar_url":"https://github.com/grdsdev.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Swift VCR\n\nA Swift port of the popular [VCR](https://github.com/vcr/vcr) Ruby gem. Record your test suite's HTTP interactions and replay them during future test runs for fast, deterministic, accurate tests.\n\n## Features\n\n- **Record \u0026 Replay**: Automatically record HTTP interactions and replay them in subsequent test runs\n- **URLSession Support**: Works seamlessly with Foundation's URLSession\n- **Multiple Record Modes**: Control when and how interactions are recorded\n- **Flexible Matching**: Match requests by method, URI, headers, or body\n- **JSON Storage**: Human-readable cassette files in JSON format\n- **Thread-Safe**: Built with Swift concurrency in mind\n- **Swift 6 Ready**: Fully compatible with Swift 6 and modern concurrency\n\n## Installation\n\nAdd Swift VCR to your `Package.swift`:\n\n```swift\ndependencies: [\n    .package(url: \"https://github.com/grdsdev/swift-vcr.git\", from: \"0.1.0\")\n]\n```\n\n## Quick Start\n\n### 1. Configure VCR\n\n```swift\nimport VCR\n\n// In your test setup\nVCR.configure(cassetteLibraryDirectory: \"fixtures/cassettes\")\n```\n\n### 2. Use a Cassette\n\n```swift\nfunc testAPIRequest() async throws {\n    // Create a VCR-enabled URLSession\n    let session = VCR.urlSession()\n\n    try await VCR.shared.useCassette(\"my_api_test\") {\n        let url = URL(string: \"https://api.example.com/data\")!\n        let (data, response) = try await session.data(from: url)\n\n        // Your assertions here\n        #expect(response.statusCode == 200)\n    }\n}\n```\n\n### 3. Run Your Tests\n\n- **First run**: VCR records the HTTP interaction to `fixtures/cassettes/my_api_test.json`\n- **Subsequent runs**: VCR replays the recorded interaction - no network calls!\n\n## Usage\n\n### Record Modes\n\nControl when interactions are recorded:\n\n```swift\n// Record once, then replay (default)\ntry await VCR.shared.useCassette(\"test\", recordMode: .once) {\n    // Your code\n}\n\n// Record new interactions not in cassette\ntry await VCR.shared.useCassette(\"test\", recordMode: .newEpisodes) {\n    // Your code\n}\n\n// Always record (overwrite cassette)\ntry await VCR.shared.useCassette(\"test\", recordMode: .all) {\n    // Your code\n}\n\n// Never record (error if interaction not found)\ntry await VCR.shared.useCassette(\"test\", recordMode: .none) {\n    // Your code\n}\n```\n\n### Request Matching\n\nChoose how requests are matched to recorded interactions:\n\n```swift\n// Match by HTTP method and URI (default)\ntry await VCR.shared.useCassette(\"test\", matcher: .methodAndURI) {\n    // Your code\n}\n\n// Match by method, URI, and request body\ntry await VCR.shared.useCassette(\"test\", matcher: .methodURIAndBody) {\n    // Your code\n}\n```\n\n### Manual Cassette Management\n\nFor more control, manually insert and eject cassettes:\n\n```swift\n// Insert a cassette\ntry VCR.shared.insertCassette(\"my_cassette\", recordMode: .once)\n\n// Create VCR-enabled session and make requests\nlet session = VCR.urlSession()\nlet (data, _) = try await session.data(from: url)\n\n// Eject and save\ntry VCR.shared.ejectCassette()\n```\n\n### Advanced Configuration\n\n```swift\nlet config = VCRConfiguration(\n    cassetteLibraryDirectory: URL(fileURLWithPath: \"/path/to/cassettes\"),\n    defaultRecordMode: .newEpisodes,\n    defaultMatcher: .methodAndURI\n)\nVCR.shared.configure(config)\n```\n\n## Cassette File Format\n\nCassettes are stored as JSON files with a clean, readable format:\n\n```json\n{\n  \"name\" : \"my_api_test\",\n  \"record_mode\" : \"once\",\n  \"matcher\" : \"method_uri\",\n  \"interactions\" : [\n    {\n      \"request\" : {\n        \"method\" : \"GET\",\n        \"url\" : \"https://api.example.com/data\",\n        \"headers\" : {\n          \"Accept\" : \"application/json\"\n        }\n      },\n      \"response\" : {\n        \"statusCode\" : 200,\n        \"headers\" : {\n          \"Content-Type\" : \"application/json\"\n        },\n        \"body\" : \"eyJzdGF0dXMiOiJvayJ9\"\n      },\n      \"recordedAt\" : \"2025-10-23T09:00:00Z\"\n    }\n  ]\n}\n```\n\n## Important: URLSession Configuration\n\nSwift VCR uses a custom `URLProtocol` to intercept HTTP requests. **You must use a VCR-enabled URLSession**:\n\n```swift\n// ✅ Correct - use VCR.urlSession()\nlet session = VCR.urlSession()\n\n// ❌ Won't work - URLSession.shared doesn't use custom protocols\nlet session = URLSession.shared\n```\n\n## Examples\n\n### Testing an API Client\n\n```swift\nimport Testing\nimport VCR\n\n@Suite(\"API Client Tests\")\nstruct APIClientTests {\n    @Test func fetchUser() async throws {\n        VCR.configure(cassetteLibraryDirectory: \"Tests/Fixtures/Cassettes\")\n\n        let client = APIClient(session: VCR.urlSession())\n\n        let user = try await VCR.shared.useCassette(\"fetch_user\") {\n            try await client.fetchUser(id: 123)\n        }\n\n        #expect(user.name == \"John Doe\")\n    }\n}\n```\n\n### POST Requests with Body Matching\n\n```swift\n@Test func createUser() async throws {\n    VCR.configure(cassetteLibraryDirectory: \"Tests/Fixtures\")\n\n    try await VCR.shared.useCassette(\"create_user\", matcher: .methodURIAndBody) {\n        var request = URLRequest(url: URL(string: \"https://api.example.com/users\")!)\n        request.httpMethod = \"POST\"\n        request.httpBody = try JSONEncoder().encode(newUser)\n\n        let (data, response) = try await VCR.urlSession().data(for: request)\n        #expect((response as! HTTPURLResponse).statusCode == 201)\n    }\n}\n```\n\n## Differences from Ruby VCR\n\nSwift VCR focuses on core functionality for Swift/URLSession:\n\n- **URLSession only**: Unlike Ruby VCR which supports multiple HTTP libraries, Swift VCR focuses on URLSession\n- **JSON cassettes**: Uses JSON instead of YAML for better Swift compatibility\n- **Async/await first**: Built for modern Swift concurrency\n- **Type-safe**: Leverages Swift's type system for safer cassette handling\n\n## Requirements\n\n- Swift 6.0+\n- iOS 13+, macOS 10.15+, or Linux\n\n## Contributing\n\nContributions are welcome! Please feel free to submit a Pull Request.\n\n## License\n\nMIT License - see [LICENSE](LICENSE) file for details\n\n## Acknowledgments\n\nInspired by the excellent [VCR](https://github.com/vcr/vcr) Ruby gem by Myron Marston and the VCR contributors.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgrdsdev%2Fswift-vcr","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgrdsdev%2Fswift-vcr","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgrdsdev%2Fswift-vcr/lists"}