Swift Check If Value Is of Type Array (Of Any Type)

Swift check if value is of type array (of any type)

Got it working like this, although it's not as beautiful as I would've hoped:

protocol ArrayType {}
extension Array : ArrayType {}

let intArray : Any = [1, 2, 3]
let stringArray : Any = ["hi", "hello", "world"]

intArray is ArrayType // true
stringArray is ArrayType // true

EDIT: I think I misunderstood your question before, now I got it though:

let intArray = [1, 2, 3]

let anyArray = intArray.map{ $0 as Any }

This is the only way to my knowledge.

Swift - Check if a value belongs is in an array

First of all, arrays define with [Type] like [User]

Second of all init method calls as with (Arguments) like User(name: ,age:)

And last but not least, don't forget the ',' between elements of the array.

So

struct User: Identifiable {
var id = UUID()
var name: String
var age: String
}

var array: [User] = [
User(name: "AZE", age: "10"),
User(name: "QSD", age: "37")
]

So now you can check your element inside with contains like

array.contains(where: { user in user.name == "AZE" }) // returns `true` if it is

Tips

Try name arrays not array. Use plural names instead like users



To returtning the found one:

users.first(where: { user in user.name == "AZE" }) 


To summarizing it

users.first { $0.name == "AZE" } 

How to check generic class type is array?

As commentator @Holex says, you can use Any. Combine it with Mirror and you could, for example, do something like this:

func isItACollection(_ any: Any) -> [String : Any.Type]? {
let m = Mirror(reflecting: any)
switch m.displayStyle {
case .some(.collection):
print("Collection, \(m.children.count) elements \(m.subjectType)")
var types: [String: Any.Type] = [:]
for (_, t) in m.children {
types["\(type(of: t))"] = type(of: t)
}
return types
default: // Others are .Struct, .Class, .Enum
print("Not a collection")
return nil
}
}

func test(_ a: Any) -> String {
switch isItACollection(a) {
case .some(let X):
return "The argument is an array of \(X)"
default:
return "The argument is not an array"
}
}

test([1, 2, 3]) // The argument is an array of ["Int": Swift.Int]
test([1, 2, "3"]) // The argument is an array of ["Int": Swift.Int, "String": Swift.String]
test(["1", "2", "3"]) // The argument is an array of ["String": Swift.String]
test(Set<String>()) // The argument is not an array
test([1: 2, 3: 4]) // The argument is not an array
test((1, 2, 3)) // The argument is not an array
test(3) // The argument is not an array
test("3") // The argument is not an array
test(NSObject()) // The argument is not an array
test(NSArray(array:[1, 2, 3])) // The argument is an array of ["_SwiftTypePreservingNSNumber": _SwiftTypePreservingNSNumber]

How to check that object of type `Any` is an array of concrete class implementing some protocol

I found a workaround to solve this problem:

func classTypeFrom(_ className: String) -> AnyClass!{
if let appName = Bundle.main.object(forInfoDictionaryKey: "CFBundleName") as? String {
let classStringName = "_TtC\(appName.characters.count)\(appName)\(className.characters.count)\(className)"
return NSClassFromString(classStringName)
}
return nil;
}

func update(object: Any){
if let array = object as? [Initiatable]{
let arrayTypeName = "\(type(of: ar))"
let objectTypeName = arrayTypeName.substringFrom(index: 6, length: arrayTypeName.characters.count - 7)
if let arrayType = classTypeFrom(objectTypeName) as? Initiatable.Type{
process(array: array, type: arrayType)
}
}
}

func process(array: [Initiatable], type: Initiatable.Type){
var ar = array
let newObj = type.init()
ar.append(newObj)
}

Checking if an object is a given type in Swift

If you want to check against a specific type you can do the following:

if let stringArray = obj as? [String] {
// obj is a string array. Do something with stringArray
}
else {
// obj is not a string array
}

You can use "as!" and that will throw a runtime error if obj is not of type [String]

let stringArray = obj as! [String]

You can also check one element at a time:

let items : [Any] = ["Hello", "World"]
for obj in items {
if let str = obj as? String {
// obj is a String. Do something with str
}
else {
// obj is not a String
}
}

Array of objects that have specific type

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
}

Checking if a metatype is an Array with an Element type of kind MyProtocol

The problem is that while instances of [MyDto] can be freely converted to [MappableProtocol] and [Any], these are really just magical conversions that the compiler does behind the scenes (see this Q&A for more information).

The same conversions don't exist for metatype values, which is why Swift says that a [MyDto].Type is not a [MappableProtocol].Type nor a [Any].Type – they are unrelated metatype types.

Likely the simplest solution in your case would just be to forget working with metatypes, and instead just declare different overloads of test(a:) to handle different ResponseType types.

// 'default' overload of test(a:)
func test<T : ResponseProtocol>(a: T) -> String {
return "notFound"
}

func test<T : ResponseProtocol>(a: T) -> String where T.ResponseType == String {
return "String"
}

func test<T : ResponseProtocol>(a: T) -> String where T.ResponseType : MappableProtocol {
return "MappableProtocol"
}

func test<T : ResponseProtocol>(a: T) -> String where T.ResponseType == [MyDto] {
return "Array<MDto>"
}

// overload of test(a:) that accepts a type that conforms to ResponseProtocol, where the
// ResponseType is an Array with arbitrary Element type.
func test<T : ResponseProtocol, ResponseTypeElement : MappableProtocol>(a: T) -> String
where T.ResponseType == [ResponseTypeElement]
{
return "Array<MappableProtocol>"
}

print(test(a: A1())) // String
print(test(a: A2())) // MappableProtocol
print(test(a: A3())) // Array<MyDto>

// A MappableProtocol with a ResponseType that conforms to MappableProtocol,
// but isn't MyDto.
class Foo : MappableProtocol { required init(map: String) { } }
class A4 : ResponseProtocol { typealias ResponseType = [Foo] }

print(test(a: A4())) // Array<MappableProtocol>

(I removed your API class just to simplify things)

The compiler will simply resolve which overload to call at compile time, rather than having the runtime jump through lots of type-casting hoops.


If you insist on working with metatype values, one possible solution is to define a dummy protocol for Array to conform to (see for example this similar Q&A), which we can then cast the metatype values to. We can then declare an elementType static requirement in order to extract the Array's Element.self metatype value, which we can then inspect the type of in order to determine what the array is convertible to.

For example, if we define and conform Array to _ArrayProtocol:

protocol _ArrayProtocol {
static var elementType: Any.Type { get }
}

extension Array : _ArrayProtocol {
static var elementType: Any.Type {
return Element.self
}
}

We can now use test(a:) like so:

func test<T : ResponseProtocol>(a: T) -> String {

if T.ResponseType.self is String.Type {
return "String"
}

if T.ResponseType.self is MappableProtocol.Type {
return "MappableProtocol"
}

// attempt to cast the T.ResponseType.self metatype value to the existential metatype
// type _ArrayProtocol.Type (i.e a type that conforms to _ArrayProtocol),
// in this case, that's only ever Array.
if let responseType = T.ResponseType.self as? _ArrayProtocol.Type {

// switch on the element type, attempting to cast to different metatype types.
switch responseType.elementType {
case is MyDto.Type:
return "Array<MyDto>"
case is MappableProtocol.Type:
return "Array<MappableProtocol>"
default:
return "Array<Any>"
}
}

return "notFound"
}

print(test(a: A1())) // String
print(test(a: A2())) // MappableProtocol
print(test(a: A3())) // Array<MyDto>
print(test(a: A4())) // Array<MappableProtocol>


Related Topics



Leave a reply



Submit