How can I make a weak protocol reference in 'pure' Swift (without @objc)
You need to declare the type of the protocol as AnyObject
.
protocol ProtocolNameDelegate: AnyObject {
// Protocol stuff goes here
}
class SomeClass {
weak var delegate: ProtocolNameDelegate?
}
Using AnyObject
you say that only classes can conform to this protocol, whereas structs or enums can't.
Confusions about weak delegate in swift
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 struct
s 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.
Swift expose the protocol to objc in protocol oriented programming
Add the @objc attribute on the protocol method to specify a different method name to be exposed to Objective-C, in this case match it to the relevant method of AVCaptureDevice
(authorizationStatusForMediaType:
):
@objc // added this attribute
public protocol PermissionProvider {
@objc(authorizationStatusForMediaType:) static func authorizationStatus(for mediaType: AVMediaType) -> AVAuthorizationStatus
}
extension AVCaptureDevice: PermissionProvider {}
Why?
When Objective-C methods are mapped into Swift, argument descriptions that match the type of the corresponding parameter are pruned (see SE-0005 'Better Translation of Objective-C APIs into Swift'), but when Swift is mapped into Objective-C a corresponding 'de-pruning' doesn't happen.
For example, if in Objective-C we have defined:
- (NappingStatus)nappingStatusForKoala:(Koala *)koala;
- (NappingStatus)nappingStatusForEchidna:(Koala *)koala;
when mapped into Swift these will become:
func nappingStatus(for: Koala) -> NappingStatus
func nappingStatus(forEchidna: Koala) -> NappingStatus
Notice in the first method Koala
has been pruned from the parameter name because it matches the type of the parameter it is describing, but in the second method Echidna
hasn't been pruned because this doesn't match the type of of the parameter it is describing (Koala).
When mapping from Swift to Objective-C, reversal of this pruning doesn't happen. For example, if in Swift we have defined
func nappingStatus(for: Koala) -> NappingStatus
when mapped into Objective-C it will become:
- (NappingStatus)nappingStatusFor:(Koala *)koala;
So for your case the AVCaptureDevice method in Objective-C that is:
+ (AVAuthorizationStatus)authorizationStatusForMediaType:(AVMediaType)mediaType;
in Swift is mapped to (notice MediaType pruned):
class func authorizationStatus(for mediaType: AVMediaType) -> AVAuthorizationStatus
However your protocol method, which in Swift is:
static func authorizationStatus(for mediaType: AVMediaType) -> AVAuthorizationStatus
when mapped into Objective-C will be (notice MediaType not `de-pruned'):
+ (AVAuthorizationStatus)authorizationStatusFor:(AVMediaType)mediaType;
which doesn't match the Objective-C method in AVCaptureDevice, so we just provide the correct Objective-C method name via the @objc attribute:
@objc(authorizationStatusForMediaType:) static func authorizationStatus(for mediaType: AVMediaType) -> AVAuthorizationStatus
iOS - Pass Data from Swift to ObjC VC using protocol and delegate
Very basic example...
Assuming we have in Storyboard:
- an Objective-C controller
ObjcViewController
with a "Present It" button - a Swift controller
SwiftViewController
with a "Done" button, withIdentifier: "SwiftVC"
and the buttons are connected to the IBAction
methods...
ObjcViewController.h
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface ObjcViewController : UIViewController
@end
NS_ASSUME_NONNULL_END
ObjcViewController.m
#import "ObjcViewController.h"
#import "YourProject-Swift.h"
@interface ObjcViewController () <DataEnteredDelegate>
@end
@implementation ObjcViewController
- (IBAction)goToSwiftVC:(id)sender {
SwiftViewController *vc = (SwiftViewController *)[self.storyboard instantiateViewControllerWithIdentifier:@"SwiftVC"];
[vc setMyDelegate:self];
[self presentViewController:vc animated:YES completion:nil];
}
- (void)userDidEnterInformationWithInfo:(NSString * _Nonnull)info {
NSLog(@"Delegate sent back: %@", info);
}
@end
SwiftViewController.swift
import UIKit
@objc public protocol DataEnteredDelegate {
func userDidEnterInformation(info: String)
}
class SwiftViewController: UIViewController {
@objc public var myDelegate: DataEnteredDelegate?
@IBAction func doneBtn(_ sender: Any) {
myDelegate?.userDidEnterInformation(info: "from SwiftVC")
self.dismiss(animated: true, completion: nil)
}
}
why do I have to mark both the protocol & contained optional functions in a swift protocol as @objc?
UICollectionViewDataSource is imported from ObjC. The auto-generated Swift header doesn't insert @objc
on every element. It is common for these headers to be invalid Swift (for example, they define structs and classes without implementations, which isn't valid Swift).
When you're writing Swift (rather than looking at auto-generated headers), you need to tell the compiler that it needs to bridge certain things to ObjC, and you do that with @objc
. Imported ObjC doesn't have to be bridged.
Related Topics
Firestore: How to Get Random Documents in a Collection
Nsobject Subclass in Swift: Hash VS Hashvalue, Isequal VS ==
Using Decodable in Swift 4 With Inheritance
How to Unwrap an Optional Value from Any Type
How to Create Generic Protocols in Swift
Print Without Newline in Swift
Constant Unassigned Optional Will Not Be Nil by Default
How Does Collisionbitmask Work? Swift/Spritekit
Selecting Global or Object Print Function
Round Trip Swift Number Types To/From Data
Swift 5.0: 'Withunsafebytes' Is Deprecated: Use 'Withunsafebytes≪R≫(...)
Flatten an Array of Arrays in Swift
Initialize @Stateobject With a Parameter in Swiftui
How to Remove Diacritics from a String in Swift