Can't conform to obj-c protocol in Swift - ios

I'm creating an iOS app with Swift. I discovered an animation I'd like to implement in my table view, but the code is in Objective-C.
Repository: https://github.com/recruit-mp/RMPZoomTransitionAnimator
I have successfully bridged Obj-C code to Swift but can't seem to conform to a required protocol.
The protocol:
#protocol RMPZoomTransitionAnimating <NSObject>
#required
- (UIImageView *)transitionSourceImageView;
- (UIColor *)transitionSourceBackgroundColor;
- (CGRect)transitionDestinationImageViewFrame;
#end
My Swift implementation:
First class that implements the protocol:
class ChallengeViewController: UIViewController, RMPZoomTransitionAnimating
func transitionSourceImageView() -> UIImageView {
return imageView
}
func transitionSourceBackgroundColor() -> UIColor {
return UIColor.whiteColor()
}
func transitionDestinationImageViewFrame() -> CGRect {
return imageView.frame
}
Second class:
class ChallengeTableViewController: UITableViewController, RMPZoomTransitionAnimating
func transitionSourceImageView() -> UIImageView {
return imageForTransition!
}
func transitionSourceBackgroundColor() -> UIColor {
return UIColor.whiteColor()
}
func transitionDestinationImageViewFrame() -> CGRect {
return imageFrame!
}
This check that occurs before animating always fails:
Protocol *animating = #protocol(RMPZoomTransitionAnimating);
BOOL doesNotConfirmProtocol = ![self.sourceTransition conformsToProtocol:animating] || ![self.destinationTransition conformsToProtocol:animating];
I've read this topic How to create class methods that conform to a protocol shared between Swift and Objective-C? but didn't found any help
Any clues would be really appreciated

Swift classes by themselves are not (by default) Objective-C Compatible.
You get compatibility by either inheriting from NSObject or adding #objc in front of your class. I suspect this "may" be your issues - but I unfortunately can't test it at the moment.
You may also have to add a few initializers like in your case one from NSCoder or something - I don't unfortunately recall off the top of my head - and I don't have access to Xcode right now.
https://developer.apple.com/library/ios/documentation/Swift/Conceptual/BuildingCocoaApps/MixandMatch.html
Try:
#objc
class ChallengeViewController: UIViewController, RMPZoomTransitionAnimating
func transitionSourceImageView() -> UIImageView {
return imageView
}
func transitionSourceBackgroundColor() -> UIColor {
return UIColor.whiteColor()
}
func transitionDestinationImageViewFrame() -> CGRect {
return imageView.frame
}
This will tell the compiler your class is objective-c compatible

Related

View Controller only Protocol has no access to View Controller properties

I have a UIViewController only protocol
protocol VCProtocol where Self: UIViewController {}
I have a function with VCProtocol parameter. Inside the function I can not access any property of UIViewController
func testFunction(vcProtocol: VCProtocol) {
// vcProtocol.view ‼️ error: Value of type 'VCProtocol' has no member 'view'
}
Though I can cast the protocol parameter to UIViewController and then access the property like this:
func testFunction(vcProtocol: VCProtocol) {
(vcProtocol as! UIViewController).view
}
Is this is the way? Do we have any better way?
You can use the & operator to combine protocols
protocol VCProtocol where Self: UIViewController {}
func testFunction(vcProtocol: VCProtocol & UIViewController) {
let view = vcProtocol.view
}
It seems like this is now supported properly from Swift 5. You can try it Xcode 10.2 beta 4. For older versions, you would have to resort to #Ricky Mo's solution.
protocol VCProtocol: UIViewController {
func testFunction(vcProtocol: VCProtocol)
}
class A: UIViewController, VCProtocol {
func testFunction(vcProtocol: VCProtocol) {
debugPrint(vcProtocol.view)
}
}
From the notes,
Protocols can now constrain their conforming types to those that
subclass a given class. Two equivalent forms are supported:
protocol MyView: UIView { /*...*/ }
protocol MyView where Self: UIView { /*...*/ }
Swift 4.2 accepted the second form, but it wasn’t fully implemented
and could sometimes crash at compile time or runtime. (SR-5581)
(38077232)

How to add protocol type as subview

So I wrote a simple protocol:
protocol PopupMessageType{
var cancelButton: UIButton {get set}
func cancel()
}
and have a customView:
class XYZMessageView: UIView, PopupMessageType {
...
}
and then I currently have:
class PopUpViewController: UIViewController {
//code...
var messageView : CCPopupMessageView!
private func setupUI(){
view.addSubview(messageView)
}
}
But what I want to do is:
class PopUpViewController: UIViewController {
//code...
var messageView : PopupMessageType!
private func setupUI(){
view.addSubview(messageView) // ERROR
}
}
ERROR I get:
Cannot convert value of type 'PopupMessageType!' to expected argument
type 'UIView'
EDIT:
I'm on Swift 2.3!
Change the type of property messageView to (UIView & PopupMessageType)!
I mean
class PopUpViewController: UIViewController {
//code...
var messageView : (UIView & PopupMessageType)!
private func setupUI(){
view.addSubview(messageView) // ERROR
}
}
In Swift 4 you can do this:
typealias PopupMessageViewType = UIView & PopupMessageType
And then use PopupMessageViewType as the type of the variable.
DISCLAIMER: I do not have the swift 2.3 compiler anymore since swift 4 is the new normal for iOS development. The following code may possibly need tweaks to get it working in swift 2.3
Essentially we will be making a 2x1 mux where the two inputs are the same object. The output depends on whether you set the mux to choose the first or the second one.
// The given protocol
protocol PopupMessageType{
var cancelButton: UIButton {get set}
func cancel()
}
// The object that conforms to that protocol
class XYZMessageView: UIView, PopupMessageType {
var cancelButton: UIButton = UIButton()
func cancel() {
}
}
// The mux that lets you choose the UIView subclass or the PopupMessageType
struct ObjectPopupMessageTypeProtocolMux<VIEW_TYPE: UIView> {
let view: VIEW_TYPE
let popupMessage: PopupMessageType
}
// A class that holds and instance to the ObjectPopupMessageTypeProtocolMux
class PopUpViewController: UIViewController {
var messageWrapper : ObjectPopupMessageTypeProtocolMux<UIView>!
private func setupUI(){
view.addSubview(messageWrapper.view)
}
}
//...
let vc = PopUpViewController() // create the view controller
let inputView = XYZMessageView() // create desired view
// create the ObjectPopupMessageTypeProtocolMux
vc.messageWrapper = ObjectPopupMessageTypeProtocolMux(view: inputView, popupMessage: inputView) //<-- 1
vc.messageWrapper.view // retreive the view
vc.messageWrapper.popupMessage.cancel() // access the protocol's methods
vc.messageWrapper.popupMessage.cancelButton // get the button
1) I input the "inputView" twice for the initializer of ObjectPopupMessageTypeProtocolMux. They are the same class instance, but they get casted to different types.
I hope this helps you get to where you wanna go in swift 2.3

How to create constraint on protocol

I tried to create protocol which can be only implemented by classes which inherit from UIView, what was my surprise when this code compiles without errors (in Swift 3.0):
protocol TestsProtocol {
func test()
}
extension TestsProtocol where Self: UIView { }
class FooClass: TestsProtocol {
func test() {
}
}
We can see that FooClass don't inherit from UIView, using protocol extension I wan't to force that only classes which inherit from UIView can implement it.
As far as I remember this would not compile in Swift 2.1
You cannot do this in Swift. The extension syntax does something else:
extension TestsProtocol where Self: UIView {
func useful() {
// do something useful
}
}
now any class which implements TestsProtocol and is a UIView (or subclass) also has the useful() function.
You can do that easily by limit protocol from be extendable from any type other than UIView :
protocol TestsProtocol:UIView {
func test()
}
class FooClass: TestsProtocol {
func test() {
}
}
So this will cause compile error
'TestsProtocol' requires that 'FooClass' inherit from 'UIView'

Checking for class and conformance to a protocol [duplicate]

In Objective-C, it's possible to specify a class conforming to a protocol as a method parameter. For example, I could have a method that only allows a UIViewController that conforms to UITableViewDataSource:
- (void)foo:(UIViewController<UITableViewDataSource> *)vc;
I can't find a way to do this in Swift (perhaps it's not possible yet). You can specify multiple protocols using func foo(obj: protocol<P1, P2>), but how do you require that the object is of a particular class as well?
You can define foo as a generic function and use type constraints to require both a class and a protocol.
Swift 4
func foo<T: UIViewController & UITableViewDataSource>(vc: T) {
.....
}
Swift 3 (works for Swift 4 also)
func foo<T: UIViewController>(vc:T) where T:UITableViewDataSource {
....
}
Swift 2
func foo<T: UIViewController where T: UITableViewDataSource>(vc: T) {
// access UIViewController property
let view = vc.view
// call UITableViewDataSource method
let sections = vc.numberOfSectionsInTableView?(tableView)
}
In Swift 4 you can achieve this with the new & sign:
let vc: UIViewController & UITableViewDataSource
The Swift book documentation suggests that you use type constraints with a where clause:
func someFunction<C1: SomeClass where C1:SomeProtocol>(inParam: C1) {}
This guarantees that "inParam" is of type "SomeClass" with a condition that it also adheres to "SomeProtocol". You even have the power to specify multiple where clauses delimited by a comma:
func itemsMatch<C1: SomeProtocol, C2: SomeProtocol where C1.ItemType == C2.ItemType, C1.ItemType: SomeOtherProtocol>(foo: C1, bar: C2) -> Bool { return true }
Swift 5:
func foo(vc: UIViewController & UITableViewDataSource) {
...
}
So essentially Jeroen's answer above.
With Swift 3, you can do the following:
func foo(_ dataSource: UITableViewDataSource) {
self.tableView.dataSource = dataSource
}
func foo(_ delegateAndDataSource: UITableViewDelegate & UITableViewDataSource) {
//Whatever
}
What about this way?:
protocol MyProtocol {
func getTableViewDataSource() -> UITableViewDataSource
func getViewController() -> UIViewController
}
class MyVC : UIViewController, UITableViewDataSource, MyProtocol {
// ...
func getTableViewDataSource() -> UITableViewDataSource {
return self
}
func getViewController() -> UIViewController {
return self
}
}
func foo(_ vc:MyProtocol) {
vc.getTableViewDataSource() // working with UITableViewDataSource stuff
vc.getViewController() // working with UIViewController stuff
}
Update for Swift 5:
func yourFun<V: YourClass>(controller: V) where V: YourProtocol
Note in September 2015: This was an observation in the early days of Swift.
It seems to be impossible. Apple has this annoyance in some of their APIs as well. Here is one example from a newly introduced class in iOS 8 (as of beta 5):
UIInputViewController's textDocumentProxy property:
Defined in Objective-C as follows:
#property(nonatomic, readonly) NSObject<UITextDocumentProxy> *textDocumentProxy;
and in Swift:
var textDocumentProxy: NSObject! { get }
Link to Apple' documentation:
https://developer.apple.com/library/prerelease/iOS/documentation/UIKit/Reference/UIInputViewController_Class/index.html#//apple_ref/occ/instp/UIInputViewController/textDocumentProxy

Overriding delegate property of UIScrollView in Swift (like UICollectionView does)

UIScrollView has a delegate property which conforms to UIScrollViewDelegate
protocol UIScrollViewDelegate : NSObjectProtocol {
//...
}
class UIScrollView : UIView, NSCoding {
unowned(unsafe) var delegate: UIScrollViewDelegate?
//...
}
UICollectionView overrides this property with a different type UICollectionViewDelegate
protocol UICollectionViewDelegate : UIScrollViewDelegate, NSObjectProtocol {
//...
}
class UICollectionView : UIScrollView {
unowned(unsafe) var delegate: UICollectionViewDelegate?
//...
}
When I try to override UIScrollViews delegate with my protocol like so:
protocol MyScrollViewDelegate : UIScrollViewDelegate, NSObjectProtocol {
//...
}
class MyScrollView: UIScrollView {
unowned(unsafe) var delegate: MyScrollViewDelegate?
}
the compiler gives me two warnings:
Property 'delegate' with type 'MyScrollViewDelegate?' cannot override a property with type 'UIScrollViewDelegate?'
'unowned' cannot be applied to non-class type 'MyScrollViewDelegate?'
How can I subclass UIScrollView and override type of delegate property (i.e. use a custom delegate protocol) ?
I think overriding an inherited property is something that's possible in Objective-C but not (at least currently) in Swift. The way I've handled this is to declare a separate delegate as a computed property of the correct type that gets and sets the actual delegate:
#objc protocol MyScrollViewDelegate : UIScrollViewDelegate, NSObjectProtocol {
func myHeight() -> CGFloat
// ...
}
class MyScrollView: UIScrollView {
var myDelegate: MyScrollViewDelegate? {
get { return self.delegate as? MyScrollViewDelegate }
set { self.delegate = newValue }
}
}
This way anything that calls the scroll view delegate normally still works, and you can call your particular delegate methods on self.myDelegate, like this:
if let height = self.myDelegate?.myHeight() {
// ...
}
You can do like this:
protocol ExtendedUIScrollViewDelegate: UIScrollViewDelegate {
func someNewFunction()
}
class CustomScrollView: UIScrollView {
weak var myDelegate: ExtendedScrollViewDelegate?
override weak var delegate: UIScrollViewDelegate? {
didSet {
myDelegate = delegate as? ExtendedScrollViewDelegate
}
}
}
Hope this helps
My favoured method personally is not to subclass scrollviews directly but to make a UIView subclass containing and acting as delegate for a separate scrollview, then forward that scrollview's delegate messages on to the UIView subclass's own delegate where necessary. This also allows for the adding of custom controls outside of the area defined by the scroll view. It may seem a little inelegant compared to a direct subclass, but it does at least avoid unpleasant hacks.
Here is a solution for changing the type of the overriding properties in Swift. It is especially useful when you need to extend protocols of delegates.
#objc protocol ExtendedUIScrollViewDelegate: UIScrollViewDelegate {
func someNewFunction()
}
class CustomScrollView: UIScrollView {
weak var delegateInterceptor: ExtendedScrollViewDelegate?
override var delegate: UIScrollViewDelegate! {
didSet {
if let newValue = delegate {
let castedDelegate = unsafeBitCast(delegate, ExtendedScrollViewDelegate.self)
delegateInterceptor = castedDelegate
}
else {
delegateInterceptor = nil
}
}
}
}
This works as tested with Swift version 1.2. I hope it helps.
You can override get and set method by declare function like:
func setDelegate(delegate:UITableViewDelegate?){
self.delegateInterceptor = delegate;
}
swift compiler the property to method as Objective-c does.
Consider the following situation:
class BaseProp {}
class Base {
var prop: BaseProp
}
Then if you do this:
class DerivedProp: BaseProp {}
class Derived: Base {
override var prop: DerivedProp
}
Then if would break the subclassing principles (namely, the Liskov Substitution Principle). Basically what you are doing is limiting the scope of "var prop" from wider "BaseProp" type to a more narrow "DerivedProp" type. Then this kind of code would be possible, which does not make sense:
class UnrelatedProp: BaseProp {}
let derived = Derived()
let base = derived as Base
base.prop = UnrelatedProp()
Note that we are assigning an instance of UnrelatedProp to the property, which does not make sense for the Derived instance which we actually operate with. ObjectiveC allows such kind of ambiguity, but Swift doesn't.

Resources