I'm an iOS newbie developer. I'm trying to import the results of an executeFetchRequest into a structure I created for viewing later into a table. I"m getting "Array index out of range" in func getTasks(), I'm pretty sure I"m supposed to append it, but not sure quite how.
I'm sure there's a better way of setting this up in general. Right now I'm just trying to get things to work. But other suggestions would be appreciated.
import UIKit
import CoreData
var taskMgr: TaskManager = TaskManager()
struct task {
var name = "Un-Named"
var desc = "Un-Described"
}
class TaskManager: NSObject {
var tasks = task[]()
init() {
super.init()
self.getTasks()
}
func addTask(name: String, desc: String) {
tasks.append(task(name: name, desc: desc))
let appDel:AppDelegate = UIApplication.sharedApplication().delegate as AppDelegate
let context:NSManagedObjectContext = appDel.managedObjectContext
let ent = NSEntityDescription.entityForName("Tasks", inManagedObjectContext: context)
var newTask = Tasks(entity: ent, insertIntoManagedObjectContext: context)
newTask.name = name
newTask.desc = desc
println("Object saved")
context.save(nil)
}
func getTasks() {
let appDel:AppDelegate = UIApplication.sharedApplication().delegate as AppDelegate
let context:NSManagedObjectContext = appDel.managedObjectContext
var request = NSFetchRequest(entityName: "Tasks")
request.returnsObjectsAsFaults = false;
var results:NSArray = context.executeFetchRequest(request, error: nil)
if (results.count > 0) {
self.tasks = task[]()
var i = 0
for element in results {
tasks[i].name = element.name // fatal error: Array index out of range
tasks[i].desc = element.desc
i++
}
}
}
}
class Tasks: NSManagedObject {
#NSManaged var name: String
#NSManaged var desc: String
}
You can't use subscripting to add items to an array -- you need to call append() or use the += operator instead. Try this:
self.tasks = task[]()
for element in results {
tasks += task(name: element.name, desc: element.desc)
}
Related
My code below fetches Core Data. The problem is that the Core Data fetch should display the names. Example "Ron", "Paul", "Joe". Instead it will just be 3 for the number of items in the array. How can I get the name printed on the label?
class tv: UIViewController {
var itemsName : [NSManagedObject] = []
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
let appD = UIApplication.shared.delegate as! AppDelegate
let context = appD.persistentContainer.viewContext
let FetchRequest = NSFetchRequest<NSManagedObject>(entityName: "Team")
do {
itemsName = try context.fetch(FetchRequest)
}catch {
print("Ashley Tisdale")
}
geroge.text = String(itemsName.count)
view.backgroundColor = UIColor.brown
}
#IBOutlet var geroge: UILabel!
}
You are actually printing the number of items in the array.
Also you should change the FetchRequest like this:
let FetchRequest = Team.fetchRequest()
Now the itemsName is [Team] type.
Now create an array to store the names from the results like this:
var nameArray:[String] = []
for item in itemsName {
nameArray.append(item.name) // here I assume the Team object has the key name which you want to retrieve
}
Now you can have it printed:
george.text = "\(nameArray)"
Edit
Replace this:
let appD = UIApplication.shared.delegate as! AppDelegate
let context = appD.persistentContainer.viewContext
let FetchRequest = NSFetchRequest<NSManagedObject>(entityName: "Team")
do {
itemsName = try context.fetch(FetchRequest)
}catch {
print("Ashley Tisdale")
}
geroge.text = String(itemsName.count)
With this:
let appD = UIApplication.shared.delegate as! AppDelegate
let context = appD.persistentContainer.viewContext
let FetchRequest:NSFetchRequest<Team> = Team.fetchRequest()
do {
itemsName = try context.fetch(FetchRequest)
}catch {
print("Ashley Tisdale")
}
var nameArray:[String] = []
for item in itemsName {
nameArray.append(item.lorde)
}
geroge.text = "\(nameArray)"
And change data type of itemsName from [NSManagedObjects] to [Team] like this:
var itemsName:[Team] = []
I'm using Core Data entity with attributes, here is generated subclass code:
extension City {
#nonobjc public class func fetchRequest() -> NSFetchRequest<City> {
return NSFetchRequest<City>(entityName: "City")
}
#NSManaged public var name: String?
#NSManaged public var description: String?
#NSManaged public var temp: Double
#NSManaged public var temp_max: Double
#NSManaged public var temp_min: Double
}
I'm parsing JSON data and handle it through Weather model, and save the data into the database using this code (it's work well):
func saveWeatherData() {
let ad = UIApplication.shared.delegate as! AppDelegate
let context = ad.persistentContainer.viewContext
let city = City(context: context)
city.name = Weather.locationName!
city.description = Weather.details!
city.temp = Weather.temp
city.temp_min = Weather.tempMin
city.temp_max = Weather.tempMax
ad.saveContext()
}
The question is... how can I check the coincidence of city names (name attribute)? and if such a city already exists in the database, instead of creating a new record, overwrite (update) the values of current attributes (description, temp, temp_max, temp_min)?
Thanks.
You simply need to try and fetch an existing object. If the fetch succeeds, update its properties. If a match isn't found then create a new object. Something like:
func saveWeatherData() {
let ad = UIApplication.shared.delegate as! AppDelegate
let context = ad.persistentContainer.viewContext
let fetchRequest:NSFetchRequest<City> = City.fetchRequest()
fetchRequest.predicate = NSPredicate(format:"name = %#",Weather.locationName)
fetchRequest.fetchLimit = 1
do {
let result = try context.fetch(fetchRequest)
let city = result.first ?? City(context: context)
city.name = Weather.locationName!
city.description = Weather.details!
city.temp = Weather.temp
city.temp_min = Weather.tempMin
city.temp_max = Weather.tempMax
ad.saveContext()
}
catch {
print("Error fetching city: \(error.localizedDescription)"
}
}
There is a code containing description of Dictionary struct and receiving corresponding entries from Core Data
What string(s) should I insert before last bracket to cast NSArray to array of dictionaries?
struct Dictionary {
var name: String
var enableDirect: Bool
var enableReverse: Bool
init(name: String, enableDirect: Bool, enableReverse: Bool) {
self.name = name
self.enableDirect = enableDirect
self.enableReverse = enableReverse
}
}
func loadDictionariesFromStore() -> [Dictionary] {
var appDel: AppDelegate = (UIApplication.sharedApplication().delegate as! AppDelegate)
var context:NSManagedObjectContext = appDel.managedObjectContext!
var request = NSFetchRequest(entityName: "Dictionaries")
request.returnsObjectsAsFaults = false
var results:NSArray = context.executeFetchRequest(request, error: nil)!
}
If you are sure that the NSArray is of type [Dictionary] you can make a forced downcast of results and return it:
return results as! [Dictionary]
// or directly return it
return context.executeFetchRequest(request, error: nil)! as! [Dictionary]
Core Data works great for the most part. When I click on name first VC (Items) and performSeque to the second VC (Costs), I can see the costsName and other data. But when I add second name in first VC I can see the same data as in first name.
I'm trying to make a one to many relationship.
I have 2 data models:
import Foundation
import CoreData
#objc(Items)
class Items: NSManagedObject {
#NSManaged var count: NSNumber
#NSManaged var name: String
#NSManaged var cost: NSSet
}
import Foundation
import CoreData
#objc(Costs)
class Costs: NSManagedObject {
#NSManaged var costsDate: NSDate
#NSManaged var costsName: String
#NSManaged var costsValue: NSNumber
#NSManaged var account: Items
}
Here is addAccount's (name of the first VC) save action:
#IBAction func saveButtonPressed(sender: UIBarButtonItem) {
let appDelegate = (UIApplication.sharedApplication().delegate as AppDelegate)
var managedObjectContext = appDelegate.managedObjectContext
let entityDescription = NSEntityDescription.entityForName("Items", inManagedObjectContext: managedObjectContext!)
let account = Items(entity: entityDescription!, insertIntoManagedObjectContext: managedObjectContext!)
account.name = cellOneNameTextField.text
if cellTwoCountTextField.text.isEmpty {
} else {
account.count = (cellTwoCountTextField.text).toInt()!
}
// Saving data
appDelegate.saveContext()
var request = NSFetchRequest(entityName: "Items")
var error:NSError? = nil
var results:NSArray = managedObjectContext!.executeFetchRequest(request, error: &error)!
self.navigationController?.popViewControllerAnimated(true)
}
Here is addCost's save action:
#IBAction func saveButtonTapped(sender: UIBarButtonItem) {
// CoreData Access
let appDelegate = (UIApplication.sharedApplication().delegate as AppDelegate)
var managedObjectContext = appDelegate.managedObjectContext
let entityDescription = NSEntityDescription.entityForName("Costs", inManagedObjectContext: managedObjectContext!)
let cost = Costs(entity: entityDescription!, insertIntoManagedObjectContext: managedObjectContext!)
cost.costsName = cellThreeNoteTextField.text
cost.costsValue = (cellOnePriceTextField.text).toInt()!
cost.costsDate = datePicker.date
// Saving data
appDelegate.saveContext()
var request = NSFetchRequest(entityName: "Costs")
var error:NSError? = nil
var results:NSArray = managedObjectContext!.executeFetchRequest(request, error: &error)!
for res in results {
println(res)
}
delegate?.refreshTable()
self.navigationController?.popViewControllerAnimated(true)
}
I don't know if you do it somewhere, but your Items should attach a Count object to itself using your cost variable from your Items. Something like :
let account = Items(...)
let cost = Cost(...)
account.cost.addObject(cost)//and changing your var cost:NSSet into var cost:NSMutableSet
//then save Items
(I haven't tried the addObject but you understand the principle)
I'm new to Swift and iOS development. This is probably a simple misconception on my part.
I'm using core data. I have four attributes in my entity:
"title" type String
"longitude", "latitude" type Decimal
"timestamp" type Date
In the code below I'm getting the error "'[Locations]' is not convertible to '()'".
import UIKit
import CoreData
#objc(Locations)
class Locations: NSManagedObject {
#NSManaged var title:String
#NSManaged var longitude:Double
#NSManaged var latitude:Double
#NSManaged var timestamp:NSDate
class func getAllLocations(){
let appDel:AppDelegate = (UIApplication.sharedApplication().delegate as AppDelegate)
let context:NSManagedObjectContext = appDel.managedObjectContext!
var request = NSFetchRequest(entityName: "Locations")
request.returnsObjectsAsFaults = false
var results:NSArray = context.executeFetchRequest(request, error: nil)!
return results as [Locations]
}
}
The compiler complains because you did not specify a return type for your function,
it should be
class func getAllLocations() -> [Locations] { ... }
There are also some unnecessary type annotations, and all the objects can be
declared as constants:
class func getAllLocations() -> [Locations] {
let appDel = (UIApplication.sharedApplication().delegate as AppDelegate)
let context = appDel.managedObjectContext!
let request = NSFetchRequest(entityName: "Locations")
request.returnsObjectsAsFaults = false
let results = context.executeFetchRequest(request, error: nil)!
return results as [Locations]
}
Note also that
let results = context.executeFetchRequest(request, error: nil)!
will throw a runtime exception if the executing the fetch request failed.
You should use an optional binding and some error handling or fallback,
for example
if let results = context.executeFetchRequest(request, error: nil) as? [Locations] {
return results
} else {
// Failed, return empty list. (Alternatively, report error.)
return []
}