Why is the data not being saved? - ios

I am getting a fatal error: unexpectedly found nil while unwrapping an Optional value when I am trying to open a notification onto a certain view controller. I think I know why, there are some variables which are nil, causing the app to crash, but I am trying to give those variables data, but they are not saving.
func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject]) {
PFPush.handlePush(userInfo)
let text = userInfo["aps"]!["alert"]
let title = userInfo["aps"]!["alert"]!!["title"]
let artist = userInfo["aps"]!["alert"]!!["artist"]
print(text)
print(title)
print(artist)
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewControllerWithIdentifier("PlayerController") as! PlayerViewController
vc.dataModel.artistplay = artist as? String
vc.dataModel.titleplay = title as? String
window?.rootViewController = vc
}
This is the code for the PlayerViewController (The view controller I am trying to open when the push notification is opened)
#IBOutlet weak var playertitle: UILabel!
#IBOutlet weak var playerartist: UILabel!
var dicData : NSDictionary?
var dataModel : DataModelTwo?
var data: NSDictionary?
var shareDataModel: Share?
var buttonState: Int = 0;
override func viewWillAppear(animated: Bool) {
playertitle.text = dataModel!.titleplay
playerartist.text = dataModel!.artistplay
}

do NOT set UILable text from AppDelegate.
set a variable in your ViewController then use it in viewDidLoad.
func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject]) {
PFPush.handlePush(userInfo)
let text = userInfo["aps"]!["alert"]
print(text)
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewControllerWithIdentifier("PlayerController") as! PlayerViewController
vc.foo = "bar"
window?.rootViewController = vc
}
use variables and update UI :
override func viewDidLoad() {
print(self.foo) // output is "bar"
}

From the code you show, the dataModel in PlayerViewController is not initialized and, as it is an optional, its value is nil. Since you're implicitly unwrapping - dataModel! it causes the crash.
Initialize first the dataModeland then use it to get the values you want.

Related

Handle remote notification from within view controller

I am trying to handle remote notifications from within a specific view controller. I've set my app up so a certain view controller is presented with a specific image in an ImageView when I push a remote notification, but I can't change the image without pushing the same controller again. I'm trying to change the ImageView's image from another remote notification once the view controller has been presented. The idea I had for achieving this, is creating a way to handle remote notifications from within the within the file of the view controller that has just been presented, so the remote notification can edit the ImageView's image. This is how I present the view controller in my AppDelegate:
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: #escaping (UIBackgroundFetchResult) -> Void) {
if let data = userInfo["showiMac"] as? [String: String], let iMacConfig = data["iMacConfig"] {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let rootTabBarController = self.window?.rootViewController as! UITabBarController
let firstNavigationController = storyboard.instantiateViewController(withIdentifier: "initialVC1") as! UINavigationController
rootTabBarController.viewControllers![0] = firstNavigationController
//the viewController to present
let viewController = storyboard.instantiateViewController(withIdentifier: "configureiMac") as! ConfigureiMacViewController
// present VC
firstNavigationController.pushViewController(viewController, animated: true, completion: {
viewController.image = UIImage(named: "iMac" + iMacConfig)
})
completionHandler(.noData)
}
}
And here is how image is declared in my view controller:
var image: UIImage!
override func viewDidLoad() {
super.viewDidLoad()
imageView.image = image
}
In your app delegate, do:
weak var configureiMacViewController: ConfigureiMacViewController?
Create helper functions to keep your code clean and readable:
private func updateiMacImage(_ image: UIImage, in existingViewController: ConfigureiMacViewController) {
existingViewController.image = image
}
private func showiMacImage(_ image: UIImage) {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let rootTabBarController = self.window?.rootViewController as! UITabBarController
let firstNavigationController = storyboard.instantiateViewController(withIdentifier: "initialVC1") as! UINavigationController
rootTabBarController.viewControllers![0] = firstNavigationController
//the viewController to present
let viewController = storyboard.instantiateViewController(withIdentifier: "configureiMac") as! ConfigureiMacViewController
configureiMacViewController = viewController
// present VC
firstNavigationController.pushViewController(viewController, animated: true, completion: {
viewController.image = UIImage(named: "iMac" + iMacConfig)
})
}
Then update:
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: #escaping (UIBackgroundFetchResult) -> Void) {
if let data = userInfo["showiMac"] as? [String: String], let iMacConfig = data["iMacConfig"] {
let image = UIImage(named: "iMac" + iMacConfig)
if let existingViewController = configureiMacViewController {
updateiMacImage(image, in: existingViewController)
} else {
showiMacImage(image)
}
completionHandler(.noData)
}
}
The weak var in your AppDelegate will store a reference to your view controller but the "weak" keyword will allow the view controller to be released from memory if it is dismissed, in which case the variable will be nil the next time you try to access it. If the view controller is still visible, then its value will still be defined and you will be able to re-use it to update the image.
You will most likely have to also update your ConfigureiMacViewController code to:
var image: UIImage! {
didSet {
imageView.image = image
}
}
override func viewDidLoad() {
super.viewDidLoad()
imageView.image = image
}
This ensure that if the view controller was already loaded and displayed, but you update the image variable, that the image view is loaded with the most recent image.
Good luck!

Swift weak delegate

my code:
public func start() {
let storyboard = UIStoryboard(name: "Main", bundle: Bundle.main)
guard let listVC = storyboard.instantiateViewController(withIdentifier: "ListVC") as? ListVC else { return }
let viewModel = ListViewModel(dependencies: appDependencies)
viewModel.delegate = self
listVC.listViewModel = viewModel
navigationController?.pushViewController(listVC, animated: true)
}
protocol ListViewModelDelegate: class {
func needChangeScreen(cellViewModel: UserCellViewModel)
}
final class ListViewModel {
weak var delegate: ListViewModelDelegate?
func userPressed(at index: IndexPath) {
delegate?.needChangeScreen(cellViewModel: cellViewModels[index.row])
}
}
User pressed is called from UIViewController , then i want to send callback to Coordinator to start another coordinator but delegate? is always nil. I know that delegates should be weak but in this case is not working for me. Any ideas?
Okey i have fix but i do not know is it good.
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
let navigationController = UINavigationController()
navigationController.setNavigationBarHidden(true, animated: false)
window?.rootViewController = navigationController
let appDependencies = configureAppDependencies()
let coordinator = AppCoordinator(navigationController: navigationController, appDependencies: appDependencies)
coordinator.start()
window?.makeKeyAndVisible()
return true
}
That was my app delegate function(In AppCoordinator start is created and pushed ListCoordinator) but when i changed let coordinator for instance var :
var coordinator: AppCoordinator?
weak delegate is not nil and everything works.

Wrong margins when instantiate view in a callback iOS

Im having a problem with the UIView margins when Instantiating it on the callback function. When the view is instantiated like this it looks normal:
let viewController = UIStoryboard(name: "Tracking", bundle: nil).instantiateViewControllerWithIdentifier("tracking") as! CarrierTrackingVC
elDrawer.mainViewController = viewController
Normal Screen
But when I instantiate in the api request callback like this, the view looks weird
TrackingController().getTruckTrack("7RZEY3VP") { (response, errs) in
if !self.requestErrors(errs) {
let truckTrack = TruckTrack(json:response["truck_track"].description)
let viewController = UIStoryboard(name: "Tracking", bundle: nil).instantiateViewControllerWithIdentifier("tracking") as! CarrierTrackingVC
elDrawer.mainViewController = viewController
}
}
Weird margins Screen
I would like to know why that happen and any clue of how could I fix it.
Thanks.
EDIT: This is the full code:
import Foundation
import UIKit
import KYDrawerController
import PKHUD
import JLToast
class MenuVC: UITableViewController {
#IBOutlet weak var fullnameLBL: UILabel!
#IBOutlet weak var profileTypeLBL: UILabel!
#IBOutlet weak var usernameLBL: UILabel!
#IBOutlet weak var profilePicIMG: RoundedImage!
#IBOutlet weak var homeLBL: UILabel!
#IBOutlet weak var servicesLBL: UILabel!
#IBOutlet weak var signOutLBL: UILabel!
#IBOutlet weak var trackingLBL: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
initContent()
NSUserDefaults.standardUserDefaults().addObserver(self, forKeyPath: PICTURE, options: NSKeyValueObservingOptions.New, context: nil)
NSUserDefaults.standardUserDefaults().addObserver(self, forKeyPath: NAME, options: NSKeyValueObservingOptions.New, context: nil)
if let url = SessionManager.sharedInstance.picture {
profilePicIMG.imageFromUrl(url)
}
}
override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>) {
if keyPath == PICTURE {
if let url = object as? String {
profilePicIMG.imageFromUrl(url)
}
}
if keyPath == NAME {
usernameLBL.text = object as? String
}
}
deinit {
NSUserDefaults.standardUserDefaults().removeObserver(self, forKeyPath: PICTURE)
NSUserDefaults.standardUserDefaults().removeObserver(self, forKeyPath: NAME)
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath newIndexPath: NSIndexPath) {
self.tableView.deselectRowAtIndexPath(newIndexPath, animated: true)
let elDrawer = (self.navigationController?.parentViewController as! KYDrawerController)
switch newIndexPath.row {
//Profile info
case 0:
break
//Home
case 1:
elDrawer.mainViewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("MainNavigation")
//Services
case 2:
elDrawer.mainViewController = UIStoryboard(name: "Services", bundle: nil).instantiateViewControllerWithIdentifier("services")
case 3:
TrackingController().getTruckTrack("7RZEY3VP") { (response, errs) in
if !self.requestErrors(errs) {
let truckTrack = TruckTrack(json:response["truck_track"].description)
let viewController = UIStoryboard(name: "Tracking", bundle: nil).instantiateViewControllerWithIdentifier("tracking") as! CarrierTrackingVC
viewController.truckTrack = truckTrack
elDrawer.mainViewController = viewController
}
}
/*let viewController = UIStoryboard(name: "Tracking", bundle: nil).instantiateViewControllerWithIdentifier("tracking") as! CarrierTrackingVC
viewController.truckTrack = TruckTrack()
elDrawer.mainViewController = viewController*/
default:
signnOut()
}
elDrawer.setDrawerState(.Closed, animated: true)
}
func signnOut() {
HUD.show(.LabeledProgress(title: NSLocalizedString("SIGNING_OUT", comment: ""), subtitle: nil))
UserController().signOut { (response, err) in
HUD.hide()
self.changeRootViewControllerWithIdentifier("start",storyboard: "Main")
}
}
func initContent() {
fullnameLBL.text = SessionManager.sharedInstance.userFullName
usernameLBL.text = SessionManager.sharedInstance.username
profileTypeLBL.text = SessionManager.sharedInstance.profileType
}
}
EDIT: viewController is a UITabBarController, each Tab contains a Navigation Controller and it's child is a View Controller.
It looks as if the vc's view is being covered by the navigation bar, which, prior to OS 6 or 7, was the default. I can't explain why the behavior would appear in one instantiation context vs the other, but you fix by setting explicitly with:
let viewController = // yada yada
viewController.edgesForExtendedLayout = UIRectEdgeNone
EDIT if I'm right that viewController is a navigation view controller, then we need it's root to adjust the edge property...
let viewController = /*yada yada*/ as! UINavigationController
let rootVC = viewController.viewControllers[0]
rootVC.edgesForExtendedLayout = UIRectEdgeNone

Unable to retrieve view controller with NSProgressIndicator programatically

I am trying to retrieve the actual view controller and call a method to change the ProgressIndicator.
I have the following function in my AppDelegate
func applicationDidFinishLaunching(_ aNotification: Notification) {
let main = NSStoryboard(name: "Main", bundle: Bundle.main)
let vc = main.instantiateController(withIdentifier: "Installing") as! ViewController
vc.incrementBar(number: 20)
}
The incrementBar function is as follows
#IBOutlet weak var progressView: NSProgressIndicator!
public func incrementBar(number: Double){
if(self.progressView != nil){
print(number)
self.progressView.increment(by: number)
}
}
Just to be sure. I can easily call this function from viewDidLoad() or viewDidAppear() but as soon as i try to receive the actual controller it tells me that progressView is nil

Programmatically Setting Root View Controller in Swift

I'm not sure why this is so trivial, but my Swift translation has been a slow one. Anyways, I can't figure out what it is complaining about. All I am trying to do is set the root view controller, and the compiler spits an error saying:
"Splash pageController does not have a member named init"
Here's my app delegate:
var window: UIWindow?
var CLIENT_KEY = c_key()
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Override point for customization after application launch.
window = UIWindow(frame: UIScreen.mainScreen().bounds)
var url_string = "XXXX=\(CLIENT_KEY.client_key)"
var g_home_url = String.stringWithContentsOfURL(NSURL.URLWithString(url_string), encoding: NSUTF8StringEncoding, error: nil)
if (g_home_url? != nil){
NSUserDefaults.standardUserDefaults().setObject(g_home_url, forKey: "showUrl")
}
let DocumentsDirectory = NSSearchPathDirectory.DocumentationDirectory
let UserDomainMask = NSSearchPathDomainMask.UserDomainMask
if let paths = NSSearchPathForDirectoriesInDomains(DocumentsDirectory, UserDomainMask, true){
if paths.count > 0{
if let dirPath = paths[0] as? String {
// let url: NSURL = NSURL.URLWithString("\(g_home_url)/XXX\(CLIENT_KEY.client_key)")
// println(url)
var err: NSError?
var g_home_url = NSUserDefaults.standardUserDefaults().objectForKey("showUrl") as String
println(g_home_url)
var image = UIImage(data: NSData(contentsOfURL: NSURL.URLWithString("\(g_home_url)XXX=\(CLIENT_KEY.client_key)")))
let writePath = dirPath.stringByAppendingPathComponent("splash_page.png")
UIImagePNGRepresentation(image)
}
}
}
//This is where the error shows up
var rootview: SplashViewController = SplashViewController()
if let window = window {
window.rootViewController = rootview;
}
SplashViewController
class SplashViewController: UIViewController {
}
Very basic I know. Normally, in Objective-C I would init a nib name and set the view controller to that. What's the main difference in Swift?
This line:
var rootview: SplashViewController = SplashViewController()
...calls SplashViewController's init method. But you have not given SplashViewController any init method. Give it one.
What I usually do is specify the nib in my init override; for example:
override init() {
super.init(nibName:"SplashViewController", bundle:nil)
}
If you don't do something along those lines, the view controller won't find its nib and you'll end up with a black screen.
Note that that code will very likely net you a new error message complaining that you didn't implement init(coder:); so you'll have to implement it, even if only to throw an error if it is accidentally called:
required init(coder: NSCoder) {
fatalError("NSCoding not supported")
}

Resources