JSQMessagesviewcontroller can't perform segues - swift

so I created a chat view controller using the JSQMessagesViewController following this tutorial here: https://learnappmaking.com/chat-app-ios-firebase-swift-xcode/#comment-1930 my code is more or less the same, I didn't tweak anything significant in it, the tutorial is only for a single view controller so I added another view controllers for the app but every time it perform segues, I get the error SIGABRT, no matter if I segues with performSegue or with the back button in navigation bar, it keeps giving signal SIGABRT. any help would be appreciated.
this is my viewdidload:
override func viewDidLoad() {
super.viewDidLoad()
senderId = "1111"
senderDisplayName = "Bob"
title = "Steve"
inputToolbar.contentView.leftBarButtonItem = nil
collectionView.collectionViewLayout.incomingAvatarViewSize = CGSize.zero
collectionView.collectionViewLayout.outgoingAvatarViewSize = CGSize.zero
let query = Constants.refs.databaseChats.queryLimited(toLast: 10)
_ = query.observe(.childAdded, with: { [weak self] snapshot in
if let data = snapshot.value as? [String: String],
let id = data["sender_id"],
let name = data["name"],
let text = data["text"],
!text.isEmpty
{
if let message = JSQMessage(senderId: id, displayName: name, text: text)
{
self?.messages.append(message)
self?.finishReceivingMessage()
}
}
})
// Do any additional setup after loading the view.
}

SIGABRT (signal abort) is typically from a referencing error in your storyboard. Did you ever change the name of a class or make a connection from a button of one view controller to another and then delete it? If you changed the name of a class you must make the sure the name in the code of the class matches that. If you deleted a button connection between view controllers, click on the controller itself and under the connections tab you must delete it.

Related

MVVM project. How to connect Model View to the View

I am trying to make an app that will show the weather in my city. I am using MVVM architecture and I have my Model, ModelView and View as follows. I have a variable inside the WeatherModelView class that I want to use in my view controller:
label.text = "(weatherViewModel.res?.main.temp ?? -123)"
but it does not work.
(https://i.stack.imgur.com/8BTEJ.png)
View Controller](https://i.stack.imgur.com/qW54n.png)
It does not give an error, it simply prints -123.0 on the label, which is the nil case after unwrapping. I would like it to print the actual weather. I don't think there are problems with the URL or the JSON decoding.
This is what is wrongfully shown when I run it: simulator
In "viewdidload" "fetchWeather" is not complete.
You need set "label.text" after it completed
change res in your view model
var res = WeatherModel? {
didSet {
resHasData?()
}
}
var resHasData?: (() -> Void)?
add my code in the last line "viewDidLoad"
weatherViewModel.resHasData = { [weak sekf] in
guard let self = self else { return }
self.label.text = "\(self.weatherViewModel.res?.main.temp ?? -123)"
}
good luck.

Losing Data when switching to another view controller Xcode Swift

I'm having trouble figuring out exactly what's going on. Basically, I'm opening my first view controller which takes a users info from firebase. i.e. the name and uid. I'm sending data by didSet() in my second view controller (this is sent before the second view controller is opened). The data does get sent because I've tested it by using print(). However, when I navigate to my second view controller (using a tab controller and navigation controller, the data is no longer there when I navigate to my second view controller.
import Foundation
struct User {
let uid: String
let name: String
init(uid: String, dictionary: [String: Any]) {
self.uid = uid
self.name = dictionary["uname"] as? String ?? ""
}
}
Then in my first controller I use:
guard let uid = Auth.auth().currentUser?.uid else {
print("Could not init User at start")
return }
Database.database().fetchUser(withUID: uid) { (user) in
secondController.user = user
}
..
In my secondView controller I have:
var user: User? {
didSet {
printUser()
}
}
printUser(){
let uid = user?.uid
print(uid)
}
This is how I know the data is sent.
However, when I Navigate to the second viewcontroller by a tab bar controller and navigation controller, the data is not there. i.e I have printUser() in view did load, but it comes up as an error because user?.uid is nil.
I'm navigating viewControllers through storyboard.

ScrollView IBOutlet nil when using protocol to call function in viewDidDisappear

I have a scrollView that contains a dynamic amount of WeatherViewControllers each displaying the weather data of a different city the user has saved. The user can segue from the WeatherViewControllers to a CityListViewController. Where they can add and remove cities from their list which in turn should add and remove WeatherViewControllers from the scrollView upon dismissing the CityListViewController, this is where I am running into a problem.
Currently I am trying to use a protocol in to call the func reloadScrollView which calls viewDidLoad in the scrollViewController upon dismissing(viewDidDisappear) the CityListViewController but am getting an error:
Fatal error: Unexpectedly found nil while unwrapping an Optional value: file
when it gets to:
totalScrollView.addSubview(weatherScreen.view)
Using debugger I have found that totalScrollView is nil and that is causing the problem. Is there a way to make the scrollView load so it is not nil when dismissing the other viewController
OR
is the a better time to call use this protocol to call this function?
Side Note: Upon initially opening the app the scrollView loads properly with all the correct WeatherViewControllers in the UIScrollView and the correct cities in the list.
class ScrollViewController: UIViewController, ScrollReloadProtocol {
func reloadScrollView() {
print("SCROLL RELOADED!!!!!*******")
self.viewDidLoad()
}
#IBOutlet var totalScrollView: UIScrollView!
var pages = [ViewController]()
var x = 0
var weatherScreensArray = [SavedCityEntity]()
var weatherScreenStringArray = [String]()
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
var horizString = "H:|[page1(==view)]"
let defaults = UserDefaults.standard
override func viewDidLoad() {
super.viewDidLoad()
//userDefaults used to keep track of which screen is which to put different cities on different viewControllers
defaults.set(0, forKey: "screenNumber")
//load cities to get number of cities saved
loadCities()
var views : [String: UIView] = ["view": view]
//create all weatherWeatherControllers
while x <= weatherScreensArray.count {
pages.append(createAndAddWeatherScreen(number: x))
weatherScreenStringArray.append("page\(x+1)")
views["\(weatherScreenStringArray[x])"] = pages[x].view
let addToHoriz = "[\(weatherScreenStringArray[x])(==view)]"
horizString.append(addToHoriz)
x+=1
}
horizString.append("|")
let verticalConstraints = NSLayoutConstraint.constraints(withVisualFormat: "V:|[page1(==view)]|", options: [], metrics: nil, views: views)
let horizontalConstraints = NSLayoutConstraint.constraints(withVisualFormat: horizString, options: [.alignAllTop, .alignAllBottom], metrics: nil, views: views)
NSLayoutConstraint.activate(verticalConstraints + horizontalConstraints)
}
//Function to create and add weatherViewController
func createAndAddWeatherScreen(number: Int) -> ViewController {
defaults.set(number, forKey: "screenNumber")
let story = UIStoryboard(name: "Main", bundle: nil)
let weatherScreen = story.instantiateViewController(identifier: "View Controller") as! ViewController
weatherScreen.view.translatesAutoresizingMaskIntoConstraints = false
totalScrollView.addSubview(weatherScreen.view)
addChild(weatherScreen)
weatherScreen.didMove(toParent: self)
return weatherScreen
}
}
Skipping that fact that your are not doing it right, let's forcus on the one issue at a time. You are trying to access the totalScrollView implicitly in the viewDidLoad where if the outlet is linked it should be loaded at that point. If it is nil you should:
Make sure that you have the .storyboard or .xib file defining the ScrollViewController layout.
Make sure you are loading this controller from that storyboard/xib.
Make sure that the view controller in the storyboard/xib file has set its class to ScrollViewController, similar to the following print screen:
Make sure that the outlet is linked in the storyboard/xib to this property in your code file (probably ScrollViewController.swift). If not:
open storyboard and sorucecode file in separate editors
drag and drop from the dot on the left of the property declaration to the UIScrollView in the storyboard
make sure that there is added a link to Referencing Outlets

My cells are duplicating themselves

I am new to swift and I am trying to make this note app. I have split view controller that goes in my first view controller and that view controller connects to a table view controller. Everything works perfectly is just that when I launch the app I have all the notes like I want but when I try to go back to my first view controller and come back to my table view controller, all the notes are duplicated every single time I do it. I tried everything I can try, is there anyone who can help me
my MasterViewController is
import UIKit
class MasterViewController: UITableViewController {
var detailViewController: DetailViewController? = nil
override func viewDidLoad()
{
super.viewDidLoad()
Note.loadNotes() // The problem is here, I think
noteTable = self.tableView
// Do any additional setup after loading the view, typically from a nib.
let addButton = UIBarButtonItem(barButtonSystemItem: .add, target: self, action: #selector(insertNewObject(_:)))
navigationItem.rightBarButtonItem = addButton
if let split = splitViewController
{
let controllers = split.viewControllers
detailViewController = (controllers[controllers.count-1] as! UINavigationController).topViewController as? DetailViewController
}
}
My loadNotes function is
class func loadNotes()
{
let defaults:UserDefaults = UserDefaults.standard
let saveData: [NSDictionary]? = defaults.object(forKey: kAllNotes) as? [NSDictionary]
if let data:[NSDictionary] = saveData
{
for i:Int in 0 ..< data.count
{
let n:Note = Note()
n.setValuesForKeys(data[i] as! [String : Any])
allNotes.append(n)
}
}
}
Your loadNotes method keeps appending. The first line of loadNotes should be:
allNotes = [Note]()
Then it starts with an empty array and fills it up.
And why is loadNotes a static method? That's a bad design. Make Notes a normal class and make loadNotes an instance method.
On an unrelated note (no pun intended), do not use UserDefaults to store app data. Only use it to store little bits of information.

Table view is not getting updated after dismissing the popover?

I'm banging my head for some time due to this issue. I precise my scenario in detail.
I have a table view where I can add data using a popover which gets displayed on clicking the '+' button in the navigation bar. I get the values from the popover but where I'm stuck is, the data received is not getting reflected in the tableview. If I move back and forth it gets displayed. Tried to reload the table with different possibilities but nothing works.
If you do want a taste of my code, you can get it here Data stored fails to display in the table view, in one to many relationship of core data?
Could anyone solve my problem, help is very much appreciated.
The idea here is to provide a way for the Add Teams popover view controller to tell the Team table view controller to reload its table view.
In the Add Team VC swift file, define a protocol:
protocol AddTeamsDelegateProtocol {
func didAddTeam()
}
In the Add Team class, add a new delegate property which of this type:
var delegate : AddTeamsDelegateProtocol? = nil
In the same class, call the delegate method when the new Team is saved:
#IBAction func submit(sender: AnyObject) {
let entity = NSEntityDescription.entityForName("Teams", inManagedObjectContext: managedObjectContext)
let team = Teams(entity: entity!, insertIntoManagedObjectContext: managedObjectContext)
team.teamName = teamNamePO.text
team.teamImage = teamImagePO.image
do{
try managedObjectContext.save()
} catch let error as NSError{
print("\(error), \(error.userInfo)")
}
self.delegate?.didAddTeam()
dismissViewControllerAnimated(true, completion: nil)
}
In the Team table view controller, implement the didAddTeam() method:
func didAddTeam() {
let request = NSFetchRequest(entityName: "Teams")
do{
teamData = try managedObjectContext.executeFetchRequest(request) as! [Teams]
} catch let error as NSError {
print("\(error), \(error.userInfo)")
}
self.tableView.reloadData()
}
Ensure that the Team table view controller conforms to the protocol
class GroupTable: UITableViewController, NSFetchedResultsControllerDelegate, AddTeamsDelegateProtocol {
Before segueing to (or presenting) the Add Teams popover (I couldn't see how this is done in your code in the other question), set the Add Teams controller's delegate:
addTeamsVC.delegate = self