Swift constants: Struct or Enum - swift

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.

Related

Codable default values during initialization

I am new to Swift and I am working on a feature flag concept for my project and I am stuck using codable for default flag values. Currently my code looks like this
import Foundation
class KillSwitches: Codable {
public enum CodingKeys: String, CodingKeys {
case featureOne
case featureTwo
case featureThree
}
let featureOne: Bool = true
let featureTwo: Bool = true
let featureThree: Bool = false
}
I have internal helper classes which helps with encoding and decoding of all the values from the json file and that's why its not explicitly mentioned here. Prior to this implementation I didn't have any default values and was using struct reading everything from a remote config file which was working fine. Now I am in my next step to have default values for my features if in case the remote config file is unreachable.
I was expecting that I could initialize this class so I will get an object of the class with the default just like what I was getting when I read from my remote file.
I am not able to instantiate this class without passing init(from decoder:). I even tried doing
KillSwitches.init(from: KillSwitches.self) which is not working either and I get the Type does not conform to expected type Decoder.
My Json looks like this
{
"featureOne" : false,
"featureTwo" : true,
"featureThree" : true
}
Any guidance / pointers to resolve this problem is much appreciated.
Once you conform to Encodable, it's as if your class has explicitly declared a encode(to:) method and a init(from:) initialiser.
By declaring an initialiser with arguments, you immediately lose the default (parameterless) initialiser that the compiler generates for you when all properties have a default value. This is why you can't do KillSwitches(). This is stated in the documentation:
Swift provides a default initializer for any structure or class that
provides default values for all of its properties and does not provide
at least one initializer itself. The default initializer simply
creates a new instance with all of its properties set to their default
values.
KillSwitches has a init(from:) initialiser already, so Swift doesn't provide the default initialiser.
You just have to add the parameterless initialiser in yourself:
class KillSwitches: Codable {
public enum CodingKeys: String, CodingKey {
case featureOne
case featureTwo
case featureThree
}
let featureOne: Bool = true
let featureTwo: Bool = true
let featureThree: Bool = false
init() { }
}
And then you can do:
let defaultKillSwitches = KillSwitches()
if you want the default values.

Can a protocol define subscript(keyPath:) without an explicit implementation in the adopting object?

Since Swift 4, objects have gained subscript(keyPath:) which can be used to retrieve values using AnyKeyPath and its subclasses. According to the Swift book, the subscript is available on all types. For example, an instance of a class TestClass may be subscripted with an AnyKeyPath like so:
class TestClass {
let property = true
}
let anyKeyPath = \TestClass.property as AnyKeyPath
_ = TestClass()[keyPath: anyKeyPath]
This compiles correctly as expected. Use of any other valid subclass would also compile including PartialKeyPath<TestClass>, KeyPath<TestClass, Bool>, etc. This functionality is unavailable in a protocol extension. For example, the following is invalid:
class TestClass {
let property = true
}
protocol KeyPathSubscriptable {
}
extension KeyPathSubscriptable {
func test() {
let anyKeyPath = \TestClass.property as AnyKeyPath
_ = self[keyPath: anyKeyPath] // Value of type 'Self' has no subscripts
}
}
If we want to use that keyPath subscript in the protocol, we can include it in the protocol definition. However, the compiler will not resolve it automatically:
protocol KeyPathSubscriptable {
subscript(keyPath: AnyKeyPath) -> Any? { get }
}
extension KeyPathSubscriptable {
func test() {
let anyKeyPath = \TestClass.property as AnyKeyPath // This can be any valid KeyPath
_ = self[keyPath: anyKeyPath]
}
}
class TestClass: KeyPathSubscriptable { // Type 'TestObject' does not conform to protocol 'KeyPathSubscriptable'
let property = true
}
With this, we get a compile error: Type 'TestObject' does not conform to protocol 'KeyPathSubscriptable'. In order to resolve this, we must include a redundant implementation of that subscript in TestClass:
class TestClass: KeyPathSubscriptable {
let property = true
subscript(keyPath: AnyKeyPath) -> Any? {
fatalError() // This is never executed
}
}
This resolves the conformance issue and produces the goal result although it is seemingly unnecessary and illogical. I'm not sure how, but the subscript implementation is never even used. It's finding the expected implementation of subscript(keyPath:) and using that instead, but how? Where is that and is there any way to use it in a protocol? Why is this required by the compiler even though it's never used?
The context of this use case is in a logging module. The goal is that an object should be able to adopt a particular protocol which, with no additional setup on the object, would provide a human readable description of the object, instead of the default for many objects which is a memory address. The protocol would use Mirror to fetch KeyPaths of an object, read the values, and print them to the console. It is intended for debugging purposes and would not run in any production environment.
Please let me know if I can make any clarifications. I may post this to the Swift team if others think that this could potentially be a bug of sorts. All help is appreciated. Thanks in advance.
Full gist located here.

How do I combine the safety of type requirements with the safety of enums?

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?

Using switch to assign an instance variable

I'd be grateful for any help - been racking my brains for days and I can't see why this isn't working.
Essentially, I have a main view controller which will be controlled by different classes depending on which game the user selects
'classic'
'unlimited'
'timed'
When the user button is pushed, it needs to flick through the options and assign an instance of the class to a variable 'brain'.
this is what I have:
var brain = GuessMeComparer()
func switcher (random:String) {
switch random {
case "Classic": self.brain = ClassicBrain()
case "unlimited": self.brain = GuessMeComparer()
case "timed": self.brain = TimedBrain()
default:break
}
}
I get the error 'cannot assign a value of type 'ClassicBrain' to a value of type 'GuessMeComparer'.
All I can think of is that you cannot assign instance variables using switch?
Any help would be great, cheers!
Using AnyObject will work but – as vadian is saying – will force you to cast to a specific type later. A better option will be abstract a common interface for all the brain classes in a swift protocol, e.g.:
protocol BrainProtocol {
// common interface here
...
}
class /* or struct */ ClassicBrain : BrainProtocol {
// classic implementation here
...
}
class /* or struct */ TimedBrain : BrainProtocol {
// timed implementation here
...
}
...
var brain : BrainProtocol
Swift is a strong type language, the variable brain is declared as type GuessMeComparer.
Once declared you cannot change the type.
To consider different types, declare the variable explicitly as generic type AnyObject.
var brain : AnyObject = GuessMeComparer()
Now you can assign different types to the variable, but in many cases you have to cast the variable to a specific type later in the code.

Swift protocol that is using an enum with generic associated type

I'm trying to create a protocol that is using a generic enum in swift.
The compiler throws this error: Protocol can only be used as a generic constraint because it has associated type requirements
Short code snipped:
enum GenericEnum<T> {
case Unassociated
case Associated(T)
}
protocol AssociatedProtocol {
typealias AssociatedType
func foo() -> GenericEnum<AssociatedType>
}
let bar = [AssociatedProtocol]()
You can find a longer example here.
Does anybody know a solution to that issue?
Here’s the problem: imagine some subsequent lines of code.
// none of this will compile...
var bar = [AssociatedProtocol]()
bar.append(GenericEnum.Associated(1))
bar.append(GenericEnum.Associated("hello")
let foo = bar[0].foo()
What type is foo? Is it a GenericEnum<Int> or a GenericEnum<String>? Or neither?
This is especially a problem because enums, like structs, are “value types”. That means their size is determined by what they contain. Take the following code:
let x = GenericEnum.Associated(1)
sizeofValue(x) // 9 - 1 byte for the enum, 8 for the Int
let y = GenericEnum.Associated("hello")
sizeofValue(y) // 25 - 1 byte for the enum, 24 for the String
Protocols with associated types are only really there to constrain generic functions. So this would be fine:
func f<T: AssociatedProtocol>(values: [T]) {
var bar = [T]() // T is an instance of a specific
// AssociatedProtocol where T.AssociatedType
// is fixed to some specific type
}
but to use it stand-alone doesn’t make sense (at least with the current version 1.2 of Swift – new features might enable other things in the version).
If you need the protocol to be used polymorphically dynamically at runtime, you would need to ditch the typealias. Then instead it can be used as a fixed-size reference.