wondering why Swift switch statement does not allow to instantiate classes like in other languages and how to solve this. Would be glad if anyone can help out on this.
In the example i have created a simple Vehicle class and trying to instantiate its sub classes via switch depending on classSetter value. However the final line of print statement cannot print name property of any of the classes if it is instantiated within switch (or seems to any other kind of conditional) statement.
import UIKit
class Vehicle {
var name: String {return ""}
}
class Car: Vehicle {
override var name: String {return "Car"}
}
class Bike: Vehicle {
override var name: String {return "Bike"}
}
var classSetter:Int = 1
switch classSetter {
case 1:
println("Initializing Car")
var vehicle = Car()
case 2:
println("Initialized Bike")
let vehicle = Bike()
default:
println("just defaulted")
}
println("Name property from initialization is \(vehicle.name)")
Your two vehicles are being declared within the switch’s { }, so they only exist in that block (that is their “scope”). They don’t exist outside it, so you can’t refer to them there, hence the error.
The default solution to this (that other answers are giving) is to declare the vehicle as a var outside the switch, but here’s an alternative: wrap the switch in a closure expression and return a value from it. Why do this? Because then you can use let and not var to declare vehicle:
let vehicle: Vehicle = { // start of a closure expression that returns a Vehicle
switch classSetter {
case 1:
println("Initializing Car")
return Car()
case 2:
println("Initialized Bike")
return Bike()
default:
println("just defaulted")
return Vehicle()
}
}() // note the trailing () because you need to _call_ the expression
println("Name property from initialization is \(vehicle.name)")
It would be nice if if and switch were expressions (i.e. evaluated to a result) so you didn’t need this closure, but for now it’s a reasonable workaround.
Note, several of the answers here that use the var approach suggest making vehicle an Optional value (i.e. Vehicle?). This is not necessary – so long as the code is guaranteed to assign vehicle a value before it is used (and the compiler will check this for you), it doesn’t have to be optional. But I still think the closure expression version is a better way.
By the way, you might want to consider using a protocol for Vehicle instead of a base class, since that way you don’t have to give Vehicle a default but invalid implementation for name:
protocol Vehicle {
var name: String { get }
}
// one of the benefits of this is you could
// make Car and Bike structs if desired
struct Car: Vehicle {
var name: String {return "Car"}
}
struct Bike: Vehicle {
var name: String {return "Bike"}
}
Though this would mean you couldn’t have a default return from the switch statement of a Vehicle(). But chances are that would be bad anyway – an optional Vehicle? with nil representing failure might be a better option:
let vehicle: Vehicle? = {
switch classSetter {
case 1:
println("Initializing Car")
return Car()
case 2:
println("Initialized Bike")
return Bike()
default:
println("no valid value")
return nil
}
}()
// btw since vehicle is a Vehicle? you need to unwrap the optional somehow,
// one way is with optional chaining (will print (nil) here)
println("Name property from initialization is \(vehicle?.name)")
If you didn’t want this to be a possibility at all, you could consider making the indicator for different kinds of vehicles an enum so it could only be one of a valid set of vehicles:
enum VehicleKind {
case Bike, Car
}
let classSetter: VehicleKind = .Car
let vehicle: Vehicle = {
switch classSetter {
case .Car:
println("Initializing Car")
return Car()
case .Bike:
println("Initialized Bike")
return Bike()
// no need for a default clause now, since
// those are the only two possibilities
}
}()
Your assumption is incorrect.
The scope of the variable vehicle is inside the switch. You are then trying to access it outside the switch.
You need to create the variable outside the switch. You can still instantiate it inside.
var vehicle: Vehicle?
Switch... {
Case
vehicle = Car()
}
println(vehicle)
Writing on my phone so couldn't give full proper code but this will give you the idea.
What you're doing doesn't work in other languages either. You need to declare the vehicle variable before you enter the switch so that it's in the same scope as your println. After that, you an assign it whatever you need to inside the switch.
Related
I have a class Population which upon initialisation gets defined a language. For the sake of this example, every member of that population only speaks this particular language. I then have a Member class defined within the Population for the population members. In this class, I would like to store the language as a static type property to be able to reference if from Member methods like in the example code below.
This seems more efficient to me than (a) always have to pass the language as a parameter when I call a method on a members element or (b) to store the language on each Member instance. However, below code does not quite seem to work and I wonder how to best implement this. Any ideas / best-practices? Apologies if this may sound rather trivial or in case I am completely on the wrong path here.
class Population {
var language: String
var members: [Member]
init(language: String) {
self.language = language
for _ in 1...10 {members.append(Member())}
}
class Member {
static let language: String
func speak() {
switch self.language {
case "english": print("speak some english")
}
}
func write() {
switch self.language {
case "english": print("write some english")
}
}
}
}
I think there is some confusion about what static means, and it probably stems from class Member being nested in class Population. All nesting types do is enclose the nested type in a namespace, which helps a) avoid name collision on types in global scope, and b) enables you to hide it as an implementation detail of the type in which it is nested, if you choose to make it a private type.
A nested type does not belong to an instance of the enclosing type. So if you define language as a static property of Member, then it has one value for class Member, regardless of how many instances of Population you have with different languages.
What you want is for each instance of Member to have the language of the Population to which it belongs. You can accomplish that a couple of different ways, but they all involve storing something in Member.
Option 1. Make language an instance property of Member, and set it in Member.init(language: String).
Option 2. Store a reference to the Population to which the Member belongs (I'll assume it's let population: Population), and make language a computed property which returns population.language.
Option 2 is more flexible, because you can leverage having the population to fetch other Member values that it gets from its Population, but it requires some careful consideration. Does it make sense in your app for a Member to exist outside of a Population? If yes, then you probably want to define it like weak var population: Population?. If no, a Member cannot exist outside of a Population, then unowned let population: Population could be preferred (though the weak version could be used too). The point here is to avoid reference cycles that would cause a memory leak.
So maybe something like this:
class Population {
var language: String
var members: [Member] = []
init(language: String) {
self.language = language
for _ in 1...10 {members.append(Member(population: self))}
}
class Member {
unowned let population: Population
var language: String { population.language }
init(population: Population) { self.population = population }
func speak() {
switch self.language {
case "english": print("speak some English")
case "german" : print("spreche ein Bißchen deutsch")
// other cases go here
default: print("mmmm mmmm mmm mmm") // I'm a mute
}
}
func write() {
switch self.language {
case "english": print("write some English")
case "german" : print("schreibe ein Bißchen deutsch")
// other cases go here
default: print("h8hta;lushgaih9732 8##Y"); // Can't write
}
}
}
}
Though off-topic, I would recommend making language an enum, Then you can remove the default cases from the switch statements, and the compiler will helpfully, though not tactfully, show you all of the switch statements that need to be updated when you add a new language. If you like you can even make the enum based on String so you can still access the String value wherever that might be useful:
enum Language: String
{
case arabic = "arabic"
case chinese = "chinese"
case english = "english"
case french = "french"
case german = "german"
case japanese = "japanese"
case portuguese = "portuguese"
case spanish = "spanish"
// etc...
}
Then if you want the string value you can refer to language.rawValue
I have an enum with different associated value custom classes. Then trying to create a calculated value to determine if one particular type and return the value if so. The below works using a switch in the calculated value but seems a bit clumsy. Are there any cleaner approaches? Thanks.
private enum Situation: Equatable {
case inLocation(LocationMO)
case heldByBeing(BeingMO)
case inContainer(PhysicalObjectMO)
}
private var isInSituation: Situation
var isInLocation: LocationMO? {
switch isInSituation {
case .inLocation(let validLocation): return validLocation
default: return nil
}
}
I have an enum that could generate Some object, as shown below. However, when I assign the badgeValue for the Some object, it doesn't store it.
class Some {
public var badgeValue : String = "default"
}
enum MyEnum: Int {
case x
var some: Some {
let some: Some
switch self {
case .x:
some = Some()
}
return some
}
}
let x = MyEnum(rawValue: 0)
x?.some.badgeValue = "New"
print("\(x?.some.badgeValue ?? "Nothing")") // This will print `default`
I'm expecting it to print New. Why is it print default instead? If I instantiate Some directly, then the badgeValue can be updated. What's so special about the enum store var that can't be
You have made your MyEnum var some a computed property. A computed property gets evaluated every time you read its value. Thus, every time you invoke x?.some, you get a new Some object.
As Rob pointed out, my initial suggestion for a way to fix this won't work. Enums can't have stored properites. I think you'll have to use an associated value on your enum case to do what you want.
Look on you property. It's computed. Every time you create a new instance of Some class.
var some: Some {
let some: Some
switch self {
case .x:
some = Some()
}
return some
}
I am trying to understand swift enum and for that, I decided to create a data source using an associated enum. Now I am not sure if my question line is correct or not but I'll try to explain what exactly I am trying to do here.
Struct SampleClass {
enum Country: String {
case US(cityList: ChianCityList)
case Chian(cityList: USCityList)
}
enum ChianCityList {
case Bijing
case Shanghai
static var allCases = [.Bijing, .Shanghai]
}
enum USCityList {
case NewYork
case LA
static var allCases = [.NewYork, .LA]
var isCaptial:Bool
}
var country: Country
var allCityList: [?] {
switch self.conuntry {
case Chian
return CityList.allCases
case US
return USCityList.allCases
}
init(country: Country)
{
self.country = Country
}
}
Now I don't know what would be the return type of var 'allCityList'. I want it to be generic. secondly, I don't want to call 'allCases' for each enum. Is there anyway to make it more generic? This is just a simple example there is a lot of scenarios like this. Like 'isCaptial'. How could i make it more generic so that based on the country I can found out?
Currently, USCityList and ChinaCityList are two separate types. If you return one type you can‘t return the other. You have two options.
enum ChianCityList {
case Bijing
case Shanghai
static var allCases = [.Bijing, .Shanghai]
}
enum USCityList {
case NewYork
case LA
static var allCases = [.NewYork, .LA]
var isCaptial:Bool
}
Option 1: Convert the one you currently return to the same type and make said type as the return type
Example:
enum ChianCityList: String, CaseIterable {
case Bijing
case Shanghai
}
enum USCityList: String, CaseIterable {
case NewYork
case LA
var isCaptial:Bool
}
extension CaseIterable {
var allCasesToStrings: [String] {
return allCases.map{ String(describing: $0) }
}
var allCityList: [String] {
switch self.conuntry {
case Chian
return CityList.allCasesToStrings
case US
return USCityList.allCasesToStrings
}
Option 2: Make both types conform to the same protocol, then return an instance of said protocol.
Example:
protocol CityList {
var cityList: [String] { get }
Var capitalCity: String { get }
}
// Have both implement the protocol
var allCityList: CityList {
switch self.conuntry {
case Chian
return CityList.cityList
case US
return USCityList.cityList
}
For your example, I wouldn't use enums. This is because you want to store additional information about the cities (like if it's a capital city). Instead, I would use a struct called City that encapsulates all your information. Then you can have a variable chinaCities and usCities that is an array of all its respective cities.
If you truly wanted to force the use of enums, I would create one giant enum called City and store an array of Citys into two separate variables to differentiate their locations.
enum City: String {
case beijing = "beijing"
case newYork = "new york"
}
Of course, if you do this, you'll also have to do extra work to keep track of whether a city is a variable. One way you can achieve this is by having a function that takes in a City enum and returns true or false depending on whether it is a capital.
As a side note, I just wanted to point out that you spelled China and Beijing wrong. Hope this helps!
So I created a class and typecasted one of the variables to be from an enum i created. The enum has a variable named description that I call and put on the page so that as the variable changes status its description on the page will update.
That part is fine and dandy. But I would like to put a variable in the description, but have it reference the variable from its object from that class.
Here is an example:
class room: NSObject{
var roomNumber :Int
var status: requestStatus = .none
var assignedAssociate: String?
init(roomNumber:Int){
self.roomNumber = roomNumber
}
}
enum requestStatus {
case waitingForHelp = "Waiting For Help"
case beingHelped = "Being helped"
case requestClosed
case none
var description: String {
switch self {
case .waitingForHelp: return "Needs Help\n "
case .beingHelped: if return "Being Helped\n by \(super.assignedAssociate)"
case .requestClosed: return "Completed"
case .none: return ""
}
}
}
So as you can see, I would like to reference assignedAssociate from the object that was instantiated from the room class inside the description for the enum beingHelped.
I just wrote in super.assignedAssociate so that you could see kinda what I wanted to do.
Oh and before everyone jumps on me, I have spend a while trying to find the information on this but swift is still so new its hard to find good sources for this stuff.
Thanks!
Things do not automatically know what contains them (this is true in every language I know). You will need to store the information in the enum using associated data. For example, I would probably implement it this way:
struct Room {
let roomNumber: Int
var status: RequestStatus?
init(roomNumber: Int) {
self.roomNumber = roomNumber
}
}
enum RequestStatus {
case WaitingForHelp
case BeingHelped(associate: String)
case Closed
var description: String {
switch self {
case .WaitingForHelp: return "Needs Help"
case .BeingHelped(let associate): return "Being Helped by \(associate)"
case .Closed: return "Completed"
}
}
}
Note how I removed assignedAssociate. This was intentional. Instead, that information is attached to the status when the status is BeingHelped.
Also note that I removed the .none case. If the status may be "nothing" that is generally better indicated with an Optional. If you really do want an "unassigned status", then you should call it Unassigned or something similar. None looks too much like Optional.None and this can lead to surprising compiler behavior. (Also note that all types and enum values should have a leading cap.) Personally, I'd probably just get rid of the Optional, too. Can there really be "no status?"