Efficiently structuring data with protocol oriented approach in Swift
Clash Royale CLAN TAG#URR8PPP
.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty margin-bottom:0;
up vote
0
down vote
favorite
I need to integrate some server API calling in which I'm likely to get response as:
+ Response 200 (application/json)
+ Body
"code": 0,
"status": "ok",
"message": "Verification Message from API",
"result":
"values": [
"verify_status": true,
"token_key": "sure1ff45jk7858b8ae88a77fa30"
]
Sometimes the response will be as:
+ Response 200 (application/json)
+ Body
"code": 0,
"status": "ok",
"message": "Update Message from API",
"result":
"values": [
"update_status": true
]
As you can see, mostly all the fields of the response body are pretty much same. The only distinction is going to be in the section:
"values": [
"verify_status": true,
"token_key": "sure1ff45jk7858b8ae88a77fa30"
]
or,
"values": [
"update_status": true
]
I have followed the Protocol Oriented Programming (POP) pattern for my use case.
Solution:
struct Value
let verifyStatus: Bool?
let updateStatus: Bool?
let tokenKey: String?
extension Value: Codable
private enum ValueCodingKeys: String, CodingKey
case verifyStatus = "verify_status"
case updateStatus = "update_status"
case tokenKey = "token_key"
func encode(to encoder: Encoder) throws
var container = encoder.container(keyedBy: ValueCodingKeys.self)
try container.encode(verifyStatus, forKey: .verifyStatus)
try container.encode(updateStatus, forKey: .updateStatus)
try container.encode(tokenKey, forKey: .tokenKey)
init(from decoder: Decoder) throws
let container = try decoder.container(keyedBy: ValueCodingKeys.self)
verifyStatus = try container.decodeIfPresent(Bool.self, forKey: .verifyStatus)
updateStatus = try container.decodeIfPresent(Bool.self, forKey: .updateStatus)
tokenKey = try container.decodeIfPresent(String.self, forKey: .tokenKey)
protocol Resultable
var values: [Value] get
struct Result: Resultable
let values: [Value]
extension Result: Codable
private enum ResultCodingKeys: String, CodingKey
case values
func encode(to encoder: Encoder) throws
var container = encoder.container(keyedBy: ResultCodingKeys.self)
try container.encode(values, forKey: .values)
init(from decoder: Decoder) throws
let container = try decoder.container(keyedBy: ResultCodingKeys.self)
values = try container.decode([Value].self, forKey: .values)
protocol Responsable
var code: Int get
var status: String get
var message: String get
var result: Result get
struct Response: Responsable
let code: Int
let status: String
let message: String
let result: Result
extension Response: Codable
private enum ResponeCodingKeys: String, CodingKey
case code
case status
case message
case result
func encode(to encoder: Encoder) throws
var container = encoder.container(keyedBy: ResponeCodingKeys.self)
try container.encode(code, forKey: .code)
try container.encode(status, forKey: .status)
try container.encode(message, forKey: .message)
try container.encode(result, forKey: .result)
init(from decoder: Decoder) throws
let container = try decoder.container(keyedBy: ResponeCodingKeys.self)
code = try container.decode(Int.self, forKey: .code)
status = try container.decode(String.self, forKey: .status)
message = try container.decode(String.self, forKey: .message)
result = try container.decode(Result.self, forKey: .result)
I think I'm over-complicating the solution. Is there any better solution to achieve this?
Do I really have to have two
protocol
defined?
json swift protocols
add a comment |Â
up vote
0
down vote
favorite
I need to integrate some server API calling in which I'm likely to get response as:
+ Response 200 (application/json)
+ Body
"code": 0,
"status": "ok",
"message": "Verification Message from API",
"result":
"values": [
"verify_status": true,
"token_key": "sure1ff45jk7858b8ae88a77fa30"
]
Sometimes the response will be as:
+ Response 200 (application/json)
+ Body
"code": 0,
"status": "ok",
"message": "Update Message from API",
"result":
"values": [
"update_status": true
]
As you can see, mostly all the fields of the response body are pretty much same. The only distinction is going to be in the section:
"values": [
"verify_status": true,
"token_key": "sure1ff45jk7858b8ae88a77fa30"
]
or,
"values": [
"update_status": true
]
I have followed the Protocol Oriented Programming (POP) pattern for my use case.
Solution:
struct Value
let verifyStatus: Bool?
let updateStatus: Bool?
let tokenKey: String?
extension Value: Codable
private enum ValueCodingKeys: String, CodingKey
case verifyStatus = "verify_status"
case updateStatus = "update_status"
case tokenKey = "token_key"
func encode(to encoder: Encoder) throws
var container = encoder.container(keyedBy: ValueCodingKeys.self)
try container.encode(verifyStatus, forKey: .verifyStatus)
try container.encode(updateStatus, forKey: .updateStatus)
try container.encode(tokenKey, forKey: .tokenKey)
init(from decoder: Decoder) throws
let container = try decoder.container(keyedBy: ValueCodingKeys.self)
verifyStatus = try container.decodeIfPresent(Bool.self, forKey: .verifyStatus)
updateStatus = try container.decodeIfPresent(Bool.self, forKey: .updateStatus)
tokenKey = try container.decodeIfPresent(String.self, forKey: .tokenKey)
protocol Resultable
var values: [Value] get
struct Result: Resultable
let values: [Value]
extension Result: Codable
private enum ResultCodingKeys: String, CodingKey
case values
func encode(to encoder: Encoder) throws
var container = encoder.container(keyedBy: ResultCodingKeys.self)
try container.encode(values, forKey: .values)
init(from decoder: Decoder) throws
let container = try decoder.container(keyedBy: ResultCodingKeys.self)
values = try container.decode([Value].self, forKey: .values)
protocol Responsable
var code: Int get
var status: String get
var message: String get
var result: Result get
struct Response: Responsable
let code: Int
let status: String
let message: String
let result: Result
extension Response: Codable
private enum ResponeCodingKeys: String, CodingKey
case code
case status
case message
case result
func encode(to encoder: Encoder) throws
var container = encoder.container(keyedBy: ResponeCodingKeys.self)
try container.encode(code, forKey: .code)
try container.encode(status, forKey: .status)
try container.encode(message, forKey: .message)
try container.encode(result, forKey: .result)
init(from decoder: Decoder) throws
let container = try decoder.container(keyedBy: ResponeCodingKeys.self)
code = try container.decode(Int.self, forKey: .code)
status = try container.decode(String.self, forKey: .status)
message = try container.decode(String.self, forKey: .message)
result = try container.decode(Result.self, forKey: .result)
I think I'm over-complicating the solution. Is there any better solution to achieve this?
Do I really have to have two
protocol
defined?
json swift protocols
add a comment |Â
up vote
0
down vote
favorite
up vote
0
down vote
favorite
I need to integrate some server API calling in which I'm likely to get response as:
+ Response 200 (application/json)
+ Body
"code": 0,
"status": "ok",
"message": "Verification Message from API",
"result":
"values": [
"verify_status": true,
"token_key": "sure1ff45jk7858b8ae88a77fa30"
]
Sometimes the response will be as:
+ Response 200 (application/json)
+ Body
"code": 0,
"status": "ok",
"message": "Update Message from API",
"result":
"values": [
"update_status": true
]
As you can see, mostly all the fields of the response body are pretty much same. The only distinction is going to be in the section:
"values": [
"verify_status": true,
"token_key": "sure1ff45jk7858b8ae88a77fa30"
]
or,
"values": [
"update_status": true
]
I have followed the Protocol Oriented Programming (POP) pattern for my use case.
Solution:
struct Value
let verifyStatus: Bool?
let updateStatus: Bool?
let tokenKey: String?
extension Value: Codable
private enum ValueCodingKeys: String, CodingKey
case verifyStatus = "verify_status"
case updateStatus = "update_status"
case tokenKey = "token_key"
func encode(to encoder: Encoder) throws
var container = encoder.container(keyedBy: ValueCodingKeys.self)
try container.encode(verifyStatus, forKey: .verifyStatus)
try container.encode(updateStatus, forKey: .updateStatus)
try container.encode(tokenKey, forKey: .tokenKey)
init(from decoder: Decoder) throws
let container = try decoder.container(keyedBy: ValueCodingKeys.self)
verifyStatus = try container.decodeIfPresent(Bool.self, forKey: .verifyStatus)
updateStatus = try container.decodeIfPresent(Bool.self, forKey: .updateStatus)
tokenKey = try container.decodeIfPresent(String.self, forKey: .tokenKey)
protocol Resultable
var values: [Value] get
struct Result: Resultable
let values: [Value]
extension Result: Codable
private enum ResultCodingKeys: String, CodingKey
case values
func encode(to encoder: Encoder) throws
var container = encoder.container(keyedBy: ResultCodingKeys.self)
try container.encode(values, forKey: .values)
init(from decoder: Decoder) throws
let container = try decoder.container(keyedBy: ResultCodingKeys.self)
values = try container.decode([Value].self, forKey: .values)
protocol Responsable
var code: Int get
var status: String get
var message: String get
var result: Result get
struct Response: Responsable
let code: Int
let status: String
let message: String
let result: Result
extension Response: Codable
private enum ResponeCodingKeys: String, CodingKey
case code
case status
case message
case result
func encode(to encoder: Encoder) throws
var container = encoder.container(keyedBy: ResponeCodingKeys.self)
try container.encode(code, forKey: .code)
try container.encode(status, forKey: .status)
try container.encode(message, forKey: .message)
try container.encode(result, forKey: .result)
init(from decoder: Decoder) throws
let container = try decoder.container(keyedBy: ResponeCodingKeys.self)
code = try container.decode(Int.self, forKey: .code)
status = try container.decode(String.self, forKey: .status)
message = try container.decode(String.self, forKey: .message)
result = try container.decode(Result.self, forKey: .result)
I think I'm over-complicating the solution. Is there any better solution to achieve this?
Do I really have to have two
protocol
defined?
json swift protocols
I need to integrate some server API calling in which I'm likely to get response as:
+ Response 200 (application/json)
+ Body
"code": 0,
"status": "ok",
"message": "Verification Message from API",
"result":
"values": [
"verify_status": true,
"token_key": "sure1ff45jk7858b8ae88a77fa30"
]
Sometimes the response will be as:
+ Response 200 (application/json)
+ Body
"code": 0,
"status": "ok",
"message": "Update Message from API",
"result":
"values": [
"update_status": true
]
As you can see, mostly all the fields of the response body are pretty much same. The only distinction is going to be in the section:
"values": [
"verify_status": true,
"token_key": "sure1ff45jk7858b8ae88a77fa30"
]
or,
"values": [
"update_status": true
]
I have followed the Protocol Oriented Programming (POP) pattern for my use case.
Solution:
struct Value
let verifyStatus: Bool?
let updateStatus: Bool?
let tokenKey: String?
extension Value: Codable
private enum ValueCodingKeys: String, CodingKey
case verifyStatus = "verify_status"
case updateStatus = "update_status"
case tokenKey = "token_key"
func encode(to encoder: Encoder) throws
var container = encoder.container(keyedBy: ValueCodingKeys.self)
try container.encode(verifyStatus, forKey: .verifyStatus)
try container.encode(updateStatus, forKey: .updateStatus)
try container.encode(tokenKey, forKey: .tokenKey)
init(from decoder: Decoder) throws
let container = try decoder.container(keyedBy: ValueCodingKeys.self)
verifyStatus = try container.decodeIfPresent(Bool.self, forKey: .verifyStatus)
updateStatus = try container.decodeIfPresent(Bool.self, forKey: .updateStatus)
tokenKey = try container.decodeIfPresent(String.self, forKey: .tokenKey)
protocol Resultable
var values: [Value] get
struct Result: Resultable
let values: [Value]
extension Result: Codable
private enum ResultCodingKeys: String, CodingKey
case values
func encode(to encoder: Encoder) throws
var container = encoder.container(keyedBy: ResultCodingKeys.self)
try container.encode(values, forKey: .values)
init(from decoder: Decoder) throws
let container = try decoder.container(keyedBy: ResultCodingKeys.self)
values = try container.decode([Value].self, forKey: .values)
protocol Responsable
var code: Int get
var status: String get
var message: String get
var result: Result get
struct Response: Responsable
let code: Int
let status: String
let message: String
let result: Result
extension Response: Codable
private enum ResponeCodingKeys: String, CodingKey
case code
case status
case message
case result
func encode(to encoder: Encoder) throws
var container = encoder.container(keyedBy: ResponeCodingKeys.self)
try container.encode(code, forKey: .code)
try container.encode(status, forKey: .status)
try container.encode(message, forKey: .message)
try container.encode(result, forKey: .result)
init(from decoder: Decoder) throws
let container = try decoder.container(keyedBy: ResponeCodingKeys.self)
code = try container.decode(Int.self, forKey: .code)
status = try container.decode(String.self, forKey: .status)
message = try container.decode(String.self, forKey: .message)
result = try container.decode(Result.self, forKey: .result)
I think I'm over-complicating the solution. Is there any better solution to achieve this?
Do I really have to have two
protocol
defined?
json swift protocols
asked Feb 6 at 15:29
nayem
1012
1012
add a comment |Â
add a comment |Â
1 Answer
1
active
oldest
votes
up vote
1
down vote
Your intuition is correct: you don't need either the Resultable
or the Responsable
protocol. The reason is that each protocol only has one type that conforms to it. Protocols are great for sharing functionality among multiple types (using protocol extensions) or defining a set of guaranteed properties and behaviors common to a group of types. But the benefits only come when there is more than one type conforming to the protocol. Since your code only has one type conforming to each protocol, it's better for now to not have the protocol.
Also, this isn't part of your question but it might be worth investigating whether your implementation of Response
is tied too closely to the structure of your JSON. As it is, a client would have to dig pretty far into a response
's result
value to get any useful information out of it. Redefining Result
could bring the information forward and make things much easier on the client. For example:
enum Result
case verify(status: Bool, tokenKey: String)
case update(status: Bool)
struct Response
let code: Int
let status: String
let message: String
let result: Result
The Codable
implementation would be more complicated, but I think greater simplicity at the call site is worth the effort.
Well, that's a pretty good explanation aboutprotocol
. I appreciate your effort to look at this. But with your other proposal withenum
got me something to be confused. What do I do with thevalues
property inside theresult
property if I'm going to implement it your suggested way? More precisely, how would I deserialize theverifyStatus
updateStatus
tokenKey
into theresult
?
â nayem
Feb 8 at 17:17
I think that's best asked as a separate question on Stack Overflow. You'll probably need to do something like this: stackoverflow.com/a/45147203/277905 but you'll have to extract /inject the values from the json array when you decode/encode them.
â proxpero
Feb 9 at 15:54
add a comment |Â
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
1
down vote
Your intuition is correct: you don't need either the Resultable
or the Responsable
protocol. The reason is that each protocol only has one type that conforms to it. Protocols are great for sharing functionality among multiple types (using protocol extensions) or defining a set of guaranteed properties and behaviors common to a group of types. But the benefits only come when there is more than one type conforming to the protocol. Since your code only has one type conforming to each protocol, it's better for now to not have the protocol.
Also, this isn't part of your question but it might be worth investigating whether your implementation of Response
is tied too closely to the structure of your JSON. As it is, a client would have to dig pretty far into a response
's result
value to get any useful information out of it. Redefining Result
could bring the information forward and make things much easier on the client. For example:
enum Result
case verify(status: Bool, tokenKey: String)
case update(status: Bool)
struct Response
let code: Int
let status: String
let message: String
let result: Result
The Codable
implementation would be more complicated, but I think greater simplicity at the call site is worth the effort.
Well, that's a pretty good explanation aboutprotocol
. I appreciate your effort to look at this. But with your other proposal withenum
got me something to be confused. What do I do with thevalues
property inside theresult
property if I'm going to implement it your suggested way? More precisely, how would I deserialize theverifyStatus
updateStatus
tokenKey
into theresult
?
â nayem
Feb 8 at 17:17
I think that's best asked as a separate question on Stack Overflow. You'll probably need to do something like this: stackoverflow.com/a/45147203/277905 but you'll have to extract /inject the values from the json array when you decode/encode them.
â proxpero
Feb 9 at 15:54
add a comment |Â
up vote
1
down vote
Your intuition is correct: you don't need either the Resultable
or the Responsable
protocol. The reason is that each protocol only has one type that conforms to it. Protocols are great for sharing functionality among multiple types (using protocol extensions) or defining a set of guaranteed properties and behaviors common to a group of types. But the benefits only come when there is more than one type conforming to the protocol. Since your code only has one type conforming to each protocol, it's better for now to not have the protocol.
Also, this isn't part of your question but it might be worth investigating whether your implementation of Response
is tied too closely to the structure of your JSON. As it is, a client would have to dig pretty far into a response
's result
value to get any useful information out of it. Redefining Result
could bring the information forward and make things much easier on the client. For example:
enum Result
case verify(status: Bool, tokenKey: String)
case update(status: Bool)
struct Response
let code: Int
let status: String
let message: String
let result: Result
The Codable
implementation would be more complicated, but I think greater simplicity at the call site is worth the effort.
Well, that's a pretty good explanation aboutprotocol
. I appreciate your effort to look at this. But with your other proposal withenum
got me something to be confused. What do I do with thevalues
property inside theresult
property if I'm going to implement it your suggested way? More precisely, how would I deserialize theverifyStatus
updateStatus
tokenKey
into theresult
?
â nayem
Feb 8 at 17:17
I think that's best asked as a separate question on Stack Overflow. You'll probably need to do something like this: stackoverflow.com/a/45147203/277905 but you'll have to extract /inject the values from the json array when you decode/encode them.
â proxpero
Feb 9 at 15:54
add a comment |Â
up vote
1
down vote
up vote
1
down vote
Your intuition is correct: you don't need either the Resultable
or the Responsable
protocol. The reason is that each protocol only has one type that conforms to it. Protocols are great for sharing functionality among multiple types (using protocol extensions) or defining a set of guaranteed properties and behaviors common to a group of types. But the benefits only come when there is more than one type conforming to the protocol. Since your code only has one type conforming to each protocol, it's better for now to not have the protocol.
Also, this isn't part of your question but it might be worth investigating whether your implementation of Response
is tied too closely to the structure of your JSON. As it is, a client would have to dig pretty far into a response
's result
value to get any useful information out of it. Redefining Result
could bring the information forward and make things much easier on the client. For example:
enum Result
case verify(status: Bool, tokenKey: String)
case update(status: Bool)
struct Response
let code: Int
let status: String
let message: String
let result: Result
The Codable
implementation would be more complicated, but I think greater simplicity at the call site is worth the effort.
Your intuition is correct: you don't need either the Resultable
or the Responsable
protocol. The reason is that each protocol only has one type that conforms to it. Protocols are great for sharing functionality among multiple types (using protocol extensions) or defining a set of guaranteed properties and behaviors common to a group of types. But the benefits only come when there is more than one type conforming to the protocol. Since your code only has one type conforming to each protocol, it's better for now to not have the protocol.
Also, this isn't part of your question but it might be worth investigating whether your implementation of Response
is tied too closely to the structure of your JSON. As it is, a client would have to dig pretty far into a response
's result
value to get any useful information out of it. Redefining Result
could bring the information forward and make things much easier on the client. For example:
enum Result
case verify(status: Bool, tokenKey: String)
case update(status: Bool)
struct Response
let code: Int
let status: String
let message: String
let result: Result
The Codable
implementation would be more complicated, but I think greater simplicity at the call site is worth the effort.
answered Feb 7 at 16:24
proxpero
1111
1111
Well, that's a pretty good explanation aboutprotocol
. I appreciate your effort to look at this. But with your other proposal withenum
got me something to be confused. What do I do with thevalues
property inside theresult
property if I'm going to implement it your suggested way? More precisely, how would I deserialize theverifyStatus
updateStatus
tokenKey
into theresult
?
â nayem
Feb 8 at 17:17
I think that's best asked as a separate question on Stack Overflow. You'll probably need to do something like this: stackoverflow.com/a/45147203/277905 but you'll have to extract /inject the values from the json array when you decode/encode them.
â proxpero
Feb 9 at 15:54
add a comment |Â
Well, that's a pretty good explanation aboutprotocol
. I appreciate your effort to look at this. But with your other proposal withenum
got me something to be confused. What do I do with thevalues
property inside theresult
property if I'm going to implement it your suggested way? More precisely, how would I deserialize theverifyStatus
updateStatus
tokenKey
into theresult
?
â nayem
Feb 8 at 17:17
I think that's best asked as a separate question on Stack Overflow. You'll probably need to do something like this: stackoverflow.com/a/45147203/277905 but you'll have to extract /inject the values from the json array when you decode/encode them.
â proxpero
Feb 9 at 15:54
Well, that's a pretty good explanation about
protocol
. I appreciate your effort to look at this. But with your other proposal with enum
got me something to be confused. What do I do with the values
property inside the result
property if I'm going to implement it your suggested way? More precisely, how would I deserialize the verifyStatus
updateStatus
tokenKey
into the result
?â nayem
Feb 8 at 17:17
Well, that's a pretty good explanation about
protocol
. I appreciate your effort to look at this. But with your other proposal with enum
got me something to be confused. What do I do with the values
property inside the result
property if I'm going to implement it your suggested way? More precisely, how would I deserialize the verifyStatus
updateStatus
tokenKey
into the result
?â nayem
Feb 8 at 17:17
I think that's best asked as a separate question on Stack Overflow. You'll probably need to do something like this: stackoverflow.com/a/45147203/277905 but you'll have to extract /inject the values from the json array when you decode/encode them.
â proxpero
Feb 9 at 15:54
I think that's best asked as a separate question on Stack Overflow. You'll probably need to do something like this: stackoverflow.com/a/45147203/277905 but you'll have to extract /inject the values from the json array when you decode/encode them.
â proxpero
Feb 9 at 15:54
add a comment |Â
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f186924%2fefficiently-structuring-data-with-protocol-oriented-approach-in-swift%23new-answer', 'question_page');
);
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password