How to make a constrained extension of Collection generic in Swift? - swift

Context
I have a class Profile which conforms to protocol Entity. I am also adding a search method to all Swift Collections where the Element is a Profile.
However, I would like to make this generic to support not only Profile but every object conforming to Entity.
Code
protocol Entity {
var name: String { get }
}
class Profile: Entity { ... }
extension Collection where Element == Profile {
func searched(with search: String) -> [Profile] {
guard !(search.isEmpty) else { return self }
return self.filter { ($0.name.lowercased().contains(search.lowercased())) }
}
}
Question
How can I make the search method generic to support all objects conforming to Entity?

first, you should change the extension to the below code
extension Collection where Iterator.Element: Entity
next, change the return type of the method to
func searched(with search: String) -> [Iterator.Element]
Finally, the extension can be like this:
extension Collection where Iterator.Element: Entity {
func searched(with search: String) -> [Iterator.Element] {
guard !(search.isEmpty) else { return [Iterator.Element]() }
return self.filter { ($0.name.lowercased().contains(search.lowercased())) }
}
}

Related

How to conform to a protocol with a generic superscript affordance in Swift?

TL;DR
How can I conform to the supscript function of a protocol in my implementation?
Protocol:
protocol DataStore {
//...
subscript<T>(id: T.ID) -> T where T: Identifiable { get set }
}
Neither
subscript<T>(id: T.ID) -> T where T : Identifiable {
get { Project() }//just to return anything for the time being…
set {}
}
nor
subscript(id: Task.ID) -> Task {
get { Project() }//just to return anything for the time being…
set {}
}
work...
The details:
I have developed a habit of creating specific data stores for my models. They all have the same functionality. A specific example could look like this:
final class ProjectDataStore: ObservableObject {
{
static let shared = ProjectDataStore()
let persistence = AtPersistLocally.shared // that's from a personal package I made that handles saving and loading locally as json files
#Published var projects: [Project] {
didSet { //save }
}
private init(projects: [Project]? = nil) {
//load from persistence
}
subscript(id: Project.ID) -> Project? {
get { //return project with id }
set { //set project at id }
}
func getBinding(by id: Project.ID) -> Binding<Project> {
//return Binding
}
func getProjectBy(taskId: Task.ID) -> Project {
//return instance
}
private func getIndex(by id: Project.ID) -> Int? {
//return index in array
}
private func load() -> [Project] {
//load from persistence
}
private func save() {
//save from persistence
}
}
While this works as expected, I'd like to be able to introduce a protocol that I could when adding new functionality / models to have a blueprint on what's necessary for my DataStore.
Here is my first attempt:
protocol DataStore {
static var shared: Self { get }
}
extension DataStore {
var persistence: AtPersistLocally {
get {
AtPersistLocally.shared
}
}
}
To also conform to ObservableObject, I introduce a typealias
typealias ObservableObjectDataStore = DataStore & ObservableObject
and change my model to conform to this new typealias:
final class ProjectDataStore: ObservableObjectDataStore {
//...
}
With this, I already have a static instance and access to persistence available.
Next step of course is to move more and more properties to the protocol–which is what I am struggling with right now.
Let's look at superscript first of all: I guess I understand what needs to be added to the protocol:
protocol DataStore {
//...
subscript<T>(id: T.ID) -> T where T: Identifiable { get set }
}
My problem now is that I don't know how to go about conforming to this subscript now while also getting access to a concrete model from the generic T from the implementation. This attempt…
final class ProjectDataStore: ObservableObjectDataStore {
//...
subscript<T>(id: T.ID) -> T where T : Identifiable {
get { Project() }//just to return anything for the time being…
set {}
}
}
…leads to the error message Cannot convert return expression of type 'Task' to return type 'T'.
If I go with…
final class ProjectDataStore: ObservableObjectDataStore {
//...
subscript(id: Task.ID) -> Task {
get { Project() }//just to return anything for the time being…
set {}
}
}
…the error message changes to Type 'TaskDataStore' does not conform to protocol 'DataStore'.
So I guess basically what I am asking is: how can I conform to my protocol's generic superscript in my implementation of ProjectDataStore?
I have a feeling that I am not too far of, but a critical info is obviously missing…
subscript<T>(id: T.ID) -> T where T: Identifiable { get set }
This says that the caller may pick any Identifiable T, and this method promise return a value of that type. This can't be implemented (other than calling fatalError() and crashing). Identifiable doesn't even promise that the type has an init. Your protocol is impossible.
What you probably meant to write is, which says that a given DataStore will return some specific Identifiable type (not "whatever type the caller requests"):
protocol DataStore {
associatedType Item: Identifiable
subscript(id: Item.ID) -> Item { get set }
}
But I expect this would be much better implemented without a protocol, and just use a generic struct:
struct DataStore<Item: Identifiable> { ... }

Why in Swift generics factory method does compiler warn Generic class 'X' requires that 'Object' conforms to 'Y'

As an overview, I'm trying to implement a data abstraction layer in Swift. I'm using two database SDKs but I'm trying to be able to isolate their specific APIs from the rest of the system.
I'm trying to implement a factory pattern that will return the correct object based on a protocol conformance of the supplied concrete type. But the compiler is giving me red flags that I can't wrap my head around.
The Thing class is a first class entity that moves freely through the logic and UI layers of the app, and certain objects persist on a Realm store (which shouldn't matter) and have a specific type that is a subclass of the Realm object. That type is returned by a static function that has to be there to conform with the protocol FromDataSourceA.
protocol FromDataSourceA {
static func A_objectType () -> A_Object.Type
}
class MyObject {
...
}
class Thing: MyObject, FromDataSourceA {
static func A_objectType () -> A_Thing.Type
...
}
// Example usage
let target = DataTarget<Thing>(url: "url")
let dataSource = target.dataSourceBuilder()
As you can see, the concrete type Thing conforms to FromDataSourceA and is passed into the DataTarget initializer. DataSourceBuilder needs to be able to check that Thing conforms to FromDataSourceA to be able to return the correct DataSource.
The DataSourceTypeA class looks like
class DataSourceTypeA<T: MyObject & FromDataSourceA>: DataSource<T> {
let db: DatabaseTypeA // All db-specific APIs contained here
override init (target: DataTarget<T>) {
self.db = try! DatabaseTypeA()
super.init(target: target)
}
override func create<T: MyObject & FromDataSourceA> (object: T) {
db.beginWrite()
db.create(T.A_objectType(), value: object.dict()) // <-- T.A_objectType() is what the conformance to FromDataSourceA is needed for
try! db.commitWrite()
}
override func get<T: MyObject & FromDataSourceA>(id: Int) -> T {
...
}
}
class DataSource<T>: AnyDataSource {
let target: DataTarget<T>
init (target: DataTarget<T>) {
self.target = target
}
func create<T> (object: T) { }
func get<T>(id: Int) -> T { fatalError("get(id:) has not been implemented") }
}
protocol AnyDataSource {
func create<T> (object: T)
func get<T> (id: Int) -> T
}
The problem I'm facing right now is when I check that the metatype conforms to FromDataSourceA, the compiler gives me this warning
class DataTarget<T: MyObject> {
...
func dataSourceBuilder () -> DataSource<T> {
if T.self is FromDataSourceA { // <-- Thing.self conforms to FromDataSourceA
// The Problem:
return DataSourceTypeA(target: self) // Generic class 'DataSourceTypeA' requires that 'MyObject' conform to 'FromDataSourceA'
} else {
...
}
}
}
Why won't the compiler let me return the DataSourceTypeA instance with argument self if the generic T passes the conditional statement and is proven as being in conformance with FromDataSourceA?
The problem is that the call
return DataSourceTypeA(target: self)
is resolved at compile time, therefore it does not help to check
if T.self is FromDataSourceA { }
at runtime. A possible solution is to make the conformance check a compile-time check by defining a restricted extension:
extension DataTarget where T: FromDataSourceA {
func dataSourceBuilder() -> DataSource<T> {
return DataSourceTypeA(target: self)
}
}
If necessary, you can add more implementations for other restrictions:
extension DataTarget where T: FromDataSourceB {
func dataSourceBuilder() -> DataSource<T> {
// ...
}
}
or add a default implementation:
extension DataTarget {
func dataSourceBuilder() -> DataSource<T> {
// ...
}
}
For a call
let dataSource = target.dataSourceBuilder()
the compiler will pick the most specific implementation, depending on the static (compile-time) type information of target.

How can I type check a generic then cast to it in swift?

I like the ideas presented in this post, about making database-agnostic, protocol-oriented code.
So say I have a protocol such as:
protocol Database {
func loadObjects<T>(matching query: Query) -> [T]
func loadObject<T>(withID id: String) -> T?
func save<T>(_ object: T)
}
where Query is a struct that has filter and sort specifiers.
Then, given a persistence framework, such as Realm or CoreData, I can just support this protocol, as such:
extension NSManagedObjectContext: Database {
...
}
extension Realm: Database {
...
}
extension MockedDatabase: Database {
...
}
extension UITestingDatabase: Database {
...
}
The issue arises when I would want to use CoreData.
If we look at the method:
func loadObjects<T>(matching query: Query) -> [T]
I have no way to 'cast' T to NSManagedObject.
For example, my desired implementation might be something like:
extension NSManagedObjectContext: Database {
func loadObjects<T>(matching query: Query<T>) -> [T] {
// you need a fetch request for these models. This guard statement compiles. How do we make it work with NSFetchRequestResult however?
guard T.self is NSManagedObject.Type else {
return []
}
// This line below Fails compiling. Type 'T' does not conform to protocol 'NSFetchRequestResult'
var request = NSFetchRequest<T>(entityName: String(describing: T))
// then set sortDescriptors and predicate, etc.
var objects: [T] = []
self.performAndWait {
do {
if let results = try self.fetch(request!) as? [T] {
objects = results
}
} catch let error {
print("Error fetching: \(error.localizedDescription)")
}
}
return objects
}
}
So if I can determine if T's Type is a kind of NSManagedObject, then is it possible to instantiate a NSFetchRequest by 'casting' T to something that will work? It would seem I can't cast or force T to be anything.
Database is a technology-agnostic protocol and therefore shouldn't know anything about Core Data. I'd like to do this in the event I need to change my data persistence framework.
How might I accomplish this in Swift? Would I have to add to the Model protocol to return optionals that would be used for the given frameworks I would support? Or make them support NSFetchRequestResult? I would rather that only the implementation of the protocol need to care about the details of the persistence framework.
Rather than checking the type at runtime constrain the type at compile time, for example
extension NSManagedObjectContext: Database {
func loadObjects<T: NSManagedObject>(matching query: Query<T>) -> [T] {
let request = NSFetchRequest<T>(entityName: String(describing: T.self))
var objects = [T]()
self.performAndWait {
do {
objects = try self.fetch(request) // a generic fetch request returns an array of the generic type or throws an error
} catch let error {
print("Error fetching: \(error.localizedDescription)")
}
}
return objects
}
}
It looks like I can answer my own question. One can further constrain generic types by overloading them, so that the calling code can remain the same, but what gets called is dependent on the type you pass into it.
This code below demonstrates this in a playground:
public protocol Action {
func doSomething<T>(to object: T)
}
public class MyActor {
}
extension MyActor: Action {
// works for any type
public func doSomething<T>(to object: T) {
print("was generic")
}
// but if you constrain the type and your object fits that constraint...
// this code is called (same method signature)
public func doSomething<T: NSObject>(to object: T) {
print("was an object")
}
}
class MyObject: NSObject {
var name: String = "Object"
}
struct MyStruct {
var name: String = "Struct"
}
let actor = MyActor()
let object = MyObject()
let value = MyStruct()
actor.doSomething(to: value) // prints 'was generic'
actor.doSomething(to: object) // prints 'was an object'
So in the original example, I would then support Database for CoreData with:
extension NSManagedObjectContext: Database {
func loadObjects<T>(matching query: Query<T>) -> [T] {
return [] // return an empty array because we only support NSManagedObject types
}
func loadObjects<T: NSManagedObject>(matching query: Query<T>) -> [T] {
let request = NSFetchRequest<T>(entityName: String(describing: T.self))
var objects = [T]()
self.performAndWait {
do {
objects = try self.fetch(request) // a generic fetch request returns an array of the generic type or throws an error
} catch let error {
print("Error fetching: \(error.localizedDescription)")
}
}
return objects
}
}

Returning a nil from an optional generic extension

Here's something I'm playing with. The problem is that I have a container class that has a generic argument which defines the type returned from a closure. I want to add a function that is only available if they generic type is optional and have that function return a instance containing a nil.
Here's the code I'm currently playing with (which won't compile):
open class Result<T>: Resolvable {
private let valueFactory: () -> T
fileprivate init(valueFactory: #escaping () -> T) {
self.valueFactory = valueFactory
}
func resolve() -> T {
return valueFactory()
}
}
public protocol OptionalType {}
extension Optional: OptionalType {}
public extension Result where T: OptionalType {
public static var `nil`: Result<T> {
return Result<T> { nil } // error: expression type 'Result<T>' is ambiguous without more context
}
}
Which I'd like to use like this:
let x: Result<Int?> = .nil
XCTAssertNil(x.resolve())
Any idea how to make this work?
I don't think you can achieve this with a static property, however you can achieve it with a static function:
extension Result {
static func `nil`<U>() -> Result where T == U? {
return .init { nil }
}
}
let x: Result<Int?> = .nil()
Functions are way more powerful than properties when it comes to generics.
Update After some consideration, you can have the static property, you only need to add an associated type to OptionalType, so that you'd know what kind of optional to have for the generic argument:
protocol OptionalType {
associatedtype Wrapped
}
extension Optional: OptionalType { }
extension Result where T: OptionalType {
static var `nil`: Result<T.Wrapped?> {
return Result<T.Wrapped?> { nil }
}
}
let x: Result<Int?> = .nil
One small downside is that theoretically it enables any kind of type to add conformance to OptionalType.

How to create an array of instances of a subclass from a superclass

From this answer, I know that I can create an instance of a subclass from a superclass. Yet, I can't figure out how to create an array of the subclass from the superclass.
Drawing on the above example, here's my best shot so far:
class Calculator {
func showKind() { println("regular") }
required init() {}
}
class ScientificCalculator: Calculator {
let model: String = "HP-15C"
override func showKind() { println("\(model) - Scientific") }
required init() {
super.init()
}
}
extension Calculator {
class func createMultiple<T:Calculator>(num: Int) -> T {
let subclass: T.Type = T.self
var calculators = [subclass]()
for i in 0..<num {
calculators.append(subclass())
}
return calculators
}
}
let scis: [ScientificCalculator] = ScientificCalculator.createMultiple(2)
for sci in scis {
sci.showKind()
}
With that code, the line var calculators = [subclass]() shows the error Invalid use of '()' to call a value of non-function type '[T.Type]'.
How can I return an array of ScientificCalculators from Calculator.createMultiple?
You were on the right track but you've made some mistakes.
First you need to return a array of T and not just a single element. So you need to change the return type from T to [T]:
class func createMultiple<T:Calculator>(num: Int) -> [T] {
Also you can just use T to initialize new instances of your subclass like that:
var calculators:[T] = [T]()
But the other parts are correct. So you final method would look like that:
extension Calculator {
class func createMultiple<T:Calculator>(num: Int) -> [T] {
let subclass: T.Type = T.self
var calculators = [T]()
for i in 0..<num {
calculators.append(subclass())
}
return calculators
}
}
Edit
If you are using Swift 1.2 you don't have to deal with subclass anymore and you will be able to use T instead like shown in Airspeeds answer.
calculators.append(T())
EDIT: this behaviour appears to have changed in the latest Swift 1.2 beta. You shouldn’t need to use T.self. T is the type you want to create. But if you are using 1.1, it appears not to work (even if T is the subtype, it creates the supertype), and using the metatype to create the type works around this problem. See end of answer for a 1.1 version.
You don’t need to mess with subclass: T.Type = T.self. Just use T – that itself is the type (or rather, a placeholder for whatever type is specified by the caller):
extension Calculator {
// you meant to return an array of T, right?
class func createMultiple<T: Calculator>(num: Int) -> [T] {
// declare an array of T
var calculators = [T]()
for i in 0..<num {
// create new T and append
calculators.append(T())
}
return calculators
}
}
btw, you can replace that for loop with map:
class func createMultiple<T: Calculator>(num: Int) -> [T] {
return map(0..<num) { _ in T() }
}
If you are still on Swift 1.1, you need to use T.self to work around a problem where the subtype is not properly created:
extension Calculator {
// only use this version if you need this to work in Swift 1.1:
class func createMultiple<T: Calculator>(num: Int) -> [T] {
let subclass: T.Type = T.self
return map(0..<num) { _ in subclass() }
}
}