Swift - implementing repository pattern with generics - swift

I have been doing some research into repository pattern as want to use it new project I am working on. However I am having issues getting it working with generics
I have been following this guide here as an example
https://medium.com/#frederikjacques/repository-design-pattern-in-swift-952061485aa
Which does a fairly good job of explaining it. However, the guide leaves off one important detail.. which is using dependancy injection with generics.
In example code, he shows this
class ArticleFeedViewModel {
let articleRepo:ArticleRepository
init( articleRepo:ArticleRepository = WebArticleRepository() ) {
self.articleRepo = articleRepo
}
}
Which works fine if you are not using generics. But once you change ArticleRepository to Repository example... so from
protocol ArticleRepository {
func getAll() -> [Article]
func get( identifier:Int ) -> Article?
func create( article:Article ) -> Bool
func update( article:Article ) -> Bool
func delete( article:Article ) -> Bool
}
to this
protocol Repository {
associatedtype T
func getAll() -> [T]
func get( identifier:Int ) -> T?
func create( a:T ) -> Bool
func update( a:T ) -> Bool
func delete( a:T ) -> Bool
}
I can no longer get the dependancy injection working. So If I were to try re-creating the model shown above.
class WebArticleRepository: Repository {
func getAll() -> [Article] {
return [Article()]
}
func get(identifier: Int) -> Article? {
return Article()
}
func create(a: Article) -> Bool {
return true
}
func update(a: Article) -> Bool {
return false
}
func delete(a: Article) -> Bool {
return true
}
}
class ArticleFeedViewModel {
let articleRepo:Repository
init( articleRepo:Repository = WebArticleRepository() ) {
self.articleRepo = articleRepo
}
}
This no longer works. I now get an error saying
Protocol 'Repository' can only be used as a generic constraint because
it has Self or associated type requirements
Any ideas on what I am doing wrong here. It seems adding associatedType causes this to stop working. I would really like to get this functionality working as I want to be able to inject either local or web based repository pattern depending on current state of the app
Any help would be much appriecated

You need to make everything else generic as well:
protocol Repository {
associatedtype RepositoryType
func getAll() -> [RepositoryType]
func get( identifier:Int ) -> RepositoryType?
func create( a:RepositoryType ) -> Bool
func update( a:RepositoryType ) -> Bool
func delete( a:RepositoryType ) -> Bool
}
class WebArticle { }
class WebArticleRepository: Repository {
typealias RepositoryType = WebArticle
func getAll() -> [WebArticle] {
return [WebArticle()]
}
func get(identifier: Int) -> WebArticle? {
return WebArticle()
}
func create(a: WebArticle) -> Bool {
return true
}
func update(a: WebArticle) -> Bool {
return false
}
func delete(a: WebArticle) -> Bool {
return true
}
}
class ArticleFeedViewModel<T : Repository> {
let articleRepo: T
init( articleRepo: T) {
self.articleRepo = articleRepo
}
}
// you cannot have the optional parameter in the init, instead, you can extract the following line to a method
ArticleFeedViewModel(articleRepo: WebArticleRepository())
In Swift you can't use a protocol with associated types as the type of a property/parameter etc. It's supposed to make your code more type-safe.

Related

Swift: Can I have a protocol that inherits from a protocol and constrains it?

Say I have the following protocols:
protocol RateableItem {
var identifier: String { get } // placeholder. This could be a lot of properties
var name: String { get set }
var rating: Int { get set }
}
protocol RateableItemManager {
/// get some objects matching query criteria
func objects(matching query: RateableItemQuery) -> [RateableItem]
/// get a specific object
func object(withID identifier: String) -> RateableItem?
/// persists them
func save(_ object: RateableItem) throws
/// deletes the objects.
func delete(_ objects: [RateableItem])
/// creates a new object.
func create() -> RateableItem
}
and
struct RateableItemQuery {
let searchPredicate: NSPredicate? // nil means all
let sortingBlock: ((RateableItem, RateableItem) throws -> Bool)?
let groupingSpecifier: (() -> String)?
init(matching predicate: NSPredicate? = nil,
sort: ((RateableItem, RateableItem) throws -> Bool)? = nil,
groupBy: (() -> String)? = nil) {
self.searchPredicate = predicate
self.sortingBlock = sort
self.groupingSpecifier = groupBy
}
}
I can now implement a concrete type of this which returns concrete types that conform to the protocol. The concrete types that are returned are irrelevant to the rest of my code because the rest of the code only cares that they conform to a protocol. This enables me to make 'production' and 'dummy' versions of the models.
Is there a way where I can define this more generally, such as:
struct Query<T> {
let searchPredicate: NSPredicate? // nil means all
let sortingBlock: ((T, T) throws -> Bool)?
let groupingSpecifier: (() -> String)?
init(matching predicate: NSPredicate? = nil,
sort: ((T, T) throws -> Bool)? = nil,
groupBy: (() -> String)? = nil) {
self.searchPredicate = predicate
self.sortingBlock = sort
self.groupingSpecifier = groupBy
}
}
such that
struct RateableItemQuery: Query<RateableItem> {}
and
protocol ItemManager<T> {
func objects(matching query: Query<T>) -> [T]
func object(withID identifier: String) -> T?
func save(_ object: T) throws
func delete(_ objects: [T])
func create() -> T
}
and
protocol RateableItemManager: ItemManager<RateableItem>
Because I want to use this API paradigm, but don't necessarily want to constrain anything at the 'base protocol' level, as I would often just be re-writing these method signatures for the various Protocol-types I'd want to work with.
If I'm not mistaken, associated types have to be concrete, making their return types also concrete, and then I can't easily work with protocol types.
Sorry if I didn't speak "canonically". I hope I was able to convey my intentions.
Could this be what upcoming Swift 5.1 is offering in terms of opaque types, returning -> some ProtocolType ?
So it turns out Opaque types aren't the answer either.
I solved my problem by making abstract baseclasses for Managers, the Query and QueryResults were generic structs, and the concrete subclasses for the Managers could take and return protocol-based Data types.
public struct Query<T> {
var searchPredicate: NSPredicate?
var sortingBlock: ((T, T) throws -> Bool)?
var groupingSpecifier: (() -> String)?
var results: QueryResults<T>?
init(matching predicate: NSPredicate?,
sort: ((T, T) throws -> Bool)?,
groupBy: (() -> String)?) {
}
}
public struct QueryResults<T> {
public enum ChangeType: UInt {
case insert = 1
case delete
case move
case update
}
public struct Section<T> {
var items: [T]
var title: String?
}
public var sections: [Section<T>] = []
public func object(at indexPath: IndexPath) -> T? {
return nil
}
}
public class AnyObjectManager<ObjectType> {
public enum Error: Swift.Error {
case abstractImplementationRequiresOverride
}
typealias QueryDidChangeObjectBlock = ((
_ query: Query<ObjectType>,
_ didChangeObject: ObjectType,
_ atPath: IndexPath?,
_ forChangeType: QueryResults<ObjectType>.ChangeType,
_ newIndexPath: IndexPath?) -> Void)
typealias QueryDidChangeSectionBlock = ((
_ query: Query<ObjectType>,
_ didChangeSection: QueryResults<ObjectType>.Section<ObjectType>,
_ atSectionIndex: Int,
_ forChangeType: QueryResults<ObjectType>.ChangeType) -> Void)
/// get some objects matching query criteria. nil means return all
func objects(matching query: Query<ObjectType>?) -> [ObjectType] {
fatalError("Abstract implementation. You need to override this method and provide an implementation!")
}
/// get a specific object
func object(withID identifier: String) -> ObjectType? {
fatalError("Abstract implementation. You need to override this method and provide an implementation!")
}
/// deletes the objects. Does it commit that to disk?
func remove(_ objects: [ObjectType]) {
fatalError("Abstract implementation. You need to override this method and provide an implementation!")
}
/// creates a new object but does not save it.
func create() -> ObjectType {
fatalError("Abstract implementation. You need to override this method and provide an implementation!")
}
/// this is basically to mimic the functionality of a fetched results controller...
func monitorQuery(_ query: Query<ObjectType>,
willChangeBlock: ((_ query: Query<ObjectType>) -> Void)?,
didChangeObjectBlock: QueryDidChangeObjectBlock?,
didChangeSectionBlock: QueryDidChangeSectionBlock?,
didFinishChangesBlock:((_ query: Query<ObjectType>) -> Void)?) {
fatalError("Abstract implementation. You need to override this method and provide an implementation!")
}
/// and this is to stop monitoring that.
func stopMonitoringQuery() {
fatalError("Abstract implementation. You need to override this method and provide an implementation!")
}
public func saveChanges() throws {
fatalError("Abstract implementation. You need to override this method and provide an implementation!")
}
public func discardChanges() throws {
fatalError("Abstract implementation. You need to override this method and provide an implementation!")
}
}

Is it possible to have a same collection instance in a dictionary associated with multiple keys in swift?

I have a Set instance and want to put it into a Dictionary, and associate it with multiple keys so I can lookup/modify it in the future.
Following Python code is what I want to achieve in Swift.
s = set()
D = {}
D["a"] = s
D["b"] = s
D["a"].add("Hello")
D["a"].add("World")
print(D["b"]) # getting {"Hello", "World"} back
I tried something like following in Swift.
var s = Set<String>()
var D = Dictionary<String, Set<String>>()
D["a"] = s // copy of s is assigned
D["b"] = s // another copy of s is assigned
D["a"]!.insert("Hello")
D["a"]!.insert("World")
print(D["b"]!) // empty :(
Since collections in Swift hold value semantics, by the time I put a set into a dictionary, new instance is created. Is there any workaround? I know I could use NSMutableSet instead of Swift's Set, but I want to know how I can approach this by using collections with value semantics if possible.
Ah! Now we get to the heart of it. You just want a reference type based on stdlib rather than using the one that Foundation gives you. That's straightforward to implement, if slightly tedious. Just wrap a Set in a class. If you don't want full SetAlgebra or Collection conformance, you don't have to implement all of these methods. (And you might want some more init methods to make this more convenient, but hopefully those implementations are fairly obvious from your code needs.)
final class RefSet<Element> where Element: Hashable {
private var storage: Set<Element> = Set()
init() {}
}
extension RefSet: Equatable where Element: Equatable {
static func == (lhs: RefSet<Element>, rhs: RefSet<Element>) -> Bool {
return lhs.storage == rhs.storage
}
}
extension RefSet: SetAlgebra {
var isEmpty: Bool { return storage.isEmpty }
func contains(_ member: Element) -> Bool {
return storage.contains(member)
}
func union(_ other: RefSet<Element>) -> RefSet<Element> {
return RefSet(storage.union(other.storage))
}
func intersection(_ other: RefSet<Element>) -> RefSet<Element> {
return RefSet(storage.intersection(other.storage))
}
func symmetricDifference(_ other: RefSet<Element>) -> RefSet<Element> {
return RefSet(storage.symmetricDifference(other.storage))
}
#discardableResult
func insert(_ newMember: Element) -> (inserted: Bool, memberAfterInsert: Element) {
return storage.insert(newMember)
}
#discardableResult
func remove(_ member: Element) -> Element? {
return storage.remove(member)
}
#discardableResult
func update(with newMember: Element) -> Element? {
return storage.update(with: newMember)
}
func formUnion(_ other: RefSet<Element>) {
storage.formUnion(other.storage)
}
func formIntersection(_ other: RefSet<Element>) {
storage.formIntersection(other.storage)
}
func formSymmetricDifference(_ other: RefSet<Element>) {
storage.formSymmetricDifference(other.storage)
}
}
extension RefSet: Collection {
typealias Index = Set<Element>.Index
var startIndex: Index { return storage.startIndex }
var endIndex: Index { return storage.endIndex }
subscript(position: Index) -> Element {
return storage[position]
}
func index(after i: Index) -> Index {
return storage.index(after: i)
}
}

How do I create classes that conform to protocols with nested PATs?

I'm attempting to use type-erasure to create a Repository protocol that can be conformed to (similar to Swift's AnyCollection). This protocol needs to be wrapped in a type-erased class because it contains a PAT.
However, since this protocol has a nested protocol which also has a PAT, it somewhat complicates things.
Keyable is something that provides a key (which eventually I'd like to ensure is also Hashable... one thing at a time).
Repository is my generalized "container" protocol
AnyKeyable type-erased wrapper for Keyable
AnyRepository type-erased wrapper for Repository
I’ve got a playground snippet that ALMOST compiles:
protocol Keyable {
associatedtype KeyType// where KeyType: Hashable
func key() -> KeyType
}
protocol Repository {
associatedtype DataType: Keyable
func all() -> [DataType]
func get(id: DataType.KeyType) throws -> DataType?
func create(object: DataType) throws -> Bool
func update(object: DataType) throws -> Bool
func delete(object: DataType) throws -> Bool
func clear() -> Bool
}
final class AnyKeyable<T>: Keyable {
private let _key: () -> T
init<U: Keyable>(_ keyable: U) where U.KeyType == T {
_key = keyable.key
}
public func key() -> T {
return _key()
}
}
final class AnyRepository<T: Keyable>: Repository {
private let _all: () -> [T]
private let _get: (_ id: T.KeyType) throws -> T?
private let _create: (_ object: T) throws -> Bool
private let _update: (_ object: T) throws -> Bool
private let _delete: (_ object: T) throws -> Bool
private let _clear: () -> Bool
init<U: Repository>(_ repository: U) where U.DataType == T {
_get = repository.get
_create = repository.create
_delete = repository.delete
_update = repository.update
_clear = repository.clear
_all = repository.all
}
func all() -> [T] {
return _all()
}
func get<K: Keyable>(id: K.KeyType) throws -> T? where T.KeyType: Keyable, T.KeyType == K.KeyType {
let anyKeyable = AnyKeyable(id)
return try _get(anyKeyable)
}
func create(object: T) throws -> Bool {
return try _create(object)
}
func update(object: T) throws -> Bool {
return try _update(object)
}
func delete(object: T) throws -> Bool {
return try _delete(object)
}
func clear() -> Bool {
return _clear()
}
}
final class Contact {
var name: String = ""
var email: String = ""
}
extension Contact: Keyable {
public typealias KeyType = String
public func key() -> String {
return "im the key"
}
}
// Just a dummy class to see
final class ContactRepository: Repository {
typealias DataType = Contact
private var someContacts: [Contact] = []
func all() -> [Contact] {
return someContacts
}
func clear() -> Bool {
someContacts.removeAll()
return someContacts.count == 0
}
func get(id: Contact.KeyType) throws -> Contact? {
return nil
}
func update(object: Contact) throws -> Bool {
return false
}
func create(object: Contact) throws -> Bool {
return false
}
func delete(object: Contact) throws -> Bool {
return false
}
}
// Testing
let i = AnyRepository<Contact>(ContactRepository())
i.all()
i.clear()
The problem with that is the Swift compiler complains that I’m not using K in the method signature of get<K: Keyable>(id: K.KeyType)... but it looks to me like I definitely am.
My thought is that the compiler is complaining because of the DataType.KeyType declaration of get() in the protocol, rather than in the AnyRepository concrete subclass, but I'm uncertain on how to correct this to provide more context for the compiler.
Is there a better way to structure this to allow me to accomplish this pattern? What about also allowing for the first associatedtype in Keyable to become associatedtype KeyType where KeyType: Hashable?
Any help is greatly appreciated,
Thank you!
As Hamish pointed out, there was no need to introduce another generic, K. That was complicating things. Cleaning it all up, this now works without the complication of an AnyKeyable class:
public protocol Keyable {
associatedtype KeyType where KeyType: Hashable
func key() -> KeyType
}
public protocol Repository {
associatedtype DataType: Keyable
func all() -> [DataType]
func get(id: DataType.KeyType) throws -> DataType?
func create(object: DataType) throws
func create(objects: [DataType]) throws
func update(object: DataType) throws
func delete(object: DataType) throws
func delete(objects: [DataType]) throws
func clear()
}
public class AnyRepository<T: Keyable>: Repository {
private let _all: () -> [T]
private let _get: (_ id: T.KeyType) throws -> T?
private let _create: (_ object: T) throws -> Void
private let _createAll: (_ objects: [T]) throws -> Void
private let _update: (_ object: T) throws -> Void
private let _delete: (_ object: T) throws -> Void
private let _deleteAll: (_ objects: [T]) throws -> Void
private let _clear: () -> Void
public init<R: Repository>(_ repository: R) where R.DataType == T {
_get = repository.get
_create = repository.create
_createAll = repository.create
_delete = repository.delete
_deleteAll = repository.delete
_update = repository.update
_clear = repository.clear
_all = repository.all
}
public func all() -> [T] {
return _all()
}
public func get(id: T.KeyType) throws -> T? {
return try _get(id)
}
public func create(object: T) throws {
return try _create(object)
}
public func create(objects: [T]) throws {
return try _createAll(objects)
}
public func update(object: T) throws {
return try _update(object)
}
public func delete(object: T) throws {
return try _delete(object)
}
public func delete(objects: [T]) throws {
return try _deleteAll(objects)
}
public func clear() {
return _clear()
}
}

Can I specialize a generic method implementation in Swift?

I would like to implement a generic function in two ways, one of them specialized to a specific generic value, but so far I was not able to express this in Swift 3.
My setup is like this:
protocol Convertible {
func convert<T>() -> Converted<T>
}
struct Unconverted: Convertible {
func convert<T>() -> Converted<T> {
return Converted<T>()
}
}
struct Converted<T>: Convertible {
func convert<T2>() -> Converted<T2> {
return Converted<T2>()
}
}
What I would like to achieve is to add a special case for convert() on Converted<T>, for the case where T2 == T, so the following behavior is achieved:
let unconverted: Convertible = Unconverted()
let converted: Convertible = unconverted.convert() as Converted<String>
// double conversion to same generic type is ok, should ideally return self
let convertedOk = converted.convert() as Converted<String>
// re-conversion to another type is not ok and should fail
let convertedFail = converted.convert() as Converted<Int>
I tried the following approaches, but none of them worked out:
struct Converted<T> {
func convert<T2>() -> Converted<T2> {
fatalError("already converted")
}
// error: Same-type requirement makes generic parameters 'T2' and 'T' equivalent
func convert<T2>() -> Converted<T2> where T2 == T {
return self
}
}
struct Converted<T> {
func convert<T2>() -> Converted<T2> {
fatalError("already converted")
}
// method is never called, even when T2 == T
func convert() -> Converted<T> {
return self
}
}
struct Converted<T> {
func convert<T2>() -> Converted<T2> {
// does not call the specialized method, even when T2 == T
self.internalConvert(type: T2.self)
}
private func internalConvert<T2>(type: T2.Type) -> Converted<T2> {
fatalError("already converted")
}
private func internalConvert(type: T.Type) -> Converted<T> {
return self
}
}
Is there a way to express this?

How to call a swift function in JS that returns a value?

The following piece of code works like a charm to define a function in Swift (2.0) that I can call from a Javascript resource (tvos). The function storeSetPackageInfo accepts a parameter and returns nothing.
I am trying to understand how I achieve the same goal with a function that accept no parameters and returns a boolean. I don't seem to understand the syntax.
private typealias JavascriptClosure = (JSContext) -> Void
private typealias ObjectivecCompletionBlock = #convention(block) (String) -> Void
func setupStoreSetPackageInfo() {
let selectComponent: JavascriptClosure = {
[unowned self](context: JSContext) -> Void in
let objCompletion: ObjectivecCompletionBlock = {
(str: String) -> Void in
(self.delegate as? myTVAppControllerDelegate)?.storeSetPackageInfo(str)
}
context.setObject(unsafeBitCast(objCompletion, AnyObject.self), forKeyedSubscript: "storeSetPackageInfo")
}
evaluateInJavaScriptContext(selectComponent, completion: nil)
}
I tried multiple approaches which compile but resulting in the JSContext in not finding the function. Any help is very appreciated.
I described one possible way just yesterday in another context: How to retrieve values from settings.bundle in TVML?
AppDelegate.swift
func appController(appController: TVApplicationController, evaluateAppJavaScriptInContext jsContext: JSContext) {
let jsInterface: cJsInterface = cJsInterface();
jsContext.setObject(jsInterface, forKeyedSubscript: "swiftInterface")
}
JsInterface.swift
#objc protocol jsInterfaceProtocol : JSExport {
func getSetting(setting: String) -> String
}
class cJsInterface: NSObject, jsInterfaceProtocol {
func getSetting(setting: String) -> String {
return "<yourSetting>"
}
}
on the JS side...
swiftInterface.getSetting(...)
It's definitely a different syntax compared to your example, but known to work. See https://github.com/iBaa/PlexConnectApp.
After multiple attempts, I found it, the answer and the solution was in front of me at all time... I had tried before but I eventually I had other messy attempts around. To benefit whoever runs into this problems, here is the solution for any signature
private typealias ObjectivecCompletionBlock = #convention(block) () -> Bool
the completion block must match the signature with
() -> Bool in
Therefore the final code is
private typealias JavascriptClosure = (JSContext) -> Void
private typealias ObjectivecCompletionBlock = #convention(block) () -> Bool
func setupStoreSetPackageInfo() {
let selectComponent: JavascriptClosure = {
[unowned self](context: JSContext) -> Void in
let objCompletion: ObjectivecCompletionBlock = {
() -> Bool in
(self.delegate as? myTVAppControllerDelegate)?.storeSetPackageInfo(str)
}
context.setObject(unsafeBitCast(objCompletion, AnyObject.self), forKeyedSubscript: "storeSetPackageInfo")
}
evaluateInJavaScriptContext(selectComponent, completion: nil)
}
Again really straightforward (once you pull the head out of the bucket...)