I'm trying to learn the delegation process for Swift in Xcode 8.
I can get it working just fine, but have a question about the subclass in my delegate. Normally, in Objective-C, the subclass for this would be NSObject. I'm able to get it working with NSObject and AnyObject. I read a article about not crossing Objective-C because of performance. Does this really matter? If it's not a view or any other type of controller, what's the subclass in Swift for an object?
Is AnyObject the same as NSObject?
ViewController
import UIKit
class ViewController: UIViewController, TestDelegate {
// init the delegate
let theDelegate = TheDelegate ()
#IBOutlet weak var label1: UILabel!
#IBAction func button1(_ sender: Any) {
// tell the delegate what to do
theDelegate.run(add: 1)
}
override func viewDidLoad() {
super.viewDidLoad()
theDelegate.delegate = self
}
// the protocol
func didTest(int: Int) {
label1.text = "\(int)"
print ("Got back from delegate \(int)")
}
}
Object TestProtocol.swift
import Foundation
protocol TestDelegate: class {
func didTest(int: Int)
}
class TheDelegate: AnyObject{
weak var delegate: TestDelegate?
func run(add: Int){
let test = add + 1
delegate?.didTest(int: test)
}
}
There is no reason to subclass AnyObject, since everything is an AnyObject just by existing (like object in Java).
NSObject is the old base class in Obj-C, so if you are wanting to have your protocol or class be seen in Obj-C code it must either be marked #objc or it must subclass NSObject.
So unless you are also using Obj-C in your program, then you don't need to subclass NSObject.
Look at
Swift 3: subclassing NSObject or not?
https://developer.apple.com/library/content/documentation/Swift/Conceptual/BuildingCocoaApps/MixandMatch.html
Related
// QuizPopUpViewController.swift
#objc protocol QuizPopUpViewControllerDelegate {
func ApplyNowToSendBack()
}
class QuizPopUpViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, UITextViewDelegate {
weak var delegate: QuizPopUpViewControllerDelegate?
}
// giving event from here
if isError == false {
self.delegate?.ApplyNowToSendBack() // delegate method
}
}
// Another Viewcontroller
class ShortlistViewController: ParentViewController , QuizPopUpViewControllerDelegate {
}
When i add QuizPopUpViewControllerDelegate to ShortlistViewController, i get the following error:
Type "ShortlistviewController" does not conform to protocol QuizPopUpViewControllerDelegate
The problem is exactly what the error description is suggesting. You need to make your class conforming on QuizPopUpViewControllerDelegate delegate.
To do so, you need to declare the function, what is in QuizPopUpViewControllerDelegate.
// Another Viewcontroller
class ShortlistViewController: ParentViewController , QuizPopUpViewControllerDelegate {
func ApplyNowToSendBack() {
// do something with the callback.
}
}
To avoid asking such questions in the future, i would recommend to read more about the delegate pattern in Swift.
I am trying to update a label in an NSViewController from a NSWindowController.
Here is what I've done.
Created a protocol:
protocol PopoverProtocol: class {
func SearchForIt(Query:String)
}
Created a function to update the label in the NSViewController
class PopoverController: NSViewController, PopoverProtocol {
#IBOutlet weak var Label: NSTextField!
func SearchForIt(Query:String){
Label.stringValue = Query
}
}
Called the protocol and function from the NSWindowController
class TutorialViewController: NSWindowController, NSSearchFieldDelegate {
weak var responder : PopoverProtocol?
#IBAction override func controlTextDidChange(obj: NSNotification) {
PopoVer.showRelativeToRect(SearchField.bounds, ofView: SearchField, preferredEdge: NSRectEdge.MinY)
responder?.SearchForIt(SearchField.stringValue)
}
}
However nothing is happening. I am not getting any error messages, the function SearchForIt is just not being called.
Any ideas on what I am doing wrong ? Thank you for your help !
A protocol is adopted by a class as you did in your second block of code above. Now your view controller has to implement the SearchForIt method and then your view controller can take the call, not a separately defined object.
In your controlTextDidChange, replace responder with your viewcontroller instance.
I'm new to using delegates in Swift, and I can't seem to figure out how to communicate with my View Controller from a different class. Specifically, I call the custom class's functions from my App Delegate, and then from within that custom class, I call a function within my View Controller. My basic setup, following this question, is:
AppDelegate.swift:
var customClass = customClass()
func applicationDidFinishLaunching(aNotification: NSNotification) {
customClass.customFunction()
}
CustomClass.swift:
weak var delegate: ViewControllerDelegate?
func customFunction() {
delegate?.delegateMethod(data)
}
ViewController.swift:
protocol ViewControllerDelegate: class {
func customFunction(data: AnyObject)
}
class ViewController: NSViewController, ViewControllerDelegate
func customFunction(data: AnyObject){
println("called")
}
}
However, delegate is always nil. I am assuming this is either because the ViewControllerDelegate protocol never gets initialized or because I never set the delegate of the actual NSViewController? I know I'm missing something obvious/straightfoward, however I have yet to see what that is.
Your question is hard to answers because you have completely misunderstood the point of a protocol.
A protocol is a type which is used to define functionality. A class that conforms to this protocol provides the specified functionality, by implementing the required methods.
You can not initialize a protocol.
So if your CustomClass looks like this:
class CustomClass {
weak var delegate: ViewControllerDelegate?
func customFunction() {
delegate?.delegateMethod(data)
}
}
Why do you expect that delegate has suddenly a value?
Of course you have to set delegate to something first. The delegate must set delegate. If you want a ViewController instance to be the delegate, it must assign itself to delegate.
This for instance will work.
protocol ViewControllerDelegate {
func delegateMethod(data: AnyObject) //I renamed this because in
//CustomClass you are trying to call `delegateMethod` on the delegate
}
class CustomClass {
weak var delegate: ViewControllerDelegate?
func customFunction() {
delegate?.delegateMethod(data)
}
}
class ViewController: NSViewController, ViewControllerDelegate
var customClass = CustomClass()
func viewDidLoad(){
customClass.delegate = self
customClass.customFunction()
}
func delegateMethod(data: AnyObject){
println("called")
}
}
Read more about delegation here.
Is it possible for swift to have a ViewController class, initialized from xib, has a property that is also a subclass of UIViewController and conforms to some protocol?
protocol SomeProtocol {
// Some methods
}
class ViewController: UIViewController {
// contentView is initialized from xib
#IBOutlet weak var contentView: UIView!
// I'd like to declare anotherViewController both conforms to 'SomeProtocol'
// and a subclass of UIViewController
var anotherViewController: UIViewController!
...
}
When I declare ViewController as an generic class, say class ViewController<T: UIViewController, SomeProtocol>, I get an error :
"Variable in a generic class cannot be presented in Objective-C"
So how can I fulfil it if I cannot use generic class?
Please forgive me if I misunderstood your problem, but I think what you want to do is declare a new type that inherits from UIViewController and conforms to SomeProtocol, like so:
protocol SomeProtocol { }
class VCWithSomeProtocol: UIViewController, SomeProtocol {
}
class ViewController: UIViewController {
var anotherViewController: VCWithSomeProtocol!
}
So I hope I am not misunderstanding the question as well, but it sounds like you may want a multiple-inheritance object level mixin such as:
let myVC: ViewController, SomeProtocol
Unfortunately, Swift does not support this. However, there is a somewhat awkward work-around that may serve your purposes.
struct VCWithSomeProtocol {
let protocol: SomeProtocol
let viewController: UIViewController
init<T: UIViewController>(vc: T) where T: SomeProtocol {
self.protocol = vc
self.viewController = vc
}
}
Then, anywhere you need to do anything that UIViewController has, you would access the .viewController aspect of the struct and anything you need the protocol aspect, you would reference the .protocol.
For Instance:
class SomeClass {
let mySpecialViewController: VCWithSomeProtocol
init<T: UIViewController>(injectedViewController: T) where T: SomeProtocol {
self.mySpecialViewController = VCWithSomeProtocol(vc: injectedViewController)
}
}
Now anytime you need mySpecialViewController to do anything UIViewController related, you just reference mySpecialViewController.viewController and whenever you need it to do some protocol function, you reference mySpecialViewController.protocol.
Hopefully Swift 4 will allow us to declare an object with protocols attached to it in the future. But for now, this works.
Hope this helps!
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