Append Array with UserDefaults input text - arrays

I have an Array of strings which is populating a collection view and that works well. The issue is I want to append that array with Strings that is saved in user defaults from user input textfield. I am getting the UserDefault data, The issue is it is not showing up in seperate collection view cell. it is getting attached at the end of each string in the current cells. Thanks in advance, any help would be appreciated.
This is what I tried so far:
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! CollectionViewCell
let defaults = UserDefaults.standard
let value = defaults.string(forKey: "Gratitude")
print(value!)
//Array that I am trying to append with userdefault data
gratitudeArray.append(value!)
// Configure the cell
cell.cellLabel.text = gratitudeArray[indexPath.row]
return cell
}
// I am geeting user input from alert and saving in userdefaults like this:
func presentAlert() {
let alertController = UIAlertController(title: "", message: "Create your own Gratitude:", preferredStyle: .alert)
let confirmAction = UIAlertAction(title: "Save", style: .default) { (_) in
if let field = alertController.textFields?[0] {
// store data
UserDefaults.standard.set(field.text, forKey: "Gratitude")
UserDefaults.standard.synchronize()
} else {
print()
}
}
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel) { (_) in }
alertController.addTextField { (textField) in
//print(textField.text!)
//textField.placeholder = ""
}
alertController.addAction(confirmAction)
alertController.addAction(cancelAction)
self.present(alertController, animated: true, completion: nil)
}

Store String in ViewDidLoad() like below:
var strValue: String = ""
override func viewDidLoad() {
super.viewDidLoad()
let defaults = UserDefaults.standard
strValue= defaults.string(forKey: "Gratitude")
}
and display in cellForItemAt like this:
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! CollectionViewCell
cell.cellLabel.text = gratitudeArray[indexPath.row] + " " + strValue
return cell
}

If I understand your problem correctly, you want number of cells to increase by one (the value you extract out of UserDefaults). For that, you should append it some place which is outside collection view's data source methods (like viewDidLoad()) and then reload your collection view.
override func viewDidLoad() {
super.viewDidLoad()
let defaults = UserDefaults.standard
strValue = defaults.string(forKey: "Gratitude")
gratitudeArray.append(strValue)
self.collectionView.reloadData()
}

I solved the issue by creating an array in my alert controller to hold user input and then save that array to user defaults.
func presentAlert() {
let alertController = UIAlertController(title: "", message: "Create your own Gratitude:", preferredStyle: .alert)
let confirmAction = UIAlertAction(title: "Save", style: .default) { (_) in
if let field = alertController.textFields {
let textFieldArray = field as [UITextField]
let text = textFieldArray[0].text
let key = "Gratitude"
var myArray = UserDefaults.standard.array(forKey: key) ?? []
myArray.append(String(describing: text!))
UserDefaults.standard.set(myArray, forKey: key)
print(myArray)
} else {
print()
}
}
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel) { (_) in }
alertController.addTextField { (textField) in
}
alertController.addAction(confirmAction)
alertController.addAction(cancelAction)
self.present(alertController, animated: true, completion: nil)
}

Related

Search bar in Table view sends wrong data to another Table View

I have one tableview containing data that is fed from an Array with 14 entries. I have added a search bar where I can filter this data and this works fine, but when I try to send the filtered data to a second table view, the data that is sent is the count of my filtered data. So if I search something starting with G(13th and 14th place in my array), that brings back two results which is fine but when I choose the first entry off my filtered data(the two results), the data that is passed to the second Table View is the [0] entry(A) of my initial TVC and not the 13th(G12).So depending on the filtered data I always get the first few entries of my initial array.
struct ImageForManuf {
let image: UIImage
let name: String
init(nameInit: String) {
self.name = nameInit
image = UIImage(named: nameInit)!
}
}
struct SecondTable {
var secondTitle : [String]
var pic : [UIImage?]
}
//for search bar
#IBOutlet weak var searchBar: UISearchBar!
var filteredData: [ImageForManuf]!
var searching = false
var firstArray = [ImageForManuf]()
//we are adding elements on the SecondTable structure
var secondArray = [SecondTable]()
override func viewDidLoad() {
super.viewDidLoad()
//for search bar
searchBar.delegate = self
filteredData = firstArray
firstArray = [ "A","A2","B3","B4","B5","C6","C7","D8","E9","F10","F11","G12","G13","H14",
].map(ImageForManuf.init(nameInit:))
filteredData = firstArray
secondArray = [SecondTable(secondTitle: ["A1"],
pic: [UIImage(named: "A1")]),
SecondTable(secondTitle: ["B1","B2","B3"],
pic: [UIImage(named: "B1"),UIImage(named: "B2"),UIImage(named: "B3")]),
SecondTable(secondTitle: ["C1","C2"],
pic: [UIImage(named: "C1"),pic: [UIImage(named: "C2")]),
SecondTable(secondTitle: ["D1"],
pic: [UIImage(named: "D1"),
SecondTable(secondTitle: ["E1"],
pic: [UIImage(named: "E1"),
SecondTable(secondTitle: ["F1","F2"],
pic: [UIImage(named: "F1"),pic: [UIImage(named: "F2")]),
SecondTable(secondTitle: ["G1","G2"],
pic: [UIImage(named: "G1"),pic: [UIImage(named: "G2")]),
SecondTable(secondTitle: ["H1"],
pic: [UIImage(named: "H1")
]
This is my TV configuration
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
if searching
{
return filteredData.count
}else{
return firstArray.count
}
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let Cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! TableTableViewCell
Cell.textLabel?.textColor = .systemBlue
Cell.textLabel?.font = .boldSystemFont(ofSize: 20)
if searching
{
Cell.textLabel?.text = filteredData[indexPath.row].name
Cell.imageManuf?.image = filteredData[indexPath.row].image
}else{
Cell.textLabel?.text = firstArray[indexPath.row].name
Cell.imageManuf?.image = filteredData[indexPath.row].image
}
return Cell
}
//we need to create a variable that can hold the destination view controller
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if searching {
let row = self.tableView?.indexPathForSelectedRow?.row ?? 0
let destViewController = segue.destination as! SecondTableViewController var secondTableArray : SecondTable
secondTableArray = secondArray[row]
destViewController.secondTableVcArray = secondTableArray.secondTitle
destViewController.detailVcArray = thirdArray[row]
}else{
let row = self.tableView?.indexPathForSelectedRow?.row ?? 0
let destViewController = segue.destination as! SecondTableViewController
var secondTableArray : SecondTable
secondTableArray = secondArray[row]
secondTableArray created in SecondTableViewController
destViewController.secondTableVcArray = secondTableArray.secondTitle
destViewController.detailVcArray = thirdArray[row]
}
}
Lastly this is my searchBar configuration
//Mark: Search Bar config
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
filteredData = firstArray.filter({$0.name.lowercased().prefix(searchText.count) == searchText.lowercased()})
searching = true
//to reload the data
self.tableView.reloadData()
}
func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
searching = false
searchBar.text = ""
tableView.reloadData()
}
This is the second TVC:
class SecondTableViewController: UITableViewController {
//we are loading the secondArray via the secondTableVcArray
var secondTableVcArray = [String]()
//we are loading the thirdArray via the detailVcArray
var detailVcArray : [DetailView]?
override func viewDidLoad() {
super.viewDidLoad()
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return secondTableVcArray.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = self.tableView.dequeueReusableCell(withIdentifier: "secondCell", for: indexPath) as! SecondTableViewCell
cell.textLabel?.textColor = .white
cell.textLabel?.font = .boldSystemFont(ofSize: 20)
//we load the data of the secondTableVcArray to our cell
cell.textLabel?.text = secondTableVcArray[indexPath.row]
return cell
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
guard let row = self.tableView?.indexPathForSelectedRow?.row else {
print("Error: problem with second table view or selection")
return
}
guard let dest = segue.destination as? DetailsViewController else {
print("Error: couldn't find expected DetailsViewController")
return
}
guard let details = detailVcArray else {
print("Error: data not set for SecondTableViewController")
return
}
dest.displayDetail = details[row]
}
}
Can somebody help with the row element of my prepareforSeque method and advice the syntax on how to get the indexPath of my firstArray as #vadian suggested? The second condition works fine(not searching) but I don't seem to be able to pull the right item from my DataSource in the searching condition.

Unable to add json key values in array why in swift

My json contains image, type and id.. here i want my ids in separate individual array called idArray.. here i am able to get single id in log, i have append ids to idArray but i am not getting ids in array it shows nil why?
i have taken idArray as string. please help me in code.
here is my json structure:
{
"financer": [
{
"id": "45",
"icon": "https://hello.com//images/img1.png"
"tpe": "bank"
}
{
"id": "40",
"icon": "https://hello.com//images/img2.png"
"tpe": "wallet"
}
.
.
.
]
}
here is my code:
import UIKit
import SDWebImage
struct JsonData {
var iconHome: String?
var typeName: String?
init(icon: String, tpe: String) {
self.iconHome = icon
self.typeName = tpe
}
}
class HomeViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource, UITextFieldDelegate {
#IBOutlet weak var collectionView: UICollectionView!
var itemsArray = [JsonData]()
var idArray = [String]()
override func viewDidLoad() {
super.viewDidLoad()
homeServiceCall()
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return itemsArray.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! HomeCollectionViewCell
let aData = itemsArray[indexPath.row]
cell.paymentLabel.text = aData.typeName
cell.paymentImage.sd_setImage(with: URL(string:aData.iconHome!), placeholderImage: UIImage(named: "GVMC_icon"))
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if let nextViewController = self.storyboard?.instantiateViewController(withIdentifier: "MakePaymentViewController") as? MakePaymentViewController
{
nextViewController.financerId = idArray[indexPath.row]
self.navigationController?.pushViewController(nextViewController, animated: true)
}
else{
AlertFun.ShowAlert(title: "", message: "will update soon..", in: self)
}
}
//MARK:- Service-call
func homeServiceCall(){
let urlStr = "https://webservices/getfinancer"
let url = URL(string: urlStr)
URLSession.shared.dataTask(with: url!, completionHandler: {(data, response, error) in
guard let respData = data else {
return
}
guard error == nil else {
print("error")
return
}
do{
let jsonObj = try JSONSerialization.jsonObject(with: respData, options: .allowFragments) as! [String: Any]
//print("the home json is \(jsonObj)")
let financerArray = jsonObj["financer"] as! [[String: Any]]
print("home financerData \(financerArray)")
for financer in financerArray {
let id = financer["id"] as? String
let pic = financer["icon"] as? String
let typeName = financer["tpe"] as! String
print("home financer id \(String(describing: id))")
self.idArray.append(id ?? "")
print("the home financer idsArray \(self.idArray.append(id ?? ""))")
self.itemsArray.append(JsonData(icon: pic ?? "", tpe: typeName))
}
DispatchQueue.main.async {
self.collectionView.reloadData()
}
}
catch {
print("catch error")
}
}).resume()
}
}
Unable to json ids in separate array please help me in my code.
Don't use multiple arrays as data source. That's very bad practice.
Create two structs conforming to Decodable
struct Root : Decodable {
let financer : [Financer]
}
enum Type : String, Decodable {
case bank, wallet
}
struct Financer : Decodable {
let id : String
let icon : URL
let tpe : Type
}
Declare the data source array
var itemsArray = [Financer]()
and delete
var idArray = [String]()
Replace homeServiceCall with
func homeServiceCall() {
let url = URL(string: "https://dev.com/webservices/getfinancer")
URLSession.shared.dataTask(with: url!, completionHandler: {(data, response, error) in
if let error = error { print(error); return }
do {
DispatchQueue.main.async {
self.activityIndicator.startAnimating()
}
let result = try JSONDecoder().decode(Root.self, from: data!)
self.itemsArray = result.financer
DispatchQueue.main.async {
self.collectionView.reloadData()
}
}
catch {
print(error) -- print always the error instance.
}
}).resume()
}
In cellForRow get the id value with
let aData = itemsArray[indexPath.row]
cell.paymentLabel.text = aData.id
Important note:
Never never ever use synchronous Data(contentsOf to load data from a remote URL. Use an API which loads the data asynchronously and caches the images

How to pull out Data Array from CoreData TableView array, put it into a new Array and export it as csv?

I have an app that takes user inputs in an alertController, and then this values will be stored in CoreData which is then displayed in a tableview. I concatenated all the strings to gather with a comma as separator to make it easier for me to export a csv. However, when I print out the CoreData entity, I get an array that is quite complicated. The array looks like this:
[ (entity: AlarmItems; id: 0xc2bccd37cb753acb ; data: {
alarmAttributes = "Example Name, 24/11/2019, 1500, True, NIL";
}), (entity: AlarmItems; id: 0xc2bccd37cb653acb ; data: {
alarmAttributes = "Example , 12/12/2019, 24/11/2019, True, NIL";
})]
I would like to pull out just that parts after alarmAttributes to be exported into a CSV for further use.
I looked at NSEntityMapping but that did not help me. I'm quite stuck right now. I do not know how to approach the problem. Is my approach even correct in the first place? Is it even possible to export a csv using a an array that I create? The idea is to have the csv be stored in the iOS Device which can then be emailed elsewhere.
My ViewController:
class ViewController: UITableViewController {
var alarmItems: [NSManagedObject] = []
let cellId = "cellId"
override func viewDidLoad() {
super.viewDidLoad()
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)
}
}
#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
//combined string of attributes
let myStrings: [String] = alertController.textFields!.compactMap { $0.text }
let myText = myStrings.joined(separator: ", ")
self.save(myText)
self.tableView.reloadData()
}
let cancelAction = UIAlertAction(title: "Cancel", style: .destructive, handler: nil)
alertController.addTextField { (textField) in
textField.placeholder = "Enter Name of Engineer"
}
alertController.addTextField { (textField) in
textField.placeholder = "Enter Date of Alarm in DD/MM/YYYY"
}
alertController.addTextField { (textField) in
textField.placeholder = "Enter Time of Alarm in 24h (eg: 2300)"
}
alertController.addTextField { (textField) in
textField.placeholder = "Please indicate True/False (type True or False)"
}
alertController.addTextField { (textField) in
textField.placeholder = "Insert comments (if any), or NIL"
}
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)
}
}
#objc func exportCSV(_ sender: AnyObject) {
//will work on exporting csv in the future
return
}
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
}
}

how to append something to array correctly?

I'm going to make a custom cell, already have some labels on it, then I create a cell object and array, try to append that object to array then show on table, but after append, there's no content in my array's properties
I've tried to find out solutions but likely no one has these problems
//tableview implement
var recordCell : [RecordCell] = []
let note = RecordCell()
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.recordCell.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = self.tableView.dequeueReusableCell(withIdentifier: "recordCell",for: indexPath) as! RecordCell
let indexPath = indexPath.row
cell.recordFileName?.text = self.recordCell[indexPath].recordFileName?.text
cell.recordDate?.text = self.recordCell[indexPath].recordDate?.text
cell.delegate = self
cell.playBtn.tag = indexPath
return cell
}
//append to array
let alertController = UIAlertController(title: "請輸入錄音名稱", message: "錄音名稱", preferredStyle: .alert)
let okAction = UIAlertAction(title: "OK", style: .default) { (_) in
var name : String = ""
if(alertController.textFields![0].text == ""){
name = "record"
}else{
name = alertController.textFields![0].text!
}
guard self.audioRecorder == nil else{return}
self.recordNumber += 1
self.record.isEnabled = false
self.pause.isEnabled = true
self.stop.isEnabled = true
let destinationUrl = self.getFileURL().appendingPathComponent("\(name).m4a")
let settings = [AVFormatIDKey: Int(kAudioFormatMPEG4AAC),
AVSampleRateKey: 44100,
AVNumberOfChannelsKey: 2,
AVEncoderAudioQualityKey: AVAudioQuality.high.rawValue
]
do {
self.audioRecorder = try AVAudioRecorder(url: destinationUrl, settings: settings)
self.audioRecorder.record()
self.note.recordFileName?.text = name
self.note.recordDate?.text = self.getDate()
self.recordCell.append(self.note)
} catch {
print("Record error:", error.localizedDescription)
}
}
let cancelAction = UIAlertAction(title: "取消", style: .cancel) { (_) in
self.dismiss(animated: true, completion: {
self.audioRecorder.stop()
})
}
alertController.addTextField { (textField) in
textField.placeholder = "輸入名稱"
textField.keyboardType = .default
}
alertController.addAction(okAction)
alertController.addAction(cancelAction)
self.present(alertController,animated: true,completion: nil)
}
I expect when I append something, there's something in array
After appending a new value to the array, call insertRows method like this
self.recordCell.append(self.note)
self.tableView.insertRows(at: [IndexPath(row: recordCell.count-1, section: 0)], with: .automatic)
I didnt see anything wrong.
Are you sure your code is called? Try giving a print() right before adding and see if it's called.
Maybe it's not called because of guard self.audioRecorder == nil else{return}

tableview duplicating data on back button

I am creating individual members in my app, and everything works well. I validate user input, store it in a local array, and then pass the data on to Firebase when I tap 'next'. The app takes me from Step 2 to Step 3. So far so good.
This is what Step 2 looks like if I load the app from scratch: Step 2 Existing Users. The users are pulled from Firebase, and everything is correct.
If I add a new user, everything updates fine. I tap the 'next' button, and Firebase updates with 3 users, all with correct data, and I'm taken to Step 3. All is good still.
But if I tap the 'back' button in the upper left corner of Step 3 (provided by my navigation controller), the app takes me back to Step 2 and now a duplicate entry of the new user shows up in my tableview. Somehow a duplicate user is appended to my array. This only shows up AFTER I click the 'back' button. It doesn't show up in Firebase, but I don't know where it's coming from. I've spent all day trying to figure this out. I'm sure it's a simple problem that I'm overlooking, but I could really use the community's help on this one.
I've looked at this but it didn't help me. I did some tests where I printed out users.count and found that on ViewDidLoad, the count was 0, but on the unwind segue, the count updated to 3 when I added a new user (this was correct, because there were 3 users). But then when I went to Step 3 and tapped the 'back' button to get back to Step 2, Step 2's ViewDidLoad showed a users.count of 4. I have no idea why. I'm completely stumped.
Here is my Step 2 View Controller code in its entirety:
class Step2VC: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet weak var usersTableView: UITableView!
var users = [User]() // create variable called 'users' which is an array of type User (which is a class we created)
var firebaseUser: FIRUser!
var firebaseStorage: FIRStorage!
var ref: FIRDatabaseReference!
var cellStyleForEditing: UITableViewCellEditingStyle = .none
override func viewDidLoad() {
super.viewDidLoad()
navigationItem.title = "users"
usersTableView.dataSource = self
usersTableView.delegate = self
usersTableView.tableFooterView = UIView()
// --------
// Firebase
// --------
firebaseUser = FIRAuth.auth()?.currentUser
firebaseStorage = FIRStorage.storage()
ref = FIRDatabase.database().reference().child("users").child(firebaseUser.uid)
navigationItem.rightBarButtonItem = UIBarButtonItem(title: "edit", style: .plain, target: self, action: #selector(editButtonTapped))
loadExistingUsers() // check to see if there are existing users, and if so, load them into tableview
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(true)
usersTableView.reloadData()
}
// ----------
// Table View
// ----------
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return users.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "CustomCell", for: indexPath) as! Step2Cell
cell.myLabel.text = users[indexPath.row].firstName
cell.userImage.image = users[indexPath.row].photo
return cell
}
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
deleteUserConfirmationAlert(tableViewIndexPath: indexPath)
}
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
performSegue(withIdentifier: "EditUser", sender: users[indexPath.row])
}
// ----------
// Navigation
// ----------
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "EditUser" {
let nextContoller = segue.destination as! Step2UsersVC
// 'sender' is retrieved from 'didSelectRow' function above
nextContoller.user = sender as? User
nextContoller.navBarTitle = "edit user"
} else if segue.identifier == "AddUser" {
let nextController = segue.destination as! Step2UsersVC
nextController.navBarTitle = "add user"
} else {
print("Segue Initiated:",segue.identifier!)
}
}
#IBAction func unwindToStep2VC(sender: UIStoryboardSegue) {
let sourceVC = sender.source as! Step2UsersVC
let updatedUser = sourceVC.user
if let selectedIndexPath = usersTableView.indexPathForSelectedRow {
// Update an existing user
users[selectedIndexPath.row] = updatedUser!
usersTableView.reloadData()
} else {
// Add a new user
let newIndexPath = IndexPath(row: users.count, section: 0)
users.append(updatedUser!)
usersTableView.insertRows(at: [newIndexPath], with: .automatic)
users.sort(by: {$0.birthday < $1.birthday})
usersTableView.reloadData()
}
}
#IBAction func nextButtonTapped(_ sender: UIButton) {
// check for at least two users
if users.count < 2 {
createAlert(alertTitle: "Users", alertMessage: "You have not created enough users. Please enter in at least two users.")
} else {
// check for at least one parent
if numberOfParents() < 1 {
createAlert(alertTitle: "Users", alertMessage: "You must have at least one parent. Please enter in a parent.")
} else {
confirmationAlert()
}
}
}
// ---------
// Functions
// ---------
// if users exist on Firebase, load them
func loadExistingUsers() {
ref.child("members").observe(.childAdded) { (snapshot: FIRDataSnapshot) in
if let dict = snapshot.value as? [String : Any] {
let userPhotoUrl = dict["profileImageUrl"] as! String
let userFirstName = dict["firstName"] as! String
let userBirthday = dict["birthday"] as! Int
let userPasscode = dict["passcode"] as! Int
let userGender = dict["gender"] as! String
let isUserChildOrParent = dict["childParent"] as! String
let storageRef = FIRStorage.storage().reference(forURL: userPhotoUrl)
storageRef.data(withMaxSize: 1 * 1024 * 1024, completion: { (data, error) in
let pic = UIImage(data: data!)
let user = User(profilePhoto: pic!,
userFirstName: userFirstName,
userBirthday: userBirthday,
userPasscode: userPasscode,
userGender: userGender,
isUserChildOrParent: isUserChildOrParent)
self.users.append(user)
self.users.sort(by: {$0.birthday < $1.birthday})
self.usersTableView.reloadData()
})
}
}
}
func saveUsersToFirebase() {
for user in users {
let storageRef = FIRStorage.storage().reference().child("users").child(firebaseUser.uid).child("members").child(user.firstName)
let profileImg = user.photo
let imageData = UIImageJPEGRepresentation(profileImg, 0.1) // compress photos
storageRef.put(imageData!, metadata: nil, completion: { (metadata, error) in
if error != nil {
return
}
// get Firebase image location and return the URL as a string
let profileImageUrl = (metadata?.downloadURL()?.absoluteString)!
// save user data to Firebase
self.ref?.child("members").child(user.firstName).setValue(["profileImageUrl" : profileImageUrl,
"firstName" : user.firstName,
"birthday" : user.birthday,
"passcode" : user.passcode,
"gender" : user.gender,
"childParent" : user.childParent])
})
}
}
func editButtonTapped() {
if cellStyleForEditing == .none {
cellStyleForEditing = .delete
navigationItem.rightBarButtonItem = UIBarButtonItem(title: "done", style: .done, target: self, action: #selector(editButtonTapped))
} else {
cellStyleForEditing = .none
navigationItem.rightBarButtonItem = UIBarButtonItem(title: "edit", style: .plain, target: self, action: #selector(editButtonTapped))
}
usersTableView.setEditing(cellStyleForEditing != .none, animated: true)
}
func deleteUserConfirmationAlert(tableViewIndexPath: IndexPath) {
// create alert for user to confirm user deletion
let alert = UIAlertController(title: "Delete User", message: "Are you sure you want to delete \(users[tableViewIndexPath.row].firstName)? This cannot be undone.", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "okay", style: .default, handler: { (action) in
alert.dismiss(animated: true, completion: nil)
// remove user from Firebase
self.ref.child("members").child(self.users[tableViewIndexPath.row].firstName).removeValue()
self.users.remove(at: tableViewIndexPath.row)
self.usersTableView.deleteRows(at: [tableViewIndexPath], with: .fade)
}))
alert.addAction(UIAlertAction(title: "cancel", style: .cancel, handler: { (action) in
alert.dismiss(animated: true, completion: nil)
self.usersTableView.reloadData()
}))
present(alert, animated: true, completion: nil)
}
func numberOfParents() -> Int {
var parentCount = 0
for user in users {
if user.childParent == "parent" {
parentCount += 1
}
}
return parentCount
}
func createAlert(alertTitle: String, alertMessage: String) {
let alert = UIAlertController(title: alertTitle, message: alertMessage, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "okay", style: .cancel, handler: { (action) in
alert.dismiss(animated: true, completion: nil)
}))
present(alert, animated: true, completion: nil)
}
func confirmationAlert() {
let alert = UIAlertController(title: "Users", message: "You have entered in \(users.count) users. Are you finished adding family members?", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "continue", style: .default, handler: { (action) in
self.saveUsersToFirebase()
self.performSegue(withIdentifier: "GoToStep3", sender: self)
alert.dismiss(animated: true, completion: nil)
}))
alert.addAction(UIAlertAction(title: "cancel", style: .cancel, handler: { (action) in
alert.dismiss(animated: true, completion: nil)
}))
present(alert, animated: true, completion: nil)
}
}

Resources