I'm trying to iterate over a list of protocol objects, but I'm running into some issues. I could be going about the inheritance the wrong way. Any help is appreciated.
For example:
Given a property: var rows: [any CLRow]
I want to iterate over it:
ForEach(rows) { row in
}
But that yields an error:
Type 'any CLRow' cannot conform to 'Identifiable'
CLRow is defined as:
protocol CLRow: View {
var label: String { get set }
var icon: String { get set }
var showChevron: Bool { get set }
}
And I'm trying to do some inheritance with other objects. Example:
struct CLDoubleLabelRow: CLRow
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.
The following code should explain what I want to do reasonably clearly. Of course, this produces a compile time error.
What is the correct way to do what the below code intends?
protocol FilterableDataSource {
var dataClass: AnyClass { get }
var data: [dataClass.dynamicType] { get }
}
You can't use generics with protocols, but if you try, Swift gives you a hint - associated types. Code rarely explains intent clearly, but I'm guessing what you want to achieve is along the lines of...
protocol FilterableDataSource {
typealias T
var data: [T] { get }
}
class MyData { }
class MyClass: FilterableDataSource {
typealias T = MyData
var data: [MyData] { return [MyData]() }
}
print(MyClass().data.count)
I would use AnyObject instead of AnyClass. Then you can just cast it into whatever you want.
I believe what you want to do is define a typealias requirement in your protocol.
protocol FilterableDataSource
{
typealias DataClass : AnyObject
var data: [DataClass] { get }
}
class PersonDataSource:FilterableDataSource
{
typealias DataClass = Person
var data[Person] = []
}
So instead of requiring your filterable data sources to devine a variable containing the class, you require them to define a typealias for the elements of the data[] variable. Ideally your data classes have a common ancestor other than AnyObject which you can use as the typealias restriction.
I both do, and don't get why the last line of this playground throws a compiler error:
protocol Model { }
struct Post: Model {
var content = "Hello"
}
struct Posts: Model {
var allPosts: [Post] = [Post(), Post(), Post()]
}
func handler(items: [Model]) { }
var posts = Posts()
handler(posts.posts)
If you're reading between the lines, my goal is to be able to invoke a function with an argument that is an array of structs that conform to a protocol. The function should be able to deal with arrays of different types of structs. Would love to know what I'm missing, and if you have a suggestion for a better solution.
Thanks!
It seems to be Swift limitations. But you can do some workaround like this using Generics:
func handler<T: Model>(items: [T]) { }
or else make your protocol a #objc protocol which you can only apply to class type:
#objc protocol Model { }
class Post: Model {
var content = "Hello"
}
I am trying to create a fairly generic UITableView implementation that can display different types of cells. I'm using associated types to specify the data source type, cell type, and so on. I have most of it working really well, but I am having some trouble subclassing the implementation. I'm showing the least amount of code I can below to get the point across.
Here's my high-level architecture:
protocol DGTableViewAble {
typealias DGTableViewItemType
...
var items: [DGTableViewItemType] { get set }
}
class DGTableView: UITableView, DGTableViewAble {
typealias DGTableViewItemType = User
var items: [DGTableViewItemType] = [] { ... }
}
class DGPostsTableView: DGTableView {
typealias DGTableViewItemType = Post
}
...
Things work great when I assign an array of User objects to any DGTableView instance. For example, this works great:
var users: [User] = [...]
var userTableView: DGTableView
userTableView.items = users
However, when I try this:
var posts: [Post] = [...]
var postsTableView: DGPostsTableView
postsTableView.items = posts
I get the error:
Cannot assign a value of type '[Post]' to a value of type '[DGTableViewItemType]'
It seems like the compiler has trouble determining the associated types as I have things set up. Any ideas why? Any suggestions on improving the architecture?
You're not inheriting from DGTableViewAble in your class interface for DGPostsTableView:
class DGPostsTableView: DGTableView, DGTableViewAble {
typealias DGTableViewItemType = Post
...
}
Post isn't declaring viewable protocol?
class DGPostsTableView: DGTableView, DGTableViewAble {
typealias DGTableViewItemType = Post
}
It will also have to conform to said protocol. Maybe marking the unneeded protocol components as optional could solve your issue?
I have been trying to declare a static dictionary within a "struct". However, I could not achieve this. It gives me "Type 'BagItem' does not conform to protocol 'Hashable'" .
And my code is here:
struct StaticBag {
static var bag: Dictionary<BagItem, Array<BagItem>> = Dictionary<BagItem, Array<BagItem>>()
// static func AddMainItem(item: BagItem)
// {
// self.bag[item] = Array<BagItem>()
// }
}
'BagItem' in the code is my another global class.
What is the right and best way to declare this variable ?
Thank you for your answers
Best regards
As it says, the issue is that your custom BagItem type doesn't conform to the Hashable protocol. Dictionary keys need to be hashable, since dictionaries use the hash values to look up entries quickly.
What does BagItem look like? Is there a unique property that is already hashable? If so, you can add Hashable conformance by adding a hashValue property and implementing the == operator:
class BagItem : Hashable {
var uniqueID: Int = 0
var hashValue: Int { return uniqueID.hashValue }
}
func ==(lhs: BagItem, rhs: BagItem) -> Bool {
return lhs.uniqueID == rhs.uniqueID
}