I've been reading a lot about Swift's runtime lately, and became more and more interested in optimising my code using static method dispatch. This happens with the following methods:
struct methods
final class methods, i.e. declared with the final keyword, as private or in a final class
protocol methods that are defined in a protocol extension, without being declared in the protocol itself.
Problem is, non of these situations enables me to write testable code, at least not the way I do it now: injecting protocol entities that are replaced by mocks in unit testing.
So, is it possible to write testable code without giving up static method dispatch, and if so how does one go about it?
Thanks!
Generics is what you look for. You can abstract over a protocol, but the compiler still knows what exact type you are using, so there's no need for dynamic dispatch.
protocol Dependency {
func doSomething()
}
struct RealDependency: Dependency {
func doSomething() {
print("I'm doing real work")
}
}
struct MockDependency: Dependency {
func doSomething() {
print("I'm the mock, so I do nothing")
}
}
struct MyApp<D: Dependency> {
let dependency: D
func doSomething() {
dependency.doSomething()
}
}
let myAppReal = MyApp(dependency: RealDependency())
let myAppMock = MyApp(dependency: MockDependency())
myAppReal.doSomething() // Prints "I'm doing real work"
myAppMock.doSomething() // Prints "I'm the mock, so I do nothing"
However, note that in Swift, generics monomorphization is not guaranteed. So you might end with some form of dynamic dispatch anyway. See this link
Related
I'm trying to conform a class to Sendable. I have some mutable stored properties which are causing issues. However, what I can't understand is that a MainActor isolated property doesn't allow my class to conform to Sendable. However, if I mark the whole class a #MainActor, then it's fine. However, I don't actually want to conform the whole class to #MainActor.
As an example, take this code:
final class Article: Sendable {
#MainActor var text: String = "test"
}
It gives this warning: Stored property 'text' of 'Sendable'-conforming class 'Article' is mutable.
Can someone explain why? I thought that being isolated to an actor would make it fine.
The error is warning you that your class has a mutable property. That mutable property can be accessed from outside of Swift concurrency and is therefore not safe.
Consider the following:
final class Foo: Sendable {
#MainActor var counter = 0 // Stored property 'counter' of 'Sendable'-conforming class 'Foo' is mutable
}
We can now consider the following property and method of a view controller, which interacts with counter directly:
let foo = Foo()
func incrementFooManyTimes() {
DispatchQueue.global().async { [self] in
DispatchQueue.concurrentPerform(iterations: 10_000_000) { _ in
foo.counter += 1
}
print(foo.counter) // 6146264 !!!
}
}
NB: If you do have set the “Swift Concurrency Checking” build setting to “Minimal” or “Targeted”, the above will compile with only the aforementioned warning. (If you change this to “Complete”, it becomes a hard error.)
In short, you have marked the as #MainActor, but there is nothing to stop other threads from interacting with this property of the class directly. For a type to be Sendable, it must either:
be immutable;
manually synchronize its properties; or
be an actor
If you are going to have a non-actor be Sendable with mutable properties, you have to implement the thread-safety yourself. E.g.:
final class Foo: #unchecked Sendable {
private var _counter = 0
private let queue: DispatchQueue = .main // I would use `DispatchQueue(label: "Foo.sync")`, but just illustrating the idea
var counter: Int { queue.sync { _counter } }
func increment() {
queue.sync { _counter += 1 }
}
}
And
func incrementFooManyTimes() {
DispatchQueue.global().async { [self] in
DispatchQueue.concurrentPerform(iterations: 10_000_000) { _ in
foo.increment()
}
print(foo.counter) // 10000000
}
}
Obviously, you could also restrict yourself to immutable properties and no synchronization would be necessary. But I assume you needed mutability.
Now, in this mutable scenario, you can use whatever synchronization mechanism you want, but hopefully this illustrates the idea. In short, if you are going to allow it to mutate outside of Swift concurrency, you have to implement the synchronization yourself. And because we are implementing our own synchronization, we tell the compiler that it is #unchecked, meaning that you are not going to have the compiler check it for correctness, but rather that burden falls on your shoulders.
Obviously, life is much easier if you use an actor and stay within the world of Swift concurrency. E.g.:
actor Bar {
var counter = 0
func increment() {
counter += 1
}
}
And:
let bar = Bar()
func incrementBarManyTimes() {
Task.detached {
await withTaskGroup(of: Void.self) { group in
for _ in 0 ..< 10_000_000 {
group.addTask { await self.bar.increment() }
}
await print(self.bar.counter)
}
}
}
Reading the Swift Evolution proposals, it seems like complex Sendable conformance checking just hasn't been designed/implemented yet.
From the global actors proposal, it is said that types which are marked with a global actor implicitly conform to Sendable.
A non-protocol type that is annotated with a global actor implicitly conforms to Sendable. Instances of such types are safe to share across concurrency domains because access to their state is guarded by the global actor.
So you don't even need : Sendable if you mark your final class with #MainActor!
On the other hand, the proposal for Sendable mentions:
Sendable conformance checking for classes
[...] a class may conform to Sendable and be checked for memory safety by the compiler in a specific limited case: when the class is a final class containing only immutable stored properties of types that conform to Sendable:
final class MyClass : Sendable {
let state: String
}
Basically, every stored property in your class need to be a let if you don't mark your final class with a global actor.
I have no been able to find anything else in these two documents, or other proposals on Swift Evolution that are relevant.
So the current design doesn't even care about whether you add #MainActor to a property. The two sufficient conditions for a final class to conform to Sendable
The Sendable proposal also mentions:
There are several ways to generalize this in the future, but there are non-obvious cases to nail down. As such, this proposal intentionally keeps safety checking for classes limited to ensure we make progress on other aspects of the concurrency design.
I'm drawing a blank for some reason.. If I want to make a bunch of objects from a class, but I want each instance to have its own unique implementation of a certain method, how would I do this?
For example:
class MyClass {
var name: String
func doSomething() {
// Each object would have custom implementation of this method, here.
}
}
Do I provide each object with its own closure during initialization, and then call that closure in the doSomething() method? I'm trying to figure out the correct or "Swiftly" way to do this. I'm also thinking along the lines of something with protocols, but I can't seem to figure out how to go about this.
I think there're many ways to do it.
In case of Base class + some sub-classes (e.g. Animal, subclassed by Dog, Cat, etc), you can do this:
First of all it's a good idea to define a protocol:
protocol MyProtocol {
func doSomething()
}
Also provide a default implementation, which throws a fatal error if a class doesn't override that method:
extension MyProtocol {
func doSomething() {
fatalError("You must override me")
}
}
Now your base class confirms the protocol thanks to default implementation. But it will throw a fatal error at runtime:
class MyClass: MyProtocol {
// conformant
}
Child class, however, will run correctly as long as it overrides this function:
class MyOtherClass: MyClass {
func doSomething() {
print("Doing it!")
}
}
You could also move fatal error into base class, and not do any extension implementation.
In case of many instances of the same Class, that solution makes no sense. You can use a very simple callback design:
typealias MyDelegate = () -> Void
class MyClass {
var delegate: MyDelegate?
func doSomething() {
delegate?()
}
}
let x = MyClass()
x.delegate = {
print("do it!")
}
x.doSomething()
// Or you can use a defined function
func doIt() {
print("also doing it")
}
x.delegate = doIt
x.doSomething()
It can also be that you re looking for Strategy pattern, or Template pattern. Depends on your usage details.
Do I provide each object with its own closure during initialization, and then call that closure in the doSomething() method
Yes. That is extremely common and eminently Swifty. Incredibly miminalistic example:
struct S {
let f:()->()
func doYourThing() { f() }
}
let s = S { print("hello") }
let s2 = S { print("goodbye" )}
s.doYourThing() // hello
s2.doYourThing() // goodbye
Giving an object a settable method instance property is very, very easy and common. It doesn't have to be provided during initialization — you might set this property later on, and a lot of built-in objects work that way too.
That, after all, is all you're doing when you create a data task with dataTask(with:completionHandler:). You are creating a data task and handing it a function which it stores, and which it will call when it has performed the actual networking.
I'm seeing some strange behavior at the interface of protocol extensions and generics. I'm new to Swift, so possibly I misunderstand, but I don't see how this can be correct behavior.
First let's define a protocol, and extend it with a default function implementation:
protocol Foo {
}
extension Foo {
static func yo() {
print("Foo.yo")
}
}
Now define a couple of conforming types:
struct A: Foo {
}
struct B: Foo {
static func yo() {
print("B.yo")
}
}
A.yo()
B.yo()
As expected, A.yo() uses the default implementation of yo, whereas B.yo() uses the explicit implementation provided by B: the output is
Foo.yo
B.yo
Now let's make a simple generic type:
struct C<T: Foo> {
static func what() {
T.yo()
}
}
C<A>.what()
C<B>.what()
C<A>.what() prints Foo.yo, as expected. But C<B>.what() also prints Foo.yo!
Surely the meaning of C<B> is simply the template for C with B substituted in for the type parameter T? Yet B's version of yo is not being called.
What am I missing? I'm using Swift 5.2.2.
Now, as it turns out you can fix this problem by declaring yo in the original definition of Foo. If we do this:
protocol Foo {
static func yo()
}
then C<B>.what() works as I would expect, printing B.yo. I can't understand the original behavior in the first place, but even less can I understand how this would change it.
In my actual application I can't use this fix, because I am extending a pre-existing protocol with a function that I want to specialize in a particular conforming type.
Generics are resolved at compile time. They're not dynamically dispatched like method calls on class hierarchies or protocols. That staticness is kind of their point, that's where the performance wins stem from.
As far as I can tell, Foo.yo() and B.yo() are totally unrelated functions. Calling Foo.yo() does a statically dispatched call to Foo, and likewise, calling B.yo() causes a statically dispatched call to B.
Yet, if you up-cast B.self to a Foo.Type, and you call yo() on it, you end up with a statically dispatched call to Foo:
(B.self as Foo.Type).yo()
To get dynamic dispatch (to achieve the kind of polymorphism you're after), you need to define yo as a requirement of the protocol. That establishes a relationship between B.yo() (which is now a part of the conformance to the protocol) and Foo.yo() (which is a default implementation for types who don't provide their own).
protocol Foo {
// static func yo() // uncomment this
}
extension Foo {
static func yo() {
print("Foo.yo")
}
}
struct A: Foo {
}
struct B: Foo {
static func yo() {
print("B.yo")
}
}
struct C<T: Foo> {
static func what() {
T.yo()
}
}
A.yo()
B.yo()
(B.self as Foo.Type).yo()
C<A>.what()
C<B>.what()
Results before:
Foo.yo
B.yo
Foo.yo
Foo.yo
Foo.yo
Results after making yo a requirement:
Foo.yo
B.yo
B.yo
Foo.yo
B.yo
It’s hard to suggest a fix for your exact situation without more details of the exact situation- are you not able to provide these? Suffice to say this is the expected behaviour and its to do with some optimisations and assumptions the compiler makes.
You might want to check out this article on static vs dynamic dispatch in Swift: https://medium.com/#PavloShadov/https-medium-com-pavloshadov-swift-protocols-magic-of-dynamic-static-methods-dispatches-dfe0e0c85509
I want to create a protocol which is only adopted by a specific class and its subClassses in swift.
I know i can use protocol extensions like this
protocol PeopleProtocol: class {
}
extension PeopleProtocol where Self: People {
}
But the method that will go in my protocol will be an init method which will be implemented by a class or its subClasess and will return only some specific type of objects.
some thing like this.
protocol PeopleProtocol: class {
init() -> People
}
or i can do some thing like this
extension PeopleProtocol where Self : People {
init()
}
But there are two problems,
In the first approach if i put an init method in the protocol it don't allow me to put a return statement there like -> People in the first approach.
In the second approach i have to provide a function body in the protocol extensions, so this thing will be out of question, as i don't know what specific type to return for this general implementation.
So any suggestions how i can call an init method and do either:
Let the protocol (not protocol extension) to be implemented by only specific classe and its subClasses.
Or return an instance of a certain from protocol extension method without giving its body.
You could add a required method that you only extend for the appropriate classes.
for example:
protocol PeopleProtocol
{
var conformsToPeopleProtocol:Bool { get }
}
extension PeopleProtocol where Self:People
{
var conformsToPeopleProtocol:Bool {return true}
}
class People
{}
class Neighbours:People
{}
extension Neighbours:PeopleProtocol // this works
{}
class Doctors:People,PeopleProtocol // this also works
{}
class Dogs:PeopleProtocol // this will not compile
{}
This could easily be circumvented by a programmer who would want to, but at least it will let the compiler warn you if you try to apply the protocol to other classes.
To test swift objects, from what I've read, we sub-class them and mock the methods we want return our test values. I then watched a WWDC video about swift performance and the presenters suggested marking classes as final to help the compiler decide how to call methods and from the example I saw adding final can help.
The issues I'm having how can we mark classes as final but still enable sub-class mocking? Has anyone actually run into this problem or should I drop the final keyword from my declaration?
Any suggestions would be awesome or if there are none tell me I'm not doing it right.
Thanks,
Mike.
I know this thread is a bit old but thought I would comment anyway. Your other option would be to go Protocol oriented. Define your public interface to your final class in a protocol and make the final class implement that protocol. Then all you have to do is mock the protocol and your class stays final. This gives you your static dispatching and your mocks. I do not know for sure if this is the best option per se but it is the one that I have been using in our framework building so that we can test our framework code while providing an optimized binary to the consuming apps.
internal protocol MockableProtocol {
func deleteItem(_ itemId: String) -> Bool
func fetchAllItems() -> [CustomObject]
func fetchItem(for id: String) -> CustomObject?
}
internal final class MyFinalClass: MockableProtocol {
func deleteItem(_ itemId: String) -> Bool {
// Your code here
}
func fetchAllItems() -> [CustomObject] {
// Your code here
}
func fetchItem(for id: String) -> CustomObject? {
// Your code here
}
}
Then in testing:
class TestMockClass: MockableProtocol {
func deleteItem(_ itemId: String) -> Bool {
// Your code here
}
func fetchAllItems() -> [CustomObject] {
// Your code here
}
func fetchItem(for id: String) -> CustomObject? {
// Your code here
}
}
The compiler uses static dispatch when you mark something as final instead of dynamic dispatch. This has performance benefits that are pretty significant. This is also directly contrary to being able to mock something, as the whole point of static dispatch is that it doesn't have to figure out what version of the method to call.
Unfortunately you cannot have it both ways. You can either not mark it as final and so you can provide a subclass that is a mock, or you can mark it final to make it statically dispatched for performance.