Swift Concurrency: Conforming a 3rd party library to actor isolation - ios

I am in the process of converting some code bases over to using Swift concurrency and am running into a few snags along the way.
The current project I'm working through has a few 3rd party libraries that it relies on, and in one of those libraries, there is a delegate protocol that requires some data values to be returned from its methods.
Here is an example of the type of delegate methods in the library:
public protocol FooDelegate: AnyObject {
func foo() -> CGFloat
}
I'm attempting to return some values from the implementation of the protocol like this:
extension ViewController: FooDelegate {
func foo() -> CGFloat { // <- Cannot satisfy requirement from protocol
view.bounds.height
}
}
Without any modification, the above is implicitly isolated to the MainActor and can not satisfy the requirement from the FooDelegate protocol.
One solution I've tried was to mark the function implementation with nonisolated:
extension ViewController: FooDelegate {
nonisolated func foo() -> CGFloat {
view.bounds.height // <- Cannot be referenced from a non-isolated context
}
}
This did not work though because it references the view controller's view. That results in the view being referenced from a non-isolated synchronous context. (There are also a few other issues with this since any values that are passed through into any of the delegate functions need to conform to Sendable to be able to be passed across actors).
My question is, is there a way to take a 3rd party library and extend it somehow so that it conforms to proper actor isolation without having to modify its source code?

Related

Create Mocks For TypeAlias with composition of Protocols using Sourcery

I was wondering if it is possible to generate mocks for Associated types using Sourcery!
protocol A {
}
protocol B {
}
/// sourcery: AutoMockable
typealias C = A & B
If I try this nothing happens and due to this I've to create my mocks by my own, its weird though it should have work, any workaround or solution for this?

What does this function do "override func `self`() -> Self"?

I am reading AWS SDK sample code on GitHub and saw these following two functions:
// MARK: NSObjectProtocol hack
override func isEqual(object: AnyObject?) -> Bool {
return super.isEqual(object)
}
override func `self`() -> Self {
return self
}
This is only one comment above "NSObjectProtocol hack", which doesn't really make sense to me. Could anyone explain what they are trying to do here?
This is a legacy code you can ignore when using the latest Swift. The earlier versions of Swift had issues recognizing that your mapper object indirectly inherits from NSObject and implements NSObjectProtocol. The current version of Swift does not have the issue, so you can safely ignore these two methods.
This is an example of using a keyword as an identifier. You have to place backtick marks before and after the keyword to use it as such. According to Apple's documentation (under the Identifiers section)
To use a reserved word as an identifier, put a backtick (`) before and after it. For example, class is not a valid identifier, but `class` is valid. The backticks are not considered part of the identifier; `x` and x have the same meaning.
The // MARK: NSObjectProtocol hack is used for commenting a section of code that allows Xcode to format the list of properties/methods etc. into groups.

How to have multiple definitions for a function in swift [duplicate]

I am starting to learn Swift, and have been following the very good Stanford University video lectures on YouTube. Here is a link if you are interested or it helps (although it isn't required to understand my problem):
Developing iOS 8 Apps with Swift - 2. More Xcode and Swift, MVC
While following the lectures I got to a point where (as far as I could tell) my code was identical to the code in the video but on my system I got a compiler error. After a lot of trial and error I have managed to reduce my code to two examples, one of which generates an error, the other or which doesn't, but I have no idea what is actually causing the error or how to resolve it.
The code which creates the error is:
import UIKit
class BugViewController: UIViewController
{
func perform(operation: (Double) -> Double) {
}
func perform(operation: (Double, Double) -> Double) {
}
}
This creates the following compiler error:
Method 'perform' with Objective-C selector 'perform: ' conflicts with previous declaration with the same Objective-C selector
By simply removing the sub-classing of UIViewController the code compiles:
import UIKit
class BugViewController
{
func perform(operation: (Double) -> Double) {
}
func perform(operation: (Double, Double) -> Double) {
}
}
Some other information which may or may not be relevant:
I have recently upgraded to Yosemite.
When I installed Xcode, I ended up with a Beta version (Version 6.3 (6D543q)) because (if I remember correctly) this was the version I needed to run on my version of OS X.
I am half hoping this is a bug in the compiler because otherwise this doesn't make any sense to me. Any help very gratefully received!
I myself am also taking the Standford course and I got stuck here for a long time too, but after some searching, I found something from here: Xcode release notes and it mentioned something below:
Swift 1.2 is strict about checking type-based overloading of #objc
methods and initializers, something not supported by Objective-C.
// Has the Objective-C selector "performOperation:".
func performOperation(op: NSOperation) { /* do something */ }
// Also has the selector "performOperation:".
func performOperation(fn: () -> Void) {
self.performOperation(NSBlockOperation(block: fn))
}
This code would work when invoked from Swift, but could easily crash
if invoked from Objective-C. To solve this problem, use a type that is
not supported by Objective-C to prevent the Swift compiler from
exposing the member to the Objective-C runtime:
If it makes sense, mark the member as private to disable inference of #objc.
Otherwise, use a dummy parameter with a default value, for
example: _ nonobjc: () = (). (19826275)
Overrides of methods exposed
to Objective-C in private subclasses are not inferred to be #objc,
causing the Swift compiler to crash. Explicitly add the #objc
attribute to any such overriding methods. (19935352)
Symbols from SDKs are not available when using Open Quickly in a
project or workspace that uses Swift. (20349540)
what i did was just adding "private" in front of the override method like this:
private func performOperation(operation: Double -> Double) {
if operandStack.count >= 1 {
displayValue = operation(operandStack.removeLast())
enter()
}
}
Objective-C does not support method overloading, you have to use a different method name. When you inherited UIViewController you inherited NSObject and made the class interopable to Obj-C. Swift on the other hand does support overloading, that's why it works when you remove the inheritance.
As it has already been answered, ObjC doesn't support method overloading (two methods with the same name) and In swift 2 under Xcode 7 there are two options to solve this kind of problems. One option is to rename the method using the attribute: #objc(newNameMethod:)
func methodOne(par1, par2) {...}
#objc(methodTwo:)
func methodOne(par1) {...}
another option to solve this problem in Xcode 7+ is by applying #nonobjc attribute to any method, subscript or initialiser
func methodOne() {...}
#nonobjc
func methodOne() {...}
The problem is UIViewController is an #objc class. When inheriting from UIViewController, BugViewController is also a #objc class.
This means it must conform to the rules of Objective-C selectors (the name of a method). The methods func perform(operation: (Double) -> Double) and func perform(operation: (Double, Double) -> Double) both have the same selector #selector(perform:). This is not allowed.
To resolve this, use different names: like func perform1(operation: (Double) -> Double) and func perform2(operation: (Double, Double) -> Double).
I think the best way to handle this is to give your perform() methods more descriptive names. What do these methods do? How do they change the state of the view controller? Look at the other UIViewController methods to get a feel for the style of method naming, or read Method Names Should Be Expressive and Unique Within a Class
From https://developer.apple.com/library/ios/releasenotes/DeveloperTools/RN-Xcode/Chapters/xc6_release_notes.html under "Xcode 6.3 Release Notes" -> "Swift Language Changes" you find
Swift now detects discrepancies between overloading and overriding in the Swift type system and the effective behavior seen via the Objective-C runtime.
I got the same error due to having having two methods with the same Obj-C signature:
static func prepareForUpSyncing(obj : NSManagedObject!) -> Bool
static func prepareForUpSyncing(objs : [NSManagedObject]!) -> Bool
I didn't want to mark one of them as #nonobjc due to possibility of unforseen consequences at runtime. (Someone can correct me if there is no possibility)
Resolved it by using Swift's external parameter name feature (I made external name same as local name) to the second method, which effectively changes the Obj-c method signature:
static func prepareForUpSyncing(objs objs : [NSManagedObject]!) -> Bool {

Cannot declare a public protocol extension with internal requirements

I am programming a media player app and created my own framework for managing all the player functionality. In this framework I have a public protocol called PlayerControllerType and an internal protocol _PlayerControllerType. In PlayerControllerType I have declared all the methods and properties, which should be accessible from outside the framework. In _PlayerControllerType I have defined a couple of properties, which are used by the concrete types implementing PlayerControllerType inside the framework. One of these types is PlayerController. Its declaration is as follows:
public class PlayerController<Item: Equatable>: NSObject, PlayerControllerType,
_PlayerControllerType, QueueDelegate
Now I want to provide a couple of default implementations for the classes in my framework, which conform to PlayerControllerType and the internal _PlayerControllerType, for example:
import Foundation
import MediaPlayer
public extension PlayerControllerType where Self: _PlayerControllerType, Item == MPMediaItem, Self.QueueT == Queue<Item>, Self: QueueDelegate {
public func setQueue(query query: MPMediaQuery) {
queue.items = query.items ?? []
}
}
This works as expected in Xcode 7 Beta 4. Yesterday I updated to Beta 6 and got this error:
"Extensions cannot be declared public because its generic requirement uses an internal type" (also see screenshot).
I find this error irritating. Of course no type outside of my framework benefits of this extension because it cannot access the internal protocol _PlayerControllerType, but it is very useful for the types inside my framework which implement both PlayerControllerType and _PlayerControllerType.
Is this just a bug in the Swift compiler or is this the intended behavior?
It's is pretty unfortunate that this doesn't work anymore because now I have to put these methods into a newly created base class for all my PlayerControllers.
Any help or feedback would greatly appreciated.
Kai
EDIT:
Here is a shortened example of the protocols and their extensions:
public protocol PlayerControllerType {
typealias Item
var nowPlayingItem: Item {get}
func play()
}
protocol _PlayerControllerType {
var nowPlayingItem: Item {get set}
}
public extension PlayerControllerType where Self: _PlayerControllerType {
/*
I want to provide a default implementation of play() for
all my PlayerControllers in my framework (there is more than one).
This method needs to be declared public, because it implements a
requirement of the public protocol PlayerControllerType.
But it cannot be implemented here, because this extension
has the requirement _PlayerControllerType. It needs this requirement,
because otherwise it cannot set the nowPlayingItem. I don't want to
expose the setter of nowPlayingItem.
I could make a base class for all PlayerControllers, but then I'm
restricted to this base class because Swift does not support
multiple inheritance.
*/
public func play() {
if nowPlayingItem == nil {
nowPlayingItem = queue.first
}
// Other stuff
}
}
You need to declare an access level of the _PlayerControllerType protocol as 'public'.
public protocol _PlayerControllerType {
// some code
}
According to (The Swift Programming Language - Access Control),
A public members cannot be defined as having an internal or private type, because the type might not be available everywhere that the
public variable is used. Classes are declared as internal by default,
so you have to add the public keyword to make them public.
A member (class/protocol/function/variable) cannot have a higher
access level than its parameter types and return type, because the
function could be used in situations where its constituent types are
not available to the surrounding code.

Typhoon - How to inject parameter which conforms to PROTOCOL instead of CLASS

I have class which represents logged user
public class User: NSObject {
init(authenticator: Authenticator) {
self.authenticator = authenticator
}
...
}
Its only initial arguments is object which conforms to Authenticator protocol
protocol Authenticator
{
func authenticate(login:String , password:String , handler: (result:AuthenticationResult)->() )
}
In my case the Auth object is instance of class BackendService
My typhoon assembly definition is:
public dynamic func user() -> AnyObject {
return TyphoonDefinition.withClass(User.self) {
(definition) in
definition.useInitializer("initWithAuthenticator") {
(initializer) in
initializer.injectParameterWith( self.backendService() )
}
}
}
Application cause runtime-error
'Method 'initWithAuthenticator' has 0 parameters, but 1 was injected. Do you mean 'initWithAuthenticator:'?'
If i change init method to 'initWithAuthenticator:' it crashes with
'Method 'initWithAuthenticator:' not found on 'PersonalMessages.User'. Did you include the required ':' characters to signify arguments?'
At the present time, it is necessary to add the '#objc' directive to Swift protocols to have them be available for dependency injection with Typhoon. Without it the objective-c runtime's introspection and dynamic dispatch features arent available and these are required.
Similarly, in the case of a class it must extend from NSObject or have the '#objc' directive, otherwise it will also use C++ style vtable dispatch and have (essentially) no reflection. In the case of private vars or methods, they must also have the 'dynamic' modifier.
While vtable dispatch is faster, it prevents runtime method interception which many of Cocoa's most powerful features, such as KVO rely on. So both paradigms are important and its impressive that Swift can switch between them. In the case of protocols though, using the '#objc' directive is a little unfortunate as it implies a 'legacy' behavior. Perhaps 'dynamic' would've been better?
dynamic protocol Authenticator //Not supported but would've been a nicer than '#objc'?
Or perhaps another way to imply that dynamic behavior is required would be to have the protocol extend the NSObject protocol, however this does not work. So using '#objc' is the only choice.
Meanwhile, for classes the requirement to extend NSObject isn't really noticible as far as working with Cocoa/Touch apps goes.

Resources