NSProgress not working with sandboxed items? - objective-c

I'm creating an NSProgress object to show a progress indicator underneat a file in my Downloads directory. The entitlements includes com.apple.security.files.downloads.read-write.
The indicator does not show when NSProgressFileURLKey points to the sandboxed file path, e.g. /Users/mdbraber/Library/Containers/com.mdbraber.TestApp/Data/Downloads/Test.pptx.download. It does work when NSProgressFileURLKey points to the direct download location which the sandbox links to e.g. /Users/mdbraber/Downloads/Test.pptx.download
Is this a bug or should I use something else for NSProgressFileURLKey to make this work?
#define KdownloadsPath NSSearchPathForDirectoriesInDomains(NSDownloadsDirectory, NSUserDomainMask, YES).firstObject
_downloadedPath = [KdownloadsPath stringByAppendingPathComponent:fileName];
_downloadingPath = [_downloadedPath stringByAppendingString:#".download"];
NSDictionary* info = [NSDictionary dictionaryWithObjectsAndKeys:
#"NSProgressFileOperationKindDownloading", #"NSProgressFileOperationKindKey",
[NSURL fileURLWithPath:_downloadingPath], #"NSProgressFileURLKey",
nil];
self.progress = [[NSProgress alloc] initWithParent:nil userInfo:info];
[self.progress setKind:#"NSProgressKindFile"];
[self.progress setPausable:NO];
[self.progress setCancellable:YES];
[self.progress setTotalUnitCount:_totalBytes];
[self.progress publish];
(updating the progress indicator happens elsewhere)

Related

ITLibrary gives me nothing but (null)

My program automates a radio station. There is lots of communication back and forth between it and iTunes. I programmed it with scripting bridge. Scripting bridge suffers from memory leaks. Each call to scripting bridge leaks a small amount of memory. Add a lot of calls to a program that runs 24/7 and I've got software that will run for something less than 24 hours, and then quit.
My first attempt at a solution was to minimize my calls to scripting bridge. In researching that end, I came across ItunesLibrary. It isn't working for me.
NSError *error = nil;
ITLibrary *library = [ITLibrary libraryWithAPIVersion:#"1.0" error:&error];
if (library)
{
NSArray *playlists = [[NSArray alloc]init];
playlists = library.allPlaylists;
NSArray *tracks = [[NSArray alloc]init];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"mediaKind == %d", ITLibMediaItemMediaKindSong];
tracks = [library.allMediaItems filteredArrayUsingPredicate:predicate];
NSLog(#"Playlists - %#",playlists);
NSLog(#"Tracks - %#",tracks);
}
This code is pretty much right out of Apple's docs. It should work - I think.
Before I added the predicate, I got some info on each of the podcasts in my iTunes library. In the nslog output, each of my playlists produces an entry similar to "". Each of my songs shows nothing more than (null).
All of the info is in iTunes. I can read it with scripting bridge. I can read it with AVAsset
AVAsset *asset = [AVURLAsset URLAssetWithURL:myUrl options:nil];
NSArray *metadata = [asset commonMetadata];
for ( AVMetadataItem* item in metadata )
{
NSString *key = [item commonKey];
NSString *value = [item stringValue];
NSLog(#"key = %#, value = %#", key, value);
}
With AVAsset I only get the song name, album name, and artist name. I need to access the rest of iTune's ID3 tags.
What have I don to break ItunesLibrary?
The secret to getting ItunesLibrary to work seems to be in the entitlements file. You need to add the key "com.apple.security.assets.music.read-only", and set it to YES. I got this by digging through a project on github.com.

How to create .plist file under /Library/LaunchAgents

I'm trying to develop a launch agent for macOS via Apple Doc
https://developer.apple.com/library/content/documentation/MacOSX/Conceptual/BPSystemStartup/Chapters/CreatingLaunchdJobs.html
One of my requirements is that the agent should work for all users. What I understood from above document is I have to put my .plist under "/Library/LaunchAgents" folder.
When I try to create this file programatically nothing happens with the below code.
NSMutableDictionary *plist = [[NSMutableDictionary alloc] init];
[plist setObject:#"test" forKey: #"test 1"];
NSString *userLaunchAgentsPath = [[NSString alloc] initWithFormat:#"%#", #"/Library/LaunchAgents/com.xxx.agent.plist"];
[plist writeToFile:userLaunchAgentsPath atomically:YES];
Probably the reason is a privilege issue. Do you have any ideas for solving this issue?
As to privileges, the plist should be owned by root and if you want the app to run as a different user, you can do that easily by providing the username/password in the plist. Your app is probably not running as root.

Objective c save to my plist

I have the plist file under the resources folder which is name is logical-app.plist
In ios8 if you want Gps will work then NSLocationAlwaysUsageDescription or NSLocationWhenInUseUsageDescription keys should be added into the Info.plist
I want to add NSLocationAlwaysUsageDescription or NSLocationWhenInUseUsageDescription keys in code in my appdelegate class.
Im trying to add the following code for solving this issue:
//write
filePath = #"/System/Library/CoreServices/SystemVersion.plist";
plistDict = [[NSMutableDictionary alloc] initWithContentsOfFile:filePath];
[plistDict setValue:#"1.1.1" forKey:#"NSLocationWhenInUseUsageDescription"];
[plistDict writeToFile:filePath atomically: YES];
//endwrite
//read plist
filePath = #"/System/Library/CoreServices/SystemVersion.plist";
plistDict = [[NSMutableDictionary alloc] initWithContentsOfFile:filePath];
value = [plistDict objectForKey:#"NSLocationWhenInUseUsageDescription"];
im trying to get
filePath = [[NSBundle mainBundle] pathForResource:#"ClickMobileCDV-Info" ofType:#"plist"];
and it is nil too....
But with no success.... when i read it from the plist it was nil...
How can i solve this issue?
You've completely got the wrong end of the stick. In order to add NSLocationAlwaysUsageDescription and NSLocationWhenInUseUsageDescription, you edit the app's plist file within Xcode and it's included in the app bundle.
There is no code needed to read/write system .plist files at runtime (the writing of which would fail anyway, due to permission errors).

NSWorkspace vs NSTask to start iTunes from a sandboxed app

I'm trying to run iTunes from my ObjectiveC app that runs in a sandbox.
Apple documentation mentions that 'child processes created with the NSTask class inherit the sandbox of the parent app'. The result is that when running iTunes, some permission error pops up and iTunes is closed.
When running it using NSWorkspace methods it does not crash and seems it's running outside any sandbox. Does that mean that i have permission to insert some dynamic library at launch time using DYLD_INSERT_LIBRARIES ?
Here's some code:
NSString* appPath = #"/Applications/iTunes.app";
// Get application URL
NSBundle *targetBundle = [NSBundle bundleWithPath:appPath];
NSURL *applicationURL = [targetBundle executableURL];
NSString* libPath = [NSHomeDirectory() stringByAppendingPathComponent:#"myLib.dylib"];
// Environment setup
NSDictionary *config = nil;
NSDictionary *env = [NSDictionary dictionaryWithObject:libPath forKey:#"DYLD_INSERT_LIBRARIES"];
NSNumber *arch = [NSNumber numberWithInt:(int)NSBundleExecutableArchitectureI386];
config = [[NSDictionary alloc] initWithObjectsAndKeys:env, NSWorkspaceLaunchConfigurationEnvironment,
arch, NSWorkspaceLaunchConfigurationArchitecture, nil];
// Launch application
[[NSWorkspace sharedWorkspace] launchApplicationAtURL:applicationURL
options:0
configuration:config
error:nil];
[config release];
When the above code runs in a sandbox iTunes starts without any lib. Any suggestion?
Thanks,
Vlad.

iOS Record audio and store in playlist

I'm looking to record audio from the iPhone microphone via a record button. I have downloaded and got to grips with the sample project Apple provides to do this (SpeakHere).
However as a next step, I'd like to save the users recording in a "playlist" style (not using the iTunes playlist, rather a local playlist).
Is it possible to do this using Objective-C (as opposed to the C implementation currently provided) - ideally CoreData would be used to store the audio.
Thanks
Here's how I did it:
1) Find the temp file that the SpeakHere code creates -- look for the .caf extension in the SpeakHereController class. Then move that temp file to your application directory with something like this:
NSString *myFileName = #"MyName"; // this would probably come from a user text field
NSString *tempName = #"recordedFile.caf";
NSString *saveName = [NSString stringWithFormat:#"Documents/%#.caf", myFileName];
NSString *tempPath = [NSTemporaryDirectory() stringByAppendingPathComponent:tempName];
NSString *savePath = [NSHomeDirectory() stringByAppendingPathComponent:saveName];
2) Save some metadata about the file, at least its name. I'm putting that into NSUserDefaults like this:
NSDictionary *recordingMetadata = [NSDictionary dictionaryWithObjectsAndKeys:
myFileName, #"name",
[NSDate date], #"date",
nil];
[self.savedRecordings addObject:recordingMetadata]; // savedRecordings is an array I created earlier by loading the NSUserDefaults
[[NSUserDefaults standardUserDefaults] setObject:self.savedRecordings forKey:#"recordings"]; // now I'm updating the NSUserDefaults
3) Now you can display a list of saved recordings by iterating through self.savedRecordings.
4) When the user selects a recording, you can easily initialize an AVAudioPlayer with the selected file name and play it back.
5) To let users delete recordings, you can do something like this:
NSString *myFileName = #"MyName";
// delete the audio file from the application directory
NSString *fileName = [NSString stringWithFormat:#"Documents/%#.caf", myFileName];
NSString *filePath = [NSHomeDirectory() stringByAppendingPathComponent:fileName];
[[NSFileManager defaultManager] removeItemAtPath:filePath error:NULL];
// delete the metadata from the user preferences
for (int i=0; i<[self.savedRecordings count]; i++) {
NSDictionary *thisRecording = [self.savedRecordings objectAtIndex:i];
if ([myFileName isEqualToString:[thisRecording objectForKey:#"name"]]) {
[self.savedRecordings removeObjectAtIndex:i];
break;
}
}
[[NSUserDefaults standardUserDefaults] setObject:self.savedRecordings forKey:#"recordings"];
Note that if you save the audio files into the Documents folder and enable "Application supports iTunes file sharing" in your info.plist, then users can copy their recordings out of the app and save them onto their computers ... a nice feature if you want to offer it.