Return an array of tuples as AnyObject? - swift

Is it possible to return an array of tuples as AnyObject? or would I have to encapsulate the tuple variable within a class then return that class?
Current structure
public typealias Changes = (id:Int!, cors:Bool!)
struct ClientReturn{
var error: NSError?
var json: JSON?
var mutableReturn: AnyObject?
var pageResults: PageResults?
}
class func Changes(api_key: String!, startDate: String?, endDate:String?,
completion: (ClientReturn) -> ()) -> (){
//content
}
Client.Changes(api_key, startDate: nil, endDate: nil){
apiReturn in
var aReturn = apiReturn;
var changesArray = [Changes]()
for(var i = 0; i < apiReturn.json!["results"].count; i++ ){
let json = apiReturn.json!["results"]
changesArray.append((id: json[i]["id"].int, cors: json[i]["cors"].bool))
}
//aReturn.mutableReturn = changesArray as! Changes
aReturn.mutableReturn = changesArray //ERROR (Cannot assign value of type '[Changes]' to type 'AnyObject?')
completionHandler(aReturn)
}

The only thing that can be cast up to AnyObject is a class (or a Swift struct that is bridged to some class).
A tuple is not a class. Thus, it cannot be cast up to an AnyObject.
The same thing applies to an array, though by a more roundabout route. An Array is not a class, but it is bridged to NSArray. But an NSArray can contain only classes, i.e. AnyObject elements. Thus, only an array of AnyObject-castables can be cast to AnyObject. A tuple is not a class... You take it from here.
Another way of looking at it is this: AnyObject is all about interchange with Objective-C. But tuples are a Swift-only feature: Objective-C knows nothing of them. Thus, an array of tuples cannot be cast to an AnyObject, because it cannot be handed to Objective-C.

Related

How can I specify the key and value datatype of an NSDictionary argument in swift?

I would like to define a function in swift that can be used in objective-c as well. I have a enum:
#objc public enum BookType: Int {
case novel
case magazine
func name() -> String {
switch self {
case .novel:
return "novel"
case .magazine:
return "magazine"
}
}
}
And the function is:
#objc public func uploadBookImages(images: [BookType: UIImage]) {
// code here
}
This function will bring me an compile error said that:
Method cannot be marked #objc because the type of the parameter cannot
be represented in Objective-C
It might because objective-c doesn't accept swift dictionary since it is a structure. I am thinking to use NSDictionary instead of a swift dictionary. However, how can I specify the argument data type to [BookType: UIImage] using a NSDictionary? I want to pass a NSDictionary<BookType: UIImage> to the function when I call it.
You need to use NSDictionary instead of swift Dictionary
func uploadBookImages(images: NSDictionary) {
// code here
print(images)
}
Usage:
let dictParams:Dictionary<BookType, Int> = [
BookType.magazine : 1,
BookType.novel : 2,
];
uploadBookImages(images: dictParams as NSDictionary)

How to return a dictionary by reference in Swift?

In the following example, you can see that the dictionary in the myStruct instance is not returned by reference in the getDictionary() function. Therefore, any changes made to the returned dictionary is only made to the copy. How can you return the dictionary by reference?
struct myStruct {
func getDictionary() -> [Int:String] {
return dictionary
}
private var dictionary = [1:"one"]
}
let c = myStruct()
var sameDict = c.getDictionary()
sameDict[2] = "two"
print(c.getDictionary(), sameDict)
[1: "one"] [1: "one", 2: "two"]
Dictionary is a value type, it is not your choice to do some type of data structure to be reference or value, it is a Swift's choice. Only closure, class and functions can be used as reference
In Swift, Array, String, and Dictionary
https://developer.apple.com/swift/blog/?id=10
Because the Dictionary is a struct type, the only way to do this is by passing it in a function using inout keyword (indicates that the parameter will be changed) like this:
struct MyStruct {
func getDictionary() -> [Int: String] {
return dictionary
}
mutating func updateDictionary(block: (inout [Int: String]) -> Void) {
block(&dictionary)
}
private var dictionary = [1:"one"]
}
var c = MyStruct()
c.updateDictionary {
$0[2] = "two"
}
print(c.getDictionary())
Update: After modification of the copy inside the function, before the return, the modified copy WILL assign to the global variable. #AlexanderNikolaychuk and #matt pointed out that in the comments. The behavior can be seen if you run the following code in a Playground:
struct MyStruct {
var some = 1
}
var myStruct = MyStruct() {
didSet {
print("didSet")
}
}
func pass(something: inout MyStruct) {
something.some = 2
print("After change")
}
pass(something: &myStruct)
this will print:
After change
didSet
Just saying.
It seems that you did not quite understand the difference between struct and class.
When you initialise the struct and assign it to c you have your first copy of it. Then you initialise a new variable, calling it sameDict and copying the value of the c dictionary to it. Then you modify the copy called sameDict. The dictionary of c is still the same.
Check this doc:
https://docs.swift.org/swift-book/LanguageGuide/ClassesAndStructures.html
Stuct's are passed around by copying them. classes get referenced.

Is it possible to write mutating function in swift class?

I am able to write mutating functions in structure but not in class.
struct Stack {
public private(set) var items = [Int]() // Empty items array
mutating func push(_ item: Int) {
items.append(item)
}
mutating func pop() -> Int? {
if !items.isEmpty {
return items.removeLast()
}
return nil
}
}
In swift, classes are reference type whereas structures and enumerations are value types. The properties of value types cannot be modified within its instance methods by default. In order to modify the properties of a value type, you have to use the mutating keyword in the instance method. With this keyword, your method can then have the ability to mutate the values of the properties and write it back to the original structure when the method implementation ends.
If you change the struct to a class, just delete the keyword mutating wherever it appears.
That's because classes are reference types, and structures are value types.
struct TestValue {
var a : Int = 42
mutating func change() { a = 1975 }
}
let val = TestValue()
val.a = 1710 // Forbidden because `val` is a `let` of a value type, so you can't mutate it
val.change() // Also forbidden for the same reason
class TestRef {
var a : Int = 42
func change() { a = 1975 }
}
let ref = TestRef()
ref.a = 1710 // Allowed because `ref` is a reference type, even if it's a `let`
ref.change() // Also allowed for the same reason
So on classes, you don't need to specify if a function is mutating or not, because, even when defined with let variables, you can modify instance...
That's why the mutating key word has no meaning on classes.

Swift Conversion using a variable

Is there anyway to use conversion using a variable? I am using object stacking using type of "AnyObject" and I've been able to take the class type and populate a variable. Now I need to populate an array using conversion.
var myString = "Hello World"
var objectStack = [AnyObject]()
objectStack.append(myString)
let currentObject = String(describing: objectStack.last!)
var objectType = String()
let range: Range<String.Index> = currentObject.range(of: ":")!
objectType = currentObject.substring(to: range.lowerBound)
let range2: Range<String.Index> = objectType.range(of: ".")!
objectType = objectType.substring(from: range2.upperBound)
The code above will evaluate the class and set the value of "objectType" to "String". Now I'm trying to go the other way. Something like this:
for obj in objectStack{
obj = newObject as! objectType //this doesn't work
}
Is something like this possible?
There is a simpler, safer way to get the type:
let type = type(of: objectStack.last!) // String.Type
let typeString = String(describing: type) // "String"
The other way around is not possible because the type of the object is not known at compile time. Do you have a number of known types you want to try to cast to? In that case, use optional binding to check if the cast is successful:
let object = objectStack.last!
if let string = object as? String {
// do String stuff
}
else if let i = object as? Int {
// do Int stuff
}
// and so on
If you have a large number of possible types that share some common functionality: Use Protocols. See Swift Documentation for a nice introduction.
You define a protocol for some common functionality that different types can implement:
protocol Stackable {
func doStuff()
// (more methods or properties if necessary)
}
The protocol provides a contract that all types conforming to this protocol have to fulfill by providing implementations for all declared methods and properties. Let's create a struct that conforms to Stackable:
struct Foo: Stackable {
func doStuff() {
print("Foo is doing stuff.")
}
}
You can also extend existing types to make them conform to a protocol. Let's make String Stackable:
extension String: Stackable {
func doStuff() {
print("'\(self)' is pretending to do stuff.")
}
}
Let's try it out:
let stack: [Stackable] = [Foo(), "Cat"]
for item in stack {
item.doStuff()
}
/*
prints the following:
Foo is doing stuff.
'Cat' is pretending to do stuff.
*/
This worked for me:
var instance: AnyObject! = nil
let classInst = NSClassFromString(objectType) as! NSObject.Type
instance = classInst.init()

Swift AnyObject as? Set<T> crash

I came across the following behaviour while programming in Swift:
If you run this snippet:
import Foundation
class A : Hashable, Equatable {
var hashValue: Int {
get { return ObjectIdentifier(self).hashValue }
}
}
func ==(a:A, b:A) -> Bool {
return a === b
}
typealias T = A
typealias TCollection = Array<T>
var element:TCollection = [T()]
var array:[AnyObject] = [element]
println(array[0] as? TCollection)
it prints:
Optional([SetCrashTest.A])
However if you change TCollection from Array<T> to Set<T>, it crashes with the following error:
Could not cast value of type 'SetCrashTest.A' (0x1001d1530) to 'NSObject' (0x7fff72a9d0f0).
Also, it runs correctly if I change T from A to some other type, like Int, String, CGFloat etc.
What causes this behaviour? And how can one do such optional downcasting without crashing?
Edit:
I experienced this crash when trying to read a Set<T> from an NSNotification's userInfo, which is of type [NSObject:AnyObject]. It was only after this, that I've constructed the above example; so please don't advise to use [TCollection] instead of [AnyObject], as AnyObject is dictated by the NSNotification API.