Why passed value is nil in viewcontroller - ios

I know this is simple question but I couldn't understand for hours what's the problem here.
enum TypeOfAlert {
case success, error, warning, confirm
}
class MainAlertView: UIViewController {
var mode: TypeOfAlert!
var transitioner : CAVTransitioner
override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
self.transitioner = CAVTransitioner()
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
self.modalPresentationStyle = .custom
self.transitioningDelegate = self.transitioner
setupAlertView()
}
private func setupAlertView() {
print(mode) // result is nil
}
convenience init() {
self.init(nibName:nil, bundle:nil)
}
required init?(coder: NSCoder) {
fatalError("NSCoding not supported")
}
}
I have this code to show alertdialog
I opening MainAlertView by pressing a button
let vc = IFOMainAlertView()
vc.delegate = self
vc.mode = TypeOfAlert.confirm
self.present(vc,animated: true)
but when I am printing or debuging var mode is always nil. Why ?

You have printed var mode before you set it

you are trying to debug at initialisation moment,
when you called
let vc = IFOMainAlertView()
the function init was called, before you called the rest:
vc.delegate = self
vc.mode = TypeOfAlert.confirm
either add a new constructor to your class that takes mode as an argument, or delay debugging the mode attribute to the viewDidLoad method

Related

How to write custom UIViewController initialiser with property in Swift?

I am trying to write my own UIVC initializer:
let person: Person
init(withPerson person: Person) {
self.person = person
super.init()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
but in my coordinator I have this line:
func getViewController() -> UIViewController? {
return ViewController(nibName: "ViewController", bundle: Bundle.main)
}
and it causes a crash with error:
Fatal error: Use of unimplemented initializer 'init(nibName:bundle:)' for class 'ViewController'
when I try to add:
override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
}
it rises an error with:
Property 'self.person' not initialized at super.init call
is there a way how I can fix the crash?
init(withPerson person: Person) {
self.person = person
super.init (nibName: "ViewController", bundle: Bundle.main)
}
You have to call super.init(nibName:bundle:) instead of super.init() as it's a designated initializer.
If you specify nil for the nibName parameter and you do not override the loadView() method, the view controller searches for a nib file as described here.
init(withPerson person: Person) {
self.person = person
super.init(nibName: nil, bundle: nil)
}

Clean Swift dataStore and routing

I am develop am app in Clean architecture, I want pass data to main screen after user logged in, here is my router file:
import UIKit
protocol LoginRoutingLogic: class {
func routeToRegisterController()
func routeToRecoveryPassword()
func routeToMainPage()
}
protocol LoginDataPassing: class {
var dataStore: LoginDataStore? { get }
}
final class LoginRouter: LoginRoutingLogic, LoginDataPassing {
weak var viewController: LoginController?
var dataStore: LoginDataStore?
func routeToRegisterController() {
let storyboard = UIStoryboard(name: Constants.Identifiers.registerControllerIdentifier, bundle: nil)
if let viewcontroller = storyboard.instantiateViewController(withIdentifier: Constants.Identifiers.registerControllerIdentifier) as? RegisterController {
viewController?.navigationController?.pushViewController(viewcontroller, animated: true)
}
}
func routeToRecoveryPassword() {
let storyboard = UIStoryboard(name: Constants.Identifiers.forgotPasswordControllerIdentifier, bundle: nil)
if let viewcontroller = storyboard.instantiateViewController(withIdentifier: Constants.Identifiers.forgotPasswordControllerIdentifier) as? RecoveryPasswordController {
viewController?.present(viewcontroller, animated: true, completion: nil)
}
}
func routeToMainPage() {
let storyboard = UIStoryboard(name: "MainPageController", bundle: nil)
if let viewcontroller = storyboard.instantiateViewController(withIdentifier: "MainPageController") as? MainPageController {
viewController?.navigationController?.pushViewController(viewcontroller, animated: true)
}
}
}
my user model, that I want to pass the values to the main page, making the call works just fine:
import Foundation
struct User: Codable {
var token: String?
var name: String?
var email: String?
var password: String?
var statusCode: Int?
}
struct LoginError: Codable {
}
extension User {
static func parse(responseData: Data?) -> User? {
var user: User?
guard let data = responseData else {
return user
}
do {
let decoder = JSONDecoder()
user = try decoder.decode(User.self, from: data)
} catch let err {
print("Error: ", err)
}
return user
}
}
and the viewController that I want to pass the username to the username label:
import UIKit
protocol MainPageDisplayLogic: class {
func getData(viewModel: LoginModel.Fetch.ViewModel)
}
final class MainPageController: UIViewController {
var interactor: MainPageBusinessLogic?
var router: (MainPageRoutingLogic & MainPageDataPassing)?
var builder = MainPageBuilder()
// MARK: Object lifecycle
override func viewDidLoad() {
super.viewDidLoad()
setUpView()
}
override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
setup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
}
// MARK: Setup
private func setup() {
let viewController = self
let interactor = MainPageInteractor()
let presenter = MainPagePresenter()
let worker = MainPageWorker()
let router = MainPageRouter()
viewController.interactor = interactor
viewController.router = router
interactor.presenter = presenter
interactor.worker = worker
presenter.viewController = viewController
router.viewController = viewController
router.dataStore = interactor
}
}
extension MainPageController: MainPageDisplayLogic {
func getData(viewModel: LoginModel.Fetch.ViewModel) {
let name = viewModel.name
builder.usernameLabel.text = name
}
}
extension MainPageController: ViewCodeProtocol {
func setUpView() {
viewHierarchy()
makeConstraits()
setupViewNavigationBar()
}
func viewHierarchy() {
view.addSubview(builder.usernameLabel)
}
func makeConstraits() {
builder.usernameLabelConstraits()
}
func setupViewNavigationBar() {
navigationController?.setNavigationBarHidden(false, animated: true)
}
}
And the router from the main page, I could no properly figure that datastore thing yet, how do a pass the data between controllers in clean swift? at least without userdefaults.
import Foundation
protocol MainPageRoutingLogic: class {
}
protocol MainPageDataPassing: class {
var dataStore: MainPageDataStore? { get }
}
final class MainPageRouter: MainPageRoutingLogic, MainPageDataPassing {
weak var viewController: MainPageController?
var dataStore: MainPageDataStore?
}
I have figured that out, I just simply have to use the dataStore references in the destination ViewController and retrieve the data.

Instance of a class only returning nil

Fairly new to swift, and struggling with creating an instance of a class within another class. I'm firing a notification and then in my app delegate it responds by playing the music player and changing label in my viewController, but when it tries to change the label app crashes with error "Unexpectedly found nil while implicitly unwrapping an Optional value". I can get round it by adding a '?' but why is it getting nil.
Code
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, AVAudioPlayerDelegate {
var musicPlayerManager: MusicPlayerManager = MusicPlayerManager()
var viewController: ViewController = ViewController()
func application(_ application: UIApplication, didReceive notification: UILocalNotification) {
//show an alert window
var playlistName: String = ""
if let userInfo = notification.userInfo {
playlistName = userInfo["soundName"] as! String
}
musicPlayerManager.playPlaylist(chosenPlaylist: playlistName)
print(playlistName)
viewController.currentPlaylist.text = "Playlist: \(playlistName)"
}
}
I'm getting the playlistName printed in the console, and then it crashes with the last line suggesting that it's an issue with the instance of viewController, and my attempt to change the label in the view controller from the app delegate.
Outlets are nil until vc loads also you need to load it from storyboard if it exists there
viewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "id") as! ViewController
viewController.view.layoutIfNeeded()
viewController.currentPlaylist.text = "Playlist: \(playlistName)"
If you don't want to use storyboard you can create your ViewController in this structure. But you need to add your Subview(currentPlaylist) into your view and also need to set it's frame
import UIKit
class ViewController: UIViewController {
let currentPlaylist = UILabel()
override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
init() {
super.init(nibName: nil, bundle: nil)
currentPlaylist.frame = CGRect(x: 0, y: 0, width: 200, height: 50)
view.addSubview(currentPlaylist)
}
}

Custom init UIViewController query

I am hoping you can help me understand why the below code segment works and the other does not. I am wanting to create a custom initialiser for my UIViewController which has a custom nib file I have created.
My issue is that I want to understand why in the below code the references to newMember and facebookLogin are retained when I hit the viewDidLoad method but in the other segment of code they are not? Can anyone shed some light as to why this would be the case?
Working Code Block
class RegistrationFormViewController: MiOSBaseViewController
{
var newMember:Member!
var facebookLogin: Bool = false
init(member: Member, facebookLogin: Bool = false) {
self.newMember = member
self.facebookLogin = facebookLogin
super.init(nibName: "RegistrationFormViewController", bundle: nil)
}
required init?(coder aDecoder: NSCoder) {
super.init(nibName: "RegistrationFormViewController", bundle: nil)
}
override func viewDidLoad() {
super.viewDidLoad()
let view = self.view as! RegistrationFormView
view.loadViewWith(member: newMember)
view.customNavBarView.backActionBlock = {
self.newMember.deleteEntity(MiOSDataContext.sharedInstance.managedObjectContext)
_ = self.navigationController?.popViewController(animated: true)
return
}
}
}
Broken Code Block
class RegistrationFormViewController: MiOSBaseViewController
{
var newMember:Member!
var facebookLogin: Bool = false
init(member: Member, facebookLogin: Bool = false) {
self.newMember = member
self.facebookLogin = facebookLogin
super.init(nibName: "RegistrationFormViewController", bundle: nil)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func viewDidLoad() {
super.viewDidLoad()
let view = self.view as! RegistrationFormView
view.loadViewWith(member: newMember)
view.customNavBarView.backActionBlock = {
self.newMember.deleteEntity(MiOSDataContext.sharedInstance.managedObjectContext)
_ = self.navigationController?.popViewController(animated: true)
return
}
}
}
Thanks,
Michael

MMDrawerController Subclass in Swift

if you're using Swift + CocoaPods + use_frameworks! with the MMDrawerController opened source (ObjC) library you might have trouble subclassing MMDrawerController getting multiple warnings of "unused initializer"
All you'll have to do is override the initializers and call super. Pretty easy but I saw that there was absolutely nothing on this issue while Googling for this issue, so I'm just trying to help out. I've posted my code below.
import UIKit
import MMDrawerController
class TRDrawerController: MMDrawerController {
private var recordsModel = TRRecordsModel.sharedInstanceOfRecordsModel
private var itemsModel = TRItemsModel.sharedInstanceOfItemsModel
init() {
let centerViewController = UIStoryboard(name: "TRMain", bundle: nil).instantiateViewControllerWithIdentifier("TRTrackerViewController") as! TRTrackerViewController
centerViewController.recordsModel = recordsModel
centerViewController.itemsModel = itemsModel
let firstNavigationController = TRNavigationController(rootViewController: centerViewController)
let rightViewController = UIStoryboard(name: "TRMain", bundle: nil).instantiateViewControllerWithIdentifier("TRSettingsViewController") as! TRSettingsViewController
rightViewController.recordsModel = recordsModel
centerViewController.itemsModel = itemsModel
let secondNavigationController = TRNavigationController(rootViewController: rightViewController)
super.init(centerViewController: firstNavigationController, leftDrawerViewController: nil, rightDrawerViewController: secondNavigationController)
self.openDrawerGestureModeMask = [.PanningCenterView]
self.closeDrawerGestureModeMask = [.PanningCenterView, .TapCenterView, .TapNavigationBar]
self.shouldStretchDrawer = false
}
override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) {
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
}
// Won't be utilizing this, I want the app to crash if this gets used
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}

Resources