I am trying to provide an OS X service, but I am not sure how to translate the following Objective C documentation example to Swift. Any pointers (forgive the pan) would be appreciated!
From Apple's documentation (section 'Implementing the Service Method'):
- (void)simpleEncrypt:(NSPasteboard *)pboard userData:(NSString *)userData error:(NSString **)error {
//...
}
Is this the equivalent in Swift?
func simpleEncrypt(pboard: NSPasteboard!, userData: String?, error: AutoreleasingUnsafeMutablePointer<String?>) {
//...
}
Or should it be:
func simpleEncrypt(pboard: NSPasteboard, userData: String, inout error: String?) {
//...
}
Or indeed something entirely different?
Upon more careful reading of the documentation:
import AppKit
#objc public class Servicer: NSObject {
#objc public func service(pboard: NSPasteboard?, userData: String?, error: AutoreleasingUnsafeMutablePointer<NSString?>) {
error.memory = "Not yet implemented!"
}
}
This is ported to obj-c in "Servicer-Swift.h" as:
SWIFT_CLASS("_TtC8Servicer8Servicer")
#interface Servicer : NSObject
- (void)service:(NSPasteboard *)pboard userData:(NSString *)userData error:(NSString * *)error;
- (instancetype)init OBJC_DESIGNATED_INITIALIZER;
#end
pboard can probably be an implicitly unwrapped optional (NSPasteboard!) or even a plain reference (NSPasteboard), because it is very unlikely that the os will call this method with a nil pasteboard...
Related
I'm trying to observe a property (which is declared within an Objective-C) in Swift.
Objective-C protocol:
#protocol DemoViewModel <NSObject>
#property (nonatomic, strong) NSString *bla;
#end
Swift observe:
#objc public dynamic var vm: (NSObject & DemoViewModel) {
didSet {
vm.observe(#keyPath(DemoViewModel.bla)) { _,_ in
//do something
}
}
}
Interestingly I receive an error:
Member ‘observe’ cannot be used on value of protocol type ‘NSObject & DemoViewModel’; use a generic constraint instead
Any idea what's going on?
Thanks
There must be something special under the hood about the observe function, since all the functions and methods can be accessed your way. It's asking you to instead create something like this:
class Foo {
#objc public dynamic var vm: (NSObject & DemoViewModel)?
private var observer: NSKeyValueObservation?
func setVM<T>(_ vm: T) where T: NSObject &: DemoViewModel {
self.vm = vm
observer = vm?.observe(\.bla, options: [.old, .new]) { _, _ in
// do something
}
}
}
When you implement DemoViewModel, remember that you need to mark bla as dynamic, otherwise the observer won't be called.
class Bar: NSObject, DemoViewModel {
#objc dynamic var bla: String = ""
}
I'm trying to access Swift class methods from Objective-C, following this guide
https://developer.apple.com/library/content/documentation/Swift/Conceptual/BuildingCocoaApps/MixandMatch.html
This is my Swift class:
import Foundation
#objc public class MySwiftClass: NSObject {
// modifiers public, open do not help
func Hello()->String {
return "Swift says hello!";
}
}
And here is an excerpt from the autogenerated "umbrella" file MyProductModuleName-Swift.h
SWIFT_CLASS("_TtC25_MyProductModuleName12MySwiftClass")
#interface MySwiftClass : NSObject
- (nonnull instancetype)init OBJC_DESIGNATED_INITIALIZER;
#end
It seems like XCode completely ignores methods of MySwiftClass, and as a result I can't access them from Objective-C. Xcode version is 9.2.
Is it a bug or I missed anything?
You need to add #objc before each function you want to access from Objective-C:
#objc public class MySwiftClass: NSObject {
// modifiers public, open do not help
#objc func Hello() -> String {
return "Swift says hello!";
}
}
We are trying to reference Swift methods inside an Objective-C implementation.
Swift class:
import Foundation
#objc class MySwiftClass: NSObject {
override init() {
super.init()
}
func sayHello() -> Void {
print("hello");
}
func addX(x:Int, andY y:Int) -> Int {
return x+y
}
}
Objective-C implementation (Objective-c.m):
#import "ProductModuleName-Swift.h"
MySwiftClass* getData = [[MySwiftClass alloc]init];
[getData sayHello] //works
[getData addX:5 addY:5] //No visible #interface for 'MySwiftClass' declares selector 'addX:addY'
The last line of code gives the following error:
No visible #interface for 'MySwiftClass' declares selector 'addX:addY'
If you command-click on "ProductModuleName-Swift.h" in the Xcode
source file editor then you can see how the Swift methods are mapped to Objective-C.
In your case that would be
#interface MySwiftClass : NSObject
- (nonnull instancetype)init OBJC_DESIGNATED_INITIALIZER;
- (void)sayHello;
- (NSInteger)addXWithX:(NSInteger)x andY:(NSInteger)y;
#end
which is called as
MySwiftClass* getData = [[MySwiftClass alloc]init];
[getData sayHello];
NSInteger result = [getData addXWithX:5 andY:5];
A better Swift 3 method name might be
func add(x: Int, y:Int) -> Int
because x is already the argument (external) name of the first
parameter. You can also add an #objc() attribute to the Swift definition
to control the Objective-C name. For example, with
#objc(addX:andY:)
func add(x: Int, y: Int) -> Int {
return x+y
}
it would be called from Objective-C as
NSInteger result = [getData addX:5 andY:5];
As #ekscrypto pointed out, in Swift 4 and later you need to annotate individual functions with #objc. Prior to that, a single, class-level #objc was enough.
Of course in Objective-C class you must add import of NAME_PROJECT-swift.h.
If your project name is Sample then you must add:
#import Sample-swift.h
And then:
Swift 4 or Less
#objc class MySwiftClass: NSObject {
func sayHello(){
//function
}
func addX(){
//function
}
}
Swift 4 or Greater
#objc class MySwiftClass: NSObject {
#objc func sayHello(){
//function
}
#objc func addX(){
//function
}
}
In my case I had forgotten to add:
#import "MyProject-Swift.h"
Into the obj c file.
I have this objective-C protocol
#protocol Parcelable <NSObject>
#required
- (instancetype)initWithData:(NSDictionary *)data;
#end
That I can't seem to find a way to implement in a Swift class. I have tried
required init(data: NSDictionary) {
super.init()
... //do something with data
}
and even
func initWithData(data: NSDictionary) -> Self {
... //do something with data
}
But it keeps saying that the class does not conform to protocol 'Parcelable'
Any ideas?
Aha! the correct way to implement this protocol is
required init(data: [NSObject : AnyObject]) {
super.init()
... //do something with data
}
Swift translates NSDictionary to [NSObject : AnyObject] and does not recognize NSDictionary itself (?)
Just trying to get started with Swift and hit the following issue when upgrading to Swift 1.2:
#protocol MyObjcProtocol <NSObject>
#optional
#property (copy) NSString *optionalString;
- (void) optionalMethod;
#end
...
class MySwiftClass: NSObject {}
extension MySwiftClass: MyObjcProtocol {
var optionalString: NSString {
get { return "Foo" }
set(newValue) { NSLog("Whatever") }
}
// No problem here
func optionalMethod() {
NSLog("Bar")
}
}
The Swift extension implementing the Objc protocol doesn't compile with:
Objective-C method 'optionalString' provided by getter for
'optionalString' conflicts with optional requirement getter for
'optionalString' in protocol 'MyObjcProtocol'...
Objective-C method 'setOptionalString:' provided by setter for
'optionalString' conflicts with optional requirement setter for
'optionalString' in protocol 'MyObjcProtocol'...
So clearly the compiler doesn't realise I'm trying to implement the optionals from the protocol, and thinks I'm stomping on the protocol's expected ObjC symbols. The optional method func optionalMethod() compiles just fine, however. Remove #optional from the protocol and everything compiles just fine, but it's not always possible or desirable to do that as a solution.
So, how does one implement this? Trying to implement the expected ObjC methods explicitly doesn't work either:
func optionalString() {
return "foo"
}
func setOptionalString(newValue: NSString) {
NSLog("")
}
Hope someone can help! Thanks in advance!
MyObjcProtocol protocol is translated to Swift as:
protocol MyObjcProtocol : NSObjectProtocol {
optional var optionalString: String! { get set }
optional func optionalMethod()
}
So you can just use String instead of NSString.
extension MySwiftClass: MyObjcProtocol {
var optionalString: String {
get { return "Foo" }
set(newValue) { NSLog("Whatever") }
}
func optionalMethod() {
NSLog("Bar")
}
}
I guess the problem is that the protocol requires a stored property because it's specified copy, but the swift extension cannot have storage for a property
Optional properties in Objective C is equivalent to Optional Type in Swift. So it would translate like this:
var optionalString: String! // = false
in case it can be nil:
var optionalString: String? // = nil