i work with swift 3 for osx
i added this Observer in my MasterViewController: NotificationCenter.default.addObserver(self, selector: #selector(requestData), name: NSNotification.Name(rawValue: "requestData"), object: nil)
my fun requestData in the same MasterViewController:
func requestData(with predicate : NSPredicate? = nil) {
let appdelegate = NSApplication.shared().delegate as! AppDelegate
let context = appdelegate.persistentContainer.viewContext
let request = NSFetchRequest<Person>(entityName: "Person")
request.predicate = predicate
print(predicate)
do {
people = try context.fetch(request)
tableView.reloadData()
} catch { }
}
if i now call this observer from another view controller like this:
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "requestData"), object: nil)
the print result "print(predicate)" will show:
Optional(NSConcreteNotification 0x608000246150 {name = requestData})
but this is wrong.
predicate have to be in this case nil.
what do i wrong?
Related
Similar questions have already been asked but they haven't helped. I'm trying to call protocol method declared in Singleton class from a view controller but the called method never invokes.
My protocol and class are:
import Foundation
protocol ServiceDelegate {
func serviceData(data: [String: Any])
}
class Service {
private static let sharedInstance = Service() // singleton object
var delegate: ServiceDelegate?
class func shared() -> Service {
return sharedInstance
}
func login(postData:String) {
let url = URL(string: "MY_URL")!
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
let dataBody = postData.data(using:String.Encoding.ascii, allowLossyConversion: false)
request.httpBody = dataBody
let task = URLSession.shared.dataTask(with: request) { (data, response, error) in
guard let data = data, error == nil else {
print(error?.localizedDescription ?? "No data")
return
}
let responseJSON = try? JSONSerialization.jsonObject(with: data, options: [])
if let dataArray = responseJSON as? [String: Any] {
self.delegate?.serviceData(data: dataArray) // This is getting called
}
}
task.resume()
}
}
My view controller class where protocol is implemented is:
import UIKit
class LoginVC: UIViewController, ServiceDelegate {
var serviceInstance: Service = Service()
override func viewDidLoad() {
super.viewDidLoad()
serviceInstance.delegate = self
}
#IBAction func loginBtn(_ sender: Any) {
let info = "userName=\(String(describing: usernameText.text!))&password=\(String(describing: passwordText.text!))"
Service.shared().login(postData: info)
}
// This is not getting called:
func serviceData(data: [String : Any]) {
print(data)
}
Is the above implementation correct? Can I use private singleton object to successfully implement protocol method?
You are creating two Service instances. You should only be using the one returned by Service.shared(). That’s the actual singleton shared across your app.
In your view controller, replace this line:
serviceInstance.delegate = self
with:
Service.shared().delegate = self
And you should also delete this line (i.e., the unnecessary second instance):
var serviceInstance: Service = Service()
i have a nstableview which i fill with data of core data.
NSManagedObject
import Foundation
import CoreData
#objc(Person)
public class Person: NSManagedObject {
#NSManaged public var firstName: String
#NSManaged public var secondName: String
}
RequestData
func requestData() {
let appdelegate = NSApplication.shared().delegate as! AppDelegate
let context = appdelegate.persistentContainer.viewContext
let request = NSFetchRequest<NSFetchRequestResult>(entityName: "Person")
do {
person = try context.fetch(request) as! [Person]
tableViewn.reloadData()
} catch { }
}
i also have a custom cell view for my tableView.
I fill the data like this:
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
let view = tableView.make(withIdentifier: "Cell", owner: self) as? CustomCell
view?.txtfirstName.stringValue = person.firstName
view?.txtsecondName.stringValue = person.secondName
return view
}
Now i would like to realize a searchbar (which i have already in my view controller) for searching with first or second name.
but i have no idea how i can realize this.
Set the delegate of the NSSearchField to the target class
implement controlTextDidChange
override func controlTextDidChange(_ notification: Notification) {
if let field = notification.object as? NSSearchField {
let query = field.stringValue
let predicate : NSPredicate?
if query.isEmpty {
predicate = nil
} else {
predicate = NSPredicate(format: "firstName contains[cd] %# OR lastName contains[cd] %#", query, query)
}
requestData(with: predicate)
} else {
super.controlTextDidChange(notification)
}
}
Change requestData to
func requestData(with predicate : NSPredicate? = nil) {
let appdelegate = NSApplication.shared().delegate as! AppDelegate
let context = appdelegate.persistentContainer.viewContext
let request = NSFetchRequest<Person>(entityName: "Person")
request.predicate = predicate
do {
person = try context.fetch(request)
tableViewn.reloadData()
} catch { }
}
Side note:
If you are using NSManagedObject subclasses create the fetch request more specific NSFetchRequest<Person>(entityName... rather than NSFetchRequest<NSFetchRequestResult>(entityName..., that avoids the type cast in the fetch line.
I am getting this error which I cannot explain:
#IBOutlet weak var licencePlateLabel: UILabel!
var editItem: CarValues? {
didSet {
// Update the view.
self.configureView()
}
}
func configureView() {
// Update the user interface for the detail item.
if let editedCar = self.editItem {
if let licencePlate = self.licencePlateLabel {
licencePlate.text? = editedCar.licencePlate!//this gives me nil
}
else {
print("value was nil")
}
print(editedCar.licencePlate!)//this is giving me the correct value
}
if I replace the
if let licencePlate = self.licencePlateLabel {
licencePlate.text! = editedCar.licencePlate!
}//this throws an error "found nil......"
even if I do this I m still getting the "found nil..."
func configureView() {
licencePlateLabel.text = "test"
[...]
}
BUT if I put the above on viewDidLoad then it works fine
override func viewDidLoad() {
licencePlateLabel.text = "test"
[...]
}
What is going on in this code?
EDIT
I am passing the value of the editItem from the detailView to the EditView like this:
#IBAction func editButtonTapped(sender: AnyObject) {
let storyBoard = UIStoryboard(name: "Main", bundle:nil)
let editScreen = storyBoard.instantiateViewControllerWithIdentifier("ID_EditViewController")
self.navigationController?.pushViewController(editScreen, animated: true)
let controller = EditViewController()
controller.editItem = detailItem
controller.navigationItem.leftBarButtonItem = self.splitViewController?.displayModeButtonItem()
controller.navigationItem.leftItemsSupplementBackButton = true
}
You don't unwrap properties to set them, only to read from them. So your code can simply be:
if let licencePlate = self.licencePlateLabel {
licencePlate.text = editedCar.licencePlate
}
Note that because licencePlate.text is an optional value anyway, there is also no need to unwrap editedCar.licencePlate. It's ok to use its value whether it is nil or contains a String.
I was just wondering how would I be able to use a searched barcode to fetch using Core Data in Swift. I'm basically passing a barcode to a static func method, but how would I be able to use that to fetch the data from the Core Data?
Here is the barcode when detected:
func barcodeDetected(code: String) {
// Let the user know we've found something.
let alert = UIAlertController(title: "Found a Barcode!", message: code, preferredStyle: UIAlertControllerStyle.Alert)
alert.addAction(UIAlertAction(title: "Search", style: UIAlertActionStyle.Destructive, handler: { action in
// Remove the spaces.
let trimmedCode = code.stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceCharacterSet())
// EAN or UPC?
// Check for added "0" at beginning of code.
let trimmedCodeString = "\(trimmedCode)"
var trimmedCodeNoZero: String
if trimmedCodeString.hasPrefix("0") && trimmedCodeString.characters.count > 1 {
trimmedCodeNoZero = String(trimmedCodeString.characters.dropFirst())
// Send the doctored barcode
ProductDetailsViewController.searchCode(trimmedCodeNoZero)
} else {
// Send the doctored barcode
ProductDetailsViewController.searchCode(trimmedCodeString)
}
self.navigationController?.popViewControllerAnimated(true)
}))
self.presentViewController(alert, animated: true, completion: nil)
}
My Product Class:
import UIKit
import Foundation
import CoreData
class ProductDetailsViewController: UIViewController, NSFetchedResultsControllerDelegate {
#IBOutlet weak var productLabel: UILabel!
#IBOutlet weak var priceLabel: UILabel!
#IBAction func addProduct(sender: AnyObject) {
let AppDel = UIApplication.sharedApplication().delegate as? AppDelegate
let context:NSManagedObjectContext = (AppDel?.managedObjectContext)!
let ent = NSEntityDescription.entityForName("Products", inManagedObjectContext: context)
var newProduct = ProductItem(entity: ent!, insertIntoManagedObjectContext: context)
newProduct.title = productLabel.text
//newProduct.price = priceLabel.text
/*context.save(nil)
print(newProduct)
print("Object Saved")*/
}
private(set) var PRODUCT_NAME = ""
private(set) var PRODUCT_PRICE = ""
private var menuItems:[ProductItem] = []
static func searchCode(codeNumber: String) -> String{
let barcodeNumber = codeNumber
return barcodeNumber
}
deinit{
NSNotificationCenter.defaultCenter().removeObserver(self)
}
override func viewDidLoad() {
super.viewDidLoad()
productLabel.text = "Scan a Product"
priceLabel.text = ""
NSNotificationCenter.defaultCenter().addObserver(self, selector: "setLabels:", name: "ProductNotification", object: nil)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
I already added the items into Core Data successfully and was able to load all items into a table in my app. Now with the barcode scanned I want to be able to just load the products with the barcode and i'm stuck on that part. As you can see my static fun searchCode is receiving the barcode from barcodeDetected but what should I do next to fetch it? Thanks.
EDIT:
Core Data Entity
import Foundation
import CoreData
#objc(ProductItem)
class ProductItem: NSManagedObject{
#NSManaged var barcodeNum:String?
#NSManaged var box_height:NSNumber?
#NSManaged var box_length:NSNumber?
#NSManaged var box_width:NSNumber?
#NSManaged var price:NSNumber?
#NSManaged var sku:String?
#NSManaged var weight:NSNumber?
#NSManaged var title:String?
}
To fetch the correct ProductItem, you need to use a predicate (see the Apple Documentation here). In your case, you could use something like this:
let AppDel = UIApplication.sharedApplication().delegate as? AppDelegate
let context:NSManagedObjectContext = (AppDel?.managedObjectContext)!
let fetchRequest = NSFetchRequest(entityName: "ProductItem")
fetchRequest.predicate = NSPredicate(format: "barcodeNum == %#",codeNumber)
let results = try! context.executeFetchRequest(fetchRequest) as! [ProductItem]
if results.count > 0 { // great, you found (at least one) matching item
let scannedProduct = results[0]
// from here you can access the attributes of the product
// such as title, price, sku, etc.
...
} else { // not found
...
}
Note that I've use try! for brevity, but in practice you should use proper do ... catch syntax and handle any errors.
I'm not clear why you are using a static func in the ProductDetailsViewController; a common approach would be to use the above fetch within your barcodeDetected method, and then to segue to the ProductDetailsViewController passing the relevant ProductItem for display/editing or whatever. Or to display an alert view if the product was not found.
How to define objects in the code and of which type?
override func viewDidLoad() {
super.viewDidLoad()
let appDelegate=UIApplication.sharedApplication().delegate as! AppDelegate
let context=appDelegate.managedObjectContext
let request=NSFetchRequest(entityName:lineEntityName)
do{
let objects = try context.executeFetchRequest(request)
}
catch let error as NSError {
print(error)
}
if let objectList=objects
{
for oneObject in objectList
{
let lineNum=oneObject.valueForKey(lineNumberKey) as integerValue
let lineText=oneObject.valueForKey(lineTextKey) as String
let lineField=lineFields(lineNum)
textField.text=lineText
}
}
else
{
print("There was an Error")
}
let app=UIApplication.sharedApplication()
NSNotificationCenter.defaultCenter().addObserver(self, selector:"applicationWillResignActiveNotification", name: UIApplicationWillResignActiveNotification, object: app)
// Do any additional setup after loading the view, typically from a nib.
}
The recommended way is to put all good code in the do clause which solves the problem.
And executeFetchRequest returns a non-optional array so the optional binding can be omitted.
override func viewDidLoad() {
super.viewDidLoad()
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let context = appDelegate.managedObjectContext
let request = NSFetchRequest(entityName:lineEntityName)
do {
let objects = try context.executeFetchRequest(request)
for oneObject in objects
{
let lineNum = oneObject.valueForKey(lineNumberKey) as integerValue
let lineText = oneObject.valueForKey(lineTextKey) as String
let lineField = lineFields(lineNum)
textField.text = lineText
}
let app = UIApplication.sharedApplication()
NSNotificationCenter.defaultCenter().addObserver(self, selector:"applicationWillResignActiveNotification", name: UIApplicationWillResignActiveNotification, object: app)
// Do any additional setup after loading the view, typically from a nib.
}
catch let error as NSError {
print(error)
}
}