{"id":24129285,"url":"https://github.com/rsyncosx/decodeencodegeneric","last_synced_at":"2026-05-13T04:36:55.490Z","repository":{"id":253645919,"uuid":"844105879","full_name":"rsyncOSX/DecodeEncodeGeneric","owner":"rsyncOSX","description":null,"archived":false,"fork":false,"pushed_at":"2024-09-10T16:08:00.000Z","size":17,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-01-11T19:20:04.652Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/rsyncOSX.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"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}},"created_at":"2024-08-18T12:02:18.000Z","updated_at":"2024-09-10T16:08:14.000Z","dependencies_parsed_at":"2024-09-10T17:41:30.742Z","dependency_job_id":null,"html_url":"https://github.com/rsyncOSX/DecodeEncodeGeneric","commit_stats":null,"previous_names":["rsyncosx/decodeencodegeneric"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rsyncOSX%2FDecodeEncodeGeneric","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rsyncOSX%2FDecodeEncodeGeneric/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rsyncOSX%2FDecodeEncodeGeneric/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rsyncOSX%2FDecodeEncodeGeneric/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rsyncOSX","download_url":"https://codeload.github.com/rsyncOSX/DecodeEncodeGeneric/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":241317601,"owners_count":19943202,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":[],"created_at":"2025-01-11T19:20:14.494Z","updated_at":"2026-05-13T04:36:55.484Z","avatar_url":"https://github.com/rsyncOSX.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"## Hi there 👋\n\nThis package is generic code for decode and encode JSON data, as part of reading and writing data to local storage. Data are tasks, logrecords and user configuration.\n\n# DecodeEncodeGeneric Documentation\n\nA Swift package for encoding and decoding JSON data with support for strings, files, and remote URLs.\n\n## Overview\n\nDecodeEncodeGeneric provides a type-safe, generic approach to working with JSON in Swift. It handles common encoding/decoding scenarios while providing detailed error information and flexible configuration options.\n\n## Features\n\n- Generic decoding from strings, files, URLs, and raw Data\n- Generic encoding to strings, files, and Data\n- Configurable JSON encoder/decoder with custom strategies\n- Comprehensive error handling with descriptive error messages\n- Support for both single objects and arrays\n- Async/await support for remote URL fetching\n- Result-based API alternatives\n- Pretty-printing convenience methods\n\n## DecodeGeneric\n\n### Initialization\n\n```swift\n// Basic initialization\nlet decoder = DecodeGeneric()\n\n// Custom configuration\nlet decoder = DecodeGeneric(\n    urlSession: .shared,\n    jsonDecoder: JSONDecoder(),\n    dateDecodingStrategy: .iso8601,\n    keyDecodingStrategy: .convertFromSnakeCase\n)\n```\n\n### Decoding from Strings\n\n```swift\nlet jsonString = \"\"\"\n{\n    \"name\": \"John\",\n    \"age\": 30\n}\n\"\"\"\n\nstruct Person: Decodable {\n    let name: String\n    let age: Int\n}\n\ndo {\n    let person = try decoder.decode(Person.self, fromString: jsonString)\n    print(person.name) // \"John\"\n} catch {\n    print(\"Error: \\(error.localizedDescription)\")\n}\n```\n\n### Decoding Arrays from Strings\n\n```swift\nlet jsonArray = \"\"\"\n[\n    {\"name\": \"John\", \"age\": 30},\n    {\"name\": \"Jane\", \"age\": 25}\n]\n\"\"\"\n\ndo {\n    let people = try decoder.decodeArray(Person.self, fromString: jsonArray)\n    print(people.count) // 2\n} catch {\n    print(\"Error: \\(error)\")\n}\n```\n\n### Decoding from Files\n\n```swift\ndo {\n    let person = try decoder.decode(Person.self, fromFile: \"/path/to/file.json\")\n    // or for arrays\n    let people = try decoder.decodeArray(Person.self, fromFile: \"/path/to/array.json\")\n} catch {\n    print(\"Error: \\(error)\")\n}\n```\n\n### Decoding from URLs (Async)\n\nRequires macOS 12.0+ or iOS 15.0+\n\n```swift\ndo {\n    let person = try await decoder.decode(\n        Person.self, \n        fromURL: \"https://api.example.com/person\"\n    )\n    \n    // or for arrays\n    let people = try await decoder.decodeArray(\n        Person.self,\n        fromURL: \"https://api.example.com/people\"\n    )\n} catch {\n    print(\"Error: \\(error)\")\n}\n```\n\n### Decoding from Data\n\n```swift\nlet jsonData: Data = // ... your data\ndo {\n    let person = try decoder.decode(Person.self, from: jsonData)\n    // or for arrays\n    let people = try decoder.decodeArray(Person.self, from: jsonData)\n} catch {\n    print(\"Error: \\(error)\")\n}\n```\n\n## EncodeGeneric\n\n### Initialization\n\n```swift\n// Basic initialization\nlet encoder = EncodeGeneric()\n\n// Custom configuration\nlet encoder = EncodeGeneric(\n    jsonEncoder: JSONEncoder(),\n    outputFormatting: [.prettyPrinted, .sortedKeys],\n    dateEncodingStrategy: .iso8601,\n    keyEncodingStrategy: .convertToSnakeCase\n)\n\n// Pretty-printed convenience initializer\nlet prettyEncoder = EncodeGeneric.prettyPrinted()\n```\n\n### Encoding to Data\n\n```swift\nstruct Person: Encodable {\n    let name: String\n    let age: Int\n}\n\nlet person = Person(name: \"John\", age: 30)\n\ndo {\n    let data = try encoder.encode(person)\n    // or for arrays\n    let people = [person]\n    let arrayData = try encoder.encodeArray(people)\n} catch {\n    print(\"Error: \\(error)\")\n}\n```\n\n### Encoding to String\n\n```swift\ndo {\n    let jsonString = try encoder.encodeToString(person)\n    print(jsonString) // {\"name\":\"John\",\"age\":30}\n    \n    // Pretty-printed string\n    let prettyString = try encoder.encodeToPrettyString(person)\n    \n    // Array to string\n    let arrayString = try encoder.encodeArrayToString([person])\n} catch {\n    print(\"Error: \\(error)\")\n}\n```\n\n### Encoding to File\n\n```swift\ndo {\n    try encoder.encode(person, toFile: \"/path/to/output.json\")\n    \n    // With atomic write option (default is true)\n    try encoder.encode(person, toFile: \"/path/to/output.json\", atomically: true)\n    \n    // Array to file\n    try encoder.encodeArray([person], toFile: \"/path/to/array.json\")\n} catch {\n    print(\"Error: \\(error)\")\n}\n```\n\n### Result-Based API\n\nFor situations where you prefer Result types over throwing functions:\n\n```swift\nlet result = encoder.encodeResult(person)\nswitch result {\ncase .success(let data):\n    print(\"Encoded successfully\")\ncase .failure(let error):\n    print(\"Error: \\(error)\")\n}\n\nlet stringResult = encoder.encodeToStringResult(person)\nswitch stringResult {\ncase .success(let jsonString):\n    print(jsonString)\ncase .failure(let error):\n    print(\"Error: \\(error)\")\n}\n```\n\n## Error Handling\n\n### DecodeError\n\nThe package provides comprehensive error types with descriptive messages:\n\n- `.invalidStringEncoding` - Failed to encode string as UTF-8 data\n- `.invalidURL` - Invalid URL string\n- `.invalidFilePath` - Invalid file path\n- `.decodingFailed(Error)` - JSON decoding failed with underlying error\n- `.fileReadFailed(Error)` - Failed to read file with underlying error\n\n### EncodeError\n\n- `.encodingFailed(Error)` - JSON encoding failed with underlying error\n- `.fileWriteFailed(Error)` - Failed to write file with underlying error\n- `.invalidFilePath` - Invalid file path\n- `.stringConversionFailed` - Failed to convert data to UTF-8 string\n\n## Advanced Usage\n\n### Custom Date Strategies\n\n```swift\nlet decoder = DecodeGeneric(\n    dateDecodingStrategy: .iso8601\n)\n\nlet encoder = EncodeGeneric(\n    dateEncodingStrategy: .iso8601\n)\n```\n\n### Custom Key Strategies\n\n```swift\nlet decoder = DecodeGeneric(\n    keyDecodingStrategy: .convertFromSnakeCase\n)\n\nlet encoder = EncodeGeneric(\n    keyEncodingStrategy: .convertToSnakeCase\n)\n```\n\n### Custom URLSession\n\n```swift\nlet config = URLSessionConfiguration.default\nconfig.timeoutIntervalForRequest = 30\nlet customSession = URLSession(configuration: config)\n\nlet decoder = DecodeGeneric(urlSession: customSession)\n```\n\n## Internals (Maintainers)\n\n- Decoding flows through `decodeData`/`decodeDataArray` helpers so all entry points share the same error mapping to `DecodeError`.\n- Encoding uses `some Encodable` for tighter type safety while still accepting any codable type; arrays use the same helper for consistent failures.\n- `prettyPrinted()` builds an encoder with sorted keys and pretty print for deterministic output without altering the primary encoder configuration.\n- File helpers guard empty paths up-front and return `.invalidFilePath`; IO and string conversion errors surface as typed `EncodeError`/`DecodeError` for clearer logging and tests.\n\n## Requirements\n\n- Swift 5.7+\n- macOS 10.15+ / iOS 13.0+ (for basic functionality)\n- macOS 12.0+ / iOS 15.0+ (for async URL decoding)\n\n## Thread Safety\n\nBoth `DecodeGeneric` and `EncodeGeneric` are marked as `final` classes. The underlying `JSONDecoder` and `JSONEncoder` are thread-safe for read operations, but you should create separate instances for concurrent encoding/decoding operations.\n\n## License\n\nMIT\n\n## Author\n\nThomas Evensen","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frsyncosx%2Fdecodeencodegeneric","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frsyncosx%2Fdecodeencodegeneric","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frsyncosx%2Fdecodeencodegeneric/lists"}