Swift enum in objective c - objective-c

I have this enum in swift:
#objc enum HomeViewDataType: Int {
case statistics
case allTime
}
and this protocol;
#objc(TCHomeViewDataUpdaterDelegate)
protocol HomeViewDataUpdaterDelegate {
....
func homeViewDataType() -> HomeViewDataType
}
If I ask Xcode to add the protocol stubs automatically it will add the enum keyword inside the return type parenthesis:
- (enum HomeViewDataType)homeViewDataType
{
<code>
}
I never seen this before: (enum HomeViewDataType)
Any idea why?
It works with or without the enum keyword btw.

Related

Call Swift func from Objective-C with parameter

I want to integrate a Swift class into an UIViewController class.
I think I made all settings correct. I have a class rURLTask:NSObject with a function:
#objc public func primer() {
print("primer")
}
In my .swift file and can call it from my Objective-C-class with:
[URLTask primer];
and it prints nicely.
Another function is:
#objc public func ladeURL(url: URL?) {
print("loadURL")
}
but this one I cannot call from Objective-C. I try to write:
NSURL* testURL = [NSURL URLWithString:#"http://www.google.com"];
[URLTask ladeURL:testURL];
I get the error:
No visible #interface for 'rURLTask' declares the selector 'ladeURL:'
I think there is a very basic mistake. Using Objective-C in Swift 3 in another project worked well.
The reason you cannot call
#objc public func ladeURL(url: URL?) {
print("loadURL")
}
by saying
[URLTask ladeURL:testURL];
is that ladeURL: is not the name of this method as far as Objective-C is concerned. That is what the compiler means when it says that "No visible #interface for 'rURLTask' declares the selector ladeURL:".
Its name as far as Objective-C is concerned is ladeURLWithUrl:. That is because you have exposed the url: parameter name as an external label.
If it was important to you to be able to say
[URLTask ladeURL:testURL];
in Objective-C, you could have declared ladeURL like this:
#objc public func ladeURL(_ url: URL?) {
print("loadURL")
}
See the underscore? That hides the external label of the parameter.
Another solution, allowing you to keep the url: external label, would be to declare the Objective-C name as part of the objc attribution:
#objc(ladeURL:) public func ladeURL(url: URL?) {
print("loadURL")
}
That says to the compiler: "I know you would like to translate the name of this method into Objective-C as ladeURLwithUrl:, but don't; translate it as ladeURL: instead."
When you import the class to OC , the name of the method written in swift is translated concatenated with the withParameterType , as prime
method
#objc public func primer() {
print("primer")
}
has no parameters it can be called like this
[URLTask primer];
but this
#objc public func ladeURL(url: URL?) {
print("loadURL")
}
is translated to
[URLTask ladeURLWithUrl:<#NSURL#>];

Swift Enumeration method call from Objective-C

My Swift enumeration is as below:
#objc enum NetworkError: Int, RawRepresentable {
case NoData
case Generic
func description() -> String {
switch self {
case .NoData: return "No data available"
case .Generic: return "Something goes wrong, please try again later"
}
}
}
My question is, how to call the description method from my Objective-C class. In Swift the call is as simple as:
NetworkError.Generic.description()
NetworkError.NoData.description()
Thanks
I don't think you can do this. If you look at the generated header the enum looks something like this to Objective-C:
typedef SWIFT_ENUM(NSInteger, NetworkError) {
NetworkErrorNoData = 0,
NetworkErrorGeneric = 1,
};
It's just a basic C style enum, it's not an object with methods, more info https://developer.apple.com/swift/blog/?id=22

Property 'sharedInstance' not found on object of type ClassA

I am creating a swift framework. In that one class is like this as shown below.
import Foundation
#objc public class classA: NSObject {
public override init (){
super.init();
}
/**
Singleton intance is returned.
*/
public class var sharedInstance: classA {
struct Static {
static let instance = popeye();
}
return Static.instance
}
}
Now when i add this framework into a Objective c project and try to access "sharedInstance" i get this error.
Property 'sharedInstance' not found on object of type ClassA.
Fix it Replace 'sharedInstance' with 'sharedInstance'
But even if i try use Fix it, this issue isnt solved.
NOTE: This issue doesn't happen when i integrate this framework with a swift project!!!
I AM STUCK.. :(
I tried to reproduce your problem. At first the syntax highlighter in Xcode flagged the same error in Objective-C that you mentioned, but the code actually was built and ran fine.
However, there is a cleaner way of doing this. In your code you are using a computed type property, which is evaluated every time you access it! You work around this by introducing the struct Static, where you essentially do what could be done in classA itself, like this:
/**
Singleton intance is returned.
*/
public static var sharedInstance: classA = popeye()
Here we used a stored type property, which is a recommended way to implement singletons, see here:
https://developer.apple.com/library/content/documentation/Swift/Conceptual/BuildingCocoaApps/AdoptingCocoaDesignPatterns.html
And here is some documentation on different kinds of properties:
https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Properties.html
Finally i was able to fix this with a minor change !! :)
Swift framework code
#objc class SingletonTest: NSObject {
// swiftSharedInstance is not accessible from ObjC
class var swiftSharedInstance: SingletonTest {
struct Singleton {
static let instance = SingletonTest()
}
return Singleton.instance
}
// the sharedInstance class method can be reached from ObjC
class func sharedInstance() -> SingletonTest {
return SingletonTest.swiftSharedInstance
}
// Some testing
func testTheSingleton() -> String {
return "Hello World"
}
}
Objective C parent project code
SingletonTest *aTest = [SingletonTest sharedInstance];
NSLog(#"Singleton says: %#", [aTest testTheSingleton]);

How to efficiently wrap a C-library into Swift class

I want to wrap a C-library (handling some NumberTheory related items, which I wrote some 20 years ago in C) into Swift. I started writing an Objective-C wrapper for this C-library, end then wrote a Swift derived class from the Objective-C wrapper.
Since Objective-C doesn't allow me to write overloaded methods/operators, I want to accomplish this in Swift. The C-library in question has API-calls starting with 'numthe' and a libnumthe.a and a "numthe.h" include file. The Objective-C wrapper/class is called NumTheObjC and the Swift class NumThe
Currently I've the following code for the NumTheObjC.h file:
#import <Foundation/Foundation.h>
#import <stdio.h>
#import "numthe.h"
#interface NumTheObjC : NSObject
{
numthe_prime nPrime; // Holds a numthe_prime number type instance.
numthe_perfect nPerfect; // Holds a numthe_perfect number type instance.
//... more numthe types/structure instances here.
}
-(id)init; // Default initializer
-(id)init:(NSString *)str; // Initialize from string-value.
-(void)add:(NumTheObjC *)op; // a += b
+(NumTheObjC *)add:(NumTheObjC *)left right:(NumTheObjC *)right; // x = a + b
-(void)setFromInt:(signed int)nrInt; // Initialize/set from int.
-(void)setFromLong:(signed long)nrLong; // Initialize/set from long.
-(void)setFromUInt:(unsigned int)nrUInt; // Initialize/set from uint.
-(void)setFromULong:(unsigned long)nrULong; // Initialize/set from ulong.
#end
And for the Swift wrapper I came up with the NumThe.swift file:
class NumThe : NumTheObjC {
init(nr: Int) {
super.init()
setFromInt(nr)
}
init(nr: UInt) {
super.init()
setFromUInt(nr)
}
init(strNr: String) {
super.init(strNr)
}
//... more overloaded 'constructors/initializers here ...
}
//-- Overloaded operator a + b
func + (inout left: NumThe, right: NumThe) -> NumTheObjC {
return NumTheObjC.add(left, right: right)
}
//-- Overloaded operator +=
func += (inout left: NumTheObjC, right: NumThe) {
return left.add(right)
}
Still some thoughts and questions remain, viz.:
1. I'm wondering if the above is the best approach to accomplish my goal.
2. Should the signature of all methods use NumTheObjC parameter-types instead of NumThe?
3. I can't seem to figure out how to let the above overloaded operator for 'a + b' return a NumThe instance instead of the NumTheObjC-instance.
4. Is there a more efficient approach in using C++ like templates/types to get all kinds of overloaded methods/operators?
Thanks in advance for any suggestions/tips!
1. I'm wondering if the above is the best approach to accomplish my goal.
You can need a new subclass NumThe. You can add new init() via extension of NumTheObjC. See this Apple doc for more info
2. Should the signature of all methods use NumTheObjC parameter-types instead of NumThe?
If you use extension then you do not need to change your parameter types
3. I can't seem to figure out how to let the above overloaded operator for 'a + b' return a NumThe instance instead of the NumTheObjC-instance.
Solution for point 1 ('extension') should resolve this too
4. Is there a more efficient approach in using C++ like templates/types to get all kinds of overloaded methods/operators?
I don't think it is possible. Again check out this Apple doc, the Preprocessor Directives section.

Is it possible to use Swift's Enum in Obj-C?

I'm trying to convert some of my Obj-C class to Swift. And some other Obj-C classes still using enum in that converted class. I searched In the Pre-Release Docs and couldn't find it or maybe I missed it. Is there a way to use Swift enum in Obj-C Class? Or a link to the doc of this issue?
This is how I declared my enum in my old Obj-C code and new Swift code.
my old Obj-C Code:
typedef NS_ENUM(NSInteger, SomeEnum)
{
SomeEnumA,
SomeEnumB,
SomeEnumC
};
#interface SomeClass : NSObject
...
#end
my new Swift Code:
enum SomeEnum: NSInteger
{
case A
case B
case C
};
class SomeClass: NSObject
{
...
}
Update: From the answers. It can't be done in Swift older version than 1.2. But according to this official Swift Blog. In Swift 1.2 that released along with XCode 6.3, You can use Swift Enum in Objective-C by adding #objc in front of enum
As of Swift version 1.2 (Xcode 6.3) you can. Simply prefix the enum declaration with #objc
#objc enum Bear: Int {
case Black, Grizzly, Polar
}
Shamelessly taken from the Swift Blog
Note: This would not work for String enums or enums with associated values. Your enum will need to be Int-bound
In Objective-C this would look like
Bear type = BearBlack;
switch (type) {
case BearBlack:
case BearGrizzly:
case BearPolar:
[self runLikeHell];
}
To expand on the selected answer...
It is possible to share Swift style enums between Swift and Objective-C using NS_ENUM().
They just need to be defined in an Objective-C context using NS_ENUM() and they are made available using Swift dot notation.
From the Using Swift with Cocoa and Objective-C
Swift imports as a Swift enumeration any C-style enumeration marked with the NS_ENUM macro. This means that the prefixes to enumeration value names are truncated when they are imported into Swift, whether they’re defined in system frameworks or in custom code.
Objective-C
typedef NS_ENUM(NSInteger, UITableViewCellStyle) {
UITableViewCellStyleDefault,
UITableViewCellStyleValue1,
UITableViewCellStyleValue2,
UITableViewCellStyleSubtitle
};
Swift
let cellStyle: UITableViewCellStyle = .Default
From the Using Swift with Cocoa and Objective-C guide:
A Swift class or protocol must be marked with the #objc attribute to
be accessible and usable in Objective-C. [...]
You’ll have access to anything within a class or protocol that’s
marked with the #objc attribute as long as it’s compatible with
Objective-C. This excludes Swift-only features such as those listed
here:
Generics Tuples / Enumerations defined in Swift / Structures defined in
Swift / Top-level functions defined in Swift / Global variables defined in
Swift / Typealiases defined in Swift / Swift-style variadics / Nested types /
Curried functions
So, no, you can't use a Swift enum in an Objective-C class.
Swift 4.1, Xcode 9.4.1:
1) Swift enum must be prefixed with #objc and be Int type:
// in .swift file:
#objc enum CalendarPermission: Int {
case authorized
case denied
case restricted
case undetermined
}
2) Objective-C name is enum name + case name, eg CalendarPermissionAuthorized:
// in .m file:
// point to something that returns the enum type (`CalendarPermission` here)
CalendarPermission calPermission = ...;
// use the enum values with their adjusted names
switch (calPermission) {
case CalendarPermissionAuthorized:
{
// code here
break;
}
case CalendarPermissionDenied:
case CalendarPermissionRestricted:
{
// code here
break;
}
case CalendarPermissionUndetermined:
{
// code here
break;
}
}
And, of course, remember to import your Swift bridging header as the last item in the Objective-C file's import list:
#import "MyAppViewController.h"
#import "MyApp-Swift.h"
If you prefer to keep ObjC codes as-they-are, you could add a helper header file in your project:
Swift2Objc_Helper.h
in the header file add this enum type:
typedef NS_ENUM(NSInteger, SomeEnum4ObjC)
{
SomeEnumA,
SomeEnumB
};
There may be another place in your .m file to make a change: to include the hidden header file:
#import "[YourProjectName]-Swift.h"
replace [YourProjectName] with your project name. This header file expose all Swift defined #objc classes, enums to ObjC.
You may get a warning message about implicit conversion from enumeration type... It is OK.
By the way, you could use this header helper file to keep some ObjC codes such as #define constants.
If you (like me) really want to make use of String enums, you could make a specialized interface for objective-c. For example:
enum Icon: String {
case HelpIcon
case StarIcon
...
}
// Make use of string enum when available:
public func addIcon(icon: Icon) {
...
}
// Fall back on strings when string enum not available (objective-c):
public func addIcon(iconName:String) {
addIcon(Icon(rawValue: iconName))
}
Of course, this will not give you the convenience of auto-complete (unless you define additional constants in the objective-c environment).
After researching this, I kept finding only partial answers, so I created an entire example of a Swift App bridged to Objective C that has Swift enums used by Objective C code and Objective C enums used by Swift code. It is a simple Xcode project that you can run and experiment with. It was written using Xcode 10.3 with Swift 5.0
Example Project
In case you are trying to observe an enum which looks like this:
enum EnumName: String {
case one = "One"
case two = "Two"
}
this workaround helped me.
Observable Class:
create #objc dynamic var observable: String?
create your enum instance like this:
private var _enumName: EnumName? {
didSet {
observable = _enumName!.rawValue
}
}
Observer Class:
create private var _enumName: EnumName?
create private let _instance = ObservableClass()
create
private var _enumObserver: NSKeyValueObservation = _instance.observe(\.observable, options: .new, changeHandler: { [weak self] (_, value) in
guard let newValue = value.newValue else { return }
self?._enumName = EnumName(rawValue: period)!
})
Than's it. Now each time you change the _enumName in the observable class, an appropriate instance on the observer class will be immediately updated as well.
This is of course an oversimplified implementation, but it should give you an idea of how to observe KVO-incompatible properties.
this might help a little more
Problem statement :- I have enum in swift class, which I am accessing form other swift classes, and Now I need to access it form my one of the objective C class.
Before accessing it from objective-c class :-
enum NTCType {
case RETRYNOW
case RETRYAFTER
}
var viewType: NTCType?
Changes for accessing it from objective c class
#objc enum NTCType :Int {
case RETRYNOW
case RETRYAFTER
}
and add a function to pass it on the value
#objc func setNtc(view:NTCType) {
self.viewType = view; // assign value to the variable
}