NSUnknownKeyException on a model variable - firebase

'[ setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key text.'
I'm getting an NSUnknownKeyException at the text variable of my Message model when I attempt to access it in my second tableView() function (at the end of this code). I am still pretty new to swift and don't know how to resolve this. Through my google searches, I found that it could be something to do with storyboards, but I've actually opted to create my UI programmatically so I'm not sure how that could be causing this.
import UIKit
import Firebase
class ViewController: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
navigationItem.leftBarButtonItem = UIBarButtonItem(title: "chat", style: .plain, target: self, action: #selector(handleChat))
observeMessages()
}
#objc func handleChat() {
let chatController = ChatController(collectionViewLayout: UICollectionViewLayout())
navigationController?.pushViewController(chatController, animated: true)
//present(chatController, animated: true, completion: nil)
}
var messages = [Message]()
func observeMessages() {
let ref = Database.database().reference().child("FalconsVPackers").child("messages")
ref.observe(.childAdded, with: { (snapshot) in
if let dictionary = snapshot.value as? [String: AnyObject] {
let message = Message()
message.setValuesForKeys(dictionary)
self.messages.append(message)
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
}, withCancel: nil)
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return messages.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell(style: .subtitle, reuseIdentifier: "called")
let message = messages[indexPath.row]
cell.textLabel?.text = message.text
return cell
}
}
My Message model is very simple...
import UIKit
class Message: NSObject {
var text: String?
}
Any help or advice would be greatly appreciated!

You are using NSKeyValueCoding to populate Message object so you must add #objc to text property.
message.setValuesForKeys(dictionary) // NSKeyValueCoding
.
class Message : NSObject {
#objc var text: String?
}
If you just want to populate your message object, better just use properties directly instead of setValuesForKeys. And in this case you don't have to use #objc too.
let message = Message()
message.text = dictionary["text"]

Related

Wrong cell data if fast scroll CollectionView swift 5

Can anybody help me with CollectionView? I parce JSON from some API, and get String values - title and image adress, then assign that Data to custom collectionViewCell in method cellForItem(). BUT than i fast scroll the collection view some cell can be with wrong images, or duplicate images. I also override method prepareForReuse in my custom cell class and set cell.image = UIImage() , but it didnt work. Please help me to understand what wrong)
Sorry for my poor English...
collectionViewController
'''
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Result",
for: indexPath) as! ResultCell
let film = results[indexPath.item]
cell.name.text = film.title
cell.imageView.layer.borderColor = UIColor(white: 0, alpha: 0.3).cgColor
cell.imageView.layer.borderWidth = 2
cell.imageView.layer.cornerRadius = 10
cell.layer.cornerRadius = 10
// setUp image for cell
cell.imageView.image = nil
contentManager.getImage(film.poster.image, cell.imageView)
return cell
}
'''
method get image
'''
func getImage(_ url_str:String, _ imageView:UIImageView) {
let url:URL = URL(string: url_str)!
let session = URLSession.shared
let task = session.dataTask(with: url, completionHandler: {(data, response, error) in
if data != nil {
let image = UIImage(data: data!)
if(image != nil) {
DispatchQueue.main.async(execute: {
imageView.image = image
imageView.alpha = 0
UIView.animate(withDuration: 1, animations: {
imageView.alpha = 1.0
})
})
}
}
})
task.resume()
}
'''
Cell custom class
'''
class ResultCell: UICollectionViewCell {
#IBOutlet var imageView: UIImageView!
#IBOutlet var name : UILabel!
override func prepareForReuse() {
self.imageView.image = UIImage()
self.name.text = nil
super.prepareForReuse()
}
}
'''

Missing argument for parameter 'delegate' in call // JSON file corrupted in SWIFT?

I am following an e-class tutorial for SWIFT // XCODE 11.4 and I have to fetch data from Open Weather API and display it on the interface where people can type in a city and the view controller will display temperature, cloud icon, and description.
Clima App Tutorial
I am using the MVC pattern design and the delegate design to accomplish this tutorial. My swift files are as followed:
Swift Files in MVC Design Pattern
Here are the codes in each of the important files:
I. Model folder
WeatherManager.swift
protocol WeatherManagerDelegate {
func didUpdateWeather(weather: WeatherModel)
}
struct WeatherManager {
let weatherURL = "https://api.openweathermap.org/d.5/weather?appid=c8b50079338280b47a65dd6082551e3b&units=imperial"
let delegate: WeatherManagerDelegate?
func fetchWeather(cityName: String) {
let urlString = "\(weatherURL)&q=\(cityName)"
performRequest(urlString: urlString)
}
func performRequest(urlString: String) {
//create a URL
if let url = URL(string: urlString) {
//create a URLSession
let session = URLSession(configuration: .default)
//give session a task
let task = session.dataTask(with: url) { (data, response, error) in
if error != nil {
print(error!)
return //exit out of the func if there is an error
}
if let safeData = data {
if let weather = self.parseJSON(weatherData: safeData) {
self.delegate?.didUpdateWeather(weather: weather)
}
}
}
//start the tast
task.resume()
}
}
func parseJSON (weatherData: Data) -> WeatherModel? {
let decoder = JSONDecoder()
do {
let decodedData = try decoder.decode(WeatherData.self, from: weatherData)
let id = decodedData.weather[0].id
let temp = decodedData.main.temp
let name = decodedData.name
let weather = WeatherModel(conditionId: id, cityName: name, temperature: temp)
return weather
} catch {
print(error)
return nil
}
}
}
WeatherData.swift
struct WeatherData: Codable {
let name: String
let main: Main
let weather: [Weather]
}
struct Main: Codable {
let temp: Double
}
struct Weather: Codable {
let id: Int
}
WeatherModel.swift
struct WeatherModel {
let conditionId: Int
let cityName: String
let temperature: Double
var temperatureString: String {
return String(format: "%.1f", temperature)
}
var conditionName: String {
switch conditionId {
case 200...232:
return "cloud.bolt"
case 300...321:
return "cloud.drizzle"
case 500...531:
return "cloud.rain"
case 600...622:
return "cloud.snow"
case 701...781:
return "cloud.fog"
case 800:
return "sun.max"
case 801...804:
return "cloud.bolt"
default:
return "cloud"
}
}
}
II. Controller
WeatherViewController.swift (place where the error is)
class WeatherViewController: UIViewController, UITextFieldDelegate, WeatherManagerDelegate {
#IBOutlet weak var conditionImageView: UIImageView!
#IBOutlet weak var temperatureLabel: UILabel!
#IBOutlet weak var cityLabel: UILabel!
#IBOutlet weak var searchTextField: UITextField!
var weatherManager = WeatherManager()
override func viewDidLoad() {
super.viewDidLoad()
weatherManager.delegate = self
searchTextField.delegate = self
}
#IBAction func searchPressed(_ sender: UIButton) {
searchTextField.endEditing(true)
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
searchTextField.endEditing(true)
print(searchTextField.text!)
return true
}
func textFieldShouldEndEditing(_ textField: UITextField) -> Bool {
if textField.text != "" {
return true
} else {
textField.placeholder = "Type something..."
return false
}
}
func textFieldDidEndEditing(_ textField: UITextField) {
if let city = searchTextField.text {
weatherManager.fetchWeather(cityname: city)
}
searchTextField.text = ""
}
func didUpdateWeather(weather: WeatherModel) {
print(weather.temperature)
}
}
Here is the error message: Missing argument for parameter 'delegate' in call
Error message in WeatherViewControl.swift
And when I hit the run button, I also got this error in the debug console:
dataCorrupted(Swift.DecodingError.Context(codingPath: [], debugDescription: "The given data was not valid JSON.", underlyingError: Optional(Error Domain=NSCocoaErrorDomain Code=3840 "Invalid value around character 0." UserInfo={NSDebugDescription=Invalid value around character 0.})))
Error in the debug console
What should I do to get rid of these errors?
You need to change the keyword "let" to "var" in let delegate: WeatherManagerDelegate? in struct WeatherManager.
Missing argument for parameter 'delegate' in call
When a struct is create a value for each property is required.
If each property is specified with a default value and there is no user-defined initializer then Swift will create a default initializer for the struct.
If there is at least one property without a default value and there is no user-defined initializer then Swift will create a memberwise initializer which has one parameter for each property without a default value.
For example your type:
struct WeatherModel {
let conditionId: Int
let cityName: String
let temperature: Double
...
has three properties without default values. If you start typing:
let myWeatherModel = WeatherModel(
and then take the completion offered you will get (something like):
let wm = WeatherModel(conditionId: <Int>, cityName: <String>, temperature: <Double>)
The completion is showing the memberwise initializer.
Your type which produces the error is:
struct WeatherManager {
let weatherURL = "https://api.openweathermap.org/d.5/weather?appid=c8b50079338280b47a65dd6082551e3b&units=imperial"
let delegate: WeatherManagerDelegate?
which has two properties only one of which has a default value, and it has no initializers, so Swift will create a member wise initialiser automatically.
There is nothing wrong so far.
The line producing the error is:
var weatherManager = WeatherManager()
Here you are attempting to create a WeatherManager without invoking the member wise initalizer, so Swift gives you the error message.
If you click on the error message itself you will see a Fix is offered, click that and Swift will change your code to:
var weatherManager = WeatherManager(delegate: <WeatherManagerDelegate?>)
Select the <WeatherManagerDelegate?> and type the value you wish to pass.
HTH

UICollectionView-refreshing: getting each cell more than one time

Hi I'm having the issue that after refreshing my collectionView I am getting each cell twice on
my view. And after refreshing a second time I am getting each cell 3 times. I am also getting this issue when presenting the view with a segue a second time.
How can I fix that?
Can someone help me?
Thank you in advance!!!
import UIKit
import Firebase
class FeedViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
private let refreshControl = UIRefreshControl()
#IBOutlet weak var collectionview: UICollectionView!
var posts = [Post]()
override func viewDidLoad() {
super.viewDidLoad()
collectionview.refreshControl = refreshControl
refreshControl.addTarget(self, action: #selector(refreshWeatherData(_:)), for: .valueChanged)
refreshControl.tintColor = UIColor(hex: "#F17D32")
}
#objc private func refreshData(_ sender: Any) {
fetchData()
posts.removeAll()
collectionview.reloadData()
}
private func fetchData() {
getPost()
self.refreshControl.endRefreshing()
}
func getPosts(){
let ref = Database.database().reference()
ref.child("posts").queryOrderedByKey().observeSingleEvent(of: .value, with: { (snap) in
let postsSnap = snap.value as! [String : AnyObject]
for (_,post) in postsSnap {
let posst = Post()
if let author = post["author"] as? String, late date = post["date"], let postID = post["postID"] as? String, let userID = post["userID"] as? String {
posst.date = date
posst.author = author
posst.postID = postID
posst.userID = userID
self.posts.sort(by: {$0.postdateprog > $1.postdateprog})
self.posts.append(posst)
}
self.collectionview.reloadData()
}
})
}
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
print(posts.count)
return self.posts.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "postCell", for: indexPath) as! PostCell
cell.authorlabel.text = self.posts[indexPath.row].author
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize
{
return CGSize(width: self.collectionview.frame.width / 2 - 20, height: self.collectionview.frame.width * 0.7)
}
}
Firebase functions are asynchronous and the code in the closure will be called after the synchronous code runs. In other words, you have this
fetchData()
posts.removeAll()
collectionview.reloadData()
but posts.removeAll() and collectionview.reloadData() will be called before the array is populated.
So here's a truncated overview of the order in which things should be called.
#objc private func refreshData(_ sender: Any) {
self.loadFirebaseData()
}
func loadFirebaseData() {
...
self.posts = []
for (_,post) in postsSnap {
self.posts.append(posst)
}
self.posts.sort(by: {$0.postdateprog > $1.postdateprog}
self.collectionview.reloadData()
}
Notice that I moved the sort and the reloadData outside of the for loop. There's no reason to call those functions over and over as the datasource is being populated. Do it after that task is complete.

Select a Row in a tableView after fetching CoreData to pass the result to sender View Controller

I have been struggling with this issue for a long time being a novice in Swift iOs coding. Hope that someone can point me to the right direction.
In the following code I do a Fetchrequest to a CoreData Entity with names of persons. Once I get the results I am trying to pick (tapping on the corresponding row) one name and pass it back to the ViewController that invoked this view with a prepare for segue.
But each time I click on the row of the name I want to select, I end up with a: "*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Invalid index path for use with UITableView. Index paths passed to table view must contain exactly two indices specifying the section and row. Please use the category on NSIndexPath in UITableView.h if possible.'"
It seems I am invoking my indexPath in the wrong way.
Here below my code:
.....
.....
let fetchedResults =
managedObjectContext.executeFetchRequest(fetchRequest,
error: &error) as [NSManagedObject]?
if let results = fetchedResults {
names = results
} else {
println("Could not fetch \(error), \(error!.userInfo)")
}
}
// MARK: - UITableViewDataSource
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return names.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("KidCell") as UITableViewCell
let kidName = names[indexPath.row]
cell.textLabel!.text = kidName.valueForKey("kidName") as String?
if kidName != selectedKid {
cell.accessoryType = .None
} else {
cell.accessoryType = .Checkmark
selectedIndexPath = indexPath
}
return cell
}
// MARK: - UITableViewDelegate
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
if indexPath.row != selectedIndexPath.row {
if let newCell = tableView.cellForRowAtIndexPath(indexPath) {
newCell.accessoryType = .Checkmark
}
if let oldCell = tableView.cellForRowAtIndexPath(selectedIndexPath) {
oldCell.accessoryType = .None
}
selectedIndexPath = indexPath
}
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "SelectedKid" {
let cell = sender as UITableViewCell
if let indexPath = tableView.indexPathForCell(cell) {
let kidName = names[indexPath.row]
selectedKid = kidName.valueForKey("kid") as String!
}
}
}
}
The idea is that when I tap on the name I go back with an unwind segue to the sender controller and I put the selectedName in the correct place.
Thank you fro any help!
Cristiano
your problem are with the lines below...
let cell = tableView.dequeueReusableCellWithIdentifier("KidCell") as UITableViewCell
let kidName = names[indexPath.row]
cell.textLabel!.text = kidName.valueForKey("kidName") as String?
your code should look like...
let cell = tableView.dequeueReusableCellWithIdentifier("KidCell", forIndexPath: indexPath ) as UITableViewCell
let kidName = names[indexPath.row]
cell.textLabel!.text = kidName.valueForKey(kidName)

how to capture camera with UIImagePickerController in swift?

I'm trying use UIImagePickerController in swift but isn't work...
my ViewController:
class ViewController: UIViewController {
#IBOutlet var imag : UIView = nil
#IBAction func capture(sender : UIButton) {
println("Button capture")
if UIImagePickerController.isSourceTypeAvailable(UIImagePickerControllerSourceType.Camera)
{
var imag = UIImagePickerController()
imag.delegate = self
imag.sourceType = UIImagePickerControllerSourceType.Camera;
imag.mediaTypes = kUTTypeImage
imag.allowsEditing = false
self.presentViewController(imag, animated: true, completion: nil)
}
}
}
I have errors in following line of code
imag.delegate = self
(Type'ViewControlles does confoorm to protocol 'UIImagePickerControllerDelegate')
imagePicker.mediaTypes = kUTTypeImage
(use of unresolved identifier kUTTypeImage)
I have read that kUTTypeImage cant use in swift.but don't know, i am using bad this functions. Any help?
Thanks!!
You should also import MobileCoreServices in the controller:
import MobileCoreServices
and then put the type inside square brackets like this:
image.mediaTypes = [kUTTypeImage]
Swift 2.0 and Higher
image.mediaTypes = [kUTTypeImage as String]
Swift 2.0
In Swift 2.0 (Xcode 7), you need to explicitly cast kUTTypeImage (a CFString) to String:
picker.mediaTypes = [kUTTypeImage as String]
And you still need to import Mobile Core Services for this symbol to be defined:
import MobileCoreServices
That said, the default value of mediaTypes is [kUTTypeImage] anyway, so you don't need to set it if that's what you want.
also you should add UINavigationControllerDelegate to the protocols list of the ViewController
and one of the optional delegate functions (if you a planing to get a picture)
This is the working code for your issue:
import UIKit
import MobileCoreServices
class ViewController: UIViewController, UINavigationControllerDelegate, UIImagePickerControllerDelegate {
func imagePickerController(picker: UIImagePickerController!, didFinishPickingImage image: UIImage!, editingInfo: NSDictionary!){
println("i've got an image");
}
#IBAction func capture(sender : UIButton) {
if UIImagePickerController.isSourceTypeAvailable(UIImagePickerControllerSourceType.Camera){
println("Button capture")
var imag = UIImagePickerController()
imag.delegate = self
imag.sourceType = UIImagePickerControllerSourceType.Camera;
imag.mediaTypes = [kUTTypeImage]
imag.allowsEditing = false
self.presentViewController(imag, animated: true, completion: nil)
}
}
}
From Your Piece of code its very clear that you are making mistakes at two place one is setting delegate and second is setting Media type imag.mediaTypes = kUTTypeImage
First One:If you look into the delegate definition of UIImagePickerController it requires to confirm two protocol UINavigationControllerDelegate and UIImagePickerControllerDelegate so you have to adopt these two protocols in your viewcontroller class like as
class ViewController: UIViewController,UINavigationControllerDelegate, UIImagePickerControllerDelegate
second error:If you look into the definition part of mediaTypes it clearly requires array of media types to passed so do like this
imag.mediaTypes = [kUTTypeImage]
Apart from this, I have written a very descent class for the same task
It is easy to understand and integrate.
Here you go
//Declare property
var imagePicker:ImageVideoPicker?
//Call below line of code properly, it will return an image
self.imagePicker = ImageVideoPicker(frame: self.view.frame, superVC: self) { (capturedImage) -> Void in
if let captureImage = capturedImage{
//you did it.....
}
}
You have to conform to the delegate like this
class ViewController: UIViewController, UIImagePickerControllerDelegate
Per documentation, by default the media types is set to image so you can go ahead and delete that line since you are only setting it to image.
Do not forget to implement the protocol methods which are outlined in the documentation:
documentation
Try this
import UIKit
import AVFoundation
class ViewController: UIViewController, UINavigationControllerDelegate, UIImagePickerControllerDelegate {
var imagePicker: UIImagePickerController!
#IBOutlet weak var ImageView: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func takeImage(sender: AnyObject) {
imagePicker = UIImagePickerController()
imagePicker.delegate = self
imagePicker.sourceType = .Camera
presentViewController(imagePicker, animated: true, completion: nil)
}
func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : AnyObject]) {
imagePicker.dismissViewControllerAnimated(true, completion: nil)
ImageView.image = info[UIImagePickerControllerOriginalImage] as? UIImage
}
}
swift 1.2 syntax:
func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [NSObject : AnyObject]) {
let image = info[UIImagePickerControllerOriginalImage]
}

Resources