How to make a struct conforms to a protocol which has a property conforms to another protocol in swift 4? - swift

I was going to reflect some JSON data from web service into swift struct. So I created a protocol which conforms to decodable protocol and planed to create some structs to conform it. This is the protocol I had created:
protocol XFNovelApiResponse: Decodable {
var data: Decodable {get}
var error: NovelApiError {get}
}
struct NovelApiError: Decodable {
let msg: String
let errorCode: String
}
It was compiled. But when I started to write my struct I got an error. The struct's code is here:
struct XFNovelGetNovelsApiResponse: XFNovelApiResponse {
let data: NovelsData
let error: NovelApiError
struct NovelsData: Decodable {
}
}
The error says type 'XFNovelGetNovelsApiResponse' does not conform to protocol 'XFNovelApiResponse'. I know the 'data' property should be implemented in wrong way. How can I fix it? Thanks.

You are asking to describe the kind of type that data can hold, rather than the actual type. That means it needs to be an associatedtype:
protocol XFNovelApiResponse: Decodable {
associatedtype DataType: Decodable
var data: DataType {get}
var error: NovelApiError {get}
}
Note that protocols with associated types can generate a lot of complexity, so you should carefully consider if this protocol is really necessary, or if XFNovelApiResponse could, for example, be generic instead. It depends on what other types implement this protocol.
For example, another implementation of a similar set of data structures without protocols would be:
struct XFNovelApiResponse<DataType: Decodable>: Decodable {
var data: DataType
var error: NovelApiError
}
struct NovelsData: Decodable {
}
struct NovelApiError: Decodable {
let msg: String
let errorCode: String
}
let novels = XFNovelApiResponse(data: NovelsData(),
error: NovelApiError(msg: "", errorCode: ""))
Alternately, you can implement this with classes and subclasses, which allow inheritance. Structs do not inherit from protocols, they conform to protocols. If you really mean inheritance, classes are the right tool. (But I expect generics are the better solution here.)

Related

Protocol conforming to type with associated value

I've got the following snippet:
protocol MyProtocol: Identifiable where ID == UUID {
var id: UUID { get }
}
var test: [MyProtocol] = []
Protocol 'MyProtocol' can only be used as a generic constraint because it has Self or associated type requirements
Why doesn't this work? Shouldn't the where ID == UUID remove the ambiguity the error is concerned with? Am I missing something here?
I think this question is similar to this one: Usage of protocols as array types and function parameters in swift
However, I would have assumed that adding where ID == UUID should fix the problem? Why is that not the case?
Thanks!
Edit
So, this problem has occurred while experimenting with SwiftUI and struct data models. I've always used classes for any kind of data model but it seems like SwiftUI wants to get you to use structs as often as possible (I still don't see how that's realistically possible but that's why I'm experimenting with it).
In this particular case, I tried to have a manager that contains structs that all conform to MyProtocol. For example:
protocol MyProtocol: Identifiable where ID == UUID {
var id: UUID { get }
}
struct A: MyProtocol { // First data model
var id: UUID = UUID()
}
struct B: MyProtocol { // Second data model
var id: UUID = UUID()
}
class DataManager: ObservableObject {
var myData: [MyProtocol]
}
...
I don't actually have to declare Identifiable on MyProtocol but I thought it would be nicer and cleaner.
Because this is not a current feature of Swift. Once there is an associated type, there is always an associated type. It doesn't go away just because you constrain it. And once it has an associated type, it is not concrete.
There is no way to "inherit" protocols this way. What you mean is:
protocol MyProtocol {
var id: UUID { get }
}
And then you can attach Identifiable to structs that require it:
struct X: MyProtocol, Identifiable {
var id: UUID
}
(note that no where clause is required.)
There is no Swift feature today that allows you to say "types that conform to X implicitly conform to Y." There is also no Swift feature today that allows for an Array of "things that conform to Identifiable with ID==UUID." (That's called a generalized existential, and it's not currently available.)
Most likely you should go back to your calling code and explore why you require this. If you post the code that iterates over test and specifically requires the Identifiable conformance, then we may be able to help you find a design that doesn't require that.

Swift Encodable, Decodable Vs Codable

Found in Apple doc, that Codable protocol is composed of Encodable and Decodable. Thus,
Codable = Encodable & Decodable
Now, let's say I implemented below classes,
class X: Codable {
var name: String
}
class Y: Encodable, Decodable {
var name: String
}
class Z: Encodable & Decodable {
var name: String
}
So, is there any functional difference among the classes, X, Y and Z?
If there is no deference why can't we use & in the places of ,?
No there isn't any difference , Codable is a typealias for Encodable & Decodable , so it combines the 2 protocols you're free to use any way
In Swift & is the same as , in protocol composition so Encodable, Decodable = Encodable & Decodable = Codable
Don't think so. Codable means they can be decoded from and encoded into another representation. Decodable means it can be decoded, but not encoded. And encodable is the opposite of that.
They are functionally the same but you can do things with the & syntax that you can't with a comma separated list. Neither of these would work with the comma approach.
public typealias Codable = Decodable & Encodable
func doSomething(with item: ProtocolA & ProtocolB) {
}
Encoding
The process of converting your custom type instances to other representation such as JSON and pList is known as Encoding or Serialization. For encoding, custom types conform to Encodable protocol.
Decoding
The process of converting data in representation such as JSON or pList to instance of your custom type is known as Decoding or Deserialization. For decoding, custom types conform to Decodable protocol.
Codable
To support both encoding and decoding, custom types can conform to Codable protocol which conforms to both Encodable and Decodable.
to Read more about Codable, Decoding and Encoding Click on the this Link

Can I write protocol behave similar to Encodable & Decodable?

The swift4's Codable protocol is extremely useful. It provide default implementation functions if the conformation is rightly defined.
E.g. this is totally fine:
struct Good: Codable {
var foo: String // Non-optional
var bar: Int? // Optional
}
but this one will raise compile error with the request to create protocol conforming
struct Bad: Codable {
var foo: UIButton // Non-optional raise compile error for not conforming Codable Protocol
var bar: UIView? // optional is okay (not compile error because when decode failed, it fallback to nil)
var codable: SomeCodable // if the property is also Codable, then it's fine too!
}
So, the question is: Can I write a protocol that can require its conformance to follow itself (like properties need to conform same protocol)?
If yes, how? If no, why?
Also, I'd also like to know how defining CodingKeys inside the struct can change the encode/decode behaviour? Can I make something like that in my protocol as well?
Martin is correct you cannot make this on your own without touching the compiler.
First let's take a look at this basic example where I explain how coding keys are used.
struct CodableStruct: Codable {
let primitive: Int // No issues yet
enum CodingKeys: String, CodingKey {
case primitive
// This is the default coding key (i.e the JSON has structure ["primitive": 37]
// You can change this key to anything you need
//
// ex case primitive = "any_thing_you_want"
// JSON has to have structure ["any_thing_you_want": 37]
}
}
Changing the codingKey just changes the key the code will use when looking to "decode" that value from your JSON.
Now let's talk about the compiler. Let's say we create another struct
struct NotCodableStruct {
let number: Double
}
This struct does not conform to Codable. If we go and add this into our previous struct we have:
struct CodableStruct: Codable {
let primative: Int
let notCodable: NotCodableStruct // doesn't compile because this doesn't conform to codable
enum CodingKeys: String, CodingKey {
case primative
case notCodable
}
}
Since NotCodableStruct does not conform to Codable the compiler complains. In other words all variables in a struct or object that conforms to Codable must also conform to Codable. See the below screenshot for more information.
Of course if you make NotCodableStruct conform to Codable everyone will be happy again. Since there is no way for you to enforce the requirement that all variables conform to Codable you cannot make a similar protocol.

Swift Extension to Observable with Generic Type Constraint

I'm trying to add an extension to Observable.
The code looks like this:
extension Observable where Element == ApiResponse<ItemsContainer<T>>, T:Codable
I'm receiving the following exception: Use of undeclared type T.
So apparently this doesn't work.
The only thing missing is to constrain the generic inside ItemsContainer to conform to Codable.
Could be as simple as a syntactical issue or maybe I'm just not good enough with generics. Any help is appreciated!
Edit: To give the idea - ApiResponse and ItemsContainer look like this
public struct ApiResponse<ApiModel> {
public let data: ApiModel?
}
struct ItemsContainer<Items>: Codable where Items: Codable {
let items: [Items]
}
Issue
You cannot constraint extensions to a Model Type which holds generic values, without specifying the Model Type of the generic value.
You only constrain protocols based on their associatedtypes or generics based on their generic type, on the extension signature. Therefore T is not recognized, because none of the protocols or generic declare it.
Solution
So by keeping in mind what I said above, a Model Type needs to be fully defined on the extension context. But wait that does not satisfy our requirement, we want it to be generic!
Then we do not need a Model Type, we need a protocol!
We have two Model Types (ApiResponse and ItemsContainer) which we need to know the generic type, therefore we need two protocols for each of them.
ApiResponse
Let's create one named ApiResponseProtocol
public protocol ApiResponseProtocol {
associatedtype Model
var data: Model? { get }
}
Cool, the associatedtype Model will play our role as the generic value for the ApiModel on the object. Let's make ApiResponse conform to ApiResponseProtocol
public struct ApiResponse<ApiModel>: ApiResponseProtocol {
public let data: ApiModel?
}
Generic ApiModel here can be defined as Model from the protocol.
ItemsContainer
Next steps would be the same for the ItemsContainer
public protocol ItemsContainerProtocol {
associatedtype Item
var items: [Item] { get }
}
public struct ItemsContainer<Items>: Codable, ItemsContainerProtocol where Items: Codable {
public let items: [Items]
}
Extension
Now since we can access each of the generic types from the protocol (associatedtypes), the output would become something like this:
// This would be for example ApiResponse<ItemsContainer<Model>> where Model is a Model Type conforming to Codable
extension Observable where Element: ApiResponseProtocol, Element.Model: ItemsContainerProtocol, Element.Model.Item: Codable {}

Nested Codable protocols with Swift 4

I was playing with Swift 4 and Codable a little bit and got stucked with some scenario having nested protocols which all conform to Codable.
Simplified example looks like this:
protocol CodableSomething: Codable {}
protocol CodableAnotherThing: Codable {
var something: CodableSomething { get }
}
struct Model: CodableAnotherThing {
var something: CodableSomething
}
This code is making a build errors with Xcode 9 Beta 5:
Type 'Model' does not conform to protocol 'Decodable'
Type 'Model' does not conform to protocol 'Encodable'
Now, I wasn't expecting these errors as I understood that conformance to these protocols will be auto-generated by the compiler, when in fact, I couldn't even implement this conformance manually without having build errors. I've also tried several different approaches to solve this kind of a nested model structure with using Codable but I just couldn't make it work.
My question: Is this a compiler bug (it's still beta) or I'm doing something wrong?
If you switch protocol
CodableSomething
To a struct you would have no errors,
take it further and read more about Codable
what are the types that a Codable can work on and why ?
up there you are basically saying this to xCode
struct foo: Codable {
var ok: Codable
}
That is not right take a deep look at it,
Codable is a Typealias
you need to conform to to use its subs such as .Decode() , .Encode()
those methods works with values not abstraction types
so giving a Codable Type to a Variable thats not going to work out.
because Codable is a typealias that indicates
Decodable & Encodable
/// A type that can convert itself into and out of an external representation.
public typealias Codable = Decodable & Encodable
and both of Decodable and Encodable are Protocols that make sure those values are encodable and decodable.
so Codable is an abstraction it can't Decode or Encode Variables of it self Type
but can encode and decode Types that are confirmed to it.