How can I view the UITableViewDataSource protocol that is currently used by the Swift compiler? I'm guessing it is in a file somewhere that the compiler reads. How can I see that file?
I'm getting the error "Type ... does not conform to protocol 'UITableViewDataSource'" but there is no hint about why it is nonconformant. An earlier version of the app in Swift 2 didn't have this error so i'm guessing it arose because of the Swift 3 renaming. I know the protocol requires at least numberOfRowsInSection and cellForRowAtIndexPath and I have both. I probably have an argument spelling or return type wrong or something like that.
The current (and recent) API documentation drives me crazy! So many details seem to be missing.
The protocol reference is here:
https://developer.apple.com/reference/uikit/uitableviewdatasource
You should also be able to press CMD + Shift + O and type in UITableViewDataSource to jump to the header from inside your project:
Related
I'm new to Swift and UIKit and, coming primarily from Python, I'm having trouble with the copy-paste-ness of tutorials and documentation I've been looking at thus far. I'm having a hard time getting a fundamental understanding of methods like:
override func tableView( ... numberOfRowsInSection ... )
override func tableView( ... cellForRowAt ... )
In practice I can use them without much issue, but I would appreciate any clarification or pointing to references that would explain:
Why are UIKit methods structured like this, rather than having a dedicated
func tableViewNumberOfRowsInSection( ... )
func tableViewCellForRowAt( ... )
(Or, why not have numberOfRowsInSection as an attribute of the subclass of UITableViewController, rather than one parameter of a function (tableView) that seems to have hundreds of uses?)
Function/Method basics such as labels, parameter names, etc make perfect sense to me, but I can't seem to make the jump to why func tableView would be structured the way it is.
What does the eventual call of tableView look like?
Thanks in advance for any help or pointers!
These are not the same function used in different ways — they are completely different functions, because the argument labels are part of the functions' names. The full name of each function is tableView(_:numberOfRowsInSection:) and tableView(_:cellForRowAt:) as you can see in the documentation. You might also want to read the Function Argument Labels and Parameter Names section of this guide, and maybe the naming guidelines.
You are unlikely to call these methods yourself, since they exist to provide data to the table view itself. UITableView will call your methods internally, which would look something like dataSource.tableView(self, cellForRowAt: indexPath). See the Delegation section in the Protocols guide for more on this pattern.
It's also worth noting that this API was created for Objective-C (before Swift was released), where their selectors are tableView:numberOfRowsInSection: and tableView:cellForRowAtIndexPath:. That might be less confusing because the first argument label isn't "privileged" by being outside parentheses. That said, it's still considered a good design by Swift's naming standards, at least as long as the design needs to use classes (which it might not, if it didn't have to support Obj-C).
I won't go into too much detail in this answer, but it is true that delegate methods from UIKit (like the ones you show) tend to be long and near impossible to type out without help from auto-complete.
Nobody I know has memorized all of them (I've tried, and failed).
The reason being is somewhat legacy due to UIKit itself being written in Objective-C by Apple.
If UIKit were to be rewritten in Swift today (this will never happen due to the huge amount of effort Apple has invested into the framework over the years and the rise of SwiftUI), it's likely that the API would better follow practices for Swift API design.
To answer your questions specifically:
That's the way they've always been–if they were changed at all it would break a lot of developer's source code. Arguably the kind of method signatures you are proposing are not any better, just different. Also, if you read the full method signatures they do make sense and are clear enough.
Look into some tutorials for UITableView. The methods you are showing off come from UITableViewDelegate which is kind of the 'glue' you use to configure the table from your code. This is because back in the early Objective-C days the only way to do this sort of customisation was through this 'delegation pattern'.
Overview:
I have a protocol P1 which provides a default implementation of one of the Objective-C optional functions.
When I provide a default implementation of the optional function there is a warning
Compiler Warning:
Non-'#objc' method 'presentationController(_:viewControllerForAdaptivePresentationStyle:)' does not satisfy optional requirement of '#objc' protocol 'UIAdaptivePresentationControllerDelegate'
Version:
Swift: 3
Xcode: 8 (public release)
Attempts made:
Tried adding #objc but doesn't help
Question:
How do I resolved this ?
Is there a work around ?
Code:
#objc protocol P1 : UIAdaptivePresentationControllerDelegate {
}
extension P1 where Self : UIViewController {
func presentationController(_ controller: UIPresentationController, viewControllerForAdaptivePresentationStyle style: UIModalPresentationStyle) -> UIViewController? {
return UIViewController()
}
}
class A : UIViewController, P1 {
}
While I think I can answer your question, it's not an answer you will like.
TL;DR: #objc functions may not currently be in protocol extensions. You could create a base class instead, though that's not an ideal solution.
Protocol Extensions and Objective-C
First, this question/answer (Can Swift Method Defined on Extensions on Protocols Accessed in Objective-c) seems to suggest that because of the way protocol extensions are dispatched under the hood, methods declared in protocol extensions are not visible to the objc_msgSend() function, and therefore are not visible to Objective-C code. Since the method you are trying to define in your extension needs to be visible to Objective-C (so UIKit can use it), it yells at you for not including #objc, but once you do include it, it yells at you because #objc is not allowed in protocol extensions. This is probably because protocol extensions are not currently able to be visible to Objective-C.
We can also see that the error message once we add #objc states "#objc can only be used with members of classes, #objc protocols, and concrete extensions of classes." This is not a class; an extension to an #objc protocol is not the same as being in the protocol definition itself (i.e. in requirements), and the word "concrete" would suggest that a protocol extension does not count as a concrete class extension.
Workaround
Unfortunately, this pretty much completely prevents you from using protocol extensions when the default implementations must be visible to Objective-C frameworks. At first, I thought perhaps #objc was not allowed in your protocol extension because the Swift Compiler could not guarantee that conforming types would be classes (even though you have specifically specified UIViewController). So I put a class requirement on P1. This did not work.
Perhaps the only workaround is to simply use a base class instead of a protocol here, but this is obviously not completely ideal because a class may only have a single base class but conform to multiple protocols.
If you choose to go this route, please take this question (Swift 3 ObjC Optional Protocol Method Not Called in Subclass) into account. It appears that another current issue in Swift 3 is that subclasses do not automatically inherit the optional protocol requirement implementations of their superclass. The answer to that questions uses a special adaption of #objc to get around it.
Reporting the Issue
I think this is being discussed already among those working on the Swift open source projects, but you could be sure they are aware by either using Apple's Bug Reporter, which would likely eventually make its way to the Swift Core Team, or Swift's bug reporter. Either of these may find your bug too broad or already known, however. The Swift team may also consider what you are looking for to be a new language feature, in which case you should first check out the mailing lists.
Update
In December 2016, this issue was reported to the Swift community. The issue is still marked as open with a medium priority, but the following comment was added:
This is intended. There is no way to add the implementation of the method to every adopter, since the extension could be added after the conformance to the protocol. I suppose we could allow it if the extension is in the same module as the protocol, though.
Since your protocol is in the same module as your extension, however, you may be able to do this in a future version of Swift.
Update 2
In February 2017, this issue was officially closed as "Won't Do" by one of the Swift Core Team members with the following message:
This is intentional: protocol extensions cannot introduce #objc entry points due to limitations of the Objective-C runtime. If you want to add #objc entry points to NSObject, extend NSObject.
Extending NSObject or even UIViewController will not accomplish exactly what you want, but it unfortunately does not look like it will become possible.
In the (very) long-term future, we may be able to eliminate reliance on #objc methods entirely, but that time will likely not come anytime soon since Cocoa frameworks are not currently written in Swift (and cannot be until it has a stable ABI).
Update 3
As of Fall 2019, this is becoming less of a problem because more and more Apple frameworks are being written in Swift. For example, if you use SwiftUI instead of UIKit, you sidestep the problem entirely because #objc would never be necessary when referring to a SwiftUI method.
Apple frameworks written in Swift include:
SwiftUI
RealityKit
Combine
CryptoKit
One would expect this pattern to continue over time now that Swift is officially ABI and module stable as of Swift 5.0 and 5.1, respectively.
I just ran into this after enabling 'module stability' (turning on 'Build libraries for distribution') in a swift framework I use.
What I had was something like this:
class AwesomeClass: LessAwesomeClass {
...
}
extension AwesomeClass: GreatDelegate {
func niceDelegateFunc() {
}
}
The function in the extension had these errors:
'#objc' instance method in extension of subclass of 'LessAwesomeClass' requires iOS 13.0.0
Non-'#objc' method 'niceDelegateFunc' does not satisfy requirement of '#objc' protocol 'GreatDelegate'
Moving the functions into the class rather than in an extension resolved the issue.
Here's another workaround. I ran into this issue as well, and cannot switch from UIKit to SwiftUI yet. Moving the default implementations into a common base class was not an option for me either. My default implementations were quite extensive so I really did not want to have all that code duplicated. The workaround I ended up using was to use wrapper functions in the protocol, and then simply call those functions from each class. Not pretty, but may be better than the alternative, depending on the situation. Your code would then look something like this:
#objc protocol P1 : UIAdaptivePresentationControllerDelegate {
}
extension P1 where Self : UIViewController {
func wrapPresentationController(_ controller: UIPresentationController, viewControllerForAdaptivePresentationStyle style: UIModalPresentationStyle) -> UIViewController? {
return UIViewController()
}
}
class A : UIViewController, P1 {
func presentationController(_ controller: UIPresentationController, viewControllerForAdaptivePresentationStyle style: UIModalPresentationStyle) -> UIViewController? {
return wrapPresentationController(controller, viewControllerForAdaptivePresentationStyle: style)
}
}
How do I know which functions need to be implemented by a class to conform to a protocol?
E.g. FBSDKAppInviteDialogDelegate requires appInviteDialog: didCompleteWithResults and appInviteDialog:didFailWithError:.
Xcode only gives an error without specifying these functions:
Type 'InviteFriendsController' does not conform to protocol
'FBSDKAppInviteDialogDelegate'
I found the functions in FBSDKAppInviteContent.h and they are surely mentioned in the FB docs. But is there an easier way to find the functions?
Yes, there is indeed an easier way to find the missing functions! You just need to click on the Arrow next to the error in the Issue Navigator and expand it:
If you hold command and press click the protocol name (in your case FBSDKAppInviteDialogDelegate), the navigator will bring you to the protocol declaration where you can see all the required methods and properties.
As known, it is not possible to include the interface header file from a file in the -Header.h.
My actual problem is that I have the definition of a class one protocol of which is a Swift one:
#protocol arrivingDelegate;
#interface palettaTraffic : NSObject<MKMapViewDelegate, arrivingDelegate> {
}
If I import the *-Swift.h file I get into the ugly cycle when the file is included in another one that is included in the header file.
This is what happens when I use the #protocol directive: it is a warning, but quite a disturbing one.
This is how the swift protocol is defined:
#objc public protocol arrivingDelegate {
func submitManualBusLine(busStripe:StripeProtocol)
}
I also found a similar post:
Swift protocol in Objective-C class
But none of the suggestions seem to apply.
If I import the *-Swift.h file I get into the ugly cycle when the file is included in another one that is included in the header file.
Okay, but that is what you have to do. I don't see you doing it the screen shot above, which is why your protocol is not being seen.
The solution to the "ugly cycle" should be just a matter of tweaking the order in which things are imported in your various Objective-C files.
Adopting swift protocols in Objective-c is a tricky process. I fixed the issue by porting the adopting class to Swift too.
What I tend to do in my projects is putting the protocol conformance of the ObjC class in a Swift file, to avoid this error. Usually the file where the protocol is defined.
extension PalettaTraffic: ArrivingDelegate {}
Why? We're migrating our codebase from ObjC to Swift, but we cannot migrate every class at the same time. Because of this we have a large 'seem' between Swift & ObjC where Swift types need ObjC and vice versa. For me, this is the solution that causes the least amount of work right away.
I just upgraded my project to swift 1.2. And after 5 or 6 consecutive 'Convert to latest Swift' action :), i was able to make it compiles.
Then i had lot's of my UI test failing. It was due to the fact that my 'NSFetchedResultsControllerDelegate' was not called any more.
After (i might say) a very lucky attempt, i found that it as due to the fact that my delegate was not a NSObject. So i was able to fix it by subclassing NSObject or adding #obj.
Before:
class BasicFetchedResultControllerDelegate : NSFetchedResultsControllerDelegate
After:
class BasicFetchedResultControllerDelegate : NSObject, NSFetchedResultsControllerDelegate
I don't think i saw something related to this in the change log. What is the changes that lead to that.
Did you notice other changes like this?
I received this from Apple after i filled up a bug report:
This issue behaves as intended based on the following:
This is a behavior change in Swift 1.2: methods in non-Objective-C-derived classes will no longer be implicitly marked #objc even if they match an Objective-C protocol. You can explicitly mark the methods with the #objc attribute if you don't want to extend NSObject. This is described in the Xcode 6.3 release notes at https://developer.apple.com/library/ios/releasenotes/DeveloperTools/RN-Xcode/Chapters/xc6_release_notes.html#//apple_ref/doc/uid/TP40001051-CH4-SW3.
Core Data classes are still very much behaving like legacy Objective-C classes. As NSFetchedResultsController is a #protocol without any superclass, Swift needs the explicit NSObject declaration.
And you are right, there is nothing about that in the change logs.