Accessing Obj-C properties in Swift extension file - objective-c

I started writing Swift extensions on my view controllers. So I have three files right now:
My header file, ViewController.h:
#interface MyViewController : UIViewController
#end
My Obj-C implementation file, ViewController.m:
#interface MyViewController () <UIScrollViewDelegate>
#property (strong, nonatomic) UIScrollView *scrollView;
#end
#implementation MyViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.scrollView = [[UIScrollView alloc] init];
[self customiseScrollView]; //This is Swift method called from Obj-C file
}
#end
And finally, ViewController.swift:
extension MyViewController {
func customiseScrollView() {
}
}
My question is, is it possible to access my Obj-C properties from my Swift implementation file? Every time I refer to it, I got an error:
Value of type 'MyViewController' has no member 'scrollView'
Bonus 1: Can someone also clarify if the Swift component can see what the .m is a delegate of as well. (To clarify, doing scrollView.delegate = self in Swift is a compile error because the Swift file does not realise that the .m file is a UIScrollViewDelegate).
Bonus 2: Can Swift extension file call Obj-C methods that are declared from the .m counterpart?

I think that you can't access private properties from extension. Your scrollView property is in .m file, not .h - which means it's private and it's not visible from extension file.
Solution: move
#property (strong, nonatomic) UIScrollView *scrollView;
to your header file.

You can access internal objc properties and methods if you declare the objc class extension in a separate header and include that header in the bridging header.
MyClass.h
#interface MyClass : NSObject
#property (nonatomic, copy, readonly) NSString *string;
#end
MyClass+Private.h
#import "MyClass.h"
#interface MyClass ()
#property (nonatomic, copy) NSString *string;
#end
MyClass.m
#import "MyClass+private.h"
#implementation MyClass
//...
#end
Project-Bridging-Header.h
#import "MyClass.h"
#import "MyClass+Private.h"

In Swift, private properties are not accessible from another file. This is the meaning of private in Swift. For example:
file1.swift
class MyClass {
private var privateProperty: String = "Can't get to me from another file!"
}
extension MyClass: CustomStringConvertible {
var description: String {
return "I have a `var` that says: \(privateProperty)"
}
}
file2.swift
extension MyClass {
func cantGetToPrivateProperties() {
self.privateProperty // Value of type 'MyClass' has no memeber 'privateProperty'
}
}
A property declared in the implementation of an Objective-C class is a private property. As such, the property cannot be accessed from a Swift extension since this will be necessarily from a different (.swift) file...

You can. All you need is to create a bridging objective C header.
Per Apple Documentation:
To import a set of Objective-C files in the same app target as your
Swift code, you rely on an Objective-C bridging header to expose those
files to Swift. Xcode offers to create this header file when you add a
Swift file to an existing Objective-C app, or an Objective-C file to
an existing Swift app.

Just create a Bridging-Header file and later import your ObjC file in it, like:
#import <objectivec.h>
Later in your swift file:
var a = objectivec()
a.method_from_those_file
For more information read Apple Doucmentation from here.

Related

Swift class using Objective-C class using Swift class

I have an obj-c project to which I successfully added a new Swift class A, which is being used by some existing obj-c class B - the use of the automatically generated "MyProject-Swift.h" header worked as expected.
I also successfully added a new Swift class C that uses some existing obj-c class D - the use of the bridging header also worked as expected.
However, suppose I want to refer from my Swift class C to the existing obj-c class B (which in turn refers to the new Swift class A). In order to do that I need to import "B.h" to the bridging header. However, if I do that I get an error in class B: "'MyProject-Swift.h' file not found" (i.e., the file is no longer generated).
Am I doing something wrong or is this a kind of interaction between Swift and Objective-C that is not allowed? It looks like there is a kind of circular reference that the compiler is unable to solve.
--- EDIT ---
I'll try to make the question clearer by adding some code.
-- PREAMBLE --
I added a new Swift class to an obj-c project:
// SwiftClassA.swift
import Foundation
#objc class SwiftClassA : NSObject {
var myProperty = 0
}
The code compiles correctly and is translated into obj-c stubs in the automatically generated "MyProject-Swift.h" header like so:
// MyProject-Swift.h
...
SWIFT_CLASS("_TtC7MyProject11SwiftClassA")
#interface SwiftClassA : NSObject
#property (nonatomic) NSInteger myProperty;
- (instancetype)init OBJC_DESIGNATED_INITIALIZER;
#end
Now, one obj-c class uses SwiftClassA:
// ObjCClass.h
#import <Foundation/Foundation.h>
#import <MyProject-Swift.h>
#interface ObjCClass : NSObject
#property (nonatomic, strong) SwiftClassA *aProperty;
#property (nonatomic) int *aNumber;
#end
This also works seamlessly.
-- THE QUESTION --
Can I now create a new Swift class that refers to the obj-c class (ObjCClass) that is using the Swift class SwiftClassA?
This is what I can't do.
If I add the new Swift class:
// SwiftClassB.swift
import Foundation
#objc class SwiftClassB : NSObject {
var aPropertyOfClassB = 1
func someFunc() {
var objCObject = ObjCClass()
var theProperty = objCObject.aProperty
print("The property is \(theProperty)")
}
}
this of course won't compile because of "Use of unresolved identifier 'ObjCClass'". So I need to add that to the bridging header file:
// BridgingHeader.h
#ifndef MyProject_BridgingHeader_h
#define MyProject_BridgingHeader_h
...
#import "ObjCClass.h"
#endif
However, if I do that, the ObjCClass.h file won't compile giving a "'MyProject-Swift.h' file not found".
I've read in several places (with no example, though) that this may mean that there is a circular reference and that a forward reference using #class could solve the problem. However, I'm not sure what needs to be forward referenced and where, and all my attempts failed.
I hope the question is no longer confusing now!
This is a typical cyclical referencing problem.
Be careful to read the docs:
To avoid cyclical references, don’t import Swift into an Objective-C header file. Instead, you can forward declare a Swift class to use it in an Objective-C header. Note that you cannot subclass a Swift class in Objective-C.
So, you should use "forward declare" in .h, and #import in .m:
// ObjCClass.h
#import <Foundation/Foundation.h>
#class SwiftClassA;
#interface ObjCClass : NSObject
#property (nonatomic, strong) SwiftClassA *aProperty;
#property (nonatomic) int *aNumber;
#end
// ObjCClass.m
#import "ObjCClass.h"
#import "MyProject-Swift.h"
#implementation ObjCClass
// your code
#end

How to call Objective-C++ (.mm) from Objective-C (.m)

Is there a say to do this without changing every .m file to .mm?
OK. I am trying to implement the answer but having trouble. Take a look at my Objective C++ .h and .mm below
Objective-C++ - IDCaptureTemplateCommand.h:
#include "Template.h"
#interface IDCaptureTemplateCommand : NSObject
#property (nonatomic, strong) IDCaptureTemplateCommand *IDCaptureTemplateCommand;
#end
Objective-C++ - IDCaptureTemplateCommand.mm:
#include "IDCaptureTemplateCommand.h"
#implementation IDCaptureTemplateCommand
- (id)init
{
self = [super init];
if (self) {
self.captureTemplateCommand = [[IDCaptureTemplateCommand alloc] init];
}
return self;
}
#end
Objective-C - IDCameraViewController.h
#import <UIKit/UIKit.h>
#import "IDCaptureTemplateCommand.h"
#interface IDCameraViewController : UIViewController <UINavigationControllerDelegate>
#property (nonatomic) IDCaptureTemplateCommand *captureCommand; //ERROR - Unknown type name 'IDCaptureTemplateCommand'
#end
You can do so in the same way as you can use C++ from C or whatever. You need to be able to declare the interface using pure Objective-C and then the implementation can be written using Objective-C++.
If your header file uses C++, e.g. your class has an std::string instance variable, then to make the functionality accessible from Objective-C you have to write a wrapper or otherwise hide the C++ at the interface, so that your Objective-C files don't need to see any of the C++ declarations.

Having two #interfaces in the same .h file

I am making a photo upload to Facebook app and I think I need two #interfaces in my .h file for my View Controller.
Here is my ViewController.h file.
#import <UIKit/UIKit.h>
#import <Social/Social.h>
#interface FirstViewController : UIViewController <UIImagePickerControllerDelegate, UINavigationControllerDelegate> {
UIImagePickerController *bailey;
UIImagePickerController *baileys;
UIImage *image;
IBOutlet UIImageView *imageView;
}
- (IBAction)TakePhoto;
- (IBAction)ChooseExisting;
#end
#interface FirstViewController : UIViewController { SLComposeViewController *slComposeViewController;
UIImage *image; }
- (IBAction)ShareFB;
#end
When I try to build this code on to my iPhone or a Emulator it says
/Users/Condrum/Desktop/project/myApp/myApp/FirstViewController.h:21:1: Duplicate interface definition for class 'FirstViewController'
Thanks in advance for the help.
-Condrum.
The pattern is to put a single public interface into the .h file:
#interface FirstViewController : UIViewController
// in here put those public properties and method declarations that
// other classes need to have access to
#end
Then put the second #implementation in the .m file as a private class extension:
#interface FirstViewController () <UIImagePickerControllerDelegate, UINavigationControllerDelegate>
// in here, place those private properties and instance variables that
// only this class needs to be aware of
#end
Note, this second interface uses the () syntax, which indicates that the interface is extending a previously defined interface.
But there's no point in putting both of those interfaces in the same .h file (why have two interfaces; it would be more logical to combine them into one). The primary value of the private class extension is you can extend your interface with details that only the implementation cares about, and avoid cluttering your nice simple public interface. So generally, keep public interface in the .h file, and move the private stuff into the class extension in the .m file.
For more information, see Class Extensions Extend the Internal Implementation.

How to simulate protected properties and methods in objective-c [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Protected methods in objective-c
The way to declare private properties is simple.
You declare that in extension that's declared in .m files.
Say I want to declare protected properties and access it from the class and subclass.
This is what I tried:
//
// BGGoogleMap+protected.h
//
//
#import "BGGoogleMap.h"
#interface BGGoogleMap ()
#property (strong,nonatomic) NSString * protectedHello;
#end
That one is compile. Then I added:
#import "BGGoogleMap+protected.h"
#implementation BGGoogleMap ()
-(NSString *) protectedHello
{
return _
}
#end
Problem starts. I can't implement class extension outside the original .m files it seems. Xcode will demand something inside that bracket.
If I do
#import "BGGoogleMap+protected.h"
#implementation BGGoogleMap (protected)
-(NSString *) protectedHello
{
return _
}
#end
I cannot access the ivar of _protectedHello declared in BGGoogleMap+protected.h
Of course I can use regular category rather than extension, but that means I can't have protected properties.
So what should I do?
The Objective-C Programming Language says this:
Class extensions are like anonymous categories, except that the methods they declare must be implemented in the main #implementation block for the corresponding class.
So you could just implement your class extension's methods in the class's main #implementation. That is the simplest solution.
A more complicated solution is to declare your “protected” messages and properties in a category, and declare any instance variables for that category in a class extension. Here's the category:
BGGoogleMap+protected.h
#import "BGGoogleMap.h"
#interface BGGoogleMap (protected)
#property (nonatomic) NSString * protectedHello;
#end
Since a category cannot add an instance variable to hold protectedHello, we need a class extension also:
`BGGoogleMap_protectedInstanceVariables.h'
#import "BGGoogleMap.h"
#interface BGGoogleMap () {
NSString *_protectedHello;
}
#end
We need to include the class extension in the main #implementation file so that the compiler will emit the instance variable in the .o file:
BGGoogleMap.m
#import "BGGoogleMap.h"
#import "BGGoogleMap_protectedInstanceVariables.h"
#implementation BGGoogleMap
...
And we need to include the class extension in the category #implementation file so that the category methods can access the instance variables. Since we declared the protectedHello property in a category, the compiler will not synthesize the setter and getter method. We have to write them by hand:
BGGoogleMap+protected.m
#import "BGGoogleMap+protected.h"
#implementation BGGoogleMap (protected)
- (void)setProtectedHello:(NSString *)newValue {
_protectedHello = newValue; // assuming ARC
}
- (NSString *)protectedHello {
return _protectedHello;
}
#end
Subclasses should import BGGoogleMap+protected.h to be able to use the protectedHello property. They should not import BGGoogleMap_protectedInstanceVariables.h because the instance variables should be treated as private to the base class. If you ship a static library without source code, and you want users of the library to be able to subclass BGGoogleMap, ship the BGGoogleMap.h and BGGoogleMap+protected.h headers, but don't ship the BGGoogleMap_protectedInstanceVariables.h header.
I wish I could tell you otherwise but you just can't. See this question for more information: Protected methods in Objective-C.
I am not sure, what you want to do? Something Hacking or Cracking of Data Abstraction out of OOPS concept?
Extensions are used to add properties. You have successfully added private property as in
#import "BGGoogleMap.h"
#interface BGGoogleMap ()
#property (strong,nonatomic) NSString * protectedHello;
#end
What are you doing in this ?
#import "BGGoogleMap+protected.h"
#implementation BGGoogleMap ()
-(NSString *) protectedHello
{
return _
}
#end
You have extended a class, now you are again implementing same class !!! Twice!!! And category only comes with .h file. I guess you are creating yourself a .m file, that not acceptable.
Private properties cant be accessed outside the class, it can be accessed only from the base class or subclass. That is what the error is.
I can't implement class extension outside the original .m files it seems.
Yes this is abstraction and data hiding of Objective-c !!!

Is there any way to add an iVar that's not in the header file (not using LLVM 2.0 or later) in Objective-C?

I recently learned that you can add ivar in a class extension with LLVM2.0. (gcc can't do this)
This is somehow really private iVar because other users don't it's existence since it's not in the header file.
like:
//SomeClass.h
#interface SomeClass : NSObject {
}
#end
//SomeClass.m
#interface SomeClass ()
{
NSString *reallyPrivateString;
}
#end
#implementation SomeClass
#end
But this does rely on the compiler. Is there any other way to declare an ivar that's not in the header file?
The only place to declare instance variables is in the interface or a class extension (which is really an extension of the interface). But you can effectively add instance variables at any time with the modern runtime using the associated object functions.
If you are implementing a library and want to hide your instance variables take a look at what Apple does in the interface for UIWebView. They have an internal webview that does not expose a header file.
#class UIWebViewInternal;
#protocol UIWebViewDelegate;
UIKIT_CLASS_AVAILABLE(2_0) #interface UIWebView : UIView <NSCoding, UIScrollViewDelegate> {
#private
UIWebViewInternal *_internal;
}
If you're just going to be using the ivar internally, and you're using the modern runtime (Snow Leopard 64 bit and iOS 3.0+, I think) then you can just declare properties in a class extension and synthesize them inside the class. No ivars are exposed in your header, no messy id _internal objects, and you get around fragile ivars, too.
// public header
#interface MyClass : NSObject {
// no ivars
}
- (void)someMethod;
#end
// MyClass.m
#interface MyClass ()
#property (nonatomic, retain) NSString *privateString;
#end
#implementation MyClass
#synthesize privateString;
- (void)someMethod {
self.privateString = #"Hello";
NSLog(#"self.privateString = %#", self.privateString);
NSLog(#"privateString (direct variable access) = %#", privateString); // The compiler has synthesized not only the property methods, but also actually created this ivar for you. If you wanted to change the name of the ivar, do #synthesize privateString = m_privateString; or whatever your naming convention is
}
#end
This works with Apple's gcc, in addition to LLVM. (I'm not sure if this works on other platforms, ie not Apple's gcc, but it will certainly work for both iOS and Snow Leopard+).