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
Related
I have two classes for language that I want to use at run-time in different conditions.
I want to get both the class's objects in one single variable, as per the condition. However, I am getting an error.
var constantCL = LanguageEnglish()
if languageType == Constants.LoginParameters.LANGUAGE_CODE_ENGLISH {
constantCL = LanguageEnglish()
} else {
constantCL = LanguageHindi()
}
Getting this error on constantCL = LanguageHindi() in my else:
Cannot assign value of type 'LanguageHindi' to type 'LanguageEnglish'
Screenshot of error:
This is an expected behavior since your language is an actual class, instead of something a bit more flexible.
Possible solutions:
Use a superclass for your languages, e.g.
class LanguageEnglish: Language { }
class LanguageHindi: Language { }
var constantCL: Language = LanguageEnglish()
Use a protocol instead, and implement it in your language classes (almost the same as above, difference is in implementation and declaration):
class LanguageEnglish: Language { }
class LanguageHindi: Language { }
var constantCL: Language = LanguageEnglish()
Or maybe use an enum to represent your languages, or a similar more elegant construct if it suits your needs:
enum Language {
case english
case hindi
// Other useful stuff
}
var constantCL: Language = .english
constantCL = .hindi
So I'm working on an sql database abstraction layer in swift and I want to write it as safe as possible. By that I mean I would prefer to make it impossible for anybody to do anything illegal in the database using my library.
One thing I'm not fully able to solve yet is how to make the conversion from Swift types to SQL data types 100% safe. So I'll explain my current implementation and its flaws:
So in SQL, many data types have parameters. For example, when you want to define a column as a VARCHAR, you need to give it a parameter that represents the max length of the VARCHAR, like VARCHAR(255). You could say that VARCHAR is one of SQL's primitive data-types and VARCHAR(255) is a more specified data-type. To represent that in a type-safe way in swift I created two protocols:
public protocol PrimitiveDataType {
associatedtype Options = Void
}
public protocol SpecifiedDataType {
associatedtype Primitive: PrimitiveDataType
static var options: Primitive.Options { get }
static func from(primitive: Primitive) -> Self
var primitive: Primitive { get }
}
So here's an example of a PrimitiveDataType:
extension String: PrimitiveDataType {
public enum DatabaseStringType {
case char(length: Int), varchar(limit: Int), text
}
public typealias Options = DatabaseStringType
}
And here's an example of a SpecifiedDataType:
struct Name {
let value: String
init?(_ value: String) {
guard case 0...50 = value.characters.count else {
return nil
}
self.value = value
}
}
extension Name: SpecifiedDataType {
static let options = String.DatabaseStringType.varchar(limit: 50)
static func from(primitive: String) -> Name {
return Name(primitive)!
}
var primitive: String {
return value
}
}
Now I can use the information elsewhere in the library to know what kind of database-column is expected whenever a Name type is requested. This gives my library the power to make sure the database columns are always of the correct type.
I haven't written all that code yet, but it might look something like this:
func createColumn<SDT: SpecifiedDataType>(ofType type: SDT) -> String {
switch SDT.options {
case let options as String.DatabaseStringType:
switch options {
case .text:
return "TEXT"
case .char(let length):
return "CHAR(\(length))"
case .varchar(let limit):
return "VARCHAR(\(limit))"
}
case let length as UInt32.Options:
// return something like UNSIGNED INTEGER(\(length)) or whatever
// case etcetera
}
}
There is just one small problem with this. The set of valid primitive data-types is limited, but the set of possible implementations of the PrimitiveDataType protocol is unlimited, because I can't stop anybody from creating their own implementation of this protocol.
So for this reason it would be better to use some kind of enum instead of a number of implementations of a protocol. Because nobody can extend an enum that I create, so I can keep the options limited to whatever I define as valid types. However, an enum would create another problem that doesn't exist in my current implementation.
You see in my current implementation, whenever someone implements SpecifiedDataType with their options defined like so:
static let options = String.DatabaseStringType.varchar(limit: 50)
I can trust that the compiler will force them to make their type compatible with string by implementing the from(primitive: String) method and the primitive: String (computed) variable. AFAIK, if I switch the protocol for an enum I lose this compiler-enforced type-safety.
So in summary, I want to have all of the following:
A way for a developer to declare what sql data-type they want their type to correspond with.
A way to then force that developer to actually make their type compatible with that sql data-type.
To guarantee that the developer using my library can't somehow extend the list of sql-data types, but can only use the ones that I have defined for him / her.
So far I only know how to do either the first two, but not the last one, or the last one, but not the first two. How do I get all three?
I'm not sure which of both are better to define constants. A struct or a enum. A struct will be copied every time i use it or not? When i think about a struct with static let constants it makes no sense that it will copied all the time, in my opinion. But if it won't copied then it doesn't matter what I take?
What advantages does the choice of a struct or enum?
Francescu says use structs.
Ray Wenderlich says use enums. But I lack the justification.
Both structs and enumerations work. As an example, both
struct PhysicalConstants {
static let speedOfLight = 299_792_458
// ...
}
and
enum PhysicalConstants {
static let speedOfLight = 299_792_458
// ...
}
work and define a static property PhysicalConstants.speedOfLight.
Re: A struct will be copied every time i use it or not?
Both struct and enum are value types so that would apply to enumerations as well. But that is irrelevant here
because you don't have to create a value at all:
Static properties (also called type properties) are properties of the type itself, not of an instance of that type.
Re: What advantages has the choice of a struct or enum?
As mentioned in the linked-to article:
The advantage of using a case-less enumeration is that it can't accidentally be instantiated and works as a pure namespace.
So for a structure,
let foo = PhysicalConstants()
creates a (useless) value of type PhysicalConstants, but
for a case-less enumeration it fails to compile:
let foo = PhysicalConstants()
// error: 'PhysicalConstants' cannot be constructed because it has no accessible initializers
Here's a short answer: Do your constants need to be unique? Then use an enum, which enforces this.
Want to use several different constants to contain the same value (often useful for clarity)? Then use a struct, which allows this.
One difference between the two is that structs can be instantiated where as enums cannot. So in most scenarios where you just need to use constants, it's probably best to use enums to avoid confusion.
For example:
struct Constants {
static let someValue = "someValue"
}
let _ = Constants()
The above code would still be valid.
If we use an enum:
enum Constants {
static let someValue = "someValue"
}
let _ = Constants() // error
The above code will be invalid and therefor avoid confusion.
Using Xcode 7.3.1 and Swift 2.2
While I agree with Martin R, and the Ray Wenderlich style guide makes a good point that enums are better in almost all use cases due to it being a pure namespace, there is one place where using a struct trumps enums.
Switch statements
Let's start with the struct version:
struct StaticVars {
static let someString = "someString"
}
switch "someString" {
case StaticVars.someString: print("Matched StaticVars.someString")
default: print("Didn't match StaticVars.someString")
}
Using a struct, this will match and print out Matched StaticVars.someString.
Now lets consider the caseless enum version (by only changing the keyword struct to enum):
enum StaticVars {
static let someString = "someString"
}
switch "someString" {
case StaticVars.someString: print("Matched StaticVars.someString")
default: print("Didn't match StaticVars.someString")
}
You'll notice that you get a compile time error in the switch statement on the case StaticVars.someString: line. The error is Enum case 'someString' not found in type 'String'.
There's a pseudo-workaround by converting the static property to a closure that returns the type instead.
So you would change it like this:
enum StaticVars {
static let someString = { return "someString" }
}
switch "someString" {
case StaticVars.someString(): print("Matched StaticVars.someString")
default: print("Didn't match StaticVars.someString")
}
Note the need for parentheses in the case statement because it's now a function.
The downside is that now that we've made it a function, it gets executed every time it's invoked. So if it's just a simple primitive type like String or Int, this isn't so bad. It's essentially a computed property. If it's a constant that needs to be computed and you only want to compute it once, consider computing it into a different property and returning that already computed value in the closure.
You could also override the default initializer with a private one, and then you'll get the same kind of compile time error goodness as with the caseless enum.
struct StaticVars {
static let someString = "someString"
private init() {}
}
But with this, you'd want to put the declaration of the struct in its own file, because if you declared it in the same file as, say, a View Controller class, that class's file would still be able to accidentally instantiate a useless instance of StaticVars, but outside the class's file it would work as intended. But it's your call.
In the Combine framework, Apple has chosen to prefer enums for namespaces.
enum Publishers
A namespace for types that serve as publishers.
enum Subscribers
A namespace for types that serve as subscribers.
enum Subscriptions
A namespace for symbols related to subscriptions.
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?"
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.