UIImagePickerController not picking the image - ios

I am trying to make a TableView App that allows adding images and names to it using the ImagePickerController and changing its name via an AlertController TextField
The problem is the ImagePicker is not picking up the Image and no error is shown on the debugging console.
When I click the Image to pick it, nothing happens.
class ViewController: UITableViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
var photos = [Photo]()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
title = "Snapshots"
navigationController?.navigationBar.prefersLargeTitles = true
navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .add, target: self, action: #selector(addPhoto))
let defaults = UserDefaults.standard
if let savedPhotos = defaults.object(forKey: "photos") as? Data {
let jsonDecoder = JSONDecoder()
do {
photos = try jsonDecoder.decode([Photo].self, from: savedPhotos)
} catch {
print("Failed to load photos.")
}
}
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return photos.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "Photo", for: indexPath) as? PhotoCell else {
fatalError("Could not load the Photo Cell")
}
let photo = photos[indexPath.row]
cell.textLabel?.text = photo.name
let path = getDocumentsDirectory().appendingPathComponent(photo.image)
cell.imageView?.image = UIImage(contentsOfFile: path.path)
return cell
}
#objc func addPhoto() {
let picker = UIImagePickerController()
picker.isEditing = true
picker.delegate = self
present(picker, animated: true)
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
guard let image = info[.editedImage] as? UIImage else { return }
let imageName = UUID().uuidString
let imagePath = getDocumentsDirectory().appendingPathComponent(imageName)
if let jpegData = image.jpegData(compressionQuality: 0.8) {
try? jpegData.write(to: imagePath)
}
let photo = Photo(name: "New Image", image: imageName)
photos.append(photo)
save()
tableView.reloadData()
dismiss(animated: true)
}
func getDocumentsDirectory() -> URL {
let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
return paths[0]
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let photo = photos[indexPath.row]
let ac = UIAlertController(title: "What you want to do with Image?", message: nil, preferredStyle: .alert)
ac.addTextField()
ac.addAction(UIAlertAction(title: "Rename", style: .default) {
[weak self, weak ac] _ in
guard let newName = ac?.textFields?[0].text else { return }
photo.name = newName
self?.save()
self?.tableView.reloadData()
})
ac.addAction(UIAlertAction(title: "Delete", style: .destructive) {
[weak self] _ in
self?.photos.remove(at: indexPath.row)
self?.tableView.reloadData()
})
ac.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil))
ac.addAction(UIAlertAction(title: "Open", style: .default) {
[weak self] _ in
if let vc = self?.storyboard?.instantiateViewController(withIdentifier: "PhotoView") as? DetailViewController {
vc.selectedImage = photo.name
self?.navigationController?.pushViewController(vc, animated: true)
}
})
present(ac, animated: true)
}
func save() {
let jsonEncoder = JSONEncoder()
if let savedData = try? jsonEncoder.encode(photos) {
let defaults = UserDefaults.standard
defaults.set(savedData, forKey: "photos")
} else {
print("Failed to save photos.")
}
}
}

I actually took your code and condensed it down quite a bit to test locally and found one possible issue here:
guard let image = info[.editedImage] as? UIImage else { return }
If your user has not edited a photo, then the return kicks in and nothing further happens.
However, if I change it to .originalImage like this, then simply selecting an image will allow it to proceed:
guard let image = info[.originalImage] as? UIImage else { return }
With that said, you may want to consider a switch statement, or some if/else to facilitate different types of images based on whatever suits your app.

Have you added Photo Library Usage permission in info.plist file? If you havn't then add "Privacy - Photo Library Usage Description " in info.plist.

Related

Collection view not showing data received from api.themoviedb.org

I am trying to create a collection view that displays a list of tv shows. When I run the code, it shows the error message “Please check the Internet connection.” I’ve narrowed down the problem to the Network Manager; apparently, there something wrong with data received from the server. I’m not sure why; I checked the header parameters in the baseurl variable to see if there’ something wrong with the but no issue. Can someone please assist? Project link below. (edited) https://github.com/lexypaul13/Trending-Tv-Shows
class NetworkManger {
static let shared = NetworkManger()
let baseURL = "https://api.themoviedb.org/3/trending/tv/week?api_key=352b794e6bc3be2fe8b0b6b3d7221ac1"
let cache = NSCache<NSString, UIImage>()
private init (){}
func getShows(page: Int, completed:#escaping(Result<[Shows],ErroMessage>)->Void){
let endpoint = baseURL + "&language=en-US&page=\(page)"
guard let url = URL(string: endpoint) else{
completed(.failure(.invalidTvName))
return
}
let task = URLSession.shared.dataTask(with: url){ data, response, error in
if let _ = error {
completed(.failure(.unableToComplete))
return
}
guard let response = response as? HTTPURLResponse, response.statusCode==200 else {
completed(.failure(.invalidResponse))
return
}
guard let data = data else{
completed(.failure(.invalidData))
return
}
do{
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
let show = try decoder.decode([Shows].self, from: data)
completed(.success(show))
} catch{
completed(.failure(.invalidData))
}
class WeeklyViewController: UIViewController, UISearchBarDelegate, UISearchResultsUpdating {
override func viewDidLoad() {
super.viewDidLoad()
configureViewcontroller()
configureCollectionView()
getTvshows(page:page)
}
func configureViewcontroller(){
view.backgroundColor = .systemGray
}
func configureCollectionView(){
collectionView = UICollectionView(frame: view.bounds, collectionViewLayout: createThreeColumnFlowLayout())
view.addSubview(collectionView)
collectionView.delegate = self
collectionView.backgroundColor = .systemGray
collectionView.register(TvCellCollectionViewCell.self, forCellWithReuseIdentifier: TvCellCollectionViewCell.reuseID)
}
func configureDataSource(){
dataSource = UICollectionViewDiffableDataSource<Section, Shows>(collectionView: collectionView, cellProvider: {
(collectionView, indexPath, shows) -> UICollectionViewCell? in
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: TvCellCollectionViewCell.reuseID, for: indexPath) as! TvCellCollectionViewCell
cell.setCell(shows: shows)
return cell
})
}
func configureSearch(){
let searchController = UISearchController()
searchController.searchResultsUpdater = self
searchController.searchBar.delegate = self
searchController.searchBar.placeholder = "Seach Tv Shows"
searchController.obscuresBackgroundDuringPresentation = false
navigationItem.searchController = searchController
}
func getTvshows(page:Int){
showLoadingView()
NetworkManger.shared.getShows(page: page) { [weak self] result in
guard let self = self else { return }
self.dismissLoadingView()
switch result{
case .success(let shows):
self.updateUI(shows)
case .failure(let error):
DispatchQueue.main.async {
let alert = UIAlertController(title: "Check Internet Connection", message: error.rawValue,preferredStyle: UIAlertController.Style.alert)
alert.addAction(UIAlertAction(title: "OK", style: UIAlertAction.Style.default, handler: nil))
self.present(alert, animated: true, completion: nil)
}
}
}
}
func updateUI(_ shows:[Shows]){
self.shows.append(contentsOf: shows)
if self.shows.isEmpty{
let alert = UIAlertController(title: "Tv Show Doesnt Exist", message: nil,preferredStyle: UIAlertController.Style.alert)
alert.addAction(UIAlertAction(title: "OK", style: UIAlertAction.Style.default, handler: nil))
self.present(alert, animated: true, completion: nil)
}
self.updateData(shows: self.shows)
}
func updateData(shows:[Shows]){ //shows follwers
var snapshot = NSDiffableDataSourceSnapshot<Section,Shows>()
snapshot.appendSections([.main])
snapshot.appendItems(shows)
DispatchQueue.main.async {
self.dataSource.apply(snapshot,animatingDifferences: true)
}
}
}
extension WeeklyViewController:UICollectionViewDelegate{
func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
let offsetY = scrollView.contentOffset.y
let contentHeight = scrollView.contentSize.height
let height = scrollView.frame.size.height
if offsetY > contentHeight-height{ ///check to see if bottom of screen is reached
page += 1 ///increments ppage number when screen reaches
getTvshows(page: page)
}
}
}
Please change model
class Welcome: Codable {
let page: Int?
let results: [Shows]?
init(page: Int?, results: [Shows]?) {
self.page = page
self.results = results
}
}
// MARK: - Result
struct Shows: Codable,Hashable {
let posterPath, originalName: String?
enum CodingKeys: String, CodingKey {
case posterPath = "poster_path"
case originalName = "original_name"
}
init(posterPath: String?, originalName: String?) {
self.posterPath = posterPath
self.originalName = originalName
}
}
than change decode
do{
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
let show = try decoder.decode(Welcome.self, from: data)
completed(.success(show.results ?? []))
} catch{
completed(.failure(.invalidData))
}
//Change in cell
func setCell(shows:Shows){
tvImage.downloadAvatarImage(shows.posterPath!)
tvName.text = shows.originalName
}
After Run app successfully your error "Please check the Internet connection" hase gone but new error appery 'Fatal: supplied item identifiers are not unique

Is there a way to force the text in a TextField in Swift to be non-optional?

I am making an app that takes inputs from textFields in an alertController, then stored into CoreData and eventually be displayed in a tableView.
The plan for now is to create a string to create a CSV by combining the textField.text together, something like
let string CSV = textField.text + " ," + textField2.text + " ," + ...
However, when I tried to do this, they say that I cannot force unwrap an optional value.
I tried to put a "!" in textField.text, etc but they are all still seen as optional values. The textFields, when employed in the app must always be filled, but I could not find a way to provide an error that forces the user of the app to fill in a value.
Here is my ViewController:
import UIKit
import CoreData
class ViewController: UITableViewController {
var alarmItems: [NSManagedObject] = []
let cellId = "cellId"
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
navigationController?.navigationBar.barTintColor = UIColor(red: 21/255, green: 101/255, blue: 192/255, alpha: 1)
navigationController?.navigationBar.tintColor = .white
navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Add Alarm", style: .plain, target: self, action: #selector(addAlarmItem))
tableView.register(UITableViewCell.self, forCellReuseIdentifier: cellId)
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return }
let managedContext = appDelegate.persistentContainer.viewContext
let fetchRequest = NSFetchRequest<NSManagedObject>(entityName: "AlarmItems")
do {
alarmItems = try managedContext.fetch(fetchRequest)
} catch let err as NSError {
print("Failed to fetch items", err)
}
}
//now to figure out how to add multiple text fields
#objc func addAlarmItem(_ sender: AnyObject) {
print("this works")
let alertController = UIAlertController(title: "Add New Item", message: "Please fill in the blanks", preferredStyle: .alert)
let saveAction = UIAlertAction(title: "Save", style: .default) { [unowned self] action in
guard let textField = alertController.textFields!.first, let itemToAdd = textField.text else { return }
//guard let textField2 = alertController.textFields?.first, let itemToAdd2 = textField2.text else { return } //tried this to get the input of a second textField
/*
one thing I tried:
let textField1 = alertController.textFields[0]
let textField1String = textField1.text!
let textField2 = alertController.textFields![1]
let textField2String = textField2.text
let itemToAdd = textField1String + textField2String
*/
//var textField1String = textField1.text
//let textField2 = alertController.textFields![1]
//var textField2String = textField2.text
self.save(itemToAdd)
//self.save(itemToAdd2)
self.tableView.reloadData()
}
let cancelAction = UIAlertAction(title: "Cancel", style: .destructive, handler: nil)
//alertController.addTextField(configurationHandler: nil)
alertController.addTextField { (textField) in
textField.placeholder = "First"
}
alertController.addTextField { (textField) in
textField.placeholder = "Second"
}
//let combinedString = UITextField[0] + " ," + UITextField[1]
alertController.addAction(saveAction)
alertController.addAction(cancelAction)
present(alertController, animated: true, completion: nil)
}
func save(_ itemName: String) {
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return }
let managedContext = appDelegate.persistentContainer.viewContext
let entity = NSEntityDescription.entity(forEntityName: "AlarmItems", in: managedContext)!
let item = NSManagedObject(entity: entity, insertInto: managedContext)
item.setValue(itemName, forKey: "alarmAttributes")
do {
try managedContext.save()
alarmItems.append(item)
} catch let err as NSError {
print("Failed to save an item", err)
}
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int{
return alarmItems.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: cellId, for: indexPath)
let alarmItem = alarmItems[indexPath.row]
cell.textLabel?.text = alarmItem.value(forKeyPath: "alarmAttributes") as? String
return cell
}
}
You can use compactMap to easily convert an array of optionals into an array of non-optionals.
let myStrings: [String] = alert.textfields!.compactMap { $0.text }
let myText = myStrings.joined(separator: ", ")
To learn more about compactMap, head over to https://developer.apple.com/documentation/swift/sequence/2950916-compactmap
One of the ways you can do the error validation is that when save is pressed, do empty checks on the textfields values in a separate function and show an error alert box.
I don't think you can show some kind of error inside your initial textfield alert controller. You have to validated the text separately and then show a separate alert error alert box.
You can use String extension to unwrap text this way:
extension Optional where Wrapped == String{
func safe() -> String{
if self == nil{
return ""
}else{
return self.unsafelyUnwrapped
}
}
Hope this helps.

TableView not updating after button action

I'm relatively new to swift, and I'm trying to have a view that when it loads it will display some info on my tableView, then in the same view I have a textfield and a button
I want that the button performes an action that fetchs data from my server and updates my tableView
The problem is that the table is not being updated. How can I get the table to be updated?
CODE:
override func viewDidLoad() {
super.viewDidLoad()
tableView.rowHeight = 80;
tableView.tableFooterView = UIView();
self.url = URL(string: "http://xxxxxxxx.com/xxxxx/api/produtos/listagem/favoritos/\(userID)");
downloadJson(_url: url!);
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return produtos.count;
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "ProdutoCell1") as? ProdutoCell else{
return UITableViewCell()
}
cell.lbl_nome_json.text = produtos[indexPath.row].nome;
cell.lbl_categoria_json.text = produtos[indexPath.row].categoria;
cell.lbl_loja_json.text = produtos[indexPath.row].loja
//= produtos[indexPath.row].categoria;
// cell.lbl_loja.text = produtos[indexPath.row].loja;
if let imageURL = URL(string: "http://xxxxxxxxxx.com/myslim/api/"+produtos[indexPath.row].url){
DispatchQueue.global().async {
let data = try? Data(contentsOf: imageURL);
if let data = data{
let image = UIImage(data: data);
DispatchQueue.main.async {
cell.imgView_json.image = image;
}
}
}
}
return cell;
}
ACTION:
#IBAction func btn_search(_ sender: Any) {
if self.txt_search.text == "" {
let alert = UIAlertController(title:"Alert",message: "Insira algo para pesquisar",preferredStyle: UIAlertController.Style.alert)
alert.addAction(UIAlertAction(title: "Click", style: UIAlertAction.Style.default, handler: nil))
self.present(alert,animated: true,completion: nil)
}else{
var pesquisa = String();
pesquisa = self.txt_search.text!;
let url2 = URL(string: "http://xxxxxxxx.com/xxxxxx/api/produtos/listagem/favoritos/\(pesquisa)/\(self.userID)");
downloadJson(_url: url2!);
}
func downloadJson(_url: URL){
guard let downloadURL = url else {return}
URLSession.shared.dataTask(with: downloadURL){data, urlResponse, error in
guard let data = data, error == nil, urlResponse != nil else{
print("algo está mal");
return;
}
do{
let decoder = JSONDecoder();
let downloadedProdutos = try decoder.decode([ProdutoModel].self, from: data);
self.produtos = downloadedProdutos
print(downloadedProdutos);
DispatchQueue.main.async {
self.tableView.reloadData();
print("reload");
}
}catch{
print("algo mal depois do download")
}
}.resume();
}
EDIT
I added some print to see how many object were being returned on my downloadedProdutos variable, on the downloadJson() function.
And at the viewdidload I get 2 object, its normal because I only have 2 Products, but when the action is done I still get 2 object although I should get only 1 object
You need to set tableView.dataSource = self in viewWillAppear and it looks you missed func numberOfSections() -> Int method.
Add UITableViewDataSource like this
class YourViewController: UIViewController, UITableViewDataSource and it will recommend you required methods

App crash when trying to delete cell

I'm having a problem regarding a feature where You can delete a cell and so delete and event using an Alamofire JSON request.
When I swipe the cell and click delete, the app crashes, but the event get deleted successfully and with no errors, in facts on Laravel side I get the event deleted.
I tried everything, but I really can't figure out how to fix the crash.
Can someone help me please?
here is my .Swift code:
import UIKit
import Alamofire
class EventViewController: UITableViewController {
#objc var transition = ElasticTransition()
#objc let lgr = UIScreenEdgePanGestureRecognizer()
#objc let rgr = UIScreenEdgePanGestureRecognizer()
let rc = UIRefreshControl()
#IBOutlet weak var myTableView: UITableView!
var myTableViewDataSource = [NewInfo]()
let url = URL(string: "http://ns7records.com/staffapp/api/events/index")
override func viewDidLoad() {
super.viewDidLoad()
loadList()
// Add Refresh Control to Table View
if #available(iOS 10.0, *) {
tableView.refreshControl = rc
} else {
tableView.addSubview(rc)
}
// Configure Refresh Control
rc.addTarget(self, action: #selector(refreshTableData(_:)), for: .valueChanged)
let attributesRefresh = [kCTForegroundColorAttributeName: UIColor.white]
rc.attributedTitle = NSAttributedString(string: "Caricamento ...", attributes: attributesRefresh as [NSAttributedStringKey : Any])
DispatchQueue.main.async {
}
// MENU Core
// customization
transition.sticky = true
transition.showShadow = true
transition.panThreshold = 0.3
transition.transformType = .translateMid
// menu// gesture recognizer
lgr.addTarget(self, action: #selector(MyProfileViewController.handlePan(_:)))
rgr.addTarget(self, action: #selector(MyProfileViewController.handleRightPan(_:)))
lgr.edges = .left
rgr.edges = .right
view.addGestureRecognizer(lgr)
view.addGestureRecognizer(rgr)
}
#objc private func refreshTableData(_ sender: Any) {
// Fetch Table Data
//myTableViewDataSource.removeAll()
tableView.reloadData()
loadList()
}
func loadList(){
var myNews = NewInfo()
// URLSession.shared.dataTask(with: url!, completionHandler: { (data, response, error) in
//
// })
let task = URLSession.shared.dataTask(with:url!) {
(data, response, error) in
if error != nil
{
print("ERROR HERE..")
}else
{
do
{
if let content = data
{
let myJson = try JSONSerialization.jsonObject(with: content, options: .mutableContainers)
//print(myJson)
if let jsonData = myJson as? [String : Any]
{
if let myResults = jsonData["data"] as? [[String : Any]]
{
//dump(myResults)
for value in myResults
{
if let myTitle = value["title"] as? String
{
//print(myTitle)
myNews.displayTitle = myTitle
}
if let myLocation = value["local"] as? String
{
myNews.location = myLocation
}
if let myDate = value["date"] as? String
{
myNews.date = myDate
}
if let myDescription = value["description"] as? String
{
myNews.description = myDescription
}
if let myCost = value["cost"] as? String
{
myNews.cost = myCost
}
if let myNumMembers = value["num_members"] as? String
{
myNews.num_members = myNumMembers
}
if let myNumMembers_conf = value["num_members_confirmed"] as? String
{
myNews.num_members_confirmed = myNumMembers_conf
}
if let myStartEvent = value["time_start"] as? String
{
myNews.startEvent = myStartEvent
}
if let myEndEvent = value["time_end"] as? String
{
myNews.endEvent = myEndEvent
}
if let myId = value["id"] as? Int
{
myNews.idEvent = myId
}
//x img
// if let myMultimedia = value["data"] as? [String : Any]
// {
if let mySrc = value["event_photo"] as? String
{
myNews.event_photo = mySrc
print(mySrc)
}
self.myTableViewDataSource.append(myNews)
}//end loop
dump(self.myTableViewDataSource)
DispatchQueue.main.async
{
self.tableView.reloadData()
self.rc.endRefreshing()
}
}
}
}
}
catch{
}
}
}
task.resume()
}
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath)->CGFloat {
return 150
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return myTableViewDataSource.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let myCell = tableView.dequeueReusableCell(withIdentifier: "reuseCell", for: indexPath)
let myImageView = myCell.viewWithTag(11) as! UIImageView
let myTitleLabel = myCell.viewWithTag(12) as! UILabel
let myLocation = myCell.viewWithTag(13) as! UILabel
let DateLabelCell = myCell.viewWithTag(14) as! UILabel
let numMembLabel = myCell.viewWithTag(15) as! UILabel
let numMembConfLabel = myCell.viewWithTag(16) as! UILabel
myTitleLabel.text = myTableViewDataSource[indexPath.row].displayTitle
myLocation.text = myTableViewDataSource[indexPath.row].location
DateLabelCell.text = myTableViewDataSource[indexPath.row].date
numMembLabel.text = myTableViewDataSource[indexPath.row].num_members
numMembConfLabel.text = myTableViewDataSource[indexPath.row].num_members_confirmed
if let imageURLString = myTableViewDataSource[indexPath.row].event_photo,
let imageURL = URL(string: AppConfig.public_server + imageURLString) {
myImageView.af_setImage(withURL: imageURL)
}
return myCell
}
//per passare da un viewcontroller a detailviewcontroller
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let destination = segue.destination as? EventDetailViewController {
destination.model = myTableViewDataSource[(tableView.indexPathForSelectedRow?.row)!]
// Effetto onda
let vc = segue.destination
vc.transitioningDelegate = transition
vc.modalPresentationStyle = .custom
}
//menu
if let vc = segue.destination as? MenuViewController{
vc.transitioningDelegate = transition
vc.modalPresentationStyle = .custom
//endmenu
}
}
//menu slide
#objc func handlePan(_ pan:UIPanGestureRecognizer){
if pan.state == .began{
transition.edge = .left
transition.startInteractiveTransition(self, segueIdentifier: "menu", gestureRecognizer: pan)
}else{
_ = transition.updateInteractiveTransition(gestureRecognizer: pan)
}
}
//endmenuslide
////ximg
func loadImage(url: String, to imageView: UIImageView)
{
let url = URL(string: url )
URLSession.shared.dataTask(with: url!) { (data, response, error) in
guard let data = data else
{
return
}
DispatchQueue.main.async
{
imageView.image = UIImage(data: data)
}
}.resume()
}
/// star to: (x eliminare row e x muove row)
override func tableView(_ tableView: UITableView, moveRowAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) {
let movedObjTemp = myTableViewDataSource[sourceIndexPath.item]
myTableViewDataSource.remove(at: sourceIndexPath.item)
myTableViewDataSource.insert(movedObjTemp, at: destinationIndexPath.item)
}
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if (editingStyle == .delete){
// print(parameters)
let idEvent = (myTableViewDataSource[indexPath.item].idEvent)
let parameters = [
// "id": UserDefaults.standard.object(forKey: "userid")! ,
"id" : idEvent,
] as [String : Any]
let url = "http://www.ns7records.com/staffapp/public/api/deleteevent"
print(url)
Alamofire.request(url, method:.post, parameters:parameters,encoding: JSONEncoding.default).responseJSON { response in
switch response.result {
case .success:
print(response)
let JSON = response.result.value as? [String : Any]
//self.myTableView.reloadData()
let alert = UIAlertController(title: "Yeah!", message: "Evento modificato con successo!", preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.destructive, handler: nil))
self.present(alert, animated: true, completion: nil)
// let data = JSON! ["data"] as! NSDictionary
if let jsonData = JSON as? [String : Any]
{
print(jsonData)
self.myTableViewDataSource.remove(at : indexPath.item)
self.myTableView.deleteRows(at: [indexPath], with: .automatic)
let indexPath = IndexPath(item: 0, section: 0)
//self.myTableView.deleteRows(at: [indexPath], with: .fade)
//self.myTableView.reloadData()
// }
// }
//}
}
case .failure(let error):
print(error)
let alert = UIAlertController(title: "Aia", message: "Non puoi cancellare questo evento!", preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.destructive, handler: nil))
self.present(alert, animated: true, completion: nil)
}
}
}
}
#IBAction func EditButtonTableView(_ sender: UIBarButtonItem) {
self.myTableView.isEditing = !self.myTableView.isEditing
sender.title = (self.myTableView.isEditing) ? "Done" : "Edit"
}
/// end to: (x eliminare row e x muove row)
}
// MARK: -
// MARK: UITableView Delegate
extension ViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
}
}

Downloading too many items in table view from Firebase

When I post from a different device, the tableview I on the current device keeps the items that were previously there and then downloads the items again including the new post, can anyone help me to make it so that it only displays one of each item?
I think the issue is in my downloadFromFirebase().
Here is my code:
class DisplayVC: UIViewController, UITableViewDelegate, UITableViewDataSource, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
#IBOutlet weak var captionField: RoundTextField!
#IBOutlet weak var tableView: UITableView!
#IBOutlet weak var myMainImg: roundImage!
var imageSelected = false
static var imageCache: NSCache<NSString,UIImage> = NSCache()
var posts = [Post]() // array for the posts
var imagePicker: UIImagePickerController!
#IBAction func addImageTapped(_ sender: Any) {
present(imagePicker, animated: true, completion: nil)
}
#IBAction func logoutPressed(_ sender: Any) {
//remove keychain and sign out of firebase
let keychainResult = KeychainWrapper.standard.removeObject(forKey: KEY_UID)
print("AA: ID removed from keychain: \(keychainResult)")
try! FIRAuth.auth()?.signOut()
performSegue(withIdentifier: "goToSignIn", sender: nil)
}
#IBAction func postBtnTapped(_ sender: Any) {
//does this exist? or is it null? if condition is not true then it is executed
guard let caption = captionField.text, caption != "" else {
let alert = UIAlertController(title: "Bad Caption", message: "Caption must be entered", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .cancel, handler: nil))
self.present(alert, animated: true, completion: nil)
return
}
guard let img = myMainImg.image, imageSelected == true else {
let alert = UIAlertController(title: "Bad Image", message: "Image must be choosen", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .cancel, handler: nil))
self.present(alert, animated: true, completion: nil)
return
}
if let imgData = UIImageJPEGRepresentation(img, 0.2){
let imgUid = NSUUID().uuidString
let metadata = FIRStorageMetadata()
metadata.contentType = "image/jpeg"
DataService.ds.REF_POST_IMAGES.child(imgUid).put(imgData, metadata: metadata, completion: { (metadata, error) in
if error != nil{
print("unable to upload image to Firebase storage")
}else{
print("GREAT SUCESS FOR IMAGE ON STORAGE")
let downloadURL = metadata?.downloadURL()?.absoluteString
if let url = downloadURL{
self.postToFireBase(imageURL: url)
}
}
})
}
}
func postToFireBase(imageURL: String){
let post: Dictionary<String, AnyObject> = ["caption": captionField.text as AnyObject,"imageURL":imageURL as AnyObject, "likes":0 as AnyObject,"userName": userName as AnyObject]
let firebasePost = DataService.ds.REF_POSTS.childByAutoId()
firebasePost.setValue(post)
captionField.text = ""
imageSelected = false
myMainImg.image = UIImage(named: "add-image")
posts.removeAll()
tableView.reloadData()
}
//cannot use the view did load for the guard method!
override func viewDidAppear(_ animated: Bool) {
}
override func viewDidLoad() {
super.viewDidLoad()
self.hideKeyboardWhenTappedAround()
tableView.delegate = self
tableView.dataSource = self
imagePicker = UIImagePickerController()
imagePicker.allowsEditing = true
imagePicker.delegate = self
posts.removeAll()
self.downloadFromFirebase()
}
func downloadFromFirebase(){
//"POSTS" Listener, initialize listener and it will work constantly
DataService.ds.REF_POSTS.observe(.value, with: { (snapshot) in
if let snapshot = snapshot.children.allObjects as? [FIRDataSnapshot]{
for snap in snapshot{
print("SNAP: \(snap)") // make free objects by parsing the JSON
if let postDict = snap.value as? Dictionary<String, AnyObject>{
let key = snap.key
let post = Post(postKey: key, postData: postDict)
self.posts.append(post) //stick the post in the posts Array
}
}
}
self.tableView.reloadData()
})
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return posts.count //number of total posts
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let post = posts[indexPath.row]
if let cell = tableView.dequeueReusableCell(withIdentifier: "PostCell") as? PostCell{
if let img = DisplayVC.imageCache.object(forKey: post.imageURL as NSString){
cell.configureCell(post: post, image: img)
return cell
}else{
cell.configureCell(post: post, image: nil)
return cell
}
}else{
return PostCell()
}
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
if let image = info[UIImagePickerControllerEditedImage] as? UIImage{
myMainImg.image = image
}else{
print("AA: Valid image wasn't selected")
}
imagePicker.dismiss(animated: true, completion: nil)
imageSelected = true
}
func hideKeyboardWhenTappedAround() {
let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(dismissKeyboard))
view.addGestureRecognizer(tap)
}
func dismissKeyboard() {
view.endEditing(true)
}
}
Try this:
Include after
let post = Post(postKey: key, postData: postDict)
the following:
if !posts.contains(post) {
self.post.append(post)
}
Let me know if it works!

Resources