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.
Related
I am working on a Swift service that stores codable types in JSON. The type also defines a nested CodingKeys enum to define the property names when coded to JSON. For example, I might have a type that looks like this:
struct MyType : Codable {
let myIntProperty: Int
let myStringProperty: String
enum CodingKeys: String, CodingKey {
case myIntProperty = "int_property"
case myStringProperty = "string_property"
}
}
Now I have a function that wants to operate on one of these properties, as specified by a key path to that property. Using the type from the example, I want a function that can be called like this:
let result = myFunc(usingProperty: \MyType.myIntProperty)
My function is declared using the KeyPath type:
func myFunc<T: Codable, G>(usingProperty: KeyPath<T, G>) -> G {
...
}
In order to implement my function, I would like to somehow get access to not just the key path parameter named usingProperty, but also to the coding key that it is associated with. So when called with the key path to the myIntProperty property, I would want to get the name used when encoding it, which is int_property.
Is there any way to derive this value from the KeyPath parameter and its root type's CodingKeys enumeration?
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.
I have a generic response serializer:
class ResponseArraySerializer<Element: Ball> {
}
Is there any way to convert that to string, so I will store that string in Core Data database, and then to convert back? Typically I use it like this:
let redBallSerializer = ResponseArraySerializer<RedBall>()
requestInfo.responseSerializer = redBallSerializer
So I what to do the same after initialization from a string (so I can initialize the object when I have only string description of the class). Is it possible?
This SO answer describes how to use a dictionary to map a string to a class.
You can do the same thing with the various strings you store. However, generics are a bit harder to manage.
First, you need to make a Serializer protocol that ResponseArraySerializer conforms to. This protocol will have the required init:
protocol Serializer {
init()
}
You then have to add a required initializer to the serializer to satisfy the compiler:
required init() {}
Finally, create the mapping as a dictionary:
let serializerMapping: [String: Serializer.Type] = [
"redBallSerializer":ResponseArraySerializer<RedBall>.self
"greenBallSerializer":ResponseArraySerializer<GreenBall>.self
]
let serializerKey = "redBallSerializer"
let redBallSerializer = serializerMapping[serializerKey]!.init()
Creating a mapping for just the generic type itself does not seem to work, so you have to store the whole class.
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.
I have a Swift framework that defines a struct:
public struct CollectionTO {
var index: Order
var title: String
var description: String
}
However, I can't seem to use the implicit memberwise initialiser from another project that imports the library. The error is:
'CollectionTO' cannot be initialised because it has no accessible initialisers
i.e. the default synthesized memberwise initialiser is not public.
var collection1 = CollectionTO(index: 1, title: "New Releases", description: "All the new releases")
I'm having to add my own init method like so:
public struct CollectionTO {
var index: Order
var title: String
var description: String
public init(index: Order, title: String, description: String) {
self.index = index;
self.title = title;
self.description = description;
}
}
... but is there a way to do this without explicitly defining a public init?
Quoting the manual:
"Default Memberwise Initializers for Structure Types
The default memberwise initializer for a structure type is considered private if any of the structure’s stored properties are private. Otherwise, the initializer has an access level of internal.
As with the default initializer above, if you want a public structure type to be initializable with a memberwise initializer when used in another module, you must provide a public memberwise initializer yourself as part of the type’s definition."
Excerpt from "The Swift Programming Language", section "Access Control".
While it is not possible to have the default memberwise initializer at least you can make one quickly with the following steps:
UPDATE: Xcode 11 and later
As mentioned by Brock Batsell on the comments, for Xcode 11 and later all you need to is this:
Right click the class or struct name and choose refactor ->
Generate Memberwise Initializer
Xcode 10 and earlier answer
Make the object a class temporarily instead of a struct
Save
Right click the class name and choose refactor ->
Generate Memberwise Initializer
Change it back to a struct
We now have a ruby gem 💎 to parse a complete swift data model file, line-by-line, and add public access modifiers, public member-wise default initializers, and other things into a separate auto-generated output swift file.
This gem is called swift_republic
Please check out the following documentation for running this gem:
https://github.com/mehul90/swift_republic
Sometimes it's really annoying having an initializer when you don't need one. If you're constantly updating the variables to the object, it becomes bothersome very quickly to update the variables in 3 places (variable declaration, initializer parameter, and initializer implementation).
A workaround I've used for this issue is to have a static variable on the struct to act as (or essentially wrap) the "initializer". For instance:
struct MyStruct {
static var empty = Self()
static func empty(name: String) -> Self {
.init(privateName: name)
}
private var identifier: String = ""
}
Then you can call it similar to how you would an initializer (with autocomplete and everything!):
func someFunction(_ value: MyStruct) { ... }
//someFunction(.init()) -> ERROR, invalid due to `private` variable
someFunction(.empty)
someFunction(.empty(name: "Dan IRL"))
let myObject = MyStruct.empty
let myObject2 = MyStruct.empty(name: "Monty Python")
You have to define public init by yourself, luckily starting from Xcode 14 🥳 there is an automatic initializer completion (source - 60399329)