Expose a constant variable from Objective-C to Swift - objective-c

I have to do a bitwise operation that is for some reason only possible in swift so I am looking to initialize some constants with Objective-C to be used within my application.
I am not that great with objective-c yet so the only way I knew how to do this was to create a class and give it a method that returns the value but I figure that there is a more efficient value.
There must be a more succinct way to achieve this. Currently I am doing the following:
Header:
#import <Foundation/Foundation.h>
#include <simd/simd.h>
#import <MetalKit/MetalKit.h>
#import <Metal/Metal.h>
#interface Bridge:NSObject
#property NSString *url;
- (MTLTextureUsage)readAndWrite;
#end
Implementation:
#import "MPS-Bridging-Header.h"
#implementation Bridge
- (MTLTextureUsage)readAndWrite {
return MTLTextureUsageShaderRead | MTLTextureUsageRenderTarget | MTLTextureUsageShaderWrite;
}
#end
Swift Usage:
let bridge = Bridge()
Texture.usage = bridge.readAndWrite()
It would be great if this could be simplified to like
MTLTexReadAndWrite as if it were a constant or perhaps have it so that I can do Bridge().readAndWrite() so it is all on one line?

If you wanted to expose this to Swift, I'd define a class property:
// Bridge.h
#import Foundation;
#import Metal;
#interface Bridge : NSObject
#property (class, nonatomic, readonly) MTLTextureUsage readAndWrite;
#end
And
// Bridge.m
#import "Bridge.h"
#implementation Bridge
+ (MTLTextureUsage)readAndWrite {
return MTLTextureUsageShaderRead | MTLTextureUsageRenderTarget | MTLTextureUsageShaderWrite;
}
#end
And you could then use it like so:
let readAndWrite = Bridge.readAndWrite
But I wonder why you don't just define this constant in Swift:
let readAndWrite: MTLTextureUsage = [.shaderRead, .renderTarget, .shaderWrite]
If you need the same constant in both Objective-C and Swift, use the above bridging pattern, but if you only need it in Swift, then I'd just define it there and eliminate Bridge altogether.

Related

Swift protocol in Objective-C category

I have a protocol defined in Swift:
#objc public protocol UploadProtocol: class {
func isUploaded() -> Bool
...
}
I'm importing my ProjectName-Swift.h file in my ProjectName_Prefix.pch file (old project).
In Objective-C I have a category on a class which implements the protocol. I'm trying to figure out how to let the compiler know about that implementation. This is what I have been doing:
// Person+Upload.h
#import "Person.h"
#interface Person (Upload)
- (BOOL)isUploaded;
#end
// Person+Upload.m
#import "Person+Upload.h"
#implementation Person (Upload)
...
#end
This gives me:
Cannot find protocol definition for 'UploadProtocol'
I can only think of three other ways of setting this up and all of them have problems. What am I missing?
First Way (Apple Recommended)
In Using Swift with Cocoa and Objective-C (Swift 2.2), Apple says, "An Objective-C class can adopt a Swift protocol in its implementation (.m) file by importing the Xcode-generated header for Swift code and using a class extension." But their example is for a standard class. If I try to do this with my category:
// Person+Upload.h
#import "Person.h"
#interface Person (Upload)
- (BOOL)isUploaded;
#end
// Person+Upload.m
#import "Person+Upload.h"
#interface Person (Upload) <UploadProtocol>
#end
#implementation Person (Upload)
...
#end
This results in:
Duplicate definition of category 'Upload' on interface 'Person'
Second Way
Ok, so I want to avoid defining the upload class twice. So I'll remove it from the .h.
// Person+Upload.h
#import "Person.h"
#interface Person ()
- (BOOL)isUploaded;
#end
// Person+Upload.m
#import "Person+Upload.h"
#interface Person (Upload) <UploadProtocol>
#end
#implementation Person (Upload)
...
#end
In this case I get:
Category is implementing a method which will also be implemented by its primary class
I'm currently declaring the method in the .h so that other classes can call some of those methods without casting to the protocol to access that method.
Third Way
If I leave the category name in the .h but take it off in the .m
// Person+Upload.h
#import "Person.h"
#interface Person (Upload)
- (BOOL)isUploaded;
#end
// Person+Upload.m
#import "Person+Upload.h"
#interface Person () <UploadProtocol>
#end
#implementation Person (Upload)
...
#end
Then I get:
Category is implementing a method which will also be implemented by its primary class
Is the only solution to cast it to the protocol every time? Adding this type of line in multiple places seems like a code smell:
Person <Upload> *s = (Person <Upload> *)self;
Any other solutions?
Thanks for the interesting question!
Your initial approach will actually work with an addition of a forward declaration of the protocol:
// Person+Upload.h
#import "Person.h"
#protocol UploadProtocol; // NOTE THIS!!!
#interface Person (Upload) <UploadProtocol>
//- (BOOL)isUploaded; // This is not really needed
#end
// Person+Upload.m
#import "Person+Upload.h"
#import "...-Swift.h" // IMPORTANT!!!
#implementation Person (Upload)
// HERE you implement isUploaded, of course.
...
#end
This will still cause a compiler warning Cannot find protocol definition for 'UploadProtocol', but it should work.
The Apple-recommended way also works, the Duplicate definition of category is just a warning, but Person won't be recognized as conforming to the protocol by Swift code, even though its conformance will be recognized by Objective-C. The initial approach will be fine in Swift, too.

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.

Making class conform to protocol with category for existing methods

I have a protocol named MyProtocol.
MyProtocol has an required method:
- (NSUInteger)length;
And some other methods.
Now i want to make the NSString class conform to MyProtocol with a category. Like so:
#interface NSString (NSStringWithMyProtocol) <MyProtocol>
#end
In this category i implement all methods excluding the 'length' method, because i want the original NSString implementation.
I do not want to override it in this particular class.
Now i get a warning because of an incomplete implementation of MyProtocol in the category.
I know there are a few solutions to solve this.
Make the method optional
Pointer Swizzling
Adding subclass to class which is conform to the protocol. Then leave out the implementation.
I do not want to use these options because they result in a bad design for the rest of my code.
Option 3 is bad, because of existing direct subclasses will not be conform to the protocol.
Does anybody know how to remove the warning without implementing the length method?
NOTE: The class, category and protocol are just examples. I did encounter this problem with other classes which i could not post about.
Thanks
EDIT: Added the third option.
Full Code:
The protocol:
#protocol MyProtocol <NSObject>
- (void) myMethod;
- (NSInteger) length;
#end
The category header:
#import <Foundation/Foundation.h>
#import "MyProtocol.h"
#interface NSString (MyProtocol) <MyProtocol>
#end
The category implementation:
#implementation NSString (MyProtocol)
- (void)myMethod {
}
#end
This results in the following warnings.
Incomplete implementation
Method in protocol not implemented
In this screenshot you can see my warning:
I tried compiling with LLVM GCC 4.2 and the Apple LLVM 3.0 compiler.
I also compiled on xcode 4.0.2 and Xcode 4.2.
I'm on OS X 10.6.8.
I cannot reproduce this issue. Can you post code that demonstrates it? The following compiles without warnings on 10.7.
#import <Foundation/Foundation.h>
#protocol MyProtocol <NSObject>
- (NSUInteger)length;
#end
#interface NSString (NSStringWithMyProtocol) <MyProtocol>
#end
int main (int argc, const char * argv[]) {
#autoreleasepool {
id<MyProtocol> foo = #"foo";
NSLog(#"%#", foo);
}
return 0;
}
You might consider this a bit of a hack, and I don't know if it will even work, but how about declaring it as a read only property
#property (readonly, assign) NSUInteger length;
and then in the implementation of your category, make it #dynamic

Dynamically typed class generates compiler warnings on method selection

Perhaps this is the wrong way to go about this, but it seems like such a clean and workable approach that I wonder how I can make the compiler warning go away?
#interface SomeView : UIView {
NSString *stringOfsomeImportance;
RelatedClass *niftyService;
}
#property (nonatomic, copy) NSString * stringOfnoImportance;
#property (nonatomic, retain) RelatedClass *niftyService;
#implementation
-(void)someMethod;
-(void)otherMethods;
#implementation RelatedClass *pvSomeObj = [[RelatedClass alloc] initWithSender:self];
[self setNiftyService:pvSomeObj];
Now, looking at the RelatedClass implementations...
#interface RelatedClass : NSObject {
id thesender;
#property (nonatomic, retain) id thesender;
#implementation
[thesender otherMethods]; // this generates a compiler warning
// that otherMethods cannot be found
// in SomeView, though it *is* found
// and seems to execute just fine
This seems like a valid approach, so I'm left wondering why the warning?
Is there a way to better "explain" this to the compiler?
Could someone kindly share if this type of linkage is encouraged or if there is a better way to link two related, interdependent classes that need to communicate with one another?
I can't statically declare the sender object (SomeView) in RelatedClass because that seems to cause a recursion problem, as SomeView is defined with RelatedClass as a member...
Any suggestions?
You can define a protocol and say that your thesender object must conform to it:
#protocol MyProtocol
-(void)otherMethods;
#end
#interface RelatedClass : NSObject {
id<MyProtocol> thesender; // Now compiler knows that thesender must respond
// to otherMethods and won't generate warnings
}
You can send otherMethods message another way (you may need to define theSender as NSObject here):
if ([theSender respondsToSelector:#selector(otherMethods)])
[theSender performSelector:#selector(otherMethods)];
Edit: Actually you can also define thesender as SomeView* in your RelatedClass using forward class declaration:
//SomeView.h
#class RelatedClass;
#interface SomeView : UIView {
RelatedClass *niftyService;
}
// then include RelatedClass.h in SomeView.m
//RelatedView.h
#class SomeView;
#interface RelatedClass : NSObject {
SomeView* thesender;
}
// then include SomeView.h in RelatedClass.m
In your headers, you can forward declare classes that you want to use. In your implementation files, you can include the full header of those classes that you forward-declared.
For example:
SomeView.h
#import <FrameworkHeader.h>
// Here, you are saying that there is a class called RelatedClass, but it will be
// defined later.
#class RelatedClass;
#interface SomeView : UIView
{
RelatedClass *niftyService;
}
#end
SomeView.m
#import "SomeView.h"
#import "RelatedClass.h"
// By including "RelatedClass.h" you have fulfilled the forward declaration.
#implementation SomeView
// Can use "RelatedClass" methods from within here without warnings.
#end
RelatedClass.h
#import <FrameworkHeader.h>
#class SomeView;
#interface RelatedClass
{
SomeView *someView;
}
// methods
#end
RelatedClass.m
#import "RelatedClass.h"
#import "SomeView.h"
#implementation RelatedClass
// Can use "SomeView" methods from within here without warnings.
#end
id thesender = ....;
[thesender otherMethods]; // this generates a compiler warning
// that otherMethods cannot be found
// in SomeView, though it *is* found
// and seems to execute just fine
For the above to generate the warning as you describe, it is entirely because the method -otherMethods has not been declared someplace where the compiler sees the declaration before attempting to compile the call site.
That is, the declaration of the method:
- (void) otherMethods;
Must appear in a header file that is imported -- directly or indirectly -- by the implementation file compiling that particular call site or the method declaration must appear in the #implementation before the call site.