Core Data strange issue with properties - objective-c

I just noticed a strange issue with my app using Core Data (which didn't happen before).
Here is a section of code:
NSArray *allTransactions = [CDMTransaction transactionsFrom:_dateFrom to:_dateTo bankAccounts:_displayedBankAccounts categories:categories recipients:recipients tags:tags unclosedOnly:unclosedTransactionsOnly];
for (CDMTransaction *transaction in allTransactions) {
if (transaction.inverseTransaction != nil) {
...
}
}
What causes the issue is the call to transaction.inverseTransaction.
CDMTransaction is a subclass of NSManagedObject, which contains several properties and relationships (like inverseTransaction).
Everything worked fine until now, but I get an crash when this property is called:
CoreData: error: warning snapshot_get_value_as_object called on NULL, and it sends me to the inverseTransaction property implementation.
But when I try to log the allTransactions array contents, before entering the "for" loop, it just work fine!
Here is the updated code:
NSArray *allTransactions = [CDMTransaction transactionsFrom:_dateFrom to:_dateTo bankAccounts:_displayedBankAccounts categories:categories recipients:recipients tags:tags unclosedOnly:unclosedTransactionsOnly];
NSLog(allTransactions: %#, allTransactions);
for (CDMTransaction *transaction in allTransactions) {
if (transaction.inverseTransaction != nil) {
...
}
}
And what I get in the log console:
2015-01-11 11:54:51.613 Cash[14383:1403274] allTransactions: (
"<CDMTransaction: 0x7b94d4a0> (entity: CDMTransaction; id: 0x7b935f30 <x-coredata://12493FBF-2D83-469C-9610-D7C29E21F12B/CDMTransaction/p1> ; data: <fault>)"
)
Without any issue after...
It doesn't seem to be an issue with the inverseTransaction property because it does the same with all other properties (NSString, XNSNumber, ...) and relationships...
What is wrong?
Edit 1: when it crashes I get the property implementation line highlighted in green with a EXC_BAD_INSTRUCTION error (and not an EXC_BAD_ACCESS)
Edit 2: The app doesn't crash anymore when I add request.returnsObjectsAsFaults = NO; to my NSFetchRequest... So it seems to be OK now, but why do I have to write that? It didn't need it before...

Related

Mac OSX app : Issue Related to NSArrayController NSTableView Core data Adding Record

I have using NSArrayController NSTableView and Core data binding.
I have take one button and connect add: method of NSArrayController to its action.
On Adding new record
TableView added and shows new record.
NSArrayController's add: method called
Problem :
Value is not added into core data (Sqlite type).
On application relaunching shows old data.
This is an apple example code. Basically it tries to save the context before app is terminated. Depending on your specific case you might move the functionality to somewhere else.
func applicationShouldTerminate(_ sender: NSApplication) -> NSApplication.TerminateReply {
// Save changes in the application's managed object context before the application terminates.
let context = persistentContainer.viewContext
if !context.commitEditing() {
NSLog("\(NSStringFromClass(type(of: self))) unable to commit editing to terminate")
return .terminateCancel
}
if !context.hasChanges {
return .terminateNow
}
do {
try context.save()
} catch {
let nserror = error as NSError
// Customize this code block to include application-specific recovery steps.
let result = sender.presentError(nserror)
if (result) {
return .terminateCancel
}
let question = NSLocalizedString("Could not save changes while quitting. Quit anyway?", comment: "Quit without saves error question message")
let info = NSLocalizedString("Quitting now will lose any changes you have made since the last successful save", comment: "Quit without saves error question info");
let quitButton = NSLocalizedString("Quit anyway", comment: "Quit anyway button title")
let cancelButton = NSLocalizedString("Cancel", comment: "Cancel button title")
let alert = NSAlert()
alert.messageText = question
alert.informativeText = info
alert.addButton(withTitle: quitButton)
alert.addButton(withTitle: cancelButton)
let answer = alert.runModal()
if answer == .alertSecondButtonReturn {
return .terminateCancel
}
}
// If we got here, it is time to quit.
return .terminateNow
}
You're probably missing setting the managedObjectContext and entityName on NSArrayController.

AVCaptureSession Crashing when using webRTC

I am using WebRTC and its using AVCaptureSession. It works fine a few times but sometimes its getting crashed with this Exception.
Assertion failed: (_internal->figCaptureSession == NULL), function
-[AVCaptureVideoPreviewLayer attachToFigCaptureSession:], file /BuildRoot/Library/Caches/com.apple.xbs/Sources/EmbeddedAVFoundation/EmbeddedAVFoundation-1187.37.2.1/Aspen/AVCaptureVideoPreviewLayer.m
I recently had this problem aswell. In my code i kept an instance of AVCaptureOutput and added and removed it. When trying to add the same AVCaptureOutput instance to the same capture session again, this error appeared.
This is how i solved it:
private var _captureOutput: AVCaptureOutput?
var captureOutput: AVCaptureOutput {
guard let captureOutput = _captureOutput else {
let photoOutput = AVCapturePhotoOutput()
photoOutput.isHighResolutionCaptureEnabled = true
_captureOutput = photoOutput
return photoOutput
}
return captureOutput
}
Initialize the instance once when needed and when removed, also nullify it.
captureSession.outputs.forEach { [weak self] output in
self?.captureSession.removeOutput(output)
self?._captureOutput = nil
}
Before using instance of RTCCameraPreviewView you have to nill'ify its captureSession and assert will go away. Faced same issue.

Having Trouble Getting the UIDocumentBrowserController to open docs in a Document based app

I've been working on a new Document-based app, and was super glad about the new UIDocumentBrowserController...trying to roll my own solution for the document browser UI was tricky!
I'm having some trouble getting the browser to open documents after they've been created.
What happens now is that when I choose to create a new document in the document browser, the document is created and opened as expected, although an error message is logged. However, after the doc is closed, I cannot reopen the file, either immediately or upon subsequent launches, even though the document is displayed. However, a weird clue here is that if I stop running the app after creating the document, but without adding new information to it (triggering the save cycle), and run the project again, I can open the file correctly. Whuch makes me think that there's something in the way the files are being saved that is the issue.
(Note: At this phase, I'm working on getting the local, non/icloud implentation working, before I move on to the icloud implementation.)
Here is the error message at any point in the code whenthe document is saved to disk (or at least most of the time!):
2017-06-20 13:21:58.254938-0500 Sermon Design 2 iOS[22454:5000138] [default] [ERROR] Could not get attribute values for item file:///Users/stevenhovater/Library/Developer/CoreSimulator/Devices/9A4364F2-B3A1-4AD9-B680-FB4BC876C707/data/Containers/Data/Application/DD534ED8-C4A3-40FE-9777-AED961976878/Documents/Untitled-9.sermon. Error: Error Domain=NSFileProviderInternalErrorDomain Code=1 "The reader is not permitted to access the URL." UserInfo={NSLocalizedDescription=The reader is not permitted to access the URL.}
I suspect that the issue lies somewher in my document types plists, which I've tried to set up by imitating the setup in the video for wwdc 2017 session 229.
My docs are encapuslated by an NSData object, using what I take to be a pretty standard subclass implentation of UIDocument. (I'm omitting the code to generate the thumbnails)
override func contents(forType typeName: String) throws -> Any {
print("Saving Document Changes")
if sermon != nil {
let newData = NSKeyedArchiver.archivedData(withRootObject: sermon!)
return newData
} else {
let newData = NSKeyedArchiver.archivedData(withRootObject: Sermon())
return newData
}
}
override func fileAttributesToWrite(to url: URL, for saveOperation: UIDocumentSaveOperation) throws -> [AnyHashable : Any] {
let thumbnail:UIImage = self.createThumbnail()
let thumbnaildict = [URLThumbnailDictionaryItem.NSThumbnail1024x1024SizeKey : thumbnail]
let dict = [URLResourceKey.thumbnailDictionaryKey:thumbnaildict]
return dict
}
override func load(fromContents contents: Any, ofType typeName: String?) throws {
guard let newSermon:Sermon = NSKeyedUnarchiver.unarchiveObject(with: contents as! Data) as? Sermon else{
throw documentErrors.invalidFile
}
self.sermon = newSermon
}
In my subclass of UIDocumentBrowserViewController, Here is my code for getting a local filename and for creating the new document.
func documentBrowser(_ controller: UIDocumentBrowserViewController, didRequestDocumentCreationWithHandler importHandler: #escaping (URL?, UIDocumentBrowserViewController.ImportMode) -> Void) {
var newDocumentURL: URL? = nil
print("creating new local document")
guard let target = self.newLocalFilename() else {
return
}
let targetSuffix = target.lastPathComponent
let tempURL = URL(fileURLWithPath: NSTemporaryDirectory() + targetSuffix)
let newDocument:SDDocument = SDDocument(fileURL: tempURL)
newDocument.sermon = Sermon()
/
newDocument.save(to: tempURL, for: .forCreating) { (saveSuccess) in
/
guard saveSuccess else {
/
importHandler(nil, .none)
return
}
/
newDocument.close(completionHandler: { (closeSuccess) in
/
guard closeSuccess else {
/
importHandler(nil, .none)
return
}
/
importHandler(tempURL, .move)
})
}
}
func newLocalFilename() -> URL? {
let fileManager = FileManager()
guard let baseURL = self.localDocumentsDirectoryURL.appendingPathComponent("Untitled")
else {return nil}
var target = baseURL.appendingPathExtension(DocumentBrowserViewController.documentExtension)
var nameSuffix = 2
while fileManager.fileExists(atPath: target.path) {
target = URL(fileURLWithPath: baseURL.path + "-\(nameSuffix).\(DocumentBrowserViewController.documentExtension)")
nameSuffix += 1
}
let targetSuffix = target.lastPathComponent
print("Target name: \(targetSuffix)")
print("new url: \(target)")
return target
}
After four or five hours of work banging my head against this problem, I discovered a simple solution: don't test in the Simulator. I switched to testing on my device and instantly everything started working as advertised.
[I can't speak from experience here, but it may be that the "doesn't work in the Simulator" problem is confined to Sierra, but that the Simulator does work in High Sierra. This would explain why some users see this issue and others don't, and especially why Apple seems blissfully unaware of it in the WWDC video.]
I had exactly the same issue when I was trying to save to NSTemporaryDirectory().
If you instead save to the documents directory ([[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] firstObject]), it appears to work fine!
Update: it looks like this issue is fixed in iOS 11 beta 3, and you can now save newly created documents to NSTemporaryDirectory() correctly.
Here is my current theory.
This error
Error Domain=NSFileProviderInternalErrorDomain Code=1 "The reader is not permitted to access the URL."
shows up when first creating a UIDocument at a new URL using -initWithFileURL. It's basically saying "this URL doesn't exist yet," but in a way that makes it sound more like a permissions issue.
As far as I can tell, it doesn't prevent you from saving, opening, editing, or closing the file. So I think it's just a superfluous error that Apple should have taken out.
I found that the error happens on simulator when LSSupportsOpeningDocumentsInPlace property is set to YES in the info.plist.
Turn this property to NO, then it starts working, in my case.
On the real device, it works anyway without having error.
Had similar problem, and tried the method for providing a default Core Location in the Schemes settings and it works now. The method was mentioned in this answer: IOS 9 Error Domain=kCLErrorDomain Code=0 "(null)"

How to handle a NSDictionary as a return type in iOS swift?

I'm trying to convert and existing, working objective c application over to swift and I'm getting a little tripped up with "closures". Here's the old working objective c block that makes returns a value from a web service:
- (IBAction)didTapSayHiButton {
[self.meteor callMethodName:#"sayHelloTo" parameters:#[self.username.text] responseCallback:^(NSDictionary *response, NSError *error) {
NSString *message = response[#"result"];
[[[UIAlertView alloc] initWithTitle:#"Meteor Todos" message:message delegate:nil cancelButtonTitle:#"Great" otherButtonTitles:nil] show];
}];
}
So here I'm getting either getting back a dictionary or response. And it works.
And here's how I'm trying to go about this with swift (the method is slightly different):
#IBAction func sayHi(sender : AnyObject) {
var params = [
"name": "Scotty"
]
meteor.callMethodName("sayHi", parameters: params, responseCallback: {
(response: Dictionary<String, String>, error: NSError) in
println("the name recieved back is: \(response)")
})
}
The error I'm getting in xCode: "NSDictionary is not a subtype of 'Dictionary'"
After looking through the swift book this is the best educated attempt that I can make. I've tried a few other things but each resulted in another type of error.
How do I make this work with swift?
Edit: I've also tried just using Dictionary and Dictionary<String, String>
I should also note that I'm using a bridging header to access objective c code (objectiveDDP). And that callMethodNamed is written in objective c as can be seen here: https://github.com/boundsj/ObjectiveDDP/blob/master/ObjectiveDDP/MeteorClient.h#L47
Update: by changing the method to:
meteor.callMethodName("sayHi", parameters:["scotty"] , responseCallback:nil)
we were able to get it to work. But the second we try to add in the closure, it starts throwing the same original errors.
Try changing from using a Swift dictionary to explicitly using NSDictionary:
#IBAction func sayHi(sender : AnyObject) {
var params: NSDictionary = [
"name": "Scotty"
]
meteor.callMethodName("sayHi", parameters: params, responseCallback: {
(response: NSDictionary!, error: NSError) in
println("the name recieved back is: \(response)")
})
}
The trick, in this particular case, is to completely omit the closure's parameter types, and let the compiler figure it out. After searching around for a while, I found this post which led me to the solution.
meteor.callMethodName("sayHi", parameters:["scotty"] , responseCallback: {
response, error in
let me:String = response["result"] as String!
println("called back \(me)")
})
If your not worried about accessing the closures parameters, apparently you can also using an underscore to ignore them completely:
meteor.callMethodName("sayHi", parameters:["scotty"] , responseCallback: {
_ in
// Do something here
})

How is it better to wait an asynchronous method to be finished in iPhone app?

everybody.
I want to understand, how i shoud procceed situations when an asynchronous method has "didFinish:#selector(SEL)" parameter.
My code example is:
//
// Authentication check
- ( void )authenticationSuccess: ( GDataServiceTicket* ) ticket
authenticatedWithError: ( NSError* ) error {
if ( error == nil )
{
NSLog( #"authentication success" );
}
else
{
NSLog( #"authentication error" );
}
}
//
- ( void ) fetchFeedOfSpreadsheets {
//create and authenticate to a google spreadsheet service
if ( !(mService) )
{
GDataServiceGoogleSpreadsheet *service = [self spreadsheetService];
[mService autorelease];
mService = [service retain];
}
// check autentication success ( invoke "authenticationSuccess" method for debug success & error )
[mService authenticateWithDelegate: self
didAuthenticateSelector:#selector(authenticationSuccess:
authenticatedWithError:) ];
// HERE I WANT TO MAKE A PAUSE AND WHAIT THE RESULT, EITHER I AUTHENTICATED OR NOT
// AND MAKE AN "IF" STATEMENT TO CONTINTUE WORKING ON SERVER, OR RETURN ERROR
//fetch retrieves the feed of spreadsheets entries
NSURL *feedURL = [ NSURL URLWithString: kGDataGoogleSpreadsheetsPrivateFullFeed ];
GDataServiceTicket *ticket;
ticket = [mService fetchFeedWithURL: feedURL
delegate: self
didFinishSelector: #selector(spreadsheetsTicket:finishedWithFeed:
error: ) ];
// HERE I WANT TO WAIT SECOND TIME. I WANT "spreadsheetsTicket:
// finishedWithFeed:error:" TO PROCCEED ERROR AND PUT A FEED IN SOME NSARRAY OBJECT
// AND AFTER THAT I WANT TO WORK WITH THAT NSARRAY RIGHT HERE
}
I's clear, that i can push the code i want into the end of "authenticationSuccess" method section, but it's also clear, that it's a wrong a way to solve the proble. There a number of situations like this, where i call an asynchronous method with a selector parameter, and i want to find a solution providing me a flexible code writing.
Thanks in advance.
It's a standard practice in Objective-C to put the code to be executed after the authentication in the authenticationSucess: method. You might not like it, but that is life.
Many people had the same complaint as you, so
on iOS 4 and later, there's something called blocks which allow you to write the code to be executed after the authentication in the method which initiates the authentication, as in
[mService authenticateAndExecute:^{
code to be executed when successfully authenticated ;
} whenError:^{
code to be executed when authentication failed;
} ];
But in this case you need to modify the API, which is possible by using categories. See this blog post by Mike Ash. He has many other posts on blocks on the same blog, which are also very instructive.
If you're going to use a library that works asynchronously (and therefore doesn't block your UI), you should have a good reason for trying to force it to work synchronously.
You should be checking for an authentication error at the end of your authenticationSuccess:authenticatedWithError: method, and calling the next request from there if there's a success. Similarly, in your spreadsheetsTicket:finishedWithFeed:error: check for an error, and continuing processing if there isn't one. It might be a better design to do that continued work in a separate method, but that's up to you.
Is there a specific reason you want to use the GData API in a synchronous fashion?