Swift Accessing Data Structure Inside Cell Setup - ios

I am appending a Firebase Data snapshot to a NSObject of items being a "customer", "employee", and "business". Set up like this:
var customerData = [CustomerData]()
var employeeData = [EmployeeData]()
var businessData = [BusinessData]()
func getCustomerData() {
Database.database().reference().child("user_profiles").observe(.childAdded, with: { snapshot in
self.customerData.append(CustomerData(snapshot: snapshot))
})
}
func getEmployeeData() {
Database.database().reference().child("employees").observe(.childAdded, with: { snapshot in
self.employeeData.append(EmployeeData(snapshot: snapshot))
})
}
func getBusinessData() {
Database.database().reference().child("Businesses").observe(.childAdded, with: { snapshot in
self.businessData.append(BusinessData(snapshot: snapshot))
})
}
Data structure is the same for customer, employees, and business as below
import UIKit
import Firebase
class CustomerData: NSObject {
var customerName: String?
var customerPicture: String?
var customerUID: String?
init(snapshot: DataSnapshot) {
if let dictionary = snapshot.value as? [String: AnyObject] {
customerName = dictionary["name"] as? String
customerUID = dictionary["uid"] as? String
customerPicture = dictionary["profPicString"] as? String
}
}
}
I just want to access this snapshot data inside the cell to keep my message details up-to-date, like the profile picture and name. Below is my cell set up:
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath) as! ChatMessageCell
cell.chatLogController = self
let customer = customerData
let employee = employeeData
let business = businessData
let message = messages[indexPath.row]
cell.message = message
cell.customer = customer
cell.employee = employee
cell.business = business
setupChatMessageCell(cell,message,customer,employee,business)
if let text = message.text {
cell.textView.text = text
cell.bubbleWidthAnchor?.constant = estimateSizeOfText(text).width + 32
cell.textView.isHidden = false
} else if message.imageUrl != nil {
cell.bubbleWidthAnchor?.constant = 200
cell.textView.isHidden = true
}
cell.playButton.isHidden = message.videoUrl == nil
return cell
}
private func setupChatMessageCell(_ cell: ChatMessageCell, _ message: GroupMessage, _ customer: CustomerData, _ employee: EmployeeData, _ business: BusinessData) {
if message.fromId == customer.customerUID {
//outgoing messages
cell.bubbleView.backgroundColor = ChatMessageCell.blueColor
cell.textView.textColor = .white
cell.bubbleLeftAnchor?.isActive = false
cell.bubbleRightAnchor?.isActive = true
cell.profileImageView.isHidden = true
cell.nameLabel.textColor = .gray
cell.nameRightAnchor?.isActive = true
cell.nameLeftAnchor?.isActive = false
cell.nameLabel.text = message.name?.description
//cell.nameLabel.text = message.customerName
} else if message.fromId == employee.employeeUID {
//incoming messagese
let customerImage = employee.employeePicture
cell.profileImageView.loadImageUsingCacheWithUrlString(customerImage!)
cell.profileImageView.isHidden = false
cell.bubbleView.backgroundColor = UIColor(red: 240, green: 240, blue: 240)
cell.textView.textColor = .black
cell.bubbleLeftAnchor?.isActive = true
cell.bubbleRightAnchor?.isActive = false
cell.profileImageView.isHidden = false
cell.nameRightAnchor?.isActive = false
cell.nameLeftAnchor?.isActive = true
cell.nameLabel.textColor = .black
cell.nameLabel.text = message.name?.description
} else if message.fromId == business.businessUID {
let customerImage = business.businessPicture
cell.profileImageView.loadImageUsingCacheWithUrlString(customerImage!)
cell.profileImageView.isHidden = false
cell.bubbleView.backgroundColor = UIColor(red: 240, green: 240, blue: 240)
cell.textView.textColor = .black
cell.bubbleLeftAnchor?.isActive = true
cell.bubbleRightAnchor?.isActive = false
cell.profileImageView.isHidden = false
cell.nameRightAnchor?.isActive = false
cell.nameLeftAnchor?.isActive = true
cell.nameLabel.textColor = .black
cell.nameLabel.text = message.name?.description
}
if let imageUrl = message.imageUrl {
cell.messageImageView.loadImageUsingCacheWithUrlString(imageUrl)
cell.messageImageView.isHidden = false
cell.bubbleView.backgroundColor = .clear
} else {
cell.messageImageView.isHidden = true
}
}
The problem is that I don't think accessing it as a "[index.path]" is the correct way just how I am doing with "messages". How can I access these data structures within the cell setup so I can keep my users information always up-to-date? I am getting errors like "Cannot assign value of type '[CustomerData]' to type 'CustomerData?'" so what is the proper way to access these data structures inside the cell?

The problem is that I don't think accessing it as a "[index.path]" is the correct way just how I am doing with "messages".
This is not true. Passing row or item property of IndexPath as index of element in data source array is correct way how to get certain element.
But, you're using UICollectionView, so you should use item property instead of row even if functionality is the same
UITableView
let item = dataSourceArray[indexPath.row]
UICollectionView
let item = dataSourceArray[indexPath.item]
But you should never pass certain cell as parameter for some other method where you're setting it.
Instead in your collection view cell subclass create method for setting cell's views etc.
class ChatMessageCell: UICollectionViewCell {
...
var message: Message!
...
func setupCell() {
... // here you can work with cell's properites e.g. message, ...
}
}
... and then call it in cellForItemAt
Inside this method you should change things connected with content. "Cosmetic" stuff like changing color of views, etc. you can set inside overridden UICollectionViewCell's method prepareForReuse()
override func prepareForReuse() {
super.prepareForReuse()
...
}

Related

Swift Firebase Group Messenger Data - Profile Picture and Name

I have a group messenger and am trying to keep users profile image and name up-to-date incase they decide to change their profile image or update their name. The problem is when I am populating the cells, the data is getting mismatched. So everything loads, the image and name, but it is the wrong people getting loaded.
Calling and setting all users (customers,employees, and businesses)
This is setting the users to a data structure
func getCustomerData() {
Database.database().reference().child("user_profiles").observe(.childAdded, with: { snapshot in
self.customerData.append(CustomerData(snapshot: snapshot))
print(snapshot)
print("Printed Customer Data")
})
}
func getEmployeeData() {
Database.database().reference().child("employees").observe(.childAdded, with: { snapshot in
self.employeeData.append(EmployeeData(snapshot: snapshot))
print(snapshot)
print("Printed Employee Data")
})
}
func getBusinessData() {
Database.database().reference().child("Businesses").observe(.childAdded, with: { snapshot in
self.businessData.append(BusinessData(snapshot: snapshot))
print(snapshot)
print("Printed Business Data")
})
}
Data Structure for customers, employees, and business. Same type of structure for all 3
import UIKit
import Firebase
class CustomerData: NSObject {
var customerName: String?
var customerPicture: String?
var customerUID: String?
init(snapshot: DataSnapshot) {
if let dictionary = snapshot.value as? [String: AnyObject] {
customerName = dictionary["name"] as? String
customerUID = dictionary["uid"] as? String
customerPicture = dictionary["profPicString"] as? String
}
}
}
Cell Set Up
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath) as! ChatMessageCell
cell.chatLogController = self
let customer = customerData[indexPath.item]
let employee = employeeData[indexPath.item]
let business = businessData[indexPath.item]
let message = messages[indexPath.row]
cell.message = message
cell.customer = customer
cell.employee = employee
cell.business = business
setupChatMessageCell(cell,message,customer,employee,business)
if let text = message.text {
cell.textView.text = text
cell.bubbleWidthAnchor?.constant = estimateSizeOfText(text).width + 32
cell.textView.isHidden = false
} else if message.imageUrl != nil {
cell.bubbleWidthAnchor?.constant = 200
cell.textView.isHidden = true
}
cell.playButton.isHidden = message.videoUrl == nil
return cell
}
private func setupChatMessageCell(_ cell: ChatMessageCell, _ message: GroupMessage, _ customer: CustomerData, _ employee: EmployeeData, _ business: BusinessData) {
if message.fromId == Auth.auth().currentUser?.uid {
//outgoing messages
cell.bubbleView.backgroundColor = ChatMessageCell.blueColor
cell.textView.textColor = .white
cell.bubbleLeftAnchor?.isActive = false
cell.bubbleRightAnchor?.isActive = true
cell.profileImageView.isHidden = true
cell.nameLabel.textColor = .gray
cell.nameRightAnchor?.isActive = true
cell.nameLeftAnchor?.isActive = false
cell.nameLabel.text = customer.customerName?.description
//cell.nameLabel.text = message.customerName
} else if message.fromId == business.businessUID?.description {
//incoming messagese
let customerImage = business.businessPicture?.description
cell.profileImageView.loadImageUsingCacheWithUrlString(customerImage!)
cell.profileImageView.isHidden = false
cell.bubbleView.backgroundColor = UIColor(red: 240, green: 240, blue: 240)
cell.textView.textColor = .black
cell.bubbleLeftAnchor?.isActive = true
cell.bubbleRightAnchor?.isActive = false
cell.profileImageView.isHidden = false
cell.nameRightAnchor?.isActive = false
cell.nameLeftAnchor?.isActive = true
cell.nameLabel.textColor = .black
cell.nameLabel.text = business.businessName
} else {
let customerImage = employee.employeePicture?.description
cell.profileImageView.loadImageUsingCacheWithUrlString(customerImage!)
cell.profileImageView.isHidden = false
cell.bubbleView.backgroundColor = UIColor(red: 240, green: 240, blue: 240)
cell.textView.textColor = .black
cell.bubbleLeftAnchor?.isActive = true
cell.bubbleRightAnchor?.isActive = false
cell.profileImageView.isHidden = false
cell.nameRightAnchor?.isActive = false
cell.nameLeftAnchor?.isActive = true
cell.nameLabel.textColor = .black
cell.nameLabel.text = employee.employeeName
}
if let imageUrl = message.imageUrl {
cell.messageImageView.loadImageUsingCacheWithUrlString(imageUrl)
cell.messageImageView.isHidden = false
cell.bubbleView.backgroundColor = .clear
} else {
cell.messageImageView.isHidden = true
}
}
Firebase Group Messages Structure
I need help on how I can match the message "fromId" to the right user. I have 3 different profiles, customers, employees, and businesses. As of now the wrong data is being set for the business and employee messages. The data for the customer is correct which is the first "if else" statement.
How Data is loaded
So as you can see he displayed name and profile picture is different from the name inside the message.

Unable to add dynamic data in expandable tableview iOS swift?

I'm new to iOS development I'm trying to implement expandable table with dynamic data which comes from server. I'm using https://github.com/amratab/ThreeLevelAccordian this expandable table view.
In this library they added statically like below code.
cells.append(TLAHeaderItem(value: "Bathroom" as AnyObject, imageURL: "bathroom_grey_32.png"))
cells.append(TLACell(value: "Shower" as AnyObject))
cells.append(TLASubItem(value: "Shower pores should be cleaned effectively by brushing." as AnyObject))
cells.append(TLACell(value: "Tap" as AnyObject))
cells.append(TLASubItem(value: "Taps must be washed with soap and all the salt removed." as AnyObject))
cells.append(TLACell(value: "Toilet" as AnyObject, imageURL: "toilet_grey_32.png"))
cells.append(TLASubItem(value: "Should be made stains and germs free." as AnyObject))
cells.append(TLAHeaderItem(value: "Bedroom" as AnyObject, imageURL: "bedroom_grey_32.png"))
cells.append(TLACell(value: "Bed" as AnyObject))
cells.append(TLASubItem(value: "Remove all the dust." as AnyObject))
cells.append(TLACell(value: "Dressing" as AnyObject))
cells.append(TLAHeaderItem(value: "Kitchen" as AnyObject, imageURL: "kitchen_grey_32.png"))
cells.append(TLACell(value: "Utensils" as AnyObject))
cells.append(TLASubItem(value: "There are many type of utensils like tongs, rolling pin, pan, non stick pans. Wash them all." as AnyObject))
cells.append(TLACell(value: "Sink" as AnyObject))
cells.append(TLASubItem(value: "Clean the sink" as AnyObject))
in cellforrowindexpath they are using like this.
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let item = self.cells[(indexPath as NSIndexPath).row]
let value = item.value as? String
if let cell = tableView.dequeueReusableCell(withIdentifier: cellReuseIdentifier) {
cell.textLabel?.text = value
let label = cell.textLabel!
cell.imageView?.image = nil
if let headerImage = item.imageURL, let image = UIImage(named: headerImage) {
cell.imageView?.image = image
}
if let accessoryView = accessory(for: indexPath, and: .expand) {
cell.accessoryView = accessoryView
} else {
cell.accessoryType = UITableViewCellAccessoryType.none
cell.accessoryView = nil
}
if let _ = item as? TLAHeaderItem {
if let headerFont = headerCellFont {
cell.textLabel?.font = headerFont
}
if let headerCellBackgroundColor = self.headerCellBackgrondColor {
cell.backgroundColor = headerCellBackgroundColor
}
if let headerCellTextColor = self.headerCellTextColor {
cell.textLabel?.textColor = headerCellTextColor
}
} else if (item as? TLASubItem != nil) {
if isMultiline {
label.lineBreakMode = NSLineBreakMode.byWordWrapping
label.numberOfLines = 0
label.sizeToFit()
}
cell.accessoryView = nil
cell.accessoryType = UITableViewCellAccessoryType.none
if let subItemCellBackgrondColor = self.subItemCellBackgrondColor {
cell.backgroundColor = subItemCellBackgrondColor
}
if let subItemCellTextColor = self.subItemCellTextColor {
cell.textLabel?.textColor = subItemCellTextColor
}
if let subItemCellFont = self.subItemCellFont {
cell.textLabel?.font = subItemCellFont
}
} else {
if let itemCellBackgrondColor = self.itemCellBackgrondColor {
cell.backgroundColor = itemCellBackgrondColor
}
if let itemCellTextColor = self.itemCellTextColor {
cell.textLabel?.textColor = itemCellTextColor
}
if let itemCellFont = self.itemCellFont {
cell.textLabel?.font = itemCellFont
}
}
return cell
}
return UITableViewCell()
}
instead of adding statically i want to add dynamically array of data. to the cell.
Example:
cell.textLabel?.text = self.tableViewData[indexPath.row]

Trouble reusing TableViewCells in Swift

I have some trouble reusing cells in swift. I want the code below to only execute for the cells where post.altC.isEmpty actually is true. The problem is that it makes botBtnsStackView.isHidden = true for all cells, even though altC is not empty in all. What am I doing wrong?
The code below is from my PostCell file(just a part of the configureCell code at the bottom, but it's this part that is going wrong):
if post.altC.isEmpty == true {
botBtnsStackView.isHidden = true
} else {
altCLabel.text = post.altC["text"] as? String
if let votes = post.altC["votes"]{
self.altCVotesLbl.text = "\(votes)"
}
}
cellForRowAt:
func tableView(_ tableView: UITableView, cellForRowAt indexpath: IndexPath) -> UITableViewCell {
let post = posts[indexpath.row]
if let cell = tableView.dequeueReusableCell(withIdentifier: "PostCell", for: indexpath) as? PostCell{
cell.configureCell(post: post)
return cell
} else {
return PostCell()
}
}
ConfigureCell from PostCell file:
func configureCell(post: Post) {
self.post = post
//ALT A
if post.altA.isEmpty == false {
altALabel.text = post.altA["text"] as? String
if let votes = post.altA["votes"]{
self.altAVotesLbl.text = "\(votes)"
}
} else {
print("No data found in Alt A")
}
//ALT B
if post.altB.isEmpty == false {
altBLabel.text = post.altB["text"] as? String
if let votes = post.altB["votes"]{
self.altBVotesLbl.text = "\(votes)"
}
} else {
print("No data found in Alt B")
}
//ALTD
if post.altD.isEmpty == false {
altDLabel.text = post.altD["text"] as? String
if let votes = post.altD["votes"]{
self.altDVotesLbl.text = "\(votes)"
}
} else {
altDView.backgroundColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0)
altDVotesView.isHidden = true
altDLabelView.isHidden = true
}
//ALT C
if post.altC.isEmpty == true {
print("No data found in Alt C")
//altCView.isHidden = true
botBtnsStackView.isHidden = true
} else {
altCLabel.text = post.altC["text"] as? String
if let votes = post.altC["votes"]{
self.altCVotesLbl.text = "\(votes)"
}
}
Cells are reused. So anything you do in an if statement you need to undo in the else.
So your snipped needs to be changed to:
if post.altC.isEmpty == true {
botBtnsStackView.isHidden = true
} else {
botBtnsStackView.isHidden = false
altCLabel.text = post.altC["text"] as? String
if let votes = post.altC["votes"]{
self.altCVotesLbl.text = "\(votes)"
}
}
Your others need to be update as well. For example, for "ALT A":
if post.altA.isEmpty == false {
altALabel.text = post.altA["text"] as? String
if let votes = post.altA["votes"]{
self.altAVotesLbl.text = "\(votes)"
}
} else {
altALabel.text = ""
self.altAVotesLbl.text = ""
print("No data found in Alt A")
}
I'm guessing a bit here but this gives you an idea. Adjust this to suit your actual needs. The important thing to remember is that whatever you set for one condition, you must reset for other conditions.
Unrelated but you should rewrite your cellForRowAt as:
func tableView(_ tableView: UITableView, cellForRowAt indexpath: IndexPath) -> UITableViewCell {
let post = posts[indexpath.row]
let cell = tableView.dequeueReusableCell(withIdentifier: "PostCell", for: indexpath) as! PostCell
cell.configureCell(post: post)
return cell
}
This is a case where a force-cast is appropriate. You want your app to crash early on in development if you have setup your cell identifier and cell type incorrectly. Once setup properly and working, it can't crash at runtime unless you do something to break it.

File uploading progress issue

I am uploading a single file in a table view cell using AFNetworking.
Uploading is working alright. But when I scroll my cell off view, the progress is gone or some times it displays different progress values like 20% or 50%, and again 30%.
This is my code:
//Tableview Cell configure
var cell : ChattingPhotoCell!
if message.vendorType == .Receiver {
//Receive Image message
cell = tableView.dequeueReusableCell(withIdentifier: "ReceiveChattingPhotoCell") as! ChattingPhotoCell
if cell == nil {
cell = Bundle.main.loadNibNamed("ReceiveChattingPhotoCell", owner: self, options: nil)?[0] as! ChattingPhotoCell
}
cell.reloadDelegate = self
cell.mDelegate = self
cell.accessoryType = cell.isSelected ? .checkmark : .none
cell.conficureImageCell(msg: message,indexPath: indexPath)
} else {
// Send Image Message
cell = tableView.dequeueReusableCell(withIdentifier: "SenderChattingPhotoCell") as! ChattingPhotoCell
if cell == nil {
cell = Bundle.main.loadNibNamed("SenderChattingPhotoCell", owner: self, options: nil)?[0] as! ChattingPhotoCell
}
cell.reloadDelegate = self
cell.mDelegate = self
cell.accessoryType = cell.isSelected ? .checkmark : .none
cell.conficureImageCell(msg: message,indexPath: indexPath)
}
//MyCell code
func conficureImageCell(msg:Message,indexPath:IndexPath) {
self.message = msg
self.indexPath = indexPath
if self.message.vendorType == .Sender {
self.senderCellConfigure()
} else {
self.receiverCellConfigure()
}
}
// sender configure methods
func senderCellConfigure() {
// Send Message
if message.upload == 1 {
self.btnRetry.isHidden = true
}else{
self.btnRetry.isHidden = false
}
if message.is_send == 0 && !self.message.isUploadMedia && message.upload != 1 {
let image = UIImage.init(contentsOfFile: documentDir.path)
if image != nil {
self.uploadImageToServer(ArrImage_Video: NSMutableArray.init(array: [image!]), strMsgType: "3")
}
}
if self.message.isUploadMedia {
self.progressView.isHidden = false
self.btnRetry.isHidden = true
} else {
self.progressView.isHidden = true
}
}
// MARK:- WebserviceCalling // Hiren
func uploadImageToServer(ArrImage_Video:NSMutableArray,strMsgType: String){
self.message.isUploadMedia = true
self.btnRetry.isHidden = true
if self.str_media_url.isBlank {
self.progressView.isHidden = false
let accessToken = Singleton.sharedSingleton.retriveFromUserDefaults(key:Global.kLoggedInUserKey.AccessToken)
print(accessToken!)
let param: NSMutableDictionary = NSMutableDictionary()
param.setValue(fromJID, forKey: "from_user_id")
param.setValue(toJID, forKey: "to_user_id")
param.setValue(strMsgType, forKey: "message_type")
param.setValue("ABC", forKey: "from_user_name")
param.setValue(UIDevice.current.model, forKey: "device_type")
param.setValue("897584acac541d73d5f01f294fe944ddb35b6f67ea894e9ac29b03c7da69ca48", forKey: "device_token")
param.setValue("jpeg", forKey: "file_type")
param.setValue("135", forKey: "message_id")
param.setValue(accessToken, forKey: "access_token")
AFAPIMaster.sharedAPIMaster.PostMediatoServer_chat(params: param, arrMedia: ArrImage_Video, showLoader: false, enableInteraction: true, viewObj: self, onSuccess: {
(DictResponse) in
let dictResponse: NSDictionary = DictResponse as! NSDictionary
let dictData: NSDictionary = dictResponse.object(forKey: "SuccessResponse") as! NSDictionary
let media_url = dictData.object(forKey: "media_url") as? String ?? ""
let media_id = dictData.object(forKey: "id") as? Int ?? 0
let thumb_image = dictData.object(forKey: "thumb_image") as? String ?? ""
print(media_url)
print(thumb_image)
DispatchQueue.main.async {
self.progressView.isHidden = true
let onChatMaster = OnChatMasterTable()
let messageObj = onChatMaster.getMessageFromDB(strMessageID: self.msgID, strFromUId: self.fromJID, strToUId: self.toJID)
messageObj.media_url = media_url
messageObj.thumb_url = thumb_image
messageObj.upload = 1
messageObj.message = self.imageName
messageObj.media_id = media_id
self.str_media_ID = String(media_id)
self.message.media_id = media_id
DBHelper.sharedInstance.queue?.inDatabase() {
db in
DBHelper.sharedInstance.chilaxDB.open()
let strUpdQuery = "UPDATE OnChat_Master SET upload = 1 , media_url = '\(media_url)', thumb_url = '\(thumb_image)' , media_id = \(media_id) where message_id = '\(messageObj.message_id)' AND from_user_id = '\(messageObj.from_user_id)' AND to_user_id = '\(messageObj.to_user_id)'"
DBHelper.sharedInstance.chilaxDB.executeUpdate(strUpdQuery, withArgumentsIn: [])
DBHelper.sharedInstance.chilaxDB.close()
}
//onChatMaster.updateMessageInDB(messsageObj: messageObj)
self.sendMediaDataToSender(media_url: media_url, thumb_image: thumb_image,strMediaId:self.str_media_ID)
self.message.isUploadMedia = false
}
}, displayProgress: {
(progress) in
DispatchQueue.main.async {
let progressTask : Float = Float((progress as! Progress).fractionCompleted)
print(progressTask)
self.progressView.setProgress(progressTask, animated: true)
}
}, onFailure: {
self.progressView.setProgress(0.0, animated: true)
self.progressView.isHidden = true
self.btnRetry.isHidden = false
self.message.isUploadMedia = false
})
} else {
self.sendMediaDataToSender(media_url: str_media_url, thumb_image: str_thumb_url, strMediaId: str_media_ID)
}
}
I reuse cell using table view deque methods. Any wrong in this code.

Automatically saving changes in a cell to object when editing finishes?

im having a real nightmare with my project where i need to save cell contents to an object, for each object in an array. I cant get this to work by looping through table cells adn array objects and trying to match them all up.
So my next idea was to add didFinishEditing related functions into the cellForRowAt function?
Im not sure this would work either, but this is what i have:
Each row here has a label for the set, a picker for the reps that can be scrolled to a number, and a textfield to put a weight. Then i save each row as an object storing the set, rep and weight.
Issue is when editing this, how can i save these again overwriting the old values? Hence my plan above to use didFinishEditing methods.
My previous plan was the code below, but i cant figure out the annotated part. So i was hoping someone had guidance on how i can approach saying when editing rather than this save button function that doesnt work!
func saveUserExerciseSets() {
if userExercise == nil {
print("CREATING A FRESH SET OF SETS FOR THE NEW EXERCISE")
for cell in self.customSetsTable.visibleCells as! Array<NewExerciseTableViewCell> {
print("SAVING THESE CELLS \(customSetsTable.visibleCells)")
let newUserExerciseSet = UserExerciseSet(context: self.managedObjectContext)
newUserExerciseSet.setPosition = Int64(cell.setNumber.text!)!
newUserExerciseSet.setReps = Int64(cell.repsPicker.selectedRow(inComponent: 0))
newUserExerciseSet.parentExerciseName = self.userExerciseName.text
if self.localeIdentifier == "en_GB" {
let kgWeight = Measurement(value: Double(cell.userExerciseWeight.text!)!, unit: UnitMass.kilograms)
newUserExerciseSet.setWeight = kgWeight as NSObject?
newUserExerciseSet.initialMetricSystem = self.localeIdentifier
} else if self.localeIdentifier == "en_US" {
let lbsWeight = Measurement(value: Double(cell.userExerciseWeight.text!)!, unit: UnitMass.pounds)
newUserExerciseSet.setWeight = lbsWeight as NSObject?
newUserExerciseSet.initialMetricSystem = self.localeIdentifier
}
let fetchRequest: NSFetchRequest<UserExercise> = UserExercise.fetchRequest()
fetchRequest.predicate = NSPredicate(format: "name == %#", self.exerciseNameToAddTo!)
do {
let parentExercise = try self.managedObjectContext.fetch(fetchRequest).first
parentExercise?.addToExercisesets(newUserExerciseSet)
print("SET ADDED TO EXERCISE")
} catch {
print("Fetching Routine Failed")
}
}
} else if self.userExercise != nil {
print("UPDATING EXISTING SETS FOR THE EXISTING EXERCISE")
let cells = self.customSetsTable.visibleCells as! Array<NewExerciseTableViewCell>
for cell in cells {
let exerciseSets = self.userExercise?.exercisesets?.allObjects as! [UserExerciseSet]
let sortedexerciseSets = exerciseSets.sorted { ($0.setPosition < $1.setPosition) }
let cellsSet = sortedexerciseSets //match the sortedexerciseSets set object to the cell index positions
cellsSet.setPosition = Int64(setsCell.setNumber.text!)!
cellsSet.setReps = Int64(setsCell.repsPicker.selectedRow(inComponent: 0))
if self.localeIdentifier == "en_GB" {
let kgWeight = Measurement(value: Double(cell.userExerciseWeight.text!)!, unit: UnitMass.kilograms)
cellsSet.setWeight = kgWeight as NSObject?
} else if self.localeIdentifier == "en_US" {
let lbsWeight = Measurement(value: Double(cell.userExerciseWeight.text!)!, unit: UnitMass.pounds)
cellsSet.setWeight = lbsWeight as NSObject?
}
cellsSet.parentExerciseName = self.userExerciseName.text
}
}
do {
try self.managedObjectContext.save()
print("THE SET HAS BEEN SAVED")
} catch {
fatalError("Failure to save context: \(error)")
}
delegate?.didFinishEditing()
self.dismiss(animated: true, completion: nil)
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as? NewExerciseTableViewCell
else {
fatalError("Unexpected Index Path")
}
cell.backgroundColor = UIColor.customBackgroundGraphite()
cell.textLabel?.textColor = UIColor.white
cell.repsPicker.dataSource = self
cell.repsPicker.delegate = self
configure(cell, at: indexPath)
return cell
}
func configure(_ cell: NewExerciseTableViewCell, at indexPath: IndexPath) {
// configuring cells when theres a loaded exercise causes the issues --------------------
if self.userExercise != nil {
print("RESTORING CELLS FOR THE EXISTING EXERCISE")
let unsortedExerciseSets = self.userExercise?.exercisesets?.allObjects as! [UserExerciseSet]
let exerciseSets = unsortedExerciseSets.sorted { ($0.setPosition < $1.setPosition) }
let cellsSet = exerciseSets[indexPath.row]
cell.setNumber.text = String((indexPath.row) + 1)
let indexRow = Int(cellsSet.setReps)
print("INDEX ROW INT IS \(indexRow)")
cell.repsPicker.selectRow(indexRow, inComponent: 0, animated: true) //fix this crashing issue!
let localeIdentifier = Locale(identifier: UserDefaults.standard.object(forKey: "locale") as! String)
let setWeight = cellsSet.setWeight as! Measurement<UnitMass>
let formatter = MassFormatter()
formatter.numberFormatter.locale = localeIdentifier
formatter.numberFormatter.maximumFractionDigits = 2
if localeIdentifier.usesMetricSystem {
let kgWeight = setWeight.converted(to: .kilograms)
let finalKgWeight = formatter.string(fromValue: kgWeight.value, unit: .kilogram)
let NumericKgResult = finalKgWeight.trimmingCharacters(in: CharacterSet(charactersIn: "0123456789.").inverted)
cell.userExerciseWeight.text = NumericKgResult
} else {
let lbsWeight = setWeight.converted(to: .pounds)
let finalLbWeight = formatter.string(fromValue: lbsWeight.value, unit: .pound)
let NumericLbResult = finalLbWeight.trimmingCharacters(in: CharacterSet(charactersIn: "0123456789.").inverted)
cell.userExerciseWeight.text = NumericLbResult
}
} else if self.userExercise == nil {
print("NEW SET CELL ADDED FOR FRESH EXERCISE")
cell.setNumber.text = String((indexPath.row) + 1)
}
}
Try something like this to match the setIds correctly. That's where I think the issue is.
for x in sortedexerciseSets {
if x.setPosition == Int64(setsCell.setNumber.text!)! {
//save
}
}
Proper way to do it would be to have an array of those sets (I guess, since you tagged core-data, they are instances of NSManagedObject?). When user does ANY change in the cell (write new value in the text field or scroll to another value for reps) you need to update the approproate object in your array immediately. Then you could call save on NSManagedObjectContext when you're sure you want to save changes, or just call rollback on the context to cancel all changes.

Resources