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.
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'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.
I want to access super class iBoutlets objetcs in my subclass. Is that possible?. I am trying in the following way but am always getting nil.
Here is my code:
My super class
#import <UIKit/UIKit.h>
#interface SuperClassA : UIViewController {
}
#property (weak, nonatomic, getter=getDummyView) IBOutlet UIView *dummyView;
#end
#implementation SuperClassA
- (void)viewDidLoad {
[super viewDidLoad];
SubClassB *obj = [SubClassB new];
[obj printSuperClassiBouletObject];
}
#end
my subclass:
#import <UIKit/UIKit.h>
#interface SubClassB : SuperClassA {
}
#end
#implementation SubClassB
-(void)printSuperClassiBouletObject
{
NSLog(#"view: %#", [self getDummyView]);
}
#end
The above code gives me nil value always. Any idea how to get the actual iBoutlet object?. But when i pass the iBoutlet as an function argument then the object was not nil. In the super class i tried strong property, using #synthesize in implementation file but no helps. Any help that really might be appreciated.
My guess is that the problem is here:
SubClassB *obj = [SubClassB new];
[obj printSuperClassiBouletObject];
New will use default initializer, that will try to load nib with your class name, which is SubClassB. Do you have SubClassB.xib that set the outlet your project? If not, then SubClassB will be initialized with empty properties, by the default objc initializer.
Try this:
SubClassB *obj = [[SubClassB alloc] initWithNibName:#"SuperClassA"]; //or xib name that SuperClassA uses to initialize
There's nothing special about IBOutlet relative to the scope rules. You can inherit any property from super by including its declaration in the subclass. The simple way to do this is by placing those property declarations in the superclass's public interface (in superclass.h). Since we know that the subclass must import that, we know that everything in there will be available to the subclass.
A more complicated arrangement is required if you'd like the subclass to access the property but not other classes. These "protected" declarations need to go into a third header file that only the super and subclass import.
In other words... (simple case):
// MySuper.h
#interface MySuper : NSObject
#property (weak,nonatomic) IBOutlet UIView *someOutlet;
#end
// MySub.h
#import "MySuper.h"
#interface MySub : MySuper
// stuff for my sub
#end
Now both MySuper and MySub can refer to someOutlet
For "protected", something like this:
// MySuper.h
#interface MySuper : NSObject
// only public stuff here
#end
// MySuper-protected.h
#interface MySuper ()
#property (weak,nonatomic) IBOutlet UIView *someOutlet;
#end
// MySuper.m
#import "MySuper.h"
#import "MySuper-protected.h"
// MySub.h
// same as simple case
// MySub.m
#import "MySub.h"
#import "MySuper-protected.h"
Finally, i was able to achieve this by using this way!!!!
In my subclass i changed the method to:
-(void)printSuperClassiBouletObject:(SuperClassA*)superClassObj
and in my super class i am calling this way:
SubClassB *obj = [SubClassB new];
[obj printSuperClassiBouletObject:self];
And in my subclass i was access the super class objects and its variable from the super class instance it self.
Thanks for all the help !!!!!! :-)
Since the ViewController's code is getting too large, I was wondering how to split the code into multiple files. Here's the problem I ran into:
// In the original .m file, there are bunch of outlets in the interface extension.
#interface aViewController()
#property (weak, nonatomic) IBOutlet UIView *contentView1;
#property (weak, nonatomic) IBOutlet UIView *contentView2;
#property (weak, nonatomic) IBOutlet UIView *contentView3;
#end
I want to split the file into 3 categories, according to three different views.
// In category aViewController+contentView1.m file
#interface aViewController()
#property (weak, nonatomic) IBOutlet UIView *contentView1;
#end
If I delete the original contentView1 outlet, however, it doesn't work.
Question
Why do I have to keep the contentView1 outlet in the original .m file?
An Objective-C category doesn't allow you to add additional properties to a class, only methods. Thereby, you aren't allowed to add additional IBOutlets within a category. A category is denoted similar to #interface aViewController (MyCategoryName) (note the name given inside the parentheses).
You can, however, add additional properties within a class extension. A class extension is denoted with the same name as the original class followed by (). In your code example, both lines referring to #interface aViewController() actually declare a class extension (not a category), regardless of which header file they're actually in.
Furthermore, you are allowed to create multiple class extensions across several different headers. The trick is that you need to import these correctly.
In example, let's consider a class called ViewController. We want to create ViewController+Private.h and ViewController+Private2.h that have additional privateView outlets, which will still be accessible within ViewController.m.
Here's how we can do it:
ViewController.h
// Nothing special here
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController
// some public properties go here
#end
ViewController+Private.h
// Note that we import the public header file
#import "ViewController.h"
#interface ViewController()
#property (nonatomic, weak) IBOutlet UIView *privateView;
#end
ViewController+Private2.h
// Note again we import the public header file
#import "ViewController.h"
#interface ViewController()
#property (nonatomic, weak) IBOutlet UIView *privateView2;
#end
ViewController.m
// Here's where the magic is
// We import each of the class extensions in the implementation file
#import "ViewController.h"
#import "ViewController+Private.h"
#import "ViewController+Private2.h"
#implementation ViewController
// We can also setup a basic test to make sure it's working.
// Just also make sure your IBOutlets are actually hooked up in Interface Builder
- (void)viewDidLoad
{
[super viewDidLoad];
self.privateView.backgroundColor = [UIColor redColor];
self.privateView2.backgroundColor = [UIColor greenColor];
}
#end
And that's how we can do it.
Why Your Code Wasn't Working
Most likely, you've probably mixed up the #import statements. To fix this,
1) Make sure that each class extension file imports the original class header (i.e. ViewController.h)
2) Make sure that the class implementation (i.e. ViewController.m) file imports each of the class extension headers.
3) Make sure the class header (i.e. ViewController.h) file doesn't import any of the class extension headers.
For reference, you can also checkout the Apple docs on Customizing Existing Classes.
I have started new iOS project and have added only one property in ViewControler header file. but it gives me error:
expected specifier-qualifier-list before 'property'
here is the code:
#import <UIKit/UIKit.h>
#interface QuoteGenViewController : UIViewController {
#property (retain) NSArray *myQuotes;
}
#end
Here the general structure of a class interface
#interface Class : Superclass
{
// Instance variable declarations.
// Theirs definition could be omitted since you can declare them
// in the implementation block or synthesize them using declared properties.
}
// Method and property declarations.
#end
Since a property provides a simple way to declare and implement an object’s accessor methods (getter/setter), you need to put them in the Method and property declarations section.
I really suggest to read ocDefiningClasses doc for this.
Hope that helps.
Your code should look like this:
#import <UIKit/UIKit.h>
#interface QuoteGenViewController : UIViewController {
}
#property (retain) NSArray *myQuotes;
#end