Can I group two functions with Generics into one function in Swift? - swift

I have two bellow functions with Generics.
func objectFunc<T:SomeProtocol>(obj:T)
func arrayFunc<T:SomeProtocol>(obj:[T])
Can I group these functions into one function?
I found the link Checking if an object is a given type in Swift, but this is a little different.
Added.
For example, I want to do like bellow.
func objectAndArrayFunc<T>(arg:T, someEnum:SomeEnum){
switch someEnum {
case A:
// something
case B:
// something
}
if let items = arg as? [T] {
for item in items {
// something
}
} else if let item = arg as? T {
// something
}
// I want to do something [T] and T common processing
}
enum SomeEnum {
case A
case B
}
Also, SomeEnum count might increase.

Generally speaking, depends on what // something is. There are many ways ...
Private common processor
protocol Property {
var name: String{ get }
}
enum SomeEnum {
case A, B
}
func process<T:Property>(object:T, someEnum:SomeEnum) {
process(object, nil, someEnum)
}
func process<T:Property>(objects:[T], someEnum:SomeEnum) {
process(nil, objects, someEnum)
}
private func process<T:Property>(object: T?, objects:[T]?, someEnum:SomeEnum) {
switch someEnum {
case .A:
// something
break
case .B:
// something
break
}
// holds all items for common processing
var itemsToProcess: [T] = []
if let items = objects {
// process items
itemsToProcess = items
for item in items {
println("\(item.name)")
}
} else if let item = object {
// process single item
itemsToProcess = [item]
println("\(item.name)")
}
// iterate over single/all items and process them
for item in itemsToProcess {
println("\(item.name)")
}
}
Wrap it to Enum
protocol Property {
var name: String{ get }
}
enum SomeEnum {
case A, B
}
enum Objects<T> {
case Single(T)
case Multiple([T])
}
private func process<T:Property>(objects: Objects<T>, someEnum:SomeEnum) {
switch someEnum {
case .A:
// something
break
case .B:
// something
break
}
// holds all items for common processing
var itemsToProcess: [T] = []
switch objects {
case .Multiple(let items):
// process items
itemsToProcess = items
for item in items {
println("\(item.name)")
}
case .Single(let item):
// process single item
itemsToProcess = [item]
println("\(item.name)")
}
// iterate over single/all items and process them
for item in itemsToProcess {
println("\(item.name)")
}
}
struct Prop: Property {
var name: String {
return "hi"
}
}
let prop = Prop()
process(.Single(prop), .A)
process(.Multiple([prop]), .B)
Unfortunately, the 2nd example segfaults Swift 1.2 compiler.
Anyway, it really depends on what your goal is. Why you do not want pass even single item as [T], ...
To answer your question - no, you can't pass T or [T] in one argument, different types. Unless you wrap it to Enum or whatever, unless you do want to use AnyObject and make as dances, ...

Related

SOLVED - Swift Enum - Casting Nested Enums to String Enum to allow .rawValue

SOLVED
Thank you #New Dev and #Joakim Danielson for your help. I used #Joakim Danielson's answer to improve my code.
I have an extension method to assign accessibilityIdentifiers to views based on a given String Enum. I updated the method to directly accept String Enum Cases as a parameter, thus COMPLETELY eliminating the need for the AccessibilityId enum class as shown below, awesome!
Changes
Before:
.accessibility(identifier: .home(.clickButton))
// Simplified for StackOverflow.
// Imagine 20 more cases..
enum AccessibilityId {
case home(HomeId)
var rawValue: String {
switch self {
case .home(let id):
return id.rawValue
}
}
}
extension View {
func accessibility(identifier: AccessibilityId) -> ModifiedContent<Self, AccessibilityAttachmentModifier> {
self.accessibility(identifier: identifier.rawValue)
}
}
After:
.accessibility(identifier: HomeId.clickButton)
extension View {
func accessibility<T: RawRepresentable>(identifier: T) -> ModifiedContent<Self, AccessibilityAttachmentModifier> where T.RawValue == String {
self.accessibility(identifier: identifier.rawValue)
}
}
---------------------------------------------------------------
Original Question
What I have
enum Item {
case animal(AnimalId)
case vehicle(VehicleId)
case food(FoodId)
var rawValue: String {
switch self {
case .animal(let id):
return id.rawValue
case .vehicle(let id):
return id.rawValue
case .food(let id):
return id.rawValue
}
}
}
enum AnimalId: String {
case cat
case dog
}
// etc.
// Imagine more cases and more enums.
What I want
enum Item {
case animal(AnimalId)
case vehicle(VehicleId)
case food(FoodId)
var rawValue: String {
switch self {
case self as StringEnum:
return id.rawValue
default:
return ""
}
}
}
Usage
func test() {
foo(.animal(.cat))
foo(.vehicle(.plane))
foo(.food(.tacos))
}
func foo(_ item: Item) {
print(item.rawValue)
}
I am happy with the usage, but I'd like to reduce the amount of duplicate cases in the given switch statement. Notice how they all have return id.rawValue. The above is just an example, in reality I have around 30 cases.
My Question
Is there a way for me to catch all Nested String Enums in a single switch or let case to reduce the duplicate code I have to write without losing the intended usage?
Thank you for your efforts, I hope to find an improvement for my code!
Here is a solution that is not based on Item being an enum but instead a generic struct
struct Item<T: RawRepresentable> where T.RawValue == String {
let thing: T
var rawValue: String {
thing.rawValue
}
}
With this solution you don't need to change your other enums.
Example
let item1 = Item(thing: AnimalId.cat)
let item2 = Item(thing: VehicleId.car)
print(item1.rawValue, item2.rawValue)
outputs
cat car
You need something common between all these associated values, like a conformance to a shared protocol, e.g. protocol RawStringValue:
protocol RawStringValue {
var rawValue: String { get }
}
// String enums already conform without any extra implementation
extension AnimalId: RawStringValue {}
extension VehicleId: RawStringValue {}
extension FoodId: RawStringValue {}
Then you could create a switch self inside like so:
var rawValue: String {
switch self {
case .animal (let id as RawStringValue),
.vehicle (let id as RawStringValue),
.food (let id as RawStringValue):
return id.rawValue
}
}
That being said, enum with associated values isn't the most convenient type to work with, so be sure that it's the right choice.

Enum Case with Object

How can one get the object inside an emun case? Is it possible without a switch statement?
enum ItemType {
case person(Person)
case dog(Dog)
case cat(Cat)
}
var items = [ItemType]()
var dog = items[index] // Would like the actual dog object
You need to check that you have the right case (since items[index] might be a Cat or a Person, instead).
let item = items[index]
if case .dog(let dog) = item {
// use `dog`
}
If you plan on accessing this a lot, you could add a computed property on ItemType.
extension ItemType {
var dog: Dog? {
switch self {
case .dog(let dog): return dog
default: return nil
}
}
}
Note that this is optional (since not every ItemType has a dog). But then you could say:
if let dog = items[index].dog { ... }

Subscribing to fetch a nested array

I have an object and its properties as following:
class Section {
var cards: [MemberCard]
init(card: [MemberCard]) {
}
}
class MemberCard {
var name: String
var address: String?
init(name: String) {
self.name = name
}
}
I'm subscribing to a RxStream of type Observable<[Section]>. Before I subscribe I would to want flat map this function.
where the flat map would perform the following actions:
let sectionsStream : Observable<[Section]> = Observable.just([sections])
sectionsStream
.flatMap { [weak self] (sections) -> Observable<[Section]> in
for section in sections {
for card in section.cards {
}
}
}.subscribe(onNext: { [weak self] (sections) in
self?.updateUI(memberSections: sections)
}).disposed(by: disposeBag)
func getAddressFromCache(card: MemberCard) -> Observable<MemberCard> {
return Cache(id: card.name).flatMap ({ (card) -> Observable<MemberCard> in
asyncCall{
return Observable.just(card)
}
}
}
How would the flatmap look like when it comes to converting Observable<[Section]> to array of [Observable<MemberCard>] and back to Observable<[Section]>?
Technically, like that -
let o1: Observable<MemberCard> = ...
let o2: Observable<Section> = omc.toList().map { Section($0) }
let o2: Observable<[Section]> = Observable.concat(o2 /* and all others */).toList()
But I do not think it is an optimal solution, at least because there is no error handling for the case when one or more cards cannot be retrieved. I would rather build something around aggregation with .scan() operator as in https://github.com/maxvol/RaspSwift
Here you go:
extension ObservableType where E == [Section] {
func addressedCards() -> Observable<[Section]> {
return flatMap {
Observable.combineLatest($0.map { getAddresses($0.cards) })
}
.map {
$0.map { Section(cards: $0) }
}
}
}
func getAddresses(_ cards: [MemberCard]) -> Observable<[MemberCard]> {
return Observable.combineLatest(cards
.map {
getAddressFromCache(card: $0)
.catchErrorJustReturn($0)
})
}
If one of the caches emits an error, the above will return the MemberCard unchanged.
I have a couple of other tips as well.
In keeping with the functional nature of Rx, your Section and MemberCard types should either be structs or (classes with lets instead of vars).
Don't use String? unless you have a compelling reason why an empty string ("") is different than a missing string (nil). There's no reason why you should have to check existence and isEmpty every time you want to see if the address has been filled in. (The same goes for arrays and Dictionaries.)
For this code, proper use of combineLatest is the key. It can turn an [Observable<T>] into an Observable<[T]>. Learn other interesting ways of combining Observables here: https://medium.com/#danielt1263/recipes-for-combining-observables-in-rxswift-ec4f8157265f

How to check Swift nested enums for equality of the outer enum only?

I'm trying to use nested enums to describe my model, in a way that makes illegal states impossible and provides categorisation at the top level. Right now, my (simplified) code is:
enum SportsEvent {
enum RunningEvent {
case sprint
case marathon
}
enum ThrowingEvent {
case hammer
case javelin
case discus
}
case running(event: RunningEvent)
case throwing(event: ThrowingEvent)
func isSameCategory(as other: SportsEvent) -> Bool {
return false
}
}
let sprint = SportsEvent.running(event: .sprint)
let javelin = SportsEvent.throwing(event: .javelin)
let hammer = SportsEvent.throwing(event: .hammer)
sprint.isSameCategory(as: javelin) // False
hammer.isSameCategory(as: javelin) // True
It feels like it should be trivial with an if case ... and a wildcard, but I'm not seeing how to achieve that. I'm hoping a giant switch statement isn't necessary, as my actual model is more complex.
I think you need a switch-statement, with a “compound case” listing all
possible “same value combinations” of the outer enumeration,
plus a default case:
func isSameCategory(as other: SportsEvent) -> Bool {
switch (self, other) {
case (.running, .running),
(.throwing, .throwing):
return true
default:
return false
}
}
Or (attribution goes to #Hamish):
func isSameCategory(as other: SportsEvent) -> Bool {
switch (self, other) {
case (.running, .running),
(.throwing, .throwing):
return true
case (.running, _),
(.throwing, _):
return false
}
}
which has the advantage that the compiler checks that all cases are covered. For an enumeration with n cases that makes 2 * n
cases in the switch statement (which is better than n * n if you checked all possible combinations).
Depending on your use case, you might be able to convert SportEvent to a protocol:
enum RunningEvent {
case sprint
case marathon
}
enum ThrowingEvent {
case hammer
case javelin
case discus
}
enum SportEventCategory {
case running
case throwing
}
protocol SportEvent {
var category: SportEventCategory { get }
}
extension RunningEvent: SportEvent {
var category: SportEventCategory {
return .running
}
}
extension ThrowingEvent: SportEvent {
var category: SportEventCategory {
return .throwing
}
}
let sportEvent1: SportEvent = RunningEvent.sprint
let sportEvent2: SportEvent = ThrowingEvent.hammer
print(sportEvent1.category == sportEvent2.category)
or even as one flat enum:
enum SportEvent {
enum Category {
case running
case throwing
}
case sprint
case marathon
case hammer
case javelin
case discus
var category: Category {
switch self {
case .sprint, .marathon, .hammer:
return .running
case .javelin, .discus:
return .throwing
}
}
}
let sportEvent1: SportEvent = .sprint
let sportEvent2: SportEvent = .marathon
print(sportEvent1.category == sportEvent2.category)

Best way to test mutual membership for set of enums

Is there a good way to group different enums into sets to test for mutual membership, without resorting to a lot of code duplication?
For example, below, when I get a .coldBeverage I get [.cola, .milk, .wine] , and likewise if I get either .cola, .milk, or .wine I get .coldBeverage.
enum BeverageType {
case coldBeverage
case hotBeverage
}
enum Beverage {
case cola
case milk
case wine
case coffee
case tea
case hotChocolate
}
Of course, I could always create a var on each enum, and enter the reciprocal relationship for each type. Was just curious if there was some other structure.
i.e.
extension BeverageType {
var associatedBeverages: [Beverage] {
switch self {
case .coldBeverage:
return [.cola, .milk, .wine]
case .hotBeverage:
return [.coffee, .tea, .hotChocolate]
}
}
}
extension Beverage {
var beverageType: BeverageType {
switch self:
case .cola:
return .coldBeverage
case .milk:
return .coldBeverage
//etc...
}
}
You could avoid the duplication using a static dictionary in one of the enums:
extension BeverageType
{
var associatedBeverages:[Beverage] { return Beverage.associatedBeverages[self]! }
}
extension Beverage
{
var beverageType:BeverageType { return Beverage.beverageTypes[self]! }
static var beverageTypes:[Beverage:BeverageType]
= [
.cola : .coldBeverage,
.milk : .coldBeverage,
.wine : .coldBeverage,
.coffee : .hotBeverage,
.tea : .hotBeverage,
.hotChocolate : .hotBeverage
]
static var associatedBeverages:[BeverageType:[Beverage]] =
{
var beveragesByType:[BeverageType:[Beverage]] = [:]
Beverage.beverageTypes.forEach
{beveragesByType[$0.1] = (beveragesByType[$0.1] ?? []) + [$0.0]}
return beveragesByType
}()
}
This approach does not require duplicating the list of enum entries (in addition to the mapping, which you have to do somewhere). It is also more efficient than a sequential search which could become significant for large or frequently used enums.
The static variables are evaluated only once, so from the second use onward, you benefit from the O(1) performance of dictionaries in both direction of the relationship.
Note that you could build the dictionaries the other way around (i.e. from [BeverageType:[Beverage]] to [Beverage:BeverageType]) and you could also place the static variables in each enum or all in the BeverageType enum.
I felt that beverages should know their BeverageType and are more likely to be expanded to new drinks so I chose to define the relationship in that (many to one) direction.
This could even be generalized further by defining a bidirectional Dictionary (generic) class to use in these situations so that the boiler plate code for the inverted dictionary doesn't pollute the extension.
[EDIT] With a bidirectional dictionary for the relation, the definition becomes even cleaner:
extension BeverageType
{
var associatedBeverages:[Beverage] { return Beverage.beverageTypes[self] }
}
extension Beverage
{
var beverageType:BeverageType { return Beverage.beverageTypes[self]! }
static var beverageTypes = ManyToOne<Beverage,BeverageType>(
[
.coldBeverage : [.cola, .milk, .wine],
.hotBeverage : [.coffee, .tea, .hotChocolate]
])
}
struct ManyToOne<M:Hashable,O:Hashable>
{
var manyToOne:[M:O] = [:]
var oneToMany:[O:[M]] = [:]
init( _ m2o:[M:O] )
{
manyToOne = m2o
for (many,one) in m2o { oneToMany[one] = (oneToMany[one] ?? []) + [many] }
}
init( _ o2m:[O:[M]])
{
oneToMany = o2m
for (one,many) in o2m { many.forEach{ manyToOne[$0] = one } }
}
subscript(many:M) -> O? { return manyToOne[many] }
subscript(one:O) -> [M] { return oneToMany[one] ?? [] }
}
You can use one membership to define the other:
extension Beverage {
static var beverages: [Beverage] {
return [.cola, .milk, .wine, .coffee, .tea, .hotChocolate]
}
var type: BeverageType {
switch self {
case .cola, .milk, .wine:
return .coldBeverage
case .coffee, .tea, .hotChocolate:
return .hotBeverage
}
}
}
extension BeverageType {
var associatedBeverages: [Beverage] {
return Beverage.beverages.filter { $0.type == self }
}
}