Skip to content

Instantly share code, notes, and snippets.

@thecoolwinter
Created March 16, 2021 21:28
Show Gist options
  • Save thecoolwinter/737124963c0e48c71f125ed7c50881d2 to your computer and use it in GitHub Desktop.
Save thecoolwinter/737124963c0e48c71f125ed7c50881d2 to your computer and use it in GitHub Desktop.
Helper methods for working with Codable objects and JSON Data and dictionaries. Provides two initializers and two methods for creating a Codable object using a dictionary or a Data object. And two methods for retreiving a dictionary or Data from the Codable instance.
//
// ExtraCodable.swift
//
// Created by Khan Winter on 3/16/21.
// Copyright © 2021 WindChillMedia. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
import Foundation
/// An extension providing helper methods for working with JSON data and dictionaries
protocol ExtraCodable: Codable {
/// Initializes an ExtraCodable instance using a JSON dictionary, throws on parsing error
/// - Parameter dict: JSON dictionary, can be an array
init(_ dict: [String: Any]) throws
/// Initializes an ExtraCodable instance using JSON Data, throws on parsing error
/// - Parameter data: Raw JSON Data, can be an array
init(_ data: Data) throws
/// Serializes the ExtraCoable instance to a JSON Dictionary, throws on parsing error
func getJSONDictionary() throws -> [String: Any]
/// Serializes the ExtraCoable instance into JSON Data, throws on parsing error
func getJSONData() throws -> Data
/// Optional wrapper for the `getJSONDictionary()` method, nil on parsing error
var jsonData: Data? { get }
/// Optional wrapper for the `getJOSNData()` method, nil on parsing error
var jsonDictionary: [String: Any]? { get }
}
extension ExtraCodable {
//MARK: - Init
init(_ dict: [String: Any]) throws {
let jsonData = try JSONSerialization.data(withJSONObject: dict, options: [])
let decoder = JSONDecoder()
self = try decoder.decode(Self.self, from: jsonData)
}
init(_ data: Data) throws {
let decoder = JSONDecoder()
self = try decoder.decode(Self.self, from: data)
}
//MARK: - Data
func getJSONDictionary() throws -> [String: Any] {
let data = try getJSONData()
guard let dictionary = try JSONSerialization.jsonObject(with: data, options: .allowFragments) as? [String: Any] else {
throw NSError()
}
return dictionary
}
func getJSONData() throws -> Data {
let encoder = JSONEncoder()
encoder.dateEncodingStrategy = .secondsSince1970
return try encoder.encode(self)
}
//MARK: - Properties
var jsonData: Data? {
get {
return try? getJSONData()
}
}
var jsonDictionary: [String: Any]? {
get {
return try? getJSONDictionary()
}
}
}
@thecoolwinter
Copy link
Author

thecoolwinter commented Mar 16, 2021

Sample usage:

struct MeetingNotes: ExtraCodable {
  var date: Date
  var message: String
}

let dataFromServer = Data()
do {
  /// Initialize from `Data`
  let note = try MeetingNotes(dataFromServer)

  note.date = Date()

  /// Grab a dictionary from the object
  let dataToReturn = try note.getJSONDictionary()

} catch {
  print(error)
}

Or a real-world example with Firebase's Firestore

Firestore.firestore().collection("MeetingNotes").getDocuments { (snapshot, error) in
    guard let snapshot = snapshot, error == nil else {
        return
    }
    var notes: [MeetingNote] = []
    for document in snapshot.documents {
        if let note = try? MeetingNote(document.data()) { // Get the object from the firestore `data()` preoperty
            notes.append(note)
        }
    }
    return notes
}

Also please note
The default implementation sets the JSON serializer to use dateEncodingStrategy = .secondsSince1970 which will convert Dates to Doubles. Simply comment out those lines and it'll do dates normally.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment