I have a bit of a strange situation. I'm working on a project that contains a lot of legacy objective-c code. We are working on migrating to swift, but in the meantime are relying a bit on using swift class extensions to get the ball rolling. I'm looking for a way to store a cancellable on the extended objective-c class, but I can't access AnyCancellable from objective-c land. This function, declared within the class extension in swift, creates a few notification observers. They need to be retained in memory so long as the class itself is.
How would I go about adding a class property to objective-c that I can use to store the cancellable in? I tried creating a sort of proxy class like this:
#objc class CancellablesHolder: NSObject {
var cancellables: [AnyCancellable] = []
}
and declaring a property in the objc class header like so:
#property (nonatomic, strong) CancellablesHolder *cancellablesHolder;
But Xcode throws a failed to emit precompiled header. Is there a better way?
Would creating an NSMutableArray on the objective-c class and simply doing array.add(publisher.sink {...}) suffice, or is there a better way I'm not seeing?
I had a similar problem and solved it by writing a wrapper class that Swift exposes to Objective-C:
import Foundation
import Combine
#objc final class MyCancellable: NSObject {
let cancellable: AnyCancellable
init(cancellable: AnyCancellable)
{
self.cancellable = cancellable
}
#objc func cancel() {
self.cancellable.cancel()
}
}
It's a Obj-C friendly box that hides a Swift AnyCancellable inside of it. Then you can pass these around in Obj-C and even cancel them from Obj-C if you like.
Related
I have a class that needs to set a component to self. That component requires the class to implement a protocol MyDelegate. Eventually, it fails (SIGNAL SIGABRT).
// I need the class to be a NSObject for unrelated requirements
class MyClass: NSObject {
// I force the compilation, but it then breaks apart at runtime anyway
private let myComponent =
Component(requiresAnObjectofTypeMyDelegate: self as! MyDelegate)
}
// in the same file
extension MyClass: MyDelegate {
func myUsefulDelegateCall() {
}
}
Why?
The problem is the usage of self inside the stored property myComponent.
Typically, it is not allowed to hand out self before the initializer has finished intializing the whole object. Therefore, your problem has nothing to do with protocols or extensions. More simple:
import Foundation
class Component {
init(requiresAnObjectofTypeMyClass:MyClass) {
}
}
// I need the class to be a NSObject for unrelated requirements
class MyClass : NSObject {
// I force the compilation, but it then breaks apart at runtime anyway
private let myComponent =
Component(requiresAnObjectofTypeMyClass : self as! MyClass)
}
let m = MyClass()
also crashes.
If you leave out the NSObject subclassing, you get a compiler error:
use of unresolved identifier 'self'
Component(requiresAnObjectofTypeMyClass : self as! MyClass)
This shows the problem: You must not use self here.
I think it's just an Xcode bug; Xcode seems to ignore the syntax error when subclassing NSObject. The cast as! MyClass is also a hint that we are looking for a strange workaround that finally get's Xcode to it's knees and causes the runtime crash.
To work-around, you could create a lazy property, which will be evaluated after the initialization process and therefore will allow self to be handed into the Component initializer:
private(set) lazy var myComponent = Component(requiresAnObjectofTypeMyClass:self)
Here, you also do not need the cast. Unfortunately, lazy let is not allowed in swift (and nobody knows why), so private(set) is close to it's semantic.
It's easy to transfer this code to your protocol example.
A type cast is not needed. As MyClass adopts MyDelegate it is also MyDelegate.
And initialize the property lazily to be able to use self on the top level at all.
private lazy var myComponent = Component(requiresAnObjectofTypeMyDelegate: self)
I'm trying to use a ValueTransformer (né NSValueTransformer) in Swift that is being used by the first window that my application opens. Value transformers need to be registered with ValueTransformer.registerValueTransformer(_:forName:) before they can be queried by the user interface runtime.
The documentation for NSValueTransformer recommends registering value transformers in +[AppDelegate initialize]. However, Swift doesn't allow you to override +initialize. I tried to register from applicationWillFinishLaunching(_) and applicationDidFinishLaunching(_), but they both happen too late and my window doesn't get filled because the runtime can't find the value transformer.
Where should I register my value transformer?
In AppDelegate you can use a dummy property of type Void with a closure. The closure is even executed before init
private let transformer : Void = {
let myTransformer = MyValueTransformer()
ValueTransformer.setValueTransformer(myTransformer, forName:NSValueTransformerName("MyValueTransformer"))
}()
I found that I can count on the app delegate class to be initialized early and only once, so I stuck my ValueTransformer.registerValueTransformer call in it.
#NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
override init() {
ValueTransformer.setValueTransformer(MyValueTransformer(), forName: NSValueTransformerName("MyValueTransformer"))
}
}
You are right, you can register your value transformers in the AppDelegate. If you want something that closer resembles ObjectiveC's +initialize you can use lazy initialization of a class variable. E.g:
class AppDelegate: NSObject, NSApplicationDelegate {
static let doInitialize: Void = {
// register transformers here
}()
override init() {
super.init()
AppDelegate.doInitialize
}
}
This pattern should also work for classes other than the AppDelegate if you want to keep the transformers things closer to the classes that actually use them.
Let us suppose we have a protocol
protocol MyProtocol {
fun someFunc()
}
class AClass {
var delegate: MyProtocol?
}
AClass doesn't care if the delegate is a class or struct. What I want is sometimes the delegate can be a class and sometimes it can be assigned to a struct.
My question is if I should make the delegate to be "weak".
If so, I have to make MyProtocol be a "class Protocol" so that the delegate has to be a class only.
If not, when I assign the delegate to class, how can I avoid retain cycle?
Thanks for any hint!
should make the delegate to be "weak"
The answer is that if MyProtocol is not restricted to classes, you cannot make it weak, the compiler won't let you.
The reason for the above is that structs are value types. There isn't a reference that can be strong or weak, because logically the entire struct is copied in when you assign the delegate.
how can I avoid retain cycle?
This means that you have got to be careful that your delegate contains no strong references back to the instance of the class. So, for instance
struct ConcreteDelegate: MyProtocol
{
fun someFunc() {}
var instance: AClass
init()
{
instance = AClass()
instance.delegate = self
}
}
Causes a reference cycle. It can be broken by declaring instance as
weak var instance: AClass!
Alternatively, and a better solution (IMO), your protocol functions can pass the instance as a parameter so the delegate never needs to store a reference to the instance.
protocol MyProtocol {
func someFunc(caller: AClass)
}
You'll see the above approach adopted in Cocoa in lots of places, for example with the table view data source protocol.
I think that you forgot that struct are not reference type but a value type. Meaning that a class has a reference in the memory heap but structures, enums haven't. By remembering this fact, there is no meaning to put weak if the delegate of your protocol is a struct because it can't cause a retain cycle.
You need to worry about retain cycles when you use only classes. If your delegate of your protocol is a class put weak if you think that your class have a reference of your protocol AND your protocol can have a reference of your class put weak that's the retain cycle.
If you want to check it put deinit functions when you are testing and see if your class is correctly deinit and not kept in memory.
It's basically what I know hope it will help you.
I'm very new to Swift and programming in general, a bit of Fortran 77 way back, and more recently some simple programming of microcontrollers. I'm working through the basics and all was well until i came across something that i just can't quite get to grips with - delegates. All the online posts don't quite get the concept across, at least for me, so to give myself something that i can refer back to, i've set up a basic template shown below in playground. If i run the code below it works and prints "Something done" to the terminal, but if i make the protocol a "class" protocol ie "protocol SomeDelegate: class {" and make the "var delegate" a "weak var delegate" as recommended in various posts, it doesn't work - what am i doing wrong?
import UIKit
protocol SomeDelegate {
func DoSomething()
}
class MyViewcontroller: UIViewController, SomeDelegate {
func DoSomething() {
print("Something done")
}
}
class OtherClass {
var delegate: SomeDelegate?
func DoSomething() {
delegate?.DoSomething()
}
}
var myVar = OtherClass()
myVar.delegate = MyViewcontroller()
myVar.DoSomething()
It doesn't print because the delegate is nil right after you set it. The reason for this is simple: no instance owns it (the reference count is zero). No one owns delegate because you declared it a weak property of OtherClass. Try establishing an ownership, e.g.
var myVar = OtherClass()
let viewController = MyViewController()
myVar.delegate = viewController
Even though delegate is weak, it will now print Something done again.
Declaring delegates as weak makes sense because it prevents circular references causing delegate to never be release in memory – that's a whole different story though – check how reference counting works, then you will understand why this is a good practice.
I'm having a lot of difficulty moving from Objective-C to Swift and writing my first Swift app. I'm having more difficulty finding anything online in a concise manner.
How can I create a static var in a ViewController class.
For example
class ViewController: UIViewController {
static var SOME_VIEW_WIDTH_AND_HEIGHT = 70.0
...
func someFunc() {
self.someView = SomeView.init(frame: CGRectMake(0,0,SOME_VIEW_WIDTH_AND_HEIGHT,SOME_VIEW_WIDTH_AND_HEIGHT)
etc.
However, I cannot do this. How can this be achieved?