In an attempt to port some small existing code to Swift I'm running into a bit of a problem.
I can do the following in Objective-C
NSMutablArray *myMutableArray = [NSMutableArray array];
myMutablArray = [self.managedObjectContext executeFetchRequest:fetchRequest error:&error].mutableCopy;
However in my attempt at trying this in swift I get 'Could not find member executeFetchRequest'
class exampleTableViewController: UITableViewController {
var managedObjectContext: NSManagedObjectContext? = nil
var myMutableArray: NSMutableArray = NSMutablArray()
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
var request = NSFetchRequest(entityName: "TestTable")
var error: NSError? = nil
self.myMutableArray = self.managedObjectContext!.executeFetchRequest(request, error: &error)
}
...
}
Could someone please point me in the right direction?
Many thanks
EDIT: Pasted code as it looks in xcode.
The error is kind of misleading.
It is saying in full "I don't know any functions which return a mutable array"
Swift is type safe and the compiler enforces that with enthusiasm.
Change the type of your var to NSArray , which is what the function executeFetchRequest returns.
class exampleTableViewController: UITableViewController {
var managedObjectContext: NSManagedObjectContext? = nil
var myArray: NSArray? = nil;
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
var request = NSFetchRequest(entityName: "TestTable")
var error: NSError? = nil
self.myArray = self.managedObjectContext!.executeFetchRequest(request, error: &error)
}
}
Should you want a mutable array you will need to do an explicit cast
let results = self.managedObjectContext!.executeFetchRequest(request, error: &error)
myMutableArray = NSMutableArray(array: results);
You do not need to initialize the array before you call executeFetchRequest, the call will return an array and destroy the one you created already.
I do not see your creation of error and it appears you have a "local" managedObjectContext variable yet you are attempting to reference self. Try:
self.myMutableArray = managedObjectContext!.executeFetchRequest(request, error: &error)
Assuming there is no code between the two lines you posted in that second code block.
Related
I have been using this following thing to do my plist and save data and display contents of plist in a Table View. I am using a NSMutableArray to fetch the contents of the array and display it in the table view . VC1 is my table view & VC2 is my EnterDetail VC So I have declared my code as follows -
class ListTableViewController: UITableViewController
{
var arr = NSMutableArray()
var plistfinalpath = String()
override func viewDidLoad()
{
super.viewDidLoad()
}
override func didReceiveMemoryWarning()
{
super.didReceiveMemoryWarning()
}
override func viewDidAppear(animated: Bool)
{
let path = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, .UserDomainMask, true)[0]
plistfinalpath = path.stringByAppendingString("/login.plist")
print(plistfinalpath)
arr = (NSMutableArray(contentsOfFile: plistfinalpath))!
print("time of appearing\(arr)")
/* if let temparray = NSMutableArray(contentsOfFile: plistfinalpath)
{
arr = temparray
}*/
self.tableView.reloadData()
}
So as you can see that I have declared a 'arr' as a NSMutableArray type and it is not optional. And when I use in my didappear() the compiler forces me to force unwrap it. Why is that so why does it happens evenif I have declared 'arr' as non optional? And here comes the real problem whenever I run my app for the first time the plist is empty implies 'arr' is also empty. And since I have used forced unwrapping my app crashes due to fatal error. And if I uncomment my commented line in the above code, the if let part, it works fine . So any suggestions. Thanks.
there is no trouble with your NSMutableArray.
var arr = NSMutableArray()
create non optional instance of an array. so far, so good ... An expression
NSMutableArray(contentsOfFile: plistfinalpath)
can return nil, so it returns the optional value. Here you try to create another instance of NSMutableArray and assign the result to your var arr.
A mutable array containing the contents of the file specified aPath.
Returns nil if the file can’t be opened or if the contents of the file
can’t be parsed into a mutable array.
Probably the best way for you is something like
import Foundation
var arr = NSMutableArray(contentsOfFile: "somePath") ?? NSMutableArray()
Here your variable is guaranty to have the default value (an empty instance of NSMutableArray) if the file does not exist yet (or other failure happened)
To resolve your fatal error check your file exist or not :-
//Check whether your file exist or not
let path = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, .UserDomainMask, true)[0]
plistfinalpath = path.stringByAppendingString("/login.plist")
if NSFileManager.defaultManager().fileExistsAtPath(plistfinalpath)
{
arr = (NSMutableArray(contentsOfFile: plistfinalpath))!
}
Declare it like
var arr : NSMutableArray = []
// Or
var arr : NSMutableArray!
I've come across a memory leak when I make a Core Data fetch request using Swift. However, I make an almost identical fetch request in a different part of the app, but it doesn't cause a leak. In both cases, the fetch requests are made in viewDidLoad of a view controller, and the results of the fetch request are assigned to an optional property of the view controller.
Here's the method for the fetch request that does not cause any leaks:
class LocationFilter {
//Lots of other code...
class func getAllPlacesOfRegionType<T: Region>(regionType: RegionType) -> [T] {
let fetchRequest = NSFetchRequest(entityName: regionType.rawValue)
var places: [T]
do {
places = try CoreDataStack.sharedInstance.context.executeFetchRequest(
fetchRequest) as! [T]
} catch let error as NSError {
NSLog("Fetch request failed: %#", error.localizedDescription)
places = [T]()
}
places.sortInPlace({ (firstPlace, nextPlace) -> Bool in
//Ingenious sorting code...
})
return places
}
}
This method is called in viewDidLoad of a viewController, and the result is assigned to the property var allRegions: [Region]? without any leaks. Here's the code:
class PlacesTableViewController: UITableViewController {
var allRegions: [Region]?
#IBOutlet weak var segmentedRegions: UISegmentedControl!
#IBAction func selectRegionSegment(sender: UISegmentedControl) {
// When the segmented control is tapped, the appropriate list will be loaded.
switch sender.selectedSegmentIndex {
case 0: //Country Segment
allRegions = LocationFilter.getAllPlacesOfRegionType(RegionType.Country)
case 1: //States segment
allRegions = LocationFilter.getAllPlacesOfRegionType(RegionType.Province)
case 2: //Cities segment
allRegions = LocationFilter.getAllPlacesOfRegionType(RegionType.City)
case 3: //Addresses segment
allRegions = LocationFilter.getAllPlacesOfRegionType(RegionType.Address)
default:
break
}
// Then reload the cells with animations.
let index = NSIndexSet(index: 0)
tableView.reloadSections(index, withRowAnimation: UITableViewRowAnimation.Automatic)
}
override func viewDidLoad() {
super.viewDidLoad()
selectRegionSegment(segmentedRegions)
}
}
The following method is called in viewDidLoad of a different viewController to set the property var allDays: [Day]!.
class DateFilter {
//Lots of other code in the class...
class func getAllDays() -> [Day] {
let fetchRequest = NSFetchRequest(entityName: "Day")
let days: [Day]
do {
days = try CoreDataStack.sharedInstance.context.executeFetchRequest(
fetchRequest) as! [Day]
} catch let error as NSError {
NSLog("Fetch request failed: %#", error.localizedDescription)
days = [Day]()
}
return days
}
}
This is where it is called:
class SearchViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var allDays: [Day]!
override func viewDidLoad() {
super.viewDidLoad()
allDays = DateFilter.getAllDays()
let backgroundView = UIView(frame: CGRectZero)
tableView.tableFooterView = backgroundView
tableView.backgroundColor = UIColor.groupTableViewBackgroundColor()
}
}
Xcode instruments detect a memory leak when this is called. Supposedly the responsible library is libswiftFoundation.dylib, and the responsible frame is static Array<A>._forceBridgeFromObjectiveC<A>(NSArray, result:inout [A]?) -> (). When I look at Cycles & Roots, it shows an NSArray at the root, with +16 list: (null) and +24 [no ivar]: (null) branching off.
Am I doing something wrong with how I store the results of my fetch request? Or is this a bug in how Swift interacts with Core Data?
Edit: Tidied up code in accordance with Mundi's suggestion.
Edit 2: Added code that calls the fetch request functions.
After trying lots of things, I'm pretty sure it's a bug in how Core Data converts NSArray to Swift Arrays when fetching my Day entities. Perhaps it has to do with the relationships or attributes of the Day entities. I'll continue to look into it.
For now, I found a work around. Instruments kept pointing back to the libswiftFoundation method for converting NSArray to Array, and the Cycles & Roots kept showing an NSArray with no ivar. Based on my research this has to do with the initialization of the NSArray created by the fetch request, which is converted behind the scenes to a Swift array. Since I can't change this, I made a new Array from the fetch results:
override func viewDidLoad() {
super.viewDidLoad()
let fetchResults: [Day] = DateFilter.getAllDays()
allDays = fetchResults.map({$0})
let backgroundView = UIView(frame: CGRectZero)
tableView.tableFooterView = backgroundView
tableView.backgroundColor = UIColor.groupTableViewBackgroundColor()
}
And magically, the memory leak is gone! I'm not entirely sure why the fetchResults array is leaky, but that seems to be the source of the problem.
Just experienced this exact same issue. I went around adding weak self to every capture list, to no avail. Turned out to be a name space collision where the system failed to infer which array (or maybe its type) I was talking about (because they shared the same name). I renamed the below 'foundCategories' from 'categories' which was a property of my view controller.
func fetchCategoriesAndNotes() {
categories = []
fetchEntity("Category", predicates: nil) { [weak self] (found) -> Void in
guard let foundCategories = found as? [Category] else { return }
for category in foundCategories {} ... } }
Memory leak is gone.
I think your fetch code is overly verbose. In particular, I think that assigning the fetch result to another variable causes some sort of conversion from Objective-C class NSArray (which is the result type of a fetch request) which in some way causes your leak. (I also do not fully understand why but I think it also has to do with the fact that this is a class function defining variables.)
I would suggest simplifying your code.
let request = NSFetchRequest(entityName: "Day")
do { return try context.executeFetchRequest(request) as! [Day]) }
catch { return [Day]() }
I am fetching the entities from Core Data, but I can´t access the object values to assign a variable etc.
This is my code:
#IBAction func loadItem(sender: UIButton) {
let appDel: AppDelegate = (UIApplication.sharedApplication().delegate as! AppDelegate)
let context: NSManagedObjectContext = appDel.managedObjectContext
let request = NSFetchRequest(entityName: "Words")
request.returnsObjectsAsFaults = false
do {
let results:NSArray = try context.executeFetchRequest(request)
for res in results{
print(res)
print(res.word)
}
} catch {
print("Unresolved error")
abort()
}
}
print(res) works fine and give me the object in the console:
<NSManagedObject: 0x7ff0095355f0> (entity: Words; id: 0xd000000000040000 <x-coredata://0E645702-9493-4D1A-8D55-4482B7948054/Words/p1> ; data: {
image = <89504e47 0d0a1a0a 0000000d 49484452 00000a6c 000006ec 08020000 009fcbb9 fc000001 18694343 50494343 2050726f 6669>;
word = test;
})
But how can I access for instance the "word" value? It only returns this error:
"ViewController.swift:36:23: Value of type 'Element' (aka 'AnyObject') has no member 'word'"
I´m trying to cast "res" to be a NSManagedObject, but I can´t seem to make it work.
Any help?
You have two options. Either use Key-Value Coding:
let results = try context.executeFetchRequest(request)
for res in results {
print(res)
print(res.valueForKey("word"))
}
Or (better) use "Xcode->Editor->Create NSManagedObject subclass ...".
In Xcode 7 this will add two files "Words.swift" and "Words+CoreDataProperties.swift" to your project.
The first file
"Words.swift" defined the Word class (and you can extend the class
definition e.g. to add custom methods).
The second file "Words+CoreDataProperties.swift"
contains property definitions for all your Core Data properties,
for example:
extension Words {
#NSManaged var word: String?
// ...
}
And now you can cast the objects from the fetch request to the Word type and access the properties directly:
let results = try context.executeFetchRequest(request) as! [Words]
for res in results {
print(res)
print(res.word)
}
I keep getting the following error :
fatal error: unexpectedly found nil while unwrapping an Optional value
and I guess I just don't understand why. Can someone please help me find my mistake? Is it that variable results is optional?
The error keeps pointing to a line in viewDidLoad(), I commented where. Thanks.
//
// ViewController.swift
// Physics Help!
//
// Created by Sam Hanson on 2/8/15.
// Copyright (c) 2015 Sam Hanson. All rights reserved.
//
import UIKit
import CloudKit
class ViewController: UIViewController {
//VARIABLES********************************************************
#IBOutlet var c1Answer: UILabel!
#IBOutlet var questions: UILabel!
var resultsOfDB : String = ""
var indexes : [Int] = []
var counter : Int = 0
var newStr : String = ""
//*****************************************************************
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.questions.text = String(getNewQbutton()) //error points to here*********
}
//load the answers, grab them from the cloud base
#IBAction func loadAnswers() {
let container = CKContainer.defaultContainer()
var publicDB = container.publicCloudDatabase
let myQuery = CKQuery(recordType: "QuestionsTable", predicate: NSPredicate(value: true))
publicDB.performQuery(myQuery, inZoneWithID: nil){
results, error in
if error != nil {
println(error)
}
else
{
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.c1Answer.text = results.description
println(results.description)
})
}
}
}
#IBAction func getNewQbutton() {
let container = CKContainer.defaultContainer()
var publicDB = container.publicCloudDatabase
let myQuery = CKQuery(recordType: "QuestionsTable", predicate: NSPredicate(value: true))
publicDB.performQuery(myQuery, inZoneWithID: nil){
results, error in
if error != nil {
println(error)
}
else
{
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.resultsOfDB = results.description
//for each character in resultsOfDB
for character in self.resultsOfDB{
if(character == "\""){
self.indexes.append(self.counter)
}
self.counter++
}
self.newStr = self.resultsOfDB.substringWithRange(Range<String.Index>(start: advance(self.resultsOfDB.startIndex, self.indexes[0] + 1), end: advance(self.resultsOfDB.endIndex, -(self.counter - self.indexes[1]))))
self.questions.text = self.newStr
})
}
self.counter = 0
}
}
There can be two reasons for this problem:
1.
This can mean that you are trying to call a function (text?) of an object (questions?) which is not initialized.
My guess is that questions is not initialized. So, when your call questions.text, you are calling text function on a nil outlet.
Make sure that your outlets questions are hooked up properly in the storyboard (you should see a circle near your #IBOutlet). Also, make sure you haven't set up multiple connections to your outlet.
2.
Your function getNewQbutton is an #IBAction that returns nothing. So the statement String(getNewQbutton()) doesn't make a lot of sense. Since your function getNewQbutton has no return type (and is an #IBOutlet), you are probably giving nil to String(). That may be the second reason of this issue.
I'm using core data to save a category in vc1 and want to add list properties to a list in vc2. My data model is one category to many list properties.
I'm adding the category like this in vc1:
func createNewCategory() {
var category: NSManagedObject! = NSEntityDescription.insertNewObjectForEntityForName("Category", inManagedObjectContext: self.context) as NSManagedObject
category.setValue(self.categoryTextField.text, forKey: "name")
var error: NSError? = nil
self.context.save(&error)
}
Setting up the data in vc2:
func setupCoreData() {
var appDelegate: AppDelegate = (UIApplication.sharedApplication()).delegate as AppDelegate
self.context = appDelegate.managedObjectContext!
var request: NSFetchRequest = NSFetchRequest(entityName: "Category")
if (self.context.executeFetchRequest(request, error: nil)) {
var error: NSError? = nil
self.listData = self.context.executeFetchRequest(request, error: &error)
self.managedObject = self.listData.objectAtIndex(0) as NSManagedObject
}
}
It crashes on the last row: self.managedObject = ... saying:
CoreData: error: Failed to call designated initializer on NSManagedObject class 'NSManagedObject'
The managed object is in the array if I put a break point and print the array. What's wrong?
The dedicated initializer is
class func insertNewObjectForEntityForName(_ entityName: String!,
inManagedObjectContext context: NSManagedObjectContext!) -> AnyObject!
Clearly, you are not inserting a new object, so you really want an optional value. Maybe you declared your class variable managedObject as NSManagedObject!? Try setting it to NSManagedObject? and also change the operator to as?.
An entity in CoreData is equivalent to a class. Did you add the entity to your managed object model, and subclass the entity? (Check out the programming guide for a fuller background.)