How to mimic SwiftUI List's data-less initializers when wrapping UITableView - uitableview

Apple offers the following SwiftUI List initializer:
List<SelectionValue: Hashable, Content: View>.init(selection: Binding<SelectionValue?>?, content: () -> Content)
I would like to make a similar component, but customizing some of the UITableView behaviors not exposed in List. Also, I would like to mirror this single-selection, data-less initializer, because I don't have much arbitrary data, and would like to define my rows in-line in a ViewBuilder closure. So, wrapping UITableView:
struct CustomizedList<SelectionValue: Hashable, Content: View>: UIViewRepresentable {
#Binding var selection: SelectionValue
#ViewBuilder var content: () -> Content
func makeUIView(context: Context) -> UITableView {
let tableView = UITableView()
tableView.delegate = context.coordinator
tableView.dataSource = content.coordinator
return tableView
}
func updateUIView(_ tableView: UITableView, context: Context) {
// todo
}
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
final class Coordinator: NSObject, UITableViewDelegate, UITableViewDataSource {
private var parent: CustomizedList
init(_ parent: CustomizedList) {
self.parent = parent
}
// delegate methods to customize behavior
// ...
// data source methods
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// ???
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// ???
}
}
}
What do I do in the data source delegate methods? Since List is a wrapper around UITableView, how is Apple implementing the data source delegate methods for the data-less initializers of List? (Also notice that for List's data initializers, Data is only a generic of the initializer, not List itself.)
Is it possible to use UITableView without a data source, and just pass on the Content (with some View-to-UIView wrapping)?

Related

SwiftUI List is extremely slow at showing actions (leading/trailing, contextMenu) when the dataset is big

I'm facing performance problems using a SwiftUI List with lots of data. I've created a demo app just to showcase the problem with 500_000 Strings and to show a trailing action for one of them, the CPU hits 100% for a few seconds, which is totally unusable. I also went ahead and wrapped UITableView to use it on SwiftUI using the same dataset (the same half a million Strings) and the trailing action is displayed instantly.
Is there any way to speed things up on the SwiftUI List or is this just a limitation of the framework?
I've made it easy to test both implementations by just changing the var named listKind, here's the sample code:
import SwiftUI
#main
struct LargeListPerformanceProblemApp: App {
var body: some Scene {
WindowGroup {
NavigationView {
ContentView().navigationBarTitleDisplayMode(.inline)
}
}
}
}
enum ListKind {
case slow
case fast
}
struct ContentView: View {
var listKind = ListKind.slow
var items: [String]
init() {
self.items = (0...500_000).map { "Item \($0)" }
}
var body: some View {
switch listKind {
case .slow:
List {
ForEach(items, id: \.self) { item in
Text(item).swipeActions(edge: .trailing, allowsFullSwipe: true) {
Button("Print") {
let _ = print("Tapped")
}
}
}
}.navigationTitle("Slow (SwiftUI List)")
case .fast:
FastList(items: self.items)
.navigationTitle("Fast (UITableView Wrapper)")
}
}
}
// UITableView wrapper
struct FastList: UIViewRepresentable {
let items: [String]
init(items: [String]) {
self.items = items
}
func makeUIView(context: Context) -> UITableView {
let tableView = UITableView(frame: .zero, style: .insetGrouped)
tableView.dataSource = context.coordinator
tableView.delegate = context.coordinator
return tableView
}
func updateUIView(_ uiView: UITableView, context: Context) {
uiView.reloadData()
}
func makeCoordinator() -> Coordinator {
Coordinator(items: items)
}
class Coordinator: NSObject, UITableViewDataSource, UITableViewDelegate {
var items: [String]
init(items: [String]) {
self.items = items
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
self.items.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell(style: .default, reuseIdentifier: nil)
cell.textLabel?.text = self.items[indexPath.row]
return cell
}
func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
let printAction = UIContextualAction(style: .normal, title: "Print") { _, _, block in
print("Tapped")
block(true)
}
return UISwipeActionsConfiguration(actions: [printAction])
}
}
}
Profiling in instruments it appears that List creates meta data for every item in order to track changes (for things like insert/delete animations).
So even though List is optimized to avoid creating invisible rows, it still has the meta data overhead that the direct UITableView implementation doesn't incur.
One other demonstration of the overhead of List is to instead use a ScrollView/LazyVStack combination. I don't recommend this as an alternative (in addition to the visual differences, it will blow up as you scroll down the list), but because it doesn't do change tracking, it too will have a fairly quick initial display.

Compatibility between SwiftUI's large title navigation bar and UIViewControllerRepresentable UITableViewController

In order to implement swipe to right in SwiftUI List, I am trying to make UIViewControllerRepresentable UITableViewController.
However, it is not compatible with the navigation bar when the displayMode is .large.
I guess, when the scroll moves, it should
Resize the navigation bar.
Adjust the frame of Scrollview or List.
The #2 is not work with my table view, so something needs to be done.
And I need your help.
[Images]
First rendering works well.
After scrolling, there is the extra space above the table view
After scroll-to-top with tapping the top of the display, the position is broken.
View Hierarchy after scrolling (fixed after the first rendering)
[Code] to reproduce (After creating a swiftui project from xcode, replace the contents of
the ContentView.swift with the code below.)
import SwiftUI
final class TableViewWrapper: UIViewControllerRepresentable {
func makeUIViewController(context: Context) -> UITableViewController {
let viewController = UITableViewController()
viewController.tableView.delegate = context.coordinator
viewController.tableView.dataSource = context.coordinator
return viewController
}
func updateUIViewController(_ uiViewController: UITableViewController, context: Context) {
uiViewController.tableView.reloadData()
}
func makeCoordinator() -> Coordinator {
Coordinator()
}
class Coordinator: NSObject, UITableViewDataSource, UITableViewDelegate {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 100
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell(style: .subtitle, reuseIdentifier: nil)
cell.backgroundColor = .black
cell.textLabel!.textColor = .white
cell.textLabel!.text = "Test"
return cell
}
}
}
struct ContentView: View {
var body: some View {
NavigationView {
TableViewWrapper()
.navigationBarTitle("Nav title", displayMode: .large)
}
}
}
I got a solution.
TableViewWrapper()
.edgesIgnoringSafeArea(.top)

UITableView with controller separate from ViewController

I'm a newbie to Swift and XCode, taking a class in iOS development this summer. A lot of projects we're doing and examples I'm seeing for UI elements like PickerViews, TableViews, etc. are defining everything in the ViewController.swift file that acts as the controller for the main view. This works fine, but I'm starting to get to the point of project complexity where I'd really like all of my code to not be crammed into the same Swift file. I've talked to a friend who does iOS development on the side, he said this is sane and reasonable and well in-line with proper object-oriented programming... but I just can't seem to get it to work. Through trial and error I've gotten to this situation: the app runs in the simulator, the UITableView appears, but I'm not getting it populated with entries. I can get it working just fine when all the code is in the ViewController, but once I start trying to create a new controller class and make an instance of that class the dataSource/delegate of the UITableView I start getting nothing. I feel like I'm either missing some core understanding of Swift here, or doing something wrong with the Interface Builder in XCode.
My end result should be a UITableView with three entries in it; currently I'm getting a UITableView with no entries. I'm following along with a few different examples I've Googled, but primarily this other SO question: UITableView example for Swift
ViewController.swift:
import UIKit
class ViewController: UIViewController{
#IBOutlet var stateTableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
var viewController = StateViewController()
self.stateTableView.delegate = viewController
self.stateTableView.dataSource = viewController
}
}
StateViewController.swift:
import UIKit
class StateViewController: UITableViewController{
var states = ["Indiana", "Illinois", "Nebraska"]
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return states.count;
}
func tableView(cellForRowAttableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
let cell = UITableViewCell(style:UITableViewCellStyle.default, reuseIdentifier:"cell")
cell.textLabel?.text = states[indexPath.row]
return cell
}
}
In XCode I have the UITableView hooked up to the View Controller; the outlets are set to dataSource and delegate and the referencing outlet is stateTableView.
I'm not getting any errors; I do get a warning on my `var viewController = StateViewController()' statement in ViewController.swift where it wants me to use a constant, but switching it to a constant doesn't change the behavior (this is as it should be, I assume).
Originally I assumed that the error was in my StateViewController.swift file, where I'm not creating an object that adheres to the UITableViewDataSource or UITableViewDelegate protocol, but if I even add them into the class statement I immediately get errors like "Redundant conformance of 'StateViewController' to protocol 'UITableViewDataSource'" - I'm reading that this is because inheriting from UITableViewController automatically inherits the other protocols as well.
The last thing I tried was instead referring to self.states in the StateViewController's tableView functions, but I'm pretty sure self in Swift works the same as it does in Python and it feels like I'm just trying to add magic words at this point.
I've investigated as far as my currently-limited Swift knowledge can take me, so any answer that explains what I'm doing wrong rather than just telling me what to fix would be very appreciated.
Your issue is being caused by a memory management problem. You have the following code:
override func viewDidLoad() {
super.viewDidLoad()
var viewController = StateViewController()
self.stateTableView.delegate = viewController
self.stateTableView.dataSource = viewController
}
Think about the lifetime of the viewController variable. It ends when the end of viewDidLoad is reached. And since a table view's dataSource and delegate properties are weak, there is no strong reference to keep your StateViewController alive once viewDidLoad ends. The result, due to the weak references, is that the dataSource and delegate properties of the table view revert back to nil after the end of viewDidLoad is reached.
The solution is to create a strong reference to your StateViewController. Do this by adding a property to your view controller class:
class ViewController: UIViewController{
#IBOutlet var stateTableView: UITableView!
let viewController = StateViewController()
override func viewDidLoad() {
super.viewDidLoad()
self.stateTableView.delegate = viewController
self.stateTableView.dataSource = viewController
}
}
Now your code will work.
Once you get that working, review the answer by Ahmed F. There is absolutely no reason why your StateViewController class should be a view controller. It's not a view controller in any sense. It's simply a class that implements the table view data source and delegate methods.
Although I find it more readable and understandable to implement dataSource/delegate methods in the same viewcontroller, what are you trying to achive is also valid. However, StateViewController class does not have to be a subclass of UITableViewController (I think that is the part that you are misunderstanding it), for instance (adapted from another answer for me):
import UIKit
// ViewController File
class ViewController: UIViewController {
var handler: Handler!
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
handler = Handler()
tableView.dataSource = handler
}
}
Handler Class:
import UIKit
class Handler:NSObject, UITableViewDataSource {
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 10
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("myCell")
cell?.textLabel?.text = "row #\(indexPath.row + 1)"
return cell!
}
}
You can also use adapter to resolve this with super clean code and easy to understand, Like
protocol MyTableViewAdapterDelegate: class {
func myTableAdapter(_ adapter:MyTableViewAdapter, didSelect item: Any)
}
class MyTableViewAdapter: NSObject {
private let tableView:UITableView
private weak var delegate:MyTableViewAdapterDelegate!
var items:[Any] = []
init(_ tableView:UITableView, _ delegate:MyTableViewAdapterDelegate) {
self.tableView = tableView
self.delegate = delegate
super.init()
tableView.dataSource = self
tableView.delegate = self
tableView.rowHeight = UITableViewAutomaticDimension
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
}
func setData(data:[Any]) {
self.items = data
reloadData()
}
func reloadData() {
tableView.reloadData()
}
}
extension MyTableViewAdapter: UITableViewDataSource, UITableViewDelegate {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return items.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = "Hi im \(indexPath.row)"
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
delegate?.myTableAdapter(self, didSelect: items[indexPath.row])
}
}
Use Plug and Play
class ViewController: UIViewController, MyTableViewAdapterDelegate {
#IBOutlet var stateTableView: UITableView!
var myTableViewAdapter:MyTableViewAdapter!
override func viewDidLoad() {
super.viewDidLoad()
myTableViewAdapter = MyTableViewAdapter(stateTableView, self)
}
func myTableAdapter(_ adapter: MyTableViewAdapter, didSelect item: Any) {
print(item)
}
}
You are trying to set datasource and delegate of UITableView as UITableViewController. As #Ahmad mentioned its more understandable in same class i.e. ViewController, you can take clear approach separating datasource and delegate of UITableView from UIViewController. You can make subclass of NSObject preferably and use it as datasource and delgate class of your UITableView.
You can also also use a container view and embed a UITableViewController. All your table view code will move to your UITableViewController subclass.Hence seprating your table view logic from your View Controller
Hope it helps. Happy Coding!!
The way I separate those concerns in my projects, is by creating a class to keep track of the state of the app and do the required operations on data. This class is responsible for getting the actual data (either creating it hard-coded or getting it from the persistent store). This is a real example:
import Foundation
class CountriesStateController {
private var countries: [Country] = [
Country(name: "United States", visited: true),
Country(name: "United Kingdom", visited: false),
Country(name: "France", visited: false),
Country(name: "Italy", visited: false),
Country(name: "Spain", visited: false),
Country(name: "Russia", visited: false),
Country(name: "Moldova", visited: false),
Country(name: "Romania", visited: false)
]
func toggleVisitedCountry(at index: Int) {
guard index > -1, index < countries.count else {
fatalError("countryNameAt(index:) - Error: index out of bounds")
}
let country = countries[index]
country.visited = !country.visited
}
func numberOfCountries() -> Int {
return countries.count
}
func countryAt(index: Int) -> Country {
guard index > -1, index < countries.count else {
fatalError("countryNameAt(index:) - Error: index out of bounds")
}
return countries[index]
}
}
Then, I create separate classes that implement the UITableViewDataSource and UITableViewDelegate protocols:
import UIKit
class CountriesTableViewDataSource: NSObject {
let countriesStateController: CountriesStateController
let tableView: UITableView
init(stateController: CountriesStateController, tableView: UITableView) {
countriesStateController = stateController
self.tableView = tableView
self.tableView.register(UITableViewCell.self, forCellReuseIdentifier: "UITableViewCell")
super.init()
self.tableView.dataSource = self
}
}
extension CountriesTableViewDataSource: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// return the number of items in the section(s)
return countriesStateController.numberOfCountries()
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// return a cell of type UITableViewCell or another subclass
let cell = tableView.dequeueReusableCell(withIdentifier: "UITableViewCell", for: indexPath)
let country = countriesStateController.countryAt(index: indexPath.row)
let countryName = country.name
let visited = country.visited
cell.textLabel?.text = countryName
cell.accessoryType = visited ? .checkmark : .none
return cell
}
}
import UIKit
protocol CountryCellInteractionDelegate: NSObjectProtocol {
func didSelectCountry(at index: Int)
}
class CountriesTableViewDelegate: NSObject {
weak var interactionDelegate: CountryCellInteractionDelegate?
let countriesStateController: CountriesStateController
let tableView: UITableView
init(stateController: CountriesStateController, tableView: UITableView) {
countriesStateController = stateController
self.tableView = tableView
super.init()
self.tableView.delegate = self
}
}
extension CountriesTableViewDelegate: UITableViewDelegate {
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("Selected row at index: \(indexPath.row)")
tableView.deselectRow(at: indexPath, animated: false)
countriesStateController.toggleVisitedCountry(at: indexPath.row)
tableView.reloadRows(at: [indexPath], with: .none)
interactionDelegate?.didSelectCountry(at: indexPath.row)
}
}
And this is how easy is to use them from the ViewController class now:
import UIKit
class ViewController: UIViewController, CountryCellInteractionDelegate {
public var countriesStateController: CountriesStateController!
private var countriesTableViewDataSource: CountriesTableViewDataSource!
private var countriesTableViewDelegate: CountriesTableViewDelegate!
private lazy var countriesTableView: UITableView = createCountriesTableView()
func createCountriesTableView() -> UITableView {
let tableViewOrigin = CGPoint(x: 0, y: 0)
let tableViewSize = view.bounds.size
let tableViewFrame = CGRect(origin: tableViewOrigin, size: tableViewSize)
let tableView = UITableView(frame: tableViewFrame, style: .plain)
return tableView
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
guard countriesStateController != nil else {
fatalError("viewDidLoad() - Error: countriesStateController was not injected")
}
view.addSubview(countriesTableView)
configureCountriesTableViewDelegates()
}
func configureCountriesTableViewDelegates() {
countriesTableViewDataSource = CountriesTableViewDataSource(stateController: countriesStateController, tableView: countriesTableView)
countriesTableViewDelegate = CountriesTableViewDelegate(stateController: countriesStateController, tableView: countriesTableView)
countriesTableViewDelegate.interactionDelegate = self
}
func didSelectCountry(at index: Int) {
let country = countriesStateController.countryAt(index: index)
print("Selected country: \(country.name)")
}
}
Note that ViewController didn't create the countriesStateController object, so it must be injected. We can do that from the Flow Controller, from the Coordinator or Presenter, etc. I did it from AppDelegate like so:
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
let countriesStateController = CountriesStateController()
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
if let viewController = window?.rootViewController as? ViewController {
viewController.countriesStateController = countriesStateController
}
return true
}
/* ... */
}
If it's never injected - we get a runt-time crash, so we know we must fix it straight away.
This is the Country class:
import Foundation
class Country {
var name: String
var visited: Bool
init(name: String, visited: Bool) {
self.name = name
self.visited = visited
}
}
Note how clean and slim the ViewController class is. It's less than 50 lines, and if create the table view from Interface Builder - it becomes 8-9 lines smaller.
ViewController above does what it's supposed to do, and that's to be a mediator between View and Model objects. It doesn't really care if the table displays one type or many types of cells, so the code to register the cell(s) belongs to CountriesTableViewDataSource class, which is responsible to create each cell as needed.
Some people combine CountriesTableViewDataSource and CountriesTableViewDelegate in one class, but I think it breaks the Single Responsibility Principle. Those two classes both need access to the same DataProvider / State Controller object, and ViewController needs access to that as well.
Note that View Controller had now way to know when didSelectRowAt was called, so we needed to create an additional protocol inside UITableViewDelegate:
protocol CountryCellInteractionDelegate: NSObjectProtocol {
func didSelectCountry(at index: Int)
}
And we also need a delegate property to make the communication possible:
weak var interactionDelegate: CountryCellInteractionDelegate?
Note that neither CountriesTableViewDataSource not CountriesTableViewDelegate class knows about the existence of the ViewController class. Using Protocol-Oriented-Programming - we could even remove the tight-coupling between those two classes and the CountriesStateController class.

Swift - iOS - Multiple table view controllers sharing a single data source.

I'd like to get started using swift to make a small list based application. I was planning on using two table view controllers to display the two lists, and was wondering if it were possible to have them share a common data source.
Essentially the data would just be an item name, and two integers representing the amount of the item owned vs needed. When one number increases, the other decreases, and vice versa.
I figured this might be easiest to do using a single data source utilized by both table view controllers.
I did some googling on shared data sources and didn't find anything too useful to help me implement this. If there are any good references for me to look at please point me in their direction!
You can create one data source class and use it in both view controllers:
class Item {
}
class ItemsDataSource: NSObject, UITableViewDataSource {
var items: [Item] = []
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCellWithIdentifier("cell") as! UITableViewCell
//setup cell
// ...
return cell
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return items.count
}
}
class FirstViewController : UITableViewController {
var dataSource = ItemsDataSource()
override func viewDidLoad() {
self.tableView.dataSource = dataSource
self.tableView.reloadData()
}
}
class SecondViewController : UITableViewController {
var dataSource = ItemsDataSource()
override func viewDidLoad() {
self.tableView.dataSource = dataSource
self.tableView.reloadData()
}
}
use singleton design pattern, it means both tables will get data source from the same instance
class sharedDataSource : NSObject,UITableViewDataSource{
static var sharedInstance = sharedDataSource();
override init(){
super.init()
}
//handle here data source
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int{
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell{
}
}
var tableOne = UITableView();
var tableTwo = UITableView();
tableOne.dataSource = sharedDataSource.sharedInstance;
tableTwo.dataSource = sharedDataSource.sharedInstance;
The first argument to the delegate method is:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
}
At that point, your one Datasource delegate can decide which table view is wanting a cell, for example, and return results accordingly.

Swift: My custom UITableViewDataSource class thinks it doesn't conform to protocol 'UITableViewDataSource'

I have checked a few other questions out there, of which the answers seem to be in the datasource methods' declarations. However, I cannot find what's wrong with my methods declarations.
Here is my class:
import UIKit
class GuestControl: UITableViewDataSource, UITableViewDelegate {
#IBOutlet weak var leftTable: UITableView!
#IBOutlet weak var rightTable: UITableView!
var userList = [User]() //even numbers including 0 go on the left table, odd numbers go on the
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
//Here is where we link to that user's contact page
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell: GuestTableViewCell = tableView.dequeueReusableCellWithIdentifier("guestCell") as! GuestTableViewCell
if tableView == leftTable {
cell.configureCellWithUser(userList[indexPath.row+indexPath.row])
} else {
cell.configureCellWithUser(userList[indexPath.row+indexPath.row+1])
}
return cell
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
let num = Double(userList.count) / 2.0
if tableView == leftTable {
return Int(round(num)) //round up
} else {
return Int(num) //round down
}
}
}
And here is the exact error:
Protocol requires function 'tableView(_:numberOfRowsInSection:)' with type '(UITableView, numberOfRowsInSection: Int) -> Int'
Candidate is not '#objc', but protocol requires it
Protocol requires function 'tableView(_:cellForRowAtIndexPath:)' with type '(UITableView, cellForRowAtIndexPath: NSIndexPath) -> UITableViewCell'
Candidate is not '#objc', but protocol requires it
What is wrong?
As the error message indicates, the protocol requires that the table view data source methods are "Objective-C compatible". This can be achieved by
making your class inherit from NSObject (or any NSObject subclass)
class GuestControl: NSObject, ...
or adding the #objc attribute to the class definition
#objc class GuestControl: ...
or adding the #objc attribute to the data source methods
#objc func tableView(tableView: UITableView, numberOfRowsInSection section: Int) ...
In the first two cases, all methods of the class are made Objective-C
compatible.
If a (table) view controller is used as the table view data source then this automatically satisfied because UI(Table)ViewController inherits
from NSObject.

Resources