I have just downloaded the new Xcode 7.0 beta and did a migration from Swift 1.2 to Swift 2. The migration apparently did not change the whole code, in fact a method saveContext() which was fine until throws 2 errors for the line:
if moc.hasChanges && !moc.save() {
Binary operator '&&' cannot be applied to two Bool operands
and
Call can throw, but it is not marked with 'try' and the error is not handled
The method looks like this:
// MARK: - Core Data Saving support
func saveContext () {
if let moc = self.managedObjectContext {
var error: NSError? = nil
if moc.hasChanges && !moc.save() {
// Replace this implementation with code to handle the error appropriately.
// abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
NSLog("Unresolved error \(error), \(error!.userInfo)")
abort()
}
}
}
Any ideas on how to get it working?
The first of the two errors you provided is misleading, but the second is spot on. The problem is in !moc.save() which as of Swift 2, no longer returns Bool and is instead annotated throws. This means that you you have to try this method and catch any exceptions that it may emit, instead of just checking wether its return value is true or false.
To reflect this, a new project created in Xcode 7 using Core Data will produce the following boilerplate code which can replace the code you're using.
func saveContext () {
if managedObjectContext.hasChanges {
do {
try managedObjectContext.save()
} catch {
// Replace this implementation with code to handle the error appropriately.
// abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
let nserror = error as NSError
NSLog("Unresolved error \(nserror), \(nserror.userInfo)")
abort()
}
}
}
The answer by 0x7fffffff is correct, but to improve upon Apple's boilerplate code, you can catch the specific error in the catch block using catch let error as NSError like so:
func saveContext () {
if managedObjectContext.hasChanges {
do {
try managedObjectContext.save()
} catch let error as NSError {
NSLog("Unresolved error \(error), \(error.userInfo)")
// Handle Error
}
}
}
The best practice is to use the var error witch will still be available if you just use it this way :
func saveContext () {
if managedObjectContext.hasChanges {
do {
try managedObjectContext.save()
} catch {
NSLog("Unresolved error \(error), \(error.userInfo)")
// Handle Error
}
}
}
In the same way, if you are sure that managedObjectContext.save() will not throw an exception, the code is slimmed down to:
func saveContext () {
if managedObjectContext.hasChanges {
try! managedObjectContext.save()
}
}
And to extrapolate on why managedObjectContext is not optional in the Swift 2 code, it is because the NSManagedObject(concurrencyType:) is an initializer that does not fail. In Xcode 6, the boilerplate code returned an optional context if the NSPersistentStoreCoordinator is nil, but you can handle this easily by checking.
lazy var managedObjectContext: NSManagedObjectContext = {
let coordinator = self.persistentStoreCoordinator
var moc = NSManagedObjectContext(concurrencyType: .MainQueueConcurrencyType)
moc.persistentStoreCoordinator = coordinator
return moc
}()
Related
I'm trying to use CoreData to save my favourites locally and I have a set that acts as storage so that they will be accessed when the program terminates but I am not sure how to make my object persistent to do that. This is what I tried to do.
import Foundation
import CoreData
class PersistenceService {
private init() {}
static var context: NSManagedObjectContext {
return persistentContainer.viewContext
}
// MARK: CoreData
static var persistentContainer: NSPersistentContainer = {
/*
The persistent container for the application. This implementation
creates and returns a container, having loaded the store for the
application to it. This property is optional since there are legitimate
error conditions that could cause the creation of the store to fail.
*/
let container = NSPersistentContainer(name: "playerModel")
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
// Replace this implementation with code to handle the error appropriately.
// fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
/*
Typical reasons for an error here include:
* The parent directory does not exist, cannot be created, or disallows writing.
* The persistent store is not accessible, due to permissions or data protection when the device is locked.
* The device is out of space.
* The store could not be migrated to the current model version.
Check the error message to determine what the actual problem was.
*/
fatalError("Unresolved error \(error), \(error.userInfo)")
}
})
return container
}()
static func saveContext () {
let context = persistentContainer.viewContext
if context.hasChanges {
do {
try context.save()
} catch {
// Replace this implementation with code to handle the error appropriately.
// fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
let nserror = error as NSError
fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
}
}
}
}
This is the object I want to save
var item = CurrentPlayers(context: PersistenceService.context) //make it reference NSManagededContext so it can be saved
//this method adds to Favourites
#IBAction func addToFav(_ sender: Any) {
let alert = UIAlertController(title: "Favourite Added 💙", message: "\(name.text ?? "") is added to favourites", preferredStyle: .alert)
alert.addAction(UIAlertAction(
title: "OK",
style: UIAlertAction.Style.default)
{ [self] _ in
FavouriteManager.shared.add(item)
})
self.present(alert, animated: true, completion: nil)
print("Favourite button Pressed")
}
This is where I want to store my objects
import Foundation
class FavouriteManager {
static let shared = FavouriteManager()
var favSet: Set<CurrentPlayers> = Set()
func add(_ player: CurrentPlayers) {
favSet.insert(player)
NotificationCenter.default.post(
name: .passFavNotification,
object: player
)
}
}
I am implementing Swift CoreData into my application, However I continue to face the error 'Unrecognised selector sent to Instance' when adding data to the class set up as you can see below. I realise that this is probably a rookie error, so thanks for your patience.
func coreDataRequest(){
container = NSPersistentContainer(name: "tileData")
container.loadPersistentStores { StoreDescription, error in
if let error = error {
print("Unresolved Error \(error)")
}
}
var campaign = Campaign()
campaign.countryName = "France"
}
The Error
unrecognized selector sent to instance
Here is my XCDataModel
Have you generate Campaign() class, that you add to tileData.xcdatamodeld?
if so create a NSManagedObjectContext like this:
var context: NSManagedObjectContext = {
return persistentContainer.viewContext
}()
and then use it to create Campaign like this:
var campaign = Campaign(context: context)
campaign.countryName = "France"
then if you want to store your object you have to call save on the context:
func saveContext () {
if context.hasChanges {
do {
try context.save()
} catch {
context.rollback()
let nserror = error as NSError
fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
}
}
}
I recommend you to look through few tutorials, it would save your time in future. There are a lot of them, just google it.
Without seeing all your code its difficult to say what is wrong, just from what I can see the container is missing a "let" in-front unless that was declared outside the function. This is a really good tutorial on setting up and using CoreData https://www.raywenderlich.com/books/core-data-by-tutorials/v7.0/chapters/3-the-core-data-stack#toc-chapter-006-anchor-011
here is my code (Swift):
import UIKit
import AVFoundation
class PlaySoundViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
if var filePath = NSBundle.mainBundle().pathForResource("movie_quote",ofType: "mp3"){
var filePathUrl = NSURL.fileURLWithPath(filePath)
AVAUdioPlayer audioPlayer = AVAudioPlayer(contentsOfURL:filePathUrl) throws
}
else{
print("filePath is empty")
}
}
#IBAction func playSlowAudio(sender: UIButton) {
}
func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
this is the method I found on my "Documentation and API References" to play audio:
``
initWithContentsOfURL:error:
init(contentsOfURL url: NSURL) throws
So, I return a String as source path, then conver it to NSURL. Now i want to play the audio but the method I am using needs to throw the error and handle it. How should I throw and handle the error ?
Swift 2.0
AVAudioPlayer will throw an exception if its initializer fails. Catch the error by wrapping its initialization in a do/catch clause.
do {
let audioPlayer = try AVAudioPlayer(contentsOfURL: filePathUrl)
// use audioPlayer
} catch {
// handle error
}
As you can see, the keyword try is inserted before any method call that can throw exceptions. As long as the try statement doesn't throw, you can continue your code as normal. If the try statement does throw, you program will jump to the catch clause.
Examining the error
If you'd like to examine the error, you can convert it to an NSError by writing your catch statement like so (as seen in Apple's Objective-C/Swift Interoperability Docs):
do {
let audioPlayer = try AVAudioPlayer(contentsOfURL: filePathUrl)
// use audioPlayer
} catch let error as NSError {
// error is now an NSError instance; do what you will
}
Converting to NSError is only necessary if you want to examine an error thrown by one of Apple's Cocoa objects. Native Swift code, throwing native ErrorType errors, require no conversion.
I recommend you read Apple's new docs on error handling in Swift.
Swift 1.2
If you are using Swift 1.2, then there is no error handling available. Instead, AVAudioPlayer's initialization method will fail and return nil.
If you are using Swift 1.2, I would recommend initializing the audio player like this:
var initError: NSError?
if let audioPlayer = AVAudioPlayer(contentsOfURL: filePathUrl, error: &initError) {
// use audioPlayer
} else {
println(initError) // handle error
}
Since Swift 1.2, you cannot throw/handle exceptions. While it is available in swift2 (need XCode7 support) which is still in beta. See this article for detail (https://www.hackingwithswift.com/new-syntax-swift-2-error-handling-try-catch).
This is the core data tutorial that I am trying to complete. The error is in the saveContext() function.
import UIKit
import CoreData
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
// ...
func saveContext () {
var error: NSError? = nil
// There second line below this comment is providing for the error referenced in the question title.
let managedObjectContext = self.managedObjectContext
if (managedObjectContext != nil)
{
if managedObjectContext.hasChanges && !managedObjectContext.save(&error) {
// Replace this implementation with code to handle the error appropriately.
// abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
//println("Unresolved error \(error), \(error.userInfo)")
abort()
}
}
}
// #pragma mark - Core Data stack
// Returns the managed object context for the application.
// If the context doesn't already exist, it is created and bound to the persistent store coordinator for the application.
var managedObjectContext: NSManagedObjectContext {
if !(_managedObjectContext != nil) {
let coordinator = self.persistentStoreCoordinator
if (coordinator != nil) {
_managedObjectContext = NSManagedObjectContext()
_managedObjectContext!.persistentStoreCoordinator = coordinator
}
}
return _managedObjectContext!
}
var _managedObjectContext: NSManagedObjectContext? = nil
// Returns the managed object model for the application.
// If the model doesn't already exist, it is created from the application's model.
var managedObjectModel: NSManagedObjectModel {
if (_managedObjectModel != nil) {
let modelURL = NSBundle.mainBundle().URLForResource("ContactU", withExtension: "momd")
_managedObjectModel = NSManagedObjectModel(contentsOfURL: modelURL!)
}
return _managedObjectModel!
}
var _managedObjectModel: NSManagedObjectModel? = nil
// Returns the persistent store coordinator for the application.
// If the coordinator doesn't already exist, it is created and the application's store added to it.
var persistentStoreCoordinator: NSPersistentStoreCoordinator {
if !(_persistentStoreCoordinator != nil) {
let storeURL = self.applicationDocumentsDirectory.URLByAppendingPathComponent("ContactU.sqlite")
var error: NSError? = nil
_persistentStoreCoordinator = NSPersistentStoreCoordinator(managedObjectModel: self.managedObjectModel)
if _persistentStoreCoordinator!.addPersistentStoreWithType(NSSQLiteStoreType, configuration: nil, URL: storeURL, options: nil, error: &error) == nil {
/*
Replace this implementation with code to handle the error appropriately.
abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
Typical reasons for an error here include:
* The persistent store is not accessible;
* The schema for the persistent store is incompatible with current managed object model.
Check the error message to determine what the actual problem was.
If the persistent store is not accessible, there is typically something wrong with the file path. Often, a file URL is pointing into the application's resources directory instead of a writeable directory.
If you encounter schema incompatibility errors during development, you can reduce their frequency by:
* Simply deleting the existing store:
NSFileManager.defaultManager().removeItemAtURL(storeURL, error: nil)
* Performing automatic lightweight migration by passing the following dictionary as the options parameter:
[NSMigratePersistentStoresAutomaticallyOption: true, NSInferMappingModelAutomaticallyOption: true]
Lightweight migration will only work for a limited set of schema changes; consult "Core Data Model Versioning and Data Migration Programming Guide" for details.
*/
//println("Unresolved error \(error), \(error!.userInfo)")
abort()
}
}
return _persistentStoreCoordinator!
}
var _persistentStoreCoordinator: NSPersistentStoreCoordinator? = nil
// #pragma mark - Application's Documents directory
// Returns the URL to the application's Documents directory.
var applicationDocumentsDirectory: NSURL {
let urls = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)
return urls[urls.count-1] as NSURL
}
}
In saveContext() your constant managedObjectContext is of type NSManagedObjectContext, yet in the next line you are testing it against nil, which is never possible. Only an optional type (e.g. NSManagedObjectContext?) can be nil. See "Optionals" in The Swift Programming Language: The Basics for more info.
This could be the reason for your error, although it is unclear from the code and error exactly what it is being referred to. Swift's compiler errors aren't all fantastically helpful at this early stage in the development of the language, and so they sometimes need to be taken with a pinch of salt.
I can't give you an answer for sure however you can try this saveContext() way
lazy var managedObjectContext: NSManagedObjectContext? =
{
// Returns the managed object context for the application (which is already bound to the persistent store coordinator for the application.) This property is optional since there are legitimate error conditions that could cause the creation of the context to fail.
let coordinator = self.persistentStoreCoordinator
if coordinator == nil
{
return nil
}
var managedObjectContext = NSManagedObjectContext()
managedObjectContext.persistentStoreCoordinator = coordinator
return managedObjectContext
}()
func saveContext ()
{
if let moc = self.managedObjectContext
{
var error: NSError? = nil
if moc.hasChanges && !moc.save(&error) {
// Replace this implementation with code to handle the error appropriately.
// abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
NSLog("Unresolved error \(error), \(error!.userInfo)")
abort()
}
}
}
Besides if you want to pure-swift you shouldn't use "#pragma", Just use // MARK:: - Your tag here
Everything was fine in Xcode beta 5 but now in full fledged Xcode I'm getting 2 similar errors in my App Delegate:
"cannot invoke '!=' with argument list of type '(NSManagedObjectContext, NilLiteralConvertible')"
"cannot invoke '!=' with argument list of type '(NSPersistentStoreCoordinator, NilLiteralConvertible')"
I tried replacing the != with !== and I get a different error. I don't see what the problem is with !=.
The code:
func saveContext () {
var error: NSError? = nil
let managedObjectContext = self.managedObjectContext
if managedObjectContext != nil { //THIS LINE
if managedObjectContext.hasChanges && !managedObjectContext.save(&error) {
// Replace this implementation with code to handle the error appropriately.
// abort() causes the application to generate a crash log and terminate.
//println("Unresolved error \(error), \(error.userInfo)")
abort()
}
}
}
// #pragma mark - Core Data stack
// Returns the managed object context for the application.
// If the context doesn't already exist, it is created and bound to the persistent store coordinator for the application.
var managedObjectContext: NSManagedObjectContext {
if _managedObjectContext !== nil {
let coordinator = self.persistentStoreCoordinator
if coordinator != nil { //THIS LINE
_managedObjectContext = NSManagedObjectContext()
_managedObjectContext!.persistentStoreCoordinator = coordinator
}
}
return _managedObjectContext!
}
var _managedObjectContext: NSManagedObjectContext? = nil
I have a new error: "'NSManagedObjectContext?' does not have a member named 'hasChanges'"
In this code:
func saveContext () {
var error: NSError? = nil
let managedObjectContext = self.managedObjectContext
if managedObjectContext != nil {
if managedObjectContext.hasChanges && !managedObjectContext.save(&error) { //This line
// Replace this implementation with code to handle the error appropriately.
// abort() causes the application to generate a crash log and terminate.
//println("Unresolved error \(error), \(error.userInfo)")
abort()
}
}
}
The reason for this is because NSManagedObjectContext, and probably NSPersistentStoreCoordinator, do not conform to the Equatable protocol. In order to use == and !=, an object needs to conform to this protocol.
=== and !== do not check equality in that sense. These check to see if two objects are in fact the *same object`. Not with similar values. They both point to the same object in memory.
You are able to check this on the two noted types because they are objects. This should work fine for your situation.
About your second issue, your code should look like this:
func saveContext () {
var error: NSError? = nil
let managedObjectContext = self.managedObjectContext
if let moc = managedObjectContext {
if moc.hasChanges && !moc.save(&error) { //This line
// Replace this implementation with code to handle the error appropriately.
// abort() causes the application to generate a crash log and terminate.
//println("Unresolved error \(error), \(error.userInfo)")
abort()
}
}
}
The difference here is if let moc = managedObjectContext: if managedObjectContext is not nil, the value is stored in the constant moc. Otherwise, whatever is inside does not get executed. You can read more about optionals here.
self.managedObjectContext is here typed as an NSManagedObjectContext, not an NSManagedObjectContext?.
You can't compare a var to nil unless it's of type Optional<>, implements Equatable, or is nil itself.