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
Related
I am facing the issue of "Cannot assign to immutable expression of type 'Bool'" . Please look at the below code. I am getting error in viewForHeaderInSection. Actually where should i do modification to make it work?.
struct VenueDetail {
var isVeg: Bool
}
struct VenueDetailDTOMapper {
static func map(_ dto: DetailDataDTO) -> VenueDetail {
return VenueDetail(isVeg: dto.isVeg)
}
}
In API Manager I have get the data from api and use above struct as follow
let venueDetail = VenueDetailDTOMapper.map(getDetail)
ViewModel:
enum VenueDetailVMTypes {
case veueInfoInfo
}
protocol VenueDetailVMItems {
var type: VenueDetailVMTypes { get }
}
struct VenueInfoViewModel: VenueDetailVMItems {
var type: VenueDetailVMTypes {
return .veueInfoInfo
}
var headerSection: VenueDetail
}
func cretaDataSource() {
if let getVenueDetails = self.venueDetails {
let vmType = VenueInfoViewModel(headerSection: getVenueDetails)
arrayDataSource.append(vmType)
}
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let headerView = UIView()
let venueDetailVMItems = viewModel.arrayDataSource[section]
switch venueDetailVMItems.type {
case .veueInfoInfo:
let headerCell = tableView.dequeueReusableCell(withIdentifier: kCellIdentifierVenueHeader) as! VenueHeaderTVCell
headerCell.updateCellData(detail: (venueDetailVMItems as! VenueInfoViewModel).headerSection)
headerCell.foodTypeHandler = { [weak self] (isOn) in
guard let strongSelf = self else {
return
}
strongSelf.viewModel.showOnlyVegMenu(shouldShowVeg: isOn)
(venueDetailVMItems as! VenueInfoViewModel).headerSection.isVeg = isOn. //Cannot assign to immutable expression of type 'Bool'
strongSelf.tableView.reloadData()
}
headerView.addSubview(headerCell)
break
}
return headerView
}
Structs are value types, so each time you assign a struct, it makes a copy. You're treating it as a reference type. Stripping away all the as! casting, what you've done is:
let value = array[index]
value.someBool = true
reloadData()
Even if value were mutable (which it could be), that wouldn't do anything. value is a copy of array[index], not a reference to it. If you want it to be a reference, then you need to make it a reference type (a class).
You've used a protocol and a "type" identifier, where what I think you really wanted was an enum with associated data:
enum VenueDetail {
case veueInfoInfo(VenueInfoViewModel)
}
With this, you get rid of all of the dangerous and complicated as! casting.
But all of that doesn't really change the issue you're describing. Either way (with a protocol or with an enum), what you need to do is:
var value = array[index]
// change value the ways you want; set the bool, etc.
array[index] = value
A structure is an aggregation of fields; if a particular structure instance is mutable, its fields will be mutable; if an instance is immutable, its fields will be immutable. A structure type must thus be prepared for the possibility that the fields of any particular instance may be mutable or immutable.
Please check this
So try to change let to be var
Make sure the the arrayDataSource is mutable user var not let
var arrayDataSource = [VenueInfoViewModel]()
After struggling i just create a method in viewModel that removes objects in array of type .venueInfo and reload, i know its kind of hack but time being i have no option. In case if somebody found better way, really appreciated
func changeHeaderSwitch(isVeg: Bool) {
arrayDataSource.removeAll { (venueDetailVMItems) -> Bool in
return venueDetailVMItems.type == .veueInfoInfo
}
if var getVenueDetails = self.venueDetails {
getVenueDetails.isVeg = isVeg
let vmType = VenueInfoViewModel(headerSection: getVenueDetails, arrayMenuInfo: [])
arrayDataSource.append(vmType)
}
}
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.
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 can I check if a property is an Array (of any type)? This code always only prints "Worker". Is there a way (dynamically) to know if a property is an Array without inform the type?
final class Worker: NSObject {
var id: Int?
var array: Array<Worker>?
}
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let worker = Worker()
worker.id = Int(2) as Int?
worker.array = [Worker(),Worker(),Worker()]
let mirror = reflect(worker)
for i in 0..<mirror.count {
let (name, childMirror) = mirror[i]
if childMirror.disposition == .Optional {
let (newName,subChildMirror) = childMirror[0]
if subChildMirror.valueType is Array<AnyClass>.Type {
println("AnyClass")
}
if subChildMirror.valueType is Array<AnyObject>.Type {
println("AnyObject")
}
if subChildMirror.valueType is Array<Any>.Type {
println("Any")
}
if subChildMirror.valueType is Array<NSObject>.Type {
println("NSObject")
}
if subChildMirror.valueType is Array<Worker>.Type {
println("Worker")
}
}
}
}
}
Ps.: I need to deal with Array<>
An array of any type can always be casted to a NSArray. So you could check if it's an array with code like this:
if _ = subChildMirror.valueType as? NSArray {
println("Array")
}
It's also possible to dynamically get the type of the objects of that array. In my EVReflection library I do something similar. I extended the Array in order to get a new element of an object what should be in that Array. In your case you could then get the .dynamicType from dat object.
So the code would become:
let arrayType = worker.array.getTypeInstance().dynamicType
Here is the Array extension
extension Array {
/**
Get the type of the object where this array is for
:returns: The object type
*/
public func getTypeInstance<T>(
) -> T {
let nsobjectype : NSObject.Type = T.self as! NSObject.Type
let nsobject: NSObject = nsobjectype.init()
return nsobject as! T
}
}
I have a list translated with J2Objc:
var userDtoIds:JavaUtilList = // some list
Now I want to iterate over this list with a for in loop. I tried:
for iten in userDtoIds {
}
Error: Type 'JavaUtilList' does not conform to protocol 'SequenceType'
With:
let arr:IOSObjectArray = userDtoIds.toArray()
for iten in arr {
}
I got the Error: Type 'IOSObjectArray' does not conform to protocol 'SequenceType'
The only way how it works is:
for var i:Int32=0; i < userDtoIds.size(); i++ {
}
Can I use a for in loop to iterate over a JavaUtilList?
Edit:
The following code leads to a runtime error:
var list = userDtoIds as! NSArray
for item:String in list as! [String] {
}
extension IOSObjectArray: Sequence {
func makeIterator() -> Iterator {
// Create iterator
}
Here is a wonderful article on conforming custom collection types, even if you are using j2objc.