Does any body know what I need to check if app freezes after some time ? I mean, I can see the app in the iPhone screen but no view responds.
I did some google and i found that, i've blocked the main thread somehow.
But my question is how to identify which method causes blocking of main thread ? is there any way to identify ?
I using XMPP for chat and core data for storing and retrieving the messages.I connect the app with XMPP Server in app delegate class.But i don't know how XMPP Delegate call again and again. According to me core data doing its work fine but in the offline mode XMPP try to disconnect and until it does not complete its task it freeze the app for some time and then works fine in the offline mode.
But I am not sure what is the culprit.I am not able to find the issue.
And i tried the pause button too when it freeze but in the left window i am not able to find which method make the app freeze.
Here is code which i am using
(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifier = #"IMChatMessageCell";
CMMessageArchiving_Message_CoreDataObject *msgInfo = (CMMessageArchiving_Message_CoreDataObject *)[[self messageFetchResultController] objectAtIndexPath:indexPath];
IMPromos *promo = nil;
if (msgInfo.promo_Object)
{
promo = msgInfo.promo_Object;
////NSLog(#"%#", promo);
}
else
{
promo = [IMDataHandler fetchPromoForPromoID:msgInfo.promo_id];
msgInfo.promo_Object = promo;
}
if (msgInfo.mediaThumbnailPath)
return [self mediaMessageCell:tableView indexPath:indexPath messageInfo: msgInfo];
IMChatMessageCell chatMessageCell = (IMChatMessageCell)[tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (chatMessageCell == nil)
{
NSArray *objects = [[NSBundle mainBundle] loadNibNamed:cellIdentifier owner:nil options:nil];
for (NSObject *obj in objects)
{
if ([obj isKindOfClass:[IMChatMessageCell class]])
{
chatMessageCell = (IMChatMessageCell *)obj;
break;
}
}
}
int imageWith;
NSString *message = [msgInfo body];
if (NO == msgInfo.isOutgoing)// left aligned
{
// Just set the textfield here ///
NSString* promoImage = [[IMUtils getImageFolderPath] stringByAppendingPathComponent: [chatMessageCell.cellPromo.promo_image lastPathComponent]];
//NSLog(#"%#",promoImage);
UIImage *image = [UIImage imageWithContentsOfFile:promoImage];
if (image)
{
chatMessageCell.userImageView.image = image;
if(!promo.promo_message.length)
{
chatMessageCell.userImageView.frame = CGRectIntegral(CGRectMake(padding, 15, image.size.width,image.size.height));
bubbleItemOrigin=USER_IMAGE_Y_POS_WITHOUT_HEADER;
if(!promo.promo_link.length)
bubbleItemOrigin-=5;
}
else
{
chatMessageCell.userImageView.frame = CGRectIntegral(CGRectMake(padding, USER_IMAGE_Y_POS, image.size.width, image.size.height));
bubbleItemOrigin=USER_IMAGE_Y_POS;
}
}
return chatMessageCell;
}
(void)viewWillAppear:(BOOL)animated
{
[self scrollToBottom];
[super viewWillAppear:animated];
[self refreshTitleView];
messagesFetchResultController.delegate = self;
messageTextField.font = [IMUtils appFontWithSize:font_size];
}
I am using this code.
When i am running the app without network then it freeze
and something like this printing into logs
and XMPP'S delegate get called.
I need to display entire friend list's avatar images inside UITableView.
I could as well ask this into Quickblox forums but seems like their support is less responsive there.
I have read Quickblox documentation but can't find an efficient way of getting user's avatar images. All I see is [QBContent TDownloadFileWithBlobID], but I do not know how to use it.
I am fetching contacts using chatContactListDidChange delegate call like below. Now how can I also get all these users' avatar images also?
Note that friendsArray is my data source for the table view, so ideally I would want to store the avatar images as part of the same array.
- (void)chatContactListDidChange:(QBContactList *)contactList
{
NSLog(#"contact list changed");
NSLog(#"current contact list %#", [QBChat instance].contactList);
[self fetchContacts:[QBChat instance].contactList.contacts];
}
- (void) fetchContacts : (NSArray *) contactArray
{
NSMutableArray * userIDArray = [[NSMutableArray alloc] init];
for (QBContactListItem * contact in contactArray)
{
NSString * userIDString = [NSString stringWithFormat:#"%ld", (unsigned long)contact.userID];
[userIDArray addObject:userIDString];
}
if (!userIDArray.count)
{
return;
}
NSString * requestString = [userIDArray componentsJoinedByString:#","];
[QBUsers usersWithIDs:requestString delegate:self context:#"FetchFriends"];
}
- (void)completedWithResult:(Result *)result context:(void *)contextInfo
{
if([result isKindOfClass:[QBUUserPagedResult class]])
{
NSString * context = (__bridge NSString *)contextInfo;
// Success result
if(result.success)
{
QBUUserPagedResult * pagedResult = (QBUUserPagedResult *)result;
if ([context isEqualToString:#"FetchFriends"])
{
if (friendsArray)
{
[friendsArray removeAllObjects];
}
else
{
friendsArray = [[NSMutableArray alloc] init];
}
QBUUser * user = [pagedResult.users objectAtIndex:0];
//user.blobID
friendsArray addObjectsFromArray:pagedResult.users];
}
}
else
{
NSLog(#"Error getting users: %#", context);
}
}
}
Use QBUUser's fields - customData for public url of user's image and blobID for uploaded blob.
Image --> [QBContent TUploadFile...] --> Getting callback with result (QBCFileUploadTaskResult) --> getting blob (QBCBlob) --> save blob id to user.blobID --> get public url of your image: [currentBlob publicURL] --> save to user.customData --> update user: [QBUsers updateUser...]
Hope it helps you.
I want to hide a phone call completely in ios. My priority is to do this on ios 7 (latest ios version at this time!) but i would like to know how to hide a phone call on ios 6 and below too if possible. I have found some functions to do so as hooking into initWithAlertController method of class SBUIFullscreenAlertAdapter. Thanks to creker in this link I found another method to hook that is better to do so. The problem is it still has a callbar when the phone is not locked or when the phone is locked the phone shows that it's it in the middle of communication. Here are screenshots:
link to image
I want to know what are the methods dealing with this to hook? Is there anything else that i should know for achieving what i want?
For deleting any other traces that are left i thought of after the call is finished i delete the call history from it's database. Is there a better way?
I will try to post as much code as I can but it will not work from scratch. I use my own macroses to generate hooks so you have to rewrite them to work with your code. I will use pseudo function IsHiddenCall to determine if a given call is our hidden call (simple phone number check). It's here to simplify the code. You obviously have to implement it yourself. There will be other pseudo functions but their implementation is very simple and will be obvious from their names. It's not a simple tweak so bear with me.
Also, the code is non-ARC.
Basically, we hook everything that might tell iOS that there is a phone call.
iOS 7
Let's start with iOS 7 as it's the last version of iOS right now and hidden call implementation is simpler than on iOS 6 and below.
Almost everything we need is located in private TelephonyUtilities.framework. In iOS 7 Apple moved almost everything related to phone calls in that framework. That's why it got simpler - all other iOS components use that framework so we only need to hook it once without the need to poke around in every iOS daemon, framework that might do something with phone calls.
All methods are hooked in two processes - SpringBoard and MobilePhone (phone application). Bundle IDs are com.apple.springboard and com.apple.mobilephone, respectively.
Here is the list of TelephonyUtilities.framework methods I hook in both processes.
//TUTelephonyCall -(id)initWithCall:(CTCallRef)call
//Here we return nil in case of a hidden call. That way iOS will ignore it
//as it checks for nil return value.
InsertHookA(id, TUTelephonyCall, initWithCall, CTCallRef call)
{
if (IsHiddenCall(call) == YES)
{
return nil;
}
return CallOriginalA(TUTelephonyCall, initWithCall, call);
}
//TUCallCenter -(void)handleCallerIDChanged:(TUTelephonyCall*)call
//This is CoreTelephony notification handler. We ignore it in case of a hidden call.
//call==nil check is required because of our other hooks that might return
//nil object. Passing nil to original implementation might break something.
InsertHookA(void, TUCallCenter, handleCallerIDChanged, TUTelephonyCall* call)
{
if (call == nil || IsHiddenCall([call destinationID]) == YES)
{
return;
}
CallOriginalA(TUCallCenter, handleCallerIDChanged, call);
}
//TUCallCenter +(id)callForCTCall:(CTCallRef)call;
//Just like TUTelephonyCall -(id)initWithCall:(CTCallRef)call
InsertHookA(id, TUCallCenter, callForCTCall, CTCallRef call)
{
if (IsHiddenCall(call) == YES)
{
return nil;
}
return CallOriginalA(TUCallCenter, callForCTCall, call);
}
//TUCallCenter -(void)disconnectAllCalls
//Here we disconnect every call there is except our hidden call.
//This is required in case of a hidden conference call with hidden call.
//Our call will stay hidden but active while other call is active. This method is called
//when disconnect button is called - we don't wont it to cancel our hidden call
InsertHook(void, TUCallCenter, disconnectAllCalls)
{
DisconnectAllExceptHiddenCall();
}
//TUCallCenter -(void)disconnectCurrentCallAndActivateHeld
//Just like TUCallCenter -(void)disconnectAllCalls
InsertHook(void, TUCallCenter, disconnectCurrentCallAndActivateHeld)
{
DisconnectAllExceptHiddenCall();
}
//TUCallCenter -(int)currentCallCount
//Here we return current calls count minus our hidden call
InsertHook(int, TUCallCenter, currentCallCount)
{
return CallOriginal(TUCallCenter, currentCallCount) - GetHiddenCallsCount();
}
//TUCallCenter -(NSArray*)conferenceParticipantCalls
//Hide our call from conference participants
InsertHook(id, TUCallCenter, conferenceParticipantCalls)
{
NSArray* calls = CallOriginal(TUCallCenter, conferenceParticipantCalls);
BOOL isThereHiddenCall = NO;
NSMutableArray* callsWithoutHiddenCall = [NSMutableArray array];
for (id i in calls)
{
if (IsHiddenCall([i destinationID]) == NO)
{
[callsWithoutHiddenCall addObject:i];
}
else
{
isThereHiddenCall = YES;
}
}
if (callsWithoutHiddenCall.count != calls.count)
{
//If there is only two calls - hidden call and normal - there shouldn't be any sign of a conference call
if (callsWithoutHiddenCall.count == 1 && isThereHiddenCall == YES)
{
[callsWithoutHiddenCall removeAllObjects];
}
[self setConferenceParticipantCalls:callsWithoutHiddenCall];
[self _postConferenceParticipantsChanged];
}
else
{
return calls;
}
}
//TUTelephonyCall -(BOOL)isConferenced
//Hide conference call in case of two calls - our hidden and normal
InsertHook(BOOL, TUTelephonyCall, isConferenced)
{
if (CTGetCurrentCallCount() > 1)
{
if (CTGetCurrentCallCount() > 2)
{
//There is at least two normal calls - let iOS do it's work
return CallOriginal(TUTelephonyCall, isConferenced);
}
if (IsHiddenCallExists() == YES)
{
//There is hidden call and one normal call - conference call should be hidden
return NO;
}
}
return CallOriginal(TUTelephonyCall, isConferenced);
}
//TUCallCenter -(void)handleCallStatusChanged:(TUTelephonyCall*)call userInfo:(id)userInfo
//Call status changes handler. We ignore all events except those
//that we marked with special key in userInfo object. Here we answer hidden call, setup
//audio routing and doing other stuff. Our hidden call is indeed hidden,
//iOS doesn't know about it and don't even setup audio routes. "AVController" is a global variable.
InsertHookAA(void, TUCallCenter, handleCallStatusChanged, userInfo, TUTelephonyCall* call, id userInfo)
{
//'call' is nil when this is a hidden call event that we should ignore
if (call == nil)
{
return;
}
//Detecting special key that tells us that we should process this hidden call event
if ([[userInfo objectForKey:#"HiddenCall"] boolValue] == YES)
{
if (CTCallGetStatus(call) == kCTCallStatusIncoming)
{
CTCallAnswer(call);
}
else if (CTCallGetStatus(call) == kCTCallStatusActive)
{
//Setting up audio routing
[AVController release];
AVController = [[objc_getClass("AVController") alloc] init];
SetupAVController(AVController);
}
else if (CTCallGetStatus(call) == kCTCallStatusHanged)
{
NSArray *calls = CTCopyCurrentCalls(nil);
for (CTCallRef call in calls)
{
CTCallResume(call);
}
[calls release];
if (CTGetCurrentCallCount() == 0)
{
//No calls left - destroying audio controller
[AVController release];
AVController = nil;
}
}
return;
}
else if (IsHiddenCall([call destinationID]) == YES)
{
return;
}
CallOriginalAA(TUCallCenter, handleCallStatusChanged, userInfo, call, userInfo);
}
Here is Foundation.framework method I hook in both processes.
//In iOS 7 telephony events are sent through local NSNotificationCenter. Here we suppress all hidden call notifications.
InsertHookAAA(void, NSNotificationCenter, postNotificationName, object, userInfo, NSString* name, id object, NSDictionary* userInfo)
{
if ([name isEqualToString:#"TUCallCenterControlFailureNotification"] == YES || [name isEqualToString:#"TUCallCenterCauseCodeNotification"] == YES)
{
//'object' usually holds TUCall object. If 'object' is nil it indicates that these notifications are about hidden call and should be suppressed
if (object == nil)
{
return;
}
}
//Suppressing if something goes through
if ([object isKindOfClass:objc_getClass("TUTelephonyCall")] == YES && IsHiddenCall([object destinationID]) == YES)
{
return;
}
CallOriginalAAA(NSNotificationCenter, postNotificationName, object, userInfo, name, object, userInfo);
}
Here is the last method I hook in both processes from CoreTelephony.framwork
//CTCall +(id)callForCTCallRef:(CTCallRef)call
//Return nil in case of hidden call
InsertHookA(id, CTCall, callForCTCallRef, CTCallRef call)
{
if (IsHiddenCall(call) == YES)
{
return nil;
}
return CallOriginalA(CTCall, callForCTCallRef, call);
}
Here is SetupAVController function I used earlier. Hidden call is trully hidden - iOS doesn't know anything about it so when we answer it audio routing is not done and we will not hear anything on the other end. SetupAVController does this - it setups audio routing like iOS does when there is active phone call. I use private APIs from private Celestial.framework
extern id AVController_PickableRoutesAttribute;
extern id AVController_AudioCategoryAttribute;
extern id AVController_PickedRouteAttribute;
extern id AVController_AllowGaplessTransitionsAttribute;
extern id AVController_ClientPriorityAttribute;
extern id AVController_ClientNameAttribute;
extern id AVController_WantsVolumeChangesWhenPaused;
void SetupAVController(id controller)
{
[controller setAttribute:[NSNumber numberWithInt:10] forKey:AVController_ClientPriorityAttribute error:NULL];
[controller setAttribute:#"Phone" forKey:AVController_ClientNameAttribute error:NULL];
[controller setAttribute:[NSNumber numberWithBool:YES] forKey:AVController_WantsVolumeChangesWhenPaused error:NULL];
[controller setAttribute:[NSNumber numberWithBool:YES] forKey:AVController_AllowGaplessTransitionsAttribute error:NULL];
[controller setAttribute:#"PhoneCall" forKey:AVController_AudioCategoryAttribute error:NULL];
}
Here is method I hook only in MobilePhone process
/*
PHRecentCall -(id)initWithCTCall:(CTCallRef)call
Here we hide hidden call from call history. Doing it in MobilePhone
will hide our call even if we were in MobilePhone application when hidden call
was disconnected. We not only delete it from the database but also prevent UI from
showing it.
*/
InsertHookA(id, PHRecentCall, initWithCTCall, CTCallRef call)
{
if (call == NULL)
{
return CallOriginalA(PHRecentCall, initWithCTCall, call);
}
if (IsHiddenCall(call) == YES)
{
//Delete call from call history
CTCallDeleteFromCallHistory(call);
//Update MobilePhone app UI
id PHRecentsViewController = [[[[[UIApplication sharedApplication] delegate] rootViewController] tabBarViewController] recentsViewController];
if ([PHRecentsViewController isViewLoaded])
{
[PHRecentsViewController resetCachedIndexes];
[PHRecentsViewController _reloadTableViewAndNavigationBar];
}
}
return CallOriginalA(PHRecentCall, initWithCTCall, call);
}
Methods I hook in SpringBoard process.
//SpringBoard -(void)_updateRejectedInputSettingsForInCallState:(char)state isOutgoing:(char)outgoing triggeredbyRouteWillChangeToReceiverNotification:(char)triggered
//Here we disable proximity sensor
InsertHookAAA(void, SpringBoard, _updateRejectedInputSettingsForInCallState, isOutgoing, triggeredbyRouteWillChangeToReceiverNotification, char state, char outgoing, char triggered)
{
CallOriginalAAA(SpringBoard, _updateRejectedInputSettingsForInCallState, isOutgoing, triggeredbyRouteWillChangeToReceiverNotification, state, outgoing, triggered);
if (IsHiddenCallExists() == YES && CTGetCurrentCallCount() == 1)
{
BKSHIDServicesRequestProximityDetectionMode = (void (*)(int))dlsym(RTLD_SELF, "BKSHIDServicesRequestProximityDetectionMode");
BKSHIDServicesRequestProximityDetectionMode(0);
}
}
//BBServer -(void)publishBulletin:(id)bulletin destinations:(unsigned int)destinations alwaysToLockScreen:(char)toLockScreen
//Suppress hidden call bulletins
InsertHookAAA(void, BBServer, publishBulletin, destinations, alwaysToLockScreen, id bulletin, unsigned int destinations, char toLockScreen)
{
if ([[bulletin section] isEqualToString:#"com.apple.mobilephone"] == YES)
{
NSArray *recordTypeComponents = [[bulletin recordID] componentsSeparatedByString:#" "];
NSString *recordType = recordTypeComponents[0];
NSString *recordCode = recordTypeComponents[1];
//Missed call bulletin
if ([recordType isEqualToString:#"missedcall"] == YES)
{
NSArray *recordCodeComponents = [recordCode componentsSeparatedByString:#"-"];
NSString *phoneNumber = recordCodeComponents[0];
if (IsHiddenCall(phoneNumber) == YES)
{
return;
}
}
}
CallOriginalAAA(BBServer, publishBulletin, destinations, alwaysToLockScreen, bulletin, destinations, toLockScreen);
}
//TUCallCenter -(id)init
//CoreTelephony notifications handler setup
InsertHook(id, TUCallCenter, init)
{
CTTelephonyCenterAddObserver(CTTelephonyCenterGetDefault(), self, CallStatusNotificationCallback, kCTCallStatusChangeNotification, NULL, CFNotificationSuspensionBehaviorDeliverImmediately);
return CallOriginal(TUCallCenter, init);
}
//Call status changes handler. Here we redirect status changes into hooked TUCallCenter method and doing some other stuff.
void CallStatusNotificationCallback(CFNotificationCenterRef center, void* observer, CFStringRef name, const void* object, CFDictionaryRef userInfo)
{
if (object == NULL)
{
return;
}
if (IsHiddenCall((CTCallRef)object) == YES)
{
[observer handleCallStatusChanged:object userInfo:[NSDictionary dictionaryWithObject:[NSNumber numberWithBool:YES] forKey:#"HiddenCall"]];
}
else
{
if (CTCallGetStatus((CTCallRef)object) == kCTCallStatusHanged)
{
if (IsHiddenCallExists() == YES)
{
//Setting up all the audio routing again. When normal call is hanged iOS may break audio routing as it doesn't know there is another active call exists (hidden call)
SetupAVController(AVController);
}
else if (CTGetCurrentCallCount() == 0)
{
[AVController release];
AVController = nil;
}
}
}
if (CTGetCurrentCallCount() > 1 && IsHiddenCallExists() == YES)
{
//Here we setup hidden conference call
NSArray *calls = CTCopyCurrentCalls(nil);
for (CTCallRef call in calls)
{
CTCallJoinConference(call);
}
[calls release];
}
}
iOS 5-6
iOS 5-6 is more complex. Telephony code is scattered arount many iOS components and APIs. I might post the code later as I don't have time right now. The answer is already really long.
I'm trying to write the state of a UISwitch to file, so every time the app starts up it remembers whether it was on or off previously.
-(IBAction) switchValueChanged {
if (Hard1ON.on) {
isH1 = (#"YES");
//save above yes to file
After a bit of searching, I found this is the piece of code used to save to file:
- (BOOL)writeToFile:(NSString *)path atomically:(BOOL)useAuxiliaryFile encoding:(NSStringEncoding)enc error:(NSError **)error
However it is evoking a 'Use of undeclared identifier 'writeToFile' error. Can anyone tell me what's wrong?
To save to a file as a string (likely not the best solution):
- (IBAction)switchValueChanged:(id)sender
{
NSString *stateAsString;
if ([sender isOn]) {
stateAsString = #"YES";
} else {
stateAsString = #"NO";
}
[stateAsString
writeToFile:#"/path/to/file"
atomically:NO
encoding:NSUTF8StringEncoding
error:NULL
];
}
It would probably be a better idea to write the state to NSUserDefaults:
#define kSwitchStateKey #"SwitchState"
- (IBAction)switchValueChanged:(id)sender
{
[[NSUserDefaults standardUserDefaults]
setObject:#([sender isOn)
forKey:kSwitchStateKey
];
}
Analyzer keeps saying that I have a leak in the line with the * at the beginning and end, how would I fix this leak so it gets rid of the warning?
+ (void)flushOfflineQueue
{
// TODO - if an item fails, after all items are shared, it should present a summary view and allow them to see which items failed/succeeded
// Check for a connection
if (![self connected])
return;
// Open list
NSMutableArray *queueList = [self getOfflineQueueList];
// Run through each item in the quietly in the background
// TODO - Is this the best behavior? Instead, should the user confirm sending these again? Maybe only if it has been X days since they were saved?
// - want to avoid a user being suprised by a post to Twitter if that happens long after they forgot they even shared it.
if (queueList != nil)
{
SHK *helper = [self currentHelper];
if (helper.offlineQueue == nil)
***helper.offlineQueue = [[NSOperationQueue alloc] init];***
SHKItem *item;
NSString *sharerId, *uid;
for (NSDictionary *entry in queueList)
{
item = [SHKItem itemFromDictionary:[entry objectForKey:#"item"]];
sharerId = [entry objectForKey:#"sharer"];
uid = [entry objectForKey:#"uid"];
if (item != nil && sharerId != nil)
[helper.offlineQueue addOperation:[[[SHKOfflineSharer alloc] initWithItem:item forSharer:sharerId uid:uid] autorelease]];
}
// Remove offline queue - TODO: only do this if everything was successful?
[[NSFileManager defaultManager] removeItemAtPath:[self offlineQueueListPath] error:nil];
}
}
Thanks!
When you use properties they will often perform the proper memory management. In your situation you need to autorelease the class you set.
helper.offlineQueue = [[[NSOperationQueue alloc] init] autorelease];