I am looking for a general solution to access:
Obj-C named property getters and named property setters from Swift
Conform to an Objective-C #protocol with readonly properties
Similar to Creating an Objective-C equivalent Getter and Setter in Swift, which is closed, yet does not offer a satisfying answer.
Objective-C to Swift Properties Example:
I have an Objective-C protocol defined with two problematic properties, one with a custom getter isEnabled and another with a private setter exists.
#protocol SomeProtocol <NSObject>
#property (nonatomic, assign, getter = isEnabled) BOOL enabled;
#property (nonatomic, readonly) BOOL exists;
#end
How can I access these Objective-C properties from Swift?
This does not seem to work:
func isEnabled() -> Bool { return self.enabled }
and neither does:
var isEnabled:Bool {
get { }
set { }
}
Straight from the documentation:
Use the #objc(<#name#>) attribute to provide Objective-C names for properties and methods when necessary. For example, you can mark a property called enabled to have a getter named isEnabled in Objective-C like this:
SWIFT
var enabled: Bool {
#objc(isEnabled) get {
/* ... */
}
}
Named Objective-C Getter Property in Swift
var _enabled:Bool = false
var enabled:Bool {
#objc(isEnabled) get {
return self._enabled
}
set(newValue){
_enabled = newValue
}
}
readonly Objective-C Property in Swift
Either
var _exists:Bool = false
private(set) var exists:Bool {
get{
return self._exists
}
set(newValue){
self._exists = newValue
}
}
or
var _exists:Bool = false
var exists:Bool {
get{
return self._exists
}
}
and access self._exists directly since there is no setter.
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 have a custom Objective-C class that contains two CGFloats:
#property (nonatomic, assign) IBInspectable CGFloat minimumConstant NS_REFINED_FOR_SWIFT;
#property (nonatomic, assign) IBInspectable CGFloat maximumConstant NS_REFINED_FOR_SWIFT;
Both are marked as IBInspectable. In the initializer of the class I set both bot NaN as I need to represent a difference between 0 and nothing. To make the class nice to use from Swift I marked both properties as NS_REFINED_FOR_SWIFT and created a Swift extension with the refined implementation:
#IBInspectable public var minimumConstant: CGFloat? {
get {
let constant = __minimumConstant
return constant.isNaN ? nil : constant
}
set {
if let constant = newValue {
__minimumConstant = constant
return
}
__minimumConstant = CGFloat.nan
}
}
#IBInspectable public var maximumConstant: CGFloat? {
get {
let constant = __maximumConstant
return constant.isNaN ? nil : constant
}
set {
if let constant = newValue {
__maximumConstant = constant
return
}
__maximumConstant = CGFloat.nan
}
}
This works great when using my class from code. When using Swift NaN will be mapped to nil and I can use optional unwrapping as usual.
The issue is that as soon as I add NS_REFINED_FOR_SWIFT the Interface Builder will no longer recognize my properties as IBInspectable and does not show them.
Is this a known issue? Is there any workaround for this?
Is there something I don't understand?
Protocol:
public protocol SLKTypingIndicatorProtocol : NSObjectProtocol {
/**
Returns YES if the indicator is visible.
SLKTextViewController depends on this property internally, by observing its value changes to update the typing indicator view's constraints automatically.
You can simply #synthesize this property to make it KVO compliant, or override its setter method and wrap its implementation with -willChangeValueForKey: and -didChangeValueForKey: methods, for more complex KVO compliance.
*/
public var visible: Bool { get set }
/**
Dismisses the indicator view.
*/
optional public func dismissIndicator()
}
My code:
public class TypingListView: UIView, SLKTypingIndicatorProtocol {
var _visible: Bool = false
public var visible: Bool {
get {
return self._visible
}
set (val) {
self._visible = val
}
}
public func isVisible() -> Bool {
return self.visible
}
public func dismissIndicator() {
self.visible = false
}
// Other code...
}
The error I keep getting: "Type 'TypingListView' does not conform to protocol 'SLKTypingIndicatorProtocol'"
When I expand the error it states: "Protocol requires property 'visible' with type 'Bool'". It also says "Objective-C method 'visible' provided by getter for 'visible' does not match the requirement's selector ('isVisible')"
I found how the protocol actually reads in Objective-C as well:
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
/** Generic protocol needed when customizing your own typing indicator view. */
#protocol SLKTypingIndicatorProtocol <NSObject>
#required
/**
Returns YES if the indicator is visible.
SLKTextViewController depends on this property internally, by observing its value changes to update the typing indicator view's constraints automatically.
You can simply #synthesize this property to make it KVO compliant, or override its setter method and wrap its implementation with -willChangeValueForKey: and -didChangeValueForKey: methods, for more complex KVO compliance.
*/
#property (nonatomic, getter = isVisible) BOOL visible;
#optional
/**
Dismisses the indicator view.
*/
- (void)dismissIndicator;
#end
NS_ASSUME_NONNULL_END
Tips, try this style:
public var visible: Bool {
#objc(isVisible) get {
return self._visible
}
set (val) {
self._visible = val
}
}
This is how it is defined in objective-c:
#interface AFHTTPRequestOperation : AFURLConnectionOperation
#property (readonly, nonatomic, strong) id responseObject;
How to assign sth to this property?
I would like to mock this property for later usage. So I need there my own responseObject overriden in Swift. Any ideas how to do this? I can subclass, extend or whatever. The only thing is it must by type of AFHTTPRequestOperation.
class MyRequestOperation: AFHTTPRequestOperation {
override var responseObject: [String: AnyObject]
}
It produces an error:
Getter for responseObject with Objective-C selector responseObject conflicts with getter for responseObject from superclass AHTTPRequestOperation with the same Objective-C selector
So, the answer is simple as #Atul Mishra said:
class MyRequestOperation: AFHTTPRequestOperation {
var myOverridenResponseObject: AnyObject?
override var responseObject: AnyObject? {
get {
return myOverridenResponseObject
}
set {
myOverridenResponseObject = newValue
}
}
}
You can present an inherited read-only property as a read-write property by providing both a getter and a setter in your subclass property override.but however you cannot provide an inherited read-write property as read-only property.
for better clarification refer https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Inheritance.html#//apple_ref/doc/uid/TP40014097-CH17-XID_300
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