Thursday, 24 May 2012

Sending Email Landscape style in Cocos2D

 

iPhone-Mail[Concrete/Interesting] You know when sometimes a piece of code seems really simple, but it just won’t go right? The code just won’t behave properly. Fix one problem and another one pops up...

Well, all I wanted was to use MFMailComposeViewController in my Cocos2d application. I’ve done it before so thought it would be a quick job. After many, many hours, I got it working, so to save you all the effort, here is what I did.

Landscape and MFMailComposeViewController

The problem was the game is in Landscape mode and it seems the mail view controller doesn’t like landscape. Works beautifully on the iPad, but the iPhone sees the world in Portrait.

The standard method to launch email is to call presentModelViewController. Here’s the sort of thing that works well in Portrait:

//Create the mail view controller
MFMailComposeViewController *picker = [[MFMailComposeViewController alloc] init];
picker.mailComposeDelegate = self;
[picker setSubject:@"The Title"];

// Email Body
NSMutableString *emailBody = [[[NSMutableString alloc] initWithString:@"<html><body>"] retain];
[emailBody appendString:@"<p>Some body text</p>"];
[emailBody appendString:@"</body></html>"];
[picker setMessageBody:emailBody isHTML:YES];

//An image
NSData *imageData = [NSData dataWithData:UIImagePNGRepresentation([self previewImage])];
[picker addAttachmentData:imageData mimeType:@"image/png" fileName:@"image.png"];

//Present the mail view controller
[[[CCDirector sharedDirector] view] addSubview:emailController.view];
[emailController presentModalViewController:picker animated:YES];
[picker release];
 
Ok, to get this working you need to ensure the class calling this code is marked as a MFMailComposeViewController delegate by inheriting from <MFMailComposeViewControllerDelegate>. You will also need a suitable UIViewController. This is emailController in the code above.
 
Notice also that I’m using cocos2d version 2. If you use an earlier version you will need to change the code below:
 
//Cocos2d version 2 uses view
//[[[CCDirector sharedDirector] view] addSubview:emailController.view];

//For earlier version use openGLView
[[[CCDirector sharedDirector] openGLView] addSubview:emailController.view];

Under Landscape it all goes wrong on the iPhone

But the code above doesn’t work in an application forced into Landscape orientation. What you get is part of the picker showing, clipped and rotated. Oddly enough, the onscreen keyboard is shown correctly, but this is like rubbing salt into an open wound. You will also find that touch handling is messed up and you can’t cancel the email process using the ‘Delete’ or ‘Save as Draft’. Just not working at all well.

There are a number of tips and tricks on the internet to get around this. One method is to handle the presentation of the modal view controller yourself. Tried this and it worked well, up to a point.

But everything went south after choosing an email address using the address book picker. Once again you end up with a clipped and ill orientated modal view.

Solution: Use the Root Controller

Digging deeper it looks like the mail picker and other pickers launched during the process are just not aware of the orientation of the device and application. They seem to be doing their best to ignore what is around them and are going for Portrait. Why?

Well it all comes down to the UIViewController used to present the pickers. In the world of MVC, the view controller used to present other views are responsible for describing the container’s properties, including the orientation. The problem is the UIViewController being used is not doing enough for the picker. In fact the solution is ever so simple, use the application root controller that is responsible for managing cocos2d and the views in this environment:

UIViewController *rootViewController = (UIViewController *)[[[CCDirector sharedDirector] view] nextResponder];
[rootViewController presentModalViewController:picker animated:YES];

The nextResponder to cocos2d’s main view is the root view controller. If you use this to present the modal mail picker, it all works like a charm. Only took me 8 hours to get to this solution.
 

Wrapping it all up

 
I like to wrap things up tight so I created a class called EmailScene that encapsulates all the code to present a mail picker:
 

//EmailScene.h

#import "cocos2d.h"
#import <MessageUI/MFMailComposeViewController.h>

 

@interface EmailScene : CCScene <MFMailComposeViewControllerDelegate>
{
NSString *emailTitle;
NSString *emailBody;
UIImage *emailImage;
MFMailComposeViewController *picker;
}

-(id)initWithTitle:(NSString *)title body:(NSString *)body image:(UIImage *)image;

@end


The implementation looks like this:
 
//EmailScene.m

#import "EmailScene.h"

@implementation EmailScene

- (id) init {
self = [super init];
if (self != nil) {
[self showMailPicker];
}
return self;
}

-(id)initWithTitle:(NSString *)title body:(NSString *)body image:(UIImage *)image
{
self = [super init];
if (self != nil) {
emailTitle = title;
emailBody = body;
emailImage = image;
[self showMailPicker];
}
return self;
}

-(void)showMailPicker
{
picker = [[MFMailComposeViewController alloc] init];
picker.mailComposeDelegate = self;
picker.modalPresentationStyle = UIModalPresentationFullScreen;
picker.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;

[picker setSubject:emailTitle];
[picker setMessageBody:emailBody isHTML:YES];

NSData *imageData = [NSData dataWithData:UIImagePNGRepresentation(emailImage)];
[picker addAttachmentData:imageData mimeType:@"image/png" fileName:[NSString stringWithFormat:@"%@.png", emailTitle]];

[[CCDirector sharedDirector] pause];
UIViewController *rootViewController = (UIViewController *)[[[CCDirector sharedDirector] view] nextResponder];
[rootViewController presentModalViewController:picker animated:YES];
[picker release];
}

- (void)mailComposeController:(MFMailComposeViewController*)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError*)error
{
[[CCDirector sharedDirector] resume];
[controller dismissModalViewControllerAnimated: YES];
}
@end

And to show the picker from any scene in any orientation on iPhone and iPad, simply call the appropriate initializer:
 
// Email Body
NSMutableString *emailBody = [[[NSMutableString alloc] initWithString:@"<html><body>"] retain];
[emailBody appendString:@"<p>Example Email Body</p>"];
[emailBody appendString:@"</body></html>"];

//Show the mail picker
[[EmailScene alloc] initWithTitle:@"Subject Line" body:emailBody image:takeScreenshot(rtx)];

Friday, 27 January 2012

LINQ to SQL Dynamic Stored Procedures

linqpadlogo[Concrete/Little bit interesting] I wanted to call stored procedures by name using a string parameter. Sounds simple and with ADO it's a breeze. But with LINQ to SQL, life is a little more complicated. Here is how I cracked the problem.

 

 

 

LINQ to SQL

In Visual Studio, when you add LINQ to SQL Classes to you project, you are creating a class that provides a convenient set of classes for managing tables, views, functions and stored procedures:

image

When you add LINQ to SQL Classes to your project, Visual Studio creates a .DBML file set. These files describe the managed entities with partial class definitions. The main class is derived from System.Data.Linq.DataContext and provides the link between your code and the database connection. LINQ to SQL works through defining class members that maps LINQ onto the entity and makes use of attributes to map columns, parameters and returns to .Net classes.

LINQ to SQL Classes takes a lot of code out of your application and is easy to use. For example, to call a stored procedure, simply open the .DBML, drag a stored procedure from a data connection on the Server Explorer or Toolbox over onto the Object Relational Designer: this creates the class entries that maps the entity onto LINQ, then from your code, call the stored procedure as shown below:

using (CatalogueDataContext db = new CatalogueDataContext())    
{
db.usp_Biometrics_Update_OperatorFingerprint(input.OperatorId, input.Fingerprint, false);
}

In the above example, I’m calling the stored procedure usp_Biometrics_Update_OperatorFingerprint which is defined in a database called Catalogue, LINQ to SQL provides me with the data context on which the stored procedures are defined. All I need to do us use an instance of CatalogueDataContext and from this, call the stored procedure. Because it’s all about classes, Visual Studio can help me with the parameters and returns through Intellisense. 

The Problem

Under most circumstances calling a stored procedure as described above is sufficient. But there are times when you need to call a stored procedure by name, not by class method. Say for example you have a bunch of stored procedures called:

usp_get_TableName_As_XML

where TableName is a name of a table, any table. The implementation of this stored procedure for any given named table may be complex, for example, they may include selecting output from child tables where foreign key are present. So in this example, a table called Customer may return the following XML:

<rows>
<row>
<customerId>1</customerId>
<customerName>Nigel</customerName>
<addresses>
<address>First Postal Address<address>
<address>Second Postal Address<address>
</addresses>
</row>
</rows>
 
So for each row of Customer data, there may be multiple address rows.
 
For another table called Car the data is simpler:
<rows>
<row>
<registration>AAAAAAA</registration>
<make>Chrysler</make>
<model>Grand Voyager</model>
</row>
</rows>

So for each table, I need a specific stored procedure that returns table data as XML. Each one of these stored procedure returns the XML data as an output parameter and all the stored procedures have the same signature. Below is an illustration of the stored procedure for the table Car:
 
Create Procedure usp_get_Car_As_XML
@xml XML OUTPUT
AS
BEGIN
SET NOCOUNT ON

set @xml = (
Select
registration, make, model
From
Car
)
For XML RAW('row'), Root('rows'))

Return @@Error
END

So the problem is, from code, how do I call the stored procedure for a given table name, returning the XML representation of that table?

The Solution

Well of course I could use some sort of if…elseif… or big switch statement. This will work but I’m not keen on big switch statements. What I really want is to call the stored procedure by name. Fortunately, we can use .Net reflection to find the method in the data context and execute this method:

using (CatalogueDataContextEx db = new CatalogueDataContextEx())
{
XElement xml = null;
String script = String.Format("usp_get_{0}_As_XML", tableName);
db.CallScriptByName(script, ref xml);

//handle the results in xml variable
}
Notice that in this example, the data context is called CatalogueDataContextEx. This is a derived class based on CatalogueDataContext and implements the CallScriptByName() method:
public partial class CatalogueDataContextEx : CatalogueDataContext
{
public void CallScriptByName(String script, ref XElement xml)
{
var sp = typeof(CatalogueDataContext).GetMethod(script);
var result = this.ExecuteMethodCall(this, sp, xml);
xml = (XElement)result.GetParameterValue(0);
}
}

So what is going on here?

In my derived class I have access to all public and protected methods of the data context. This is important as I want to call the ExecuteMethodCall() method on the class, which is protected.

I’m using reflection to get a reference to the stored procedure by name, This is then executed using the ExecuteMethodCall. This call takes a list of the parameters to this stored procedure. In our case, there is only a single output parameter for the XML.

Once the call is made, the results are passed back in a IExecuteResult return parameter. This return can be used to access the call’s parameters, in our case the xml output parameter.

And that’s it.

Future Improvements

This implementation is not completely dynamic. Specifically, the CatalogueDataContext must have definitions for the stored procedures I’m going to call. A better implementation would use a generic data context and build up the method to call.