How to send us crash reports from an iOS app

At ReignDesign, we always test our apps thoroughly before releasing them. But we're human, and occasionally apps can crash because of a combination of circumstances we didn't consider. If one of our apps crashes on your device, sometimes we'll ask if you can send us a crash report to help us diagnose the problem. Here's how to do that:

1. Sync your iOS device to iTunes on your Mac or PC.

2. Open the folder on your computer that contains crash logs for your iOS device:

On Mac OSX

a) Go to the Finder

b) From the "Go" menu choose "Go to folder..."

c) Enter ~/Library/Logs/CrashReporter/MobileDevice and click the "Go" button

d) A Finder window will open, open the sub-folder whose name matches your iOS device

On Windows XP

a) Press the "windows" key and the "e" key at the same time to open a new "Windows Explorer" window

b) In the "Address" area at the top of the window paste in %HOMEPATH%\Application Data\Apple computer\Logs\CrashReporter and hit the "ENTER" key on your keyboard.

c) Open the sub-folder whose name matches your iOS device

On Windows Vista & Windows 7 & Windows 8

a) Press the "windows" key and the "e" key at the same time to open a new "Windows Explorer" window

b) In the "Address" area at the top of the window paste in "%HOMEPATH%\AppData\Roaming\Apple computer\Logs\CrashReporter\MobileDevice" and hit the "enter" key on your keyboard.

c) Open the sub-folder whose name matches your iOS device

Now, two final steps:

3. Find the crash reports you wish to send (for example anything starting with "PigRush_")

4. Attach them to an email and send to support@reigndesign.com

ReignDesign’s most popular blog posts of 2012

ReignDesign had a fun and exciting year! We released some apps, visited conferences and  contemplated new technology! Here's a Top Ten List of our biggest moments.

1. Oooohh, look at the pretty pictures, iPad Retina vs. Non-retina. We loved the clarity of the retina display. How did we ever live without it?
2.  Git and SCP: this tutorial is perfect if you can't find your USB stick or if you don't feel like walking across the room to find one.
3.  Ever wondered about the controls for touchscreen games? Watch this talk that founder, Matt Mayer, gave in Kiev.
4.  In iOS5, Apple introduced native Twitter integration via the "Tweet Sheet" (aka TWTweetComposeViewController), allowing you to easily implement Twitter in your iOS app without worrying about all the heavy lifting. Continue reading this tutorial for more details.
5.  Look At Teh Pritee Kittehs! Our app, Cat Scan, was fun to make, and we enjoyed turning all of our phone contacts into cute kittens.
6.  Adding labels and re-assigning Github issues: Two ways to automate this common task!
7. Making Flockwork was a great learning experience filled with a few technical hurdles. We wrote a series of blog posts explaining the challenges in cocos2d.
8. Controls for games are important....like, really important.
9. Almost a year ago, we wondered how to best design an app for Windows 8  How accurate were we?
10. Remember the trend of making videos and lists about Sh*t People Say/Do, etc? We made one for iPhone Developers. You're welcome.
ReignDesign is excited about 2013, and we are looking forward to creating and updating.
Happy New Year!

How to launch Google Maps or Apple Maps from an iOS app

Today Google announced their much-anticipated Google Maps app for iOS. They also plan to make available a Google Maps SDK for iOS allowing iOS developers to integrate Google Maps in their apps, however at present API keys are only available for selected developers.

If you want to provide your app's users with the ability to open a location in Google Maps, there is an option which works today: Google have added a comgooglemaps URL scheme to their app so third-party apps can launch the Google Maps app.

Assuming you'd like to give your users a choice of opening a location in both Apple or Google maps, you can set up some code like this in a new view controller:

  1. #import "ViewController.h"
  2. #import <MapKit/MapKit.h>
  3.  
  4. @implementation ViewController
  5.  
  6. - (void)viewDidLoad
  7. {
  8. [super viewDidLoad];
  9. // Add a button to pop open an action sheet
  10. UIButton *btn = [UIButton buttonWithType:UIButtonTypeRoundedRect];
  11. btn.frame = CGRectMake(80,100,160,50);
  12. [btn setTitle:@"Open placemark" forState:UIControlStateNormal];
  13. [btn addTarget:self action:@selector(openActionSheet:) forControlEvents:UIControlEventTouchUpInside];
  14. [self.view addSubview:btn];
  15.  
  16. }
  17. -(void)openActionSheet:(id)sender {
  18. //give the user a choice of Apple or Google Maps
  19. UIActionSheet *sheet = [[UIActionSheet alloc] initWithTitle:@"Open in Maps" delegate:self cancelButtonTitle:nil destructiveButtonTitle:nil otherButtonTitles:@"Apple Maps",@"Google Maps", nil];
  20. [sheet showInView:self.view];
  21. }
  22. -(void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex {
  23. //coordinates for the place we want to display
  24. CLLocationCoordinate2D rdOfficeLocation = CLLocationCoordinate2DMake(31.20691,121.477847);
  25. if (buttonIndex==0) {
  26. //Apple Maps, using the MKMapItem class
  27. MKPlacemark *placemark = [[MKPlacemark alloc] initWithCoordinate:rdOfficeLocation addressDictionary:nil];
  28. MKMapItem *item = [[MKMapItem alloc] initWithPlacemark:placemark];
  29. item.name = @"ReignDesign Office";
  30. [item openInMapsWithLaunchOptions:nil];
  31. } else if (buttonIndex==1) {
  32. //Google Maps
  33. //construct a URL using the comgooglemaps schema
  34. NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"comgooglemaps://?center=%f,%f",rdOfficeLocation.latitude,rdOfficeLocation.longitude]];
  35. if (![[UIApplication sharedApplication] canOpenURL:url]) {
  36. NSLog(@"Google Maps app is not installed");
  37. //left as an exercise for the reader: open the Google Maps mobile website instead!
  38. } else {
  39. [[UIApplication sharedApplication] openURL:url];
  40. }
  41. }
  42. }
  43. @end

Here's the app in action, example code is on Github.

    

For more details, see the Google Maps URL Scheme documentation.

Sharing images to Weixin/WeChat from an iOS app

Weixin (微信) is Tencent's mobile messaging product. It's highly popular in China, with hundreds of millions of users, as well as many international users who may know it by its English name WeChat. It includes both a chat feature, similar to WhatsApp, and a timeline feature similar to Path.

Tencent have a developer site in English, but the documentation is still quite sparse.

For a recent project, we needed to integrate sharing of images to Weixin.

The workflow for the user will be:

1. Create an image in the app
2. Tap a button to share to Weixin
3. The Weixin app will launch, and the user confirms they want to add the image to their "Moments".
4. The user taps a button in the Weixin app to return to our app.

STEP 1: Download and add the iOS SDK

1. Download the latest iOS SDK from the WeChat developer site. After unzipping you should have four files:

2. If you're building with the latest Xcode and targeting iPhone 5, you'll probably want to use the version of the library which supports armv7s, so copy WXApi.hWXApiObject.h and libWeChatSDK_armv7_armv7s.a to your project folder. Rename libWeChatSDK_armv7_armv7s.a to libWeChatSDK.a.

3. Now in your XCode project go to File > Add Files and add the three files to your project.

STEP 2: Register for an app id

1. Register as a WeChat developer using the signup form. Once you've confirmed your email address, head to the My Apps page and tap "Register my apps"

2. Fill out the name of your app and other details. You can skip some optional fields like icon for now. Make sure you choose "Mobile app" as the app type, not Web app.

3. At the end of the process you will be given an app id which looks like wx123456789012

STEP 3: Integrate with the SDK

1. In your application:didFinishLaunchingWithOptions: method, add code to register your app. Be sure to replace wx123456789012 with the code you obtained earlier.

  1. if (![WXApi registerApp:@"wx123456789012"]) {
  2. NSLog(@"Failed to register with Weixin");
  3. }

2. After the Weixin app is launched it needs a way to re-launch your app. It does this by trying to open a URL with your app id as the protocol, for example wx123456789012://. We need to ensure we can handle these URLs, so implement these two methods:

  1. - (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url {
  2. return [WXApi handleOpenURL:url delegate:self];
  3. }
  4. - (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation {
  5. return [WXApi handleOpenURL:url delegate:self];
  6. }

3. We need to ensure that our app delegate implements WXApiDelegate, so first add WXApiDelegate to your AppDelegate.h file:

  1. @interface AppDelegate : UIResponder<WXApiDelegate>

and then add empty implementations of these two callbacks in the .m:

  1. - (void) onReq:(BaseReq*)req {
  2. }
  3. - (void) onResp:(BaseResp*)resp {
  4. }

4. We also need to make a change in the Info.plist to ensure we can handle these kind of URLs. Go to your target, select the Info tab and add a new URL type as follows. The identifier can be "weixin" and the URL scheme should match your app id.

5. Now let's add a method to send an image.

  1. - (void) sendImageContentToWeixin:(UIImage *)image {
  2. //if the Weixin app is not installed, show an error
  3. if (![WXApi isWXAppInstalled]) {
  4. UIAlertView *alert = [[UIAlertView alloc] initWithTitle:nil message:@"The Weixin app is not installed" delegate:nil cancelButtonTitle:@"OK" otherButtonTitles: nil];
  5. [alert show];
  6. return;
  7. }
  8. //create a message object
  9. WXMediaMessage *message = [WXMediaMessage message];
  10. //set the thumbnail image. This MUST be less than 32kb, or sendReq may return NO.
  11. //we'll just use the full image resized to 100x100 pixels for now
  12. [message setThumbImage:[image resizedImage:CGSizeMake(100,100) interpolationQuality:kCGInterpolationDefault]];
  13. //create an image object and set the image data as a JPG representation of our UIImage
  14. WXImageObject *ext = [WXImageObject object];
  15. ext.imageData = UIImageJPEGRepresentation(image, 0.8);
  16. message.mediaObject = ext;
  17. //create a request
  18. SendMessageToWXReq* req = [[SendMessageToWXReq alloc] init];
  19. //this is a multimedia message, not a text message
  20. req.bText = NO;
  21. //set the message
  22. req.message = message;
  23. //set the "scene", WXSceneTimeline is for "moments". WXSceneSession allows the user to send a message to friends
  24. req.scene = WXSceneTimeline;
  25. //try to send the request
  26. if (![WXApi sendReq:req]) {
  27. UIAlertView *alert = [[UIAlertView alloc] initWithTitle:nil message:@"Error" delegate:nil cancelButtonTitle:@"OK" otherButtonTitles: nil];
  28. [alert show];
  29. }
  30. }

6. Add callback code. This will be called once control is returned to our app after returning from Weixin.

  1. - (void) onResp:(BaseResp*)resp {
  2. if([resp isKindOfClass:[SendMessageToWXResp class]]) {
  3. NSString *strMsg = [NSString stringWithFormat:@"Result:%d", resp.errCode];
  4. NSLog(@"Response from Weixin was: %@",strMsg);
  5. }
  6. }

STEP 4: Test!

All being well, you should now be able to share an image from your code, using something like:

  1.  
  2. UIImage *test = [UIImage imageNamed:@"test.png"];
  3. [self sendImageContentToWeixin:test];
  4.  

After sharing the image, you should be returned to your app.

If you found this tutorial useful, or have any suggestions, do leave a comment!

Movember Matt

The eleventh month of the year, formerly known as November, is now Movember at ReignDesign! I've been growing out my facial hair to help raise awareness of men's health, particularly prostate and testicular cancers.

It's important to remember that 47% of testicular cancer cases occur in men under 35 years. Check out BUPA's Movember page for more facts and prevention tips.

The evidence, Day 1 and Day 30.