Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save YannisDC/f21a7c7ceb03363b799b881b8b82ac72 to your computer and use it in GitHub Desktop.
Save YannisDC/f21a7c7ceb03363b799b881b8b82ac72 to your computer and use it in GitHub Desktop.
This works but not sure how fragile
import Cocoa
fileprivate extension CodingUserInfoKey {
static let fragmentBoxedType = CodingUserInfoKey(
rawValue: "CodingUserInfoKey.fragmentBoxedType"
)!
}
extension JSONDecoder {
private struct FragmentDecodingBox<T : Decodable> : Decodable {
var value: T
init(from decoder: Decoder) throws {
let type = decoder.userInfo[.fragmentBoxedType] as! T.Type
var container = try decoder.unkeyedContainer()
self.value = try container.decode(type)
}
}
private func copy() -> JSONDecoder {
let decoder = JSONDecoder()
decoder.dataDecodingStrategy = dataDecodingStrategy
decoder.dateDecodingStrategy = dateDecodingStrategy
decoder.keyDecodingStrategy = keyDecodingStrategy
decoder.nonConformingFloatDecodingStrategy = nonConformingFloatDecodingStrategy
decoder.userInfo = userInfo
return decoder
}
public func decode<T : Decodable>(
_ type: T.Type, from data: Data, allowFragments: Bool
) throws -> T {
// If we're not allowing fragments, just delegate to decode(_:from:).
guard allowFragments else { return try decode(type, from: data) }
if "\(T.self)" == "String" { return String(data: data, encoding: .utf8) as! T }
// Box the JSON object in an array so we can pass it off to JSONDecoder.
// The round-tripping through JSONSerialization isn't ideal, but it
// ensures we do The Right Thing regardless of the encoding of `data`.
//
// FIX-ME: Try to detect encoding and add the '[]' delimeters directly to
// the data without going through `JSONSerialization`.
let jsonObject = try JSONSerialization
.jsonObject(with: data, options: .allowFragments)
let boxedData = try JSONSerialization.data(withJSONObject: [jsonObject])
// Copy the decoder so we can mutate the userInfo without having to worry
// about data races.
let decoder = copy()
decoder.userInfo[.fragmentBoxedType] = type
// Use FragmentDecodingBox to decode the underlying fragment from the
// array.
//
// We're intentionally *not* doing `decode([T].self, ...)` here, as
// that loses the dynamic type passed – breaking things like:
//
// class C : Decodable {}
// class D {}
//
// let type: C.Type = D.self
// let data = ...
// let decoded = try JSONDecoder().decode(type, from: data, allowFragments: true)
//
// The above would decode a `C` instead of a `D` if we didn't preserve
// the dynamic type.
//
// (Admittedly this is a bit of contrived example, as by default such types
// would decode using keyed containers and therefore not be fragments –
// nontheless it is possible for them to implement their decoding such that
// they use a single value container).
return try decoder.decode(FragmentDecodingBox<T>.self, from: boxedData).value
}
}
struct Person: Codable {
let name: String
let age: Int
let friends: [Person]?
}
let person = Person(name: "Brad", age: 53, friends: nil)
protocol Identifiable {
var uuid: String { get set }
}
protocol Encryptable {
var encrypted: Bool { get }
func encrypt()
func decrypt()
}
public class EncryptableProperty<T: Codable>: Encryptable {
var value: T
var encryptedValue: String
var encrypted: Bool
init(value: T, encrypted: Bool) {
self.value = value
self.encrypted = encrypted
self.encryptedValue = ""
}
func encrypt() {
if value is LosslessStringConvertible, let value = value as? LosslessStringConvertible {
self.encryptedValue = value.description
} else {
do {
let object = try JSONEncoder().encode(value)
// Do actual encryption and get the signature back
if let string = String(data: object, encoding: .utf8) {
print(string)
encryptedValue = string
} else {
print("not a valid UTF-8 sequence")
}
} catch{
print("Encoding failed")
}
}
}
func decrypt() {
do {
let data : [UInt8] = [UInt8](self.encryptedValue.utf8)
// Do actual decryption and get the data back
let object = try JSONDecoder().decode(T.self, from: Data(data), allowFragments: true)
self.value = object
} catch{
print("Decoding failed")
}
}
}
protocol EncryptableObject {
func encryptObject()
func decryptObject()
}
struct Test: EncryptableObject, Identifiable {
public let testInt: EncryptableProperty<Array<String>>
var uuid: String
init(uuid: String = UUID().uuidString) {
self.testInt = EncryptableProperty<Array<String>>(value: ["one", "two"], encrypted: true)
self.uuid = uuid
}
func encryptObject() {
print("\nEncryption happens")
Mirror(reflecting: self)
.children
.forEach { (property) in
if let encryptableValue = property.value as? Encryptable {
encryptableValue.encrypt()
}
}
}
func decryptObject() {
print("\nDecryption happens")
Mirror(reflecting: self)
.children
.forEach { (property) in
if let encryptableValue = property.value as? Encryptable {
encryptableValue.decrypt()
}
}
}
}
let test = Test()
print("Value= \(test.testInt.value) EncryptedValue= \(test.testInt.encryptedValue)")
test.encryptObject()
print("Value= \(test.testInt.value) EncryptedValue= \(test.testInt.encryptedValue)")
print("\nDecrypted value gets updated")
test.testInt.encryptedValue = #"["sir", "yannis"]"#
print("Value= \(test.testInt.value) EncryptedValue= \(test.testInt.encryptedValue)")
test.decryptObject()
print("Value= \(test.testInt.value) EncryptedValue= \(test.testInt.encryptedValue)")
struct Test2: EncryptableObject, Identifiable {
public let testString: EncryptableProperty<String>
public let testInt: EncryptableProperty<String>
var uuid: String
init(uuid: String = UUID().uuidString) {
self.testString = EncryptableProperty<String>(value: "Blockstack", encrypted: true)
self.testInt = EncryptableProperty<String>(value: "Blockstack", encrypted: true)
self.uuid = uuid
}
func encryptObject() {
print("\nEncryption happens")
Mirror(reflecting: self)
.children
.forEach { (property) in
if let encryptableValue = property.value as? Encryptable {
encryptableValue.encrypt()
}
}
}
func decryptObject() {
print("\nDecryption happens")
Mirror(reflecting: self)
.children
.forEach { (property) in
if let encryptableValue = property.value as? Encryptable {
encryptableValue.decrypt()
}
}
}
}
let test2 = Test2()
print("Value= \(test2.testInt.value) EncryptedValue= \(test2.testInt.encryptedValue)")
test2.encryptObject()
print("Value= \(test2.testInt.value) EncryptedValue= \(test2.testInt.encryptedValue)")
print("\nDecrypted value gets updated")
test2.testInt.encryptedValue = "Test"
print("Value= \(test2.testInt.value) EncryptedValue= \(test2.testInt.encryptedValue)")
test2.decryptObject()
print("Value= \(test2.testInt.value) EncryptedValue= \(test2.testInt.encryptedValue)")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment