I was toying in the playground in xcode 7.3.1 with swift. I am a bit confused about the type casting in swift.
So, here is a bit of code that I tried.
class MediaItem {
var name: String
init(name: String) {
self.name = name
}
}
class Movie: MediaItem {
var director: String
init(name: String, director: String) {
self.director = director
super.init(name: name)
}
}
class Song: MediaItem {
var artist: String
init(name: String, artist: String) {
self.artist = artist
super.init(name: name)
}
}
var movieItem = Movie(name: "GOT", director: "RRMartin")
movieItem.dynamicType //Movie.Type
(movieItem as? MediaItem).dynamicType //Optional<MediaItem>.Type
var someItm = movieItem as! MediaItem //Movie
someItm.dynamicType //Movie.Type
I've shown the output from the playground in the comment. Here you can see the type in each line.
Now according the docs of apple, The conditional form, as?, returns an optional value of the type you are trying to downcast to. As per the docs, I am trying to downcast to MediaItem, and I am getting the MediaItem as optional type.
But when I use force unwrap(that is as!) the returned type is Movie. But I wanted it to be MediaItem.
Also, another thing to notice is that, the type is actually changed. Some data are actually truncated. Because when I tried to access the director property which is present in the Movie, I cannot access it. As I've downcast it.
So, if the type is downcast, why the returned type is Movie? Shouldn't it be MediaType?
So, my question is this, when I type cast some derived class(Movie) to base class(MediaType), shouldn't the converted type be base class(MediaType)?
dynamicType tells you what the underlying type of the object is. It doesn't tell you what the type of var currently referencing that object is.
For instance:
let a: Any = 3
a.dynamicType // Int.Type
Swift, of course, keeps track of these underlying types which is what allows you to later downcast a MediaItem to a Movie (if that is what it really is).
The confusion for you came when you did:
(movieItem as? MediaItem).dynamicType //Optional<MediaItem>.Type
An Optional is it's own type. It is an enumeration with two values: .None and .Some(T). The .Some value has an associated value that has its own dynamic type. In your example, when you asked for the dynamicType, it returned the underlying type of the Optional which is Optional<MediaItem>.Type. It didn't tell you what the dynamic type of the value associated with that Optional is.
Consider this:
let x = (movieItem as? MediaItem)
x.dynamicType // Optional<MediaItem>.Type
x!.dynamicType // Movie.Type
Related
I'm currently experimenting with Generics in Swift and came to some problem with casting some types around such as SomeType<Protocol> to SomeType<ProtocolImpl>. So basically I have some type that takes a generic parameter which is handled as a Protocol and which at a later point is casted to a more concrete type. My question is if that isn't possible to do?
/// 'dict' is of type [String: SomeType<Protocol>]
if let element = dict["str"], // 'element' here is of type SomeType<Protocol>
let castedElement = element as? SomeType<ProtocolImpl> { // This is always false
return castedElement // Here I want to return castedElement with type SomeType<ProtocolImpl>
}
Is there any way to make this cast work? I'm already working on another solution for my problem, but I'm still interested if there's a way to make this work somehow.
Edit: Because #jtbandes wanted a example he can paste somewhere, here:
class SomeType<T> {
let value: T
init(value: T) {
self.value = value
}
}
protocol Protocol {}
class ProtocolImpl: Protocol {}
var dict: [String: SomeType<Protocol>] = ["str": SomeType(value: ProtocolImpl())]
if let element = dict["str"],
let castedElement = element as? SomeType<ProtocolImpl> {
print(castedElement.value) // I want to get here
}
Long story short, generics in Swift are not covariant, which means that SomeType< ProtocolImpl> is not convertible SomeType<Protocol>, even if ProtocolImpl conforms to Protocol. Thus the direct answer to your question is: this is not currently possible in Swift.
However you might ask yourself is why do you need the downcast in the first place. As you're storing the instances in a container, polymorphic behaviour might be better suited. You could declare the functionality you need to access as part of the protocol, and access is though the protocol interface. This way you don't need to know which is the concrete implementation under the hood, which is one of the main reason of using a protocol.
It's hard for me to tell what you're trying to achieve from the question. Still, maybe the below will help you.
class SomeType<T>: Protocol { // Maybe SomeType IS your ProtoImpl?
let value: T
init(value: T) {
self.value = value
}
}
protocol Protocol {}
//class ProtocolImpl: Protocol {}
//var dict: [String: SomeType<Protocol>] = ["str": SomeType(value: ProtocolImpl())]
var dict: [String: Protocol] = ["str1": SomeType<String>(value: "Some Type"),
"str2": SomeType<Int>(value: 1)
]
if let castedElement = dict["str1"] as? SomeType<String> {
print(castedElement.value) // --> "Some Type"
}
if let castedElement = dict["str2"] as? SomeType<Int> {
print(castedElement.value) // --> "1"
}
As you can see the image, I totally mess up the Data Type (The red circle). Is there a way to change the Data Type into Integer?
EDIT
I want to change the data type from a String to an Int and I have existing data so I can't start with a fresh realm and just change the var type.
class Item: Object {
#objc dynamic var name: String?
#objc dynamic var itemid: String?
#objc dynamic var cateid: String?
}
I may have misunderstand the question but you appear to have existing data stored as as a String and you want to 'convert' all of those to an Int.
You cannot directly change the type to another type and have the stored data changed as well. If you do, it will be flagged with an error.
Error!
Migration is required due to the following errors:
- Property 'Item.itemid' has been changed from 'string' to 'int'.
You need to incorporate a migration block to 'convert' the string value to an Int. Assuming we add a new Int property to our object `item_id', something along these lines will migrate your strings to int's and in the case where the string is not a valid it, it will be assigned a value of 0
Realm.Configuration.defaultConfiguration = Realm.Configuration(
schemaVersion: 1,
migrationBlock: { migration, oldSchemaVersion in
if (oldSchemaVersion < 1) {
migration.enumerateObjects(ofType: Item.className()) { oldObject, newObject in
let stringValue = oldObject!["itemid"] as! String
newObject!["item_id"] = Int(stringValue) ?? 0
}
}
})
Also, as soon as Realm is accessed, the object models are written to the Realm file. So a simple matter of
let items = realm.object(Item.self)
Will store that model even if no data was ever written. If, after that line, the var type is changed from a String to an Int, it will throw the migration error.
Deleting the Realm and starting from scratch is one option if that's the case, and as mentioned above, a migration block.
If this is brand new model that has never been used, then as the comments and other answer suggest, just change the String to an Int.
Simply change the String to Int in your Object model. Please note that the Realm documentation says:
String, NSDate, and NSData properties can be declared as optional or non-optional using the standard Swift syntax.
So unlike the String in your previous model, you will not be able to declare your Int as optional. You have two options:
Declare a default value:
class Item: Object {
#objc dynamic var name: String?
#objc dynamic var itemid: Int = 0
#objc dynamic var cateid: Int = 0
}
Declare it as a RealmOptional:
class Item: Object {
#objc dynamic var name: String?
#objc dynamic var itemid = RealmOptional<Int>()
#objc dynamic var cateid = RealmOptional<Int>()
}
For more information on each solution please see this SO answer and the Realm documentation.
I want to know how Literal Convertibles work in Swift. The little I know is that the fact that, in var myInteger = 5, myInteger magically becomes an Int is because Int adopts a protocol, ExpressibleByIntegerLiteral and we don't have to do var myInteger = Int(5). Similarly String, Array, Dictionary etc all conform to some Literal protocols.
My Question is
Am I right in my little understanding of Literal Convertibles?
How can we implement these in our own types. For example
class Employee {
var name: String
var salary: Int
// rest of class functionality ...
}
How can I implement Literal Protocols to do var employee :Employee = "John Doe" which will automatically assign "John Doe" to employee's name property.
You are partially correct in your understanding of the various ExpressibleBy...Literal protocols. When the Swift compiler parses your source code into an Abstract Syntax Tree, it already identified what literal represents what data type: 5 is a literal of type Int, ["name": "John"] is a literal of type Dictionary, etc. Apple makes the base type conform to these protocols for the sake of completeness.
You can adopt these protocols to give your class an opportunity to be initialized from a compile-time constant. But the use case is pretty narrow and I don't see how it applies to your particular situation.
For example, if you want to make your class conform to ExpressibleByStringLiteral, add an initializer to set all your properties from a String:
class Employee: ExpressibleByStringLiteral {
typealias StringLiteralType = String
var name: String
var salary: Int
required init(stringLiteral value: StringLiteralType) {
let components = value.components(separatedBy: "|")
self.name = components[0]
self.salary = Int(components[1])!
}
}
Then you can init your class like this:
let employee1: Employee = "John Smith|50000"
But if you dream about about writing something like this, it's not allowed:
let str = "Jane Doe|60000"
let employee2: Employee = str // error
And if you pass in the wrong data type for salary, it will be a run time error instead of a compile-time error:
let employee3: Employee = "Michael Davis|x" // you won't know this until you run the app
TL, DR: it is a very bad idea to abuse these ExpressibleBy...Literal types.
This can be a scenario to work with Convertibles in custom types.
struct Employee : ExpressibleByStringLiteral {
var name: String = ""
init() {}
init(stringLiteral name: String) {
self.name = name
}
}
func reportName(_ employee: Employee) {
print("Name of employee is \(employee.name)")
}
reportName("John Doe") //Name of employee is John Doe
By Drewag in his answer to the question
Every member constant must have a value by the time initialization is complete. Sometimes, a constant cannot be initialized with its correct value during initialization, but it can still be guaranteed to have a value before being accessed.
Using an Optional variable gets around this issue because an Optional is automatically initialized with nil and the value it will eventually contain will still be immutable. However, it can be a pain to be constantly unwrapping a variable that you know for sure is not nil. Implicitly Unwrapped Optionals achieve the same benefits as an Optional with the added benefit that one does not have to explicitly unwrap it everywhere.
The following code defines two classes, Country and City, each of which stores an instance of the other class as a property. Every country must have a capital city and every city must always belong to a country.
class Country {
let name: String
var capitalCity: City! //why I can't use let!
init(name: String, capitalName: String){
self.name = name
self.capitalCity = City(name: capitalName, country: self)
}
deinit {
print("\(name) has been de-initialized the city \(capitalCity.name) is gone with with the country")
}
}
class City{
let name: String
unowned let country: Country
init(name: String, country: Country){
self.name = name
self.country = country
}
deinit {
print("The city \(name) has been de-initialized ")
}
}
var country = Country(name: "Canada", capitalName: "Ottawa")
}
However, if I changed the line var capitalCity: City! into let capitalCity: City!, the compiler given out the following error warning.
Question: Isn't that we can use Implicitly Unwrapped Optional when a constant that cannot be defined during initialization? What's the error here?
The important part is here:
City(name: capitalName, country: self)
You are definitely using self in the expression which is executed before the assignment to the property capitalCity.
If you want to use self in any of the expressions, it needs to be in the second phase of two phase initialization, which means all properties needs to be initialized before the usage of self.
With using var, Swift assigns a default initial value nil for the property capitalCity. So the property can be considered as "already initialized", so, you can use self after you have initialized another property name.
(You know giving nil to a let-constant of ImplicitlyUnwrappedOptional is ridiculous.)
By the way private(set) var is often used in similar cases:
private(set) var capitalCity: City!
I have a number of structs which implement a Resource protocol. This defines that they must have a variable extendedInfo which conforms to ExtendedInfo protocol to provide a way to initialise them with json via init(json: [String: AnyObject]. I'm trying to provide a way to dynamically instantiate these, with JSON, providing the right type of ExtendedInfo and assign it to the struct's extendedInfo variable. However, I'm getting a Argument labels '(json:)' do not match any available overloads error when trying to instantiate them via their dynamicType
protocol Resource {
associatedtype ExtendedInfoTypeAlias: ExtendedInfo
var extendedInfo: ExtendedInfoTypeAlias? { get set }
}
protocol ExtendedInfo {
init(json: [String: AnyObject])
}
struct User: Resource {
typealias ExtendedInfoTypeAlias = UserExtendedInfo
let name: String = "Name"
var extendedInfo: UserExtendedInfo?
}
struct UserExtendedInfo: ExtendedInfo {
let age: Int?
init(json: [String: AnyObject]) {
age = json["age"] as? Int
}
}
let user = User()
let sampleJSON = ["age": 50]
let userExtendedInfo = user.extendedInfo.dynamicType.init(json: sampleJSON) // Argument labels '(json:)' do not match any available overloads
user.extendedInfo = userExtendedInfo
Any ideas guys? Thanks
First of all, you don't need to explicitly define the type of ExtendedInfoTypeAlias in your struct implementation – you can just let it be inferred by the type you provide for extendedInfo.
struct User: Resource {
let name: String = "Name"
var extendedInfo: UserExtendedInfo?
}
Second of all, you can just use the protocol's associated type of your given struct's dynamicType in order to use your given initialiser. For example:
user.extendedInfo = user.dynamicType.ExtendedInfoTypeAlias.init(json: sampleJSON)
print(user.extendedInfo) // Optional(Dynamic_Protocols.UserExtendedInfo(age: Optional(50)))
As for why your current code doesn't work, I suspect it's due to the fact that you're getting the dynamicType from an optional – which is preventing you from calling your initialiser on it.
I did find that the following works, even when extendedInfo is nil. (This is a bug).
user.extendedInfo = user.extendedInfo!.dynamicType.init(json: sampleJSON)
Change:
let user = User()
To:
var user = User()
and try this:
user.extendedInfo = UserExtendedInfo(json: sampleJSON)