I have a problem with my project, when my App starts, the configuration is automatically updated from the server, the json from server like this:
{
"version":1
"config":
{
"key1": {xxx}
"key2": {xxx}
"key3": {xxx}
"key4": {xxx}
"key5": {xxx}
"key6": {xxx}
}
And I use the ObjectMapper
to convert json to model, like this:
struct GlobalConfig: Mappable {
var version = 0
var key1: [String: [LocalizedText]]?
var key2: [RouteObject]?
var key3: UrlConfig?
var key4: [String: [String: [[String: Any]]]]?
var key5: DegreeInfoList?
var key6: [String: String]?
init?(map: Map) { }
mutating func mapping(map: Map) {
version <- map["version"]
key1 <- map["key1"]
key2 <- map["key2"]
key3 <- map["key3"]
key4 <- map["key4"]
key5 <- map["key5"]
key6 <- map["key6"]
}
Now there is a problem, when the json from server is large, it will consume a lot of traffic. In fact, some configurations do not need to be updated. So the json from server will like this:
{
"version":2
"config":
{
"key1": {xxx}
"key2": {xxx}
}
It means key1
and key2
will be updated, key3
, key4
, key5
and key6
keep the old values.
How to handle the JSON?
I don't understand what's the Mapper
object is. It can be done with Codable
.
You can do like this to update your local config with incoming config.
struct GlobalConfig: Codable {
var version = 0
var key1: [String: [LocalizedText]]?
var key2: [RouteObject]?
var key3: UrlConfig?
var key4: [String: [String: [[String: Any]]]]?
var key5: DegreeInfoList?
var key6: [String: String]?
mutating func combine(with config: GlobalConfig) {
if let k = config.key1 { key1 = k }
if let k = config.key2 { key2 = k }
...
}
}
class App {
var config: GlobalConfig
func receiveConfig(newConfig: GlobalConfig) {
config.combine(with: newConfig)
}
}
Edited :
In response to comment, here a possible solution for 'deep' combine.
Very roughly :
extension Dictionary {
func combine(with dictionary: Dictionary) {
}
}
extension Array {
func combine(with array: Array) {
}
}
protocol Combinable: Codable {
func combine(with object: Self)
}
struct GlobalConfig: Combinable {
var version = 0
var key1: [String: [LocalizedText]]?
var key2: [RouteObject]?
mutating func combine(with config: GlobalConfig) {
if let k = config.key1 { key1.combine(with: k) }
if let k = config.key2 { key2.combine(with: k) }
...
}
}
This is another subject, but types like [String: [String: [[String: Any]]]]
should be avoided.
It is hard to read, debug, and combine ;)
I think it is better to use structured sub-models. Much safer, scalable and 'documentable'.
struct GlobalConfig: Codable {
...
// Old format: var key4: [String: [String: [[String: Any]]]]?
var key4: MegaSettings?
...
}
struct MegaSettings: Combinable {
struct ScreenOptions: Combinable {
var options: [String: Any]
}
struct DisplayOptions: Combinable {
struct DeviceDisplayOptions {
var screen: [ScreenOptions]
}
var optionsPerDevice: [String: DeviceDisplayOptions]
}
var displayOptions: [String: DisplayOptions]
}