Protocol with "where" causes crash [duplicate] - ios

This question already has answers here:
Swift protocol with "where Self" clause
(1 answer)
Forced to cast, even if protocol requires given type
(5 answers)
Closed 4 years ago.
I want to use Protocol to hide type of classes which is subclass of UIViewController. So I create a Protocol looks like this:
protocol Displayable where Self: UIViewController {
func display()
}
and the concrete class:
class DisplayableViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
}
extension DisplayableViewController: Displayable {
func display() {
_ = view
}
}
Everything goes well, until I perfom display() at runtime:
class ViewController: UIViewController {
private var displayable: Displayable!
override func viewDidLoad() {
super.viewDidLoad()
displayable = DisplayableViewController()
displayable.display()
}
}
Crashes occurs at _ = view.
Thread 1: EXC_BAD_ACCESS (code=1, address=0x0)
Remove where Self: UIViewController or make (displayable as? DisplayableViewController).display() solve this problem, but why?
And I just want subclass of UIViewController to conform it.
Here is the demo to reproduce it.
Swift version: 4.2

Make your type a composite of UIViewController and Displayable.
For example:
private var displayable: (UIViewController & Displayable)!
Here is a link to some docs that contain relevant info on composite types:
https://docs.swift.org/swift-book/ReferenceManual/Types.html

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 write a protocol to pass data backwards to UITableViewController? [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 6 years ago.
Improve this question
i was wondering how to write a protocol to pass data backwards to UITableViewController? If i write a normal protocol as one would write for UIViewController i get an error that "Type "TableViewController" does not conform to type protocol "PresentedViewControllerDelegate". "
Thanks for any help!
Only a protocol is not sufficient, you need also a delegate
Declare your protocol:
protocol PresentedViewControllerDelegate {
func method1(data:[String:AnyObject])
func method2(controller:PresentedViewController)
}
In the first method you pass a custom object (Dictionary),
in the second method the destination controller itself.
In the destination view controller PresentedViewController (the sender) create a delegate property:
weak var delegate : PresentedViewControllerDelegate?
and add code to call the methods
delegate?.method1(someDictionary)
delegate?.method2(self)
The optional chaining is very convenient, the methods aren't called if the delegate is nil.
In the source view controller (the receiver) add PresentedViewControllerDelegate to the declaration line, implement the required methods of the protocol and add a line in prepareForSegue to set the delegate.
let destinationController = segue.destinationController as! PresentedViewController
destinationController.delegate = self
Create your protocol, and have your custom UITableViewController subclass implement the protocol you created. Implement the required methods and properties in your controller and you're good to go.
Ex:
protocol YourProtocol
{
func method1(par1: String, par2: Int)
}
class YourTableViewController: UITableViewController, YourProtocol
{
//MARK:- YourProtocol Methods
func method1(par1: String, par2: Int)
{
//Receive your data through this method
//And Do your thing here
}
}
Your TableViewController have not implemented functions declared in the PresentedViewControllerDelegate protocol. Implement those functions and you'll be fine.
protocol PresentedViewControllerDelegate {
func somefunction(parameter: String)
func anotherfunction()
}
class Table: UITableViewController, PresentedViewControllerDelegate {
func somefunction(parameter: String) {
}
func anotherfunction() {
}
}

How to use delegates properly in Swift?

I read a lot about the delegates but in practice I cannot use it properly.
Description: I have A: UIViewController, B: UIView, C: UIViewController. I want to run segue from A: UIViewController to the C: UIViewController from the inside of B: UIView.
I've tried:
protocol SegueDelegate {
func runSegue(identifier: String)
}
class B: UIView { ... }
where in my A: UIViewController:
override func viewDidLoad() {
B().delegate = self
}
func runSegue(identifier: String) {
self.performSegueWithIdentifier(identifier, sender: self)
}
and trying to call it via:
#IBAction func send(sender: AnyObject) {
let a: SegueDelegate? = nil
a!.runSegue("goToMainPage")
}
but I'm sure that I do not use it properly. Can anyone help me with it? I do not want just an answer. Please describe me it concept shortly
Delegates are just a Design Pattern that you can use in a number of ways. You can look at the Apple Frameworks to see how and where to use delegates as examples. A table view delegate is probably the best known delegate in UIKit.
Delegates serve as a callback mechanism for code to communicate with an instance of an unknown class without knowing more than that that instance will respond to the methods of the delegate protocol.
An alternative to a delegate is to use a closure (what we used to call a block in Objective-C). When to use one vs. the other is a matter of taste. There are a couple of rules of thumb, like for instance outlined here.
What you are doing is, IMO, the proper way to use delegates. You separate the view functionality from the View Controller's functionalities via a delegate, and so the contract for your view is clear: the user needs to respond to the delegate method.
Your code works and is correct. I made a quick implementation here: https://github.com/kristofvanlandschoot/DelegateUsage/tree/master
The main difference from your example, and maybe that's the place where you made a mistake is the third part of your code where you should write something like:
#IBAction func send(sender: AnyObject) {
delegate?.runSegue("segueAB")
}
There are multiple errors in your code, for example:
Here you are creating a new B, and setting A as a delegate of that new instance, no the one you actually want
override func viewDidLoad() {
«B()».delegate = self
}
And here you are creating force unwrapping a nil value
#IBAction func send(sender: AnyObject) {
let a: SegueDelegate? = «nil»
«a!».runSegue("goToMainPage")
}
If what you want to do is tell A to perform a segue to C, from inside B, all you need to do is to call performSegueWithIdentifier on A
For example:
class B: UIView {
weak var referenceToA: UIViewController? = nil // set this somewhere
#IBAction func send(sender: AnyObject) {
guard let a = referenceToA else {
fatalError("you didn't set the reference to a view controller of class A")
}
a.performSegueWithIdentifier("goToMainPage", sender: self)
}
}

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

Delegate declared in UIView subclass won't work in UIViewController?

I'm trying to create a reusable UIView that I can place in multiple UIViewControllers. I gave it delegate methods that I want the parent UIViewControllers to access, but it throws me an error (commented in the code below). What's a better way I can solve this?
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var cameraView: CameraView!
override func viewDidLoad() {
super.viewDidLoad()
self.cameraView.delegate = self
//ERROR: Cannot assign a value of type 'viewController' to a value of type 'CameraViewDelegate?'
}
}
protocol CameraViewDelegate {
func cameraViewShutterButtonTapped()
func cameraViewimagePickerTapped(imageData: NSData)
}
class CameraView: UIView {
var delegate:CameraViewDelegate?
//Ect...
}
You have not specified that ViewController conforms to the CameraViewDelegate protocol. You should amend your code to this:
class ViewController: UIViewController, CameraViewDelegate {
…at which point Xcode will complain that you have not implemented cameraViewShutterButtonTapped() and cameraViewimagePickerTapped(), which at least tells you that you're on the right track!
Side note: do you really want the camera view to have a strong reference to its delegate? You might want that to be weak.
You need to have your ViewController class implement the CameraViewDelegate protocol, like so:
class ViewController : UIViewController, CameraViewDelegate { ... }

Resources