App crashes when passing Dictionary in prepareForSegue - ios

So, I've done this a million times, but today, it seems like my brain has come to a halt and I'm completely stuck on this... Here it goes:
I've got to pass a JSON responseDict to a CollectionViewController via a Push Segue. The response is a timeline feed from Instagram.
In my sourceVC the Dictionary is defined like this: var timelineResponse: [String : AnyObject]? and in my destinationVC the response Dictionary is defined the same way var responseDict: [String : AnyObject]? Although I've tried to define it all sorts of way to see if that was the problem.
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if let identifier = segue.identifier {
if (identifier == "photoSegue") {
//This println correctly outputs the contents of the dict, so I know that the dict is not nil before passing it.
println("sending timelineResponseDict to PhotoCollView with following data: \(timelineResponse)")
// I've tried the following two methods of passing the dict to my PhotoCollView
// (segue.destinationViewController as PhotoCollectionView).responseDict = timelineResponse!
let toVC: PhotoCollectionView = segue.destinationViewController as PhotoCollectionView
toVC.responseDict = timelineResponse!
toVC.stringResp = "Test"
}
}
}
As you can see, I tried to pass a test string, to see if that worked, which it did. So it seems to be the Dictionary type it doesnt want to pass along. But why?
SOLVED
I was trying to pass type AnyObject to a Dictionary, and hence the crash. Sorry for wasting a bit of time here! :/ I appreciate the comments guys!

Related

iOS Swift: best way to pass data to other VCs after query is completed

Context: iOS App written in Swift 3 powered by Firebase 3.0
Challenge: On my app, the user's currentScore is stored on Firebase. This user can complete/un-complete tasks (that will increase/decrease its currentScore) from several ViewControllers.
Overview of the App's architecture:
ProfileVC - where I fetch the currentUser's data from Firebase & display the currentScore.
⎿ ContainerView
⎿ CollectionViewController - users can update their score from here
⎿ DetailsVC - (when users tap on a collectionView cell) - again users can update their score from here.
Question: I need to pass the currentScore to the VCs where the score can be updated. I thought about using prepare(forSegue) in cascade but this doesn't work since it passes "nil" before the query on ProfileVC is finished.
I want to avoid having a global variable as I've been told it's bad practice.
Any thoughts would be much appreciated.
Why don't you create a function that will pull all data before you do anything else.
So in ViewDidLoad call...
pullFirebaseDataASYNC()
Function will look like below...
typealias CompletionHandler = (_ success: Bool) -> Void
func pullFirebaseDataASYNC() {
self.pullFirebaseDataFunction() { (success) -> Void in
if success {
// Perform all other functions and carry on as normal...
Firebase function may look like...
func pullFirebaseDataFunction(completionHandler: #escaping CompletionHandler) {
let refUserData = DBProvider.instance.userDataRef
refUserData.observeSingleEvent(of: .value, with: { snapshot in
if let dictionary = snapshot.value as? [String: AnyObject] {
self.userCurrentScore = dictionary["UserScore"] as! Int
completionHandler(true)
}
})
}
Then when you segue the information across...
In ProfileVC
Create 2 properties
var containerVC: ContainerVC!
var userCurrentScore = Int()
Include the below function in ProfileVC...
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "ProfileToContainerSegue" {
let destination = segue.destination as! ContainerVC
containerVC = destination
containerVC.userCurrentScore = userCurrentScore
}
}
In ContainerVC create a property
var userCurrentScore = Int()
Ways to improve could be an error message to make sure all the information is pulled from Firebase before the user can continue...
Then the information can be segued across the same way as above.
Try instantiation, first embed a navigation controller to your first storyboard, and then give a storyboardID to the VC you are going to show.
let feedVCScene = self.navigationController?.storyboard?.instantiateViewController(withIdentifier: "ViewControllerVC_ID") as! ViewController
feedVCScene.scoreToChange = current_Score // scoreToChange is your local variable in the class
// current_Score is the current score of the user.
self.navigationController?.pushViewController(feedVCScene, animated: true)
PS:- The reason why instantiation is much healthier than a modal segue storyboard transfer , it nearly removes the memory leaks that you have while navigating to and fro, also avoid's stacking of the VC's.

Unnecessary conditional cast (compiler's warning) required for let binding to succeed

I am having a weird behavior with a conditional cast, there is no problem for the app to work but I am trying to understand why this is happening. The issue is when I try to pass a tuple in the sender: parameter of performSegue.
I have a Navigation Controller as my Storyboard Entry Point. There are some cases when I unwind to my root view controller then I perform a segue programmatically from my Navigation Controller, this would look something like this:
#IBAction func unwindSegue(segue: UIStoryboardSegue) {
DispatchQueue.main.async {
self.navigationController?.performSegue(withIdentifier: "someSegue",
sender: ("Hello", "World"))
}
}
Now, in my Navigation Controller I expect the segue like this:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "someSegue" {
if let someViewController = segue.destination as? SomeViewController,
let tuple = sender as? (String,String) {
someViewController.navigationItem.title = tuple.0 + " " + tuple.1
}
}
}
The issue is that this doesn't work, I have to make a conditional cast in my unwindSegue like this:
self.navigationController?.performSegue(withIdentifier: "someSegue",
sender: ("Hello", "World") as? (String,String))
Of course the compiler throws warnings at this:
Conditional cast from '(String, String)' to '(String, String)' always succeeds
Why is this happening? Shouldn't the let binding in my prepare(for segue:,sender:) suffice? Why should I cast it in my unwindSegue(segue:) if sender will arrive as Any? no matter what.
The reason why you have to make a cast is because the sender is an Any? and this implicates that you don't know what you are receiving. It can be anything or nil. You do, however, send a tuple of two strings, that you don't necessarily need to cast.
The question is whether you are unwinding in the right way. Normally the sender is an object that gives information on the sender unwinding the previous view controller. This, of course, can be a tuple of two strings, but the prepare for segue does expect something else.
Hope this gives you some info on the topic.

Error "cannot assign a value of type" in prepareForSegue method (Swift, Xcode 6)

I just want to make a segue by an example here http://www.codingexplorer.com/segue-uitableviewcell-taps-swift/
But when I made this code:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == blogSegueIdentifier {
if let destination = segue.destinationViewController as? specialitiesViewController {
if let cellIndex = tableView.indexPathForSelectedRow()?.row {
destination.formuleName = formulesList[cellIndex]
}
}
}
}
Appeared error called:
cannot assign a value of type '(Formules)' to a value of type String"
this is a class which contents a data for formuleList
formuleList is an array with a list of cell names
how to remove error?
Thank u for your help.
The way that this segue block works is you are basically telling the UI that I'm going to segue to the next view controller
if let destination = segue.destinationViewController as? specialitiesViewController - says that the ViewController we are going to is of custom type specialitiesViewController
My assumption would be that in your specialitiesViewController you have a variable called forumlaName that is of type String and that your formulesList is of type [Formules] - and as such this assignment is invalid.
You are trying to assign a specific Formules to a String and thus occurs your error.
Fix choices would be to make sure the type Formules implements one of the String protocols such as Printable that allows it to be represented as a string. Alternativley perhaps you have something like a name variable inside that is a string
Depending on the version of Swift you are using you could do something like:
extension Formules : Printable {
override public var description: String {
return "TEST"
}
}
but with out more actual code info i can't tell you if this will actually work

Passing data to another ViewController in Swift

Before I begin, let me say that I have taken a look at a popular post on the matter: Passing Data between View Controllers
My project is on github https://github.com/model3volution/TipMe
I am inside of a UINavigationController, thus using a pushsegue.
I have verified that my IBAction methods are properly linked up and that segue.identifier corresponds to the segue's identifier in the storyboard.
If I take out the prepareForSegue: method then the segue occurs, but obviously without any data updating.
My specific error message is: Could not cast value of type 'TipMe.FacesViewController' (0x10de38) to 'UINavigationController' (0x1892e1c).
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// Get the new view controller using segue.destinationViewController.
if segue.identifier == "toFacesVC" {
let navController:UINavigationController = segue.destinationViewController as! UINavigationController
let facesVC = navController.topViewController as! FacesViewController
facesVC.balanceLabel.text = "Balance before tip: $\(balanceDouble)"
}
}
Below is a screenshot with the code and error.
side notes: using Xcode 6.3, Swift 1.2
A couple of things:
1: change your prepareForSegue to
if segue.identifier == "toFacesVC" {
let facesVC = segue.destinationViewController as! FacesViewController
facesVC.text = "Balance before tip: $\(balanceDouble)"
}
2: add a string variable to your FacesViewController
var text:String!
3: change the FacesViewController viewDidLoad
override func viewDidLoad() {
super.viewDidLoad()
balanceLabel.text = text
}
The reasons for all the changes: the segue destinationViewController is the actual FacesViewController you transition to -> no need for the navigationController shenanigans. That alone will remove the "case error", but another will occur due to unwrapping a nil value because you try to access the balanceLabel which will not have been set yet. Therefore you need to create a string variable to hold the string you actually want to assign and then assign that text in the viewDidLoad - at the point where the UILabel is actually assigned.
Proof that it works:
4: If you want display two decimal places for the balance you might change the String creation to something like (following https://stackoverflow.com/a/24102844/2442804):
facesVC.text = String(format: "Balance before tip: $%.2f", balanceDouble)
resulting in:

segue with Dictionary SWIFT

Another question which just should be simple but I am again stumped
I have created a dictionary from a log in.
It is full of values. I now want to segue to a new View controller and take the data with me
So I set up
typealias JSONDict = [String:AnyObject]
func prepareForSegue(segue: UIStoryboardSegue, sender: JSONDict!) {
if (segue.identifier == "toHome"){
var svc = segue.destinationViewController as! OtherViewController
svc.toPass2 = parsed!
Parsed shows as JSONDict - all seems well
Over on OtherViewcontroller I set up to catch it:
var toPass2:JSONDict = JSONDict()
this builds for me quite happily and segues as expected.
But when I
println("check data: \(toPass2)")
I get [:]
Nothing.
It seems to me I have built it - but nothing is coming. I could pass individual values - but all I rally want to do is bring the dictionary with me
I also tried the same thing with the unparsed data string - and this too got me a zero result.
Any help appreciated
in your OtherViewController try this:
class OtherViewController: UIViewController {
var toPass2: JSONDict? {
didSet {
println(toPass2)
}
}
}

Resources