I have a ViewController called Home, in Home viewDidAppear have a function CheckStatus that I need to call every time it received a specific notify.
So currently in AppDelegate, I call this code to present Home anytime the notify is received, which cause memory leaking and crashes:
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let controller = storyboard.instantiateViewController(withIdentifier: "MainTabBarController") as! MainTabBarController
//Home is the first ViewController of the TabBar
self.window?.rootViewController = controller
What is the solution for this?
Updated ViewDidAppear and it's functions
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
self.tabBarController?.tabBar.isHidden = false
setupTabbar() //setup tab bar UI
self.locationService.getLocation()
self.checkRequestStatus()
self.navigationController?.setNavigationBarHidden(false, animated: animated)
}
func checkRequestStatus(){
API.checkRequestStatus{ [weak self] json, error in
if let error = error {
}else {
if let json = json {
let status = json[Const.STATUS_CODE].boolValue
if (!API.isSuccess(response: json)){
if (API.getErrorCode(response: json) == Const.INVALID_TOKEN){
let alert = UIAlertController(title: "Message".localized(), message: "You have logged in from another device. Please login again.", preferredStyle: UIAlertController.Style.alert)
alert.addAction(UIAlertAction(title: "OK".localized(), style: UIAlertAction.Style.default, handler:
{(action:UIAlertAction!) in
let defaults = UserDefaults.standard
print ("got here")
defaults.set("", forKey: Const.Params.TOKEN)
if self?.presentingViewController != nil {
self?.dismiss(animated: false, completion: {
self?.navigationController!.popToRootViewController(animated: true)
})
}
else {
self?.navigationController!.popToRootViewController(animated: true)
}
}))
self!.present(alert, animated: true, completion: nil)
}
}
let defaults = UserDefaults.standard
if let currency : String = json[Const.CURRENCEY].rawString() {
defaults.set(currency, forKey: json[Const.CURRENCEY].rawString()!)
}
if let cancellation : Int = json[Const.CANCELLATION_FINE].intValue {
let str : String = String(cancellation)
defaults.set(str, forKey: Const.CANCELLATION_FINE)
}
if(status){
let requestDetail: RequestDetail = RequestDetail()
let jsonAry:[JSON] = json[Const.DATA].arrayValue
let defaults = UserDefaults.standard
if jsonAry.count > 0 {
let driverData = jsonAry[0]
if driverData.exists() {
defaults.set(driverData["request_id"].stringValue, forKey: Const.Params.REQUEST_ID)
defaults.set(driverData["provider_id"].stringValue, forKey: Const.Params.DRIVER_ID)
requestDetail.initDriver(rqObj: driverData)
}
let invoiceAry:[JSON] = json[Const.INVOICE].arrayValue
if invoiceAry.count > 0 {
let invoiceData = invoiceAry[0]
defaults.set(invoiceData.rawString(), forKey: Const.CURRENT_INVOICE_DATA)
requestDetail.initInvoice(rqObj: invoiceData)
}
self?.processStatus(json: json, tripStatus:requestDetail.tripStatus)
} else {
requestDetail.tripStatus = Const.NO_REQUEST
let defaults = UserDefaults.standard
defaults.set(Const.NO_REQUEST, forKey: Const.Params.REQUEST_ID)
}
}
}
}
}
}
You are force-unwrapping self:
self!.present(alert, animated: true, completion: nil) First see if your app still crashes when your replace the call with self?..
Related
I want to ask I have a sign in view controller who don't want to dismiss after correctly add email and password. but when I try the simulator for the first time the sign in is working and directing to me to my home controller, but after I sign out. and try to sign in again, then the sign in not dismissing my sign in view controller. how is that possible? at first is working later on is not working, here I show you my code.
// this is my sign out button
#objc private func handleSignOut() {
let alert = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
alert.addAction(UIAlertAction(title: "Log Out".localized(), style: .destructive, handler: { (_) in
self.progressHUD.show(in: self.view)
ProfileServices.shared.signOutUser { success in
if success {
self.progressHUD.dismiss(animated: true)
let signInVC = SigninViewController()
self.present(signInVC, animated: true, completion: nil)
} else {
self.progressHUD.textLabel.text = "Error"
self.progressHUD.dismiss(afterDelay: 0.4)
}
}
}))
alert.addAction(UIAlertAction(title: "Cancel".localized(), style: .cancel, handler: nil))
present(alert, animated: true, completion: nil)
}
// this is my sign out function in ProfileServices.shared
func signOutUser(completion: #escaping (Bool) -> Void) {
AF.request(API_URL.AUTHENTICATION.LOGOUT, method: .delete, parameters: nil, encoding: URLEncoding.default, headers: HEADERS, interceptor: nil).responseData { (dataResponse) in
if dataResponse.error == nil {
let domain = Bundle.main.bundleIdentifier!
UserDefaults.standard.removePersistentDomain(forName: domain)
UserDefaults.standard.synchronize()
UserDefaults.removeToken()
completion(true)
} else {
completion(false)
}
}
}
// this is my sign in route in my sign in view controller
func routeToMainView(_ data: SigninModel.Response) {
let school = UserDefaults.getSelectedSchool()
guard let schools = data.schools?.schools else { return }
if let selectedSchool = school, let selected = schools.first(where: { $0.id == selectedSchool.id}) {
UserDefaults.saveSelectedSchool(data: selected)
let vc = MainViewController()
self.viewController?.navigationController?.setViewControllers([vc], animated: true)
} else {
if schools.count > 1 {
let vc = SwitchSchoolViewController()
self.viewController?.navigationController?.setViewControllers([vc], animated: true)
} else {
guard let selected = schools.first else { return }
UserDefaults.saveSelectedSchool(data: selected)
DispatchQueue.main.async {
let vc = MainViewController()
self.viewController?.navigationController?.setViewControllers([vc], animated: true)
}
}
}
}
// this is in my appDelegate
var root: UIViewController?
root = SigninViewController()
if UserDefaults.getToken() != nil {
root = MainViewController()
}
In logout you need to dissmiss the presented viewController.
inplace :
let signInVC = SigninViewController()
self.present(signInVC, animated: true, completion: nil)
you need to use:
self.dismiss(animated: true, completion: nil)
or pop if you will use Push.
I want to show this popup in my view controller and when I push the button go back
This is the part of I want to show the pop up:
#IBAction func entrarAction(_ sender: UIButton) {
sender.isEnabled = false
let l = showLoader()
let user = userTextField.text!
let passwd = passwordTextField.text!
if !validaEntrada(user: user, password: passwd){
// TODO: show errors!
// Show popup here
return
}
let login = VenLogin(usuario: user, contrasenia: passwd)
try! RequestManager.fcReq(url: .login, req: login, res: VenLoginResponse.self) { loginResp, err in
sender.isEnabled = true
l.dismiss(animated: true, completion: {
if let r:VenLoginResponse = loginResp as? VenLoginResponse {
if r.code0 {
let c = self.getAppDelegateContainer()
c.register(from: .loginResponse, value: r)
c.register(from: .tokenJwt, value: r.tokenJwt!)
self.pushServicios()
}
}
})
}
}
Instead of a custom View Controller for your popup you can use an Alert View
#IBAction func entrarAction(_ sender: UIButton) {
sender.isEnabled = false
let l = showLoader()
let user = userTextField.text!
let passwd = passwordTextField.text!
if !validaEntrada(user: user, password: passwd){
// Show popup here
showErrorAlert()
return
// the rest of your code
}
func showErrorAlert() {
let alert = UIAlertController(title: "Usuario o contraseña no válido", message: "", preferredStyle: UIAlertController.Style.alert)
alert.addAction(UIAlertAction(title: "Aceptar", style: UIAlertAction.Style.cancel, handler: nil))
self.present(alert, animated: true, completion: nil)
}
But if you still want to use your custom View Controller use this to show the popup:
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let popupErrorLogin = storyboard.instantiateViewController(withIdentifier: "PopupErrorLogin") as! PopupErrorLogin
self.present(popupErrorLogin, animated: true, completion: nil)
And this to close it:
#IBAction func aceptarAction(_ sender: Any) {
self.dismiss(animated: true, completion: nil)
}
the process is :
1 the user login with touch id
2 the user logout (logout dismiss to login)
then the app autotrigger touch id with success response (this not happen in the emulators) HELP!!
this is my listener on login viewcontroller
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
if authenticationContext.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) && DataContainerSingleton.sharedDataContainer.fingerPrintEnabled == "SI"{
self.touchIdListener()
}
}
with
func touchIdListener(){
authenticationContext.evaluatePolicy(
.deviceOwnerAuthenticationWithBiometrics,
localizedReason: self.valFromCurrentLanguaje(valor: "LOGIN_FINGER_MSG"),
reply: { [unowned self] (success, error) -> Void in
if( success ) {
let keychain = KeychainSwift()
let data = keychain.get(Constants.USER_KEY)?.components(separatedBy: ",")
DispatchQueue.main.async {
self.showProgress(text: Constants.EMPTY_STRING)
}
self.fromTouchId = true
self.loginBL.startLogin(email: (data?[0])!, password: data?[1])
}else {
// Check if there is an error
if let error = error {
print("\(error.localizedDescription)")
}
}
})
}
So the solution was change the dismiss
self.dismiss(animated: true, completion: nil)
for a new instance
var storyboard = UIStoryboard()
let vc : LoginViewController
if (Constants.IS_IPAD) {
storyboard = UIStoryboard.init(name: "LoginIpad", bundle: nil)
vc = storyboard.instantiateViewController(withIdentifier: "ipadViewController") as! LoginViewController
} else {
storyboard = UIStoryboard.init(name: "Login", bundle: nil)
vc = storyboard.instantiateViewController(withIdentifier: "iphoneViewController") as! LoginViewController
}
let navigationController = UINavigationController(rootViewController: vc)
self.present(navigationController, animated: true, completion: nil)
I want to display alert for check new version of my app from API. And from that view if userDefault data is store so base on that I want to redirect to another view. My redirection code is work perfectly but when I add alert code so redirection doesn't work. I think alert present in "self" and that time I also try to redirect from that view so it's may create problem. Here is my code..
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
updateUserData() //base on userDefault redirection
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
checkNewVersion() //Alert function for API calling
}
func checkNewVersion() -> Void {
//TODO: For check App new version
WebRequester.shared.getAppNewVersion { (result, error) in
if result != nil {
if result?.value(forKey: "status") as! Bool {
let strVer = (result?.object(forKey: "data") as! NSDictionary).object(forKey: "platform_version") as! String
if UserData.getAppVersion() < strVer {
let alert = UIAlertController(title: "Alert", message: "New version of App available", preferredStyle: .alert)
let ok = UIAlertAction(title: "Ok", style: .default, handler: { (action) in
})
let AppStore = UIAlertAction(title: "App Store", style: .default, handler: { (action) in
if let url = URL(string: "itms-apps://itunes.apple.com/app/id1024941703"),
UIApplication.shared.canOpenURL(url){
UIApplication.shared.openURL(url)
}
})
alert.addAction(ok)
alert.addAction(AppStore)
self.present(alert, animated: true, completion: nil)
// OperationQueue().addOperation {
// // Put queue to the main thread which will update the UI
// OperationQueue.main.addOperation({
// self.present(alert, animated: true, completion: nil)
// })
// }
}
}
else {
if (result?.object(forKey: "data") as! NSArray).object(at: 0) as? String ?? "" == "Unauthorised access." {
Model.shared.deleteAllCoreDataRecord(entity: "CartItem")
UIApplication.topViewController()?.navigationController?.popToRootViewController(animated: true)
}
let msg = result?.value(forKey: "data") as! [String]
Model.shared.showAlert(title: "Error", msg: msg[0], controller: self)
}
}
else {
Model.shared.showAlert(title: "Error", msg: error?.localizedDescription ?? "Something went wrong at add new address", controller: self)
}
}
}
func updateUserData() -> Void {
if UserData.getAppVersion() == "1.0" {
if UserData.getUserData() != nil {
//TODO: check for user Updated data
let params = ["mobile":UserData.getUserMobile()] as [String : Any]
let propic = UIImage(named: "temp")
weak var objWeek = self
Model.shared.showActivity(WithTouchEnable: false,controller: self)
WebRequester.shared.customerSignup(params: params as NSDictionary, proImg: propic!){ (result,error) -> Void in
Model.shared.HideActivity(controller: self)
if (error == nil) {
print("login result:",result ?? "")
//handle response of sign up
let statusstr = result?["status"] as! Bool
if (statusstr == false) {
//This condition for pepsi welcome offer is expire or not
let user = result!["user_info"] as! NSDictionary
self.storeUserData(user: user)
if UserData.getUserMobileNumVerify() == "No" {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let registerScreen = storyboard.instantiateViewController(withIdentifier: "UserRegisterPhoneVC") as! UserRegisterPhoneVC
objWeek?.navigationController?.pushViewController(registerScreen, animated: true)
}
else {
if UserData.getPepsiOfferRedim() == "1" || UserData.getPepsiOfferRedim() == "2" {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let offerScreen = storyboard.instantiateViewController(withIdentifier: "OfferViewController") as! OfferViewController
objWeek?.navigationController?.pushViewController(offerScreen, animated: true)
}
else {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let promoScreen = storyboard.instantiateViewController(withIdentifier: "PromotionViewController") as! PromotionViewController
objWeek?.navigationController?.pushViewController(promoScreen, animated: true)
}
}
}
}
}
else {
Model.shared.showAlert(title: "Error", msg: (error?.localizedDescription)!, controller: self)
}
}
}
}
}
To achieve this , you need to click on alert OK button then only it will automatically navigate to other controller , without this not possible .
Here is code :
Alert controller block help you to achieve this :
//show an alert and navigate to previous controller
let alertController: UIAlertController = UIAlertController(title: "Password updatd", message: "your alert message", preferredStyle: .alert)
let okAction: UIAlertAction = UIAlertAction(title: "OK", style: .default) { action -> Void in
//Redirect to new viewcontroler
let newVC = self.storyboard.instantiateViewcontroller(identifier: "newvc") as? NewVC
self.navigationController?.pushViewController(newVC,animated: true)
}
alertController.addAction(okAction)
self.present(alertController, animated: true, completion: nil)
Feel free to comment. Thanks
I am making Email verification using OTP . I have used two API , one for registration and other for OTP verification. I want to move on the next page when user is valid. For this , I want to use NSUserDefault to store the token from the API response. When , I use this , i am unable to store this . Please anybody help me for this.
Here is my code
class OTPVerification: UIViewController, UITextFieldDelegate {
#IBOutlet weak var tfReceivedOTP: UITextField!
var datapassed:String!
let loader = MFLoader()
override func viewDidLoad() {
super.viewDidLoad()
tfReceivedOTP.attributedPlaceholder = NSAttributedString(string:"OTP",
attributes:[NSForegroundColorAttributeName: UIColor.whiteColor()])
tfReceivedOTP.delegate = self
print(datapassed)
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(true)
let defaults = NSUserDefaults.standardUserDefaults()
if defaults.objectForKey("email") == nil {
if let loginController = self.storyboard?.instantiateViewControllerWithIdentifier("ConfirmationMassage") as? SignInConformation {
self.navigationController?.presentViewController(loginController, animated: true, completion: nil)
}
}
let defaults = NSUserDefaults.standardUserDefaults()
if defaults.objectForKey("token") == nil {
if let loginController = self.storyboard?.instantiateViewControllerWithIdentifier("ConfirmationMassage") as? SignInConformation {
self.navigationController?.presentViewController(loginController, animated: true, completion: nil)
}
}
}
func textFieldShouldReturn(textField: UITextField) -> Bool {
textField.resignFirstResponder()
return true
}
override func prefersStatusBarHidden() -> Bool {
return true
}
#IBAction func btnOTPVerificationTapped(sender: AnyObject) {
loader.showActivityIndicator(self.view)
let rgModel = CAOTPVerify()
rgModel.email = datapassed!
rgModel.otpPassword = tfReceivedOTP.text!
rgModel.otpRegister({(Void, Any) -> Void in
let defaults = NSUserDefaults.standardUserDefaults()
if let name = defaults.stringForKey("userNameKey") {
print("\(name )hjhjkhkhkh")
}
self.loader.hideActivityIndicator(self.view)
if let response = Any as? [String : AnyObject] {
//print(response)
if let messgae = response["message"] as? String {
let alert = UIAlertController(title: "Alert", message: messgae, preferredStyle: UIAlertControllerStyle.Alert)
alert.addAction(UIAlertAction(title: "Ok", style: UIAlertActionStyle.Default, handler: {action in
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let OPTView = storyboard.instantiateViewControllerWithIdentifier("sideBarMenu") as! SideBarMenu
self.navigationController!.pushViewController(OPTView, animated: true)
}))
self.presentViewController(alert, animated: true, completion: nil)
}
}
}, errorCallback: {(Void, NSError) -> Void in
self.loader.hideActivityIndicator(self.view)
})
}
You didnt posted the code to save a string to the NSUserDefaults. You can refer following code to save a string in NSUserDefaults
let myString = "Hello World"
NSUserDefaults.standardUserDefaults().setObject(myString, forKey: "myKey")