Accessing Class Variables by enum - swift

I have following code which basically defines enum for different companies and different methods for each company.
protocol Fetcher {
func getDetail()
static var idRegex:String {get}
}
class FooFetcher:Fetcher {
static var idRegex = "(^\\d{5}$)"
var company = Company.Foo
init (name:String?, id:String) {
println("Name: \(name) id: \(id)")
}
func getDetail() {
println("Foo Get detail")
}
}
class BarFetcher:Fetcher {
static var idRegex = "(^\\d{11}$)"
var company = Company.Bar
init (name:String?, id:String) {
println("Name: \(name) id: \(id)")
}
func getDetail() {
println("Bar Get detail")
}
}
enum Company:Int {
case Foo
case Bar
func fetcher(name:String?, id:String) -> AnyObject {
switch self {
case Foo:
return FooFetcher(name: name, id: id)
case .Bar:
return BarFetcher(name: name, id: id)
}
}
}
var compList = [Company.Foo , Company.Bar]
How can I get idRegex by using compList array only? I don't want to create another array like [FooFetcher.idRegex,BarFetcher.idRegex] because it will bloat the file. I want to use enum because it is easy to store their rawValue with Core Data.
I also wonder how to write better fetcher function for Company enum. Currently I use fetcher function like below
var company = Company.Foo
var strategy = company.fetcher("Some name", id: "Some id") as! Fetcher
strategy.getDetail()

AnyObject does not support Fetcher, in the company.fetcher function instead of returning AnyObject is -> Fetcher
enum Company:Int {
case Foo
case Bar
func fetcher(name:String?, id:String) -> Fetcher {
switch self {
case .Foo:
return FooFetcher(name: name, id: id)
case .Bar:
return BarFetcher(name: name, id: id)
}
}
}
FOO also there corresponds almost .foo

Along with eliasRuizHz's answer, add a new regex method to the Company enum.
func regex() -> String {
switch self {
case .Foo:
return FooFetcher.idRegex
case .Bar:
return BarFetcher.idRegex
}
}

The two responses both provide good contributions. For example, returning a Fetcher rather than AnyObject makes complete sense, as does a regex() function. The thing that I query, however, is whether you could use enums with associated values rather than this enum + class approach, but putting that to one side and answering the question in hand if you really want to directly access the .idRegex from an instance then it needs to be a regular instance variable not a class variable. One way to have it both ways is to return the type variable through an instance variable like so:
protocol Fetcher {
func getDetail()
var idRegex:String {get}
static var regex:String {get}
}
class FooFetcher:Fetcher {
static var regex = "(^\\d{5}$)"
var idRegex:String { return FooFetcher.regex }
var company = Company.Foo
init (name:String?, id:String) {
println("Name: \(name) id: \(id)")
}
func getDetail() {
println("Foo Get detail")
}
}
class BarFetcher:Fetcher {
static var regex = "(^\\d{11}$)"
var idRegex:String { return BarFetcher.regex }
var company = Company.Bar
init (name:String?, id:String) {
println("Name: \(name) id: \(id)")
}
func getDetail() {
println("Bar Get detail")
}
}
enum Company:Int {
case Foo
case Bar
func fetcher(name:String?, id:String) -> Fetcher {
switch self {
case Foo:
return FooFetcher(name: name, id: id)
case .Bar:
return BarFetcher(name: name, id: id)
}
}
}
var compList = [Company.Foo, Company.Bar]
var company = Company.Foo
var strategy = company.fetcher("Some name", id: "Some id")
strategy.getDetail()
strategy.idRegex
Edit Responding to Query
If you want the fetcher to not necessarily initialize an instance then set whatever requirement there is to determine whether a value or nil is returned.
enum Company:Int {
case Foo
case Bar
func fetcher(name:String?, id:String) -> Fetcher? {
switch self {
case Foo:
if FooFetcher.regex == "(^\\d{11}$)" {
return FooFetcher(name: name, id: id)}
else {return nil}
case .Bar:
if BarFetcher.regex == "(^\\d{11}$)" {
return BarFetcher(name: name, id: id)
}
else {return nil}
}
}
}
var compList = [Company.Foo, Company.Bar]
var company = Company.Foo
var strategy = company.fetcher("Some name", id: "Some id")
strategy?.getDetail()
strategy?.idRegex

Related

Some Problems about Opaque Return Type and Protocol with associatedtype In Swift

How to deal with this problem?
Definitions:
protocol NameProtocol {
var rawValue: String { get }
}
struct CatName: NameProtocol {
enum CatNamesEnum: String {
case Tiger, Max, Sam
}
var literal: CatNamesEnum
var rawValue: String {
literal.rawValue
}
}
struct DogName: NameProtocol {
enum DogNamesEnum: String {
case Jack, Toby, Sadie
}
var literal: DogNamesEnum
var rawValue: String {
literal.rawValue
}
}
Definitions:
protocol AnimalProtocol {
associatedtype Name: NameProtocol
var name: Name { get }
func cry() -> String
}
class Cat: AnimalProtocol {
var name: CatName
func cry() -> String {
return "meow, I am \(name.rawValue)"
}
init(name: CatName) {
self.name = name
}
// some other cat actions...
}
class Dog: AnimalProtocol {
var name: DogName
func cry() -> String {
return "bark, I am \(name.rawValue)"
}
init(name: DogName) {
self.name = name
}
// some other dog actions...
}
The code above are some definition code structure, should not be modified.
And the functions below takes some problem:
Protocol with asccociatedtype cannot be the dictionary value type.
Function with Opaque Return Type cannot return some different types extends the same protocol.
// <1>
// Error: Protocol 'AnimalProtocol' can only be used as a generic constraint because it has Self or associated type requirements
let animals: [String: AnimalProtocol] = [
"cat": Cat(name: CatName(literal: .Sam)),
"dog": Dog(name: DogName(literal: .Jack))
// ...
// maybe a lot of animals
]
for (animal, entity) in animals {
print("\(animal): \(entity.cry())")
}
// <2>
// Error: Function declares an opaque return type, but the return statements in its body do not have matching underlying types
func animalCry(animal: String) -> some AnimalProtocol {
switch animal {
case "cat":
return Cat(name: CatName(literal: .Sam))
default:
return Dog(name: DogName(literal: .Toby))
}
}
Any solutions?
Or best practice of different types(with embed generic type) in a list.
You say that the definitions should not be modified but this is exactly what I have done here:
I removed the associated type from the AnimalProtocol protocol since it wasn't used elsewhere in any conforming types
protocol AnimalProtocol {
var name: NameProtocol { get }
func cry() -> String
}
Then I changed both Cat and Dog to conform to the protocol by changing the name declaration in both to
var name: NameProtocol
this resolves the issue with the dictionary and the function was fixed by changing the return type from some AnimalProtocol to AnimalProtocol

Return templet string from the selected country code

I would like to get the pre-define temple name based on the country selection. Here I'm trying some code, but unable to get that from code. How do I get back messages based on the country code input?
enum Descriptor: String, CaseIterable, CustomStringConvertible {
case fr = "FR"
case jp = "JP"
var description: String {
get {
return self.rawValue
}
}
var mesage : String {
let templet = "Welcome to "
switch self {
case .fr:
return templet + "France"
case .jp:
return templet + "Japan"
}
}
}
extension Descriptor {
static func hasCountry(code: String) -> String? {
return Descriptor.allCases
.map({$0.rawValue})
.first(where: {$0.description == code})
}
}
let x = Descriptor.hasCountry(code: "JP")
print(x)
// Expected output is like
// Welcome to Japan
// or
// Welcome to France
extension Descriptor {
static func hasCountry(code: String) -> String? {
return Descriptor.allCases
.first(where: {$0.description == code})?
.mesage
}
}
You have a tiny bit of mistake in your extension.
So when you do .map({$0.rawValue}), you actually transform all your enum cases to a strings array ["FR", "JP"].
What you actually want to be doing is find your first enum case, and call .mesage on that one.

Is it possible to convert instance of a class to an instance of a subclass?

I've recently came across a case where it would be very convenient to convert an instance of a class to a subclass, while the instance has been created within the parent class. But I've never seen such thing. So is there a way to do something like:
class Foo {
var name: String
}
class Bar: Foo {
var friendName: String
}
let foo = Foo(name: "Alice")
foo.toBar(friendName: "Bob")
// foo now of type Bar, as if I'd done
// foo = Bar(name: "Alice", friendName: "Bob")
If that's not possible, is there some reasons this would be impossible from a design perspective?
===edit=== description of a use case where it could make sense
Let say there's two views representing what correspond to the same database record for a book, on is a just a preview of the book and another is a more complex view. Models could be:
protocol BookMetaDelegate {
func onReadStatusUpdate()
}
/// describe a book
class BookMeta {
var delegate: BookMetaDelegate?
private var _hasBeenRead: Bool
var hasBeenRead: Bool {
get {
return _hasBeenRead
}
set {
guard newValue != _hasBeenRead else { return }
_hasBeenRead = newValue
delegate?.onReadStatusUpdate()
}
}
var title: String
}
/// contains all the content of a book
class Book: BookMeta {
var content: BookContent
var lastPageRead: Int
/// some logic that only makes sense in a Book instance
func getLastPageRead() {
return content.getPage(lastPageRead)
}
}
and views could look like:
class BookPreview: UIView, BookMetaDelegate {
var book: BookMeta
init(book: BookMeta) {
book.delegate = self
}
func onReadStatusUpdate() {
print("read status has changed! UI should update")
}
}
class BookView: UIView {
var book: Book
init(book: Book) {
book.hasBeenRead = true
}
}
Then things could happen like
fetch(bookMetaWithId: 123).then { bookMeta in // bookMeta is of type BookMeta
let preview = BookPreview(book: bookMeta)
...
fetch(contentOf: bookMeta).then { content, lastPageRead in
bookMeta.asBook(content: content, lastPageRead: lastPageRead)
let bookView = BookView(book: bookMeta) // doing so will change the hasBeenRead flag and message the instance's delegate, ie the preview
...
}
}
Thinking more about it, it sounds like that if such thing was possible, it'd break things like:
class Foo {
var name: String
}
class Bar: Foo {
var friendName: String
}
class Bla: Foo {
var surname: String
}
func something(foo: Foo) {
foo.toBla(surname: "Will")
}
let bar = Bar(name: "Alice", friendName: "Bob")
something(foo: bar) // what does that do ??? is bar a Bla now ?
so that'd be a good reason for making such casting impossible.

Does injecting factory make sense in this scenario (Swinject)

What is the right (cleanest and most concise) way to have PetOwner that can at any later point in program create new instances of Cat?
Let's assume that createAnotherAnimal can be called by PetOwner itself after it gets response to some async request, therefore creating as many instances of Cat as needed at the time of creating PetOwner is not possible.
I solved the problem with injecting factory, but I am not convinced that it is the best way to tackle the problem, what are the alternatives in Swinject?
protocol AnimalType {
var name: String? { get set }
func sound() -> String
}
class Cat: AnimalType {
var name: String?
init(name: String?) {
self.name = name
}
func sound() -> String {
return "Meow!"
}
}
protocol PersonType {
func play() -> String
func createAnotherAnimal() -> Void
}
class PetOwner: PersonType {
var pets: [AnimalType] = []
let petFactory : AnimalFactory
init(petFactory : AnimalFactory) {
self.petFactory = petFactory
}
func createAnotherAnimal() {
let pet = petFactory.factoryMethod()
self.pets.append(pet)
}
func play() -> String {
if(pets.count>0) {
let pet : AnimalType = pets[0];
let name = pet.name ?? "someone"
return "I'm playing with \(name). \(pet.sound())"
} else {
return "No animals"
}
}
}
class AnimalFactory {
let factoryMethod : () -> AnimalType
init(factoryMethod: () -> AnimalType) {
self.factoryMethod = factoryMethod
}
}
// Create a container and register service and component pairs.
let container = Container()
container.register(AnimalType.self) { _ in Cat(name: "Mimi") }
container.register(PersonType.self) { r in PetOwner(petFactory: r.resolve(AnimalFactory.self)!) }
container.register(AnimalFactory.self){r in AnimalFactory(factoryMethod:{ () -> AnimalType in r.resolve(AnimalType.self)!}) }
// The person is resolved to a PetOwner with a Cat.
let person = container.resolve(PersonType.self)!
person.createAnotherAnimal()
print(person.play())

get the type/class of a property from its name in swift

Lets say I have this class:
class Node {
var value: String
var children: [Node]?
}
If I have the name of one of its properties (for example "children") how can I get its type? (In this case [Node]?)
I imagine having a global function like below will solve my needs:
func typeOfPropertyWithName(name: String, ofClass: AnyClass) -> AnyClass? {
//???
}
// Example usage:
var arrayOfNodesClass = typeOfPropertyWithName("children", Node.self)
Swift 2 (Note: Reflection changed):
import Foundation
enum PropertyTypes:String
{
case OptionalInt = "Optional<Int>"
case Int = "Int"
case OptionalString = "Optional<String>"
case String = "String"
//...
}
extension NSObject{
//returns the property type
func getTypeOfProperty(name:String)->String?
{
let type: Mirror = Mirror(reflecting:self)
for child in type.children {
if child.label! == name
{
return String(child.value.dynamicType)
}
}
return nil
}
//Property Type Comparison
func propertyIsOfType(propertyName:String, type:PropertyTypes)->Bool
{
if getTypeOfProperty(propertyName) == type.rawValue
{
return true
}
return false
}
}
custom class:
class Person : NSObject {
var id:Int?
var name : String?
var email : String?
var password : String?
var child:Person?
}
get the type of the "child" property:
let person = Person()
let type = person.getTypeOfProperty("child")
print(type!) //-> Optional<Person>
property type checking:
print( person.propertyIsOfType("email", type: PropertyTypes.OptionalInt) ) //--> false
print( person.propertyIsOfType("email", type: PropertyTypes.OptionalString) //--> true
or
if person.propertyIsOfType("email", type: PropertyTypes.OptionalString)
{
//true -> do something
}
else
{
//false -> do something
}
Reflection is achieved in Swift using the global reflect() function. When passing an instance of some type to reflect() it returns a MirrorType, which has a range of properties allowing you to analyze your instance:
var value: Any { get }
var valueType: Any.Type { get }
var objectIdentifier: ObjectIdentifier? { get }
var count: Int { get }
var summary: String { get }
var quickLookObject: QuickLookObject? { get }
var disposition: MirrorDisposition { get }
subscript(i: Int) -> (String, MirrorType) { get }
This seems to work:
func getTypeOfVariableWithName(name: String, inInstance instance: Any) -> String? {
let mirror = reflect(instance)
var variableCollection = [String: MirrorType]()
for item in 0..<mirror.count {
variableCollection[mirror[item].0] = mirror[item].1
}
if let type = variableCollection[name] {
let longName = _stdlib_getDemangledTypeName(type.value)
let shortName = split(longName, { $0 == "."}).last
return shortName ?? longName
}
return nil
}
Here's some example code on SwiftStub.
Edit:
The result for optional values is only "Optional".
The result for arrays is only "Array".
The result for dictionaries is only "Dictionary".
I'm not sure if it is possible to extract what kind of optional/array/dictionary it is. But I guess this would also be the case for custom data structures using generics.
Building on #PeterKreinz answer I needed to be able to check types of inherited properties as well so added a little to his above code:
extension NSObject {
// Returns the property type
func getTypeOfProperty (name: String) -> String? {
var type: Mirror = Mirror(reflecting: self)
for child in type.children {
if child.label! == name {
return String(child.value.dynamicType)
}
}
while let parent = type.superclassMirror() {
for child in parent.children {
if child.label! == name {
return String(child.value.dynamicType)
}
}
type = parent
}
return nil
}
}
Hope this may help someone.
Swift 3 update:
// Extends NSObject to add a function which returns property type
extension NSObject {
// Returns the property type
func getTypeOfProperty (_ name: String) -> String? {
var type: Mirror = Mirror(reflecting: self)
for child in type.children {
if child.label! == name {
return String(describing: type(of: child.value))
}
}
while let parent = type.superclassMirror {
for child in parent.children {
if child.label! == name {
return String(describing: type(of: child.value))
}
}
type = parent
}
return nil
}
}
The solution provided by #peter-kreinz using Swift's class Mirror works beautifully when you have an instance of a class, and want to know the types of the properties. However if you want to inspect the properties of a class without having an instance of it you might be interested in my solution.
I have a solution that finds the name and type of a property given any class that inherits from NSObject.
I wrote a lengthy explanation on StackOverflow here, and my project is available here on Github,
In short you can do something like this (but really check out the code Github):
public class func getTypesOfProperties(inClass clazz: NSObject.Type) -> Dictionary<String, Any>? {
var count = UInt32()
guard let properties = class_copyPropertyList(clazz, &count) else { return nil }
var types: Dictionary<String, Any> = [:]
for i in 0..<Int(count) {
guard let property: objc_property_t = properties[i], let name = getNameOf(property: property) else { continue }
let type = getTypeOf(property: property)
types[name] = type
}
free(properties)
return types
}