Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Delegate methods in child class sometimes not called with Swift 5 compiler

EDIT: As sunshinejr pointed out here, this has been fixed and will be released together with the next Xcode/Swift version.


I've seen a lot of weird behaviour after updating Xcode 10.1 to Xcode 10.2, both with Swift 4 and Swift 5 codebases.

One of the problems is that on one ViewController the ScrollView delegate methods are no longer called. The simplified view hierarchy is as follows:

| ScrollView (ParentScrollView)
| -- Stack View
| ---- ScrollView (ChildScrollView)
| ---- ScrollView (ChildScrollView)
| ---- ScrollView (ChildScrollView)

It acts as a view with several pages: ParentScrollView can be scrolled horizontally, the ChildScrollViews vertically.

The ViewController is the delegate of all Scrollviews (set in Storyboard), but the delegate methods (like scrollViewDidEndDecelerating) are not called when scrolling any of the views (ParentScrollView or ChildScrollView). The base class of ViewController conforms to UIScrollViewDelegate.

I have tried setting the delegates in code, other than that I have no idea what I could be doing wrong. The conversion did not change any code in the class, but everything worked well before updating. I also couldn't find any changes to gestures, delegates or ScrollViews in general in the Swift 5 Release Notes.

This seems to be a bug with the Swift 5 compiler. Additionally, sometimes it does work, sometimes it doesn't - all without changing any code or project settings.

Why does this no longer work? Has anyone else experienced similar behaviour?

like image 383
Jan Erik Schlorf Avatar asked Mar 28 '19 08:03

Jan Erik Schlorf


People also ask

How do protocol delegates work in Swift 5?

Creating Optional Delegate Methods In Swift, using the @objc modifier a protocol can be marked as an Objective-C protocol. After marking the protocol as @objc , specific protocol functions can be marked with @objc which allows the optional modifier to be used on the marked function.

What is delegate method in Swift?

Delegates are a design pattern that allows one object to send messages to another object when a specific event happens. Imagine an object A calls an object B to perform an action.

Are there delegates in SwiftUI?

A new SwiftUI project doesn't have an app or scene delegate by default. Since iOS 14, you create scenes in the App structure. SwiftUI has some built-in scene types like WindowGroup or DocumentGroup .


2 Answers

EDIT: As sunshinejr pointed out here, this has been fixed and will be released together with the next Xcode/Swift version.


I've found the issue, here's how to reproduce it.

class A: UIViewController, UIScrollViewDelegate {
    // ...does not implement 'scrollViewDidEndDecelerating'
}

class B: A {
    func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
        // Will not be called!
    }
}

What does work:

class A: UIViewController, UIScrollViewDelegate {
    func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
        // Probably empty
    }
}

class B: A {
    override func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
        // Will be called!
    }
}

The compiler seems to think that a delegate method is not implemented if the base class did not implement it. If only the child class implements it, it can't find it.

I still can't explain why this behaviour changed with Swift 5, but at least I've found a solution. Maybe someone can give further insights?

like image 87
Jan Erik Schlorf Avatar answered Oct 16 '22 18:10

Jan Erik Schlorf


We ran into this with a UITextViewDelegate

Another workaround is to add the @objc tag to the method in the superclass

like image 25
ndis1 Avatar answered Oct 16 '22 18:10

ndis1