NSManagedObjectContext(): `init()` was deprecated in iOS 9.0: Use -initWithConcurrencyType - cocoa

I was working through Core Data Stack in Swift - Demystified but when I got to the line
self.context = NSManagedObjectContext()
I got the warning
`init()` was deprecated in iOS 9.0: Use -initWithConcurrencyType: instead
I see that I can do one of the following for self.context =
NSManagedObjectContext(concurrencyType: NSManagedObjectContextConcurrencyType.ConfinementConcurrencyType)
NSManagedObjectContext(concurrencyType: NSManagedObjectContextConcurrencyType.MainQueueConcurrencyType)
NSManagedObjectContext(concurrencyType: NSManagedObjectContextConcurrencyType.PrivateQueueConcurrencyType)
but since ConfinementConcurrencyType is also deprecated now that leaves me MainQueueConcurrencyType and PrivateQueueConcurrencyType. What is the difference between these two and how should I choose which one to use? I read this documentation, but I didn't really understand.

You essentially will always have at least 1 context with NSMainQueueConcurrencyType and many contexts with NSPrivateQueueConcurrencyType. NSPrivateQueueConcurrencyType is used typically for saving or fetching things to core data in the background (like if attempting to sync records with a Web Service).
The NSMainQueueConcurrencyType creates a context associated with the main queue which is perfect for use with NSFetchedResultsController.
The default core data stack uses a single context with NSMainQueueConcurrencyType, but you can create a much better app by leveraging multiple NSPrivateQueueConcurrencyType to do any work that does not affect the UI.

Replace these two function with the following one:
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
var managedObjectContext = NSManagedObjectContext(concurrencyType: .MainQueueConcurrencyType)
managedObjectContext.persistentStoreCoordinator = coordinator
return managedObjectContext
}()
// MARK: - Core Data Saving support
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()
}
}
}

Related

Using Apple Activity Tracing

Apple introduced Activity Tracing to support debugging asynchronous code. I have some difficulties using it properly. My example scenario is a small MacOS app just downloading a file:
- (IBAction)actionDownload:(NSButton *)sender {
os_activity_label_useraction("actionDownload");
os_log_t logDemo = os_log_create("ActivityTracingDemo", "demo");
os_log_debug(logDemo, "actionDownload start (1)");
os_activity_t actTop = os_activity_create(
"HTTP Download",
OS_ACTIVITY_NONE,
OS_ACTIVITY_FLAG_DETACHED);
os_activity_apply(actTop, ^{
os_log_debug(logDemo, "actionDownload start");
dispatch_after(dispatch_time(DISPATCH_TIME_NOW,
(int64_t)(2 * NSEC_PER_SEC)),
dispatch_get_main_queue(), ^{
os_log_debug(logDemo,
"actionDownload two second later");
});
NSURL *url = [NSURL URLWithString:
#"https://www.google.de/images/branding/googlelogo/2x/googlelogo_color_272x92dp.png"];
// Get the current activity (or create a new one,
// if no current activity exists):
os_activity_t act = os_activity_create(
"HTTP Response",
OS_ACTIVITY_CURRENT,
OS_ACTIVITY_FLAG_IF_NONE_PRESENT);
NSURLSessionDownloadTask *downloadPhotoTask =
[ [NSURLSession sharedSession]
downloadTaskWithURL:url
completionHandler:^(NSURL *location,
NSURLResponse *response,
NSError *error)
{
os_activity_apply(act, ^{
// Now the same activity is active,
// that initiated the download.
os_log_debug(logDemo, "actionDownload received data (restored activity)");
});
os_log_debug(logDemo, "actionDownload received data");
}];
[downloadPhotoTask resume];
});
}
Filtering for my messages in Console.app I am getting:
Obviously NSURLSession does not call the completionHandler with the same activity active that initiated the download. I had to manually apply that activity within the callback. Is there a better way to do this? I thought that activities are designed to trace things across process. In that case it is not even working inside the same process without doing some extra work.
In the activity view of the Console.appI am getting:
The tree view looks promising, to get a quick overview about what application scenarios are triggered. Initially I thought that it is not
necessary to apply a new activity in an action callback and instead it would be possible to use os_activity_label_useraction to get the scenario displayed in Console.appactivity view on top level. Obviously that's not the case. I can't find that label in any log.
My solution is to create a new detached activity in actionDownload. This activity is visible on top level in the Console.appactivity view. What I dislike with this solution are two things:
First, I had to explicitly create a new activity with a new scope. This adds lots of noise to the source code. I have many very short action methods in my project. Also the messages view works without this. There I am just getting what I am interested in by filtering for subsystem, category and activity id.
Second, the connection to the initiating activity gets lost.
Would be great to get some hints about how to properly use Activity Tracing and especially the hierarchy thingy.

What is the correct way to use backgroundCompletionHandler in Alamofire?

I'm not clear on how to use this properly but had seen other people doing this type of thing:
func application(application: UIApplication, handleEventsForBackgroundURLSession identifier: String, completionHandler: () -> Void) {
manager.sharedInstance.backgroundCompletionHandler = completionHandler
}
In our similar implementation, at this point completionHandler is partial apply forwarder for reabstraction thunk helper...
Where manager is (despite being a singleton) essentially:
let configuration = NSURLSessionConfiguration.backgroundSessionConfigurationWithIdentifier("com.ourcompany.app")
let manager = Alamofire.Manager(configuration: configuration)
However this causes the following warning to be printed in the console:
Warning: Application delegate received call to -application:handleEventsForBackgroundURLSession:completionHandler: but the completion handler was never called.
I set a breakpoint here and at this point the message is already visible in the console and backgroundCompletionHandler is nil.
We're building against the iOS 9 SDK with Xcode 7.0 and currently using Alamofire 2.0.2
I originally thought this was introduced when we merged our Swift 2.0 branch but I'm also seeing the message with an earlier commit using Xcode 6.4 against the iOS 8 SDK.
Update 1
To address #cnoon's suggestions:
The identifier matches - the configuration and manager are set inside a singleton so there's no way for it to be wrong.
When adding and printing inside of didSet on backgroundCompletionHandler in the Manager class, the message is logged before the warning.
When printing inside of the closure set to sessionDidFinishEventsForBackgroundURLSession on the delegate inside the Manager class, the message is printed after the warning.
When overriding sessionDidFinishEventsForBackgroundURLSession and printing inside of it before calling backgroundCompletionHandler, the message is printed after the warning.
As for verifying I have my Xcode project set up correctly for background sessions, I'm not sure how to do that and couldn't find any documentation on how to do so.
I should note that when trying to upload some screenshots from my phone I was initially unable to reproduce this issue in order to try these suggestions.
It was only after trying to share some photos that I was able to reproduce this again. I'm not sure or the correlation (if any) but it may be related to the photos taking longer to upload.
Update 2
The UIBackgroundModes are set exactly as #Nick described, and calling completionHandler() directly inside of application:handleEventsForBackgroundURLSession:completionHandler: does not display the warning.
Update 3
So, it appears I overlooked an small but important detail. There's a wrapper around Alamofire.Manager that doesn't expose it directly. The relevant part of its implementation looks like this:
private var manager: Manager
public var backgroundCompletionHandler: (() -> Void)? {
get {
return manager.backgroundCompletionHandler
}
set {
manager.backgroundCompletionHandler = backgroundCompletionHandler
}
}
and setting the completion handler in the AppDelegate executes that code path.
func application(application: UIApplication, handleEventsForBackgroundURLSession identifier: String, completionHandler: () -> Void) {
myManager.sharedInstance.backgroundCompletionHandler = completionHandler
}
I've confirmed that the following change to expose the instance of Alamofire.Manager and access it directly does not produce the warning:
public var manager: Manager
// public var backgroundCompletionHandler: (() -> Void)? {
// get {
// return manager.backgroundCompletionHandler
// }
// set {
// manager.backgroundCompletionHandler = backgroundCompletionHandler
// }
// }
and in the AppDelegate:
func application(application: UIApplication, handleEventsForBackgroundURLSession identifier: String, completionHandler: () -> Void) {
myManager.sharedInstance.manager.backgroundCompletionHandler = completionHandler
}
Based on this it appears that using a computed property to proxy the completion handler is the cause of the issue.
I'd really prefer not to expose this property and would love to know of any workarounds or alternatives.
It appears as though everything you are doing is correct. I have an example app that does exactly what you've described that works correctly and does not throw the warning you are seeing. I'm guessing you still have some small error somewhere. Here are a few ideas to try out:
Verify the identifier matches the identifier of your background session
Add a didSet log statement on the backgroundSessionHandler in the Manager class temporarily to verify it is getting set
Add a log statement into the sessionDidFinishEventsForBackgroundURLSession to verify it is getting called as expected
Override the sessionDidFinishEventsForBackgroundURLSession on the delegate and manually call the backgroundSessionHandler
Verify you have your Xcode project set up correctly for background sessions
Update 2
Your computed property is wrong. Instead it needs to set the backgroundCompletionHandler to newValue. Otherwise you are never setting it to the new value correctly. See this thread for more info.

NSManagedObjectContext save unsuccessful, but returns nil error

I am developing an OS X Application that uses a single-threaded Core Data model without nested contexts.
I am creating objects on the main thread in the defaultContext and try to save them after creation, but the save fails without returning an error. I have not overwritten any methods in my CoreData objects, but I am using the latest version of MagicalRecord.
The code that fails:
// pseudocode for createOrFetchWithData:inContext:
// fetch object from value in objectDict
// if(!object) create project in context
// [object importValuesForKeysWithObject:objectData] // MR method
// return object
MyObject *object = [MyObject createOrFetchWithData:objectData
inContext:[NSManagedObjectContext defaultContext]];
if(!object) return; // just to emphasise that I am sure the object is not nil.
[[NSManagedObjectContext defaultContext] saveOnlySelfWithCompletion:^(BOOL saveSuccessful, NSError *error) {
if(saveSuccessful) {
NSLog(#"yay");
} else {
NSLog(#"nay");
}
}];
The return value of [NSManagedObjectContext defaultContext] is not nil and I have verified that the code is executed on the main thread.
I have a relationship that isn't set in the MyObject *object, but it is marked as optional in the data model.
Any idea what might cause this simple operation to fail? I have other entities that save just fine, but this particular case fails.
Note: I am running OS X Mavericks DP 8.
In the end, it was my misunderstanding of MagicalRecord's (and possibly CoreData's) implementation of the save: methods: in case there is no change to the context ([context hasChanges] == NO) AND the parent context (!!), the save will be aborted, and the completion block is called with NO for successful and no error object.

Can I receive a callback whenever an NSPasteboard is written to?

I've read Apple's Pasteboard Programming Guide, but it doesn't answer a particular question I have.
I'm trying to write a Cocoa application (for OS X, not iOS) that will keep track of everything that is written to the general pasteboard (so, whenever any application copies and pastes, but not, say, drags-and-drops, which also makes use of NSPasteboard). I could (almost) accomplish this by basically polling the general pasteboard on a background thread constantly, and checking changeCount. Of course, doing this would make me feel very dirty on the inside.
My question is, is there a way to ask the Pasteboard server to notify me through some sort of callback any time a change is made to the general pasteboard? I couldn't find anything in the NSPasteboard class reference, but I'm hoping it lurks somewhere else.
Another way I could imagine accomplishing this is if there was a way to swap out the general pasteboard implementation with a subclass of NSPasteboard that I could define myself to issue a callback. Maybe something like this is possible?
I would greatly prefer if this were possible with public, App Store-legal APIs, but if using a private API is necessary, I'll take that too.
Thanks!
Unfortunately the only available method is by polling (booo!). There are no notifications and there's nothing to observe for changed pasteboard contents. Check out Apple's ClipboardViewer sample code to see how they deal with inspecting the clipboard. Add a (hopefully not overzealous) timer to keep checking for differences and you've got a basic (if clunky) solution that should be App-Store-Friendly.
File an enhancement request at bugreporter.apple.com to request notifications or some other callback. Unfortunately it wouldn't help you until the next major OS release at the earliest but for now it's polling until we all ask them to give us something better.
There was once a post on a mailing list where the decision against a notification api was described. I can't find it right now though. The bottom line was that probably too many applications would register for that api even though they really wouldn't need to. If you then copy something the whole system goes through the new clipboard content like crazy, creating lots of work for the computer. So i don't think they'll change that behavior anytime soon. The whole NSPasteboard API is internally built around using the changeCount, too. So even your custom subclass of NSPasteboard would still have to keep polling.
If you really want to check if the pasteboard changed, just keep observing the changeCount very half second. Comparing integers is really fast so there's really no performance issue here.
Based on answer provided by Joshua I came up with similar implementation but in swift, here is the link to its gist: PasteboardWatcher.swift
Code snippet from same:
class PasteboardWatcher : NSObject {
// assigning a pasteboard object
private let pasteboard = NSPasteboard.generalPasteboard()
// to keep track of count of objects currently copied
// also helps in determining if a new object is copied
private var changeCount : Int
// used to perform polling to identify if url with desired kind is copied
private var timer: NSTimer?
// the delegate which will be notified when desired link is copied
weak var delegate: PasteboardWatcherDelegate?
// the kinds of files for which if url is copied the delegate is notified
private let fileKinds : [String]
/// initializer which should be used to initialize object of this class
/// - Parameter fileKinds: an array containing the desired file kinds
init(fileKinds: [String]) {
// assigning current pasteboard changeCount so that it can be compared later to identify changes
changeCount = pasteboard.changeCount
// assigning passed desired file kinds to respective instance variable
self.fileKinds = fileKinds
super.init()
}
/// starts polling to identify if url with desired kind is copied
/// - Note: uses an NSTimer for polling
func startPolling () {
// setup and start of timer
timer = NSTimer.scheduledTimerWithTimeInterval(2, target: self, selector: Selector("checkForChangesInPasteboard"), userInfo: nil, repeats: true)
}
/// method invoked continuously by timer
/// - Note: To keep this method as private I referred this answer at stackoverflow - [Swift - NSTimer does not invoke a private func as selector](http://stackoverflow.com/a/30947182/217586)
#objc private func checkForChangesInPasteboard() {
// check if there is any new item copied
// also check if kind of copied item is string
if let copiedString = pasteboard.stringForType(NSPasteboardTypeString) where pasteboard.changeCount != changeCount {
// obtain url from copied link if its path extension is one of the desired extensions
if let fileUrl = NSURL(string: copiedString) where self.fileKinds.contains(fileUrl.pathExtension!){
// invoke appropriate method on delegate
self.delegate?.newlyCopiedUrlObtained(copiedUrl: fileUrl)
}
// assign new change count to instance variable for later comparison
changeCount = pasteboard.changeCount
}
}
}
Note: in the shared code I am trying to identify if user has copied a
file url or not, the provided code can easily be modified for other general
purposes.
For those who need simplified version of code snippet that gets the job done in Swift 5.7,
it just works (base on #Devarshi code):
func watch(using closure: #escaping (_ copiedString: String) -> Void) {
let pasteboard = NSPasteboard.general
var changeCount = NSPasteboard.general.changeCount
Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { _ in
guard let copiedString = pasteboard.string(forType: .string),
pasteboard.changeCount != changeCount else { return }
defer {
changeCount = pasteboard.changeCount
}
closure(copiedString)
}
}
how to use is as below:
watch {
print("detected : \($0)")
}
then if you attempt copy any text in your pasteboard, it will watch and print out to the console like below..
detected : your copied message in pasteboard
detected : your copied message in pasteboard
in case, full code sample for how to use it for example in SwiftUI:
import SwiftUI
#main
struct TestApp: App {
var body: some Scene {
WindowGroup {
ContentView()
.onAppear {
watch {
print("detect : \($0)")
}
}
}
}
func watch(using closure: #escaping (_ copiedString: String) -> Void) {
let pasteboard = NSPasteboard.general
var changeCount = NSPasteboard.general.changeCount
Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { _ in
guard let copiedString = pasteboard.string(forType: .string),
pasteboard.changeCount != changeCount else { return }
defer {
changeCount = pasteboard.changeCount
}
closure(copiedString)
}
}
}
It's not necessary to poll. Pasteboard would generally only be changed by the current view is inactive or does not have focus. Pasteboard has a counter that is incremented when contents change. When window regains focus (windowDidBecomeKey), check if changeCount has changed then process accordingly.
This does not capture every change, but lets your application respond if the Pasteboard is different when it becomes active.
In Swift...
var pasteboardChangeCount = NSPasteboard.general().changeCount
func windowDidBecomeKey(_ notification: Notification)
{ Swift.print("windowDidBecomeKey")
if pasteboardChangeCount != NSPasteboard.general().changeCount
{ viewController.checkPasteboard()
pasteboardChangeCount = NSPasteboard.general().changeCount
}
}
I have a solution for more strict case: detecting when your content in NSPasteboard was replaced by something else.
If you create a class that conforms to NSPasteboardWriting and pass it to -writeObjects: along with the actual content, NSPasteboard will retain this object until its content is replaced. If there are no other strong references to this object, it get deallocated.
Deallocation of this object is the moment when new NSPasteboard got new content.

NSOperation Causing Crash when Passed to Delegate

For an iPhone app, I'm using a NSOperationQueue to limit access to the SQLite database to one query at a time. I created a subclass of NSOperation and in the main function I have the following:
- (void)main
{
// ... other code here ...
if( [_delegate respondsToSelector:#selector(queryCompleted:)] )
{
[_delegate performSelectorOnMainThread:#selector(queryCompleted:)
withObject:self
waitUntilDone:NO];
}
}
Delegate side:
- (void)queryCompleted:(QueryOperation*)aQueryOperation
{
// Breakpoint here allows me to explore and see the members of aQueryOperation
id results = [aQueryOperation resultSet]; // Crashes here
// ... more code here ...
}
The reason I am passing self is to allow the delegate to access the ID for the query operation (in the case where there is more than one request open per delegate) and the results from the query.
In the documentation for performSelectorOnMainThread:withObject:waitUntilDone:, it clearly states:
"This method retains the receiver and the arg parameter until after the selector is performed."
However when the delegate method tries to access the argument, an "EXC_BAD_ACCESS" exception is thrown. Any thoughts on why?
Oddly enough, if I set a breakpoint before the crashing reference to the NSOperation object, the debugger permits me to see the object instance and the values of all the parameters.
Try setting the waitUntilDone: parameter to YES. Possibly there is a race condition which is allowing the NSOperation to deallocate itself.

Resources