Fixing Microsoft Communicator/Lync Support on Adium 1.5.4 with Mac OS 10.8 Mountain Lion

The following are steps that I used to fix my support for Microsoft Communicator on my install of Adium on my Mac. Enjoy!

  1. Delete SIPE plugin if you already have it installed
  2. Quit Adium
  3. Download and Unzip SIPE Plugin Located here: http://users.rcn.com/zer0/SIPEAdiumPlugin.AdiumLibpurplePlugin(64bit).zip
  4. Double Click Plugin which will automatically install Plugin and start up Adium
  5. Quit Adium again
  6. Create the following Symlinks with in the /Applications/Adium.app/Contents/Frameworks/libpurple.framework/Versions directory:
    ln -s Current /Applications/Adium.app/Contents/Frameworks/libpurple.framework/Versions/0.10.0
    ln -s Current /Applications/Adium.app/Contents/Frameworks/libglib.framework/Versions/2.0.0
    ln -s Current /Applications/Adium.app/Contents/Frameworks/libintl.framework/Versions/8
  7. Add your Office Communicator Account settings back and you should be able to connect again.

How do you build the UI when the API doesn’t exist yet?

Recently I have been involved on a project where we needed to build a mobile application within 2 weeks. The project needs to access an API which doesn’t exist yet and it all needs to come together at the end with minimal testing and minimal integration refactoring.

You are probably thinking this is all impossible. Well it can be done but not easily. All you need to do is be persistant.

  1. First create an initial API contract and data model
  2. Create code from initial API contract and mock the call backs.
  3. Generate mock data as close as possible to what the API will provide.
  4. Try not to make changes to the API as much as possible. (Adding fields is easier then complete redesign)
  5. Keep probing the API developer on when they have finished so you can start testing integration.

Following this pattern although not fool proof can make it doable to develop an API and UI at the same time.

Do many developers spoil a mobile project?

I am currently working on a iOS mobile project for a client which would be considered quite large for a mobile project. The project is made up of two streams iPad and iPhone.

We have 6 developers in total spread across the two applications which effectively do the same thing but have different user experience to differentiate them.

There is a team of testers 5 in total and the project manager. The testers effectively sit there running manual test cases on the application on all the different devices and OS versions we are supporting.

This seems to happen on every single iteration of the project. I guess some observations that I have made is that. To many developers can spoil the broth as you say. Sometimes people don’t ask questions and they reinvent the wheel making the codebase more complicated. Sometimes developers don’t pay enough attention to detail and defects keep going backward and forward as more edge cases come up. More capable developers can be overloaded to fix complicated issues.

The next thing is how do you remove the physical aspect of testing or at least reduce the cost of it on a mobile project.

There are different frameworks like KIF and cucumber which can reduce this but they don’t necessarily stamp out the need for physical testers. Also if you haven’t started with them it can be hard to add them later.

I have been fighting with these in my head for a while. I really want to create a tangible plan for the client going forward which will not only save them money but will improve the process going forward for them and reduce all this time wasted on defects and testing. My first thought is maybe creating smaller scope and as such smaller and more quicker release cycles.

Anyway does anyone have any thoughts out there or ideas? Add a comment if you do.

Building projects and sub projects with schemes

I have been in a recent situation where a client has decided that they wanted a separate iPhone and iPad application. Normally you would say what is hard about that?

Well they both effectively do the same thing. Also they wanted separate ratings for each one in the App store. So what we did was split out the UI and create a shared kit for calling the backend and parsing its responses. This all sounds good and maximises reuse between the two very different UI’s.

Now comes the problem. We used pre-compiler directives to set the URL’s of which environment we are calling and compile the right ones into the shared kit. Now you may ask why are we doing this?

Well the main reason is security. We don’t want to make it easy for people with jailbroken phones to change the URL’s the application calls. This can introduce things like man in the middle attacks.

So if you want to compile your code to UAT as an example you would need your root project to have a schema called UAT and your sub-project to have a schema called UAT as well. That way when you execute a build for schema UAT in the root project it will chain to sub-project schemes with the same name or use the default scheme. This can also be useful for turning on logging for all your projects too.

  • Root project
    • UAT
  • Sub-project
    • UAT

MKMapView won’t use my region in viewDidAppear

Well after spending a good couple of hours today trying to work out why my MKMapView wouldn’t change to my region. I worked out you can’t do it from viewDidLoad or viewWillAppear but viewDidAppear. Something to do with the MKMapView needs to be rendered on the screen before you can change its region. Okay so to continue my problem. When the person goes off the view and back onto it I wanted it to stay at the position the map was already on. So here comes my master plan. Now this isn’t perfect but it works. Also in my case the client wanted the map to be visible over Australia so I have also included the optimum region for that as far as I know. BTW this code will not work as it is but gives you an idea of what to do.

@interface MapViewController : UIViewController
{
     //BOOLEAN indicating if it is the first time the View Controller has been shown
     BOOL firstTime;
}

@property(nonatomic,retain) MKMapView *mapView;

@end

@implementation MapViewController

-(void) viewDidLoad
{
     [super viewDidLoad];
     firstTime = YES;
     [self setMapView:[[MKMapView alloc] initWithFrame:self.view.frame]];

     [self setView:[self mapView]];
}

-(void) viewDidAppear:(BOOL)animated
{
     [super viewDidAppear:animated];
     if(firstTime)
     {
          MKCoordinateRegion australia;
          australia.center.latitude = -27.041756;
          australia.center.longitude = 131.886769;
          australia.span = MKCoordinateSpanMake(36.687434,62.361841);

          firstTime = NO;
          [[self mapView] setRegion:australia];
     }
}

-(void) dealloc
{
     [mapView release];
     [super dealloc];
}

A MKMapView Category with lots of goodness

I have been working on some mapping stuff recently in the iOS space and have built myself up a few bits and pieces which make up some good categories on the MKMapView. They are attached below:

@interface MKMapView (Extensions)

/**
@brief Will return all annotations on the map except for the users.
*/
-(NSArray*)annotationsWithoutUserLocation;

/**
@brief Returns YES if the specified region is "null".
*/
+ (BOOL) isMKCoordinateRegionNull: (MKCoordinateRegion) region;

/**
@brief Returns YES if the specified region is "null" or empty.
*/
+ (BOOL) isMKCoordinateRegionEmpty: (MKCoordinateRegion) region;

/**
@brief Returns a debug description string for the region.
*/
+ (NSString*) MKCoordinateRegionDebugDescription: (MKCoordinateRegion) region;

/**
@brief This method will fit and zoom all the annotations onto the map
*/
-(void) zoomToFitAnnotations;

/**
@brief This method will fit and zoom all the annotations on the map except the user one
*/
-(void) zoomToFitAnnotationsWithoutUserLocation;

@end

#import "MKMapView+Extensions.h"

@implementation MKMapView (Extensions)
-(NSArray*)annotationsWithoutUserLocation
{
// remove the user location which also is considered an annotation
NSMutableArray *annotations = [NSMutableArray arrayWithArray:self.annotations];
if ( self.userLocation )
[annotations removeObject:self.userLocation];

return annotations;
}

+ (BOOL) isMKCoordinateRegionNull: (MKCoordinateRegion) region;
{
return isinf(region.center.latitude) || isinf(region.center.longitude);
}

+ (BOOL) isMKCoordinateRegionEmpty: (MKCoordinateRegion) region
{
return [MKMapView isMKCoordinateRegionNull: region] || (region.span.latitudeDelta == 0.0 && region.span.longitudeDelta == 0.0);
}

+ (NSString*) MKCoordinateRegionDebugDescription: (MKCoordinateRegion) region
{
return [NSString stringWithFormat:@"{center: {%f,%f}, span: {%f,%f}}",
region.center.latitude,
region.center.longitude,
region.span.latitudeDelta,
region.span.longitudeDelta];
}

-(void) zoomToFitAnnotations {
MKMapRect zoomRect = MKMapRectNull;
for (id annotation in self.annotations) {
MKMapPoint annotationPoint = MKMapPointForCoordinate(annotation.coordinate);
MKMapRect pointRect = MKMapRectMake(annotationPoint.x, annotationPoint.y, 0, 0);
if (MKMapRectIsNull(zoomRect)) {
zoomRect = pointRect;
} else {
zoomRect = MKMapRectUnion(zoomRect, pointRect);
}
}
[self setVisibleMapRect:zoomRect animated:YES];
}

-(void) zoomToFitAnnotationsWithoutUserLocation {
MKMapRect zoomRect = MKMapRectNull;
for (id annotation in self.annotationsWithoutUserLocation) {
MKMapPoint annotationPoint = MKMapPointForCoordinate(annotation.coordinate);
MKMapRect pointRect = MKMapRectMake(annotationPoint.x, annotationPoint.y, 0, 0);
if (MKMapRectIsNull(zoomRect)) {
zoomRect = pointRect;
} else {
zoomRect = MKMapRectUnion(zoomRect, pointRect);
}
}
[self setVisibleMapRect:zoomRect animated:YES];
}

- (void) zoomToAnnotation:(id )annotation {
MKCoordinateSpan span = {0.027, 0.027};
MKCoordinateRegion region = {[annotation coordinate], span};
[self setRegion:region animated:YES];
}

- (MKMapRect) getMapRectUsingAnnotations:(NSArray*)theAnnotations {
MKMapPoint points[[theAnnotations count]];

for (int i = 0; i <; [theAnnotations count]; i++) {
id annotation = [theAnnotations objectAtIndex:i];
points[i] = MKMapPointForCoordinate(annotation.coordinate);
}

MKPolygon *poly = [MKPolygon polygonWithPoints:points count:[theAnnotations count]];

return [poly boundingMapRect];
}

- (void) addMapAnnotationToMapView:(id )annotation {
if ([self.annotations count] == 1) {
// If there is only one annotation then zoom into it.
[self zoomToAnnotation:annotation];
} else {
// If there are several, then the default behaviour is to show all of them
//
MKCoordinateRegion region = MKCoordinateRegionForMapRect([self getMapRectUsingAnnotations:self.annotations]);

if (region.span.latitudeDelta <; 0.027) {
region.span.latitudeDelta = 0.027;
}

if (region.span.longitudeDelta <; 0.027) {
region.span.longitudeDelta = 0.027;
}
[self setRegion:region];
}

[self addAnnotation:annotation];
[self selectAnnotation:annotation animated:YES];
}

@end

Doing Logging in Objective-c

Ever need to log something in Objective-c and then have it switched off when you deploy to the AppStore. Well that can be done by using the simple macros below. By defining the preprocessor variable DEBUG against a build target you switch on logging. Without it no logs come out.

#ifdef DEBUG
#define VLog(fmt, ...) NSLog((@"%s [Line %d] " fmt), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__);
#define VLogFrame(identifier, frame) DLog(@"%s: %@ - X:%lf Y:%lf W:%lf H:%lf", __PRETTY_FUNCTION__, identifier,     frame.origin.x, frame.origin.y, frame.size.width, frame.size.height);
#define VLogSize(identifier, size) DLog(@"%s: %@ - width:%lf height:%lf", __PRETTY_FUNCTION__, identifier, size.width, size.height);
#else
#define VLog(...)
#define VLogFrame(identifier, frame)
#define VLogSize(identifier, size)
#endif