I would like to have my function return an array in which the first element is a String and the second is a UIImageView object. E.g.
["An example string", UIImageView()]
How do I tell the function that this will be returned in the section that follows the ->
So basically I want a function like this:
func DoSomething(num:Int) -> Array[String, UIImageView()] {
// Each of the following objects in the Array are UIImageView objects
Let imageViewObjects = [image1, image2, image3]
return [String(num), imageViewObjects[num]]
}
But the part I know I am getting wrong is the
Array[String, UIImageView]
P.S. I need to declare this because if I use [AnyObject] it will raise an error later on in the code basically saying that it cant manipulate an object of type AnyObject
Note that an Array is declared as [Int] or Array<Int> rather than Array[Int]. [Int] and Array<Int> are the same thing. You don't need to use both at once.
The easiest thing to do is use a tuple, declared like this:
(String, UIImageView)
I would use it like this (your code with corrections):
import UIKit
typealias StringView = (String, UIImageView)
// Returning an Optional to pass back that num may be out of range
// note that I'm using the typealias StringView here
func DoSomething(num:Int) -> StringView? {
let image1 = UIImageView()
let image2 = UIImageView()
let image3 = UIImageView()
let imageViewObjects = [image1, image2, image3]
// Need to check that num is a valid index
guard num < imageViewObjects.count else { return nil }
// return the tuple if num is valid
return (String(num), imageViewObjects[num])
}
Example usage:
if let returned = DoSomething(2) {
// printing the first item in returned tuple
print(returned.0)
}
// output: "2"
You can also use protocols to create a common protocol, add it to the classes in an extension, and then use the protocol in the declaration:
protocol Foo {}
extension String : Foo {}
extension UIImageView: Foo {}
var myArray:[Foo] ...
If you're going to use the returned value in a lot of places you might want to make it a full-blown struct or class:
import UIKit
struct StringView {
let string:String
let view:UIImageView
}
// Returning an Optional to pass back that num may be out of range
// note that I'm using the typealias StringView here
func DoSomething(num:Int) -> StringView? {
let imageViewObjects = [UIImageView(),
UIImageView(),
UIImageView()]
// Need to check that num is a valid index
guard num < imageViewObjects.count else { return nil }
// return the tuple if num is valid
return StringView(string: String(num), view: imageViewObjects[num])
}
if let returned = DoSomething(2) {
// printing the member "string" in the returned struct
print(returned.string)
}
// output: "2"
A tuple is usually the better choice unless you are using custom structs and classes.
You can use Dictionary objects in swift, here you can use key as your String and value as array of ImageView
let imageViewObjects = [image1, image2, image3]
let array : [String : UIImageView] = [
String(num) : imageViewObjects[num]
]
If you want to return only array you can do like this
let imageViewObjects = [image1, image2, image3]
var array : [AnyObject] = [AnyObject]()
array.append(String(num))
array.append(imageViewObjects[num])
In this you have to be sure that first object is a String and second is array of UIImageView
Related
I'm trying to understand this how the mapValues method works in the following code from Calendar Heatmap.
First, a function loads a dictionary:
private func readHeatmap() -> [String: Int]? {
guard let url = Bundle.main.url(forResource: "heatmap", withExtension: "plist") else { return nil }
return NSDictionary(contentsOf: url) as? [String: Int]
}
heatmap.plist is a key/value list like this:
<key>2019.5.3</key>
<integer>3</integer>
<key>2019.5.5</key>
<integer>4</integer>
<key>2019.5.7</key>
<integer>3</integer>
A property is initialized using the above function:
lazy var data: [String: UIColor] = {
guard let data = readHeatmap() else { return [:] }
return data.mapValues { (colorIndex) -> UIColor in
switch colorIndex {
case 0:
return UIColor(named: "color1")!
case 1:
return UIColor(named: "color2")!
case 2:
return UIColor(named: "color3")!
case 3:
return UIColor(named: "color4")!
default:
return UIColor(named: "color5")!
}
}
}()
Finally, the data property defined above is used in the following function:
func colorFor(dateComponents: DateComponents) -> UIColor {
guard let year = dateComponents.year,
let month = dateComponents.month,
let day = dateComponents.day else { return .clear}
let dateString = "\(year).\(month).\(day)"
return data[dateString] ?? UIColor(named: "color6")!
}
Apple's documentation states that mapValues returns a dictionary "containing the keys of this dictionary with the values transformed by the given closure."
My questions is, what exactly is the value colorIndex passed into the closure in data.mapValues { (colorIndex) -> UIColor in? Is it from heatmap.plist? I'm confused how a String is passed into date, date[dateString] from the colorFor(dateComponents: ) function, but colorIndex is Int.
Originally, data is like this:
"2019.5.3" : 3
"2019.5.5" : 4
"2019.5.7" : 3
Suppose you did data.mapValues(f), where f is a function, the resulting dictionary will look like this:
"2019.5.3" : f(3)
"2019.5.5" : f(4)
"2019.5.7" : f(3)
So now, the value type of the dictionary changes to the return type of f, while the key type remains unchanged.
what exactly is the value colorIndex passed into the closure?
It's every value in data. Every value will be passed into closure once.
To see this more clearly, I've written one possible way that mapValues could be implemented:
extension Dictionary {
func myMapValues<T>(_ transform: (Value) throws -> T) rethrows -> [Key: T] {
var retVal = [Key: T]()
for entry in self {
retVal[entry.key] = try transform(entry.value)
}
return retVal
}
}
Is it from heatmap.plist?
Indirectly, yes. The contents of the local variable data (the [String: Int]) was originally from heatmap.plist, but mapValues operates directly on the data already read from the file.
I'm confused how a String is passed into data, data[dateString] from the colorFor(dateComponents: ) function, but colorIndex is Int.
colorIndex is irrelevant here. colorIndex is simply the name of the function parameter of the function that you pass to mapValues. mapValues has been called at this point, and the dictionary's values have been transformed.
You can pass a String into data because the data dictionary has Strings as keys. Recall that mapValues doesn't change the key type. Note that this data is different from the local variable data. I'm talking about the lazy property data, of type [String: UIColor].
I am making am function to convert a Set to an array using extension to Set. But I am not able to get generic type of Set in that extension. For example if there is an object of Set of type String then toArray() function should return an array of String ( [String] ). I am creating this function like this.
extension Set{
func toArray() -> [/*What type should take*/]{
var array = [/*What type should take*/]()
for s in self{
array.append(s)
}
return array
}
}
// Here is what I am expecting from the above func.
var myset = Set<String>()
let arr = myset.toArray() // Should return [String]
It was simple use Element to get its Generic type
extension Set{
func toArray() -> [Element]{
var array = [Element]()
for s in self{
array.append(s)
}
return array
}
}
You can use Element to get the element type, however there is no real need for this extension as you can simply say let arr = Array(mySet)
If you did want to use the extension you can simplify it to:
extension Set {
func toArray() -> [Element] {
return Array(self)
}
}
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
I have been trying to fix all my code since swift 2.0 update. I have a problem that seems to be the way tuples work now:
public func generate() -> AnyGenerator <(String, JSON)> {
switch self.type {
case .Array:
let array_ = object as! [AnyObject]
var generate_ = array_.generate()
var index_: Int = 0
return anyGenerator{
if let element_: AnyObject = generate_.next() {
return ("\(index_++)", JSON(element_))
} else {
return nil
}
}
case .Dictionary:
let dictionary_ = object as! [String : AnyObject]
var generate_ = dictionary_.generate()
return anyGenerator{
if let (key_: String, value_: AnyObject) = generate_.next() {
return (key_, JSON(value_))
} else {
return nil
}
}
default:
return anyGenerator{
return nil
}
}
}
Specifically the line:
if let (key_: String, value_: AnyObject) = generate_.next()
Is throwing the error: Tuple pattern element label 'key' must be '_'
I tried to make that change already, but I didnt work...
Any ideas?
The problem is: We cannot use type annotation inside of tuple patterns anymore.
In the release notes:
Type annotations are no longer allowed in patterns and are considered part of the outlying declaration. This means that code previously written as:
var (a : Int, b : Float) = foo()
needs to be written as:
var (a,b) : (Int, Float) = foo()
if an explicit type annotation is needed. The former syntax was ambiguous with tuple element labels. (20167393)
So, you can:
if let (key_, value_): (String, AnyObject) = generate_.next() {
But in this case, you could omit : (String, AnyObject):
if let (key_, value_) = generate_.next() {
I am taking my first foray into writing generic functions in Swift. What I am trying to do is write a function that takes an array input of any type as long as that type is convertible to a floating point number. I am wondering if I can leverage some of the Swift standard library protocols to do this. Here is a trivial example (I am searching for what to use as ConvertibleToFloatingPointTypeProtocol):
func toDoubleArray<T: ConvertibleToFloatingPointTypeProtocol>(array: [T]) -> [Double] {
var doubleArray = [Double]()
for arrayItem in array {
doubleArray.append(Double(arrayItem))
}
return doubleArray
}
The compiler error I get from this when I try FloatingPointType, etc. is: "Cannot find an initializer for type 'Double' that accepts an argument list of type '(T)'"
Now I know another option is to create my own protocol and then extend the types that I am interested in to adopt it, but this just feels like something that exists right under my nose.
Try FloatLiteralConvertible:
import Darwin
// Swift 2.0
func toDoubleArray<T : FloatLiteralConvertible>(arr : [T]) -> [Double] {
return arr.flatMap { $0 as? Double }
}
// Swift 1.2
func toDoubleArray<T : FloatLiteralConvertible>(arr : [T]) -> [Double] {
var result = [Double]()
for a in arr {
if let d = a as? Double {
result.append(d)
}
}
return result
}
let a = toDoubleArray([1, 2, 3])
let b = toDoubleArray([M_PI, 2 as Int, 3.3])
let c = toDoubleArray(["a", "b", "c"]) // Error, cannot convert [String] to [Double]
let d = toDoubleArray([1, 2, 3, "a"]) // Error, cannot convert [NSObject] to [Double]