Dynamic type casting in swift 4 - swift

I've got a class named Filters
class Filters {
typealias States = [State]
typealias Cities = [City]
typealias Areas = [Area]
var states : States?
var cities : Cities?
var areas : Areas?
}
In a FilterViewController, based on user's selection, pickerViewItems will be populated with items of filters.states, filters.cities or filters.areas.
The problem is when I populate the pickerViewItems with one of the three array item, e.g. filters.states, I can't cast it to States so I will be able to use
pickerViewItems[row].name
Value of type 'Any' has no member 'name'
How can I set the type of pickerViewItems dynamically in Swift?

Swift is pretty much a static language so you can't dynamically change the type. Therefore, we are going to use polymorphism to work around this.
Assuming that State, City and Area all have a name property, you can create an protocol like this:
protocol NamedLocation {
var name: String { get; }
}
And make all three classes conform to the protocol:
extension State: NamedLocation { }
extension City: NamedLocation { }
extension Area: NamedLocation { }
Now you can make your pickerViewItems to be of type [NamedLocation] and you can still access the name property.

Related

Swift generic type

I have a structure name Feature, i hope it can be generic.
struct Feature<T> {}
I have another two type: Person, Dog.
struct Person {}
struct Dog {}
What i want to ask is:
Can i call different functions or different property in Feature according to differrent T
//for Person type
let feature = Feature<Person>()
feature.hair// i want this visible for Person
feature.fur// i want this invisible for Person
//for Dog type
let feature = Feature<Dog>()
feature.hair// i want this invisible for Dog
feature.fur// i want this visible for Dog
I don't know the full scope of your situation so I am only answering your question. See below:
protocol GenericFeatureProtocol {
var eyeColor : String? { get set }
//other shared properties & functions
}
struct Person : GenericFeatureProtocol {
var eyeColor: String?
var hair: String?
}
struct Dog : GenericFeatureProtocol {
var eyeColor: String?
var fur: String?
}
As you can see, the GenericFeatureProtocol defines the shared properties and functions that are to be implemented by those comforting to it. The properties respective to Person stay on Person, and those respective to Dog stay on Dog.

When to use associated type with constraint in Swift?

protocol Item {
init(name: String)
}
//case1
protocol SomeProtocol {
associatedtype ItemType: Item
var items: [ItemType] { get set }
}
//case2
protocol SomeProtocol {
var items: [Item] { get set }
}
What is the difference in case 1 and case 2. As per my understanding, case 1 says, items is an array of any type that implements Item protocol and case 2 is kind of saying the same thing as protocols themselves can't be initiated.So items in case is again an array of objects that implements Item protocol.
As per my understanding, case 1 says, items is an array of any type that implements Item protocol and case 2 is kind of saying the same thing as protocols themselves can't be initiated.So items in case is again an array of objects that implements Item protocol.
Let's give your protocols better names to demonstrate how the two cases are different. Let's rename SomeProtocol to ItemStorage. Also, let's say there are two classes that conform to Item, FooItem and BarItem.
Now, I want to create two classes that implement ItemStorage called FooItemStorage and BarItemStorage. I want them to only be able to store their respective type of Item. If this were case 1, I could do this very easily:
class FooItemStorage: ItemStorage {
typealias ItemType = FooItem
var items: [FooItem] = []
...
}
class BarItemStorage: ItemStorage {
typealias ItemType = BarItem
var items: [BarItem] = []
...
}
However, in case 2, the arrays must be of type [Item], so the above code won't compile. I have to do something like this:
class FooItemStorage: ItemStorage {
typealias ItemType = FooItem
var items: [Item] {
get { return myItems }
set {
myItems = newValue as? [FooItem] ?? myItems
}
}
var myItems: [FooItem] = []
...
}
I have to declare an extra property called myItems that has my desired type of [FooItem], and delegate the getter and setter of items to myItems. Also, from the perspective of the client code, it looks as if FooItemStorage can now store any kinds of item.
Note this is not to say that protocols with associated types are always better. Protocols with associated types can't be used as the type of a variable. So if you don't need something like FooItemStorage and BarItemStorage where the conforming classes need different types, and you want to use the protocol as the type of a variable, you should not use associated types.

Changing a struct with one type to another type

I have two structs with the same fields. What is the best way to merge them.
struct Type1{
var variable1:String?
var variable2:Double?
var variable3:String?
var notImporant:String?
}
struct Type2{
var variable1A:String?
var variable2A:String?
var variable3A:String!
}
What is the best way to convert type2 to type1? I am getting a return from an API and parsing it using codable but there are two different structs and I need to get one struct. The data is the same, it is just mapped differently in terms of types. Some of the structs have more info and others have less.
Just make a copy constructor in both structs like so:
struct Type2 {
var variable1A:String?
var variable2A:String?
var variable3A:String!
init(_ otherType: Type1) {
variable1A = otherType.variable1
variable2A = otherType.variable2
variable3A = otherType.variable3
}
}
You cannot cast two unrelated structs. What you can do is define a common Protocol for the two of them, and use them in places where you don't care which underlying object it is.
protocol CommonProtocol {
var variable1: String? { get }
var variable3: String? { get }
}
struct Type1: CommonProtocol {
var variable1:String?
var variable2:Double?
var variable3:String?
var notImporant:String?
}
struct Type2: CommonProtocol {
var variable1A:String?
var variable2A:String?
var variable3A:String!
}
Then, in whichever place you're currently stuck with a type1 instead of a type2, have that function just accept a CommonProtocol instead, and you can use either.
Note that, while both of your types have a variable2, one of them is a Double? while the other is a String?. There are a few different ways to approach that, which I leave to you. I just left it out of the protocol.
On another note, it's Swift standard to capitalize the names of structs (Type1, Type2). In certain instances, you can run into problems if you don't, so I suggest you do.

How can I specify a CollectionType for a generic struct/class?

For example, say I have an struct AdjacencyList where I want to specify the type of container that the vertices are stored in such that the user can choose Set if they don't want duplicates, or Array if they do.
(I've omitted protocol conformances for the types since my example code is already incorrect as-is and many would have to be conformed to depending on the container type. For example, Set elements would need to be Hashable.)
public struct AdjacencyList<VertexType, EdgeType, VertexContainerType, EdgeContainerType> {
var vertices: VertexContainerType<VertexType>
...
}
The issue you are running into is that CollectionType is not generic. One way around the specific issue you point out is to just have the client specify the container types and then you can extract the actual element types.
For example:
struct AdjacencyList<VertexContainerType: CollectionType, EdgeContainerType: CollectionType> {
var vertices: VertexContainerType
typealias VertexType = VertexContainerType.Generator.Element
typealias EdgeType = EdgeContainerType.Generator.Element
func printTypes() {
print("VertexType: \(VertexType.self)\nEdgeType: \(EdgeType.self)")
}
}
let a = AdjacencyList<Array<Int>, Array<Double>>(vertices: [Int]())
a.printTypes()
// Prints:
// VertexType: Int
// EdgeType: Double

Statically typed properties in Swift protocols

I'm trying to use Protocol-Oriented Pgrogramming for model layer in my application.
I've started with defining two protocols:
protocol ParseConvertible {
func toParseObject() -> PFObject?
}
protocol HealthKitInitializable {
init?(sample: HKSample)
}
And after implementing first model which conforms to both I've noticed that another model will be basically similar so I wanted to create protocol inheritance with new one:
protocol BasicModel: HealthKitInitializable, ParseConvertible {
var value: AnyObject { get set }
}
A you can see this protocol has one additional thing which is value but I want this value to be type independent... Right now I have models which use Double but who knows what may show up in future. If I leave this with AnyObject I'm sentenced to casting everything I want to use it and if I declare it as Double there's no sense in calling this BasicModel but rather BasicDoubleModel or similar.
Do you have some hints how to achieve this? Or maybe I'm trying to solve this the wrong way?
You probably want to define a protocol with an "associated type",
this is roughly similar to generic types.
From "Associated Types" in the Swift book:
When defining a protocol, it is sometimes useful to declare one or
more associated types as part of the protocol’s definition. An
associated type gives a placeholder name (or alias) to a type that is
used as part of the protocol. The actual type to use for that
associated type is not specified until the protocol is adopted.
Associated types are specified with the typealias keyword.
In your case:
protocol BasicModel: HealthKitInitializable, ParseConvertible {
typealias ValueType
var value: ValueType { get set }
}
Then classes with different types for the value property can
conform to the protocol:
class A : BasicModel {
var value : Int
func toParseObject() -> PFObject? { ... }
required init?(sample: HKSample) { ... }
}
class B : BasicModel {
var value : Double
func toParseObject() -> PFObject? { ... }
required init?(sample: HKSample) { ... }
}
For Swift 2.2/Xcode 7.3 and later, replace typealias in the
protocol definition by associatedtype.