Currently I'm trying to test some simple functions:
class SampleViewController: UIViewController {
#IBOutlet weak var sampleView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
}
func hideView() {
sampleView.isHidden = true
}
func showView() {
sampleView.isHidden = false
}
}
Here are the test cases:
import XCTest
#testable import unitUITestPractice
class unitUITestPracticeTests: XCTestCase {
var sut: SampleViewController?
override func setUp() {
super.setUp()
sut = SampleViewController()
}
override func tearDown() {
sut = nil
super.tearDown()
}
func test_hideView() {
sut?.hideView()
XCTAssertNil(sut?.sampleView)
}
func test_showView() {
sut?.showView()
XCTAssertNotNil(sut?.sampleView)
}
}
The issue is I keep getting the error:
Thread 1: Fatal error: Unexpectedly found nil while unwrapping an
Optional value
when it tries and run the function in the tests case:
func hideView() {
sampleView.isHidden = true //error here
}
Does anyone know a better way to test whether a view has changed?
Related
Ideally, I want to create a BaseViewController class that takes in a protocol type (of a delegate) and have a weak variable as the delegate. Something like this:
class BaseViewController<Delegate: AnyObject> {
weak var delegate: Delegate?
init(delegate: Delegate) {
self.delegate = delegate
super.init(...)
}
}
And then inherit from a view controller like so:
protocol MyDelegate: AnyObject {
func funcA()
func funcB()
}
class SomeViewController: BaseViewController<MyDelegate> {
func doSomething() {
delegate?.funcA()
}
}
This doesn't work as the compiler complains:
'BaseViewController' requires that 'MyDelegate' be a class type
How can I work this around to achieve what I need?
Thanks in advance :)
Thats because in swift protocols doesn't confirm to them selves, you can't use "MyProtocol" as concrete type confirming to protocol "MyDelegate"
What you can rather do is
protocol MyDelegate: AnyObject {
func funcA()
func funcB()
}
class BaseViewController<Delegate: MyDelegate> {
weak var delegate: Delegate?
init(delegate: Delegate) {
self.delegate = delegate
super.init(...)
//keeping OPs code as is
}
}
class SomeOtherDelegateClass: MyDelegate {
func funcA() {
//some code here
}
func funcB() {
//some code here
}
}
class SomeViewController: BaseViewController<SomeOtherDelegateClass> {
func doSomething() {
self.delegate?.funcA()
}
}
EDIT 1:
As OP mentioned in comment, he is trying to introduce a generic property in BaseViewController that will simply hold a weak reference to any instance whose class is decided/declared by Child classes of BaseViewController using generics, I am simplifying the above answer a bit
Try this
protocol MyDelegate {
func funcA()
func funcB()
}
class BaseViewController<Delegate> where Delegate: AnyObject {
weak var delegate: Delegate?
init(delegate: Delegate) {
self.delegate = delegate
super.init(...)
//keeping OPs code as is
}
}
class SomeOtherDelegateClass: MyDelegate {
func funcA() {
//some code here
}
func funcB() {
//some code here
}
}
class SomeViewController: BaseViewController<SomeOtherDelegateClass> {
func doSomething() {
self.delegate?.funcA()
}
}
protocol MyDelegate2 {
func funcABCD()
}
class SomeOtherDelegateClass2: MyDelegate2 {
func funcABCD() {
//some code here
}
}
class SomeViewController2: BaseViewController<SomeOtherDelegateClass2> {
func doSomething() {
self.delegate?.funcABCD()
}
}
TBH, I really dont see much of benefit of this design! Probably you need to revisit the code structure and see if you can come up with better code structure :)
You should set your delegate as a constraint for the generic type T in BaseViewController:
protocol MyDelegate: AnyObject {
func funcA()
func funcB()
}
class Delegated1: MyDelegate {
func funcA() { print("A1") }
func funcB() {}
}
class Delegated2: MyDelegate {
func funcA() { print("A2") }
func funcB() {}
}
class BaseViewController<T: MyDelegate>: UIViewController {
var delegate: T?
func doSomething() {
delegate?.funcA()
}
}
class SomeViewController1: BaseViewController<Delegated1> {}
class SomeViewController2: BaseViewController<Delegated2> {}
class TestClass {
let viewController1: SomeViewController1 = {
let viewController = SomeViewController1(nibName: nil, bundle: nil)
viewController.delegate = .init()
return viewController
}()
let viewController2: SomeViewController2 = {
let viewController = SomeViewController2(nibName: nil, bundle: nil)
viewController.delegate = .init()
return viewController
}()
// prints:
// A1
// A2
func myFunc() {
viewController1.doSomething()
viewController2.doSomething()
}
}
I am using Xcode 10.3. I have protocol which method is not calling. What is wrong?
My first view controller with protocol:
protocol MyProtocol: class {
func doGetUpdateInfo(valu1:String,value2:String);
}
class Class_A : UIViewController{
weak var myprotocolDelegate:MyProtocol? = nil;
override func viewDidLoad() {
super.viewDidLoad()
myprotocolDelegate?.doGetUpdateInfo(account_no:value1, account_title: value2)
}
}
My second view controller
class Class_B: UIViewController,UpdateBeneficiaryProtocol {
var class_a = Class_A()
override func viewDidLoad() {
super.viewDidLoad()
class_a.myprotocolDelegate = self;
}
func doGetUpdateInfo(value1: String, value2: String) {
print("not calling****")
}
}
What is the wrong with it?
Please see the below example. you are creating a new class for A but so it will not be called. you need to provide a reference for the current class
protocol MyProtocol: class {
func doGetUpdateInfo(valu1:String,value2:String);
}
class Class_A : UIViewController{
weak var myprotocolDelegate:MyProtocol? = nil;
override func viewDidLoad() {
super.viewDidLoad()
myprotocolDelegate?.doGetUpdateInfo(account_no:value1, account_title: value2)
}
func navigateToClassB() {
let classb = Class_B()
classb.class_a = self
self.navigationController?.pushViewcontroller(classb, animated:true)
}
}
And class b should be
class Class_B: UIViewController,UpdateBeneficiaryProtocol {
var class_a : Class_A?
override func viewDidLoad() {
super.viewDidLoad()
class_a.myprotocolDelegate = self;
}
func doGetUpdateInfo(value1: String, value2: String) {
print("not calling****")
}
}
Push controller Class_b as per display in method navigateToClassB.
If you face still issue comment here I will assist you.
I want to call the label from ViewController and set it at different points in the code for example there are 2 different functions and both of them will set the label to lets say "hi" and 2nd function will set it to "hello"
I used Swift3 protocol way just like this: Access label.text from separate class in swift
I have another init in the class so I'm not sure why it would not set the label text to new values but is nil.
I hit the connect button first.
Here is my code:
protocol HiResponder: class {
func hi()
func hello()
}
class ViewController: UIViewController, HiResponder {
#IBOutlet weak var status: UILabel!
var test: Test!
override func viewDidLoad() {
super.viewDidLoad()
test.responder = self //gives me nil fatal error: unexpectedly found nil while unwrapping an Optional value
}
#IBAction func connectBtn(_ sender: Any) {
self.test = Test(
p1: "hey",
p2: "there"
)
self.test.connect(p1: "hey", p2: "there")
}
func hi() {
status.text = "hi"
}
hello() {
status.text = "hello"
}
}
This is the class that sets the value of these responders:
class Test {
var test:TestQQ?
var p1:String?
var p2: String?
weak var responder: HiResponder?
init(p1: String, p2:String) {
self.p1 = p1
self.p2 = p2
}
init(responder: TestResponder) {
self.responder = responder
}
// Connect
func connect(p1:String, p2:String) {
//code
}
func setHello(){
responder?.hello()
}
func setHi(){
responder?.hi()
}
}
I tried to generalize my code but thats the idea. My connect function is being called in my viewDidload of viewController.
You didn't instantiate test. Since viewdidload will run first it is of course nil. Therefore, you cannot acces anything from it. What you need to do is find a spot to instantiate it first before you use it. Maybe in connectBTN you can set the responder after you create it instead lf in viewdidload
The solution to deal with the unwrapped nil Optional value is to set test as an Optional Test object.
There is another problem though. You never actually use either of the functions from the protocol. hi() or hello()
Below I used both functions from the protocol but not from another class. In fact I didn't use Test at all. Not sure what your goal was with using a different class to set the Label text.
protocol HiResponder: class {
func hi()
func hello()
}
class ViewController: UIViewController, HiResponder {
#IBOutlet weak var status: UILabel!
var num = 1
var test: Test?
override func viewDidLoad() {
super.viewDidLoad()
test?.responder = self //gives me nil fatal error: unexpectedly found nil while unwrapping an Optional value
}
#IBAction func connectBtn(_ sender: Any) {
if num % 2 == 0 {
hi()
} else {
hello()
}
num += 1
}
func hi() {
status.text = "hi"
}
func hello() {
status.text = "hello"
}
}
class Test {
var test: Test?
var p1: String?
var p2: String?
weak var responder: HiResponder?
init(p1: String, p2:String) {
self.p1 = p1
self.p2 = p2
}
init(responder: HiResponder) {
self.responder = responder
}
// Connect
func connect(p1:String, p2:String) {
//code
}
func setHello(){
responder?.hello()
}
func setHi(){
responder?.hi()
}
}
In my project I have implemented a protocol which makes some URL call and return result, and my intent is to show result in UILabel. Following is my code :
protocol RestAPIResult
{
func retriveDriverInfo()
}
class RestAPICall : RestAPIResult
{
func retriveDriverInfo()
{
self.dashBoardViewController.getDriverInfo(driverProfile)
// calling another function of Next View for Lable Setup
}
}
getDriverInfo is in NextView which has Outlet of textVIew
class DashBoardViewController: UIViewController
{
override func viewDidLoad()
{
super.viewDidLoad()
restDeledate = RestAPICall()
restDeledate!.retriveDriverInfo()
// IF LABEL SET HERE NO ERROR
//totalTripLabel.text = "Testing" // NO ERROR
}
func getDriverInfo(driverInfoArray : NSArray)
{
totalTripLabel.text = "Testing" // HERE IS ERROR
}
}
If Text is set in ViewDidLoad() it doesn't crash. but when i tried to set value in delegate function it crash saying found null.
protocol RestAPIResult: NSObjectProtocol
{
func retriveDriverInfo(message: String)
}
class RestAPICall : RestAPIResult
{
override func viewDidLoad()
{
super.viewDidLoad()
//create an instance of your vc and
instance.delegate = self
//if you code for push vc then write this line there insatance.delegate = self
}
func retriveDriverInfo(message: String)
{
yourLbaelOutlet.text = message
}
}
class DashBoardViewController: UIViewController
{
weak var delegate: RestAPIResult!
override func viewDidLoad()
{
super.viewDidLoad()
}
func getDriverInfo(driverInfoArray : NSArray)
{
self.delegate.retriveDriverInfo(message: "youMessage")
}
}
*
I got this error message when trying to develop video player.
/Users/MNurdin/Documents/iOS/SwiftVideoPlayer/SwiftVideoPlayer/ViewController.swift:12:1:
Type 'ViewController' does not conform to protocol 'MPMediaPlayback'
My code
import UIKit
import MediaPlayer
class ViewController: UIViewController, MPMediaPlayback { //error message here
var isPreparedToPlay: Bool = true
var currentPlaybackRate: Float = 0.0
var currentPlaybackTime: NSTimeInterval = 0.0
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
func play() {
}
func pause() {
}
func stop() {
}
func prepareToPlay() {
}
func beginSeekingBackward() {
}
func beginSeekingForward() {
}
func endSeeking() {
}
}
What am I missing here?
Source code: https://github.com/datomnurdin/SwiftVideoPlayer
Reference: https://developer.apple.com/library/ios/documentation/MediaPlayer/Reference/MPMediaPlayback_protocol/index.html#//apple_ref/occ/intfp/MPMediaPlayback/currentPlaybackTime