Swift protocol with properties that are not always used - protocols

This is a follow-up to this question: Can I have a Swift protocol without functions
Suppose I want to add more properties to my protocol:
protocol Nameable {
var name: String {get}
var fullName: String: {get}
var nickName: String: {get}
}
However, not every struct that conforms to this protocol may have a fullName and/or nickName. How do I go about this? Can I make these two properties somehow optional? Or maybe I need three separate protocols? Or do I just add them to each struct, but leave them empty, like this:
struct Person : Nameable {
let name: String
let fullName: String
let nickName: String
let age: Int
// other properties of a Person
}
let person = Person(name: "Steve", fullName: "", nickName: "Stevie", age: 21)
That compiles and works, but I don't know if this is the 'correct' approach?

Unlike in Objective-C, you cannot define optional protocol requirements in pure Swift. Types that conform to protocols must adopt all the requirements specified.
One potential way of allowing for optional property requirements is defining them as optionals, with a default implementation of a computed property that just returns nil.
protocol Nameable {
var name : String? { get }
var fullName : String? { get }
var nickname : String? { get }
}
extension Nameable {
var name : String? { return nil }
var fullName : String? { return nil }
var nickname : String? { return nil }
}
struct Person : Nameable {
// Person now has the option not to implement the protocol requirements,
// as they have default implementations that return nil
// What's cool is you can implement the optional typed property requirements with
// non-optional properties – as this doesn't break the contract with the protocol.
var name : String
}
let p = Person(name: "Allan")
print(p.name) // Allan
However the downside to this approach is that you potentially pollute conforming types with properties that they don't implement (fullName & nickName in this case).
Therefore if it makes no logical sense for a type to have these properties (say you wanted to conform City to Nameable – but cities don't (really) have nicknames), you shouldn't be conforming it to Nameable.
A much more flexible solution, as you say, would be to define multiple protocols in order to define these requirements. That way, types can choose exactly what requirements they want to implement.
protocol Nameable {
var name : String { get }
}
protocol FullNameable {
var fullName : String { get }
}
protocol NickNameable {
// Even types that conform to NickNameable may have instances without nicknames.
var nickname : String? { get }
}
// A City only needs a name, not a fullname or nickname
struct City : Nameable {
var name : String
}
let london = City(name: "London")
// Person can have a name, full-name or nickname
struct Person : Nameable, FullNameable, NickNameable {
var name : String
var fullName: String
var nickname: String?
}
let allan = Person(name: "Allan", fullName: "Allan Doe", nickname: "Al")
You could even use protocol composition in order to define a typealias to represent all three of these protocols for convenience, for example:
typealias CombinedNameable = Nameable & FullNameable & NickNameable
struct Person : CombinedNameable {
var name : String
var fullName: String
var nickname: String?
}

You can give a default implementation to these property using protocol extension and override the property in classes/structs where actually you needed
extension Nameable{
var fullName: String{
return "NoFullName"
}
var nickName: String{
return "NoNickName"
}
}
struct Foo : Nameable{
var name: String
}

Related

Is it possible to convert string to type in Scala reflection?

I want to convert string to type in scala.Take two following cases as an example:
case class Product(id: String, title: String, description: String)
type aType = Product
client.getProduct[aType](id, "/products").map { x => println(s"Retrieved Product with id '$id': $x") } // Working
// Retrieved Product with id 'test_id': Some(Product(test_id,MyTitle,The text of my Product))
case class Store(id: String, title: String, Address: String)
type aType = Store
client.getStore[aType](id, "/stores").map { x => println(s"Retrieved Store with id '$id': $x") } // working
// Retrieved Store with id 'test_id': Some(Store(test_id, Store Name,The address of my Store))
I want to make this code general for any request, given case classes are already defined. for example
case class Product(id: String, title: String, description: String)
case class Store(id: String, title: String, Address: String)
case class API_Detail(Name: String, CaseClassName: String, Api_Url:String)
var API_List = List[API_DS]()
val request_type = "Product" // or "Store"
val id = "test_id"
val API_List_Item = API_List.filter(_.Name == request_type)
// Want to do like this...
type aType = API_List_Item.CaseClassName.toType /**/
val RequestURL = API_List_Item.Api_Url
/* Interested to know how to convert string to type. To my knowledge
some form of reflection will be implemented. */
client.getRespone[aType](id, RequestURL).map { x => println(s"Retrieved $request_type with id '$id': $x") } // Working
// Retrieved Product with id 'test_id': Some(Product(test_id,MyTitle,The text of my Product))
thanks for inviting me to this thread. Perhaps you can keep the solution simple enough by using pattern matching. The other solution using implicit class is also valid.
val requestUrl = "/products"
requestUrl match {
case "/products" => Products(...)
case "/store" => Store(...)
case _ => UnknownType
}
For additional examples on Pattern Matching, see my tutorial at allaboutscala.com
I'm extending my previous answer on Pattern Matching. There is no need to re-pattern match if we have a Polymorphic Function and I'm making use of Type Classes. I also provide detailed tutorials at allaboutscala.com on the building blocks for type classes: traits, implicits and functions in general.
You will need to expand getResponse(...) to build and populate the fields for your concrete types.
case class Product(id: String, title: String, description: String)
case class Store(id: String, title: String, address: String)
trait Client[T] {
def getResponse(s: String): Option[T]
}
object Client {
implicit val productClient: Client[Product] = new Client[Product] {
override def getResponse(s: String): Option[Product] = s match {
case "/product" => Some(Product("id", "title", "description"))
case _ => None
}
}
implicit val storeClient: Client[Store] = new Client[Store] {
override def getResponse(s: String): Option[Store] = s match {
case "/store" => Some(Store("id", "title", "address"))
case _ => None
}
}
def apply[T : Client](s: String): Option[T] =
implicitly[Client[T]].getResponse(s)
}
val product: Option[Product] = Client[Product]("/product")
val store: Option[Store] = Client[Store]("/store")

Golang - Unmarshal/rebuild an object in 2 step passing by interface

I'm working with json and golang. I've made a TCP server and, I need to Unmarshal the message to know which type of service is asked, before Unmarshal the contained data. It's a bit hard to explain so this is my code:
package main
import (
"fmt"
"encoding/json"
)
type Container struct {
Type string
Object interface{}
}
type Selling struct {
Surname string
Firstname string
//......
Price int
}
type Buying struct {
ID int
Surname string
Firstname string
//..........
}
/*
type Editing struct {
ID int
...............
}
Informations, etc etc
*/
func main() {
tmp_message_json1 := Selling{Surname: "X", Firstname: "Mister", Price: 10}
//tmp_message_json1 := Buying{ID: 1, Surname: "X", Firstname: "Mister"}
tmp_container_json1 := Container{Type: "Selling", Object: tmp_message_json1}
json_tmp, _ := json.Marshal(tmp_container_json1)
/*...........
We init tcp etc etc and then a message comes up !
...........*/
c := Container{}
json.Unmarshal(json_tmp, &c)
//I unmarshal a first time to know the type of service asked
// first question: Does Unmarshal need to be used only one time?
// does I need to pass c.Object as a string to unmarshal it in the called functions?
if c.Type == "Buying" {
takeInterfaceBuying(c.Object)
} else if c.Type == "client" {
takeInterfaceSelling(c.Object)
} else {
fmt.Println("bad entry")
}
}
func takeInterfaceBuying(Object interface{}) {
bu := Object
fmt.Println(bu.Firstname, bu.Surname, "wants to buy the following product:", bu.ID)
}
func takeInterfaceSelling(Object interface{}) {
se := Object
fmt.Println(se.Firstname, se.Surname, "wants to sell something for:", se.Price)
}
I need to handle messages which comes up like it, but I don't know if it's possible? does it possible?
Thank for help !
There is json.RawMessage for this purpose.
package main
import (
"encoding/json"
"fmt"
)
type Container struct {
Type string
Object json.RawMessage
}
type Selling struct {
Surname string
Firstname string
Price int
}
type Buying struct {
ID int
Surname string
Firstname string
}
func main() {
rawJson1 := []byte(`{"Type":"Selling","Object":{"Surname":"X","Firstname":"Mister","Price":10}}`)
rawJson2 := []byte(`{"Type":"Buying","Object":{"ID":1,"Surname":"X","Firstname":"Mister"}}`)
processMessage(rawJson1)
processMessage(rawJson2)
}
func processMessage(data []byte) {
var c Container
json.Unmarshal(data, &c)
switch {
case c.Type == "Buying":
processBuying(c)
case c.Type == "Selling":
processSelling(c)
default:
fmt.Println("bad entry")
}
}
func processBuying(c Container) {
var bu Buying
json.Unmarshal(c.Object, &bu)
fmt.Println(bu.Firstname, bu.Surname, "wants to buy the following product:", bu.ID)
}
func processSelling(c Container) {
var se Selling
json.Unmarshal(c.Object, &se)
fmt.Println(se.Firstname, se.Surname, "wants to sell something for:", se.Price)
}
I might be wrong, but I don't think you can do it in one step.
First idea: Unmarshal in map[string]interface{}
Don't use type with unmarshal, instead use a map[string]interface{}, and then construct Selling/Buying from this map (or use directly the map)
type Container struct {
Type string
Object map[string]interface{}
}
Second idea: Two steps / clueless container
First: Unmarshall in a clueless Container that doesn't know the type
type CluelessContainer struct {
Type string
Object interface{} `json:"-"` // or just remove this line ?
}
Then unmarshall in the type aware container. You could use a factory pattern to come with the right struct.

Access String value in enum without using rawValue

I would like to replace my global string constants with a nested enum for the keys I'm using to access columns in a database.
The structure is as follows:
enum DatabaseKeys {
enum User: String {
case Table = "User"
case Username = "username"
...
}
...
}
Each table in the database is an inner enum, with the name of the table being the enum's title. The first case in each enum will be the name of the table, and the following cases are the columns in its table.
To use this, it's pretty simple:
myUser[DatabaseKeys.User.Username.rawValue] = "Johnny"
But I will be using these enums a lot. Having to append .rawValue to every instance will be a pain, and it's not as readable as I'd like it to be. How can I access the String value without having to use rawValue? It'd be great if I can do this:
myUser[DatabaseKeys.User.Username] = "Johnny"
Note that I'm using Swift 2. If there's an even better way to accomplish this I'd love to hear it!
While I didn't find a way to do this using the desired syntax with enums, this is possible using structs.
struct DatabaseKeys {
struct User {
static let identifier = "User"
static let Username = "username"
}
}
To use:
myUser[DatabaseKeys.User.Username] = "Johnny"
Apple uses structs like this for storyboard and row type identifiers in the WatchKit templates.
You can use CustomStringConvertible protocol for this.
From documentation,
String(instance) will work for an instance of any type, returning its
description if the instance happens to be CustomStringConvertible.
Using CustomStringConvertible as a generic constraint, or accessing a
conforming type's description directly, is therefore discouraged.
So, if you conform to this protocol and return your rawValue through the description method, you will be able to use String(Table.User) to get the value.
enum User: String, CustomStringConvertible {
case Table = "User"
case Username = "username"
var description: String {
return self.rawValue
}
}
var myUser = [String: String]()
myUser[String(DatabaseKeys.User.Username)] = "Johnny"
print(myUser) // ["username": "Johnny"]
You can use callAsFunction (New in Swift 5.2) on your enum that conforms to String.
enum KeychainKey: String {
case userId
case email
}
func callAsFunction() -> String {
return self.rawValue
}
usage:
KeychainKey.userId()
You can do this with custom class:
enum Names: String {
case something, thing
}
class CustomData {
subscript(key: Names) -> Any? {
get {
return self.customData[key.rawValue]
}
set(newValue) {
self.customData[key.rawValue] = newValue
}
}
private var customData = [String: Any]()
}
...
let cData = CustomData()
cData[Names.thing] = 56
Edit:
I found an another solution, that working with Swift 3:
enum CustomKey: String {
case one, two, three
}
extension Dictionary where Key: ExpressibleByStringLiteral {
subscript(key: CustomKey) -> Value? {
get {
return self[key.rawValue as! Key]
}
set {
self[key.rawValue as! Key] = newValue
}
}
}
var dict: [String: Any] = [:]
dict[CustomKey.one] = 1
dict["two"] = true
dict[.three] = 3
print(dict["one"]!)
print(dict[CustomKey.two]!)
print(dict[.three]!)
If you are able to use User as dictionary key instead of String (User is Hashable by default) it would be a solution.
If not you should use yours with a nested struct and static variables/constants.

Initialize nested struct definition

How do you initialize the following struct?
type Sender struct {
BankCode string
Name string
Contact struct {
Name string
Phone string
}
}
I tried:
s := &Sender{
BankCode: "BC",
Name: "NAME",
Contact {
Name: "NAME",
Phone: "PHONE",
},
}
Didn't work:
mixture of field:value and value initializers
undefined: Contact
I tried:
s := &Sender{
BankCode: "BC",
Name: "NAME",
Contact: Contact {
Name: "NAME",
Phone: "PHONE",
},
}
Didn't work:
undefined: Contact
Your Contact is a field with anonymous struct type. As such, you have to repeat the type definition:
s := &Sender{
BankCode: "BC",
Name: "NAME",
Contact: struct {
Name string
Phone string
}{
Name: "NAME",
Phone: "PHONE",
},
}
But in most cases it's better to define a separate type as rob74 proposed.
How about defining the two structs separately and then embedding "Contact" in "Sender"?
type Sender struct {
BankCode string
Name string
Contact
}
type Contact struct {
Name string
Phone string
}
if you do it this way, your second initialization attempt would work. Additionally, you could use "Contact" on its own.
If you really want to use the nested struct, you can use Ainar-G's answer, but this version isn't pretty (and it gets even uglier if the structs are deeply nested, as shown here), so I wouldn't do that if it can be avoided.
type NameType struct {
First string
Last string
}
type UserType struct {
NameType
Username string
}
user := UserType{NameType{"Eduardo", "Nunes"}, "esnunes"}
// or
user := UserType{
NameType: NameType{
First: "Eduardo",
Last: "Nunes",
},
Username: "esnunes",
}

Swift: Convert enum value to String?

Given the following enum:
enum Audience {
case Public
case Friends
case Private
}
How do I get the string "Public" from the audience constant below?
let audience = Audience.Public
The idiomatic interface for 'getting a String' is to use the CustomStringConvertible interface and access the description getter. You could specify the 'raw type' as String but the use of description hides the 'raw type' implementation; avoids string comparisons in switch/case and allows for internationalization, if you so desire. Define your enum as:
enum Foo : CustomStringConvertible {
case Bing
case Bang
case Boom
var description : String {
switch self {
// Use Internationalization, as appropriate.
case .Bing: return "Bing"
case .Bang: return "Bang"
case .Boom: return "Boom"
}
}
}
In action:
> let foo = Foo.Bing
foo: Foo = Bing
> println ("String for 'foo' is \(foo)"
String for 'foo' is Bing
Updated: For Swift >= 2.0, replaced Printable with CustomStringConvertible
Note: Using CustomStringConvertible allows Foo to adopt a different raw type. For example enum Foo : Int, CustomStringConvertible { ... } is possible. This freedom can be useful.
Not sure in which Swift version this feature was added, but right now (Swift 2.1) you only need this code:
enum Audience : String {
case public
case friends
case private
}
let audience = Audience.public.rawValue // "public"
When strings are used for raw values, the implicit value for each case
is the text of that case’s name.
[...]
enum CompassPoint : String {
case north, south, east, west
}
In the example above, CompassPoint.south has an implicit raw value of
"south", and so on.
You access the raw value of an enumeration case with its rawValue
property:
let sunsetDirection = CompassPoint.west.rawValue
// sunsetDirection is "west"
Source.
In swift 3, you can use this
var enumValue = Customer.Physics
var str = String(describing: enumValue)
from Swift how to use enum to get string value
For now, I'll redefine the enum as:
enum Audience: String {
case Public = "Public"
case Friends = "Friends"
case Private = "Private"
}
so that I can do:
audience.toRaw() // "Public"
But, isn't this new enum definition redundant? Can I keep the initial enum definition and do something like:
audience.toString() // "Public"
I like to use Printable with Raw Values.
enum Audience: String, Printable {
case Public = "Public"
case Friends = "Friends"
case Private = "Private"
var description: String {
return self.rawValue
}
}
Then we can do:
let audience = Audience.Public.description // audience = "Public"
or
println("The value of Public is \(Audience.Public)")
// Prints "The value of Public is Public"
A swift 3 and above example if using Ints in Enum
public enum ECategory : Int{
case Attraction=0, FP, Food, Restroom, Popcorn, Shop, Service, None;
var description: String {
return String(describing: self)
}
}
let category = ECategory.Attraction
let categoryName = category.description //string Attraction
Updated for the release of Xcode 7 GM. It works as one would hope now--thanks Apple!
enum Rank:Int {
case Ace = 1, Two, Three, Four, Five, Six, Seven, Eight, Nine, Ten, Jack, Queen, King
}
let r = Rank.Ace
print(r) // prints "Ace"
print("Rank: \(r)!") // prints "Rank: Ace!"
It couldn't get simpler than this in Swift 2 and the latest Xcode 7 (no need to specify enum type, or .rawValue, descriptors etc...)
Updated for Swift 3 and Xcode 8:
enum Audience {
case Public
case Friends
case Private
}
let audience: Audience = .Public // or, let audience = Audience.Public
print(audience) // "Public"
For anyone reading the example in "A Swift Tour" chapter of "The Swift Programming Language" and looking for a way to simplify the simpleDescription() method, converting the enum itself to String by doing String(self) will do it:
enum Rank: Int
{
case Ace = 1 //required otherwise Ace will be 0
case Two, Three, Four, Five, Six, Seven, Eight, Nine, Ten
case Jack, Queen, King
func simpleDescription() -> String {
switch self {
case .Ace, .Jack, .Queen, .King:
return String(self).lowercaseString
default:
return String(self.rawValue)
}
}
}
You can also use "\(enumVal)"
Here is an example :
enum WeekDays{ case Sat, Sun, Mon, Tue, Wed, The, Fri }
let dayOfWeek: String = "\(WeekDays.Mon)"
Tried and tested in Swift 5
After try few different ways, i found that if you don't want to use:
let audience = Audience.Public.toRaw()
You can still archive it using a struct
struct Audience {
static let Public = "Public"
static let Friends = "Friends"
static let Private = "Private"
}
then your code:
let audience = Audience.Public
will work as expected. It isn't pretty and there are some downsides because you not using a "enum", you can't use the shortcut only adding .Private neither will work with switch cases.
Starting from Swift 3.0 you can
var str = String(describing: Audience.friends)
Modern, minimalist way
enum Audience: String {
case Public = "Public"
case Friends = "Friends"
case Private = "Private"
func callAsFunction() -> String {
self.rawValue
}
}
let audience = Audience.Public() // "Public"
There are multiple ways to do this. Either you could define a function in the enum which returns the string based on the value of enum type:
enum Audience{
...
func toString()->String{
var a:String
switch self{
case .Public:
a="Public"
case .Friends:
a="Friends"
...
}
return a
}
Or you could can try this:
enum Audience:String{
case Public="Public"
case Friends="Friends"
case Private="Private"
}
And to use it:
var a:Audience=Audience.Public
println(a.toRaw())
Use Ruby way
var public: String = "\(Audience.Public)"
One more way
public enum HTTP{
case get
case put
case delete
case patch
var value: String? {
return String(describing: self)
}
Friendly by guides if you need to use static strings as enum values:
class EncyclopediaOfCats {
struct Constants {
static var playHideAndSeek: String { "Play hide-and-seek" }
static var eat: String { "Eats" }
static var sleep: String { "Sleep" }
static var beCute: String { "Be cute" }
}
}
enum CatLife {
case playHideAndSeek
case eat
case sleep
case beCute
typealias RawValue = String
var rawValue: String {
switch self {
case .playHideAndSeek:
return EncyclopediaOfCats.Constants.playHideAndSeek
case .eat:
return EncyclopediaOfCats.Constants.eat
case .sleep:
return EncyclopediaOfCats.Constants.sleep
case .beCute:
return EncyclopediaOfCats.Constants.beCute
}
}
init?(rawValue: CatLife.RawValue) {
switch rawValue {
case EncyclopediaOfCats.Constants.playHideAndSeek:
self = .playHideAndSeek
case EncyclopediaOfCats.Constants.eat:
self = .eat
case EncyclopediaOfCats.Constants.sleep:
self = .sleep
case EncyclopediaOfCats.Constants.beCute:
self = .beCute
default:
self = .playHideAndSeek
}
}
}
XCode 14.0.1/Swift 5.7:
This is something that should be simple to achieve, but that is quite confusing, with some pitfalls!
Most answers here explain how to associate strings to each enum values, or to create enums of explicit Strings, but is only possible when you are creating your own enums.
In case of enums already existing, like numerous enums in Apple's APIs, one could really want to convert enums values to String.
To take a concrete example, if one wants to display NWConnection.States values in a SwiftUI Text view.
According to some answers, and assuming that we have something like:
Class tcpClient() {
var connection: NWConnection
(...)
then
Text("Current state: \(tcpClient.connection.state)")
should work. But you get a "No exact matches in call to instance method 'appendInterpolation'" error.
A reason to explain this behaviour is because despite being described as enum in Apple's documentation, some enumerations are in fact not Swift enumerations, but C-Style enumerations. And according to Apple, Swift can't print the text version, because all it has at runtime is enum's number value. This could be the case for quite some APIs.
See: https://stackoverflow.com/a/70910402/5965609
Other solution proposed here is:
Text(String(describing: tcpClient.connection.state))
Seems to work, but #Eric Aya comment here says : String(describing:) should never be used to convert anything to String, it's not its purpose and will give unexpected results in many cases. Better use string interpolation or string initializers.
So, in case of C-Style enumarations from APIs or other imported code, the only viable solution seems to have a function with a switch case to associate Strings to each enum's value.
I agree with all the above answers but in your enum private and the public cases can't be defined since those are default keywords. I'm including CaseIterable in my answer, it may help you to get all cases if you required to loop over.
enum Audience: String, CaseIterable {
case publicAudience
case friends
case privateAudience
var description: String {
switch self {
case .publicAudience: return "Public"
case .friends: return "Friends"
case .privateAudience: return "Private"
}
}
static var allAudience: [String] {
return Audience { $0.rawValue }
}
}

Resources