Loading content from plist to NSDictionary not working - ios

I have a rather large plist that I am trying to load data from. Here is an example of the plist, but there are 580 records so I can't put them all here:
<plist version="1.0">
<array>
<dict>
<key>Grave #</key>
<string></string>
<key>Last Name</key>
<string>?</string>
<key>First Name</key>
<string>Ada Lou daughter of</string>
<key>Dates</key>
<string>?-? Unable to read stone</string>
<key>Notes</key>
<string></string>
<key></key>
<string></string>
</dict>
<dict>
<key>Grave #</key>
<string></string>
<key>Last Name</key>
<string>?</string>
<key>First Name</key>
<string>Stone w/ Cherokee syllabry and also in english says: Here we rest</string>
<key>Dates</key>
<string></string>
<key>Notes</key>
<string></string>
<key></key>
<string></string>
</dict>
I have my property in my .h file
#property (nonatomic, strong) NSDictionary *graves;
Synthesized in my .m file:
#synthesize graves;
Here is my code that loads the file in my view did load function:
NSString *file = [[NSBundle mainBundle] pathForResource:#"RossCemeteryList" ofType:#"plist"];
NSFileManager *fileManager = [NSFileManager defaultManager];
if ([fileManager fileExistsAtPath:file]) {
NSLog(#"The file exists");
} else {
NSLog(#"The file does not exist");
}
graves = [[NSDictionary alloc] initWithContentsOfFile:file];
NSLog(#"%i", [graves count]);
The NSLog messages says it finds the file, the there are 0 rows in the second NSLog message. I can try to spit out the contents graves using "NSLog (#"%#", graves);" and it returns NULL.
I am a little lost. Any help appreciated.

You are trying to create an NSDictionary object and initialize it from the contents of that plist while that plist actually contains an NSArray as its root object. You need to change your NSDictionary into an NSArray or NSMutableArray.

I don't know if you're still looking for a solution, but this code should allow you to load a plist file and determine the type of its contents:
// load a Property List ("plist") file (fully-qualified path) into a generic object, if
// it's available...
- ( NSObject * ) testLoadPListData: ( NSString * const ) plistPath
{
// load the file data into a raw data object...
NSData * const data = [ NSData dataWithContentsOfFile: plistPath ];
// fields returned from property list creation...
NSPropertyListFormat fmt = 0;
NSError * err = nil;
// load the file data into a serialized property list object...
NSPropertyListSerialization * plist = [ NSPropertyListSerialization propertyListWithData: data
options: NSPropertyListImmutable
format: &fmt
error: &err
];
// if there was an error creating the serialized object...
NSString * const errorText = err ? [ err description ] : nil;
if ( errorText )
{
// log the error string...
NSLog( #"error while reading data from file \"%#\": %#"
, plistPath
, errorText
);
} // end error creating serialized object
#if defined( DEBUG )
//////////////////////////////
// //
// DEBUG PURPOSES ONLY... //
// //
//////////////////////////////
// if this file is in a format that can be readily translated into one of our target data types...
if ( plist )
{
// if this property list file contains a dictionary...
if ( [ plist isKindOfClass: [ NSDictionary class ] ] )
{
// dump the contents of the dictionary to the debug output window...
NSDictionary * const dict = ( NSDictionary * )( plist );
__CCLOG( #"<Dictionary>%#\n</Dictionary>", dict );
} // end is dictionary plist file
// if this property list file contains an array...
else if ( [ plist isKindOfClass: [ NSArray class ] ] )
{
// dump the contents of the array to the debug output window...
NSArray * const arr = ( NSArray * )( plist );
__CCLOG( #"<Array>%#</Array>", arr );
} // end is array plist file
} // end valid file format
#endif // defined( DEBUG )
return plist;
} // end testLoadPListData

Related

Objective C Reading Complete File To String

I'm trying to make a file manager for iOS (my code has an exploit built in so it works).
The main code for my application is written in Swift and the code for running the exploit and reading files is written in Objecive-C.
This is the code I'm currently using in Objective-C:
NSString* readFile(NSString* file) {
char* buffer;
long lSize;
char* convertedFileName = [file UTF8String];
char* newFileName = deleteLastChar(convertedFileName); >> removes last / from path
char ch;
FILE *fp=fopen(newFileName, "r");
fseek( fp , 0L , SEEK_END);
lSize = ftell( fp );
rewind( fp );
buffer = calloc( 1, lSize+1 );
if( !buffer ) fclose(fp),fputs("memory alloc fails",stderr),exit(1);
if( 1!=fread( buffer , lSize, 1 , fp) )
fclose(fp),free(buffer),fputs("entire read fails",stderr),exit(1);
printf(buffer);
fclose(fp);
return [[NSString alloc] initWithUTF8String:buffer];
}
And this is the code for Swift:
func openFile(file: String) {
let fileContents = readFile(file)
print(fileContents) >> outputs nil
}
This is an example of a file I'm trying to open (plist file):
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>canvas_height</key>
<integer>1136</integer>
<key>canvas_width</key>
<integer>640</integer>
</dict>
</plist>
This is what the printf in the Objective-C code returns:
bplist00\322]canvas_height\canvas_width\240\332
(+
And Swift says the string is nil
The file is being read but there is still something wrong. Any ideas on how to fix this or a better way to handle this?

OSX Generated key can't encrypt (SecKeyCreateRandomKey & SecKeyCreateEncryptedData)

I am basically follow this guide to generated a private key, copy the public key, and then encrypt a message. However, it gives me the error (OSStatus error -67712 - CSSM Exception: -2147415791 CSSMERR_CSP_INVALID_KEY_REFERENCE).
Initially, I thought I set the attributes incorrectly. However, if I create the public key (with the same attributes) by the SecKeyGeneratePair() function, everything works perfectly. Is it weird?
void TestEncryptDecrpt() {
OSStatus status;
NSData* tag = [#"com.example.keys.mykey" dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary* attributes =
#{ (id)kSecAttrKeyType: (id)kSecAttrKeyTypeRSA,
(id)kSecAttrKeySizeInBits: #1024,
(id)kSecPrivateKeyAttrs:
#{ (id)kSecAttrIsPermanent: #YES,
(id)kSecAttrApplicationTag: tag,
},
};
CFErrorRef error = NULL;
SecKeyRef privateKey = SecKeyCreateRandomKey((__bridge CFDictionaryRef)attributes, &error);
SecKeyRef publicKey = SecKeyCopyPublicKey(privateKey);
// *** it will work if I generate the key by SecKeyGeneratePair ***
// status = SecKeyGeneratePair( (__bridge CFDictionaryRef)attributes, &publicKey, &privateKey );
// start encrypt and decrypt a message
static char const kMessage[] = "This is a secret!\n";
SecKeyAlgorithm algorithm = kSecKeyAlgorithmRSAEncryptionRaw;
BOOL canEncrypt = SecKeyIsAlgorithmSupported(publicKey, kSecKeyOperationTypeEncrypt, algorithm);
NSData* plainData = [NSData dataWithBytes:kMessage length:sizeof(kMessage)];
canEncrypt &= ([plainData length] < (SecKeyGetBlockSize(publicKey)-130));
NSData* cipherText = nil;
if (canEncrypt) {
CFErrorRef error = NULL;
cipherText = (NSData*)CFBridgingRelease( SecKeyCreateEncryptedData(publicKey, algorithm, (__bridge CFDataRef)plainData, &error));
if (!cipherText) {
NSError *err = CFBridgingRelease(error); // ARC takes ownership
// Handle the error. . .
NSLog(#"error = %#, %#", [err userInfo], [err localizedDescription]);
}
}
}
Problem solved. You need the "kSecAttrIsPermanent" property as well in the public key setting.
Not sure why this is not mentioned in the example.

How to change "Call Waiting" programmatically in iOS (jailbreak device)?

I wan't to turn on/off the call waiting feature of iPhone (that can be found at Settings > Phone > Call Waiting) programmatically.
What I have figured out is that the phone settings calls -setCallWaitingEnabled:specifier: method from PhoneSettingsCallWaitingController class found here. I found it in /System/Library/PreferenceBundles/MobilePhoneSettings.bundle/Call Waiting.plist:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>id</key>
<string>com.apple.preferences.phone.call-waiting</string>
<key>items</key>
<array>
<dict>
<key>cell</key>
<string>PSGroupCell</string>
</dict>
<dict>
<key>cell</key>
<string>PSSwitchCell</string>
<key>get</key>
<string>callWaitingEnabled:</string>
<key>label</key>
<string>Call Waiting</string>
<key>set</key>
<string>setCallWaitingEnabled:specifier:</string>
</dict>
</array>
<key>title</key>
<string>Call Waiting</string>
</dict>
</plist>
I loaded the bundle manually and initialized the above class and called the method I said but nothing happens. Here's the code:
NSBundle *b = [NSBundle bundleWithPath:#"/System/Library/PreferenceBundles/MobilePhoneSettings.bundle"];
BOOL success = [b load];
if (success)
{
NSLog(#"\n\n\n\n Bundle load successfull! \n\n\n\n ");
Class PhoneSettingsCallWaitingController = NSClassFromString(#"PhoneSettingsCallWaitingController");
id tc = [[PhoneSettingsCallWaitingController alloc] init];
[tc setCallWaitingEnabled:0 specifier:0];
} else NSLog(#"\n\n\n\n Bundle load failure! \n\n\n\n ");
I don't know if I'm in the correct path. Is there another way to turn on/off the Call Waiting setting?
Declarations for CoreTelephony.framework private APIs
CF_EXTERN_C_BEGIN
CF_EXPORT CFNotificationCenterRef CTTelephonyCenterGetDefault();
CF_EXPORT void CTTelephonyCenterAddObserver(CFNotificationCenterRef center, const void *observer, CFNotificationCallback callBack, CFStringRef name, const void *object, CFNotificationSuspensionBehavior suspensionBehavior);
CF_EXPORT void CTSettingRequest(NSDictionary* dict);
CF_EXPORT void CTSettingSave(NSDictionary* dict);
CF_EXPORT NSString* kCTSettingCallClassVoice;
CF_EXPORT NSString* kCTSettingTypeCallWaiting;
CF_EXPORT NSString* kCTSettingType;
CF_EXPORT NSString* kCTSettingCallClass;
CF_EXPORT NSString* kCTSettingEnabled;
CF_EXTERN_C_END
Setting requests and saves are done asynchronously. Results will be sent to telephony center callback:
void SettingCallback(CFNotificationCallback center, void* observer, NSString* name, const void* objec, NSDictionary* userInfo)
{
if ([name isEqualToString:#"kCTSettingRequestSuccessNotification"])
{
//Setting request results are in 'userInfo' argument
}
else if ([name isEqualToString:#"kCTSettingRequestErrorNotification"])
{
//Setting request error
}
else if ([name isEqualToString:#"kCTSettingSaveSuccessNotification"])
{
//Setting saved
}
else if ([name isEqualToString:#"kCTSettingSaveErrorNotification"])
{
//Setting save error
}
}
...
CTTelephonyCenterAddObserver(CTTelephonyCenterGetDefault(), NULL, SettingCallback, NULL, NULL, CFNotificationSuspensionBehaviorHold);
Request call waiting setting:
CTSettingRequest(#{#"STSettingTypeUniqueIdentifier" : kCTSettingTypeCallWaiting,
kCTSettingCallClass : kCTSettingCallClassVoice,
kCTSettingType : kCTSettingTypeCallWaiting});
Save call waiting setting (in this case, enabled):
CTSettingSave(#{#"STSettingTypeUniqueIdentifier" : kCTSettingTypeCallWaiting,
kCTSettingCallClass : kCTSettingCallClassVoice,
kCTSettingType : kCTSettingTypeCallWaiting,
kCTSettingEnabled : #YES});

Is it possible to parse an NSInputStream using SBJson4?

I am trying to a read JSON file containing contact info objects consisting of NSString types and NSMutableArrays. Currently, I am using NSData to read the whole file and then parsing through it. I have utilised Stig's example as mentioned here: SBJson4Parser Example
SBJson4ValueBlock block = ^(id obj, BOOL *stop) {
NSLog(#"Found: %#", #([obj isKindOfClass:[NSDictionary class]]));
//contactsData *contact = obj;
NSDictionary *contact = obj;
NSLog(#"Contact: %#",contact);
/* NSString *fName, *lName;
fName = [contact objectForKey:#"mFirstName"];
lName = [contact objectForKey:#"mLastName"];
NSLog(#"First Name: %#",fName);
NSLog(#"Last Name: %#",lName);
*/
};
SBJson4ErrorBlock eh = ^(NSError* err){
NSLog(#"Oops: %#",error);
};
NSLog(#"Parse work");
id parser = [SBJson4Parser multiRootParserWithBlock:block
errorHandler:eh];
//uint8_t buf[1024];
//unsigned int len = 0;
NSLog(#"Trying to push stream to data");
//[inputStream read:buf maxLength:len];
NSData *data = [NSData dataWithContentsOfFile:filePath options:NSUTF8StringEncoding error:NULL];
//id data = [json da:NSUTF8StringEncoding];
SBJson4ParserStatus status = [parser parse:data];
NSLog(#"Status: %u",status);
These days people seem to have hundreds or even thousands of contacts, thanks to social networks. Will this lead to a larger memory footprint on an iOS device ? If so, how do I parse a single object from a stream ? If I have to use a delegate, an example would be greatly appreciated.
Please note that I am new to the world of iOS development as well as Objective-C.
The structure of the json file in question:
{
"mAddresses": [
],
"mContactPhoto": "",
"mDisplayName": ",tarun,,,,israni,,",
"mPhoneNumberList": [
{
"mLabel": "_$!<Home>!$_",
"mNumber": "(988) 034-5678",
"mType": 1
}
]
}{
"mAddresses": [
],
"mContactPhoto": "",
"mDisplayName": ",Sumit,,,,Kumar,,",
"mPhoneNumberList": [
{
"mLabel": "_$!<Home>!$_",
"mNumber": "(789) 034-5123",
"mType": 1
}
]
}
Your solution looks like it should work to me. If your file so big that you don't want to hold it all in memory, i.e. you want to avoid this line:
NSData *data = [NSData dataWithContentsOfFile:filePath options:NSUTF8StringEncoding error:NULL];
then you can use an NSInputStream (untested, but hopefully you get the gist):
id parser = [SBJson4Parser multiRootParserWithBlock:block
errorHandler:eh];
id is = [NSInputStream inputStreamWithFileAtPath:filePath];
[is open];
// buffer to read from the input stream
uint8_t buf[1024];
// read from input stream until empty, or an error;
// better error handling is left as an exercise for the reader
while (0 > [is read:buffer maxLength: sizeof buffer]) {
SBJson4ParserStatus status = [parser parse:data];
NSLog(#"Status: %u",status);
// handle parser errors here
}
[is close];
However, you still have to read and parse the whole file to guarantee that you find a particular contact. There is no way to read just a specific contact this way. If that is something you do often, you may want to store your contacts a different way that supports that scenario better. One way would be to use e.g. SQLLite.

How can I determine if a file is a zip file?

I need to determine if a file in my app's documents directory is a zip file. The file name cannot be used in making this determination. So I will need to be able read the MIME type or find some other property that only applies to zips.
NOTE: A solution that requires putting the entire file into memory is not ideal as files could potentially be pretty large.
According to http://www.pkware.com/documents/casestudies/APPNOTE.TXT,
a ZIP file starts with the "local file header signature"
0x50, 0x4b, 0x03, 0x04
so it is sufficient to read the first 4 bytes to check if the file is possibly a ZIP file.
A definite decision can only be made if you actually try to extract the file.
There are many methods to read the first 4 bytes of a file. You can use NSFileHandle,
NSInputStream, open/read/close, ... . So this should only be taken as one possible example:
NSFileHandle *fh = [NSFileHandle fileHandleForReadingAtPath:#"/path/to/file"];
NSData *data = [fh readDataOfLength:4];
if ([data length] == 4) {
const char *bytes = [data bytes];
if (bytes[0] == 'P' && bytes[1] == 'K' && bytes[2] == 3 && bytes[3] == 4) {
// File starts with ZIP magic ...
}
}
Swift 4 version:
if let fh = FileHandle(forReadingAtPath: "/path/to/file") {
let data = fh.readData(ofLength: 4)
if data.starts(with: [0x50, 0x4b, 0x03, 0x04]) {
// File starts with ZIP magic ...
}
fh.closeFile()
}
Try this
NSWorkspace *ws = [NSWorkspace sharedWorkspace];
NSString *description = [ws localizedDescriptionForType:[ws typeOfFile:#"/full/path/to/file" error:nil]];
Or for mime this
+ (NSString*) mimeTypeForFileAtPath: (NSString *) path {
if (![[NSFileManager defaultManager] fileExistsAtPath:path]) {
return nil;
}
CFStringRef UTI = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, (CFStringRef)[path pathExtension], NULL);
CFStringRef mimeType = UTTypeCopyPreferredTagWithClass (UTI, kUTTagClassMIMEType);
CFRelease(UTI);
if (!mimeType) {
return #"application/octet-stream";
}
return [NSMakeCollectable((NSString *)mimeType) autorelease];
}
I'd just use file, then grep if it has the text "zip" or "Zip Archive" to be safe.
if file -q $FILENAME | grep "Zip archive"; then
echo "zip";
else
echo "not zip";
fi
Since both .zip and .xlsx having the same Magic number, I couldn't find the valid zip file (if renamed).
So, I have used Apache Tika to find the exact document type.
Even if renamed the file type as zip, it finds the exact type.
Reference Apache tika use cases

Resources