I'm trying to migrate some code from Objective-c to Swift, but i have problems at the beggining when i want to conform a Swift class with a Objective-c protocol and access this class from a objetive-c class. I'm doing something wrong, but i don't see it.
Objective-c protocol and obj-c class for testing (test_class.h)
#import <Foundation/Foundation.h>
#protocol test_delegate
-(void)returnData:(NSString*)data InMethod:(NSString*)method;
#end
#interface test_class : NSObject
#property (weak, nonatomic) id<test_delegate> delegate;
-(void)sendData:(NSString * )data;
#end
Objective-c implementation
#import "test_class.h"
#implementation test_class
-(id) init{
self = [super init];
if (self != nil){
self.delegate= nil;
}
return self;
}
-(id) initWithDelegate:(id<test_delegate>) delegate{
self = [super init];
if (self != nil){
self.delegate = delegate;
}
return self;
}
-(void)sendData:(NSString *)data{
[self.delegate returnData:data InMethod:#"method test"];
}
#end
Objetive-c brigde file
//
// Use this file to import your target's public headers that you would like to expose to Swift.
//
#import "test_class.h"
Swift file (FirstViewController.swift)
import UIKit
class FirstViewController: UIViewController, test_delegate {
var test : test_class!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
test = test_class()
test.delegate=self
test.sendData("show in log")
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func returnData(data: String!, inMethod method: String!) {
NSLog(data)
}
}
Finally the error compile gives me
2015-01-27 08:21:05.787 Test[4566:51164] -[Test.FirstViewController returnData:InMethod:]: unrecognized selector sent to instance 0x7fa6abd0aa20
2015-01-27 08:21:05.792 Test[4566:51164] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[Test.FirstViewController returnData:InMethod:]: unrecognized selector sent to instance 0x7fa6abd0aa20'
This code, using only Obj-c classes works fine. What I'm doing wrong?
Using Xcode 6.1.1 and deployment target iOS 8.0
Thank you in advance.
UPDATE:
Swift has a weird behaviour when implement some method from a objective-c protocol: param's name must start with a tiny letter at the the begginig, else, compiler give you a compile-time error, and if not, give you a runtime error.
The unique solution I've found, is modify objective-c protocol method definition this way:
#protocol test_delegate
-(void)returnData:(NSString*)data InMethod:(NSString*)method;
#end
to:
#protocol test_delegate
-(void)returnData:(NSString*)data inMethod:(NSString*)method;
#end
Doing this change, it runs perfectly.
If anyone has an answer for this behaviour, is welcome to post and explain why this happens.
UPDATE 2:
Thank you to #Adam Freeman to point out another weird issue of Swift with classes names and variables names. Copied (with his permission) the code here:
Another thing to watch out for that is if your protocol delegate method takes its class as one of its parameters. Using this example:
#import <Foundation/Foundation.h>
#class TestClass;
#protocol TestDelegate
-(void)TestClass:(TestClass*)TestClass returnData:(NSString*)data inMethod:(NSString*)method;
#end
#interface TestClass : NSObject
#property (weak, nonatomic) id<TestDelegate> delegate;
-(void)sendData:(NSString * )data;
#end
This will cause problems with TestClass being found in your Swift code. The fix is:
#import <Foundation/Foundation.h>
#class TestClass;
#protocol TestDelegate
-(void)testClass:(TestClass*)testClass returnData:(NSString*)data inMethod:(NSString*)method;
#end
#interface TestClass : NSObject
#property (weak, nonatomic) id<TestDelegate> delegate;
-(void)sendData:(NSString * )data;
#end
Incidentally the Swift spec states that such things as classes and delegates should start with an upper case letter and methods and parameter names should start with a lower case letter.
Change:
func returnData(data: String!, inMethod method: String!) {
to
func returnData(data: String!, InMethod method: String!) {
and it should work. You use a capital letter in inMethod.
Another thing to watch out for that I ran into and this post helped me fix (thanX!) and that others may run into is if your protcol delegate method takes its class as one of its parameters. Such as ==>
#import <Foundation/Foundation.h>
#class TestClass;
#protocol TestDelegate
-(void)TestClass:(TestClass*)TestClass returnData:(NSString*)data inMethod:(NSString*)method;
#end
#interface TestClass : NSObject
#property (weak, nonatomic) id<TestDelegate> delegate;
-(void)sendData:(NSString * )data;
#end
This will cause problems with TestClass being found in your Swift code. The fix is:
#import <Foundation/Foundation.h>
#class TestClass;
#protocol TestDelegate
-(void)testClass:(TestClass*)testClass returnData:(NSString*)data inMethod:(NSString*)method;
#end
#interface TestClass : NSObject
#property (weak, nonatomic) id<TestDelegate> delegate;
-(void)sendData:(NSString * )data;
#end
Incidentally the Swift spec states that such things as classes and delegates should start with an upper case letter and methods and parameter names should start with a lower case letter. I know this is nit-picky but test_class really should be TestClass according to Apple's spec.
Related
I am trying to port an old obj-c application to swift, and in the process restructure and reprogramm everything. Some things need to be portet at a later point and I have to use old obj-c in swift, which isn't a problem, but I ran into a serious issue which seems like i cannot solve.
I have a obj-c "connection" class which is called from a swift wrapper. The problem is, i cannot pass the delegate object to obj-c or at least i dont know how.
Here is my code:
//swift protocol
#objc protocol ConnectionDelegate
{
#objc func connected() -> Void
}
//swift class
#objc class ConnectionManager : NSObject, ConnectionDelegate
{
var connectionThread : ConnectionThread
init(){
connectionThread.inti()
connectionThread.registerDelegate(self) //Value of type 'ConnectionThread' has no member of 'registerDelegate'
connectionThread.testFunc() //all ok
}
#objc func connected(){
}
}
//obj-c header ConnectionThread.h
#class ConnectionDelegate;
#property (nonatomic, weak) ConnectionDelegate* delegate;
-(void) registerDelegate: (ConnectionDelegate*) delegate;
-(void) testFunc;
//obj-c class ConnectionThread.h
#import ".....Swift.h"
#synthesize delegate;
-(void) registerDelegate:(ConnectionDelegate*) delegate
{
self.delegate = delegate;
}
-(void) testFunc
{
}
In the future, please copy and paste your actual code into your question. The code in your question is full of errors, which means it isn't your real code, and those errors might make it impossible to answer your question correctly.
So, assuming you haven't made too many errors in the code you posted, the problem is that you are lying to the compiler. Specifically, your Objective-C header file ConnectionThread.h says this:
#class ConnectionDelegate;
But ConnectionDelegate is not a class. It is a protocol, so you need to declare it as a protocol. Then you will also have to use the proper Objective-C syntax for a type that conforms to the protocol, which is id<ConnectionDelegate>.
// ConnectionThread.h
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
#protocol ConnectionDelegate;
#interface ConnectionThread : NSObject
#property (nonatomic, weak) id<ConnectionDelegate> delegate;
- (void)registerDelegate:(id<ConnectionDelegate>)delegate;
#end
NS_ASSUME_NONNULL_END
// ConnectionThread.m
#import "ConnectionThread.h"
#implementation ConnectionThread
- (void)registerDelegate:(id<ConnectionDelegate>)delegate {
self.delegate = delegate;
}
#end
I have a class written in Swift where I've defined a protocol:
protocol PhotoIngestionDelegate {
func pictureTaken()
}
I am trying to have a class (CameraViewController) written in Objective-C conform to this protocol.
CameraViewController.h:
#import <GSSDK/GSSDK.h>
#protocol PhotoIngestionDelegate;
#interface CameraViewController : GSKCameraViewController <PhotoIngestionDelegate>
#end
CameraViewController.m:
#import "CameraViewController.h"
#import <GSSDK/GSSDK.h>
#import "EditFrameViewController.h"
#import "Scan.h"
#interface CameraViewController<PhotoIngestionDelegate> ()
#property (nonatomic, strong) UIView *toolbar;
#property (nonatomic, strong) UIButton *cameraButton;
#end
#implementation CameraViewController
...
The implementation in CameraViewController.m continues, but I cut it off to keep it brief. I know that I need to define the function pictureTaken() in CameraViewController.m, but I can't seem to get the delegate hookup to work. In CameraViewController.h I am getting that it Cannot find protocol definition for 'PhotoIngestionDelegate'.
Try this
#objc protocol PhotoIngestionDelegate {
}
I am trying to migrate my project to Swift, and started by making an Objective-C Bridger File filled with #imports to relevant .h files. I've checked exhaustively that the Build Settings options and path are correct. I created a Swift file for a UITableViewController that looks something like this:
TableViewController.swift
class TableViewController {
var title: String = ""
var summary: String = ""
override init(style: UITableViewStyle) {
super.init(style: style)
}
override func viewDidLoad() {
super.viewDidLoad()
title = UIResources.getString(settingsKey, withSuffix: "title")
summary = UIResources.getString(settingKey, withSuffix: "summary")
}
...
}
However, every line starting with the first override result in errors like
Initializer does not override a designated initializer from its superclass
and
'super' members cannot be referenced in a root class
I'm pretty unfamiliar with working with Objective-C, so I'm not sure what I need to change, if anything. Here is the .h file of the class I imported in the bridging header also. I'm really stumped on this one.
TableViewController.h
#import <UIKit/UIKit.h>
#class TableViewController
#protocol TableViewControllerDelegate <NSObject>
- (void) tableViewControllerDidSave: (UIViewController *)controller forKey:(NSString *)key withNewValue:(id) value;
#end
#interface TableViewController : UITableViewController
#property (strong, nonatomic) id <TableViewControllerDelegate> delegate;
#property (strong, nonatomic) NSString *settingKey;
#property (strong, nonatomic) id stringValue;
#end
I didn't realize I needed to add the type to the class definition
class TableViewController : UITableViewController, TableViewControllerDelegate {
That's what you get when you blind trust objectivec2swift.com :P
I also added the variables and protocol outlined in the .h file to the Swift file.
I want to inherit my base class properties and methods which will be used by my several derived classes. I want these properties and methods to be exactly protected so that they will only be visible in derived class and not to any external class. But it always gives me some errors.
#interface BasePerson : NSObject
#end
#interface BasePerson ()
#property (nonatomic, strong) NSMutableArray<Person*>* savedPersons;
#property (nonatomic) BOOL shouldSavePerson;
#end
#interface DerivedPerson1 : BasePerson
#end
#implementation DerivedPerson1
- (instancetype)init
{
if (self = [super init]) {
self.savedPersons = [NSMutableArray array];
self.shouldSavePerson = NO;
}
return self;
}
It always gives me an error that
Property 'savedPersons' not found on object of type 'DerivedPerson1 *'
Property 'shouldSavePerson' not found on object of type 'DerivedPerson1 *'
How i can make use of inheritance in Objective C, I don't want savedPersons and shouldSavePerson properties to be visible to external classes. I only want them to visible in my base class and all the derived classes.
Any help will be great. Thanks
This is not something that the objectiveC really support. There are some ways though. So lets see.
If you put a property in the source file class extension then it is not exposed and you can not access it in the subclass either.
One way is to put all of the subclasses into the same source file as the base class. This is not a good solution at all as you do want to have separate files for separate classes.
It seems logical to import the BaseClass.m in the SubClass source file but that will produce a linker error saying that you have duplicate symbols.
And the solution:
Separate the extension into a separate header. So you have a MyClass
Header:
#import <Foundation/Foundation.h>
#interface MyClass : NSObject
#end
Source:
#import "MyClass.h"
#import "MyClassProtected.h"
#implementation MyClass
- (void)foo {
self.someProperty = #"Some text from base class";
}
#end
Then you create another header file (only the header) MyClassProtected.h which has the following:
#import "MyClass.h"
#interface MyClass ()
#property (nonatomic, strong) NSString *someProperty;
#end
And the subclass MyClassSubclass
Header:
#import "MyClass.h"
#interface MyClassSubclass : MyClass
#end
And the source:
#import "MyClassSubclass.h"
#import "MyClassProtected.h"
#implementation MyClassSubclass
- (void)foo {
self.someProperty = #"We can set it here as well";
}
#end
So now if the user MyClassSubclass he will not have the access to the protected property which is essentially what you want. But the downside is the user may still import MyClassProtected.h after which he will have the access to the property.
Objective-C doesn't have member access control for methods, but you can emulate it using header files.
BasePerson.h
#interface BasePerson : NSObject
#property (strong,nonatomic) SomeClass *somePublicProperty;
-(void) somePublicMethod;
#end
BasePerson-Private.h
#import "BasePerson.h"
#interface BasePerson ()
#property (nonatomic, strong) NSMutableArray<Person*>* savedPersons;
#property (nonatomic) BOOL shouldSavePerson;
#end
BasePerson.m
#import "BasePerson-Private.h"
...
DerivedPerson1.h
#import "BasePerson-Private.h"
#inteface DerivedPerson1 : BasePerson
...
#end
Now any class that #imports BasePerson.h will only see the public methods. As I said though, this is only emulating access control since if a class #imports *BasePerson-Private.h" they will see the private members; this is just how C/Objective-C is.
We can achieve using #protected access specifier
#interface BasePerson : NSObject {
#protected NSMutableArray *savedPersons;
#protected BOOL shouldSavePerson;
}
DerivedPerson1.m
#implementation DerivedPerson1
- (instancetype)init
{
if (self = [super init]) {
self->savedPersons = [NSMutableArray array];
self->shouldSavePerson = NO;
}
return self;
}
#end
OtherClass.m
#import "OtherClass.h"
#import "BasePerson.h"
#implementation OtherClass
- (void)awakeFromNib {
BasePerson *base = [[BasePerson alloc]init];
base->savedPersons = #[];//Getting Error. Because it is not a subclass.
}
#end
Throughout my app, I'm getting semantic issue warnings when I set ViewController.delegate = self. I have searched and found similar posts but none were able to solve my problem.
ViewController.m:
GameAddViewController *gameAddViewContoller = [[navigationController viewControllers] objectAtIndex:0];
gameAddViewContoller.delegate=self;
I get the error message when setting .delegate=self.
GameAddViewController.h:
#protocol GameAddViewControllerDelegate <NSObject>
- (void)gameAddViewControllerDidCancel:(GameAddViewController *)controller;
- (void)gameAddViewController:(GameAddViewController *)controller didAddGame:(Game *) game;
#end
#interface GameAddViewController : UITableViewController <GameAddViewControllerDelegate>
{
sqlite3 *pitchcountDB;
NSString *dbPath;
}
#property (nonatomic, strong) id <GameAddViewControllerDelegate> delegate;
...
#end
ViewController.h:
#import "GameAddViewController.h"
#class ViewController;
#protocol ViewControllerDelegate <NSObject>
- (void)ViewControllerDidCancel:(ViewController *)controller;
#end
#interface ViewController : UIViewController <ViewControllerDelegate>
-(void) checkAndCreateFile;
#end
Can anyone point me in the right direction to resolve the warning messages?
At this line :
gameAddViewContoller.delegate=self;
Notice that self is of type ViewController which does NOT conform to the GameAddViewController protocol.
For me what ended up happening is that I wasn't adding the delegate to the #interface on my header file
For example
#interface TheNameOfYourClass : UIViewController <TheDelegatorClassDelegate>
#end
You are putting the < GameAddViewControllerDelegate > in the wrong place. It doesn't go on GameAddViewController, it goes on ViewController.
This might help other people who are adding Multipeer Connectivity straight to a ViewController. At the top of myViewControllerName.h add '<MCSessionDelegate>':
#interface myViewControllerName : UIViewController<MCSessionDelegate>
also, if you define your delegate on xx.m, but you use it in other class. you may get this problem. so, just put protocol define on xx.h, when it is needed.
If you have a hybrid project, the protocol in Swift and the assignment in Objective-C:
Swift declaration:
protocol BackgroundTasking {
func beginTask(withName: String, expirationHandler handler: (()->Void)?)
func endTask(withName: String)
}
Objective-C assignment:
#property (nonatomic) id<BackgroundTasking> backgroundTasker;
_backgroundTasker = [[BackgroundTasker alloc] init]; // WARNING
Assigning to '__strong id' from incompatible type 'BackgroundTasker *'
You need to declare the class (to remove this warning) and the functions (to make them accessible) as #objc for them to be correctly bridged.
Correct Swift declaration:
#objc protocol BackgroundTasking {
#objc func beginTask(withName: String, expirationHandler handler: (()->Void)?)
#objc func endTask(withName: String)
}
On hybrid projects you should add your delegates on .h file instead of .m file