I cannot seem to locate any documentation on this, so hopefully someone can confirm the behavior I am seeing with Apple's sample SMJobBless code.
I was under the impression that it would only ask for an admin password if it detected a that a new version of the helper tool needed to be installed.
However, this impression is apparently incorrect.
The behavior I am seeing under 10.6 is that if I launch the app for the first time, it will ask for the password. If I launch almost immediately, it won't. However, if I wait a long enough time, it will ask for the password again. During all of this, the helper tool does not change.
Can anyone point to documentation that defines this as the correct behavior?
If anyone is interested, this (probably) turned out to be a bug and one has been filed. rdar://10280469
The way the system currently works is that it will ask for an admin password every time regardless of whether or not the SMJobBless function needs to install the helper tool or not. The bug is (probably) that a admin password request should not be made if the helper tool does not need to be installed (for example, it is already installed and has the same version as the one in the app bundle).
So, what this means is that the determination of whether or not the helper tool needs to be installed needs to be made before a call to SMJobBless and SMJobBless should only be called if it is already known the helper tool needs to be installed.
In my case, I only need to check whether the tool is installed (SMJobCopyDictionary handles this) and, if the tool is installed, whether or not it's version is older then the version of the tool in my app bundle.
Some (incomplete) code to check whether the tool is installed and what the versions are is below.
There is another alternative to do a version check of the helper tool which is for the helper tool to receive a request for it's version and for it to send a version reply back. Personally, I like the method below, but wanted to mention this alternative as it may be the best path in some situations.
NSDictionary* installedHelperJobData;
installedHelperJobData = (NSDictionary*)SMJobCopyDictionary( kSMDomainSystemLaunchd, (CFStringRef)#"com.apple.bsd.SMJobBlessHelper" );
NSString* installedPath = [[installedHelperJobData objectForKey:#"ProgramArguments"] objectAtIndex:0];
NSURL* installedPathURL = [NSURL fileURLWithPath:installedPath];
NSDictionary* installedInfoPlist = (NSDictionary*)CFBundleCopyInfoDictionaryForURL( (CFURLRef)installedPathURL );
NSString* installedBundleVersion = [installedInfoPlist objectForKey:#"CFBundleVersion"];
NSInteger installedVersion = [installedBundleVersion integerValue];
NSLog( #"installedVersion: %ld", (long)installedVersion );
NSBundle* appBundle = [NSBundle mainBundle];
NSURL* appBundleURL = [appBundle bundleURL];
NSURL* currentHelperToolURL = [appBundleURL URLByAppendingPathComponent:#"Contents/Library/LaunchServices/com.apple.bsd.SMJobBlessHelper"];
NSDictionary* currentInfoPlist = (NSDictionary*)CFBundleCopyInfoDictionaryForURL( (CFURLRef)currentHelperToolURL );
NSString* currentBundleVersion = [currentInfoPlist objectForKey:#"CFBundleVersion"];
NSInteger currentVersion = [currentBundleVersion integerValue];
NSLog( #"currentVersion: %ld", (long)currentVersion );
Related
I'm working on a little iOS app for my own use (initially, at least) and I thought I would use Bonjour to configure networking between two iOS devices.
My server's interface defines
NSNetService *netService;
and the implementation uses the following code to advertise its existence:
const NSString *kSEBonjourServiceDomain = #""; //use defaults
const NSString *kSEBonjourServiceName = #"_test._tcp.";
//...
netService = [[NSNetService alloc] initWithDomain:(NSString *)kSEBonjourServiceDomain type:(NSString *)kSEBonjourServiceName name:#"" port:sin.sin_port];
if (netService)
{
netService.delegate = self;
netService.includesPeerToPeer = YES;
[netService publish];
}
else NSLog(#"Failed to create NSNetService");
When I start the server, my NSNetService object does call the -netServiceDidPublish: delegate method, but when I use Discovery to browse the bonjour services on my network, my service doesn't appear. Apart from the const strings, this code is the same as some code in one of my Mac apps (which works as expected) and also very similar to Apple's sample code.
I've gone through all the options in Xcode's capabilities tab in case I needed to add something there, but I can't see anything relevant. I've also read a bunch of documents and tutorials that don't mention having to do anything more than that, but it wouldn't surprise me to learn that this only works on iOS if you set an obscure build setting to a string that you had to learn about by reading the Human Interface Guidelines for watchOS last September.
So, can anyone enlighten me? What am I missing?
Thanks in advance!
It turned out that due to a silly error elsewhere in my code, sin.sin_port was set to 0 by the time I was registering my NSNetService. Although NSNetService doesn't see this as a problem, it was causing the service to be ignored by service browsers.
So the answer is, a service with a port of 0 will register without error, but won't work.
Thanks to gaige for the assistance.
How do you change the user agent used by WKWebview?
With the older WebView, I could write the following to change the user agent:
[myWebView setCustomUserAgent:#"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_4)
AppleWebKit/537.77.4 (KHTML,like Gecko) Version/7.0.5 Safari/537.77.4"];
Very simple in Swift. Just place the following into your App DelegatedidFinishLaunchingWithOptions.
NSUserDefaults.standardUserDefaults().registerDefaults(["UserAgent" : "Custom Agent"])
If you want to append to the existing agent string then:
let userAgent = UIWebView().stringByEvaluatingJavaScriptFromString("navigator.userAgent")! + " Custom Agent"
NSUserDefaults.standardUserDefaults().registerDefaults(["UserAgent" : userAgent])
Note: You will need to uninstall and reinstall the App to avoid appending to the existing agent string.
I don't have an answer for this. However, some pointers from my research so far:
In iOS, it's possible to set a custom user agent for a UIWebView like this:
NSDictionary *dictionary = [NSDictionary dictionaryWithObjectsAndKeys:agent, #"UserAgent", nil];
[[NSUserDefaults standardUserDefaults] registerDefaults:dictionary];
In OSX, there was a setCustomUserAgent method for WebView elements that did the trick.
However, this doesn't work for WKWebView (at least, in OSX). I couldn't find any documentation about it from Apple, either.
Hope somebody can help!
I ran into the same issue, but managed to work around it using a combination of loadHTMLString on the WKWebView and a NSMutableURLRequest to do the heavy lifting.
My search on how to call some method on the WKWebView itself lead me to http://trac.webkit.org/changeset/165594, which implies there is a private method _setCustomUserAgent to do this. I'm not proficient enough in cocoa/swift to figure this one out.
I ended up using the code below, as I really only need to fetch the contents of a single URL and display it, but it may be helpful in some way.
What it does is simply loading the contents of an URL into the WKWebView as string, I suspect you may lose back/forward navigation and such, and it will only work for the initial page display, as the WKWebView will take over clicks and asset loading.
(please note, this example is written in Swift and not Objective-C)
self.webView = WKWebView(frame: webViewRect, configuration: webViewConfig)
// create the request
let url = NSURL(string: "https://example.com/")
let request = NSMutableURLRequest(URL: url!)
request.setValue("YourUserAgent/1.0", forHTTPHeaderField: "User-Agent")
NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue.mainQueue()) {(response, data, error) in
let content = NSString(data: data, encoding: NSUTF8StringEncoding)
self.webView!.loadHTMLString(content!, baseURL: url)
}
It wouldn't be ideal but you could likely implement a custom NSURLProtocol handler to intercept HTTP requests and modify them with your custom user-agent header. I don't think this would work on iOS since WKWebView makes requests out-of-process and bypasses any registered NSURLProtocols. But it might work on OS X?
With a user entered URL in a text field, you can completely control the NSURLRequest using an NSMutableURLRequest object, and set the header field for it.
However, with things the user actually clicks on within the web view, you're kind of not in control from obvious and clearly documented Objective-C land.
I do not see any documented way beyond what WKWebView seems to push things toward, JavaScript. So, that means you can do things like posted here:
Mocking a useragent in javascript?
Using the script injection APIs.
This is why I do not like WKWebView. I want to like it, but I do not want to learn to do half of everything in JavaScript.
So, you can create a WKUserScript object to do this, and set its injection time to WKUserScriptInjectionTimeAtDocumentStart. That will enable you to handle things requested from page elements within the document, as long as the page itself is not loading other scripts that conflict.
First Quit Safari. Then open Terminal and paste this command, and press enter:
defaults write com.apple.Safari CustomUserAgent "\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_0) AppleWebKit/538.46 (KHTML, like Gecko) Version/7.1 Safari/537.85.7\""
Open Safari and you're done.
To undo this change, close Safari and paste this command into terminal:
defaults delete com.apple.Safari CustomUserAgent
Sometimes a restart may be required to get these changes to stick, not sure why, could be a cache thing.
Briefly
I want to code a status bar app that will send commands through an https connection using PHP / cURL.
What I Have and Know
Mac OS X running Mountain Lion (with X-code)
Extra-beginner coding skills in Objective-C
An API's documentation with instructions on what text to send via PHP/cURL
YouTube tutorial: Xcode 4.1 Tutorial - Create a menu bar application (NSStatusItem / StatusBar app) + DOWNLOAD
Interest in starting simple, but building to more complexity
What I Don't Have or Know
How to code the POST/GET commands into the app so that they fire upon click.
The Specific Use Case
I want to create a very basic status bar app that does two things related to the Cloudflare CDN / proxy app. CloudFlare's API allows for commands to a user's account sent over https with PHP / CURL commands.
I'd like to build a status bar app with hard wired commands executable via click. The click would perform either a mode change (like entering development mode) for a website, or a cache purge for the website. These are tasks that can be performed in the front end of the CloudFlare website and the API docs give some very clear direction on how to structure the GET/POST requests (so no help is needed on that end of things).
Gratitudocity
I'm super grateful in advance to anyone who can provide me with some pointers, resources, or additional things to think about.
Well I am not going to answer the part about setting up the status bar and adding status bar items and connecting them to actions in your controller, but I will point you in the direction of NSURLConnection and NSMutableURLRequest, rather than curl.
Example:
NSMutableURLRequest * req = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:#"www.example.com"]];
[req setAllHTTPHeaderFields:#{#"header1":#"value1"}];
[req setHTTPBody:[NSData data]];//not really doing anything
[req setHTTPMethod:#"GET"];
NSURLResponse * respMeta = nil; //will contain returned headers, etc
NSError * err = nil; // on failure will contain error object
NSData * response = [NSURLConnection sendSynchronousRequest:req returningResponse:&respMeta error:&err];
I am working on a Mac & iOS App, with iCloud CoreData in between to synchronize the data.
When I update some thing from iOS App, and the updates are already migrated to the PersistentStore in Mac App while the Mac App is running. The problem is I cannot find an effective way to force the NSArrayController to reload all data from the store.
tried -(void) fetch:(id)sender; only can see the delete or added entity, but the updated model property not refreshed...
Please help. Thanks
If you see the latest data in the managed object context, but not in the array controller, you want:
[_yourManagedObjectContext processPendingChanges];
[_yourArrayController fetchWithRequest:nil merge:YES error:&error];
[_yourArrayController rearrangeObjects];
I use this in a Mac/iOS iCloud app to update the Mac app's data when the iCloud store changes.
Following is the reply from Apple's Developer Technical support. It worked for me. Thanks all for providing solutions.
For the OS X app, when reloadFetchResults is called, you can ask the NSManagedObjectContext to reset itself and perform the fetch again.
- (void)reloadFetchedResults:(NSNotification *)note
{
NSDictionary *userInfoDict = [note userInfo];
NSManagedObjectContext *moc = self.coreDataController.mainThreadContext;
// this only works if you used NSMainQueueConcurrencyType
// otherwise use a dispatch_async back to the main thread yourself
//
[moc performBlock:^{
[self mergeiCloudChanges:userInfoDict forContext:moc];
[moc reset];
[self.tableArrayController fetch:self];
}];
}
I've found that
[self.managedObjectContext reset];
[myArrayController fetch:self];
forces my NSTableView (with NSArrayController) to re-populate and display newly processed NSManagedObjects.
in case someone else finds this...
in my case, I was building the array from user interaction (not Core Data), and then trying to show the tableView when they were done (on the same window)... and of course... seeing nothing!
[arrayController rearrangeObjects];
just before I wanted to show the tableView fixed it for me.
I've got a problem with my CoreData, but I'm sure I'm doing something wrong conceptually.
I'm trying to access a CoreData sql file on one machine in my network from another machine. I'm trying to do this from a cluster-like application. Each machine has the same copy of the software and needs to point to the database on this one machine.
My model and context load fine for the machine that the database is on. The other machine, gives me error 13400 NSPersistentStoreInvalidTypeError
Here's the bit of code:
NSError *error = nil;
NSURL *mdlurl = [NSURL fileURLWithPath: [[NSBundle mainBundle] pathForResource:#"OsiriXDB_DataModel" ofType:#"mom"]];
_model = [[NSManagedObjectModel alloc] initWithContentsOfURL: url];
NSURL *dburl = [NSURL URLWithString:[NSString stringWithUTF8String:_DBPath.c_str()]];
// The dburl has a format like: file://192.168.0.2/Users/slate/Documents/OsiriX%20Data/Database.sql which addresses the machine the data sits on.
_storeCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel: _model];
_context = [[NSManagedObjectContext alloc] init];
[_context setPersistentStoreCoordinator: _storeCoordinator];
if (![_storeCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:dburl options:nil error:&error]) {
NSLog(#"Error loading store: %#", error); // Error Shows up Here
NSLog(#"MOM: %#",_model); // Model looks OK. Lots of print outs, with the correct names and stuff. (so technical).
}
I confess to not knowing a ton about CoreData. Is it because it's on a different machine? I read this online but I don't think that's my issue. If it is, I have no idea how to fix it because I can't find any .xml files in my ~/Library/Application\ Support/ directory relating to either MyApp or OsiriX which is the program that created the database.
Am I doing the wrong thing to load CoreData across a network?
If not, what should I be doing?
Thanks,
The error indicates that persistent store coordinator thinks that the file is not the proper format for a NSSQLiteStoreType. That suggest that the file was found. If it couldn't locate the file or access the directory you would get another error.
I'm not sure what you problem is specifically but I can tell you in general that Core Data is not intended as a concurrent database. It's not even really a database at all. It's actually a runtime object graph management system intended to manage an app's model layer with persistence tacked on the side as an option. There are no Core Data options for controlling multiple instances of an app simultaneously accessing the same store. You might be able to do so by setting the store as readonly but I don't know for sure.
It sounds like you need a real database running on your server.