Using a Swift protocol in Objective-C - objective-c

I'm working on converting my project from Objective-c to Swift, and a Swift class I'm using, I have a protocol I'm trying to access in an Objective-c class. My problem is, the delegate is not accessible in the objective-c class. Here is my swift class:
protocol RateButtonDelegate {
func rateButtonPressed(rating: Int)
}
class RateButtonView: UIView {
var delegate: RateButtonDelegate?
var divider1: UIView!
var divider2: UIView!
}
When I look at the MyProject-Swift.h file, I don't see the delegate:
#interface RateButtonViewTest : UIView
#property (nonatomic) UIView * divider1;
#property (nonatomic) UIView * divider2;
#end
and when I try to use rateButtonView.delegate in my Objective-c class, I get a compiler error.
Anyone know the issue? How do access a Swift protocol in Objective-c?

You need to mark your protocol with the #objc attribute for it to be accessible from Objective-C:
#objc protocol RateButtonDelegate {
func rateButtonPressed(rating: Int)
}
This page from Apple's documentation covers the issue.

Related

Can not find Swift Protocol declaration in Obj-C class

I have created on Class in Swift and that class and its protocol I am using in Obj-C enabled project but I am getting below error while compiling my project.
cannot find protocol declaration for 'SpeechRecognizerDelegate'; did
you mean 'SFSpeechRecognizerDelegate'?
Can anyone guide me on this how can I use Swift class protocol in my Obj-C class.
Here is my Swift code:
protocol SpeechRecognizerDelegate : class {
func speechRecognitionFinished(_ transcription:String)
func speechRecognitionError(_ error:Error)
}
class SpeechRecognizer: NSObject, SFSpeechRecognizerDelegate {
open weak var delegate: SpeechRecognizerDelegate?
}
Protocol use in Obj-C:
#import "ARBot-Swift.h"
#interface ChatScreenViewController : JSQMessagesViewController <SpeechRecognizerDelegate>
Let me know if required more info.
Thanks in Advance.
in Swift:
#objc public protocol YOURSwiftDelegate {
func viewReceiptPhoto()
func amountPicked(selected: Int)
}
class YourClass: NSObject {
weak var delegat: YOURSwiftDelegate?
}
in Objective-C headerFile.h
#protocol YOURSwiftDelegate;
#interface YOURController : UIViewController < YOURSwiftDelegate >
in Objective-C Implementation.m
SwiftObject * swiftObject = [SwiftObject alloc] init];
swiftObject.delegate = self
Define your Swift protocol like this inside your Swift file.
#objc protocol SpeechRecognizerDelegate: class{
func speechRecognitionFinished(_ transcription:String)
func speechRecognitionError(_ error:Error)
}
Create a Swift Module inside your project setting then use it. You can find here complete blog for the mix language coding.
Then use Protocol inside Objective C class,
We required to add protocol inside Objective C file -
#import "ARBot-Swift.h"
#interface ChatScreenViewController : JSQMessagesViewController <SpeechRecognizerDelegate>
Then you need to conform to the protocol methods -
- (void)viewDidLoad {
[super viewDidLoad];
SpeechRecognizer * speechRecognizer = [[SpeechRecognizer alloc] init];
speechRecognizer.delegate = self;
}
#pragma mark - Delegate Methods
-(void)speechRecognitionFinished:(NSString *) transcription{
//Do something here
}
-(void)speechRecognitionError:(NSError *) error{
//Do something here
}
I had a similar issue after following (import header + Objc annotation on protocol). I got the warning when using Swift code from Objective C headers. Solved by only importing into the implementation .m files.
Add #objc attribute to your protocol:
#objc protocol SpeechRecognizerDelegate : class {
//...
}
Include Swift Classes in Objective-C Headers Using Forward Declarations
//MySwiftClass.swift
#objc protocol MySwiftProtocol {}
#objcMembers class MySwiftClass {}
// MyObjcClass.h
#class MySwiftClass;
#protocol MySwiftProtocol;
#interface MyObjcClass : NSObject
- (MySwiftClass *)returnSwiftClassInstance;
- (id <MySwiftProtocol>)returnInstanceAdoptingSwiftProtocol;
// ...
#end

Access instance properties of an Objective-C NSDocument subclass from Swift plugin?

The goal is to write a Swift plugin which can access public instance properties of an Objective-C NSDocument subclass. The main application is in Objective-C. The plugin is intended to be just Swift.
The main application has a public Objective-C document interface:
#interface ObjcNSDocument : NSDocument
#property (nonatomic, readwrite) NSString *markdown;
#property (nonatomic, readonly) NSString *html;
#end
A reasonable Swift counterpart would seem to be:
public class SwiftNSDocument: NSDocument {
public var markdown: String = ""
public var html: String = ""
}
The following Swift code retrieves the current NSDocument OK. However, the casting fails.
public func doFilter() {
let dc = NSDocumentController.shared()
if let document: NSDocument = dc.currentDocument {
print("document.displayName = \(document.displayName)") // works OK
let markdownSource = document as! SwiftNSDocument // FAIL: cannot cast as
}
}
While I'm asking this is general, my particular test case is to rewrite the macdown-gistit Objective-C plugin as close as possible to pure Swift.
Ideally, the solution would not require including any Objective-C code from the application. What is possible, even if not ideal?
Additional Observation
In the case of a working Objective-C plugin, the Application's NSDocument subclass .h file is not included in the plugin project.
Instead, the Objective-C plugin has only the single relevant property as an NSObject subclass.
#protocol MarkdownSource <NSObject>
#property (readonly) NSString *markdown;
#end
The NSDocument is then downcast as the NSObject subclass in Objective-C:
id<MarkdownSource> markdownSource = (id)document;
NSString *markdown = markdownSource.markdown;
Theoretically, one would think that something similar might work in Swift like
public class MarkdownSource: NSObject {
public var markdown: String = ""
}
where id in Object-C would be a Swift Any. I've tried various downcastings were like such as:
let markdownSource: MarkdownSource = (nsdocument as Any) as MarkdownSource
let markdown: String = markdownSource.markdown
I have not, so far, been able to downcast an NSDocument subclass to any NSObject subclass with just the one desired property in Swift.

Delegate Declaration Swift

In Objective C we can declare a delegate this way
#property (nonatomic, weak) id<SomeProtocol> delegate
and in swift
weak var delegate : SomeProtocol?
but in Objective C we can force the delegate to be of a certain class:
#property (nonatomic, weak) UIViewController<SomeProtocol> delegate
how i do this in swift?
Swift requires you to know the size of the type at compile time, so you would need to make your class generic to your delegate type:
protocol SomeProtocol: class {}
class SomeClass<T: UIViewController> where T: SomeProtocol {
weak var delegate : T?
}
There's another option, if you really don't care about it being constrained to a certain type, but to a certain interface, you could describe the UIViewController via another protocol that exposes the methods you need of it.
protocol UIViewControllerProtocol: class,
NSObjectProtocol,
UIResponderStandardEditActions,
NSCoding,
UIAppearanceContainer,
UITraitEnvironment,
UIContentContainer,
UIFocusEnvironment {
var view: UIView! { get set }
func loadView()
func loadViewIfNeeded()
var viewIfLoaded: UIView? { get }
}
extension UIViewController: UIViewControllerProtocol {}
protocol SomeProtocol: class {}
class SomeClass {
weak var delegate : SomeProtocol & UIViewControllerProtocol?
}
This will let you use many of the methods and properties in a UIViewController, but it doesn't really constraint your delegate to be a UIViewController because any other object could implement this protocol and be used instead.
PS: this is Swift 3.0

How to use NSSet<Class> in Swift (exported as Set<NSObject>)?

I have to fulfill a protocol requirement that is defined in Objective-C like this:
#protocol AProtocol <NSObject>
+ (NSSet<Class> * _Nullable)someClasses;
#end
I want to implement this protocol in a subclass written in Swift. I want to return a Set of classes of another Object. The class I want to return is defined like this:
class B: NSObject {}
The class that conforms to the protocol is defined like this:
class A: NSObject, AProtocol {
static func someClasses() -> Set<NSObject>? {
return [B.self]
}
}
Why is NSSet<Class> bridged to Set<NSObject> instead of Set?
This solution is crashing, how can I solve the problem?
NSSet<Class> is bridged to Set<NSObject> because AnyClass does not conform to Hashable which is a precondition for the ValueType of Set.
It can be solved with the following extension for NSObjectProtocol:
extension NSObjectProtocol where Self: NSObject {
static var objcClass: NSObject {
return (self as AnyObject) as! NSObject
}
}
This returns the class of the object casted as NSObject. It is necessary to cast it first to AnyObject because the type system of Swift is so strong that it would not compile or give a warning when directly casting a type to an instance type. In Objective-C this is fine because Class is also just an object. Since NSObject is implemented in Objective-C and the extension is just for NSObjectProtocol, this is save to use (even with the force cast).
Implementing the extension on NSObjectProtocol and not on NSObject itself brings the positive effect that it is not exported to Objective-C.

Swift Classes vs. Objective-C Classes. Am I on the right track?

I am new to Swift. I've taken a couple of online course and have started converting an existing Objective-C project to Swift as a learning experience. I have a few questions if someone has time. I've searched the board but haven't found the answers. I am sorry if I missed them.
Delegate Protocols - I'm used to defining my own in Objective-C. Below I have the original Objective-C version and below it I have my new Swift version. Have I followed the correct design pattern for Swift?
I find myself making optionals for all of the properties especially objects like NSData or custom classes. Is this practice acceptable? I am not sure how I would know an initial value for most objects. I know the language wants you to set an initial value but it seems strange for certain objects.
If I am not mistaken we do not have to call self.super init in custom initializers in Swift. Is this correct?
Objective-C Version
#protocol FLOParserHandlerDelegate;
#interface FLOParserHandler: NSObject <NSXMLParserDelegate>
// Properties
#property (nonatomic, strong) NSMutableData *PHData;
#property (nonatomic, strong) NSMutableString *currentParsedCharacterData; // This grabs the characters as they come in and adds them together.
#property (nonatomic, strong) NSMutableArray *XMLDataArray; // This is the master array that holds all of the article arrays with the date, title and link objects.
#property (nonatomic, strong) NSMutableDictionary *dateTitleLinkDictionary;// This is used to gather the date, title and link in an array to added to the master array.
// Delegate Property
#property (nonatomic, weak) id <FLOParserHandlerDelegate> delegate;
// init Methods
- (id) initWithCHData: (NSMutableData *) data;
// Class Methods
-(void) startParser;
#end
#pragma mark-
#pragma mark FLOParserHandler Protocol Definition
#protocol FLOParserHandlerDelegate
#optional
- (void) floParserHandlerDidFinishParsing: (FLOParserHandler *) parserHandler;
- (void) floParserHandler: (FLOParserHandler *) parserHandler didFailWithError: (NSError *) error;
#end
Swift Version
import Foundation
protocol FLOParserHandlerDelegate
{
func floParserHandlerDidFinishParsing(parserHandler : FLOParserHandler) -> ()
func floParserHandler(parserHandler : FLOParserHandler, error : NSError) -> ()
}
// Note that we have to inherit from NSObject here. I believe this iis because we are mixing it with Objective-C.
class FLOParserHandler : NSObject, NSXMLParserDelegate
{
var PHData : NSData?
var currentParsedCharacterData : String?
var XMLDataArray : [String]?
var dateDictionary : [String:NSDate]?
var titleDictionary : [String:String]?
var linkDictionary : [String:String]?
// Delegate Property
var delegate : FLOParserHandlerDelegate?
// Init Methods
init(data : NSData)
{
self.PHData = data
}
// Class Methds
func startParser()
{
var parser = NSXMLParser(data: self.PHData)
parser.delegate = self
parser.parse()
}
}
Thank you,
Jon
Your protocol definition is valid. There is one little thing you should know about:
As with type property requirements, you always prefix type method
requirements with the class keyword when they are defined in a
protocol
protocol SomeProtocol {
class func someTypeMethod()
}
Its perfectly fine to use optionals, or you may use implicity unwrapped optionals like NSData!. In which case you should do this and where no, you may read here: Why create "Implicitly Unwrapped Optionals"?.
Shortly, you doing this in following situations:
a) Constant cannot be defined using initializtion, but you know that it would not be nil (otherwise app will crash)
b) Objective-C Api required you to use pointers, and pointers in Obj-C could be nil. In that case you use imilicity unwrapped optionals.
You always have to call 'super' if you have superclass, to be sure, that class is properly initialized.
Hope that helps.