Value binding in custom pattern matching

I quite like the ability to overload the pattern matching operator ~=. It allows for some very expressive stuff. I'm wondering if there is any way to bind a custom value for use in the matched switch branch. For example, I'm using Starscream the handy web socket package, and some web socket services that I'm using emit json data in a way that results in me receiving the .binary(Data) enum case while others encode the json data in a way that results in me receiving the .text(String) enum case. I want to match WebSocketEvent values like this:

switch webSocketEvent {
case .connect:
    break

case .json (let jsonDict):
    break

default:
    break
}

where .json is a custom pattern that not only matches either the .binary(Data) case or the .text(String) case if they are in JSON format, but also exposes that decoded, formatted value for binding in the switch case (in this case named jsonDict). Is the example clear? Is the motivation clear? Is this possible in Swift? Is likely to ever be possible (since I'm pretty sure the previous answer is no it's not currently possible)?

It's not possible to add new cases to an existing enum. You can however wrap the event in a new enum that exposes the cases that you are looking for. In this case it's required, since you need to specify the type used by the JSONDecoder:

enum DecodedWebSocketEvent<T: Decodable> {
  case connected([String: String])
  case json(T)
  case other
}

extension WebSocketEvent {
  func decoded<T: Decodable>(as type: T.Type) throws -> DecodedWebSocketEvent<T> {
    switch self {
    case .connected(let headers):
      return .connected(headers)
    case .binary(let data):
      let json = try JSONDecoder().decode(T.self, from: data)
      return .json(json)
    case .text(let string):
      guard let data = string.data(using: .utf8) else { throw MyError() }
      let json = try JSONDecoder().decode(T.self, from: data)
      return .json(json)
    default:
      return .other
    }
  }
}

Then at the use site:

switch try webSocketEvent.decoded(as: MyType.self) {
case .connected: break
case .json(let value): break
default: break
}
1 Like
Terms of Service

Privacy Policy

Cookie Policy