I am currently following a video tutorial course about test driven development of iOS in Swift, but when testing Table View in View Controller, I get stuck, since I don't understand why we need NSObject in Interface builder like the picture below:
Movie Library Data Service is inheritted NSObject class:
the class of MovieLibraryDataService
is like this:
import UIKit
class MovieLibraryDataService: NSObject, UITableViewDataSource, UITableViewDelegate {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 0
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
return UITableViewCell()
}
}
and the MovieLibraryDataService
class will be used in XCTestCase is like this :
@testable import FilmFest
class LibraryViewControllerTests: XCTestCase {
var sut: LibraryViewController!
override func setUp() {
super.setUp()
// Put setup code here. This method is called before the invocation of each test method in the class.
sut = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "LibraryViewControllerID") as! LibraryViewController
_ = sut.view
}
override func tearDown() {
// Put teardown code here. This method is called after the invocation of each test method in the class.
super.tearDown()
}
// MARK: Nil Checks
func testLibraryVC_TableViewShouldNotBeNil() {
XCTAssertNotNil(sut.libraryTableView)
}
// MARK: Data Source
func testDataSource_ViewDidLoad_SetsTableViewDataSource() {
XCTAssertNotNil(sut.libraryTableView.dataSource)
XCTAssertTrue(sut.libraryTableView.dataSource is MovieLibraryDataService)
}
// MARK: Delegate
func testDelegate_ViewDidLoad_SetsTableViewDelegate() {
XCTAssertNotNil(sut.libraryTableView.delegate)
XCTAssertTrue(sut.libraryTableView.delegate is MovieLibraryDataService)
}
// MARK: Data Service Assumptions
func testDataService_ViewDidLoad_SingleDataServiceObject() {
XCTAssertEqual(sut.libraryTableView.dataSource as! MovieLibraryDataService, sut.libraryTableView.delegate as! MovieLibraryDataService)
}
}
and the definition of LibraryViewController:
import UIKit
class LibraryViewController: UIViewController {
@IBOutlet weak var libraryTableView: UITableView!
@IBOutlet var dataService: MovieLibraryDataService!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.libraryTableView.dataSource = dataService
self.libraryTableView.delegate = dataService
}
}
I really don't understand why I need to make that MovieLibraryDataService
class
I usually use:
self.libraryTableView.dataSource = self
self.libraryTableView.delegate = self
but why do I need to write :
self.libraryTableView.dataSource = dataService
self.libraryTableView.delegate = dataService
You can use NSObject
on the Storyboard
for different purposes, one of them could be also the delegation. Instead of setting it programatically like:
self.libraryTableView.dataSource = self
self.libraryTableView.delegate = self
you can hold control and then set the corresponding delegates like below:
Because MovieLibraryDataService
that conforms to UITableViewDataSource
and UITableViewDelegate
and not your LibraryViewController
.
If you want to change it as you are always used to, change your code to:
class LibraryViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
@IBOutlet weak var libraryTableView: UITableView!
var dataService = MovieLibraryDataService()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.libraryTableView.dataSource = self
self.libraryTableView.delegate = self
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 0
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
return UITableViewCell()
}
}
Personally I would suggest to keep it as you have it, as View Controllers tend to grow and become like we call it Massive View Controller
MovieLibraryDataService
is just another class which implements UITableViewDataSource
and UITableViewDelegate
, with the difference that the storyboard instantiates it and that storyboard-created
instance is bound via the IBOutlet
@IBOutlet var dataService: MovieLibraryDataService!
. All objects in the storyboard are storyboard-created
, that's why one has to bind them to variables to use if they are not bound to other variables you use already.
Naming the variable dataService
is just a fancy way of saying that it is supposed to serve
, in this case as dataSource and delegate
for a tableView, since it implements those delegate-protocols.
Since the dataService
is instantiated by the storyboard, you could try if you can bind the dataService
inside the storyboard to tableView
. This is possible because you have the dataService
referencable in the storyboard. This would replace setting dataSource and delegate
in the viewController
.
AppDelegate
is also an NSObject
inside the Storyboard so the UIApplication/NSApplication
inside the storyboard is able to reference that to use, avoids having to set up AppDelegate
yourself outside a storyboard. (Would otherwise be ugly, maybe that's macOS only though, since mac apps have to show a Menu
even if its empty.)
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.