Is it possible to store pattern of enum with associated value in array? - swift

Let's say we have simple enum with message types:
enum MessageType {
case audio
case photo
case text
}
There is Handler class which handles messages with specific types only:
class Handler {
let allowed: [MessageType]
init(_ allowed: [MessageType]) { self.allowed = allowed }
func canHandle(_ messageType: MessageType) -> Bool {
return allowed.contains(messageType)
}
}
Basic usage example:
let handler = Handler([.audio, .photo])
print(handler.canHandle(.text)) // Prints false
I want to upgrade my MessageType and add associated value for some of message types.
class Audio {}
enum MessageType {
case audio(Audio)
case photo
case text
}
Problem is that I can't store enum's pattern in allowed array for the future check in canHandle:
// error: '_' can only appear in a pattern or on the left side of an assignment
let handler = Handler([.audio(_), .photo])
Is it possible to resolve such case in a "clean" way?
It's not possible to modify MessageType because it's in third-party library (e.g. making arguments optional and passing nil)
It's not possible to init MessageType.audio(Audio) with fake Audio because it could have private initializer
I hope to avoid switch, case let or other hard-coded checks in canHandle
Any suggestions? Thanks

If instantiating the enum with a default (or garbage) value isn't a big deal
enum MessageType {
case audio(String)
case photo
case text
}
protocol SneakyEquatableMessage {
func equals(message: MessageType) -> Bool
}
extension MessageType: SneakyEquatableMessage {
func equals(message: MessageType) -> Bool {
switch (self, message) {
case (.audio(_), .audio(_)),
(.photo, .photo),
(.text, .text):
return true
default:
return false
}
}
}
class Handler {
let allowed: [MessageType]
init(_ allowed: [MessageType]) { self.allowed = allowed }
func canHandle(_ messageType: MessageType) -> Bool {
return allowed.contains { $0.equals(message: messageType) }
}
}
Basic Usage
let handler = Handler([.audio(""), .photo])
print(handler.canHandle(.text)) // Prints false
print(handler.canHandle(.audio("abc")) //Prints true
Default (or garbage) values are unrealistic
This particular section is more specific in this context, but ultimately you're going to have breakdown your enum somehow as of Swift 4. So this is my suggestion: Dependency Injection of the Factory Pattern inside Handler. This solves all of your problems pretty cleanly without having to touch switch within Handler or optionals.
enum DisassembledMessage {
case audio
case photo
case text
}
protocol MessageTypeFactory {
func disassemble(message: MessageType) -> DisassembledMessage
func disassemble(messages: [MessageType]) -> [DisassembledMessage]
}
class Handler {
let allowed: [MessageType]
let factory: MessageTypeFactory
init(allowed: [MessageType], with factory: MessageTypeFactory) {
self.allowed = allowed
self.factory = factory
}
func canHandle(_ messageType: DisassembledMessage) -> Bool {
return factory
.disassemble(messages: allowed)
.contains { $0 == messageType }
}
}
Basic Usage
let audioValue: Audio = //...
let audioMessage = MessageType.audio(audioValue)
let factory: MessageTypeFactory = //...
let handler = Handler(allowed: [audioMessage, .photo], with: factory)
print(handler.canHandle(.text)) // Prints false
print(handler.canHandle(factory.disassemble(message: audioMessage))) //Prints true
You may be asking: wait... you just created another enum (also this is just an example, you could convert it to whatever you want in that protocol). Well I say: the enum you're using is from a library... see my notes section. Also, you can now use that factory anywhere you need to break down the library type to something, including inside Handler. You could easily expand the protocol MessageTypeFactory to convert your enum to other types (hopefully behaviors) you've created, and basically just distance yourself from the library type when you need to. I hope this helps clarify what I was getting at! I don't even think you should store MessageType in your class. You should store your own type which is some kind of mapped version of MessageType, like DisassembledType.
I hope this helps!
Notes
A few things:
I'm sorry your soul is owned by a library, really.
Use the Adapter Pattern. Clean C++ is one of many places you
can learn about it. Don't pollute your entire code base with one of
their types! That's just a tip.
I know you said you didn't want to use a switch... but you're working with enums... At least it's within the enum!
Use your own types! (Did I say that?)
Use the Adapter Pattern! (Stop it.)

Related

Sub enumeration rawValue

Consider the following code, where I declared an enum with sub enums inside of it.
enum LocalizeKey {
case message(Messages)
case buttons(Buttons)
enum Buttons: String {
case remove = "Remove"
case add = "Add"
}
enum Messages: String {
case success = "Success"
case failure = "Failure"
}
}
In a normal enum with no sub enums we can easily access .rawValue property and get the raw value of whatever case we picked.
For this case, i created a function like this just to check out what am i getting .
func keyString(for type: LocalizeKey) {
print(type)
}
keyString(for: .message(.failure)) // usage
Problem : there are no other properties than .self to access for this LocalizeKey enum .
What I am trying to achieve: perhaps you can relate by the naming, i am trying to wrap my localized keys, so i can access them easily based on the key type etc, and the rawValue that is refering to the actual key will go into the getLocalizedValue function .
Playground Output : using the function above the playground output was
message(__lldb_expr_21.LocalizeKey.Messages.failure)
Edit: without having to create a variable that switches self on every case, imagine if we had +400 key that would be a huge mess probably.
You need to switch on the type parameter and do pattern matching:
switch type {
case .message(let messages): return messages.rawValue
case .buttons(let buttons): return buttons.rawValue
}
You can also make this an extension of LocalizeKey:
extension LocalizeKey {
var keyString: String {
switch self {
case .message(let messages): return messages.rawValue
case .buttons(let buttons): return buttons.rawValue
}
}
}
You are going to have to switch somewhere. If there are only a handful of "sub-enums", it is probably the easiest to just write a switch manually:
func keyString(for type: LocalizeKey) {
switch type {
case .message(let message):
print(message.rawValue)
case .buttons(let button):
print(button.rawValue)
}
}
If you don't want to write this manually, you either have to change your data structure so it is not needed, or use a code generation tool that generates the boilerplate for you.
Although The mentioned answers do provide the solution, I'd mention the issue of the approach itself:
At this point, each new case (key) has to be added in your switch statement with an associated value, which seems to be undesired boilerplate coding; I assume that you could imagine how it will look like when having many cases in the enums.
Therefore, I'd recommend to follow an approach to be more dynamic instead of adding the value of each case manually in a switch statement. Example:
protocol Localizable {
var value: String { get }
}
extension RawRepresentable where Self: Localizable, Self.RawValue == String {
var value: String { return rawValue }
}
extension CustomStringConvertible where Self: RawRepresentable, Self.RawValue == String {
var description: String { return rawValue }
}
struct LocalizeKey {
enum Buttons: String, Localizable, CustomStringConvertible {
case remove = "Remove"
case add = "Add"
}
enum Messages: String, Localizable, CustomStringConvertible {
case success = "Success"
case failure = "Failure"
}
}
We are applying the same logic for your code, with some improvements to make it easier to maintain.
Based on that, you still able to implement your function as:
func keyString(for type: Localizable) {
print(type)
}
Usage:
keyString(for: LocalizeKey.Buttons.add) // Add
keyString(for: LocalizeKey.Messages.success) // Success
IMO, I find calling it this way seems to be more readable, straightforward rather than the proposed approach (keyString(for: .message(.failure))).

Swift where condition to check if a property is implemented

I just found another way to make a great use of protocols and protocol extensions in Swift by extending the Optional protocol to add a function so I can provide default values.
I wrote a blog post about this here: https://janthielemann.de/random-stuff/providing-default-values-optional-string-empty-optional-string-swift-3-1/
The gist of the post is that I needed a clean and easy way to provide default values for optional String which are nil or empty. To do this, I created a Emptyable protocol end extended the Optional protocol like so:
protocol Emptyable {
var isEmpty: Bool { get }
}
extension Optional where Wrapped: Emptyable {
func orWhenNilOrEmpty<T: Emptyable>(_ defaultValue: T) -> T {
switch(self) {
case .none:
return defaultValue
case .some(let value) where value.isEmpty:
return defaultValue
case .some(let value):
return value as! T
}
}
}
extension String: Emptyable {}
Now the question is: Is there a way I can get rid of the Emptyable protocol and instead have a conditional check whether or not a property or function is implemented by the generic type so that I automatically get orWhenNilOrEmpty() for each and every type which has isEmpty?
UPDATE
As suggested by Paulo, the T generic is actually not needed and I created a operator for even quicker access and more convenient usage (at least I think so. Feel free to correct me, I'm always happy to learn new things and improve myself).
I call it the "not empty nil coalescing" operator (who can come up with a better names? I feel like I suck at naming things :/ ). Hopefully some day it helps somebody:
protocol Emptyable {
var isEmpty: Bool { get }
}
infix operator ???: NilCoalescingPrecedence
extension Optional where Wrapped: Emptyable {
func orWhenNilOrEmpty(_ defaultValue: Wrapped) -> Wrapped {
switch(self) {
case .none:
return defaultValue
case .some(let value) where value.isEmpty:
return defaultValue
case .some(let value):
return value
}
}
static func ???(left: Wrapped?, right: Wrapped) -> Wrapped {
return left.orWhenNilOrEmpty(right)
}
}
extension String: Emptyable {}
extension Array: Emptyable {}
extension MyStruct: Emptyable {
let text: String
let number: Int
var isEmpty: Bool { return text.isEmpty && number == 0 }
init(text: String, number: Int) {
self.text = text
self.number = number
}
}
let mandatoryNotEmptyString = optionalOrEmptyString ??? "Default Value"
let mandatoryNotEmptyStruct = optionalOrEmptyStruct ??? MyStruct(name: "Hello World", number: 1)
No, you cannot query if an object or value has a certain property as a constraint on an extension without using a protocol. That would require reflection in a way that is currently not implemented in Swift. Also, an isEmpty property could have different meanings for different types, so testing for the existence of a method or property instead of a protocol could lead to unexpected behaviour.
You could just write
if let unwrappedString = optionalString, !unwrappedString.isEmpty {
// Do stuff
} else {
// Use default value
}
No protocol or extension required and very readable.
In Swift 4, which is coming out this fall, String will conform to BidirectionalCollection, which inherits from Collection. The Collection protocol provides an isEmpty property, so your extension could be
extension Optional where Wrapped: Collection {
// ...
}
But even then you should consider to set empty strings to nil when storing them in the first place, because you now have two states (nil and empty) which seem to represent the exact same thing.

How to get all class initializers using reflection in Swift

I'm trying to get all signature of initializer from the class in Swift. Initializer can mirror, I can find the signatures like below code.
enum MessageType {
case say
case shout
case wisper
}
class Message {
var text = ""
var type : MessageType = .say
init(text: String, type: MessageType) {
self.type = type
self.text = text
}
init(text: String) {
self.text = text
}
}
let firstInit = Message.init(text:)
let secondInit = Message.init(text:type:)
let firstMirror = Mirror(reflecting: firstInit)
let secondMirror = Mirror(reflecting: secondInit)
print(firstMirror.subjectType)
// (String) -> Message
print(secondMirror.subjectType)
// ((String, MessageType)) -> Message
However, this code requires to specify init which I want to look it up. What I expected is something like below:
let mirror = Mirror(reflecting: Message)
let inits = mirror.initializers
// something like [Message.init(text:), Message.init(text:type:)] as [Any]
for method in inits {
let mirror = Mirror(reflecting: method)
print(method.subjectType)
}
How can I get all init initializers from class using Mirror?
The Mirror struct in Swift offers some runtime introspection features, but for the default case, these focus on the instance being reflected upon rather than the type of that instance. From the language reference for Mirror:
Mirror
Representation of the sub-structure and optional “display style” of any arbitrary subject instance.
Overview
Describes the parts—such as stored properties, collection elements, tuple elements, or the active enumeration case—that make up
a particular instance. May also supply a “display style” property that
suggests how this structure might be rendered.
You can implement a custom mirror for you Message type by conforming to the CustomReflectable protocol. Implementing a custom mirror with the single purpose of listing available initializers, however, would still require manually supplying the initializer's information to the implementation of the custom mirror.
E.g.:
extension Message: CustomReflectable {
var customMirror: Mirror {
let children = DictionaryLiteral<String, Any>(dictionaryLiteral:
("init(text:)", type(of: Message.init(text:))),
("init(text:type:)", type(of: Message.init(text:type:))))
return Mirror.init(Message.self, children: children,
displayStyle: .class)
}
}
// using your custom mirror
let myMessage = Message(text: "foo")
for case (let label?, let value) in Mirror(reflecting: myMessage).children {
print("\(label), \(value)")
} /* init(text:), (String) -> Message
init(text:type:), ((String, MessageType)) -> Message */
This manual implementation requirement possibly defeats the very purpose of the exercise though. Note also that reflection must still be performed upon an instance rather than the type itself (so possibly it's easier to simply implement a dictionary describing the initializers directly as a static type property; but the manual form of this implementation defeats much of its value).

How do you use a switch statement with a nested enum?

I've created an enum for Instagram endpoints with nested enums similar to Moya.
enum Instagram {
enum Media {
case Popular
case Shortcode(id: String)
case Search(lat: Float, lng: Float, distance: Int)
}
enum Users {
case User(id: String)
case Feed
case Recent(id: String)
}
}
I would like to return the path for each endpoint.
extension Instagram: TargetType {
var path: String {
switch self {
case .Media.Shortcode(let id):
return "/media/shortcode"
}
}
}
However I'm getting an error on the switch statement above for the path.
Enum case Shortcode is not a member of type Instagram
How to fix?
Advanced Practical Enums
I'm adding a more general answer for a few reasons.
This is the only open question regarding nested enums and switch statements. The other one is sadly closed.
The only legit answer does not show how to assign the value of a nested enum to a symbol. The syntax was not intuitive to me.
None of the other answers have extensive case examples.
An enum nested 3 levels deep is more illustrative of the required syntax. Using efremidze answer still took me a while to work it out.
enum Action {
case fighter(F)
case weapon(W)
enum F {
case attack(A)
case defend(D)
case hurt(H)
enum A {
case fail
case success
}
enum D {
case fail
case success
}
enum H {
case none
case some
}
}
enum W {
case swing
case back
}
}
// Matches "3 deep"
let action = Action.fighter(.attack(.fail))
// Matches "1 deep" because more general case listed first.
let action2 = Action.weapon(.swing)
switch action {
case .fighter(.attack(.fail)):
print("3 deep")
case .weapon:
print("1 deep")
case .weapon(.swing):
print("2 deep to case")
case .fighter(.attack):
print("2 deep to another enum level")
default:
print("WTF enum")
}
By adding an associated value for the nested enum you can access it using a switch statement.
enum Instagram {
enum MediaEndpoint {
case Search(lat: Float, lng: Float, distance: Int)
}
case Media(MediaEndpoint)
}
extension Instagram: TargetType {
var path: String {
switch self {
case .Media(.Search):
return "/media/search"
}
}
}
// Demo
protocol TargetType {
var path: String { get }
}
class MoyaProvider<Target: TargetType> {
func request(_ target: Target, completion: #escaping () -> ()) {}
}
let provider = MoyaProvider<Instagram>()
provider.request(.Media(.Search(lat: 0, lng: 0, distance: 0))) {}
There is a couple of problems with your architecture. You should know when and why you need to use extensions and protocols and how you should structure your blocks of code.
If your type needs to conform to that protocol, feel free to use it to
ensure you set your own standards. I don't even see that in the github project you referred to.
Extension are good way to have a primitive type and extend its functionality in other parts of the project. It doesn't make sense to me why you should extend the type right after declaration. A good use case of it is where the String type has been extended to support URL Encoded values:
private extension String {
var URLEscapedString: String {
return self.stringByAddingPercentEncodingWithAllowedCharacters(NSCharacterSet.URLHostAllowedCharacterSet())!
}
}
When you are using this type of switch-case block
switch self {
case .Zen:
return "/zen"
case .UserProfile(let name):
return "/users/\(name.URLEscapedString)"
case .UserRepositories(let name):
return "/users/\(name.URLEscapedString)/repos"
}
The value in the case should be a member of self. that's why it can not find the type. the type is declared inside Instagram enum but it doesn't hold value in the self. it holds value inside Media. So move your media related function into the declaration of Media and access them there. That way self is referring to Media. Here's the full working code for me:
private extension String {
var URLEscapedString: String {
return self.stringByAddingPercentEncodingWithAllowedCharacters(NSCharacterSet.URLHostAllowedCharacterSet())!
}
}
public enum Instagram {
public enum Media {
case Search(String)
var path:String {
switch self {
case Media.Search(let keyword):
return "/media/search/\(keyword.URLEscapedString)"
}
}
}
}
var me = Instagram.Media.Search("me")
print(me.path)
As a piece of advice, in each step of building your whole architecture just question yourself if that piece of code belongs to that type or should be accessible publicly. In this case it makes complete sense to move search to Media cause you are searching media. You can add the same pattern for something like User and have search under user that returns different value.
Enum case Search is not a member of type Instagram
As the compiler say, Search is not a member of type Instagram. It's just an enum in the scope of Instagram. You have to create a member that is an instance of Search in Instagram
struct Instagram {
enum Media {
case Search(lat: Float, lng: Float, distance: Int)
}
// something like:
var media = .Search(lat: 0, lng: 0, distance: 0)
// I'm not sure this one is right syntax
// because I can't check it right now.
// please just get the idea
}
extension Instagram: TargetType {
var path: String {
switch self.media {
case .Search(let _, let _, let _):
return "/media/search"
}
}
}

Generic Types Collection

Building on previous question which got resolved, but it led to another problem. If protocol/class types are stored in a collection, retrieving and instantiating them back throws an error. a hypothetical example is below. The paradigm is based on "Program to Interface not an implementation" What does it mean to "program to an interface"?
instantiate from protocol.Type reference dynamically at runtime
public protocol ISpeakable {
init()
func speak()
}
class Cat : ISpeakable {
required init() {}
func speak() {
println("Meow");
}
}
class Dog : ISpeakable {
required init() {}
func speak() {
println("Woof");
}
}
//Test class is not aware of the specific implementations of ISpeakable at compile time
class Test {
func instantiateAndCallSpeak<T: ISpeakable>(Animal:T.Type) {
let animal = Animal()
animal.speak()
}
}
// Users of the Test class are aware of the specific implementations at compile/runtime
//works
let t = Test()
t.instantiateAndCallSpeak(Cat.self)
t.instantiateAndCallSpeak(Dog.self)
//doesn't work if types are retrieved from a collection
//Uncomment to show Error - IAnimal.Type is not convertible to T.Type
var animals: [ISpeakable.Type] = [Cat.self, Dog.self, Cat.self]
for animal in animals {
//t.instantiateAndCallSpeak(animal) //throws error
}
for (index:Int, value:ISpeakable.Type) in enumerate(animals) {
//t.instantiateAndCallSpeak(value) //throws error
}
Edit - My current workaround to iterate through collection but of course it's limiting as the api has to know all sorts of implementations. The other limitation is subclasses of these types (for instance PersianCat, GermanShepherd) will not have their overridden functions called or I go to Objective-C for rescue (NSClassFromString etc.) or wait for SWIFT to support this feature.
Note (background): these types are pushed into array by users of the utility and for loop is executed on notification
var animals: [ISpeakable.Type] = [Cat.self, Dog.self, Cat.self]
for Animal in animals {
if Animal is Cat.Type {
if let AnimalClass = Animal as? Cat.Type {
var instance = AnimalClass()
instance.speak()
}
} else if Animal is Dog.Type {
if let AnimalClass = Animal as? Dog.Type {
var instance = AnimalClass()
instance.speak()
}
}
}
Basically the answer is: correct, you can't do that. Swift needs to determine the concrete types of type parameters at compile time, not at runtime. This comes up in a lot of little corner cases. For instance, you can't construct a generic closure and store it in a variable without type-specifying it.
This can be a little clearer if we boil it down to a minimal test case
protocol Creatable { init() }
struct Object : Creatable { init() {} }
func instantiate<T: Creatable>(Thing: T.Type) -> T {
return Thing()
}
// works. object is of type "Object"
let object = instantiate(Object.self) // (1)
// 'Creatable.Type' is not convertible to 'T.Type'
let type: Creatable.Type = Object.self
let thing = instantiate(type) // (2)
At line 1, the compiler has a question: what type should T be in this instance of instantiate? And that's easy, it should be Object. That's a concrete type, so everything is fine.
At line 2, there's no concrete type that Swift can make T. All it has is Creatable, which is an abstract type (we know by code inspection the actual value of type, but Swift doesn't consider the value, just the type). It's ok to take and return protocols, but it's not ok to make them into type parameters. It's just not legal Swift today.
This is hinted at in the Swift Programming Language: Generic Parameters and Arguments:
When you declare a generic type, function, or initializer, you specify the type parameters that the generic type, function, or initializer can work with. These type parameters act as placeholders that are replaced by actual concrete type arguments when an instance of a generic type is created or a generic function or initializer is called. (emphasis mine)
You'll need to do whatever you're trying to do another way in Swift.
As a fun bonus, try explicitly asking for the impossible:
let thing = instantiate(Creatable.self)
And... swift crashes.
From your further comments, I think closures do exactly what you're looking for. You've made your protocol require trivial construction (init()), but that's an unnecessary restriction. You just need the caller to tell the function how to construct the object. That's easy with a closure, and there is no need for type parameterization at all this way. This isn't a work-around; I believe this is the better way to implement that pattern you're describing. Consider the following (some minor changes to make the example more Swift-like):
// Removed init(). There's no need for it to be trivially creatable.
// Cocoa protocols that indicate a method generally end in "ing"
// (NSCopying, NSCoding, NSLocking). They do not include "I"
public protocol Speaking {
func speak()
}
// Converted these to structs since that's all that's required for
// this example, but it works as well for classes.
struct Cat : Speaking {
func speak() {
println("Meow");
}
}
struct Dog : Speaking {
func speak() {
println("Woof");
}
}
// Demonstrating a more complex object that is easy with closures,
// but hard with your original protocol
struct Person: Speaking {
let name: String
func speak() {
println("My name is \(name)")
}
}
// Removed Test class. There was no need for it in the example,
// but it works fine if you add it.
// You pass a closure that returns a Speaking. We don't care *how* it does
// that. It doesn't have to be by construction. It could return an existing one.
func instantiateAndCallSpeak(builder: () -> Speaking) {
let animal = builder()
animal.speak()
}
// Can call with an immediate form.
// Note that Cat and Dog are not created here. They are not created until builder()
// is called above. #autoclosure would avoid the braces, but I typically avoid it.
instantiateAndCallSpeak { Cat() }
instantiateAndCallSpeak { Dog() }
// Can put them in an array, though we do have to specify the type here. You could
// create a "typealias SpeakingBuilder = () -> Speaking" if that came up a lot.
// Again note that no Speaking objects are created here. These are closures that
// will generate objects when applied.
// Notice how easy it is to pass parameters here? These don't all have to have the
// same initializers.
let animalBuilders: [() -> Speaking] = [{ Cat() } , { Dog() }, { Person(name: "Rob") }]
for animal in animalBuilders {
instantiateAndCallSpeak(animal)
}