I've some entities (for example hashtags, mentions etc) should be extracted from a text. To make things re-usable I've created some protocols / structures like this:
struct Match {
var value: String
var range: Range<String>
}
protocol Entity {
associatedtype instructions: RegexInstructions
var fullMatch: Match { get }
var partialMatches: [instructions.PartialKeys: Match] { get }
}
// instructions holding keys for regex subgroups and regex string, to understand how an entity should/could be extracted
protocol RegexInstructions {
associatedtype PartialKeys: EntityPartialKeys
var regexString: String { get }
}
typealias EntityPartialKeys = RawRepresentable & Hashable
I've Match
struct, to hold regex matches' value and range.
Protocol Entity
is my container for extracted regex matches.
It's instructions
associated type needed for understanding how an entity should be extracted and how it's variables will be filled.
Now I'm creating some entities:
struct EntityA : Entity {
typealias instructions = InstructionA
var fullMatch: Match
var partialMatches: [instructions.PartialKeys : Match]
}
struct InstructionA: RegexInstructions {
enum PartialKeys: String, EntityPartialKeys {
case x, y, z
}
var regexString: String {
return "A regex string"
}
}
/// and more entities
And a regex executing service class is where I stuck:
class RegExecuter {
private(set) var registered: [Entity.Type] = [] // ---> Protocol 'Entity' can only be used as a generic constraint because it has Self or associated type requirements
let text: String
init(withTexh text: String) {
self.text = text
}
func register(entityType: Entity.Type) {
registered.append(entityType)
}
func searchAll() -> [Entity] { // ---> Protocol 'Entity' can only be used as a generic constraint because it has Self or associated type requirements
var entitiesFound: [Entity] = [] // ---> Protocol 'Entity' can only be used as a generic constraint because it has Self or associated type requirements
for entityType in registered {
entitiesFound.append(contentsOf: Regex.search(entityType, inText: text)) // here I'm using type.instructions to understand how an entity type could be extracted from text and creating an entity of that type
}
return entitiesFound
}
}
let regExecuter = RegExecuter.init(withText: "a text")
// register needed entities
regExecuter.register(EntityA.self)
regExecuter.register(EntityB.self)
regExecuter.register(EntityC.self)
let foundEntities = regExecuter.searchAll()
What I'm trying to do here is something like UITableView's UITableViewCell registration.
Service should know what to search (It's going to search some objects conforming Entity protocol but which ones?) So I'm trying to register Entity's Type
s, so service will know what to search, how to search and how to process found matches.
Since protocols with associatedtypes should be used as generics, I'm stuck. I've tried many things, from type erasure to creating a BaseEntity protocol for Entity protocol, without having associatedtypes, to be able to use a downcasting / upcasting, without luck. Hope someone can help me to achieve this.