This is a strange one due to requirements of the project. Essentially I have struct conforming to Codable and CustomStringConvertible which I am using to decode JSON, however I am required to use the default implementation of description which I can't seem to do as description does not have a matching CodingKey and does not have a default value. Is there any way I can access the default value for description without using my own custom string? Example below
struct Source: CustomStringConvertible {
var description: String
var symbol: String
var line: Int
var image: String
var file: String
}
extension Source: Codable {
private enum CodingKeys : String, CodingKey {
case symbol
case line
case image
case file
}
}
you just need to change your description property declaration and make it a computed property instead:
var description: String { "Source - Symbol: \(symbol), Line: \(line), Image: \(image), File: \(file), " }
Note that there is no need to explicitly declare the coding keys:
struct Source: Codable {
let symbol, image, file: String
let line: Int
}
extension Source: CustomStringConvertible {
var description: String { "Source - Symbol: \(symbol), Line: \(line), Image: \(image), File: \(file), " }
}
Related
I am using Apollo for Swift in an iOS app. I have multiple types that all represent the same object. These types are auto-generated from a schema file and look something like this.
struct CurrentUser {
var id: String
...
}
struct MyUser {
var id: String
...
}
Basically Apollo generates multiple Swift types (one for each query) for the same underlying data type.
I want to create a new struct that unifies these types.
I would like to do something like this:
protocol UserProtocol {
var id: String { get }
}
struct User {
var id: String
...
init(_ data: UserProtocol) {
self.id = data.id
...
}
}
This approach however gives me an error when I try to construct a user object, telling me that "Type MyUser does not conform to UserProtocol". If I try to coerce the type with data as! UserProtocol I get a crash.
The only solution I've found is the following:
enum UserType {
case .currentUser(CurrentUser)
case .myUser(MyUser)
}
struct User {
var id: String
...
init(_ data: UserType) {
switch data {
case .myUser(let user):
self.id = data.id
...
case .currentUser(let user):
self.id = data.id
...
}
}
}
This approach works, but it leads to a lot of duplicated code in the init function. Is there a better way to do this in Swift?
I suspect the problem is that you need to explicitly conform the Apollo generated types to your protocol:
extension CurrentUser: UserProtocol { }
extension MyUser: UserProtocol { }
Remember that Swift is not duck-typed like some other languages, so a type with member var id: String is not UserProtocol until you declare it as such.
If for some reason you need to do some transformation of the Apollo types to fit the app models in the future, those extensions are a good place to do that, too.
I am using a protocol to define the core set of common properties to be shared across all Items.
protocol AnyItemable {
...
var parentId: String? { get set }
var parent: AnyItemable?
..
}
I also have a typealias to use as a generic where this needs to be identifiable and codable:
typealias Itemable = AnyItemable & Identifiable & Codable
Now, when I create any type of item, it implements the Itemable protocol:
struct PhotoItem: Itemable {
// Protocol fields
...
var parentId: String?
var parent: AnyItemable?
...
// Fields specific to this struct
var photoUrl: String?
}
This results in the following error:
Type 'PhotoItem' does not conform to protocol 'Decodable'
I believe it's because protocols aren't inherently decodable? How might we resolve this? Our goal is to be able for any Itemable struct to be able to also have a parent that can be N number of types that conform to Itemable
I have multiple responses which has similar pattern but one key value has always different object in response of json which i want to decode in base model where one key has variety of object type.
Response be like,
{
"status": true,
"message": "Success",
"data":[]
}
Here in data response it has any kind of array of objects or any single object
struct BaseResponseModel: Codable {
var status: Bool
var message: String
var data: DataClass
enum CodingKeys: String, CodingKey {
case message
case data
case status
}
}
what we can do here to make it single class with data type object pass,
Anyone please..!
Use Swift generics, and provide the type only at the time of decoding:
struct BaseResponseModel<DataType: Codable>: Codable {
var status: Bool
var message: String
var data: DataType
}
Usage:
let myData = try JSONDecoder().decode(BaseResponseModel<MyStruct>.self, from: data).data // For object
let myData = try JSONDecoder().decode(BaseResponseModel<[MyStruct]>.self, from: data).data // For array
Note: You don't need CodingKeys if the rawValues are the same.
I have a struct as below which is confirming a protocol as below
struct ResourceItems<T: Decodable>: MyProtocol {
}
protocol MyProtocol: Decodable {
}
Now I am creating another protocol & want to access ‘ResourceItems’ in that protocol as below
protocol ObjectConverterProtocol{
var response : ResourceItems {get set}
}
But getting the error as ‘
Reference to generic type 'ResourceItems' requires arguments in <…>
’
Please guide me what I have to pass as a generic parameter. I tried with AnyObject & Any but both of them does not confirm Codable.
I can’t get how to fix this issue. Please guide.
Since ResourceItems is a generic type you need to use it like that also in your protocol. For this you can use an associated type
protocol ObjectConverterProtocol{
associatedtype ResourceType: Decodable
var response : ResourceItems<ResourceType> {get set}
}
and then you can use your protocol
struct Converter: ObjectConverterProtocol {
typealias ResourceType = String
var response: ResourceItems<ResourceType>
//or var response: ResourceItems<String>
}
or even make Converter generic
struct Converter<C: Decodable>: ObjectConverterProtocol {
typealias ResourceType = C
var response: ResourceItems<ResourceType>
}
let converter = Converter(response: ResourceItems<String>())
Since ResourceItems is a generic type, you need to specify the type of generic parameter explicitly.
Now, since the generic parameter must be Decodable, you must define the Decodable type with ResourceItems in angular brackets <...>.
Let's assume, struct DecodableObject is of a Decodable type, i.e.
struct DecodableObject: Decodable {
//....
}
So, you can now define response of type ResourceItems like so,
protocol ObjectConverterProtocol {
var response : ResourceItems<DecodableObject> {get set}
}
I'm trying to make a list with the raw values of the cases from an enumeration with the new SwiftUI framework. However, I'm having a trouble with conforming the 'Data' to Identifiable protocol and I really cannot find information how to do it. It tells me "Initializer 'init(_:rowContent:)' requires that 'Data' conform to 'Identifiable'" The stub provides me with an ObjectIdentifier variable in the last extension, but don't know what should I return. Could you tell me how do it? How do I conform Data to Identifiable, so I can make a list with the raw values?
enum Data: String {
case firstCase = "First string"
case secondCase = "Second string"
case thirdCase = "Third string"
}
extension Data: CaseIterable {
static let randomSet = [Data.firstCase, Data.secondCase]
}
extension Data: Identifiable {
var id: ObjectIdentifier {
return //what?
}
}
//-------------------------ContentView------------------------
import SwiftUI
struct Lala: View {
var name: String
var body: some View {
Text(name)
}
}
struct ContentView: View {
var body: some View {
return List(Data.allCases) { i in
Lala(name: i.rawValue)
}
}
}
⚠️ Try not to use already used names like Data for your internal module. I will use MyEnum instead in this answer
When something conforms to Identifiable, it must return something that can be identified by that. So you should return something unique to that case. For String base enum, rawValue is the best option you have:
extension MyEnum: Identifiable {
var id: RawValue { rawValue }
}
Also, enums can usually be identified by their selves:
extension MyEnum: Identifiable {
var id: Self { self }
}
⚠️ Note 1: If you return something that is unstable, like UUID() or an index, this means you get a new object each time you get the object and this will kill reusability and can cause epic memory and layout process usage beside view management issues like transition management and etc.
Take a look at this weird animation for adding a new pet:
Note 2: From Swift 5.1, single-line closures don't need the return keyword.
Note 3: Try not to use globally known names like Data for your own types. At least use namespace for that like MyCustomNameSpace.Data
Inline mode
You can make any collection iterable inline by one of it's element's keypath:
For example to self:
List(MyEnum.allCases, id:\.self)
or to any other compatible keypath:
List(MyEnum.allCases, id:\.rawValue)
✅ The checklist of the identifier: (from WWDC21)
Exercise caution with random identifiers.
Use stable identifiers.
Ensure the uniqueness, one identifier per item.
Another approach with associated values would be to do something like this, where all the associated values are identifiable.
enum DataEntryType: Identifiable {
var id: String {
switch self {
case .thing1ThatIsIdentifiable(let thing1):
return thing1.id
case .thing2ThatIsIdentifiable(let thing2):
return thing2.id
}
}
case thing1ThatIsIdentifiable(AnIdentifiableObject)
case thing2ThatIsIdentifiable(AnotherIdentifiableObject)
You can try this way:
enum MyEnum: Identifiable {
case valu1, valu2
var id: Int {
get {
hashValue
}
}
}
Copyright © 2021 Mark Moeykens. All rights reserved. | #BigMtnStudio
Combine Mastery in SwiftUI book
enum InvalidAgeError: String, Error , Identifiable {
var id: String { rawValue }
case lessThanZero = "Cannot be less than zero"
case moreThanOneHundred = "Cannot be more than 100"
}