Casting struct with generic parameter with swift - ios

I'm trying to do the following.
protocol Vehicle {
}
class Car : Vehicle {
}
class VehicleContainer<V: Vehicle> {
}
let carContainer = VehicleContainer<Car>()
let vehicleContainer = carContainer as VehicleContainer<Vehicle>
But I get the compile error on the last line:
'Car' is not identical to 'Vehicle'
Is there any workaround for this?
Also I believe this type of casting should be possible because I can do it with Arrays which are built on generics. The following works:
let carArray = Array<Car>()
let vehicleArray = carArray as Array<Vehicle>

Expanding your array example quickly in playground works as intended
protocol Vehicle {
}
class Car : Vehicle {
}
class Boat: Vehicle {
}
var carArray = [Car]()
var vehicleArray : [Vehicle] = carArray as [Vehicle]
vehicleArray.append(Car()) // [__lldb_expr_183.Car]
vehicleArray.append(Boat()) // [__lldb_expr_183.Car, __lldb_expr_183.Boat]
Haven't done too much work with swift generics but looking quickly at the swift docs
struct Stack<T: Vehicle> {
var items = [Vehicle]()
mutating func push(item: Vehicle) {
items.append(item)
}
mutating func pop() -> Vehicle {
return items.removeLast()
}
}
and using vehicles instead of the generic type T
var vehicleStack = Stack<Vehicle>()
vehicleStack.push(Car())
vehicleStack.push(Boat())
var aVehicle = vehicleStack.pop()
appears to compile aok in an app (playground has some issues handling it though)

Related

Generic class type doesn't conform to Any

I have a problem with storing my generic classes in an array. How should I format the type for my array while keeping the reference to the original type (I know I could do var myClasses: [Any] = [] but that wouldn't be helpful when retrieving the variable from my array :(
Example is below:
import UIKit
protocol Reusable { }
extension UITableViewCell: Reusable { }
extension UICollectionViewCell: Reusable { }
class SomeClass<T> where T: Reusable {
init() { }
}
var myClasses: [SomeClass<Reusable>] = []
myClasses.append(SomeClass<UITableViewCell>())
myClasses.append(SomeClass<UICollectionViewCell>())
myClasses.append(SomeClass<UITableViewCell>())
myClasses.append(SomeClass<UICollectionViewCell>())
Edit: Just to clarify, I have used the collection and table cells as an example, I am not actually planning on storing them together :)
Edit 2 var myClasses: [SomeClass<Reusable>] = [] generates error: using 'Reusable' as a concrete type conforming to protocol 'Reusable' is not supported
Edit 3 var myClasses: [SomeClass<AnyObject>] = [] generates error: type 'AnyObject' does not conform to protocol 'Reusable'
I think you can create some sort of Holder class that can accept and retrieve your object:
class Holder {
lazy var classes: [Any] = []
func get<T>(_ type: T.Type) -> [T]? {
return classes.filter({ $0 is T }) as? [T]
}
}
And the main part:
let holder = Holder()
holder.classes.append(SomeClass<UITableViewCell>())
if let someTableViewCells = holder.get(SomeClass<UITableViewCell>.self)?.first {
// or filter out again to get specific SomeClass of UITableViewCell
print(someTableViewCells)
}
Or without holder class:
var classes: [Any] = []
classes.append(SomeClass<UITableViewCell>())
if let someTableViewCell = classes.filter({ $0 is SomeClass<UITableViewCell> }).first as? SomeClass<UITableViewCell> {
print(someTableViewCell)
}
You should use array of AnyObject in your case. Because as you know swift is strong typed language and for example
SomeClass<UITableViewCell>
and
SomeClass<UICollectionViewCell>
are different types of objects. As for example Array< Int > and Array< String >, they are both arrays, but still it's a different types of objects. So in this case you'll have to use declaration:
var myClasses: [AnyObject] = []
and check type of object or typecast them every time you'll need.
if (myClasses[0] is SomeClass<UICollectionViewCell>) { do something }
or
if let cell = myClasses[0] as? SomeClass<UICollectionViewCell> { do something }
My suggestion is adding parent protocol SomeClass Container for your SomeClass generic. Then put an array of SomeClass objects inside SomeClass.
protocol Reusable { func cakePlease() }
extension UITableViewCell: Reusable {
func cakePlease() { }
}
extension UICollectionViewCell: Reusable {
func cakePlease() { }
}
protocol SomeClassContainer {
func teaPlease()
func afternoonPlease()
}
class SomeClass<T: Reusable>: SomeClassContainer {
var item: T?
init() { }
func teaPlease() { }
func afternoonPlease() {
teaPlease()
item?.cakePlease()
}
}
var myClasses = [SomeClassContainer]()
myClasses.append(SomeClass<UITableViewCell>())
myClasses.append(SomeClass<UICollectionViewCell>())
myClasses.append(SomeClass<UITableViewCell>())
myClasses.append(SomeClass<UICollectionViewCell>())
myClasses[0].teaPlease()
if let item = (myClasses[0] as? SomeClass<UITableViewCell>)?.item {
item.cakePlease()
}
for myClass in myClasses {
if let tableCell = (myClass as? SomeClass<UITableViewCell>)?.item {
tableCell.cakePlease()
} else if let collectionCell = (myClass as SomeClass<UICollectionViewCell>)?.item {
collectionCell.cakePlease()
}
}
myClasses.forEach({ $0.afternoonPlease() })
Generally the way to type your array would be to go as specific as possible whilst still covering all bases.
What I mean by this for example is storing an array of UIViewController objects, even though each will actually be a different type. You wouldn't use Any here because you really don't need to be that general.
In your case, why not use Reusable? Since all your generic classes conform to it anyway, it is as general as you can go whilst still maintaining convenience.

Casting array of objects to array of subclassed objects in Swift

I have an array of EKReminder, like so:
var currentReminders: [EKReminder]? = ....
I want to cast this array to an array of subclasses of EKReminder. Let's say this is the subclass:
import Foundation
import EventKit
class NNReminder: EKReminder {
var additionalNotes: String?
}
How would I cast currentReminders to [NNReminder]? I tried several ways but they all failed.
Provided you are sure that all members of currentReminders are, in fact, NNReminders, you can cast them all like this:
currentReminders = currentReminders.map { $0 as! NNReminder }
Edit: if only some of the reminders are of type NNReminder, or you're not sure of the array's contents, you can use flatMap to remove the nil values:
currentReminders = currentReminders.flatMap { $0 as? NNReminder }
If you are asking how to transform a bunch of objects that were initialized as EKReminder, you should write a custom init in NNReminder that takes an EKReminder, and use this init in the above map method.
I was tired of having cryptic .map clauses all over my code so i wrote a small extension to make things neat:
extension Array{
func cast<T>(type:T.Type? = nil) -> [T]{
return self.map { $0 as! T }
}
}
Example:
class A:B{
var value:String = "default"
init(_ value:String){
self.value = value
}
}
class B{
var someNum:CGFloat = 1
init(){
}
}
var arr1:Array<A> = [A("a"),A("b"),A("c")]
let arr2:Array<B> = arr1.cast(B.self)//neat!
let arr3:Array<B> = arr1.cast()//epic!
NOTE:
the cast method supports protocols as well

Making a Swift Array conform to a Protocol

Let's say that certain items can appear in a Feed, so long as they implement the necessary properties defined by the Feedable protocol. Let's also say that the Photo object is feed-worthy:
extension Photo: Feedable { }
Is it possible to say that an Array of these photos might also be Feedable?
extension [Photo] : Feedable
Or do I always need some kind of wrapper object, such as a PhotoAlbum, to conform to Feedable?
Edit
To re-iterate, I was curious whether I can make only arrays of Photo objects Feedable. Not making Array of any content type Feedable, not making an array of Feedables itself Feedable (both of which are offered as solutions below if that's what you need).
In other words, a solution (which I doubt exists) would allow me to define a variable of type Feedable with the following outcomes:
var feedable: Feedable
//photo is feedable, so this is fine
feedable = Photo() //ok
//arrays of photos are feedable
let photo1 = Photo()
let photo2 = Photo()
feedable = [photo1, photo2]
//arrays of other things are not
feedable = ["no", "dice"] //nope
//even if the contents of an array are themselves Feedable, that's not sufficient. E.g. Video is Feedable, but Array of Videos is not.
let video1 = Video()
let video2 = Video()
feeble = video1 //fine
feedable = [video1, video2] //nope
Perhaps this gist (which doesn't compile of course) shows the intention more clearly.
You can achieve your goal in this way:
Swift 4:
protocol Feedable {
func foo()
}
extension String: Feedable {
func foo() {
}
}
extension Array: Feedable where Element: Feedable {
func foo() {
}
}
// or in more generic way to support Array, Set and other collection types
extension Collection: Feedable where Element: Feedable {
func foo() {
}
}
If there was an array of Photo and Video,what would you like to be?
1.Every element performs like what they are.
extension Array where Element : Feedable {
func foo() {
if Element.self == Photo.self {
} else {
}
}
}
2.The whole array performs as 'Video'.
extension Array where Element : Photo {
func foo() {
}
}
I think this is currently not possible. In my project I have the same issue with a ModelProducer.
protocol M: ModelType {}
protocol ModelProducerType {
associatedtype M: ModelType
var model: M? { get }
func produce()
}
struct Test: ModelType {}
class TestProducer: ModelProducerType {
var model: Test?
func produce() {
model = Test()
}
}
I use ModelType as a ghost protocol. The problem is I cannot make a model producer that produces multiple ModelTypes, because of the same reason you discovered. The solution in this case was the following:
protocol M: ModelType {}
protocol ModelProducerType {
associatedtype M: ModelType
var model: [M] { get }
func produce()
}
struct Test: ModelType {}
class TestProducer: ModelProducerType {
var model: [Test] = []
func produce() {
model = [Test()]
}
}
This is more flexible from the start. I get rid of the optional variable and single model producers just have one item in the array. Maybe you can use a similar approach.
You can make an array to conform a protocol like this:
typealias PhotoArray = [Photo]
extension PhotoArray: Feedable {}
I didn't try in playground but maybe you can simply make an Array of Feedable:
var myPhotosArray = [Feedable]()
Then everything implementing the Feedable protocol would be allowed in the Array. If you want only a photo array, You can still subclass your Photo object to make a FeedablePhoto object.
Try this in Playground instead of downvoting without even testing.
Seriously 3 downvotes without any reasons and explanations...
import UIKit
protocol Tree: class {
func grow()
}
class BigTree: Tree {
internal func grow() {
print("Big tree growing")
}
}
class SmallTree: Tree {
internal func grow() {
print("Small tree growing")
}
}
class Car {
//not a tree
}
var manyTrees = [Tree]()
manyTrees.append(BigTree())
manyTrees.append(SmallTree())
manyTrees.append(Car()) //This makes an error "Car doesn't conform to expected type 'Tree'"

How to refactor swift code to take protocol and struct types as method arguments

I have 2 functions that have a lot in common, and I want to re-factor my code to remove the repeated logic, however the things that are different are types, specifically a protocol, and and a struct type. The way I can think about it now is that to re-factor this I'd have 1 common method that would take one protocol type as an argument, and one struct type with the restriction that the struct type must implement the protocol 'DataDictionaryStore'. And would return an array of the protocol type passed in as argument 1
I've tried to implement this with generics, but from how I understand it, you still pass an instance as an argument when using generics, not the actual type itself.
The methods I'd like to re-factor in the code below are 'articles()', and 'authors()'
Here's the code (which can be copied to a playground Xcode 7+):
import Foundation
protocol Article {
var headline: NSString? { get }
}
protocol Author {
var firstName: NSString? { get }
}
protocol DataDictionaryStore {
init(dataDictionary: NSDictionary)
}
struct CollectionStruct {
let arrayOfModels: [NSDictionary]
//This function is identical to authors() except for return type [Article], and 'ArticleStruct'
func articles() -> [Article] {
var articlesArray = [Article]()
for articleDict in arrayOfModels {
let articleStruct = ArticleStruct(dataDictionary: articleDict)
articlesArray.append(articleStruct)
}
return articlesArray
}
func authors() -> [Author] {
var authorsArray = [Author]()
for authorDict in arrayOfModels {
let authorStruct = AuthorStruct(dataDictionary: authorDict)
authorsArray.append(authorStruct)
}
return authorsArray
}
}
struct ArticleStruct : Article, DataDictionaryStore {
var internalDataDictionary: NSDictionary
init(dataDictionary: NSDictionary) {
internalDataDictionary = dataDictionary
}
var headline: NSString? { return (internalDataDictionary["headline"] as? NSString) }
}
struct AuthorStruct : Author, DataDictionaryStore {
var internalDataDictionary: NSDictionary
init(dataDictionary: NSDictionary) {
internalDataDictionary = dataDictionary
}
var firstName: NSString? { return (internalDataDictionary["firstName"] as? NSString) }
}
var collStruct = CollectionStruct(arrayOfModels: [NSDictionary(objects: ["object1", "object2"], forKeys: ["key1", "headline"])])
print(collStruct)
var articles = collStruct.articles()
print(articles)
for article in articles {
print(article.headline)
}
If there is another way to re-factor this to remove the repeated logic, all suggestions welcome.
It's not exactly an answer to your question, but this might simplify it enough for you to be happy:
func articles() -> [Article] {
return arrayOfModels.map(ArticleStruct.init)
}
func authors() -> [Author] {
return arrayOfModels.map(AuthorStruct.init)
}
Based on PEEJWEEJ's answer, this refactor is also worth a shot. Instead of returning a single array, you can return a tuple of authors and articles. If you aren't going to be processing both the authors and articles arrays at once, this method is more expensive. But the syntax is much nicer than the previous solution using generics below.
func allObjects() -> (authors: [AuthorStruct], articles: [ArticleStruct]) {
let authors = arrayOfModels.map(AuthorStruct.init)
let articles = arrayOfModels.map(ArticleStruct.init)
return(authors, articles)
}
You would then call the method like this:
let objects = collection.allObjects()
let authors = objects.authors
let articles = objects.articles
I'm not a huge fan of the clarity here but maybe you can refactor it a bit. It seems to work at least.
func allObjectsOfType<T>(type: T.Type) -> [T] {
var objectArray = [T]()
for objectDict in arrayOfModels {
var objectStruct: T?
if type == Author.self {
objectStruct = AuthorStruct(dataDictionary: objectDict) as? T
} else if type == Article.self {
objectStruct = ArticleStruct(dataDictionary: objectDict) as? T
}
guard objectStruct != nil else {
continue
}
objectArray.append(objectStruct!)
}
return objectArray
}
You can then call it like this...
collection.allObjectsOfType(Author)
collection.allObjectsOfType(Article)

How to call static method provided by protocol in Swift

How to access to static protocol method within a instance
I have a list of Contact, the contact can be a FamilyContact that inherit from Contact and the GroupStatus protocol
I want to call the static method from GroupStatus but in vain...
Here is my code
protocol GroupStatus {
static func isPrivate() -> Bool // static method that indicates the status
}
protocol IsBusy {
func wizzIt()
}
class AdresseBook {
private var contacts = [Contact]()
func addOne(c: Contact) {
contacts.append(c)
}
func listNonPrivated() -> [Contact]? {
var nonPrivateContact = [Contact]()
for contact in contacts {
// here is I should call the static method provided by the protocol
if self is GroupStatus {
let isPrivate = contact.dynamicType.isPrivate()
if !isPrivate {
nonPrivateContact.append(contact)
}
}
nonPrivateContact.append(contact)
}
return nonPrivateContact
}
}
class Contact : Printable {
var name: String
init(name: String) {
self.name = name
}
func wizz() -> Bool {
if let obj = self as? IsBusy {
obj.wizzIt()
return true
}
return false
}
var description: String {
return self.name
}
}
class FamilyContact: Contact, GroupStatus {
static func isPrivate() -> Bool {
return true
}
}
I can't compile Contact.Type does not have a member named 'isPrivate'
How can I call it ? It works if I delete the static keyword, but I think is more logical to define it static.
If I replace
let isPrivate = contact.dynamicType.isPrivate()
by
let isPrivate = FamilyContact.isPrivate()
It works, but I can have more than 1 subclasses
If I remove the static keywork I can do it by this way :
if let c = contact as? GroupStatus {
if !c.isPrivate() {
nonPrivateContact.append(contact)
}
}
But I want to keep the static keyword
This looks like a bug or a non-supported feature. I would expect that
the following works:
if let gsType = contact.dynamicType as? GroupStatus.Type {
if gsType.isPrivate() {
// ...
}
}
However, it does not compile:
error: accessing members of protocol type value 'GroupStatus.Type' is unimplemented
It does compile with FamilyContact.Type instead of GroupStatus.Type. A similar problem is reported here:
Swift 1.1 and 1.2: accessing members of protocol type value XXX.Type' is unimplemented
Making isPrivate() an instance method instead of a class method is
the only workaround that I currently can think of, maybe someone comes
with a better solution ...
Update for Swift 2 / Xcode 7: As #Tankista noted below, this has
been fixed. The above code compiles and works as expected in Xcode 7 beta 3.
type(of: contact).isPrivate()
This should work in recent Swift.

Resources