iMessageExt app Error starting application - ios

I created my iMessage extension, when I try to open it, the first screen appears but it is totally frozen, and it does not react in any way.
I've put logs in the viewDidLoad of that first view and nothing appears there, after a few seconds I can already see those logs.
To make the application freezing lose that status, user has to slide screen left or right and back again.
I've tried looking all over the web for someone who happens to be the same, but I could not find anything.
It does not come to mind more screenshots or portions of code add, if you think I should provide some additional information, just let me know
Any help would be appreciated.
Thank you.
UPDATE:
This is my Project Structure.
This is my viewDidLoad code.
- (void)viewDidLoad
{
[super viewDidLoad];
NSLog(#"here viewDidLoad iMessage ext~~~!");
[self applyCornerRadiusToBtn];
[self registerPresentationAction];
NSDictionary *user = [self getUserInfoFromHostApp];
if (user) {
NSLog(#"Here != null user info");
//It is assumed that when you enter this point and run this log, the app should navigate to the next screen, but it does not.
[self performSegueWithIdentifier:#"goToYoutubeListIm" sender:nil];
} else {
NSLog(#"Here userInfo null");
}
}
- (NSDictionary *)getUserInfoFromHostApp
{
NSUserDefaults *myDefaults = [[NSUserDefaults alloc] initWithSuiteName:#"group.com.xxxxx"];
NSDictionary *userNameSaved = [myDefaults objectForKey:#"userInfoExt"];;
NSLog(#"userNameSaved in xxxx Ext ==> %#",userNameSaved);
NSURL *groupURL = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:#"group.com.xxxx"];
NSLog(#"groupURL ==> %#",groupURL);
return userNameSaved;
}

For all concerned I have found the problem or problems to be accurate.
1) I was creating my controllers type MSMessagesAppViewController. Apparently there should only be one controller of this type.
2) I had logic in the viewDidAppear in my MSMessagesAppViewController. For some strange reason this also caused the problem, I had to get the logic out there and force the user to interact with a button to execute the logic that was in the didAppear

Related

Do any iOS Action extensions really return a value?

To understand this question, return with me now through the WWDC time machine to the distant past, 2014, when Action extensions were introduced and explained in this video:
https://developer.apple.com/videos/play/wwdc2014/217/
About halfway through, in slide 71, about minute 23:30, the presenter gives instructions for returning a value back to the calling app (the app where the user tapped our Action extension's icon in an activity view):
- (IBAction)done:(id)sender {
NSData *data = self.contents;
NSItemProvider *itemProvider =
[[NSItemProvider alloc] initWithItem:data typeIdentifier:MyDocumentUTI];
NSExtensionItem *item = [[NSExtensionItem alloc] init];
item.attachments = #[itemProvider];
}
A moment later, slide 75, about minute 26, we see how the app that put up the activity view controller is supposed to unwrap that envelope to retrieve the result data:
- (void)setupActivityViewController {
UIActivityViewController *controller;
controller.completionWithItemsHandler =
^(NSString *activityType, BOOL completed,
NSArray *returnedItems, NSError *error) {
if (completed && (returnedItems.count > 0)) {
// process the result items
}
}];
}
So my question is: is that for real? Has anyone within the sound of my voice ever done either of those things? Namely:
Does your app have an Action extension that returns a value to the caller?
Does your app put up an activity view controller that receives the result of some arbitrary unknown Action extension and does something with the value?
I ask because (1) I have never seen (on my iPhone) an Action extension that actually returns a value, and (2) the code elided in "process the result items" seems to me to be complete hand-waving, because how would my app even know what kind of data to expect?
I have come to believe that this code is an aspirational pipe dream with no corresponding reality. But I would be delighted to be told I'm wrong.

Adding user before app actually start

I'm trying to add a user for the first time app is opened. That adding response back to me with a some sort of password, what we call Access Token. The token is using to reaching the API services.
Anyway, beside that story, what my problem is for the first time app open, I can add the user and get the token and I saved it as NSUserDefault, but I can't reach to API Services since it tries to reach services with null. After refreshing or reopening the app or switching another view and back that view will solve the issue. But just for the first time, it can't reach the service.
The problem is here, obviously, when the app opens for the first time, before the adding operation is finished, I try to reach to service what cause it to stay null. So here is the code I have:
- (void)viewDidLoad {
[super viewDidLoad];
//Adding user.
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; // User informations.
NSString *accessToken = [defaults objectForKey:#"accessToken"];
if(accessToken == nil)
[APIClient AddUser];
//Adding the inital viewController.
UIViewController *vc = [self viewControllerForSegmentIndex:self.typeSegmentedControl.selectedSegmentIndex];
[self addChildViewController:vc];
vc.view.frame = self.contentView.bounds;
[self.contentView addSubview:vc.view];
self.currentViewController = vc;
}
Don't hang up with what I'm doing while adding the initial viewController, rather than that please just see what happens there as I add the user and then initialize the viewController.
Being asynchronous is the problem, so I'm looking a way to adding the user before the app actually started or launched.
I'm not sure in this case if I have to add the user in application:didFinishLauncingWithOptions, but I don't think so that is the problem.
Okay, so the first thing I would do is extract token related code to the function in view controller. Let's call it tokenReceived:
-(void) tokenReceived{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; // User informations.
NSString *accessToken = [defaults objectForKey:#"accessToken"];
if(accessToken == nil)
[APIClient AddUser];
//Adding the inital viewController.
UIViewController *vc = [self viewControllerForSegmentIndex:self.typeSegmentedControl.selectedSegmentIndex];
[self addChildViewController:vc];
vc.view.frame = self.contentView.bounds;
[self.contentView addSubview:vc.view];
self.currentViewController = vc;
}
In the view did load add the following:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(onGetToken)
name:#"getToken"
object:nil];
then add the new method:
- (void) onGetToken
{
[self tokenReceived]
}
and, ofcourse, when you get the token ( in app delegate or wherever you retrieve token ) send notification to observer:
// your code, saving to nsuserdefualts etc.
[[NSNotificationCenter defaultCenter]
postNotificationName:#"getToken"
object:self];
Explanation: It is a bad practice to block main thread with network - your app will not respond to any of the events and if you don't have 3g or wifi - you are stuck. User experience is bad, and that will drive users away.
On the other hand, this will open user screen, and you can put loader if you want till you notify the view controller that you actually got something. This way you will keep async nature of network and still refresh UI when you get one. If you are concerned that you will get token before opening view controller, you can add tokenReceived to view did load as well.
One more thing, you can send NSNotification with the object, and get one from NSNotification object, but in this case you just need to be notified that something happened, you don't need that data.

Pushing A View Controller Crashes The App

Here's the scenario: I am using MWPhotoBrowser. I can push it fine once. The second time I try to push it the app crashes with nothing helpful. This is an ARC project, and this exact same code works fine in previous versions of the app. I am dumbfounded.
-(void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{
selectedIndexPath = indexPath;
if(browser == nil)
{
browser = [[MWPhotoBrowser alloc] initWithDelegate:self];
}
// Set options
browser.displayActionButton = YES; // Show action button to allow sharing, copying, etc (defaults to YES)
browser.displayNavArrows = YES; // Whether to display left and right nav arrows on toolbar (defaults to NO)
browser.zoomPhotosToFill = NO; // Images that almost fill the screen will be initially zoomed to fill (defaults to YES)
[browser setCurrentPhotoIndex:indexPath.row]; // Example: allows second image to be presented first
//browser.wantsFullScreenLayout = YES; // iOS 5 & 6 only: Decide if you want the photo browser full screen, i.e. whether the status bar is affected (defaults to YES)
// Present
[self.navigationController pushViewController:browser animated:YES];
}
Tap a cell once, MWPhotoBrowser opens fine. Go back, tap a cell again, the app crashes just with the debugger but no call stack or error reason:
(lldb):
browser is a strong member variable. It isn't being deallocated prematurely. I can also guarantee it is crashing exactly on the last line of the method.
Can someone please enlighten me on this? I'd solve it if the app at least gave me a reason for the crash and not just throw the debugger on my face.
I have used MWPhotoBrowser extensively and ran across the same problem once. The way I solved it was by checking the MWPhoto objects to see if they actually had been set properly. I realised that I had not created some of them, hence the crash. I would check the place where you store your MWPhotos and make sure they are all set. Hope this helps!

Game Center Sandbox : "Could not create game" issue

I'm currently developing a turn based game using Game Center to handle the online functionalities (for matchmaking and turns handling).
I'm using two sandbox accounts - one on my 3gs and one on the ios Simulator.
I've been testing my app using the GKTurnBasedMatchMakerViewController to do the match making for a while without any problems, but I'm now stuck with an issue:
Every time I want to invite another player for a new (with either one or the other player), the GKTurnBasedMatchMakerViewController displays a UIAlertView stating :
Could not create game - Please remove an existing game and try again.
The thing is, I've deleted all the matches for each player (none of them has any game in his list (not even a closed game). So none of the user is in any match at the moment.
In my GKTurnBaseMatchMakerViewControllerDelegate the turnBasedMatchmakerViewController:didFailWithError: is not called.
The only called function called in the delegate- when I click the OK button on the UIAlertView - is turnBasedMatchmakerViewControllerWasCancelled:
The only thing I can think of is that my games are actually not removed from GameCenter, but as I'm removing them using the GKMatchMakerViewController UI, I barely think so.
When quitting from a turn-based match I've implemented the turnBasedMatchmakerViewController:playerQuitForMatch: like this:
- (void)turnBasedMatchmakerViewController:(GKTurnBasedMatchmakerViewController *)viewController playerQuitForMatch:(GKTurnBasedMatch *)match
{
if ( [self isLocalPlayerCurrentPlayerForMatch:match] ) {
NSData* endData = match.matchData;
for (GKTurnBasedParticipant* participant in match.participants) {
participant.matchOutcome = GKTurnBasedMatchOutcomeWon;
}
match.currentParticipant.matchOutcome = GKTurnBasedMatchOutcomeLost;
[match endMatchInTurnWithMatchData:endData
completionHandler:^(NSError *error) {
if (error) {
NSLog(#"%#",error.description);
}
}];
}
}
(NB: I only have two players in the game)
where isLocalPlayerCurrentPlayerForMatch is:
- (BOOL) isLocalPlayerCurrentPlayerForMatch:(GKTurnBasedMatch*)match
{
return [[[GKLocalPlayer localPlayer] playerID] isEqualToString:match.currentParticipant.playerID];
}
Has anyone encountered and found a solution to this issue?
Am I doing something wrong here, or is it so obvious I just can't see it?
Thank you very much for any comments that would help me find the root of that issue.
Update
Thanks to #kaan-dedeoglu I managed to know that both users had an empty list of matches (consistent with the displayed state).
I also created a third Sandbox account.
Naming the two first accounts A and B, C the third one.
State 1:
A and B are not linked to any match.
A and B are both getting the "Could not create game" error while creating any game (A invites B, A||B invites other player, A||B creates new automatch).
State 2:
C (working account) can invite B and normally plays a party with B.
C (working) can invite B for another simultaneous party
C (working) invites A to play.
A can't play (can't access the list of current matches, the GKTurnBasedMatchMakerViewController directly goes to the creation of a new game).
C is not working anymore.
A, B and C are now stuck in "Could not create game" error.
As a complement here is how I initialize my GKTurnBasedMatchMakerViewController, but I don't see that being wrong.
- (void) displayMatchMakerVC
{
if (! [[GKLocalPlayer localPlayer] isAuthenticated] ) return;
GKMatchRequest* request = [[[GKMatchRequest alloc] init] autorelease];
int nbPlayers = 2;
request.minPlayers = nbPlayers;
request.maxPlayers = nbPlayers;
GKTurnBasedMatchmakerViewController* matchMakerVC = [[[GKTurnBasedMatchmakerViewController alloc] initWithMatchRequest:request] autorelease];
matchMakerVC.turnBasedMatchmakerDelegate = self;
matchMakerVC.showExistingMatches = YES;
[[CCDirector sharedDirector] presentModalViewController:matchMakerVC animated:YES];
}
NB: I'm not using ARC, could that be related to a memory issue? I'm not really a memory management guru, but it seems correct to my understanding.
Any idea of how this could be related to my code and not to game center?
Thank you very much for any answer that could help me go further.
Update 2: turnbasedMatchmakerViewController:didFindMatchMethod:
Here's my turnbasedMatchmakerViewController:didFindMatchMethod: method.
- (void)turnBasedMatchmakerViewController:(GKTurnBasedMatchmakerViewController *)viewController didFindMatch:(GKTurnBasedMatch *)match
{
BLTheme* theme = [[[BLGameConfig sharedConfig] localPlayer] userTheme];
GameSceneRemoteGCLoader* loader = [[GameSceneRemoteGCLoader alloc] initWithGKMatch:match andTheme:theme];
[viewController dismissViewControllerAnimated:NO completion:^{}];
[[CCDirector sharedDirector] replaceScene:loader];
}
When I'm launching an automatch it's launching the exact same error "Could not create game - Please remove an existing game and try again.".
This may or may not be the solution to your problem, but I had a similar issue and solved it in the following way.
It seems that either by default, or somehow, Game Center treats apps with differing CFBundleVersion (build number, not version number, or CFBundleShortVersionString) values as incompatible with one another, and thus does not show matches between apps with incremented build numbers. (Often, developers increment this number as new ad hoc builds or stable releases are distributed during development, so this is quite unfortunate).
To find and remove the "missing" games, I decremented my CFBundleVersion value (which revealed the games), and then deleted the offending matches.
Alternatively, tweaking some settings in iTunes Connect seems to have removed this CFBundleVersion incompatibility. It takes a while to propagate, but I think what did it was tapping on my app, tapping on View Details, making sure the Game Center switch is set to "Enabled", and making sure there is an item in the "Multiplayer Compatibility" table. You could also play with the possibilities within the "Manage Game Center" button from the original app screen, but I think the "Multiplayer Compatibility" setting is what finally allowed me to see all the "old" matches that were previously hidden.
Good luck!
Just to make sure: In both these devices, add these lines in your authentication completion handler and run it once. (then you can comment it out).
[GKTurnBasedMatch loadMatchesWithCompletionHandler:(^)(NSArray *matches, NSError *error) {
for (GKTurnbasedMatch *match in matches) {
[match removeWithCompletionHandler:NULL];
}
}];
This will ensure that all games are removed from your playerID.
It's ridiculous . You don't have to remove an existing match to create a new match. I'm developing a game like this and it actually works.
The following worked for me. First I ran the app on the device for each player, calling quitAllMatches. Then I ran the app again on each device, calling removeAllMatches.
In the long run, it has to be better to clean them up as you go along. But this solved the immediate problem.
-(void) quitAllMatches {
[GKTurnBasedMatch loadMatchesWithCompletionHandler:^(NSArray* matches, NSError* error) {
for (GKTurnBasedMatch* match in matches) {
GKTurnBasedParticipant* participant = match.currentParticipant;
NSString* playerID = participant.playerID;
NSString* localPlayerID = [GKLocalPlayer localPlayer].playerID;
if ([playerID isEqualToString: localPlayerID]) {
NSArray* participants = match.participants;
for (GKTurnBasedParticipant* participant in participants) {
participant.matchOutcome = GKTurnBasedMatchOutcomeTied;
}
NSData* data = [NSData data];
[match endMatchInTurnWithMatchData: data completionHandler:^(NSError* error) {
if (error) {
WJLog(#"did not end -- error %#", [error localizedDescription]);
}
else {
WJLog(#"match ended!");
}
}];
}
}
}];
}
-(void) removeAllMatches {
[GKTurnBasedMatch loadMatchesWithCompletionHandler:^(NSArray* matches, NSError* error) {
for (GKTurnBasedMatch* match in matches) {
[match removeWithCompletionHandler:^(NSError* error) {
if (error) {
WJLog(#"error: %#", [error localizedDescription]);
}
else {
WJLog(#"removed match");
}
}];
}
}];
}

Control UIView on Load

Hi everybody I am trying to make an app with a login page and profile page.
So i am using two view for them : login_view and profile_view
So login_view is my view in nib file i.e default view.
but i want to check if the user has already signed in before in viewdidLoad method like session and show him profile_view instead when app starts.
Can Anybody help me in it?
I tried this but failed
NSArray *array = [[NSArray alloc] initWithContentsOfFile:filePath];
if ([array count] == 0)
{
NSNumber * uud = [array objectAtIndex:0];
[self.view = home];
}
Might want to use a keychain. If not, you could run the risk of having unauthorized access. Lot's of other reasons too.
This sample app by Apple should help you:
http://developer.apple.com/library/ios/#samplecode/GenericKeychain/Introduction/Intro.html

Resources