I have a Dictionary<ObjcType, SwiftType>
that I have to serialize to and deserialize from JSON. SwiftType
conforms to Codable
. ObjcType
is a class written in Objective-C (used by one of the libraries I heavily depend on, so cannot change it to be in Swift). All properties of ObjcType
are either NSString
or BOOL
.
I did some search and added these functions to ObjcType
, to give it some semblance of serialization:
-(NSString*)GetJSON{
NSError *writeError = nil;
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:self options:NSJSONWritingPrettyPrinted error:&writeError];
NSString *jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
return jsonString;
}
- (instancetype)initWithObject:(NSString *)jsonString {
self = [super init];
if (self != nil)
{
NSData* jsonData = [jsonString dataUsingEncoding:NSUTF8StringEncoding];
NSError *error = nil;
NSDictionary *object = [NSJSONSerialization
JSONObjectWithData:jsonData
options:0
error:&error];
for (NSString *dictionaryKey in object) {
self.field1 = [[object valueForKey:dictionaryKey] objectForKey:@"field1"];
self.field2 = [[object valueForKey:dictionaryKey] objectForKey:@"field2"];
self.field3 = [[object valueForKey:dictionaryKey] objectForKey:@"field3"];
}
}
return self;
}
So now my ObjcType
can create its own text to put into that JSON.
But I cannot figure out how to make a wrapper that will encode\decode this dictionary. Any help?
P.S. even better is it could work with OrderedDictionary
from OrderedCollections
module instead of normal Dictionary
.
You can write a wrapper like this:
@propertyWrapper
struct Wrapper: Codable {
struct StringCodingKey: CodingKey {
var intValue: Int? { nil }
let stringValue: String
init?(intValue: Int) { return nil }
init(stringValue value: String) { stringValue = value }
}
var wrappedValue: [ObjcType: SwiftType]
func encode(to encoder: any Encoder) throws {
var container = encoder.container(keyedBy: StringCodingKey.self)
for (key, value) in wrappedValue {
// assuming there is property called 'jsonString'
let keyString = key.jsonString
try container.encode(value, forKey: StringCodingKey(stringValue: keyString))
}
}
init(wrappedValue: [ObjcType : SwiftType]) {
self.wrappedValue = wrappedValue
}
init(from decoder: any Decoder) throws {
var wrapped = [ObjcType: SwiftType]()
let container = try decoder.container(keyedBy: StringCodingKey.self)
for key in container.allKeys {
// assuming such an initialiser exists
let objcKey = ObjcType(json: key.stringValue)
wrapped[objcKey] = try container.decode(SwiftType.self, forKey: key)
}
self.wrappedValue = wrapped
}
}
Usage:
// decoding
JSONDecoder().decode(Wrapper.self, from: someJson).wrappedValue
// encoding
JSONEncoder().encode(Wrapper(wrapped: yourActualDictionary))
You can also use it as a property wrapper in other Codable
types:
struct SomeOtherCodableType: Codable {
@Wrapper var dict: [ObjcType: SwiftType]
}