I'm using nst's iOS Runtime Headers to get access to the CoreTelephony.framework.
Here is his sample code:
NSBundle *b = [NSBundle bundleWithPath:#"/System/Library/PrivateFrameworks/FTServices.framework"];
BOOL success = [b load];
Class FTDeviceSupport = NSClassFromString(#"FTDeviceSupport");
id si = [FTDeviceSupport valueForKey:#"sharedInstance"];
NSLog(#"-- %#", [si valueForKey:#"deviceColor"]);
His sample usage code gives me access to FTServices.framework but when I apply the same logic, it fails since CoreTelephony does not house a class method named sharedInstance().
Should I declare and implement that myself or is there another way?
Thanks.
EDIT:
My attempt:
NSBundle *b = [NSBundle bundleWithPath:#"/System/Library/Frameworks/CoreTelephony.framework"];
BOOL success = [b load];
Class CTTelephonyNetworkInfo = NSClassFromString(#"CTTelephonyNetworkInfo");
id si = [CTTelephonyNetworkInfo valueForKey:#"sharedInstance"]; // fails here
NSLog(#"-- %#", [si valueForKey:#"cachedSignalStrength"]);
The problem is that CTTelephonyNetworkInfo actually has no property sharedInstance. Referred from here, CTTelephonyNetworkInfo is a data structure designed to house the relevant info, and can be accessed (constructed) directly through the standard [[CTTelephonyNetworkInfo alloc] init] (referred from here).
So for your case:
NSBundle *b = [NSBundle bundleWithPath:#"/System/Library/Frameworks/CoreTelephony.framework"];
BOOL success = [b load];
Class CTTelephonyNetworkInfo = NSClassFromString(#"CTTelephonyNetworkInfo");
id si = [[CTTelephonyNetworkInfo alloc] init];
NSLog(#"-- %#", [si valueForKey:#"cachedSignalStrength"]);
Make sure you test on an actual phone though! Simulators have no such information stored.
Edits:
If you want to call methods on a generated class, use performSelector: or NSInvocation class.
Related
I'm working on voip app. fetch contact work but when I want to make call, app crash.
[ABSAddressBook contacts]: message sent to deallocated instance
0x1c1478180 warning: could not execute support code to read
Objective-C class data in the process. This may reduce the quality of
type information available.
crash happen in this line.
NSArray *lContacts = (NSArray *)ABAddressBookCopyArrayOfAllPeople(addressBook);
- (void) checkContactListForJogvoiceList {
// if (![BundleLocalData isLoadingJogvoiceContactList]) {
// [BundleLocalData setLoadingJogvoiceContactList:true];
int maxPhoneNumberSubmit = 200;
NSArray *lContacts = (NSArray *)ABAddressBookCopyArrayOfAllPeople(addressBook);
NSMutableDictionary *phoneNumberContactsDictionary = [[NSMutableDictionary alloc] init];
NSMutableArray *allPhoneNumberList = [[NSMutableArray alloc] init];
for (id lPerson in lContacts) {
ABRecordRef person = (ABRecordRef)lPerson;
NSArray *phoneList = [AppUtil getContactPhoneList:person];
for (NSString* phoneNumber in phoneList) {
NSMutableArray* contactList = phoneNumberContactsDictionary[phoneNumber];
if (!contactList) {
contactList = [[NSMutableArray alloc] init];
}
[contactList addObject:(__bridge ABRecordRef)person];
phoneNumberContactsDictionary[phoneNumber] = contactList;
}
[allPhoneNumberList addObjectsFromArray:phoneList];
if (allPhoneNumberList.count >= maxPhoneNumberSubmit) {
[self checkContactList:allPhoneNumberList phoneNumberContactsDictionary:phoneNumberContactsDictionary];
}
}
if (allPhoneNumberList.count > 0) {
[self checkContactList:allPhoneNumberList phoneNumberContactsDictionary:phoneNumberContactsDictionary];
}
// ABAddressBookUnregisterExternalChangeCallback(addressBook, sync_address_book, self);
// [BundleLocalData setLoadingJogvoiceContactList:false];
// }
}
probably because AddressBook framework deprecate in ios9? am I right?
I don’t want to use Contacts framework.
According to Apple doc ABAddressBookCreateWithOptions using address book function ABAddressBookCreateWithOptions returns NULL if no permission granted from user.
On iOS 6.0 and later, if the caller does not have access to the Address Book database:
For apps linked against iOS 6.0 and later, this function returns NULL.
For apps linked against previous version of iOS, this function returns an empty read-only database.
You should follow this article How do I correctly use ABAddressBookCreateWithOptions method in iOS 6?
During researching of the PrivateFrameworks that are available at iOS 9 i found interesting library that could give access to the call history of the jailbreak iPhone. The question is how to use this framework?
Brief example of my :
NSBundle *b = [NSBundle bundleWithPath:#"/System/Library/PrivateFrameworks/CallHistory.framework"];
BOOL success = [b load];
if (success) {
Class CallHistoryDBHandle = NSClassFromString(#"CallHistoryDBClientHandle");
SEL theSelector = NSSelectorFromString(#"fetchAllNoLimit");
id si = [CallHistoryDBHandle valueForKey:#"createForClient"];
NSLog(#"-- %#", [si performSelector:theSelector]);
} else {
NSLog(#"NO");
}
I'm developing an Apple Watch App, and I need to notify the watch when certain changes occur in the parent application. I'm using the MMWormhole library found on GitHub, but I'm having trouble passing messages from the phone to the watch. Here is my code, do you have any ideas on why this is happening?
My main viewController code looks like this
ViewController.m
- (void)viewDidLoad {
[super viewDidLoad];
self.wormhole = [[MMWormhole alloc] initWithApplicationGroupIdentifier:#"com.mycompany.myapp"
optionalDirectory:#"wormhole"];
NSString *myString = [[NSString alloc] initWithFormat:#"Test String"];
[self.wormhole passMessageObject:#{#"string" : myString}
identifier:#"messageIdentifier"];
My InterfaceController from my WatchkitExtension looks like this:
InterfaceController.m
- (void)awakeWithContext:(id)context {
[super awakeWithContext:context];
// Initialize the wormhole
self.wormhole = [[MMWormhole alloc] initWithApplicationGroupIdentifier:#"com.mycompany.myapp"
optionalDirectory:#"wormhole"];
// Obtain an initial value for the selection message from the wormhole
id messageObject = [self.wormhole messageWithIdentifier:#"messageIdentifier"];
NSString *string = [messageObject valueForKey:#"string"];
if (string != nil) {
NSLog(string);
[myLabel setText:string];
}
// Listen for changes to the selection message. The selection message contains a string value
// identified by the selectionString key. Note that the type of the key is included in the
// name of the key.
[self.wormhole listenForMessageWithIdentifier:#"messageIdentifier" listener:^(id messageObject) {
NSString *string = [messageObject valueForKey:#"string"];
if (string != nil) {
[self.myLabel setText:string];
}
}];
}
Thank you!
Is "com.mycompany.myapp" the real value you use in the app? Because group identifiers have to start with group..
If you use a wrong group identifier everything fails because the containerURLForSecurityApplicationGroupIdentifier call inside MMWormhole returns nil. Unfortunately the developers of MMWormhole didn't do any checks or asserts to make sure that the shared group identifier is correct.
So I would recommend to stop concentrating on MMWormhole for a minute. Instead add this code early in your code (e.g. applicationDidFinishLaunching) to verify that your container identifier is correct:
NSFileManager *fileManager = [[NSFileManager alloc] init];
NSURL *appGroupContainer = [fileManager containerURLForSecurityApplicationGroupIdentifier:#"group.com.mycompany.myapp"];
if (!appGroupContainer) {
NSLog(#"group identifier incorrect, or app groups not setup correctly");
}
This will tell you if your app group setup is incorrect.
I'm not sure how far you are into setting up app groups, but you have to use the group identifier you used in the App Groups capabilities section of your project.
Im new to unit testing and OCMock so this might be an obvious answer, just didn't find answer on google.
I am trying to test a model object's method.
the method has the following code:
//takes a filepath and a pk, sets the filepath to the
BoxAppDelegate *delegate = [[UIApplication sharedApplication] delegate];
NSNumber *ifExistIndexInJson = [BoxJsonDataHelper pkExistInCurrentJson:[[[self.downloadQueue objectAtIndex:0] objectForKey:#"pk"] integerValue]];
if (ifExistIndexInJson)
{
[[[self.downloadQueue objectAtIndex:0] objectForKey:#"fields"] setObject:path forKey:#"content"];
NSError *error = nil;
[[NSFileManager defaultManager] removeItemAtPath:[[[delegate.currentJsonData objectAtIndex:[ifExistIndexInJson integerValue]] objectForKey:#"fields"] objectForKey:#"content"] error:&error];
[delegate.currentJsonData removeObjectAtIndex:[ifExistIndexInJson integerValue]];
[delegate.currentJsonData addObject:[self.downloadQueue objectAtIndex:0]];
[self.downloadQueue removeObjectAtIndex:0];
if ([self.downloadQueue count] > 0)
{
[BoxServerRequestsObject downloadFileForPK:[[[self.downloadQueue objectAtIndex:0] objectForKey:#"pk"] integerValue]sender:self];
}
else
{
//end the progress or whatever
}
}
else
{
[[[self.downloadQueue objectAtIndex:0] objectForKey:#"fields"] setObject:path forKey:#"content"];
[delegate.currentJsonData addObject:[self.downloadQueue objectAtIndex:0]];
[self.downloadQueue removeObjectAtIndex:0];
if ([self.downloadQueue count] > 0)
{
[BoxServerRequestsObject downloadFileForPK:[[[self.downloadQueue objectAtIndex:0] objectForKey:#"pk"] integerValue]sender:self];
}
else
{
//end the progress or whatever
}
}
I need help with a couple of things:
when I call [BoxJsonDataHelper pkExistInCurrentJson:...]. BoxJsonDataHelper is actually self, only it's a class method not an instance, so I call it by name, How can I fake the results of the return value so theres no dependency?
How to fake a file at a path for the program to remove? than how do I check that it was removed?
how do I mock BoxServerRequestObject to make the method call the mock object instead of the real one? and than how do I check if it has been called(also a class method)
My knowledge in unit testing is limited, and I have just started with OCMock and read some examples so I would appreciate full answers :)
You can mock class methods just like instance methods. They stay mocked until the mock is dealloc'ed.
id boxJsonDataHelperMock = [OCMockObject mockForClass:BoxJsonDataHelper.class];
[[[boxJsonDataHelperMock stub] andReturn:#(1)] pkExistInCurrentJson:OCMOCK_ANY]
Are you just testing whether NSFileManager works at that point? With data objects, I prefer to do the actual writing. Why not just assert that the file doesn't exist after it is removed? If you wanted to mock, you should mock "defaultManager" on NSFileManager and return a mock object that expects removeItemAtPath:error:
Place a mock object in your download queue at index 0.
I have created a FacebookManager singleton that gets called on a background thread when my app launches. Everything is working just fine with the facebook manager the singleton, the app etc. However, when the app first launches, it is quite a few seconds before it is useful because the facebook manager has not finished doing its thing yet. So what I want to do, is use NSKeyedArchiver to save the facebookManager and all its dictionaries so that upon launch, the app has a navigable interface while the facebook data is being updated in the background. Make sense?
All within the FacebookManager.m, first, when the manager is done updating the friends dictionaries, etc, I call the method that saves the data:
- (BOOL)saveFacebookData
{
// returns success or failure
NSString *path = [self archivePath]; // just a helper method
return [NSKeyedArchiver archiveRootObject:self toFile:path];
}
Then in init, I am trying this, which doesn't seem to work. :
-(id)init
{
self = [super init];
NSString *path = [self archivePath];
self = [NSKeyedUnarchiver unarchiveObjectWithFile:path];
// If the manager hadn't been saved previously, create a new new one
if (!self) {
if (_idsByNameDict == nil) {
_idsByNameDict = [[NSMutableDictionary alloc] init];
}
if (_photosByNameDict == nil) {
_photosByNameDict = [[NSMutableDictionary alloc] init];
}
if (_installedByNameDict == nil) {
_installedByNameDict = [[NSMutableDictionary alloc] init];
}
if (_allFriendsArray == nil) {
_allFriendsArray = [[NSArray alloc] init];
}
basicPermissions = NO;
extendedPermissions = NO;
// Create synchronous dispatch queue for all facebook activity
if (_facebookUpdateQueue == nil) {
_facebookUpdateQueue = dispatch_queue_create("com.facebookUpdateQueue", NULL);
}
}
I think my general strategy is sound but I am tripping over how to actually grab the archived version of the manager during init! Any advice?
Your class needs to implement <NSCoding> and both of its methods encodeWithCoder: to archive all of your property values and initWithCoder: to in archive them. Make sure to call super in the implementations. Generally, the class using the archived class would know about the archiving but you could hide that knowledge in init by using initForReadingWithData: to create your NSKeyedUnarchiver and then calling [self initWithCoder:...];.