Objective-C enum not recognized in Swift - objective-c

I have a delegate method that passes an enum as an argument:
func gestureRecognizer(gestureRecognizer: JTTableViewGestureRecognizer!, commitEditingState state: JTTableViewCellEditingState, forRowAtIndexPath indexPath: NSIndexPath!) -> Void {
//....
}
The enum is JTTableViewCellEditingState. It's implementation is in the same header file as the delegate method. It's as follows:
typedef enum {
JTTableViewCellEditingStateMiddle,
JTTableViewCellEditingStateLeft,
JTTableViewCellEditingStateRight,
} JTTableViewCellEditingState;
Yet trying to reference a state, for example Left, gives an error:
if state == JTTableViewCellEditingState.Left {
'JTTableViewCellEditingState.Type' does not have a member named 'Left'
Trying to do it the old, Objective-C way, like some kind of peasant, gives me a different, more expected, error:
if state == JTTableViewCellEditingStateLeft {
Cannot invoke '==' with an argument list of type '(JTTableViewCellEditingState, JTTableViewCellEditingState)'
I'm wondering how I should overcome this issue? I believe referencing Objective-C enums has worked just fine in the past.

This type of enum decleration causes problems in swift. I had similar problem. My solution is to create a helper objective-c method that does comparison and use that method in swift whenever == is needed.
Other solution may be refactor that code if you can and convert it proper enum decleration in objective-c.
typedef NS_ENUM(NSInteger, MyEnum) {
MyEnumValue1,
MyEnumValue2
};

Can you use NS_ENUM instead? And then try JTTableViewCellEditingState.JTTableViewCellEditingStateLeft or even .JTTableViewCellEditingStateLeft to access your enum. If you can't change it to NS_ENUM, please have a look at Using non NS_ENUM objective-C enum in swift

In my environment - Xcode Version 6.1.1 (6A2006):
typedef enum {
JTTableViewCellEditingStateMiddle,
JTTableViewCellEditingStateLeft,
JTTableViewCellEditingStateRight,
} JTTableViewCellEditingState;
is exported to Swift as:
struct JTTableViewCellEditingState {
init(_ value: UInt32)
var value: UInt32
}
var JTTableViewCellEditingStateMiddle: JTTableViewCellEditingState { get }
var JTTableViewCellEditingStateLeft: JTTableViewCellEditingState { get }
var JTTableViewCellEditingStateRight: JTTableViewCellEditingState { get }
So, this should works:
func gestureRecognizer(gestureRecognizer: JTTableViewGestureRecognizer!, commitEditingState state: JTTableViewCellEditingState, forRowAtIndexPath indexPath: NSIndexPath!) -> Void {
if state.value == JTTableViewCellEditingStateLeft.value {
// ...
}
}

Related

Extension can't access generic parameters at runtime

I´m using AWS Cognito, in the doc it says I have to add this function.
But I´m getting this error:
Extension of a generic Objective-C class cannot access the class's generic parameters at runtime
extension AWSTask {
public func continueWithExceptionCheckingBlock(completionBlock:#escaping (_ result: Any?, _ error: Error?) -> Void) {
self.continue({(task: AWSTask) -> Any? in
if let exception = task.exception {
print("Fatal exception: \(exception)")
kill(getpid(), SIGKILL);
}
let result: AnyObject? = task.result
let error: NSError? = task.error as NSError?
completionBlock(result, error)
return nil
})
}
}
For those who have this issue in Swift 5, try adding #objc modifier to the method. Please find the example below:
extension NSLayoutAnchor {
#objc func constrainEqual(_ anchor: NSLayoutAnchor<AnchorType>, constant: CGFloat = 0) {
let constraint = self.constraint(equalTo: anchor, constant: constant)
constraint.isActive = true
}
}
Without context I can't say if you are actually doing what the error message prompts you with, but there is an open bug report describing this issue (where no offending code is actually used):
SR-2708: Extending ObjC generics in Swift 3 does not compile
ObjC:
#interface MySet<T : id<NSCopying>> : NSObject
#end
Swift:
class Foo { }
struct Bar { }
extension MySet {
func foo() -> Foo { return Foo() }
func bar() -> Bar { return Bar() }
}
Both of the extension methods result in "Extension of a generic Objective-C class cannot access the class's generic parameters at
runtime". However, neither really does anything like that (at least
not explicitly).
If you read the comments to the bug report, you'll see that a user named 'Vasili Silin' describes having this issue when attempting to extend AWSTask, so you might have to consider alternative approaches until this bug is resolved.
I just got the same error and solve it this way :
extension yourClass where T == yourType {}

UIActivityType – Property cannot be an #objc override because its type cannot be represented in Objective-C

While updating to Xcode 8 Beta 6, from what I saw a new type got introduced: UIActivityType
So I tried to do somewhere like this in my UIActivity custom class:
class FooActivity: UIActivity {
func retrieveActivityType() -> String {
return "someStringDescribingActivityType"
}
override open var activityType: UIActivityType? {
#objc(retrieveActivityType)
get {
return UIActivityType(rawValue: "someStringDescribingActivityType")
}
}
}
where retrieveActivityType() is the Objective-C equivalent since UIActivityType is only defined in Swift. But no luck so far, still having two errors:
Property cannot be an #objc override because its type cannot be represented in Objective-C
'#objc' getter for non-'#objc' property
Is there something obvious that I'm missing?
Found a quick fix by just making the return type as non-optional.
I guess there is no real workaround until beta 7 gets released.
class FooActivity: UIActivity {
override open var activityType: UIActivityType {
get {
return UIActivityType(rawValue: "someStringDescribingActivityType")
}
}
Sources:
https://bugs.swift.org/browse/SR-2344
https://github.com/apple/swift/pull/4360

Call generic function from non-generic function in Swift

MMCondition is a protocol defined in Swift, but interoperates with Objective-C (annotated with #objc).
#objc public protocol MMCondition {
static var name: String { get }
static var isMutuallyExclusive: Bool { get }
}
I have the following code:
// addCondition cannot be generic as I want it to be accessible from Objective-C as well.
public func addCondition(condition: MMCondition) {
// How do I initialize OperationConditionImplementer here?
let operationCondition = OperationConditionImplementer(condition: condition) // doesn't compile
// Error: Cannot invoke initializer for type 'OperationConditionImplementer<T>' with an argument list of type '(condition: MMCondition)'
// Can I use condition.dynamicType to init OperationConditionImplementer somehow?
}
struct OperationConditionImplementer<T: MMCondition> {
let condition: T
static var name: String {
return "Silent<\(T.name)>"
}
static var isMutuallyExclusive: Bool {
return T.isMutuallyExclusive
}
init(condition: T) {
self.condition = condition
}
}
From Objective-C, you can't use generics as stated in the documentation.
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
...
So you need to remove completely the generics code. One possible solution might be:
#objc protocol MMCondition {
static var name: String { get }
static var isMutuallyExclusive: Bool { get }
}
struct OperationConditionImplementer {
let condition: MMCondition
var name: String {
return "Silent<\(condition.dynamicType.name)>"
}
var isMutuallyExclusive: Bool {
return condition.dynamicType.isMutuallyExclusive
}
init(condition: MMCondition) {
self.condition = condition
// Here decide comparing types
if condition.dynamicType === ExampleCondition.self {
print(condition.dynamicType.name)
}
}
}
So for instance, if you try it out in a playground:
class ExampleCondition: NSObject, MMCondition {
static var name: String = "ExampleCondition"
static var isMutuallyExclusive: Bool = false
}
let example = OperationConditionImplementer(condition: ExampleCondition())
You'll see "ExampleCondition" printed.
If you eventually switch to pure Swift, you need to specify T when initializing a OperationConditionImplementer.
You can achieve that defining the addCondition method as:
func addCondition<T: MMCondition>(condition: T) {
let a = OperationConditionImplementer<T>(condition: condition)
}
Since Swift 2.0 instances of generic classes can implement Objective-C protocols. What won't be possible I believe is having a struct implement the protocol. In fact I expect that your protocol may need to inherit from NSObjectProtocol to be usable in Objective-C which would then prevent you from implementing the protocol with structs or enums.
You also rightly mention that you can't access generic functions from Objective-C.
For a concrete example of using a generic to fulfil an Objective-C protocol have a look at this blog post.

Objective-C calling parameterized Swift method crashes Swift compiler

I have a simple Swift extension on NSManagedObject, in which I have a parametrized method for finding a single object - the signature looks like:
public class func findFirst<T:NSManagedObject>(inContext context : NSManagedObjectContext? = .None) -> T?
I'm trying to call this from Objective-C, but it seems like it cannot be seen. If I create a non-parameterized version I can see and call it just fine from Objective-C:
public class func findFirstUntypedWithPredicate(predicate:NSPredicate?, inContext context : NSManagedObjectContext? = .None) -> NSManagedObject?
Is there any way for ObjectiveC to be able to reach the parameterized version of the call?
I would use Self like so:
public class func findFirst(inContext context : NSManagedObjectContext? = .None) -> Self?
using the technique found here:
How can I create instances of managed object subclasses in a NSManagedObject Swift extension?
However, that causes the Swift compiler to segfault when compiling the code (Xcode 6.3.1, or Xcode 6.4 beta 2).
Edit: Here's a link with the full source of the framework I'm trying to build, including bonus Swift compiler crashes caused by templated methods:
https://www.dropbox.com/s/fixaj9ygdoi4arp/KiGiCoreData.zip?dl=0
Generic methods are not visible from Objective-C. However you can use
the ideas from How to use generic types to get object with same type to define a findFirst() class method
which returns Self? (the Swift equivalent of instancetype) without
being generic:
// Used to cast `AnyObject?` to `Self?`, `T` is inferred from the context.
func objcast<T>(obj: AnyObject?) -> T? {
return obj as! T?
}
extension NSManagedObject
{
class func entityName() -> String {
let classString = NSStringFromClass(self)
// The entity is the last component of dot-separated class name:
let components = split(classString) { $0 == "." }
return components.last ?? classString
}
// Return any matching object, or `nil` if none exists or an error occurred
class func findFirst(context : NSManagedObjectContext, withPredicate pred : NSPredicate?) -> Self? {
let name = entityName()
let request = NSFetchRequest(entityName: name)
request.predicate = pred
var error : NSError?
let result = context.executeFetchRequest(request, error: &error)
if let objects = result {
return objcast(objects.first)
} else {
println("Fetch failed: \(error?.localizedDescription)")
return nil
}
}
}
This can be used from Swift
if let obj = YourEntity.findFirst(context, withPredicate: nil) {
// found
} else {
// not found
}
and from Objective-C:
YourEntity *obj = [YourEntity findFirst:context withPredicate:nil];

How to access private members of an Objective-C class from a Swift extension?

I'm trying to extend an Objective-C class in Swift and make it conform to the Equatable protocol. This requires to access some private members of the extended class, which the compiler doesn't let me do. What is the correct way to do it without making the private members public?
My Swift code:
import Foundation
extension ShortDate : Equatable { }
public func == (lhs: ShortDate, rhs: ShortDate) -> Bool {
if (lhs.components.year == rhs.components.year)
&& (lhs.components.month == rhs.components.month)
&& (lhs.components.day == rhs.components.day) {
return true;
}
return false;
}
Objective-C:
#interface ShortDate : NSObject<NSCopying, NSCoding> {
NSDate *inner;
NSDateComponents *components; // The date split into components.
}
...
#end
The error I'm getting:
ShortDate.swift:26:9: 'ShortDate' does not have a member named 'components'
I came across this question while trying to find a way to access a private variable of a class from one of the SDKs we use. Since we don't have or control the source code we can't change the variables to properties. I did find that the following solution works for this case:
extension ObjcClass {
func getPrivateVariable() -> String? {
return value(forKey: "privateVariable") as? String
}
open override func value(forUndefinedKey key: String) -> Any? {
if key == "privateVariable" {
return nil
}
return super.value(forUndefinedKey: key)
}
}
Overriding value(forUndefinedKey:) is optional. value(forKey:) will crash if the private variable doesn't exist on the class unless you override value(forUndefinedKey:) and provide a default value.
I believe that there is no way to access Objective-C instance variables from Swift. Only Objective-C properties get mapped to Swift properties.