Swift: different objects in one array? - ios

Is there a possibility to have two different custom objects in one array?
I want to show two different objects in a UITableView and I think the easiest way of doing this is to have all objects in one array.

Depending on how much control you want over the array, you can create a protocol that both object types implement. The protocol doesn't need to have anything in it (would be a marker interface in Java, not sure if there is a specific name in Swift). This would allow you to limit the array to only the object types you desire. See the sample code below.
protocol MyType {
}
class A: MyType {
}
class B: MyType {
}
var array = [MyType]()
let a = A()
let b = B()
array.append(a)
array.append(b)

If you know the types of what you will store beforehand, you could wrap them in an enumeration. This gives you more control over the types than using [Any/AnyObject]:
enum Container {
case IntegerValue(Int)
case StringValue(String)
}
var arr: [Container] = [
.IntegerValue(10),
.StringValue("Hello"),
.IntegerValue(42)
]
for item in arr {
switch item {
case .IntegerValue(let val):
println("Integer: \(val)")
case .StringValue(let val):
println("String: \(val)")
}
}
Prints:
Integer: 10
String: Hello
Integer: 42

You can use AnyObject array to hold any kind of objects in the same array:
var objectsArray = [AnyObject]()
objectsArray.append("Foo")
objectsArray.append(2)
// And also the inmutable version
let objectsArray: [AnyObject] = ["Foo", 2]
// This way you can let the compiler infer the type
let objectsArray = ["Foo", 2]

You can use the "type" AnyObject which allows you to store objects of different type in an array. If you also want to use structs, use Any:
let array: [Any] = [1, "Hi"]

Related

Unable to compare array value in swift ios

I'm a fresher in iOS
I'm unable to compare in an if statement.
It looks like:
for i in 0.. <array.count
{
if(array[i] == array[i+1])
{
let removedVal = array.remove(i+1)
}
}
The error shows on the if condition:
Binary operator '==' cannot be applied to two 'Any' operands
I googled it, but I am unable to understand what should I do in my case.
=======================================================================
Atlast able to find a solution.
And it worked for me
if ( ((tempArrayForTeamName[i]) as AnyObject).isEqual(tempArrayForTeamName[i+1] as AnyObject) )
need to compare array index position as Any object
And use .isEqual replace of ==
You have to Filter your Array
var newarray = [Int]()
let dictionary = ["A":0,"B":1,"C":1,"D":1,"E":1,"F":1,"G":1,"H":1,"J":0]
let newDictionary = dictionary.reduce([:]) { result, element -> [String: Int] in
guard element.value != 1 else {
return result
}
var newResult = result
newResult[element.key] = element.value
newarray.append(newResult[element.key]!)
return newResult
}
In Swift : Array is a Generic Structure, NSMutableArray is an Objective-C class[will work in Swift].
A NSMutableArray created is of type Any; an array that can contain heterogenous object(could be String, Int or Bool).
An Array is arbitrarily specilized to contain Any (using as [Any])
eg:
var array:Array = ["ABC", 123, true] as [Any]
var nsMutableArray : NSMutableArray = ["ABC", 123, true]
Generic Parameterization:
Even if there is an option to give generic parameterization(Datatype) to your NSMutableArray in Objective C[remember NSMutableArray in an Objective C class],this generic parameterization in unfortunately ignored/not allowed in Swift.
How to specify the datatype:
In Swift one cannot specify the datatype of a NSMutableArray.It would give a compilation error: Cannot specialize non-generic type NSMutableArray.
But one can always specify the datatype of Array(Swift structure) as say: Array<String>.
eg: var array:Array<String> = ["Tom", "Jerry", "Spike"]
Your code has another problem, consider your array has 3 items then i=2, and you are trying to access index 3 (i+1). And program will crash.
Crash point (array[i] == array[i+1])
Please declare specific types array for example
let myArray:[String] = ["a", "b", "c", "d"]

Swift: Store multiple Types in a single array

How do I store multiple Types in a single array?
I'm doing the following:
var vehicles = [string]()
let mustang = Car() // car is a class name
mustang.brandName = "Ford"
mustang.modelName = "Mustang" // string type
mustang.modelYear = 1968
mustang.isConvertibible = true
mustang.isHatchback = false
mustang.hasSunroof = false // bool type
mustang.numberOfDoors = 2 // int type
mustang.powerSource = "gas engine"
// Add it to array
vehicles.append(mustang.brandName)
vehicles.append(mustang.modelName)
vehicles.append(mustang.modelYear) // Error: int type not supported
vehicles.append(mustang.isConvertibible) // Error: bool type not supported
vehicles.append(mustang.brandName)
vehicles.append(mustang.brandName)
How should I achieve this? I'm new to Swift / iOS.
Instead of creating string array you can create the array of car like this and store directly car object
var cars = [Car]()
let mustang = Car() // car is a class name
mustang.brandName = "Ford"
mustang.modelName = "Mustang" // string type
mustang.modelYear = 1968
mustang.isConvertibible = true
mustang.isHatchback = false
mustang.hasSunroof = false // bool type
mustang.numberOfDoors = 2 // int type
mustang.powerSource = "gas engine"
cars.append(mustang)
Or if you want to store different types of object then you cant create array of AnyObject, So that it will store Any type of instance or object inside that array.
var arr = [AnyObject]()
let car = Car()
let bike = Bike()
arr.append(car)
arr.append(bike)
Swift 5
Using protocol oriented programming
A little more swifty solution could be to use protocols. This is type safe, so you won't have to deal with unwrapping and casting optionals.
1. Define a protocol
protocol ModeOfTransportation {
// Define anything in common between objects
// Example:
var color: UIColor { get set }
}
2. Make your models conform to the protocol
(The compiler will ensure you give it the correct types: ex. color)
struct Car: ModeOfTransportation {
var color = UIColor.red
}
struct Bicycle: ModeOfTransportation {
var color = UIColor.blue
}
3. Give your array the protocol as a type
class Example {
var myTransportList: [ModeOfTransportation] = []
func addModesOfTransport() {
let car = Car()
let bike = Bicycle()
myTransportList.append(car)
myTransportList.append(bike)
}
}
I think you're doing something really wrong.
Arrays are designed to hold only one type of stuff. If you really want to hold different types of stuff. Here's some possible methods:
you can try creating an array of AnyObjects:
-
var vehicles = [AnyObject]()
This won't work if the type you want to store does not conform to AnyObject. If you have such a type, you will have to use Any.
you can create an array of strings and convert all the values you want to store to a string
You can just create an array of Cars to store all the properties of a car. Then you can add more cars to it later on. I think this is what you intended.
Per the Swift documentation:
AnyObject can represent an instance of any class type.
Any can represent an instance of any type at all, including function types.
So you could make an array of
var vehicles = [AnyObject]()
and this would take objects (class instances) of any type.
However, the documentation goes on to say:
Use Any and AnyObject only when you explicitly need the behavior and capabilities they provide. It is always better to be specific about the types you expect to work with in your code.
So, ideally you are specific about the type your array can hold.

Combining two arrays of different types [duplicate]

I'm trying to find the best way to merge Swift arrays, that are not of same type, but they have same superclass. I've already read all the tutorials so I know I can use:
var array = ["Some", "Array", "of", "Strings"]
array += ["Another string", "and one more"]
array.append(["Or", "This"])
In this case array variable infers the type [String]. The problem I have relates to the next hypothetical situation with classes (properties do not matter in this case, just there to make a difference between classes):
class A {
var property1 : String?
}
class B : A {
var property2: String?
}
class C : A {
var property3: String?
}
So I create an array of class A and B objects:
var array = [ A(), B(), A(), B() ]
This array should now be of type [A], since this is the inferred type by both A and B classes.
Now I want to append objects to this array, that are of type C.
var anotherArray = [ C(), C(), C() ]
Since anotherArray is now of type [C], we should still be able to append it, since all C instances respond to A methods. So we try:
array += anotherArray
This fails due to:
Binary operator '+=' cannot be applied to operands of type '[A]' and '[C]'.
Similar story with using append method. While this does make sense, I cannot understand why this couldn't work.
Can someone explain why this is not working? What is the best solution to this problem?
The only sensible solution I found is to define the type of anotherArray to be [A], but are there any better ones or this is correct?
var anotherArray : [A] = [ C(), C(), C() ]
Thanks!
If C inherits from A then you can "upcast" an array of type [C] to an array of type [A]
array += anotherArray as [A]
Alternatively, use (tested with Swift 4)
array.append(contentsOf: anotherArray)
In addition to Martin's answer, you could create a protocol that all of the classes conform to and then when creating your array, make it's type that protocol.
Then you can add any of the classes to it without casting.
you can merge almost everything. the only requirement is that all elements of resulting array must conform to the same protocol.
let arr1 = [1,2,3] // [Int]
let arr2 = [4.0,5.0,6.0] // [Double]
let arr3 = ["a","b"] // [String]
import Foundation // NSDate
let arr4 = [NSDate()] // [NSDate]
// the only common protocol in this case is Any
var arr:[Any] = []
arr1.forEach { arr.append($0) }
arr2.forEach { arr.append($0) }
arr3.forEach { arr.append($0) }
arr4.forEach { arr.append($0) }
print(arr) // [1, 2, 3, 4.0, 5.0, 6.0, "a", "b", 2016-02-15 08:25:03 +0000]

Syntax explanation: square brackets in Swift

I'm studying Swift and got confusing with following syntax:
var treasures: [Treasure] = []
Treasure is custom class, declared as follow:
class Treasure: NSObject { }
In Objective-C square brackets mean method, but what do they mean in Swift?
Ok, this is the meaning of
var treasures: [Treasure] = []
var: you are declaring a variable
treasures: the name of your variable
[Treasure]: the type of your variable, in this case the type is Array of Treasure, the compiler will allow you to insert only object of type Treasure in your Array
[]: the actual object (Array) referenced by your variable, in this case an empty Array.
E.g. if you want the Array to hold 2 elements you can write
var treasures: [Treasure] = [Treasure(), Treasure()]
Hope this helps.
Update:
My example can also be written this way
var treasures = [Treasure(), Treasure()]
Infact thanks to the Type Inference the compiler can deduce the type of the variable treasures looking at the type of the assigned value.
[Treasure] is just a syntax sugar for Array<Treasure>.
The same way [String:Treasure] is just a syntax sugar for Dictionary<String,Treasure>.
[] is just an empty array of the type you defined. The same way [:] is an empty dictionary.
When it comes to Swift and square brackets, the rules are simple. They are used only in two situations:
1) working with Array and Dictionary types:
let vectors : [[Int]] = [[1,2,3],[4,5,6]]
let birthBook : [Int:[String]] = [1987:["John","William"], 1990: ["Mary"]]
2) for subscripting objects that support subscripting:
class RouteMapper {
private var routeMap : [String:String] = [:]
subscript(endpoint: String) -> String {
get {
if let route = routeMap[endpoint] {
return route
}
return "/"
}
set(newValue) {
routeMap[endpoint] = newValue
}
}
}
let routeMapper = RouteMapper()
routeMapper["users"] = "/v1/confirmed/users"
let url = routeMapper["admins"]
Since [ and ] are not allowed in custom operators, these are the only usages for now.

Error trying to access members of Array of AnyObject within a Dictionary - Is there a way around unwrapping?

I have a dictionary set up as:
var jDict = Dictionary<String, AnyObject[]>()
Where the arrays are either a collection of custom buttons (JunkButton) or Labels (JunkLabels).
I am having an issue when trying to access the members of the arrays contained in the Dictionary as follows:
let thisArray = jDict[key]
var aButton = thisArray[0] //Gives error: 'AnyObject[]? does not have a member named 'subscript'
I can get around this by downcasting the whole array as follows:
if let aArray = thisArray as? JunkButton[]{
var aButton = aArray[0]
}
This seems very cumbersome especially if I am sure I know what type the array is made up of beforehand. Is there a way to cast thisArray when it is created that would allow me to extract its elements without unwrapping them each time?
Dictionary always give you Optional value.
Your code is like this
let thisArray : Optional<AnyObject[]> = jDict[key]
You need to unwrap it to get non-optional value
let thisArray = jDict[key]! // thisArray is AnyObject[]
You really shouldn't use a dictionary for this. Swift makes it very easy to use custom little structs or classes instead of dictionaries:
struct JunkItems {
var buttons: [JunkButton] = []
var labels: [JunkLabel] = []
}
Then you can access those items like this without downcasting:
for button in junkItems.buttons {
// ...
}
Or:
if let button = junkItems.buttons[0] {
// ...
}
Btw, the array notation [JunkButton] is new in beta 3.

Resources