4

I want to use a library with a class SwipeTableViewCell (derives from UITableViewCell), but it only supports iOS 9, so I want to derive from that class if possible, but from the normal UITableViewCell class if the app is running below 9.0.

This is the best I could come up with (not working):

@available(iOS, obsoleted: 9.0)
class MyTableViewCell : UITableViewCell {
}

@available(iOS 9.0, *)
class MyTableViewCell : SwipeTableViewCell {
}

Then I try to derive from that class:

class SomeOtherTableViewCell : MyTableViewCell {
}

Xcode gives me the following error and links to the two class definitions above:

SomeOtherTableViewCell.swift:34:31: 'MyTableViewCell' is ambiguous for type lookup in this context
Found this candidate
Found this candidate

What's the cool way in Swift to conditionally derive from two different classes depending on API level?

Daniel Brown
  • 1,134
  • 1
  • 13
  • 27
  • 1
    The inheritance is evaluated at *compile time,* not at runtime. – Martin R Jul 06 '17 at 14:18
  • @MartinR that is bad, so you are saying it's not possible at all? – Daniel Brown Jul 06 '17 at 14:26
  • 1
    Perhaps I am overlooking something, but I assume that you have to define two subclasses MyTableViewCell and MySwipeTableViewCell, and then use `if #available()` to create instances of one or the other at runtime. – Martin R Jul 06 '17 at 14:33
  • Yeah, as a last resort, but we want to use it as the base class for many different TableViewCells, so it would duplicate a lot of code. – Daniel Brown Jul 07 '17 at 00:52

1 Answers1

2

Unfortunately (or fortunately depending on how you look at it), exactly what you're trying to do is not possible with Swift or Objective-C from a language perspective. The @available attribute you're using is to tell the compiler that this class is only available if you're compiling a target with a deployment target greater than what you specified.

@MartinR made mention of the #available and the general pattern that you should employ to achieve your goal.

The reason these 2 things are available and necessary to support backward compatibility is because compiled code doesn't necessarily have all of the information about the runtime. The compiler/linker check for the existence of APIs, classes, etc by using the headers and module maps of the SDK at compile time. But, your app is linked with the system frameworks dynamically at runtime. This pattern is why your app doesn't have to be distributed with copies of every part of the SDK that it uses.

In your case, you have SDK9 and are trying to support a runtime of iOS 8 or less. The SwipeTableViewCell doesn't work in iOS 8 likely because it uses APIs that are only available in iOS 9. All of this doesn't prevent SwipeTableViewCell from existing on iOS 8 devices, it just prevents it from working.

You really should evaluate whether you need to support iOS 8 at all. If you change your target's deployment target to iOS 9 and release it to the App Store, people running iOS 8 won't get the update or be able to install it.

Update

After doing some thinking, and some research, I came up with a possible solution for your situation. I only tested it in an environment that relies on a storyboard for the UI, but I think it can gracefully bridge your iOS targets.

import UIKit

class ViewController: UITableViewController {

    @IBOutlet var staticCell: Any? // In the storyboard, tell the cell to by a "MySwipeableCell"

    override func viewDidLoad() {
        super.viewDidLoad()

        if staticCell is SwipeTableViewCell {
            print("This class was converted magically, which means we're on iOS 9 or later")
        }
    }
}

class SwipeTableViewCell: UITableViewCell {
    // Standin for your library class
}

class MySwipeableCell: UITableViewCell {

    override func awakeAfter(using aDecoder: NSCoder) -> Any? {
        if #available(iOS 9, *) {
            // This will replace the MySwipeableCell with an instance of SwipeTableViewCell instead
            return SwipeTableViewCell.init(coder: aDecoder)
        }

        return self
    }
}
allenh
  • 6,582
  • 2
  • 24
  • 40
  • Great input from @MartinR and you on what's going on, especially the part about `doesn't prevent SwipeTableViewCell from existing on iOS 8` got me intrigued now! We'll have to tinker around how much annotations it takes to get it runnable, but looks like a way to go .. Active iOS 8 Users (who contribute to the community) vs Fancy Swipe Menu is currently 1:0, so it's worth getting a little brain damaged. :-) – Daniel Brown Jul 07 '17 at 19:37
  • It's worth mentioning that what you're trying to do would actually be super easy in objective-c :-) – allenh Jul 08 '17 at 14:08