I've a payload getting shuttled from one part of the system to the other.
The shuttle is carrying the payload as Any, so I could carry any kind of objects including non objects like tuples, etc.
one of the parts of the system is accepting AnyObject so is the error.
I'm confused like what type to use to carry stuff around so it's compatible between all parts of the system.
Shall I make a choice and stick to one of the types, either Any or AnyObject for the system as a whole or what's the best choice for shuttling items if you are not concerned with their actual types.
we had type Object in other languages that could carry anything around, but not sure how this works in SWIFT world
or is there a casting that could work between the two? If I'm 100% convinced that the coming object is AnyObject, I could load it off from the shuttle (Any) as an AnyObject
Note to negative voters: Please help to clear up the question if it doesn't make any sense to you or help to improve this question, since I'm new to SWIFT. I need an answer not your vote.
Edit
a case where I had to do comparison between Any and AnyObject while unit testing, how would you handle such situation.
class Test {
var name: String = "test"
}
var anyObject: AnyObject = Test()
var any: Any = anyObject
//XCTAssert(any == anyObject, "Expecting them to be equal")
any == anyObject
Any will hold any kind of type, including structs and enums as well as classes. AnyObject will only hold classes. So what Any can store is a superset of what AnyObject can. No amount of casting will cram your custom structs or enums into an AnyObject.
Sometimes it seems like AnyObject is holding a struct such as a String, but it isn’t, what has happened is somewhere along the way Swift has converted your String to an NSString (which is a class so can be stored in an AnyObject).
(technically, Any is defined as something that implements 0 or more protocols, which anything does, whereas AnyObject is defined as a special protocol all classes implicitly conform to, and that is marked as an #objc protocol, so only classes can conform to it)
edit: to answer your question about comparisons – there’s no == operator for Any or AnyObject (how would it work if you equated an Any containing a String to an Any containing an Int?). You have to cast both sides back into what they really are before you can compare them using an appropriately-defined operator for that type.
Related
In Apple documentation of NSXPCInterface.setClasses(_:for:argumentIndex:ofReply:), for Swift, the first parameter is described as:
An NSSet containing Class objects —for example, [MyObject class].
Hmmm, it looks as though someone neglected to update this from Objective-C to Swift. For Swift I presume it should be something like
A Set containing Class Types
(Someone please correct my wording.) But how do you make a Set containing Class Types? The compiler will not allow me to declare a Set whose member types are not do not conform to Hashable. This makes sense because hashes are needed to maintain uniqueness among members. But Class Types in Swift do not seem to be hashable. Try this in any Swift file, or a playground…
class Foo {}
let mySet: Set<AnyHashable>
mySet.insert(Foo)
The compiler complains:
Argument type 'Foo.Type' does not conform to expected type 'Hashable'
Or, more broadly, does anyone know how to use NSXPCInterface.setClasses(_:for:argumentIndex:ofReply:) in Swift 5?
The classes you pass need to be #objc classes (and subclasses of NSObject). You'll put them into an NSSet, and then cast that to Set:
let classSet = NSSet(object: Foo.self) as! Set<AnyHashable>
This is almost certainly a bug in NSXPCInterface (it needs some kind of "refine for Swift" or possibly a shim), and I suggest opening a defect at bugreport.apple.com.
As of Swift v5.6 (perhaps earlier), you can create the set without casting:
let classSet = NSSet().adding(Foo.self)
This is because adding() is declared like this:
open func adding(_ anObject: Any) -> Set<AnyHashable>
I've experimented with casting many types to AnyObject. Reference types obviously cast unaltered, but others are automagically converted under the hood to NSNumber, NSValue, and so on. (These are not the exact types, but close enough.) I was even able to cast a tuple successfully.
This surprised me. Are there any types that cannot be cast to AnyObject?
There are other questions and answers that may cover this material, but none of them asks this question specifically and the answer to this question can only be found in the fine print of the other answers.
AnyObject is Objective-C id, meaning any class type. Casting to AnyObject means "wrap this up in a way that Objective-C can deal with as a class type."
Obviously, an NSObject subclass just crosses the bridge directly.
For other types (i.e. nonclasses), Swift 4 follows three policies:
Some types are bridged directly to classes, e.g. String to NSString.
Some types are wrapped in Objective-C class types, e.g. Int in NSNumber or CGRect in NSValue.
All other types are boxed in an opaque wrapper; they can cross the bridge to Objective-C, and can be round-tripped successfully back to Swift, but nothing useful can be done with them in the Objective-C world.
My app loads a JSON file at launch. The JSON file includes instructions to create various instances of different types. As a basic example, I have a protocol called BasicType with some extensions to make some built-in Swift types conform to it:
protocol BasicType {
}
extension Int: BasicType { }
extension Float: BasicType { }
extension String: BasicType { }
I also have a dictionary which maps these type 'names' (as found in the JSON) to the types themselves:
let basicTypes = [
"integer": Int.self,
"float": Float.self,
"string": String.self
] as [String : BasicType.Type]
When I'm loading BasicTypes from the JSON, I look up the name specified by the JSON using the dictionary above to know which type to instantiate (in reality, the protocol also defines initialisers, so this is possible). I have some other protocols in addition to BasicType which work in exactly the same way, and each one gets its own dictionary map.
For example, my JSON might include an array of BasicTypes I want to instantiate:
{
"basic_types": [{
"type": "integer",
...
}, {
"type": "integer",
...
}, {
"type": "string",
...
}, {
"type": "float",
...
}]
}
The ellipses denote other attributes which are passed to the initialiser, like what value the integer should be. In actuality, these are custom structs and classes which take various properties. In my Swift code, I open up the basic_types array, look at the type key for each JSON object, look up the appropriate types and initialise them. So my loaded array would include two Ints, a String and a Float.
This is similar to how UIKit instantiates views from a storyboard, for example. The view's class name is stored in the storyboard, and in this case presumably it uses something like NSClassFromString to perform the mapping. I don't want to rely on the Objective-C runtime though.
The problem with this approach is that it's difficult to look up the name of a type if I already have the type or instance, as I have to inefficiently iterate the dictionary to search.
Instead, I thought a better solution might be to include each type's name (or 'type identifier') statically as a variable in the type itself, and then generate the type maps from that. So, I created a new protocol:
protocol TypeIdentifiable {
static var typeIdentifier: String { get }
}
And made BasicType (and all the other similar protocols) inherit from it:
protocol BasicType: TypeIdentifiable {
}
Which means I need to provide the names directly in the types themselves:
extension Int: BasicType {
static let typeIdentifier = "integer"
}
extension Float: BasicType {
static let typeIdentifier = "float"
}
extension String: BasicType {
static let typeIdentifier = "string"
}
I made a function (which compiles) which I intended to generically take a protocol conforming to TypeIdentifiable and including an array of types, and produce a dictionary containing the mapping:
func typeMap<T : TypeIdentifiable>(_ types: [T.Type]) -> [String : T.Type] {
return Dictionary(uniqueKeysWithValues: types.map { type in
(key: type.typeIdentifier, value: type)
})
}
I can then replace the dictionary literal with:
let basicTypes = typeMap([
Int.self,
Float.self,
String.self
] as [BasicType.Type])
This way, if I have a type I can easily get its name by accessing its static property, and if I have the name I can get the type through the generated dictionary.
Unfortunately, this doesn't work:
error: cannot convert value of type '[BasicType.Type]' to expected argument type '[_.Type]'
I think using a protocol as a generic type is forbidden in Swift, which probably makes it impossible to do what I'm trying to do.
My question is: Is there a way of doing what I'm trying to do? Otherwise, on a more general level, is there a better way of knowing what types to instantiate from something like JSON?
This is SR-3038 and is roughly "as designed." Protocols do not conform to themselves, so they cannot fulfill generic requirements. BasicType cannot be T because BasicType is a protocol. Why? Well, imagine I've written this code (which I expect is really similar to what you want to write):
protocol P {
init()
}
func makeOne<T:P>(what: T.Type) -> T {
return T.init()
}
Awesome. That's legal. Now, if protocols conformed to themselves, I could write this code:
makeOne(what: P.self)
What init should that call? Swift fixes this impasse by only allowing concrete types to conform to protocols. Since BasicType does not conform to BasicType, it also does not conform to TypeIdentifiable, and cannot be T.
So what do we do?
First and most important-top-of-the-list: Do you actually need this to be generic? How many times do you actually plan to use it for radically different JSON formats? Could you just write the parsing code directly (ideally with the new Codeable)? The tears that have been spilled trying to write complex JSON parsers to parse simple JSON formats could flood WWDC. My rule of thumb is to avoid making things generic unless I already know at least 3 different ways it will be used. (If that happens later, I refactor to generic when I have the examples.) You just don't know enough about the problem until have have several concrete examples and your generic solution will probably be wrong anyway.
OK, let's assume this really is a very general JSON format you have to parse where you have no idea what's going to be in it (maybe like the NIB format you mention). The first and most obvious answer: Use your existing solution. Your concern is that it's hard to look them up backwards (type->identifier), but that's trivial to write a method to make that easy. Unless you have many thousands, possibly millions, of types, the difference in time is beyond trivial; linear search can even be faster than dictionaries for small collections.
Next in line is to just write typeMap for each protocol (i.e. explicitly for BasicType rather than making it generic. There are several cases in stdlib where they do this. Sometimes you just have to do that.
And of course you could create a TypeDefinition struct that holds the info (like a type eraser). In practice this really just moves the problem around. Eventually you'll need to duplicate some code if there are a bunch of protocols.
But almost always when I head down this kind of road, I discover it was way over-engineered and just writing the silly code directly is the easy and scalable solution.
And of course look carefully at the new JSONDecoder in Swift 4. You want to be moving in that direction if you can anyway.
I'm confused about dynamic type checking in Swift.
Specifically, I have a weirdo case where I want to, essentially, write (or find) a function:
func isInstanceOf(obj: Any, type: Any.Type) -> Bool
In Objective-C, this is isKindOfClass, but that won't work because Any.Type includes Swift structs, which are not classes (much less NSObject subclasses).
I can't use Swift is here, because that requires a hardcoded type.
I can't use obj.dynamicType == type because that would ignore subclasses.
The Swift book seems to suggest that this information is lost, and not available for structs at all:
Classes have additional capabilities that structures do not:
...
Type casting enables you to check and interpret the type of a class instance at runtime.
(On the Type Casting chapter, it says "Type casting in Swift is implemented with the is and as operators", so it seems to be a broader definition of "type casting" than in other languages.)
However, it can't be true that is/as don't work with structures, since you can put Strings and Ints into an [Any], and pull them out later, and use is String or is Int to figure out what they were. The Type Casting chapter of the Swift Book does exactly this!
Is there something that's as powerful as isKindOfClass but for any Swift instances? This information still must exist at runtime, right?
Actually you can use is operator.
Use the type check operator (is) to check whether an instance is of a certain subclass type. The type check operator returns true if the instance is of that subclass type and false if it is not.
Since struct can't be subclassed, is is guaranteed to be consistent when applied to instance of struct because it will check on it static type, and for classes it will query the dynamic type in runtime.
func `is`<T>(instance: Any, of kind: T.Type) -> Bool{
return instance is T;
}
This work for both, struct and class.
As already stated, is/as should work fine with structs. Other corner cases can usually be done with generic functions, something like:
let thing: Any = "hi" as Any
func item<T: Any>(_ item: Any, isType: T.Type) -> Bool {
return (item as? T) != nil
}
print(item(thing, isType: String.self)) // prints "true"
No idea if this fits your specific use case.
More generally, keep in mind that Swift (currently) requires knowing the type of everything at compile time. If you can't define the specific type that will be passed into a function or set as a variable, neither will the compiler.
I really need to pass any equatable structs/types/objects to this setEquatable function.
Any idea how can I solve this problem?
public func ==(l: [String: String], r: [String: String]) -> Bool {
return true // just a stub
}
func setEquatable<T: Equatable>(v: T) {
//...
}
let isEqual = ["1": "2"] == ["1": "2"]
setEquatable(v: ["1": "2"])
For people who vote for closing the question due to duplication: I would not close the question as duplication because that way you will not prevent the same question being asked again in future. People who have problems with their code and want to understand the gist will find this question very useful. They most probably would not know anything about conditional conformance, but they have their code that doesn't compile.
This is currently a limitation of Swift's type system that is well-known and on the roadmap to fix. The specific feature being discussed to fix this is "conditional conformance" to protocols for generic types. In essence, the Dictionary type can't be universally declared Equatable, because you don't know up front how to compare every possible type of value it may hold (some of which might not themselves be Equatable).
Conditional conformance would allow you to create an extension that says that Dictionary sometimes conforms to Equatable, specifically under the condition when its Value type is Equatable. And in that case, Dictionary equality can be defined as a function that compares the equality of all keys and all values in both Dictionary instances being checked.
Here is a summary of this feature and others under consideration:
https://github.com/apple/swift/blob/master/docs/GenericsManifesto.md#conditional-conformances-
Until this type system upgrade is implemented though, there is unfortunately no way to treat any Dictionary as Equatable directly. You can however create Equatable-conforming wrapper types around Dictionaries, or overload your setEquatable function to also accept Dictionaries with Equatable values and handle accordingly.