I'm working on an iOS app with Realm as the database and I'm confronted to a problem I can't fix without your help.
I have a function that queries objects from my Realm instance like so:
static func trainings(containing exercise: Exercise) -> Results<Training>? {
return Realm.workoutDatabase?.objects(TrainingEntry.self)
.filter({ $0.exercise == exercise })
.compactMap({ $0.training })
}
This code produces the following error:
Cannot convert return expression of type 'LazyMapCollection<LazyFilterCollection<LazyMapCollection<LazyFilterCollection<Results<TrainingEntry>>, Training?>>, Training>?' to return type 'Results?'`
There error is obviously that the return type should not be Results<Training>? but LazyMapCollection<LazyFilterCollection<LazyMapCollection<LazyFilterCollection<Results<TrainingEntry>>, Training?>>, Training>?.
Outch! That type is soooooo long.
I tried to set [Training]? as the return type for the function and it worked.
But by doing so, I'm afraid that this will implicitly cast the returned result of the expression into an array, thus loosing the laziness of the collection?
As far I as know, I need a type-eraser to get a much shorter type, but I'm definitely not an expert on that particular subject.
I know that the Swift Standard Library provides a few type-erasure structs, such as AnyGenerator, AnySequence but I'm afraid I don't know enough about type-erasure to use them in my case.
So my question is, how to use type-erasure to get a cleaner return type for my function?
Edit:
I tried to cast the expression to AnyRandomAccessCollection but I get the following error:
Type 'LazyFilterCollection<LazyMapCollection<LazyFilterCollection<Results<TrainingEntry>>, Training?>>' does not conform to protocol 'RandomAccessCollection'
I also tried to cast to AnyCollection and AnyBidirectionalCollection, both of them work, but then I'm loosing the ability to subscript with an Int, which is something I want to keep.
Related
I came across a heterogeneous dictionary definition like this on a tutorial online:
var mixedMap4 = [AnyHashable(0): "Zero" as Any,
AnyHashable(1): 1.0 as Any,
AnyHashable("pi"): 3.14 as Any]
I was wondering why the author chose to write
AnyHashable(0) instead of 0 as AnyHashable. When I tried this on Swift playground, it also worked. However when I turned "Zero" as Any into Any(0) it gives the following
error: error: The Dictionary.xcplaygroundpage:41:34: error: protocol
type 'Any' cannot be instantiated var mixedMap4 = [AnyHashable(0):
Any("Zero") ,
Thank you for the answer
The clue is in the error message.
AnyHashable is a struct that type-erases the underlying hashable type, and so can be directly instantiated as an object
Any is a protocol and therefore can't be directly instantiated, although all other types can be complied with it, thus a String such as "Zero" can be cast as Any but Any(String) is meaningless.
To me it all just feels like a bucket load of trouble waiting to happen!
I have an abstract class defined this way:
class BaseCoordinator<ResultType>
Other classes inherit from this one eg.
final class AppCoordinator: BaseCoordinator<Void>
final class AuthFlowCoordinator: BaseCoordinator<AuthFlowCoordinationResult>
final class MainFlowCoordinator: BaseCoordinator<Void>
Now I want to create a factory method. I guess it's sugnature should look like:
func makeCoordinator<T>() -> BaseCoordinator<T>
But, of course, I get error like this one:
Cannot convert return expression of type 'AppCoordinator' to return type 'BaseCoordinator<T>'
What Xcode suggests me is to add as! BaseCoordinator<T>, but I hate this force downcasting (and returning ? isn't satisfying me either, as I'm 100% sure I'll have a correct default object), as I want assurance that at least a default Coordinator will be returned.
It feels like I'm missing something, but I really have no clue what is it. Is it actually possible to make such factory method, or is Swift generics limited?
T must have a predefined type at compile time. Therefore you cannot choose T using the implementation of makeCoordinator() by returning different coordinators with different ResultTypes.
In this case, the caller can choose which value he wants to assign to T, potentially breaking the function. For example the following two calls would be perfectly valid:
let coordinator: BaseCoordinator<Void> = makeCoordinator()
let coordinator: BaseCoordinator<[Int: [String]]> = makeCoordinator()
It would not make sense to use [Int: [String]] as a generic type but it is still possible. Depending on the generic type you choose at the function call, the cast may work or not, which is why a force cast will likely lead to a crash.
An optional cast, as suggested by Tom E would fix the potential crash but it would still not resolve this problem.
Therefore you cannot use a factory pattern for this without erasing ResultType to Any using a wrapper type, which would defeat the purpose of generics.
If you want type safety, you have to create a factory method for each subclass of BaseCoordinator you want to instantiate or just call their initializers manually.
You might want to try as? instead of as!. The former yields an optional, i. e. the result will be nil if the cast doesn’t succeed. This way you have a safe cast without the need of forcing anything.
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.
For fun, I am attempting to extend the Dictionary class to replicate Python's Counter class. I am trying to implement init, taking a CollectionType as the sole argument. However, Swift does not allow this because of CollectionType's associated types. So, I am trying to write code like this:
import Foundation
// Must constrain extension with a protocol, not a class or struct
protocol SingletonIntProtocol { }
extension Int: SingletonIntProtocol { }
extension Dictionary where Value: SingletonIntProtocol { // i.e. Value == Int
init(from sequence: SequenceType where sequence.Generator.Element == Key) {
// Initialize
}
}
However, Swift does not allow this syntax in the parameter list. Is there a way to write init so that it can take any type conforming to CollectionType whose values are of type Key (the name of the type used in the generic Dictionary<Key: Hashable, Value>)? Preferably I would not be forced to write init(from sequence: [Key]), so that I could take any CollectionType (such as a CharacterView, say).
You just have a syntax problem. Your basic idea seems fine. The correct syntax is:
init<Seq: SequenceType where Seq.Generator.Element == Key>(from sequence: Seq) {
The rest of this answer just explains why the syntax is this way. You don't really need to read the rest if the first part satisfies you.
The subtle difference is that you were trying to treat SequenceType where sequence.Generator.Element == Key as a type. It's not a type; it's a type constraint. What the correct syntax means is:
There is a type Seq such that Seq.Generator.Element == Key, and sequence must be of that type.
While that may seem to be the same thing, the difference is that Seq is one specific type at any given time. It isn't "any type that follows this rule." It's actually one specific type. Every time you call init with some type (say [Key]), Swift will create an entirely new init method in which Seq is replaced with [Key]. (In reality, Swift can sometimes optimize that extra method away, but in principle it exists.) That's the key point in understanding generic syntax.
Or you can just memorize where the angle-brackets go, let the compiler remind you when you mess it up, and call it day. Most people do fine without learning the type theory that underpins it.