Last active
December 5, 2017 10:13
-
-
Save rugheid/78c39e7285e52cb275d9 to your computer and use it in GitHub Desktop.
This is a dictionary coder and JSON serialiser that uses NSCoding. You can use it to convert any object conforming to NSCoding to dictionaries and JSON. I wrote a blog post about how to use this class and how it works here: http://www.rugenheidbuchel.be/2015/10/25/nscoding-to-dictionary-and-json/
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// RHCustomObjectCoder.swift | |
// | |
// Created by Rugen Heidbuchel on 13/10/15. | |
// Copyright © 2015 Rugen Heidbuchel. All rights reserved. | |
// | |
import Foundation | |
class RHCustomObjectCoder: NSCoder { | |
// MARK: Properties | |
private var dictionary = [String:AnyObject]() | |
// MARK: Settings | |
var base64EncodingOptions = NSDataBase64EncodingOptions(rawValue: 0) | |
var jsonWriteOptions = NSJSONWritingOptions.PrettyPrinted | |
// MARK: Dictionary Coding | |
class func encodeObjectAsDictionary(object: NSCoding, | |
alsoEncodeSubobjects: Bool = false, | |
jsonCompatibleEncoding: Bool = false) | |
-> [String: AnyObject] { | |
let coder = RHCustomObjectCoder() | |
return coder.encodeObjectAsDictionary(object, | |
alsoEncodeSubobjects: alsoEncodeSubobjects, | |
jsonCompatibleEncoding: jsonCompatibleEncoding) | |
} | |
func encodeObjectAsDictionary(object: NSCoding, | |
alsoEncodeSubobjects: Bool = false, | |
jsonCompatibleEncoding: Bool = false) | |
-> [String: AnyObject] { | |
object.encodeWithCoder(self) | |
if !(alsoEncodeSubobjects || jsonCompatibleEncoding) { | |
return dictionary | |
} else { | |
return self.safeVersionOfObject(dictionary, | |
alsoEncodeSubobjects: alsoEncodeSubobjects, | |
jsonCompatibleEncoding: jsonCompatibleEncoding) as! [String: AnyObject] | |
} | |
} | |
// MARK: Array Coding | |
class func encodeArrayOfCustomObjects <CustomObjectType: NSCoding> | |
(objects: [CustomObjectType], | |
alsoEncodeSubobjects: Bool = false, | |
jsonCompatibleEncoding: Bool = false) | |
-> [[String: AnyObject]] { | |
let coder = RHCustomObjectCoder() | |
return coder.encodeArrayOfCustomObjects(objects, | |
alsoEncodeSubobjects: alsoEncodeSubobjects, | |
jsonCompatibleEncoding: jsonCompatibleEncoding) | |
} | |
func encodeArrayOfCustomObjects <CustomObjectType: NSCoding> | |
(objects: [CustomObjectType], | |
alsoEncodeSubobjects: Bool = false, | |
jsonCompatibleEncoding: Bool = false) | |
-> [[String: AnyObject]] { | |
var encodedArray = [[String: AnyObject]]() | |
for object in objects { | |
encodedArray.append(RHCustomObjectCoder.encodeObjectAsDictionary(object, | |
alsoEncodeSubobjects: alsoEncodeSubobjects, | |
jsonCompatibleEncoding: jsonCompatibleEncoding)) | |
} | |
return encodedArray | |
} | |
// MARK: JSON Coding | |
class func encodeObjectAsJSONString(object: NSCoding) throws -> String { | |
let coder = RHCustomObjectCoder() | |
return try! coder.encodeObjectAsJSONString(object) | |
} | |
func encodeObjectAsJSONString(object: NSCoding) throws -> String { | |
let jsonData = try! NSJSONSerialization.dataWithJSONObject( | |
self.encodeObjectAsDictionary(object, | |
alsoEncodeSubobjects: true, jsonCompatibleEncoding: true), | |
options: self.jsonWriteOptions) | |
return String(data: jsonData, encoding: NSUTF8StringEncoding)! | |
} | |
class func encodeArrayOfCustomObjectsAsJSONString <CustomObjectType: NSCoding> | |
(objects: [CustomObjectType]) throws -> String { | |
let coder = RHCustomObjectCoder() | |
return try! coder.encodeArrayOfCustomObjectsAsJSONString(objects) | |
} | |
func encodeArrayOfCustomObjectsAsJSONString <CustomObjectType: NSCoding> | |
(objects: [CustomObjectType]) throws -> String { | |
let jsonData = try! NSJSONSerialization.dataWithJSONObject( | |
self.encodeArrayOfCustomObjects(objects, | |
alsoEncodeSubobjects: true, | |
jsonCompatibleEncoding: true), | |
options: self.jsonWriteOptions) | |
return String(data: jsonData, encoding: NSUTF8StringEncoding)! | |
} | |
// MARK: - NSCoder Overrides | |
override var allowsKeyedCoding: Bool { | |
return true | |
} | |
override func containsValueForKey(key: String) -> Bool { | |
return dictionary[key] != nil | |
} | |
// MARK: Encoding | |
override func encodeBool(boolv: Bool, forKey key: String) { | |
dictionary[key] = NSNumber(bool: boolv) | |
} | |
override func encodeBytes(bytesp: UnsafePointer<UInt8>, length lenv: Int, forKey key: String) { | |
NSException(name: "Unsupported", | |
reason: "The encoding of bytes is not supported.", | |
userInfo: nil).raise() | |
} | |
override func encodeConditionalObject(objv: AnyObject?, forKey key: String) { | |
NSException(name: "Unsupported", | |
reason: "The conditional encoding of objects is not supported.", | |
userInfo: nil).raise() | |
} | |
override func encodeDouble(realv: Double, forKey key: String) { | |
dictionary[key] = NSNumber(double: realv) | |
} | |
override func encodeFloat(realv: Float, forKey key: String) { | |
dictionary[key] = NSNumber(float: realv) | |
} | |
override func encodeInt(intv: Int32, forKey key: String) { | |
dictionary[key] = NSNumber(int: intv) | |
} | |
override func encodeInt32(intv: Int32, forKey key: String) { | |
dictionary[key] = NSNumber(int: intv) | |
} | |
override func encodeInt64(intv: Int64, forKey key: String) { | |
dictionary[key] = NSNumber(integer: Int(intv)) | |
} | |
override func encodeInteger(intv: Int, forKey key: String) { | |
dictionary[key] = NSNumber(integer: intv) | |
} | |
override func encodeObject(objv: AnyObject?, forKey key: String) { | |
if let object = objv { | |
dictionary[key] = object | |
} | |
} | |
#if os(OSX) | |
override func encodePoint(point: NSPoint, forKey key: String) { | |
NSException(name: "Unsupported", | |
reason: "The encoding of NSPoint is not supported.", | |
userInfo: nil).raise() | |
} | |
override func encodeRect(rect: NSRect, forKey key: String) { | |
NSException(name: "Unsupported", | |
reason: "The encoding of NSRect is not supported.", | |
userInfo: nil).raise() | |
} | |
override func encodeSize(size: NSSize, forKey key: String) { | |
NSException(name: "Unsupported", | |
reason: "The encoding of NSSize is not supported.", | |
userInfo: nil).raise() | |
} | |
#endif | |
override func encodeValueOfObjCType(type: UnsafePointer<Int8>, at addr: UnsafePointer<Void>) { | |
NSException(name: "Unsupported", | |
reason: "The encoding of values of a given Objective-C type is not supported.", | |
userInfo: nil).raise() | |
} | |
// MARK: Private Helpers | |
private func dateToISOString(date: NSDate) -> String { | |
let dateFormatter = NSDateFormatter() | |
dateFormatter.timeZone = NSTimeZone(abbreviation: "UTC") | |
dateFormatter.dateFormat = "YYYY-MM-dd'T'HH:mm:ss.SSS'Z'" | |
return dateFormatter.stringFromDate(date) | |
} | |
private func safeVersionOfObject(object: AnyObject, alsoEncodeSubobjects: Bool, jsonCompatibleEncoding: Bool) -> AnyObject { | |
if object is String || object is NSNumber { | |
return object | |
} else if let date = object as? NSDate { | |
return jsonCompatibleEncoding ? self.dateToISOString(date) : date | |
} else if let dictionary = object as? NSDictionary { | |
let safeDictionary = NSMutableDictionary(capacity: dictionary.count) | |
for (key, value) in dictionary { | |
safeDictionary[key as! NSCopying] = self.safeVersionOfObject(value, | |
alsoEncodeSubobjects: alsoEncodeSubobjects, | |
jsonCompatibleEncoding: jsonCompatibleEncoding) | |
} | |
return safeDictionary | |
} else if let array = object as? [AnyObject] { | |
var safeArray = [AnyObject]() | |
for value in array { | |
safeArray.append(self.safeVersionOfObject(value, | |
alsoEncodeSubobjects: alsoEncodeSubobjects, | |
jsonCompatibleEncoding: jsonCompatibleEncoding)) | |
} | |
return safeArray | |
} else if let orderedSet = object as? NSOrderedSet { | |
return self.safeVersionOfObject(orderedSet.array, | |
alsoEncodeSubobjects: alsoEncodeSubobjects, | |
jsonCompatibleEncoding: jsonCompatibleEncoding) | |
} else if let set = object as? NSSet { | |
return self.safeVersionOfObject(set.allObjects, | |
alsoEncodeSubobjects: alsoEncodeSubobjects, | |
jsonCompatibleEncoding: jsonCompatibleEncoding) | |
} else if let data = object as? NSData { | |
return jsonCompatibleEncoding ? | |
data.base64EncodedStringWithOptions(self.base64EncodingOptions) : | |
data | |
} else if let codingCompliant = object as? NSCoding where alsoEncodeSubobjects || jsonCompatibleEncoding { | |
return RHCustomObjectCoder.encodeObjectAsDictionary(codingCompliant, | |
alsoEncodeSubobjects: alsoEncodeSubobjects, | |
jsonCompatibleEncoding: jsonCompatibleEncoding) | |
} else { | |
NSException(name: "Unsupported", | |
reason: "The given object is not NSCoding compliant.", | |
userInfo: nil).raise() | |
fatalError() | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment