Check "isKind(of: )" from list of array with classes - arrays

I have an array of viewController class names like (ViewController class 1, 2 ,3 4)
let classArray = [VCclass1, VCclass2, VCclass3, VCclass4]
I want to check the object belongs to any of the class mentioned in class array like
if obj.isKind(of:(Any of the classArray elements) ) {
//do something
} else {
//execute else condition
}
How can I write "if condition" here?

Well, you're looking to see if your array contains a member according to the type of obj. So, contains(where:) is a perfect fit:
if classes.contains(where: { type(of: obj) == $0 }) {
//do something
} else {
//execute else condition
}

You can verify that in a simple way.
import Foundation
import UIKit
class A: UIViewController {}
class B: UIViewController {}
class C: UIViewController {}
let array:[UIViewController.Type] = [A.self,B.self,C.self]
let obj = A()
print(array.contains(where: { obj.isKind(of: $0) }))
Output should be true. You can run this code in Playground
However I would recommend switch for that purpose. In more complicated scenario you will want to know which class is given object or so.
switch obj {
case is A:
print("A")
case is B:
print("B")
case is C:
print("C")
default:
print("none")
}

Try this:
extension NSObject {
func isKind(of classes: [AnyClass]) -> Bool {
for aClass in classes {
if self.isKind(of: aClass) {
return true
}
}
return false
}
}
let classes = [UIViewController.self]
let obj = UIViewController()
if (obj.isKind(of: classes)) {
//do something
}
or the uglier, less OOP way...
var isKindOfClass = false
for aClass in classes {
if obj.isKind(of: aClass) {
isKindOfClass = true
break
}
}
if isKindOfClass {
//do something
}

Related

How would I change the value of a specific element in an array?

Its a popular post on the topic but it did not answer my question because our query methods are different.
Let's say I have an array:
final class SomeClass: ObservableObject {
#Published var myArray = ["cat","dog","mouse"]
func changeValueFor(index: Int, to newName: String) {
//Dont know what to put here, See below for the picture of the method I thought I could use
}
}
And I want to access its properties and change it on my view:
struct MyView: View {
#ObservedObject var vm: SomeClass
index: Int
var body: some View {
Text(vm.myArray[index])
Button("Change Name To") {vm.changeValueFor(index: index, to: "goat" }
}
}
The normal way of accessing an array to display on a view is just passing in the Int from some provided Iterator like ForEach but in this case I wasn't sure how to go about it...
I thought I could use this 👇, but apparently not
Hopefully I was clear enough but let me know if I need explain my problem more.
you can use this code safely with no issue for that func:
func changeValueFor(index: Int, to newName: String) {
if myArray.indices.contains(index) {
myArray[index] = newName
}
else {
print("Error! there is no such index found!")
}
}
Fast and efficient.
func changeValueFor(index: Int , to newName: String) {
if myArray.count > index,
index > -1 {
myArray[index] = newName
}
else {
print("index out of range")
}
}

Array of objects that have specific type

I have an array of object, eg
var objects: [AnimalDetailModel] = ...
and also three classes
AnimalDetailModel is a base class
DogDetailModel is a class that extends AnimalDetailModel
CatDetailModel is a class that extends AnimalDetailModel
From a datasource I create and add arrays of DogDetailModels, CatDetailModels and AnimalDetailModels to objects. And when populating the tableView what I want is to get an object form objects and check if it is of type DogDetailModel, CatDetailModel or AnimalDetailModel like
if let objects[indexPath.row] as? DogDetailModel {
return DogTableCell
} else if let objects[indexPath.row] as? CatDetailModel {
return CatTableCell
} else {
return AnimalTableCell
}
While doing this I get type AnimalDetailModel has no subscript members. How do we check the type of objects from array of objects?
You can use the short and simple is attribute.
which in your case will be:
switch objects[indexPath.row] {
case is DogDetailModel:
return DogTableCell
case is CatDetailModel:
return CatTableCell
default:
return AnimalTableCell
}
You need to assign the cast to a variable in your if/else but since you are not using the result of the cast you can use _
if let _ = objects[indexPath.row] as? DogDetailModel {
return DogTableCell
} else if let _ = objects[indexPath.row] as? CatDetailModel {
return CatTableCell
} else {
return AnimalTableCell
}
For your Specific case #Vollan's answer is good. However you would want to show other things like a comment liked or disliked or a user blocked by other user or rank of users etc... To show such different cases on UITableViewCells or UICollectionViewCells my approach is first define an enum
enum modelIdentifier: String {
case dogDetailModel = "dogDetailModel"
case catDetailModel = "catDetailModel"
case animalDetailModel = "animalDetailModel"
}
then pass identifiers into models:
struct DogDetailModel {
// var yourJSONobjects : String? .....
//add your identifier under your model
var modelIdentity: modelIdentifier = .dogDetailModel
}
struct CatDerailModel {
// var yourJSONobjects : String? .....
//add your identifier under your model
var modelIdentity: modelIdentifier = .catDetailModel
}
and check it wherever you want:
if objects[indexPath.row].modelIdentity == .dogDetailModel {
return DogTableCell
} else if objects[indexPath.row].modelIdentity == .catDetailModel {
return CatTableCell
} else {
return AnimalTableCell
}
The operator to check the instance of the variable is "is" and I solved my issue as below
if let objects[indexPath.row] is DogDetailModel {
return DogTableCell
} else if let objects[indexPath.row] is CatDetailModel {
return CatTableCell
} else {
return AnimalTableCell
}
You can use the swift standard library function called isKind(of: AnyClass).
For more details please refer this link

Flatten iterator of two dimensional array in Kotlin

I have a Grid class which is wrapper over 2d array of Cell objects. I would like this class to implement Iterable<Cell> interface in order to use it in loops and iterate directly overall cells. Is there a simple way to do that? Does Kotlin support yield return style iterators? My current solution is quite verbose:
override fun iterator() = object : Iterator<Cell> {
val currentOuter = grid.iterator() // grid is object of Array<Array<Cell>>
var currentInner = if (currentOuter.hasNext()) currentOuter.next().iterator() else arrayOf<Cell>().iterator()
override fun next(): Cell {
if (!hasNext()) {
throw NoSuchElementException()
}
return if (currentInner.hasNext()) {
currentInner.next()
} else {
currentInner = currentOuter.next().iterator()
currentInner.next()
}
}
override fun hasNext(): Boolean {
return currentInner.hasNext() || currentOuter.hasNext()
}
}
Does Kotlin support yield return style iterators?
Yes, it does, through the feature of coroutines. Here's a self-contained example:
data class Cell(val d: Int)
val grid: Array<Array<Cell>> = arrayOf(arrayOf(Cell(1), Cell(2)), arrayOf(Cell(3), Cell(4)))
fun cellSequence() = buildSequence {
grid.forEach { it.forEach { yield(it) } }
}
fun main(args: Array<String>) {
cellSequence().forEach { println(it) }
}
Although this particular problem could have been simply solved with just a flatMap, the presented code can serve as a template to write any kind of procedural-looking code, for example:
fun complexCellSequence() = buildSequence {
yield(Cell(-1))
if (grid.size <= 2) {
yield(Cell(2))
}
for (row in grid) {
if (row.contains(Cell(1))) {
yield(Cell(1))
} else {
yield(Cell(12))
row.forEach { yield(it) }
}
}
}
This would be quite nontrivial to rewrite without coroutines.
A very simple solution would be something like this:
val grid: Array<Array<Cell>> = ...
override fun iterator() : Iterator<Cell> = grid.flatMap { it.asList() }.iterator()

How to avoid a retain cycle when using an array of delegates in Swift [duplicate]

This question already has answers here:
Using as a concrete type conforming to protocol AnyObject is not supported
(6 answers)
Closed 5 years ago.
In one of my classes I use an array of delegates (the class is a singleton). This is causing an retain cycle. I know I can avoid the retain cycle when I use only one delegate by making the delegate weak. But this is not working for my array of delegates.
How can I avoid this retain cycle.
Example:
protocol SomeDelegate: class {
func someFunction()
}
My Class
class SomeClass {
// This class is a singleton!
static let sharedInstance = SomeClass()
var delegates = [SomeDelegate]() // this is causing a retain cycle
weak var delegate: SomeDelegate? // this is ok.
... other code...
}
The problem is that weakDelegates is a strong reference and its reference to its elements of type WeakDelegateContainer is a strong reference.
Your situation is why the class NSHashTable exists. Initialize using weakObjects(). This will give you a set of ARC-weak references, each of which will be nilified and removed when the referenced object goes out of existence (with no need for any extra bookkeeping on your part, and no need for your WeakDelegateContainer type).
Your set will have to be typed as holding AnyObject, but you can easily mediate to ensure that you are supplying and retrieving SomeDelegate-conformant objects:
let list = NSHashTable<AnyObject>.weakObjects()
func addToList(_ obj:SomeDelegate) {
list.add(obj)
}
func retrieveFromList(_ obj:SomeDelegate) -> SomeDelegate? {
if let result = list.member(obj) as? SomeDelegate {
return result
}
return nil
}
func retrieveAllFromList() -> [SomeDelegate] {
return list.allObjects as! [SomeDelegate]
}
The function retrieveAllFromList() lists only objects that still exist. Any object that has gone out existence has been changed to nil in the NSHashTable and is not included in allObjects. That is what I mean by "no extra bookkeeping"; the NSHashTable has already done the bookkeeping.
Here is code that tests it:
func test() {
let c = SomeClass() // adopter of SomeDelegate
self.addToList(c)
if let cc = self.retrieveFromList(c) {
cc.someFunction()
}
print(self.retrieveAllFromList()) // one SomeClass object
delay(1) {
print(self.retrieveAllFromList()) // empty
}
}
Alternatively, you can use NSPointerArray. Its elements are pointer-to-void, which can be a little verbose to use in Swift, but you only have to write your accessor functions once (credit to https://stackoverflow.com/a/33310021/341994):
let parr = NSPointerArray.weakObjects()
func addToArray(_ obj:SomeDelegate) {
let ptr = Unmanaged<AnyObject>.passUnretained(obj).toOpaque()
self.parr.addPointer(ptr)
}
func fetchFromArray(at ix:Int) -> SomeDelegate? {
if let ptr = self.parr.pointer(at:ix) {
let obj = Unmanaged<AnyObject>.fromOpaque(ptr).takeUnretainedValue()
if let del = obj as? SomeDelegate {
return del
}
}
return nil
}
Here is code to test it:
let c = SomeClass()
self.addToArray(c)
for ix in 0..<self.parr.count {
if let del = self.fetchFromArray(at:ix) {
del.someFunction() // called
}
}
delay(1) {
print(self.parr.count) // 1
for ix in 0..<self.parr.count {
if let del = self.fetchFromArray(at:ix) {
del.someFunction() // not called
}
}
}
Interestingly, after our SomeClass goes out of existence, our array's count remains at 1 — but cycling through it to call someFunction, there is no call to someFunction. That is because the SomeClass pointer in the array has been replaced by nil. Unlike NSHashTable, the array is not automatically purged of its nil elements. They do no harm, because our accessor code has guarded against error, but if you would like to compact the array, here's a trick for doing it (https://stackoverflow.com/a/40274426/341994):
self.parr.addPointer(nil)
self.parr.compact()
I found the solution in Using as a concrete type conforming to protocol AnyObject is not supported. All credits to Kyle Redfearn.
My solution
protocol SomeDelegate: class {
func someFunction()
}
class WeakDelegateContainer : AnyObject {
weak var weakDelegate: SomeDelegate?
}
class SomeClass {
// This class is a singleton!
static let sharedInstance = SomeClass()
fileprivate var weakDelegates = [WeakDelegateContainer]()
func addDelegate(_ newDelegate: SomeDelegate) {
let container = WeakDelegateContainer()
container.weakDelegate = newDelegate
weakDelegates.append(container)
}
func removeDelegate(_ delegateToRemove: SomeDelegate) {
// In my case: SomeDelegate will always be of the type UIViewController
if let vcDelegateToRemove = delegateToRemove as? UIViewController {
for i in (0...weakDelegates.count - 1).reversed() {
if weakDelegates[i].weakDelegate == nil {
// object that is referenced no longer exists
weakDelegates.remove(at: i)
continue
}
if let vcDelegate = weakDelegates[i].weakDelegate as? UIViewController {
if vcDelegate === vcDelegateToRemove {
weakDelegates.remove(at: i)
}
}
}
}
}
... other code ...
}

How to check if an object is in array

Edit: The problem is already solved by #vacawama. But if you are looking for an answer for NSObject classes, you should implement isEqual function which is NSObjectProtocol. Otherwise you gonna get an error says: " Redundant conformance of 'classname' to protocol 'Equatable' "
You can check this for details: Swift 2.2, Contains Method not working
In swift, how can i check if an object is in array?
I have a simple class like this;
class Test: {
private var _number: Int!
private var _type: String!
var number: Int {
return _number
}
var type: String {
return _type
}
init (number: Int, type: String) {
self._number = number
self._type = type
}
}
Also i have this class;
class TestRandom {
private let _numberArr: [Int] = [1,2,3,4,5,6,7,8,9,10]
private let _typeArr: [String] = ["x","y","z"]
public private(set) var _testArr: [Test] = []
private var _randomTest: Test!
func randomTestPicker () {
repeat {
let randomNumber = Int(arc4random_uniform(UInt32(self._numberArr.count)))
let randomType = Int(arc4random_uniform(UInt32(self._typeArr.count)))
self._randomTest = Test(number: self._numberArr[randomNumber], type: self._typeArr[randomType])
} while self._testArr.contains(_randomTest)
}
}
All i want to do is to pick different objects. Lets say i have x2,y4,x6,z3,z8,y2 in _testArr. When i call randomTestPicker, it should not pick x2 or z8. Because they are already in array.
I have tried contains as you see. However it did not work for me. Is there any solution that i can use for this purpose? Or what is the best way to do this?
Edit: I tried self._testArr.contains{$0 === _randomTest} but not working neither.
You can't use contains that way since your class doesn't conform to the Equatable protocol.
Add :Equatable to your class definition and implement the == function which compares two of your objects:
class Test: Equatable {
private var _number: Int!
private var _type: String!
var number: Int {
return _number
}
var type: String {
return _type
}
init (number: Int, type: String) {
self._number = number
self._type = type
}
}
func ==(lhs: Test, rhs: Test) -> Bool {
return lhs.number == rhs.number && lhs.type == rhs.type
}
The other way this could have been done is to use the predicate form of contains. The predicate takes two objects and returns a Bool indicating if they match. In that case, you would write:
self._testArr.contains { $0.number == _randomTest.number && $0.type == _randomTest.type }
As you can see, in this case the closure is essentially the == function from above, so implementing the Equatable protocol is the cleaner way to do it.
The closure { $0 === _randomTest } doesn't work because that only tests if the objects are the same instance. In your case, you need to check if the two objects have the same properties, and you are not interested if they are same instance. The way you are creating the objects, you never would create an instance that is already in the array, so this check would always return false.

Resources