I am new to Swift, but I have plenty of experience in other languages like Java, Kotlin, Javascript, etc. It's possible that what I want to do is not supported by the language, and I've poured over the Swift Language Guide looking for the answer.
I want to implement the decorator pattern, using generics. I easily did this in Kotlin, and I'm porting the library to Swift.
class Result<T> {
let result: T?
let error: NSError?
init(result: T?, error: NSError?) {
self.result = result
self.error = error
}
}
protocol DoSomething {
associatedtype T
func doSomething() -> Result<T>
}
protocol StoreSomething {
associatedtype T
func storeSomething(thing: Result<T>)
}
/*
* DOES NOT COMPILE
*/
class StoringSomething<T> {
private let delegate: DoSomething
private let store: StoreSomething
init(delegate: DoSomething, store: StoreSomething) {
self.delegate = delegate
self.store = store
}
func doSomething() -> Result<T> {
let result = delegate.doSomething()
store.storeSomething(thing: result)
return result
}
}
I get a Protocol 'DoSomething' can only be used as a generic constraint because it has Self or associated type requirements error from the compiler. I've tried using a typealias and other ideas from SO and the Swift manual.
Thanks to #Sweeper's suggestion on associatedtype erasure you can implement the Decorator pattern with generics like so:
class AnyDoSomething<T>: DoSomething {
func doSomething() -> Result<T> {
fatalError("Must implement")
}
}
class AnyStoreSomething<T>: StoreSomething {
func storeSomething(thing: Result<T>) {
fatalError("Must implement")
}
}
class StoringSomething<T>: DoSomething {
private let delegate: AnyDoSomething<T>
private let store: AnyStoreSomething<T>
init(delegate: AnyDoSomething<T>, store: AnyStoreSomething<T>) {
self.delegate = delegate
self.store = store
}
func doSomething() -> Result<T> {
let result = delegate.doSomething()
store.storeSomething(thing: result)
return result
}
}
class DoSomethingNice<T>: AnyDoSomething<T> {
override func doSomething() -> Result<T> {
}
}
Related
I have a class for that I want to write unit test. This class has a dependency, which is a generic class, that I want to mock.
This is the class I want to test.
class ClassToTest: SomeProtocol {
private let dependency = DependencyClass<AnotherClass>()
init() {
//Some Code Here
}
}
class DependencyClass<General: AnotherClassProtocol> {
func getData(from general: General) -> AnyPublisher<Data, Error> {
//Some Code Here
}
}
What I tried
class ClassToTest: SomeProtocol {
private let dependency: DependencyClassProtocol
init(_ dependency: DependencyClassProtocol = DependencyClass<AnotherClass>()) {
//Some Code Here
self. dependency = dependency
}
}
class DependencyClass<General: AnotherClassProtocol>: DependencyClassProtocol {
func getData(from general: General) -> AnyPublisher<Data, Error> {
//Some Code Here
}
}
protocol DependencyClassProtocol {
associatedtype General
func getData(from general: General) -> AnyPublisher<Data, Error>
}
This approach gives me error "Protocol can an only be used as a generic constraint because it has Self or associated type requirements".
How can I Mock the DependencyClass to test the exact behaviour of ClassToTest.
I've tried to use generic type with protocol:
class Weak<T: AnyObject> {
weak var value: AnyObject?
init (value: AnyObject) {
self.value = value
}
}
protocol SomeProtocol: AnyObject {
func doSomething()
}
func createWeak(object: SomeProtocol) -> Weak<SomeProtocol> {
return Weak<SomeProtocol>(value: object)
}
class SomeClass: SomeProtocol {
func doSomething() {
print("Some 2")
}
}
let temp = SomeClass()
let weakObject = createWeak(object: temp)
weakObject.value?.doSomething()
And got the compiler error:
error: 'SomeProtocol' is not convertible to 'AnyObject'
return Weak(value: object)
But without AnyObject constraint it works fine
class Weak<T> {
var value: T?
init (value: T) {
self.value = value
}
}
protocol Protocol {
func doSomething()
}
class Class: Protocol {
func doSomething() {
print("This is class")
}
}
func createWeak(object: Protocol) -> Weak<Protocol> {
return Weak(value: object)
}
let temp = Class()
let weakObject = createWeak(object: temp)
weakObject.value?.doSomething()
Why I can't use protocols inherited form AnyObject in generic classes?
Swift protocols are incomplete types, which means that you can't use them in places like generic arguments, as the compiler needs to know the whole type details so it can allocate the proper memory layout.
Your createWeak function can still be used if you make it generic:
func createWeak<T: SomeProtocol>(object: T) -> Weak<T> {
return Weak<T>(value: object)
}
The above code works because the compiler will generate at compile time a function mapped to the concrete type you pass.
Even better, you can make the initializer generic, and convert Weak to a struct (value types are preferred Swift over reference ones):
struct Weak<T: AnyObject> {
weak var value: T?
init(_ value: T) {
self.value = value
}
}
which you can use it instead of the free function:
let weakRef = Weak(temp)
Consider the following example:
class ManObj {
func baseFunc() {
print("ManObj baseFunc")
}
}
class SubObj: ManObj {
}
protocol Model {
}
extension Model { // This is protocol extension
func someFunc() { // Protocol extension default implementation
(self as! ManObj).baseFunc()
print("Model implementation")
}
}
extension SubObj: Model {
func someFunc() {
print("SubObj Implementation")
}
}
let list = SubObj()
list.someFunc() // static dispatching
let list2: Model = SubObj()
list2.someFunc() // dynamic dispatching
The output is nicely:
SubObj Implementation
ManObj baseFunc
Model implementation
But I dislike the casting in the line (self as! ManObj).baseFunc().
In fact, I only plan to apply Model protocol to subclasses of ManObj. (But not all subclasses of ManObj are Model though!) So, I tried to change Model to:
extension Model where Self: ManObj {
func someFunc() {
self.baseFunc() // No more casting needed!
print("Model implementation")
}
}
But I'm greeted with error:
list2.someFunc() <- error: 'Model' is not a subtype of 'ManObj'
So, is there a way for me to trigger Model.someFunc from list2 after I constrain Model to where Self: ManObj?
Create an empty class just for type casting
class ManObj {
func baseFunc() {
print("ManObj baseFunc")
}
}
class SubObj: ModelCaster {
func someFunc() {
print("SubObj Implementation")
}
}
protocol Model {
}
extension Model where Self: ModelCaster { // This is protocol extension
func someFunc() { // Protocol extension default implementation
print("Model implementation")
}
}
class ModelCaster: ManObj, Model{
}
let list = SubObj()
list.someFunc() //SubObj Implementation
let list2: ModelCaster = SubObj()
list2.someFunc() //Model implementation
I have multiple protocols that have the same function name. Some protocols have associated types, where I can't figure out how to call the functions as I do in non-generic protocols. I get the error: Protocol 'MyProtocol1' can only be used as a generic contraint because it has Self or associated type requirements
Here's what I'm trying to do:
protocol Serviceable {
associatedtype DataType
func get(handler: ([DataType] -> Void)?)
}
struct PostService: Serviceable {
func get(handler: ([String] -> Void)? = nil) {
print("Do something...")
}
}
protocol MyProtocol1: class {
associatedtype ServiceType: Serviceable
var service: ServiceType { get }
}
extension MyProtocol1 {
func didLoad(delegate: Self) {
print("MyProtocol1.didLoad()")
}
}
protocol MyProtocol2: class {
}
extension MyProtocol2 {
func didLoad(delegate: MyProtocol2) {
print("MyProtocol2.didLoad()")
}
}
class MyViewController: UIViewController, MyProtocol1, MyProtocol2 {
let service = PostService()
override func viewDidLoad() {
super.viewDidLoad()
didLoad(self as MyProtocol1) // Error here: Protocol 'MyProtocol1' can only be used as a generic contraint because it has Self or associated type requirements
didLoad(self as MyProtocol2)
}
}
How can I specifically call the function from a generic protocol extension?
It's simple to achieve by turning the protocol into a generic (see below), or by creating a type eraser for these protocols, but this very strongly suggests that you have a design problem and you should redesign your classes and/or extensions. A collision like this suggests strongly that MyStruct is doing too many things itself because it's being pulled in multiple directions by MyProtocol1 and MyProtocol2. There should likely be two objects here instead. (Composition rather than inheritance.)
class MyStruct: MyProtocol1, MyProtocol2 {
let service = PostService()
func prot1Load<T: MyProtocol1>(t: T) {
t.didLoad()
}
func prot2Load<T: MyProtocol2>(t: T) {
t.didLoad()
}
init() {
prot1Load(self)
prot2Load(self)
}
}
To your particular example in the comments, I would use composition rather than inheritance. You're treating protocols like multiple-inheritance, which is almost never right. Instead compose out of things that conform to a protocol.
protocol LoadProviding {
func load()
}
struct MyLoader1: LoadProviding {
func load() {
print("MyLoader1.didLoad()")
}
}
struct MyLoader2: LoadProviding {
func load() {
print("MyLoader2.didLoad()")
}
}
protocol Loader {
var loaders: [LoadProviding] { get }
}
extension Loader {
func loadAll() {
for loader in loaders {
loader.load()
}
}
}
class MyStruct: Loader {
let service = PostService()
let loaders: [LoadProviding] = [MyLoader1(), MyLoader2()]
init() {
loadAll()
}
}
Of course you don't really have to have LoadProviding be a full struct. It could just be a function if that's all you need:
typealias LoadProviding = () -> Void
func myLoader1() {
print("MyLoader1.didLoad()")
}
func myLoader2() {
print("MyLoader2.didLoad()")
}
protocol Loader {
var loaders: [LoadProviding] { get }
}
extension Loader {
func loadAll() {
for loader in loaders {
loader()
}
}
}
class MyStruct: Loader {
let service = PostService()
let loaders: [LoadProviding] = [myLoader1, myLoader2]
init() {
loadAll()
}
}
If you have time to wade through a video on the subject, you may be interested in the Beyond Crusty: Real World Protocols talk from dotSwift. It's about this and similar problems.
In order to extend some functionalities of my NSManagedObject subclasses, I have defined a series of protocols:
protocol ManagedObjectFindable {
static func find(format:String, arguments: [AnyObject]?, inContext context:NSManagedObjectContext, entityName:String?) -> Self?
}
protocol UniquelyIdentifiable: ManagedObjectFindable {
var identifier: NSNumber? { get }
static func findWithIdentifier(identifier: Int, inContext context:NSManagedObjectContext) -> Self?
}
So then every NSManagedObject that has identifier in its data model entity can conform to UniquelyIdentifiable.
For that purpose I am utilising Swift 2.0 Protocol Extensions, where:
extension UniquelyIdentifiable {
static func findWithIdentifier(identifier: Int, inContext context:NSManagedObjectContext) -> Self? {
return self.find("identifier == %lld", arguments: [NSNumber(longLong: Int64(identifier))], inContext: context, entityName:nil)
}
}
Where find is defined as:
extension NSManagedObject: ManagedObjectFindable {
/** returns single entity if found, nil otherwise */
class func find(format:String, arguments: [AnyObject]?, inContext context:NSManagedObjectContext, entityName:String? = nil) -> Self? {
let objectEntityName:String
if let name = entityName {
objectEntityName = name
} else {
objectEntityName = String(self)
}
let fetchRequest = NSFetchRequest()
fetchRequest.entity = NSEntityDescription.entityForName(objectEntityName, inManagedObjectContext: context)
fetchRequest.fetchLimit = 1
fetchRequest.predicate = NSPredicate(format: format, argumentArray: arguments)
var persistentEntityº:NSManagedObject?
context.performBlockAndWait {
do {
let fetchResults = try context.executeFetchRequest(fetchRequest)
if (fetchResults.count != 0){
persistentEntityº = fetchResults.first as? NSManagedObject
}
} catch {}
}
if let persistentEntity = persistentEntityº {
return _safeObjectSelfCast(persistentEntity)
} else {
return nil
}
}
}
func _unsafeObjectSelfCast<T>(obj: AnyObject!) -> T { return obj as! T }
func _safeObjectSelfCast<T>(obj: AnyObject) -> T? { return obj as? T }
Now these methods correctly return Self? and compiler is silent on the coding time, however when compiling it gives me that error Method 'findWithIdentifier(_:inContext:)' in non-final class must return 'Self' to conform to protocol 'UniquelyIdentifiable'
Now the thing is that if instead of implementing that method in a protocol extension I would just extend my NSManagedObject subclass, it will go fine, but that kills the purpose of protocol extensions, when you are completely duplicating the same code across dozens of your NSManagedObject subclasses.
Any workaround, or I am really missing something?
Short answer:
Change Self? in extension to the NSManagedObject?.
Long answer: Self in protocol requirement acts as a placeholder for the class that will implement that protocol. So if you have
protocol SomeProtocol {
func returnSomething() -> Self
}
That means that if you implement it on Int, function returnSomething() should return Int, and if you implement it on Double it should return Double.
Since you are implementing UniquelyIdentifiable on NSManagedObject and your protocol has Self? requirement, you should return NSManagedObject?.