Remove enum from array of enums regardless of its argument - swift

I have an enum BulletinOptions:
enum BulletinOption {
case notificationPermissions
case enableNotifications(eventId: String)
case join(hostName: String, eventId: String)
case share(type: SocialBulletinItem.BulletinType.Social, event: EventJSONModel, view: UIView)
case completedShare(type: SocialBulletinPageItem.SocialButtonType)
}
I have an array of these enums like this:
let array = [
.join(hostName: hostName, eventId: event.id),
.notificationPermissions,
.enableNotifications(eventId: event.id),
.share(type: .queue(position: 0, hostName: ""), event: event, view: view)
]
I want to create a function that can remove a specific enum from this array. I have this code:
func remove(
item: BulletinOption,
from options: [BulletinOption]) -> [BulletinOption] {
var options = options
if let index = options.firstIndex(where: {
if case item = $0 {
return true
}
return false
}) {
options.remove(at: index)
}
return options
}
What I want to do is this:
let options = remove(item: .enableNotifications, from: options)
However, this gives me two errors. The remove function says:
Expression pattern of type 'BulletinOption' cannot match values of type 'BulletinOption'
for the line:
if case item = $0
The second error is when calling that function:
Member 'enableNotifications' expects argument of type '(eventId: String)'
I just want to delete that enum regardless of its argument. How can I do that?

This is currently impossible.
What you are trying to do is essentially passing an enumeration case pattern as an argument to a method, so that that method can match each value in the array against that pattern. However, the swift guide says that:
An enumeration case pattern matches a case of an existing enumeration type. Enumeration case patterns appear in switch statement case labels and in the case conditions of if, while, guard, and for-in statements.
This means that enumeration case patterns are not allowed as arguments to functions. :(
So the best you can do is this:
array.filter {
if case .enableNotifications = $0 { return false } else { return true }
}

Related

Unwrapping associated value for all cases in switch

I have an enum similar to this, where all cases contain the same associated value content:
enum RowType {
case single(_ content: [Any])
case double(_ content: [Any])
case triple(_ content: [Any])
...
}
I know I could just use a struct with a rowType and a content attribute, but let's please not discuss this, but rather have a look at the following:
When I want to switch all the cases, I could of course do this:
switch row {
case .single(let content):
// do anything
break
case .double(let content):
// ...
...
}
or even:
switch row {
case .single(let content), .double(let content), ...:
// do the same thing for all cases
break
}
Now my enum contains a few more cases and is likely to grow further while under development, so it's inconvenient for me to list all cases in the same case statement just to unwrap the content argument.
So I got curious and wondered: Can I somehow "wildcard" the enum case itself and still unwrap the content field? Like a default case with associated value...
I was hoping to be able to do something like ...
switch row {
case _(let content):
// do something
break
}
... or maybe accessing the associated value in the default case.
I did a bit of research but couldn't find an answer, so I'm excited for your thoughts.
Try this in Playground.
enum RowType {
case single(_ content: [Any])
case double(_ content: [Any])
case triple(_ content: [Any])
case noVal
var associatedValue: Any? {
get {
let mirror = Mirror(reflecting: self)
if let associated = mirror.children.first {
return associated.value
}
print("WARNING: Enum option of \(self) does not have an associated value")
return nil
}
}
}
let row : RowType = .double([1,2,3])
let rowNoVal : RowType = .noVal
row.associatedValue
rowNoVal.associatedValue

Swift: Less code with nested enum cases conforming to equal protocols

I try to write less code in following scenario:
I have this Queryable protocol and a Parameter enum:
protocol Queryable {
var urlQuery: URLQueryItem { get }
}
enum PaginationParameter: Queryable {
case page(Int)
case pageSize(Int)
var queryItem: URLQueryItem {
switch self {
case .page(let page):
return URLQueryItem(name: "page", value: page.description)
case .pageSize(let pageSize):
return URLQueryItem(name: "page_size", value: pageSize.description)
}
}
}
And an enum that provides some default cases and some specific cases defined by a generic type:
enum Parameter<P: Queryable> {
case pagination(PaginationParameter)
case specific(P)
}
Example Usage
enum BookParameters: Queryable {
case search(String)
case id(Int)
var urlQuery: URLQueryItem {
switch self {
case .search(let string):
return URLQueryItem(name: "search", value: string)
case .id(let id):
return URLQueryItem(name: "id", value: id.description)
}
}
}
let parameters: [Parameter<BookParameters>] = [
.pagination(.pageSize(10)),
.specific(.id(123))
]
Now I need to get the url query item through both .pagination and .specific cases.
let queryItems = parameters.map({
switch $0 {
case .pagination(let param):
return param.queryItem
case .specific(let param):
return param.queryItem
}
})
It would be nice to have a way to handle the nested cases combined since they conform to the same protocol. That doesn't work since I have to go to the nested cases through the parent cases:
A small improvement would be to bury the switch statement in an extension for the Parameters enum and let it conform to the Queryable protocol as well:
extension Parameters: Queryable {
let queryItem: URLQueryItem {
switch self {
case .pagination(let param):
return param.queryItem
case .specific(let param):
return param.queryItem
}
}
}
That results in a one liner but I have only shifted my problem to a different place.
let queryItems = parameters.map({ $0.queryItem })
Since you are using nested enums with associated values, I don't really see a way to avoid having this extra switch on the top level Parameter enum. As far as I am concerned, Swift doesn't provide us with a tool to work with cases in such a way where we could cast all cases with the "same" associated value types to a single case. What you could do is to rethink the existence of Parameter type, since it doesn't seem to be really useful due to the fact you still need to refer to it as Parameter<BookParameters> or Parameter<SomeOtherTypeThatConformsToQueryable>.
Personally I would skip the top level enum, and refer to the parameters property type as [Queryable] directly.
var parameters: [Queryable] = [
PaginationParameter.pageSize(10),
BookParameters.id(123)
]
Makes things much more simpler and easier to reason about. Also there is now a way to add other cases of other types, where it would not be possible with your initial solution.
enum SomeOtherTypeThatConformsToQueryable: Queryable {
case aVeryNiceCase(Int)
}
parameters.append(SomeOtherTypeThatConformsToQueryable.aVeryNiceCase(0))
// Appending this to array of type `[Parameter<BookParameters>]`, would not be
// possible without explicitly adding new case to the `Parameter` enumeration
Also if you find yourself calling the map { $0.queryItem } often, you could provide an extension to the Array where Element is type of Queryable
extension Array where Element == Queryable {
var queryItems: [URLQueryItem] { return map { $0.queryItem } }
}
// And now you can simply call
let queryItems = parameters.queryItems
Without conforming Parameters to Queryable, you can just introduce a variable in Parameters to get the queryItem because both cases accept a type that already conform to Queryable,
enum Parameter<P: Queryable> {
case pagination(PaginationParameter)
case specific(P)
var urlQuery: URLQueryItem {
switch self {
case .pagination(let param):
return param.urlQuery
case .specific(let param):
return param.urlQuery
}
}
}

How to access implict closure parameters from higher closure level

I want to iterate an enum and then use $0 in a switch/case statement down one level in a closure that is called in a fetch operation inside the enum iteration loop, as follows:
enum GenericType: CaseIterable {
case purchase
case sale
// etc....
}
Then the code to use is as follows:
GenericType.allCases.forEach {
// let type = $0
Manager.fetchItems(ofType: $0, onSuccess: { (data) in
switch $0 {
case purchase:
// Do something
case sale:
// Do something
}
}
Xcode 10 assumes that $0 refers to data (the parameter in the closure) and gives this error message:
Anonymous closure arguments cannot be used inside a closure that has explicit arguments; did you mean 'data'?
I am able to make it work with before the fetch:
let type = $0
And then using type in the switch/case statement.
Is there a way to access the $0 shorthand argument from the higher level context inside a closure? Is the workaround a feasible solution?
Thx
$0 can only ever refer to the first closure context "up the chain". To access the parameters of outter closures, you need to name them:
GenericType.allCases.forEach { genericType in
Manager.fetchItems(ofType: genericType, onSuccess: { data in
switch genericType {
case .purchase: return
// Do something
case .sale: return
// Do something
}
})
}
Building on #Alexander and #Daniel's answer/inputs, this is the way I implemented the for loop without the switch-case statement.
enum GenericType: CaseIterable {
case purchase
case sale
var manager: GenericManager {
switch self {
case .purchase:
return PurchaseManager.shared
case .sale:
return SalesManager.shared
}
}
}
PurchaseManager and SaleManager are subclasses of GenericManager and they override the processFetchData(_:) method.
And the code is as follows:
GenericType.allCases.forEach { genericType in
FetchManager.fetchItems(ofType: genericType, onSuccess: { data in
genericType.manager.processFetchData(data)
})
}
Strictly speaking I only moved the switch statement from the biz logic to the enum declaration, but it makes it a bit more elegant.

Create an enum with parameter

I have some Outputs in my code so I regrouped all that with an Enum String.
The problem is that I have some Outputs containing variable.
Is It possible to create an Enum who takes variable ?
Exemple with this string
print("The name of the Team is \(team.name)")
I wanted to do something like that:
enum Exemple: String {
case TEAM_NAME(name: String) = "The name of the Team is \(name)"}
print(Exemple.TEAM.NAME("Team 1").rawvalue)
Thank you
It's possible to have an enum with associated values for cases. But in order to get the output you're looking for you will need a function.
enum Example {
case teamName(name: String)
case teamId(id: Int)
func printName() {
switch self {
case .teamName(name: let name):
print(name)
default:
break
}
}
}
let team = Example.teamName(name: "team1")
team.printName() // prints team1
You can define an instance method or computed property for enum that will return a string value in depend of enumeration case and associated value. See example for playground.
enum Example {
case firstItem
case secondItem(withText: String)
var stringValue: String {
switch self {
case .firstItem: return "Simple string"
case .secondItem(withText: let text): return "String with additional text: \(text)"
}
}
}
let myEnumItem: Example = .secondItem(withText: "Test")
let text = myEnumItem.stringValue
It is not possible for Enum to have both raw value and associated value. I think you can get by with the associated value. Without the raw value the enum still can provide you enough information to compose the message

Is there a way to write an `if case` statement as an expression?

Consider this code:
enum Type {
case Foo(Int)
case Bar(Int)
var isBar: Bool {
if case .Bar = self {
return true
} else {
return false
}
}
}
That's gross. I would like to write something like this instead:
enum Type {
case Foo(Int)
case Bar(Int)
var isBar: Bool {
return case .Bar = self
}
}
But such a construct does not seem to exist in Swift, or I cannot find it.
Since there's data associated with each case, I don't think it's possible to implement the ~= operator (or any other helper) in a way that's equivalent to the above expression. And in any case, if case statements exist for free for all enums, and don't need to be manually implemented.
Thus my question: is there any more concise/declarative/clean/idiomatic way to implement isBar than what I have above? Or, more directly, is there any way to express if case statements as Swift expressions?
UPDATE 2:
Another workaround... Create a var that returns an Int ONLY based on the case, then use a static (or instance, I thought static looked cleaner) method to test equivalence of just the case. It won't clash with Equatable, you don't have to overload an operator (unless you want to replace the static method with one), and you also wouldn't have to create separate var isFoo, var isBar, etc.
I know you used this example to ask a more generic question (how can I use 'if case' as an expression?) but if that's not possible, this may be a valid workaround. I apologize if this treats "the symptoms" not "the problem"
enum Something{
case Foo(Int)
case Bar(Int)
static func sameCase(a: Something, b: Something) -> Bool {
return a.caseValue == b.caseValue
}
var caseValue: Int {
switch self {
case .Foo(_):
return 0
case .Bar(_):
return 1
}
}
//if necessary
var isBar: Bool {
return Something.sameCase(self, b: Something.Bar(0))
}
}
Something.sameCase(.Bar(0), b: .Foo(0)) // false
Something.sameCase(.Bar(1), b: .Foo(2)) // false
Something.sameCase(.Foo(0), b: .Foo(0)) // true
Something.sameCase(.Bar(1), b: .Bar(2)) // true
Something.Bar(0).isBar // true
Something.Bar(5).isBar // true
Something.Foo(5).isBar // false
UPDATE 1:
Ok, so this seems to work. If you overload the == operator to ignore values and return true only when both enums are the same case, you can pass any value in your isFoo method and still determine the type.
I'm assuming you will need to customize this function to accommodate the the associated values, but it seems like a step in the right direction
enum Something {
case Foo(Int)
case Bar(Int)
var isFoo: Bool {
return self == .Foo(0) // number doesn't matter here... see below
}
}
func ==(a: Something, b: Something) -> Bool {
switch (a,b) {
case (.Bar(_), .Bar(_)):
return true
case (.Foo(_), .Foo(_)):
return true
default:
return false
}
}
let oneFoo = Something.Foo(1)
let twoFoo = Something.Foo(2)
let oneBar = Something.Bar(1)
let twoBar = Something.Bar(2)
oneFoo == twoFoo // true
oneFoo == oneFoo // true
oneFoo == oneBar // false
oneFoo == twoBar // false
OLD:
You can use self and the case name to directly check which case it is, you don't have to use the case keyword. Hopefully this will work for your situation:
enum Something{
case Foo(Int)
case Bar(Int)
var isFoo: Bool {
switch self {
case Foo:
return true
case Bar:
return false
}
}
}
So, there is a neater way, but requires a 3rd-party package: CasePaths
The idea is they work similarly to KeyPaths, and they come with a / operator to trigger it. There is also a ~= operator to check if a CasePath matches an instance.
So, you can achieve something like your original example like so:
import CasePaths
enum Type {
case Foo(Int)
case Bar(Int)
var isBar: Bool {
/Self.Bar ~= self
}
}
You can also get the value:
extension Type {
/// Returns the `Int` if this is a `Bar`, otherwise `nil`.
var barValue: Int? {
(/Self.Bar).extract(from: self)
}
}
You can do several other useful things with CasePaths as well, such as extracting the Foo values in an array of Type values:
let values: [Type] = [.Foo(1), .Bar(2), .Foo(3), .Foo(4), .Bar(5)]
let foos = values.compactMap(/Type.Foo) // [1, 3, 4]
let bars = values.compactMap(/Type.Bar) // [2, 5]
I'm sure there is somewhat of a performance cost, but it may not be an issue in your context.
I have a similar wondering, and I kept searching for some work arounds about this, and landed on this page. I came up with code like this to compromise.
fileprivate enum TypePrimitive {
case foo
case bar
}
enum Type {
case foo(Int)
case bar(Int)
fileprivate var primitiveType: TypePrimitive {
switch self {
case .foo(_): return .foo
case .bar(_): return .bar
}
}
var isFoo: Bool { self.primitiveType == .foo }
var isBar: Bool { self.primitiveType == .bar }
}
I hope Apple will provide better solution by adding some features in Swift language.
Are you looking for the ? operator ?
documentation is here under the Ternary Conditional Operator title.