Edit Info.plist programmatically in a jailbroken app - ios

How can I edit the Info.plist file in a jailbroken app I'm writing? I know it's not normally possible but given the fact that this will be released in Cydia, I feel like there must be a way. I'm not savvy on file modifications in a jailbroken environment so any info is appreciated.
The reason I want to edit the Info.plist file is to register for a URL scheme programmatically. So if there's an alternative way to accomplish that, I'd love to hear it :-)

If you want to programmatically edit your own app's Info.plist file as it runs, you can use this code:
- (BOOL) registerForScheme: (NSString*) scheme {
NSString* plistPath = [[NSBundle mainBundle] pathForResource:#"Info"
ofType:#"plist"];
NSMutableDictionary* plist = [NSMutableDictionary dictionaryWithContentsOfFile: plistPath];
NSDictionary* urlType = [NSDictionary dictionaryWithObjectsAndKeys:
#"com.mycompany.myscheme", #"CFBundleURLName",
[NSArray arrayWithObject: scheme], #"CFBundleURLSchemes",
nil];
[plist setObject: [NSArray arrayWithObject: urlType] forKey: #"CFBundleURLTypes"];
return [plist writeToFile: plistPath atomically: YES];
}
and if you call it like this:
BOOL succeeded = [self registerForScheme: #"stack"];
then your app can be open with URLs like this:
stack://overflow
However, if you look at the Info.plist file permissions:
-rw-r--r-- 1 root wheel 1167 Oct 26 02:17 Info.plist
You see that you cannot write to that file as user mobile, which is how your app will run normally. So, one way to get around this is to give your app root privileges. See here for how to do that.
After you use this code, and give your app root privileges, it still might be necessary to respring before you see your custom URL scheme recognized. I didn't have time to test that part.

Here is how I solved it for Facebook SDK in Swift
var appid: NSMutableDictionary = ["FacebookAppID": "123456789"]
var plistPath = NSBundle.mainBundle().pathForResource("Info", ofType: "plist")
appid.writeToFile(plistPath!, atomically: true)
var appName: NSMutableDictionary = ["FacebookDisplayName": "AppName-Test"]
appName.writeToFile(plistPath!, atomically: true)
var urlStuff = NSMutableDictionary(contentsOfFile: plistPath!)
var urlType = NSDictionary(objectsAndKeys: "com.appprefix.AppName", "CFBundleURLName", NSArray(object: "fb123456789"), "CFBundleURLSchemes")
urlStuff?.setObject(NSArray(object: urlType), forKey: "CFBundleURLTypes")
urlStuff?.writeToFile(plistPath!, atomically: true)

Related

Using XCode to access app data

I am using the Device tab in Xcode to view the content of my app. Here is what I see:
The app has iTunes file sharing enabled:
However I am unable to see the App in iTunes (under my Apps) even after synchronising the phone.
I am wondering if there is an alternative way to access the "test-data.csv" file that my app is generating.
XCode does seem only to list the file but does not allow me to click on the file to open it.
EDIT:
This is the file path I use:
NSString *filePath = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject] stringByAppendingPathComponent:#"test-data.csv"];
As you are using iTunes sharing. You will be able to see the data in iTunes only. Make sure that you are selecting your device first in the iTunes and then going to Application tab. If you are not seeing the apps there, this means your app might be showing in the sharing section. Scroll down a bit and you will see the File Sharing section-> Apps. See if you are able to find your application there.
Hope you might get your app there.
See the screeshot:-
Are you sure you are saving your data in the public folder of your app and not in the private one?
Public folder:
func getDocumentsDirectory() -> NSString {
let paths = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)
let documentsDirectory = paths[0]
return documentsDirectory
}
Private folder:
func getLibraryDirectory() -> NSString {
var paths = NSSearchPathForDirectoriesInDomains(.LibraryDirectory, .UserDomainMask, true)
let libraryDirectory = paths[0]
return libraryDirectory
}
You can send this file by attaching with email and then download from your email on other desktop etc.
MFMailComposeViewController *mailer = [[MFMailComposeViewController alloc] init];
mailer.mailComposeDelegate = self;
[mailer setSubject:#"CSV File"];
[mailer addAttachmentData:[NSData dataWithContentsOfFile:#"PathToFile.csv"]
mimeType:#"text/csv"
fileName:#"FileName.csv"];
[self presentModalViewController:mailer animated:YES];

Getting list of images from a folder in image.xcassets

I have a folder in image.xcassets which has more than 50 images for both iPhone and iPad.
I don't want to hard code all the names programatically. Can I get the list of images in that folder in an NSArray?
I'm not sure if this fully answers your question, but should you normally do this with the method
- (NSArray *)contentsOfDirectoryAtURL:(NSURL *)url includingPropertiesForKeys:(NSArray *)keys options:(NSDirectoryEnumerationOptions)mask error:(NSError **)error ?
This is a code snippet from a program I wrote to get all the images from a directory:
-(void)getContentOfImageDirectory
{
//Emptying the image directory content array
[_imageDirectoryContent removeAllObjects];
//Using NSFileManager to load the content of the image directory in a temporary array
NSFileManager *fm = [NSFileManager defaultManager];
NSArray *tempArray = [fm contentsOfDirectoryAtURL: _imageDirectory includingPropertiesForKeys: _imageProperties options: NSDirectoryEnumerationSkipsPackageDescendants error: nil];
//Copy the temporary array into the imageDirectoryContent before returning the NSMutableArray imageDirectoryContent
[_imageDirectoryContent addObjectsFromArray:tempArray];
}
The variable _imageProperties is just an array of "common file system resource keys" as Apple calls them. The variable _imageDirectory is the URL from which you want to get the files.
hope this helps.
I'm sorry to annoy you and misunderstood your question. However, if I use the URL file///User/<Your Userid>/your file path to the program/Images.xcassets/ I get the content of that directory.
On the other hand if I use URLsForDirectory:NSApplicationDirectory inDomains:NSUserDomainMask
and then
URLByAppendingPathComponent:#"Your Application container/Contents/Resources"
I can read all the image files of any fully compiled and operational application. I'm not aware of determining the application's resource folder in any other way.
This is a code snippet for accessing the resources directory of the Windows 7 applications folder from parallels.
-(id)initWithStartURL
{
self = [super init];
if(self)
{
//Initiating the file manager
NSFileManager *fm = [NSFileManager defaultManager];
//Getting the applications directory
NSArray *listOfURLs = [fm URLsForDirectory:NSApplicationDirectory inDomains:NSUserDomainMask];
if([listOfURLs count] >= 1)
{
_tempDirectory = [listOfURLs objectAtIndex:0];
}
_imageDirectory = [_tempDirectory URLByAppendingPathComponent:#"Windows 7 Applications.app/Contents/Resources"];
}
return self;
}

Loading image from folder using NSBundle and plist

Trying to Display the load the image from the folder present on MAC desktop using path like (/Users/sai/Desktop/images/aaa.jpg) Which is created in plist file called Data.plist at item0.
As im using NSBundle it is diaplying the image path but not loading the image from the desktop .I have done a lots of research still couldn't find the solution .Plz help me .Here is the code
NSString *path=[[NSBundle mainBundle]pathForResource:#"Data" ofType:#"plist"];
NSData *plistXML = [[NSFileManager defaultManager] contentsAtPath:path];
NSString *errorDesc = nil;
NSPropertyListFormat format;
NSDictionary *temp = (NSDictionary *)[NSPropertyListSerialization propertyListFromData:plistXML mutabilityOption:NSPropertyListMutableContainersAndLeaves format:&format errorDescription:&errorDesc];
NSArray *array=[NSArray arrayWithArray:[temp objectForKey:#"images"]];
NSString *object=[array objectAtIndex:0];
NSLog(#"object at index i %#",[object lastPathComponent]);
NSString *image=[object lastPathComponent];
mImageView.image=[UIImage imageNamed:image];
[self.view addSubview:mImageView];
Here is the screen shot of Data.plist
Simple and Short answer : It is not Possible to get image from MAC desktop, So please it better to stop fighting with it :)
It is different for mobile (iPhone application) and desktop (mac application), but why you want to do this ?? you know that your iOS application install in Apple iPhone ???? if your application is related to iOS ? then how you connect it to MAC OS ??
Best and simple way is put this image in Application Bundle or your Application's document directory and get it.

FileExistAtPath working on Simulator, but not on Device

In my game, I'm saving stats of the player in a plist that I store in the Documents directory. I have an empty dictionary of each stats that should be saved named "Default_Stats.plist" so that if it's the first time the app is loaded, it will copy it in the appropriate directory so it could be loaded and overwritten at will. The problem is, every time my app is loaded, it doesn't recognize the "Stats.plist" and overwrite it with the Default Stats, resetting every stats the player have made... And weird enough, it was perfectly working on the simulator, but not on the device. Here's my code :
In this method I read the stats :
- (void) readStatsFromFile{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *statsPath = [[paths objectAtIndex:0] stringByAppendingPathComponent:#"Stats.plist"];
//Check if the file has already been created
if (![[NSFileManager defaultManager] fileExistsAtPath:statsPath]){
[self createStatsList];
}else{
stats = [[NSMutableDictionary dictionaryWithContentsOfFile:statsPath]retain];
}
}
Here's my creating method :
- (void) createStatsList{
NSString *statsPath = [[NSBundle mainBundle] bundlePath];
statsPath = [statsPath stringByAppendingPathComponent:#"Default_Stats.plist"];
stats = [[NSMutableDictionary dictionaryWithContentsOfFile:statsPath] retain];
[self writeStatsToFile];
}
And my writing method :
- (void) writeStatsToFile{
BOOL ok;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *statsPath = [[paths objectAtIndex:0] stringByAppendingPathComponent:#"Stats.plist"];
ok = [stats writeToFile:statsPath atomically:YES];
if (!ok) {
NSLog(#"Couldn't write to file");
}else
NSLog(#"Stats written succesfully!");
}
Please help, I really don't understand what's wrong! I hope I've made myself clear enough!
Use filepath instead of absolute path.
Maybe duplicates exist in your mac, which makes exists=true on simulator, but not on device.
The easiest way to check would be to NSLog the paths encountered. Refer to these tools - they allow console logs to be captured for release builds running on your device.
Most likely that your documents directory just doesn't exist - on the simulator you share a documents directory with everyone on the Mac; on the device everyone has his own directory. Use the file manager method
createDirectoryAtURL:url withIntermediateDirectories:YES
to make sure that the directory is there before you try writing there. (I tend to use the URL methods instead of the file path methods).
PS. I'd recommend having one method that returns the path or url that you want. It's a good habit not to duplicate your code again and again.
I would do pretty much that, like everything in one session:
gets the URL for the file in the Document folder;
if the file is not there yet, copies the file from bundle to the Documents folder;
that should be the method for that, I have defined some macros for avoiding mistyping the file's name in the code:
- (NSURL *)statsFileURL {
#define NSStringFromFileNameWithExtension(filename, extension) [(filename) stringByAppendingPathExtension:(extension)]
#define kExtension #"plist"
#define kDefaultStatsFileName #"Default_Stats"
#define kCustomStatsFileName #"Stats"
NSURL *_returnURL = nil;
NSFileManager *_fileManager = [NSFileManager defaultManager];
NSURL *_documentDirectory = [[_fileManager URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
NSURL *_myFileURLInDocumentFolder = [_documentDirectory URLByAppendingPathComponent:NSStringFromFileNameWithExtension(kDefaultStatsFileName, kExtension)];
if ([_fileManager fileExistsAtPath:[_myFileURLInDocumentFolder path]]) {
_returnURL = _myFileURLInDocumentFolder;
} else {
NSURL *_myFileURLInBundle = [[NSBundle mainBundle] URLForResource:kDefaultStatsFileName withExtension:kExtension];
if ([_fileManager fileExistsAtPath:[_myFileURLInBundle path]]) {
NSError *_error = nil;
if ([_fileManager copyItemAtURL:_myFileURLInBundle toURL:_myFileURLInDocumentFolder error:&_error]) {
if (_error == nil) {
_returnURL = _myFileURLInDocumentFolder;
} else {
// some error during copying
}
} else {
// some error during copying
}
} else {
// the file does not esixts at all, not even in the bundle
}
}
return _returnURL;
}
the URL always points inside the Documents folder, so you will have read/write access to the file – or will be nil if some error happens.
after you have the URL, you can restore back to file without any issue, and at some other point in runtime you can override the file for your convenience anytime.
NOTE: you may need to extend this code for a more detailed error handling, I put the comment only the places when you need to worry about potential errors.

Loading Local PDF File Into WebView

I am attempting to put the following functionality into an iOS app I am writing:
Ship a set of PDFs in the resources folder of the project in XCode
Copy the PDFs to the app directory
Open the PDF in a webview.
As far as I can see, the first two steps work ok (I've used FileManager to check fileExistsAtPath after the copy operation).
However, the webview is empty, and is erroring out ("the requested URL does not exist on server").
My code for the file open is as follows:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask, YES);
NSString *localDocumentsDirectory = [paths objectAtIndex:0];
NSString *pdfFileName = #"example.pdf";
NSString *localDocumentsDirectoryPdfFilePath = [localDocumentsDirectory
stringByAppendingPathComponent:pdfFileName];
pdfUrl = [NSURL fileURLWithPath:localDocumentsDirectoryPdfFilePath];
[webView loadRequest:[NSURLRequestWithURL:pdfUrl];
This works fine on the simulator, but doesn't work on the device
Are you sure you don't want to let the UIDocumentInteractionController do the heavy lifting for you?
UIDocumentInteractionController *dc = [UIDocumentInteractionController interactionControllerWithURL:fileURL];
dc.delegate = self;
[dc presentPreviewAnimated:YES];
As posted by Anna Karenina above, "The device is case-sensitive. Make sure the filename matches exactly"
As bshirley suggested UIDocumentInteractionController is a great option to present your PDF. Initially I tried using the 3rd party JSQWebViewController but I was getting a blank screen on device while on simulator it was working. UIDocumentInteractionController worked great for me! For Swift you can do:
let interactionController = UIDocumentInteractionController(url: fileURL)
interactionController.delegate = self
interactionController.presentPreview(animated: true)
and implement the delegate method:
// Mark: UIDocumentInteractionControllerDelegate
func documentInteractionControllerViewControllerForPreview(_ controller: UIDocumentInteractionController) -> UIViewController {
return UIApplication.shared.keyWindow!.rootViewController!
}

Resources