Im working on an app where a merchant can add a bunch of products into a persons tab.
After selecting the customer from a table view the user can see a list of products and the total amount which I'd like to save under that customers name for him to come pay later. I've done a lot of research about relationships in CoreData but have not found a way to save many items at once.
Here is a screenshot of the view controller showing the customer and the products to add to his tab.
Add to tab view controller
I've created the data models and all and everything works great just can't link the products to each customer. I want to be able to click on a customer and see all the products in his tab. I've spent weeks now trying to find an answer and its getting very frustrating. Just need to be able to save and retrieve the items and my app will be done.
Really looking forward to an answer!
import UIKit
import MapKit
import GoogleSignIn
import CoreData
class addToTabViewController: UIViewController {
// Data Arrays
var myCart = [Cart]()
var myCartUz: [Cart] = []
var selectedIndex: Int!
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
var amount: String = ""
var transaction: String = ""
#IBOutlet weak var profilePicture: UIImageView!
#IBOutlet weak var customerName: UILabel!
#IBOutlet weak var phoneNumber: UILabel!
#IBOutlet weak var emailAddress: UILabel!
#IBOutlet weak var customerAddress: UILabel!
#IBOutlet weak var profileView: UIView!
#IBOutlet weak var map: MKMapView!
#IBOutlet weak var receiptView: UIView!
#IBOutlet weak var tableView: UITableView!
#IBOutlet weak var customerProfile: UIImageView!
#IBOutlet weak var customerProfileView: UIView!
#IBOutlet weak var totalAmount: UILabel!
#IBOutlet weak var merchantName: UILabel!
#IBOutlet weak var merchatEmail: UILabel!
// Variable
var customers: Cutomers!
override func viewDidLoad() {
super.viewDidLoad()
// Show data
configureEntryData(entry: customers)
fetchCartData()
totalAmount.text = amount
// Design parameters
hutzilopochtli()
}
// Info profile button
#IBAction func infoButton(_ sender: Any) {
profileView.isHidden = !profileView.isHidden
receiptView.isHidden = !receiptView.isHidden
customerProfileView.isHidden = !customerProfileView.isHidden
}
// Add to tab button
#IBAction func addToTabButton(_ sender: Any) {
}
// Show customer details
func configureEntryData(entry: Cutomers) {
let name = entry.name
let address = entry.address
let phone = entry.phoneNumber
let email = entry.email
customerName!.text = name
customerAddress!.text = address
phoneNumber!.text = phone
emailAddress!.text = email
self.title = name
let image = entry.profileicture as Data?
profilePicture!.image = UIImage(data: image!)
customerProfile!.image = UIImage(data: image!)
}
// Get cart data
func fetchCartData() {
do {
myCart = try context.fetch(Cart.fetchRequest())
myCartUz = myCart
DispatchQueue.main.async {
self.tableView.reloadData()
}
} catch {
}
merchantName?.text = GIDSignIn.sharedInstance().currentUser.profile.name
merchatEmail?.text = GIDSignIn.sharedInstance().currentUser.profile.email
}
// Design parameters function
func hutzilopochtli(){
profilePicture.roundMyCircle()
customerProfile.roundMyCircle()
profileView.layer.cornerRadius = 15
receiptView.layer.cornerRadius = 15
profileView.isHidden = true
map.layer.cornerRadius = 13
}
}
// Table view dataSource and delegates
extension addToTabViewController: UITableViewDataSource, UITableViewDelegate {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return myCartUz.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "discountCell", for: indexPath) as! discountTableViewCell
let price = myCartUz[indexPath.row].price
let xNSNumber = price as NSNumber
cell.productName?.text = myCartUz[indexPath.row].product
cell.amountLabel?.text = "IDR \(xNSNumber.stringValue)"
return cell
}
}
Here is the customer class
class constantCustomer: NSObject {
private class func getContext() -> NSManagedObjectContext {
let appDelegate = UIApplication.shared.delegate as! AppDelegate
return appDelegate.persistentContainer.viewContext
}
class func saveObject(customerId: String, name: String, phone: String, address: String, email: String, picture: NSData) -> Bool {
let context = getContext()
let entity = NSEntityDescription.entity(forEntityName: "Cutomers", in: context)
let managedObject = NSManagedObject(entity: entity!, insertInto: context)
managedObject.setValue(customerId, forKey: "customerID")
managedObject.setValue(NSDate(), forKey: "date")
managedObject.setValue(name, forKey: "name")
managedObject.setValue(phone, forKey: "phoneNumber")
managedObject.setValue(address, forKey: "address")
managedObject.setValue(email, forKey: "email")
managedObject.setValue(picture, forKey: "profileicture")
do {
try context.save()
return true
} catch {
return false
}
}
class func fetchObject() -> [Cutomers]? {
let context = getContext()
var myCustomers: [Cutomers]? = nil
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "Cutomers")
let sort = NSSortDescriptor(key: "date", ascending: true)
fetchRequest.sortDescriptors = [sort]
do {
myCustomers = try context.fetch(Cutomers.fetchRequest())
return myCustomers
} catch {
return myCustomers
}
}
}
Without knowing about the Customers class I can only create an example. This is for the saving process:
func saveCustomer(entry: Customers) {
let entity = NSEntityDescription.entity(forEntityName: "EntityName", in: viewContext)
let customer = Customers(entity: entity!, insertInto: viewContext)
// add data to your customer class
customer.price = price
for journalEntry in entry.entry {
/// Your class with the Relationship
let persistent = CustomersDetail(context: viewContext)
persistent.question = journalEntry.question
persistent.answer = journalEntry.answer
customer.addToRelationship(persistent)
}
/// do saving
do {
try viewContext.save()
} catch let error {
print(error.localizedDescription)
}
}
loading Customer for a specific CostumerName:
func loadCustomerData(customerName: String) -> Customers {
let fetch:NSFetchRequest<Customers> = Customers.fetchRequest()
fetch.predicate = NSPredicate(format: "customerName = %#", "\(customerName)")
var customer = [Customers]()
do {
customer = try viewContext.fetch(fetch)
} catch let error {
print(error.localizedDescription)
}
return customer
}
enter image description here
Related
I am a newbie in Swift and I am trying to build an app in which I retrieve plant images and information from this api "https://rapidapi.com/mnai01/api/house-plants2".
I managed to implement a table view in which I display the name and image of each plant in the api, and when I click on any cell in the table view I displayed that certain plant's information in a new view controller.
My problem is that no matter what I tried I couldn't also display the image of that plant in that view controller and I don't know what to do to make it work.
It is also worth to mention that the links for the images are of this format:
img: "http://www.tropicopia.com/house-plant/thumbnails/5556.jpg"
This is the class of the view controller where the image and information should be displayed:
import UIKit
import SDWebImage
class PlantDetailsViewController: UIViewController {
// image view for the plant
#IBOutlet weak var plantImage: UIImageView!
// labels for the plant information
#IBOutlet weak var commonNameLabel: UILabel!
#IBOutlet weak var latinNameLabel: UILabel!
#IBOutlet weak var otherNamesLabel: UILabel!
#IBOutlet weak var categoryLabel: UILabel!
#IBOutlet weak var useLabel: UILabel!
#IBOutlet weak var styleLabel: UILabel!
#IBOutlet weak var familyLabel: UILabel!
#IBOutlet weak var bloomSeasonLabel: UILabel!
#IBOutlet weak var wateringLabel: UILabel!
#IBOutlet weak var idealLightLabel: UILabel!
#IBOutlet weak var growthLabel: UILabel!
#IBOutlet weak var climatLabel: UILabel!
#IBOutlet weak var diseaseLabel: UILabel!
#IBOutlet weak var insectsLabel: UILabel!
#IBOutlet weak var leafColourLabel: UILabel!
#IBOutlet weak var bloomsColourLabel: UILabel!
#IBOutlet weak var availabilityLabel: UILabel!
#IBOutlet weak var bearingLabel: UILabel!
#IBOutlet weak var appealLabel: UILabel!
var plants: Plant?
var strCommonName = ""
var strLatinName = ""
var strOtherNames = ""
var strCategory = ""
var strUse = ""
var strStyle = ""
var strFamily = ""
var strBloomSeason = ""
var strWatering = ""
var strIdealLight = ""
var strGrowth = ""
var strClimat = ""
var strDisease = ""
var strInsects = ""
var strLeafColour = ""
var strBloomsColour = ""
var strAvailability = ""
var strBearing = ""
var strAppeal = ""
override func viewDidLoad() {
super.viewDidLoad()
commonNameLabel.text = strCommonName
latinNameLabel.text = strLatinName
otherNamesLabel.text = strOtherNames
categoryLabel.text = strCategory
useLabel.text = strUse
styleLabel.text = strStyle
familyLabel.text = strFamily
bloomSeasonLabel.text = strBloomSeason
wateringLabel.text = strWatering
idealLightLabel.text = strIdealLight
growthLabel.text = strGrowth
climatLabel.text = strClimat
diseaseLabel.text = strDisease
insectsLabel.text = strInsects
leafColourLabel.text = strLeafColour
bloomsColourLabel.text = strBloomsColour
availabilityLabel.text = strAvailability
bearingLabel.text = strBearing
appealLabel.text = strAppeal
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
This is is the didSelectRowAt function for the table view:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let detail:PlantDetailsViewController = self.storyboard?.instantiateViewController(withIdentifier: "showDetails") as! PlantDetailsViewController
detail.strCommonName = plants[indexPath.row].common_name?.first ?? "N/A"
detail.strLatinName = plants[indexPath.row].latin_name ?? "N/A"
detail.strOtherNames = plants[indexPath.row].other_names ?? "N/A"
detail.strCategory = plants[indexPath.row].categories ?? "N/A"
detail.strUse = plants[indexPath.row].use?.first ?? "N/A"
detail.strStyle = plants[indexPath.row].style ?? "N/A"
detail.strFamily = plants[indexPath.row].family ?? "N/A"
detail.strBloomSeason = plants[indexPath.row].blooming_season ?? "N/A"
detail.strWatering = plants[indexPath.row].watering ?? "N/A"
detail.strIdealLight = plants[indexPath.row].light_ideal ?? "N/A"
detail.strGrowth = plants[indexPath.row].growth ?? "N/A"
detail.strClimat = plants[indexPath.row].climat ?? "N/A"
detail.strDisease = plants[indexPath.row].disease ?? "N/A"
detail.strInsects = plants[indexPath.row].insects?.first ?? "N/A"
detail.strLeafColour = plants[indexPath.row].color_of_leaf?.first ?? "N/A"
detail.strBloomsColour = plants[indexPath.row].color_of_blooms ?? "N/A"
detail.strAvailability = plants[indexPath.row].availability ?? "N/A"
detail.strBearing = plants[indexPath.row].bearing ?? "N/A"
detail.strAppeal = plants[indexPath.row].appeal ?? "N/A"
self.navigationController?.pushViewController(detail, animated: true)
}
In the Manager folder I created class called "APICaller" where I fetch the data from the API. This is the function that does that:
func getAllPlants (completion: #escaping (Result<[Plant], Error>) -> Void) {
guard let url = URL(string: "\(Constants.baseURL)/all/?rapidapi-key=\(Constants.API_KEY)") else {return}
let task = URLSession.shared.dataTask(with: URLRequest(url: url)) { data, _, error in
guard let data = data, error == nil else {return}
do {
let results = try JSONDecoder().decode([Plant].self, from: data)
completion(.success(results))
} catch {
completion(.failure(APIError.failedTogetData))
}
}
task.resume()
}
And finally this is the Plant struct model:
struct Plant: Codable {
let appeal: String?
let availability: String?
let bearing: String?
let blooming_season: String?
let categories: String?
let climat: String?
let color_of_blooms: String?
let color_of_leaf: [String]?
let common_name: [String]?
let disease: String?
let family: String?
let growth: String?
let insects: [String]?
let latin_name: String?
let light_ideal: String?
let other_names: String?
let style: String?
let use: [String]?
let watering: String?
let id: String?
let img: String?
let url: String?
private enum CodingKeys: String, CodingKey {
case appeal = "Appeal"
case availability = "Availability"
case bearing = "Bearing"
case blooming_season = "Blooming season"
case categories = "Categories"
case climat = "Climat"
case color_of_blooms = "Color of blooms"
case color_of_leaf = "Color of leaf"
case common_name = "Common name"
case disease = "Disease"
case family = "Family"
case growth = "Growth"
case insects = "Insects"
case latin_name = "Latin name"
case light_ideal = "Light ideal"
case other_names = "Other names"
case style = "Style"
case use = "Use"
case watering = "Watering"
case id = "id"
case img = "Img"
case url = "Url"
}
}
I think the problem is that each image is a string which contains a link, and to be able to display it in the table view cells I used SDWebImage. The question is how do I do that to display the image in the detail view controller? Thank you for your time. Any help or piece of advice is greatly appreciated :)
UPDATE- I tried to display it like this :
I wrote this in the viewdidload function in the detail view controller:
var selectedImage: String?
if let imageToLoad = selectedImage {
plantImage.image = UIImage(named: imageToLoad)
}
and then I added this line in the didselectrowat function:
detail.selectedImage = plants[indexPath.row].img
It still doesn't work and I don't know what I am doing wrong
Swift 5.5, Xcode 14.2
plantImage.image = UIImage(named: imageToLoad) is wrong, named is to local images, when you want get images from one API, like "http://www.tropicopia.com/house-plant/thumbnails/5556.jpg", you need use :
Github Example: https://github.com/MaatheusGois/answer-75361391
let url = URL(string: image.url)
func downloadImage(from url: URL) {
print("Download Started")
getData(from: url) { data, response, error in
guard let data = data, error == nil else { return }
print(response?.suggestedFilename ?? url.lastPathComponent)
print("Download Finished")
// always update the UI from the main thread
DispatchQueue.main.async { [weak self] in
self?.plantImage.image = UIImage(data: data)
}
}
}
func getData(from url: URL, completion: #escaping (Data?, URLResponse?, Error?) -> Void) {
URLSession.shared.dataTask(with: url, completionHandler: completion).resume()
}
IMPORTANT (Common error)
https://developer.apple.com/forums/thread/119977
Heloo, i have this problem when im using contaienerView with static tableview. i want to pass my data from my main view controller to my tableview, and im having this break where it said my data is null, but it wasnt null because i already fill that data that i want to pass in. im using firebase and dictionary. heres my code :
my main controller :
class MainController: UITableViewController, AddPatientController {
private var patientLists = [PatientList]() // empty array buat isi list yg isinya nama pasien
var Segue : String = "PatientName"
var Segue2 : String = "PatientNotes"
let user : User = Auth.auth().currentUser!
private var rootRef : DatabaseReference!
override func viewDidLoad() {
super.viewDidLoad()
self.rootRef = Database.database().reference()
populateList()
}
private func populateList() {// 5. func buat fetch data dari db ke hp
self.rootRef.child(self.user.emailWithoutSpecialChar).observe(.value) { (snapshot) in
self.patientLists.removeAll()
let pasienListDict = snapshot.value as? [String:Any] ?? [:] //7. ini berarti return buat kl dict nya kosong, ini buat akses ke valuenya yg isinya itu dict[String:Any]
for (key,_) in pasienListDict {
if let pasienlistdict = pasienListDict[key] as? [String:Any]{
if let pasienlist = PatientList(pasienlistdict) {
self.patientLists.append(pasienlist)
// ini buat ngemasukin ke dalem dictionarynya, ini buat store datanya dan ngambil datanya dari firebase db
}
}
}
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
} override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == Segue {
let nc = segue.destination as! UINavigationController
let addPatientName = nc.viewControllers.first as! AddListController
addPatientName.delegate = self
}
else if segue.identifier == Segue2 {
guard let indexPath = self.tableView.indexPathForSelectedRow else {return}
let nc = segue.destination as! PasienProfileController
nc.pasien = self.patientLists[indexPath.row]
}
}
and this is my controller that should recieve the data
class NotesController: UITableViewController, AddNotesDelegate {
var pasien : PatientList!
private var rootRef : DatabaseReference!
var Segue1 : String = "AddNotes"
var Segue2 : String = "PasienNotes"
override func viewDidLoad() {
super.viewDidLoad()
self.title = pasien.name // this is the line where my code break cause it says the data is null
self.rootRef = Database.database().reference()
}
my pasienprofilecontroller and my pasienProfileTableController( the containverView one) :
class PasienTableController: UITableViewController {
#IBOutlet weak var dataKunjunganLbl: UILabel!
#IBOutlet weak var diagnosaPasienLbl: UILabel!
#IBOutlet weak var alergiPasienLbl: UILabel!
var delegete : PasienTableControllerDelegate?
var patientList = [PatientList]()
override func viewDidLoad() {
super.viewDidLoad()
tableView.dataSource = self
tableView.delegate = self
}
class PasienProfileController: UIViewController {
#IBOutlet weak var TinggiLbl: UILabel!
#IBOutlet weak var beratLbl: UILabel!
#IBOutlet weak var GolDarahLbl: UILabel!
#IBOutlet weak var NamaLbl: UILabel!
#IBOutlet weak var ImagePic: UIImageView!
var pasien : PatientList!
#IBOutlet weak var ContainerView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
self.ImagePic.makeRounded()
self.NamaLbl.text = pasien.name
}
this is the image of my storyboard to get a clear picture of what im trying to do
so my "Patient" view controller is my main controller, which after that it will show the profile pasien controller, which is uiview with container view that contain static tableview. and when i try to hit that " Data Kunjungan Pasien" Cell, it gets the error
so is theres something wrong with my logic? why it keep saying null while it can successfully show the data from Patient view to patientProfileView?
*this is my git if you guys wanna clone and check my error https://gitlab.com/afipermanaa/skripsi.git
Thanks for the help
I dont get it
if you want to pass the data from prepareForSegue with this code
else if segue.identifier == Segue2 {
guard let indexPath = self.tableView.indexPathForSelectedRow else {return}
let nc = segue.destination as! PasienProfileController
nc.pasien = self.patientLists[indexPath.row]
}
to NotesController why do you cast "nc" as PasienProfileController ?
its not the same class
i don't see where you set pasien value.
I hope it helps you if not please explain
cannot comment due to low rep, so I tried to explain it as clear as possible.
you may try to define that variable
private var patientLists = [PatientList]()
as static like below.
private static var patientLists = [PatientList]()
when you try to pass it through segue with this code
let nc = segue.destination as! PasienProfileController
nc.pasien = self.patientLists[indexPath.row]
your class creates a new instance of patientList array when there is 'self'. so, when you define as static, there won't be any new instance of it.
private static var patientLists = [PatientList]()
I'm new to RxSwift. This is quite tricky.
I'm creating like ToDoList that views which are tableView and add-item view are separated by TabBarController.
I have successfully displayed the list array and added a new item into tableView.
I also wanted to display the number of array's count and favourite count in the view that has tableView so that I have displayed it by throwing a value with .just.
But displaying a value based on the result of the array displayed by SearchBar, the value is not reflected as I expected.
In MainViewModel, I made sure if I could get the number of array's count properly by print, but apparently the value was fine.
It is just not reflected in the View.
// Model
struct Item: Codable {
var name = String()
var detail = String()
var tag = String()
var memo = String()
var fav = Bool()
var cellNo = Int()
init(name: String, detail: String, tag: String, memo: String, fav: Bool, celllNo: Int) {
self.name = name
self.detail = detail
self.tag = tag
self.memo = memo
self.fav = fav
self.cellNo = celllNo
}
init() {
self.init(
name: "Apple",
detail: "ringo",
tag: "noun",
memo: "",
fav: false,
celllNo: 0
)
}
}
struct SectionModel: Codable {
var list: [Item]
}
extension SectionModel: SectionModelType {
var items: [Item] {
return list
}
init(original: SectionModel, items: [Item]) {
self = original
self.list = items
}
}
Singleton share class
final class Sharing {
static let shared = Sharing()
var items: [Item] = [Item()]
var list: [SectionModel] = [SectionModel(list: [Item()])] {
didSet {
UserDefault.shared.saveList(list: list)
}
}
let listItems = BehaviorRelay<[SectionModel]>(value: [])
}
extension Sharing {
func calcFavCount(array: [Item]) -> Int {
var count = 0
if array.count > 0 {
for i in 0...array.count - 1 {
if array[i].fav {
count += 1
}
}
}
return count
}
}
// MainTabViewController
class MainTabViewController: UIViewController {
#IBOutlet weak var listTextField: UITextField!
#IBOutlet weak var tagTextField: UITextField!
#IBOutlet weak var itemCountLabel: UILabel!
#IBOutlet weak var favCountLabel: UILabel!
#IBOutlet weak var favIcon: UIImageView!
#IBOutlet weak var infoButton: UIButton!
#IBOutlet weak var searchBar: UISearchBar!
#IBOutlet weak var tableView: UITableView!
private lazy var viewModel = MainTabViewModel(
searchTextObservable: searchTextObservable
)
private let disposeBag = DisposeBag()
private var dataSource: RxTableViewSectionedReloadDataSource<SectionModel>!
override func viewDidLoad() {
super.viewDidLoad()
setupTableViewDataSource()
tableViewSetup()
listDetailSetup()
}
// create Observable searchBar.text to pass to ViewModel
var searchTextObservable: Observable<String> {
let debounceValue = 200
// observable to get the incremental search text
let incrementalTextObservable = rx
.methodInvoked(#selector(UISearchBarDelegate.searchBar(_:shouldChangeTextIn:replacementText:)))
.debounce(.milliseconds(debounceValue), scheduler: MainScheduler.instance)
.flatMap { [unowned self] _ in Observable.just(self.searchBar.text ?? "") }
// observable to get the text when the clear button or enter are tapped
let textObservable = searchBar.rx.text.orEmpty.asObservable()
// merge these two above
let searchTextObservable = Observable.merge(incrementalTextObservable, textObservable)
.skip(1)
.debounce(.milliseconds(debounceValue), scheduler: MainScheduler.instance)
.distinctUntilChanged()
return searchTextObservable
}
func setupTableViewDataSource() {
dataSource = RxTableViewSectionedReloadDataSource<SectionModel>(configureCell: {(_, tableView, indexPath, item) in
let cell = tableView.dequeueReusableCell(withIdentifier: "ListCell") as! ListCell
cell.selectionStyle = .none
cell.backgroundColor = .clear
cell.configure(item: item)
return cell
})
}
func tableViewSetup() {
tableView.rx.itemDeleted
.subscribe {
print("delete")
}
.disposed(by: disposeBag)
viewModel.dispItems.asObservable()
.bind(to: tableView.rx.items(dataSource: dataSource))
.disposed(by: disposeBag)
}
func listDetailSetup() {
viewModel.itemCountObservable
.bind(to: itemCountLabel.rx.text)
.disposed(by: disposeBag)
viewModel.favCountObservable
.bind(to: favCountLabel.rx.text)
.disposed(by: disposeBag)
}
}
MainTabViewModel
final class MainTabViewModel {
private let disposeBag = DisposeBag()
private let userDefault: UserDefaultManager
var dispItems = BehaviorRelay<[SectionModel]>(value: [])
private let shared = Sharing.shared
// lazy var itemCount = shared.list[0].list.count
// lazy var favCount = shared.calcFavCount
var itemCountObservable: Observable<String>
var favCountObservable: Observable<String>
init(searchTextObservable: Observable<String>,
userDefault: UserDefaultManager = UserDefault()) {
self.userDefault = userDefault
let initialValue = shared.list
shared.listItems.accept(initialValue)
dispItems = shared.listItems
// this part is to display the initil number -> success
var itemCount = shared.list[0].list.count
itemCountObservable = .just(itemCount.description + " items")
var favCount = shared.calcFavCount(array: shared.list[0].list)
favCountObservable = .just(favCount.description)
// this part is based on the searching result -> failure
searchTextObservable.subscribe(onNext: { text in
if text.isEmpty {
let initialValue = self.shared.list
self.shared.listItems.accept(initialValue)
self.dispItems = self.shared.listItems
}else{
let filteredItems: [Item] = self.shared.list[0].list.filter {
$0.name.contains(text)
}
let filteredList = [SectionModel(list: filteredItems)]
self.shared.listItems.accept(filteredList)
self.dispItems = self.shared.listItems
itemCount = filteredItems.count
self.itemCountObservable = .just(itemCount.description + " items")
favCount = self.shared.calcFavCount(array: filteredItems)
self.favCountObservable = .just(favCount.description)
print("\(itemCount) items") // the ideal number is in but not shown in the view
}
})
.disposed(by: disposeBag)
}
}
I removed unnecessary code but I mostly pasted a whole code for your understanding.
Hope you could help me.
Thank you.
I solved this issue anyway; the value was reflected.
the issue was that itemCountObservable was declared as observable and .just was used.
How .just works is to throw onNext once and it is completed, which means the change I did in searchTextObservable.subscribe(onNext~ is unacceptable.
So I shifted the itemCountObservable: Observable<String> to BehaviorRelay<String>that only onNext is thrown and not completed, then it works.
My understanding of this issue is that itemCountObservable: Observable<String> stopped throwing a value due to .just as I've written above.
Am I correct??
If you're familiar with the difference between Observable and BehaviorRelay, it would be appreciated if you could tell me.
Thanks.
I have a question :
I would like to add values in a core data model with relationships between entities and display those values in a View with pickersView.
I have created an example to try to understand how to implement relationships between entities but now i am in stand-by ... I do not know how to use mutableSetValueForKey, valueforkey, setvalue(_:forkey) in my model. in my example, devices or cards are linked to an upper item.
my coreData model : 3 entities : User, Device and Card
User - Device (nn) : n devices to n users
Device - card (1n) : 1 device can have several cards but one card is only for one dedicated device
See image of the model:
CoreData Model
To populate, i have a view with several fields, Pickers, buttons :
Usertexfield + add button to add new user
Picker of users (display / sélect users) + devicetextfield + add button to add new device to a user
Picker of device (display /sélect device) + cardtextfield + add button to add new card to a Device
Picker of cards (display / sélect cards)
delete device button to remove a device
delete card button to remove a card
I would like also to be able to modify a device or card when selected (not Made)
Question :
How to create/modify/delete a Device associated to a user (how to implement the relationship between those 2 entities in the fetchrequest ?
here is my code (not finish at all ...) : see addDeviceButton function (and addCardButton as well if possible)
// createRelationshipsViewController.swift
// testcoredatarelationships
//
// Created by jIOP on 23/03/2016.
// Copyright © 2016. All rights reserved.
//
import UIKit
import CoreData
class createRelationshipsViewController: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource {
// User
#IBOutlet weak var pickerUser: UIPickerView!
#IBOutlet weak var userNameTextField: UITextField!
#IBOutlet weak var boutonAddUser: UIButton!
// Device
#IBOutlet weak var pickerDevice : UIPickerView!
#IBOutlet weak var deviceNameTextField: UITextField!
#IBOutlet weak var boutonAddDevice: UIButton!
#IBOutlet weak var deleteActivite: UIButton!
//Card
#IBOutlet weak var pickerCard : UIPickerView!
#IBOutlet weak var carteNameTextField: UITextField!
#IBOutlet weak var boutonAddcard: UIButton!
#IBOutlet weak var deleteCard: UIButton!
//Actions
var tabpickerUser=[]
var tabpickerDevice=[]
var tabpickerCard=[]
override func viewDidLoad() {
super.viewDidLoad()
// init PickerView User only -others can't be at this time
let MOC = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext
let fetchRequest = NSFetchRequest(entityName: "User")
var requestError: NSError?
/* And exécute the fetch request on the context */
usersInCD = (try! MOC.executeFetchRequest(fetchRequest)) as! [User]
// usersInCD is a list of Users
// tbd --> if nil picker is empty else init tabpickerUser with usersInCD.Name.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func addUserBoutonAction(sender: AnyObject) {
let MOCUser = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext
let newUser = NSEntityDescription.insertNewObjectForEntityForName("User",inManagedObjectContext: MOCUser) as! User
newUser.userId = NSUUID().UUIDString
newUser.userName = userNameTextField.text // new user textfield
var savingError: NSError?
do {
try MOCUser.save()
print("New User created")
} catch let error1 as NSError {
savingError = error1
if let error = savingError
{
print("Failed to populate the database User. Error = \(error)")
}
// Update UserPicker --> Tbd
}
#IBAction func addDeviceBoutonAction(sender: AnyObject) {
let MOCDevice = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext
// How to get userId from PickerView
// How to create a new device attached to this userId (predicate, mutableSetValueForKey, other ...)
let newDevice = NSEntityDescription.insertNewObjectForEntityForName("Device",inManagedObjectContext: MOCDevice) as! Device
newDevice.deviceId = NSUUID().UUIDString
newDevice.deviceName = deviceNameTextField.text
var savingError: NSError?
do {
try MOCDevice.save()
print("New device created")
} catch let error1 as NSError {
savingError = error1
if let error = savingError
{
print("Failed to populate the database Device. Error = \(error)")
}
}
#IBAction func addCardBoutonAction(sender: AnyObject) {
// same PB but with n--1 relationships between Device and Cards
// A card is associated to 1 device only / one device car have several cards
// usage of mutableSetValueForKey ?
}
#IBAction func deleteDeviceBoutonAction(sender: AnyObject) {
// How to delete device (device) --> device will delete the card ?
}
#IBAction func deleteCardBoutonAction(sender: AnyObject) {
// How to delete card, link to a device
}
/////////////////////////// Picker managment
func numberOfComponentsInPickerView(pickerView:UIPickerView) -> Int {
return 1
}
func pickerView(pickerView: UIPickerView,numberOfRowsInComponent component:Int) -> Int {
switch pickerView.tag {
case 1:
return tabpickerUser.count
case 2:
return tabpickerDevice.count
case 3:
return tabpickerCard.count
default:
print("Something else")
}
}
func pickerView(pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String! {
switch pickerView.tag {
case 1:
return tabpickerUser[row] as! String
case 2:
return tabpickerDevice[row]as! String
case 3:
return tabpickerCard[row]as! String
default:
print("Something else")
}
}
// Here add pickerView functions to update pickers views when user, device, card is added or remove.
// not done ...
/* Init pickerView Device and pickerView Card (how to do it ?)
Init pickerView Device when user is selected in UserpickerView
let MOC = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext
// predicate to match UserId in Entity Device ?? how to do it using the relationships ????
//let predicate = NSPredicate(format: "userId == %#", tabpickerUser[row].userId!) ???????
fetchRequest.predicate = predicate
var requestError: NSError?
/* And execute the fetch request on the context */
Devices = (try! MOC.executeFetchRequest(fetchRequest)) as! [Device]
*/
/* Init pickerViewCard when Device is selected in DevicepickerView
let MOC = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext
let fetchRequest = NSFetchRequest(entityName: "Card")
var requestError: NSError?
// And execute the fetch request on the context
Cards = (try! MOC.executeFetchRequest(fetchRequest)) as! [Card]
*/
}
Having issues saving my manage objects within my code. For some reason when i place data in the first view controller everything works well. For instance
I place new categories such as "Fruits", "Dairy", "Meats". The first view controller takes the data. When I click on the specific item such as "Dairy", and put in "Milk" for items within that section. If I go back to the previous view controller and click on "Meats", I see the same data i put in under "Dairy". How do i properly manage my NSManage objects.
Here is my code below.
import UIKit
import CoreData
class HomeSpecificItemViewController: UIViewController {
var selectedItem : [Items] = []
#IBOutlet weak var itemNameTextField: UITextField!
#IBOutlet weak var brandNameTextField: UITextField!
#IBOutlet weak var caloriesTextField: UILabel!
#IBOutlet weak var priceTextField: UILabel!
#IBOutlet weak var amountTextField: UITextField!
#IBOutlet weak var threshHoldNumberField: UITextField!
#IBOutlet weak var stepper: UIStepper!
override func viewDidLoad() {
super.viewDidLoad()
stepper.wraps = true
stepper.autorepeat = true
stepper.maximumValue = 10
// Do any additional setup after loading the view.
}
#IBAction func saveButton(sender: AnyObject) {
let context = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext
let itemDescription = NSEntityDescription.insertNewObjectForEntityForName("Items", inManagedObjectContext: context) as! Items
itemDescription.setValue(itemNameTextField.text, forKey: "Items")
itemDescription.setValue(brandNameTextField.text, forKey: "Items")
do {
try context.save()
}catch _ {
}
/*
let request = NSFetchRequest(entityName: "Items")
let results : [AnyObject]?
do {
results = try context.executeFetchRequest(request)
}catch _ {
results = nil
}
if results != nil {
self.itemDescription = results as! [Items]
}
*/
}
#IBAction func cancelPressed(sender: AnyObject) {
self.dismissViewControllerAnimated(true, completion: nil)
}
#IBAction func increaseNumberStepper(sender: UIStepper) {
threshHoldNumberField.text = Int(sender.value).description
}
}
Do you have a specific view controller for each category? If so, what you have to do is add predicates to your more specific view controllers.
Something like:
var request = NSFetchRequest(entityName: "Food")
request.predicate = NSPredicate(format: "category == %#", "Meat")
meats = try! context.executeFetchRequest(request)
This would return an array of all Food objects whose category atribute holds the string "Meat".
I was saving my data to core data without properly declaring the manage context and without assigning the text labels to the core data object.
issue resolved!