Struct or Enum to use for serialization keys? - ios

Is there any reason why Apple prefers using structs over enums in the Lister demo for declaring keys for serialization? Is there might be some benefits?
For example:
private struct SerializationKeys {
static let text = "text"
static let uuid = "uuid"
static let completed = "completed"
...
//duplicated key!
static let descriptionText = "text"
}
Here we might have potential duplicates for keys. It's not a big question for small objects (don't forget copy/paste :)), but for large objects with tens fields it can be a real problem.
With enum we don't have such a problem:
private enum SerializationKeys : String {
case text = "text"
case uuid = "uuid"
case completed = "completed"
//...
case descriptionText = "text"
//here we have compiler's warning: Raw value for enum case is not unique
}
Will be happy to hear some thoughts on this.

I do the same thing, sometimes, and here's why.
With a struct, my values are directly available: so, if SerializationKeys is a struct, then SerializationKeys.text is a string.
But with an enum, the enum is the value. If SerializationKeys is an enum, then SerializationKeys.text is not a string; it's an enum. If I want the string, I have to fetch it explicitly, as the enum's rawValue. Sometimes, that's just too nutty. On the other hand, if it's acceptable, or if there's another reason why this makes a good enum, then fine, I'll use an enum.
To put it another way: if this is just a glorified namespace for some constants, a struct with static members seems simplest. An enum is for a switch, i.e. something that needs to exist in exactly one of several possible states.

Apple's reason for choosing a struct here seems to be purely semantic. SerializationKeys.descriptionText is a property. SerializationKey.DescriptionText is a type. And it is kind of semantically weird to use a type as a key.
True, in this particular instance the SerializationKey.DescriptionText type happens to have a "raw" value associated with it. But as I understand it, raw values are really only intended to be used as a sort of "bridging layer" layer between C enums. Using it for keys like this is kind of a hack.

Related

Using static let in enum instead of case

I have noticed certain cases where inside an enum, instead of case people have used static let to declare variables. Is this practice justifiable?
public enum ExampleEnum {
static let case1 = "case1"
static let case2 = "case2"
static let case3 = "case3"
}
This is a quick way of creating a namespace for constants.
You can of course achieve similar effect using a struct, however running let foo = StructOnlyForStoringConstants() will not throw an error or even a warning (can be solved using private init or even logging a warning, but we quickly lose the quick in a quick way above) and hence might be confusing (some argue). Since enums without cases (or "no-case enums") cannot be instantiated, you don't have this problem.
Another reason is that putting constants in an enum might feel more natural (than say in structs), as enums are used for storing a group of related values.

Restricting var to result of #keyPath()

I really like an idea of using #keyPath() for safety reason. Now, I want to give my enum case an associated value with the name of some property.
My question is - are there any ways to force compilation check on associated value to give me a guarantee that this path is existed?
Though it doesn't work, what I want is something like this:
enum MissingKeysError {
case propIsMissing(checkedExistedPropertyHere: String)
}
I understand that #keyPath only returns me a string, but I really would like to use similar mechanism for my associated value.

In Swift, what is the keyword to learn about "dynamic class type"?

I remember I read somewhere about something that's more or less like this:
class Process <T> {
var data: Array<T> = [];
func addData (element: T) { data.append(T); }
}
This way, I can instantiate the object like this:
let objInt = Process <Int>
objInt.addData (100);
let objString = Process <String>
objString.addData ("Hello");
I want to know what is this "T", how it works, how to implement it, and how to create a class that is partially based on this dynamic type "T". But I don't know what is the keyword to search on the internet for this. I search for dynamic type in Swift, but all google returns me is about identifying and printing class' run-time dynamic type. Can somebody helps? Thanks.
Generics
From Swift programming language book
"Generic code enables you to write flexible, reusable functions and types that can work with any type, subject to requirements that you define. You can write code that avoids duplication and expresses its intent in a clear, abstracted manner."
The generic version uses a placeholder type name (called T or any other name) instead of an actual type name (such as Int, String, or Double). So in your case Process class can hold data of any type which is specified by T parameter.
You mean generics. Here is the link

Array of custom class implementing generics not allowing custom class with subclassed generic

I have the following scenario
class Human {}
class Child: Human {}
class Person<T: Human> {}
var people = [Person<Human>]()
people.append(Person<Child>())
yet on the line people.append(Person<Child>()) I receive an error of
cannot convert value of type 'Person<Child>' to expected argument type 'Person<Human>'
This is really strange as doing the following works (which seems to be an identical situation)
var myArray = [Array<UIView>]()
myArray.append(Array<UIImageView>())
Would anyone have an understanding of why one way works and not the other?
Actually you're not putting the case strongly enough. Define:
class Human {}
class Child: Human {}
struct Holder<T> {}
I made Holder a struct so no one can accuse us of cheating: Array is a struct, Holder is a struct. And I took away your constraint on the placeholder, reducing everything to the simplest possible form.
Now just assign an array of Child where an array of Human is expected:
var arr = Array<Human>()
arr = Array<Child>()
Fine. Now try it with a Holder:
var holder = Holder<Human>()
holder = Holder<Child>() // error
The parallelism now appears perfect: Array is a struct, Holder is a struct, and all we're doing is trying to assign polymorphically. So what's the problem?
The problem, as you have probably already suspected, is that you are not Apple. Apple writes the code, so they get to define Array and similar types as covariant on the parametrized type. But it isn't an automatic feature of the language — that is, it is not true for generics in general. And in particular, you don't get to do that for types you define.
So Apple's Array is covariant, but your Holder (or Person) is not, and there's nothing that allows you to switch covariance on.
You can see why Array is covariant. It is a very special case. An array is a collection of objects. Apple knows that an array of, say, Child objects is also in fact an array of Human objects, because every Child is a Human (polymorphism). So they have implemented covariance for arrays, to ensure that this is so.
But there is no such guarantee about your Person or my Holder. Swift doesn't know what you intend to do with the placeholder T. You can probably think of cases where substituting a Holder<Child> where a Holder<Human> is expected would be wrong. So Apple makes no assumptions in that direction.
I should add that it's important to distinguish the following:
class Human {}
class Child: Human {}
struct Holder<T> {
let thing : T
}
let holder : Holder<Human> = Holder(thing:Child()) // fine
That's legal, but it has nothing whatever to do with what we've been talking about. Only one generic type is involved here: Holder<Human>. All we're doing is assigning a Child into thing, where a Human is expected. That's good old-fashioned non-generic polymorphism. But you still can't cast a Holder<Human> down to a Holder<Child>, even if thing is a Child, and you still can't assign a Holder<Child> where a Holder<Human> is expected.

Why do I need to specify what type a variable is in a class in Swift?

I'm just wondering. As I understand, var and let can be anything and Swift automates the right type like in JavaScript. But when I try to set properties in a class I get an error when I don't specify the type.
var value1, value2 // Error: missing annotations
Well, I've read some references and the variable requires a type on declaration like var foo = 0. But in my class I have an init() which will set the variables to whatever I input when creating the object of the class.
So how should I achieve this? Is it even possible?
I saw the type typealias but that didn't work either.
While both Swift and Javascript allow you to assign constants to variables without defining their type, there is a fundamental difference in the two languages. Swift is a strongly-typed, type-safe language while Javascript is not. From The Swift Programming Language -
“Swift is a type safe language. A type safe language encourages you to
be clear about the types of values your code can work with. If part of
your code expects a String, you can’t pass it an Int by mistake.”
Excerpt From: Apple Inc. “The Swift Programming Language.” iBooks.
https://itunes.apple.com/au/book/swift-programming-language/id881256329?mt=11
So when you say
var welcomeMessage = "Hello"
Swift infers that you want welcomeMessage to be a string and sets its type accordingly. Subsequently trying
welcomeMessage=3
will give run a compile-time error because you are assigning the incorrect type.
If you don't assign an initial value then Swift can't infer the type and you must specify it.
Javascript, on the other hand, will quite happily accept
var welcomeMessage="Hello"
welcomeMessage=3
because it isn't type safe and just tries to do the best it can with values it has. For example, if a string operation is performed on welcomeMessage after assigning 3 to it, Javascript would convert the value to "3" and then perform the operation.
While there are type safe extensions to Javascript, it isn't a fundamental part of the language the way it is with Swift
The types are only inferred if you assign a default value initially. From an example in the Language Reference, either declare the type:
var welcomeMessage: String
or assign an initial value that allows Swift to infer the type:
welcomeMessage = "Hello"
In the welcomeMessage example above, no initial value is provided, and so the type of the welcomeMessage variable is specified with a type annotation rather than being inferred from an initial value.
JavaScript is dynamically typed, meaning variables don't have a type assigned to them. Types are only associated with objects. So a variable can contain objects of any type.
Swift is statically typed on the other hand. Meaning variables have type. You can't place any object you want into a variable. The compiler will make sure you only place compatible objects into variables.
Swift has type inference which allow the compiler to figure out the type if you write:
var value1 = "foobar"
But just writing var value1 wouldn't let the compiler figure out what type the value1 variable is. JavaScript doesn't have this problem since variables don't have types.
Remember similar looking syntax does not mean the same semantics. Swift might look a bit like JavaScript syntax wise, but the meaning (semantics) of the keywords is quite different. If you want to simulate JavaScript:
var value1, value2
In Swift you would write:
var value1: AnyObject
var value2: AnyObject
That would allow you to put any object into the value1 and value2 variables.

Resources