Archive for the ‘iPhone’ Category

Improvements to RemObjects SDK and Data Abstract for iOS 4

 

iOS-4

Yesterday, Apple shipped iOS 4, the latest version of its operating system for iPhone and iPod touch. As you’ve undoubtedly read abut elsewhere in detail, iOS 4 brings with it many improvements for developers, mostly (as far as it affects us) in regards to multi-tasking and background operation.

We’ve been busy updating our “for OS X” products to take advantage of some of these new features in iOS 4, and there’s two areas in particular that i would like to highlight.

Background Tasks

As you probably know, iOS 4 adds support for multitasking of third party applications, but does it in a restricted fashion that ensures background applications do not negatively affect battery life or performance. Regular applications will be completely suspended when the user switches away from them, and get no time to execute – no iOS 4 multitasking is no opportunity to keep working and talking to your servers in the background, as you please.

However, iOS 4 provides a handful of opportunities for apps to keep running in the background. Most of these (VoIP, Audio Playback and Navigation) do not really affect our libraries, but there is one that does: Background Task Completion.

Background tasks basically allow your app to say “hey, i’m kinda in the middle of something, do you mind”, and let iOS give your app a chance to finish what it’s doing. Normally, this is a manual action your code has to take to request this, but RemObjects SDK encapsulates this for you: every asynchronous request (you are using asynchronous requests, right? if not, you really should) you make will automatically participate in iOS’s background task completion system, so of the user does happen to quit your app, RO will try and keep your request running until it completes (yours app will still be suspended, once it does, but you can pick up with the result of your call, once the user switches back).

Now, keep in mind that background task completion is no guarantee. The OS might still go ahead and suspend your app completely, if your request is taking too long (were you uploading a 30MB video in one large chunk, maybe?). Or your app might even be killed completely, if memory runs low. But in most scenarios, background task completion will let your request succeed, even though the app was terminated.

If iOS does happen to suspend your app ahead of time, you will of course get proper failures for your request, once your app wakes back up. If you have code in place to handle connection failure gracefully (for example in case of signal loss) now, you should be well covered.

There’s nothing you need to do in your code (except build against the iOS4 version of RemObjects SDK) to take advantage of this. the iOS4 version of RO will also gracefully fall back to not mess with background task completion if it finds that it’s running on an older iPhone OS, in case your app supports multi-targetting with iPad (3.2) or iPhones running 3.x.

Blocks and Grand Central Dispatch

Not strictly a new feature of iOS 4, Blocks and GCD were initially introduced in Mac OS X 10.6 (Snow Leopard) and now made it into iOS as well. We’ve enhanced RemObjects SDK and data Abstract thruout, to take advantage of bCGS where possible, and to provide overloaded methods that work with blocks.

For example in addition to using any of the existing beginGetDataTable* methods on DA’s Remote Data Adapter that will call back your delegate, we now have versions where you can pass a block, to be called once the request as completed, and your table has been downloads. e.g.

[rda beginGetDataTable:@"Customers" withBlock:^(DADataTable *customers){
  for (DADataTableRow *row in customers)
    NSlog(@"Name: ", [row valueForKey:@"Name"]);
}];

This helps keep request and response code in one place, and makes asynchronous remote calls a lot easier in general.

On RemObjects SDK level, RODL-generated code for your services will also get extra overloads that allow calling your own methods with a block to receive the response.

Deployment Changes

Most of the changes described here will be in the next beta builds for Data Abstract and RemObjects SDK, which should be available for customers later today. They will ship as “final” version with our July release.

We’re also making some changes to our binary deployment, alongside this.

We have switched our build machine to the new iPhone SDK 4, so we’re now building and shipping iPhone OS 3.2 (iPad) and iOS 4 (iPhone & iPod touch) binaries only; the 3.2 binary does nor use any 3.2-specific APis, so you will be safe using that for 3.x projects (and even the 4.0 binary will downgrade gracefully for older deployment targets), but if you do need binary versions built for specific older versions of iPhone OS, you can always build from source. The binaries we do ship are now in the Bin/iOS folder; you might need to adjust your projects’ Library Search Path setting

We have also changed the default build target of our Mac OS X libraries to 10.6 (Snow Leopard) and the framework binaries we ship are built for that and include full support for GCD and Blocks. Of course you can still rebuild the framework from source, if you need to deploy to 10.5 (Leopard), and don’t use GCD.

 

Ok, so that is a quick summary of what’s coming of in RODA/OSX for iOS 4 with the July release. If you are doing iOS 4 development (or even if not yet), please give the beta (available later today) a try, and let us know what you think!

yours
marc

by marc hoffman, June 22nd, 2010

WWDC

Apple finally announced their annually World Wide Developer Conference, WWDC, today, after much anticipation and delay. Of course I bought my ticket right away, and am looking forward to the conference already.

I’m also looking forward to meeting any of our customers and/or readers of this blog – if you are going, drop me a message here, via email or twitter, and we can meet up!

See you in San Francisco!

Sent from my iPad

by marc hoffman, April 28th, 2010

“Der Entwickler” Article

The latest issue of german software development magazine Der Entwickler is out, containing among other good stuff an article by yours truly on database development for the iPhone with Data Abstract for OS X.

Let me know what you think!

by marc hoffman, April 17th, 2010

The Missing Link: Schema Modeler for Mac

In this previous post, i talked about the current state of Data Abstract for OS X, and also promised a follow-up with a roadmap of where we are going next.

Where We’re at

To recap, when Data Abstract for OS X originally shipped last winter, it was client only. Basically, you needed to have existing servers written in DA/.NET or DA/Delphi (or write new ones using those same tools). This basically made DA/OSX a solution mainly for our existing Delphi and .NET customers, allowing them to expand their current solutions with native Mac and iPhone clients

In the following Spring release last month, we added Relativity Server running on all three platforms, giving developers the option to host Data Abstract services on Mac (in addition to Linux and Windows), without needing to know .NET or Delphi (or even having those tools).

This took care of two thirds of the picture, but there is still one hole in a complete Mac solution: Schema Modeling (and Relativity Administration). While Relativity server runs on the Mac, developers are still dependent on Windows based tools both to configure and control their Relativity server, and – more importantly – design and work with their schemas.

What’s Coming Next

As hinted at before, we’re working on closing this gap, and are writing a new tool for Mac developers that provides two things. First, it builds on the functionality in Relativity Server Admin for Windows, and expands it to a new concept, to what we are calling the Server Explorer. Secondly, it will allow Schema Modeling thru Relativity server, essentially providing the full functionality of Schema Modeler for Windows.

Let’s have a quick look at both of these aspects.

Server Explorer

Server Explorer will provide a single unified place for you to keep track of and administer all your Data Abstract servers. You can see a preview of what the current beta of Server Explorer looks like, here: (note that many things may still be subject to change before this ships):

Server-Explorer-(small).png

Server Explorer will automatically find ZeroConf servers on your local network, and you can manually register servers at remote locations, using their Target URL. It can handle Relativity and custom servers alike, although it will show slightly different options and features for each.

In the screenshot you see two servers folded open – one custom server (this is our Continuous Integration system, OnyxCI), and one Relativity server.

Custom DA servers don’t provide much room for configuration, so Server Explorer restricts itself to showing a list of all services it finds, and allowing you to browse data (including running DA SQL queries, if the server supports it) and view the readonly schema. That is provided you have a login to the server that authorizes data access. In this case, you can see there’s 2 data services and four other services exposed by our server.

For Relativity servers, there’s a lot more to see. As the screenshot shows, you can dive into the different Domains configured on the server (we’re looking at one called “OneSpace” here, which is a personal project of mine). You can look at the schemas (as well as browse and query the data they expose) and connection strings, and you can of course configure the Relativity server itself, such as to create new Domains.

Where things get interesting is on the Connection and Schema nodes, both of which allow you to open dedicated editor documents for what essentially are the .daConnections and .daSchema files.

Schema Modeler

Combined, these two editors give you the same abilities you have in Schema Modeler for Windows: You can define new connections and browse the actual contents of the database they connect to. And you can visually create and edit schemas, both by manually defining data tables and other elements, or by dragging tables from a Connection to the Schema document.

Connections.png

There is two core differences between what you (may) know from Schema Modeler for Windows, and how the Mac version of Schema Modeler handles things.

For one thing, while DASM/Win combines both the Schema and the Connection list into a single document window, DASM/Mac keeps them separate. The reason is simply that a Domain has one list of connections, but might have more than one schema defined. Having both in separate windows makes it more intuitive to edit several schemas at the same time – you will have one shared Connections window, where you can edit the connections, and from which you can drag objects into any of the schemas.

The second difference is more important. The current DASM/Win contains both DA/.NET and DA/Delphi server code that talks directly to the databases to retrieve metadata such as table names. If you are writing a Delphi server, it uses DA/Delphi drivers, if you are writing a .NET server or creating a schema for Relativity, it uses DA/.NET ones. This logic is embedded right into DASM/Win.

For DASM/Mac we have changed this, and have moved modeling support into the Relativity server. Relativity now exposes APIs (password protected, of course) that allow Schema Modeler to create connections for and obtain meta data from any database supported by the instance of Relativity it is talking to – over the network.

This means that you don’t need to worry about the proper drivers being installed and properly configured on your development machine, it also means that your database does not even have to be exposed to the network your development machine is on (you might have both your Relativity server and the backend database hosted somewhere on the internet, and expose only Relativity thru the firewall, for example. As long as you can talk to your Relativity server, you can work).

As a side effect, this allows Schema Modeler to be a 100% native Cocoa application and to still model and validate schemas against all the DA/.NET drivers available in Relativity.

Schema-Modeler-(small2).png

As you can see in the screenshot of the Schema document above, DASM/Mac also already exposes the new DA Scripting feature, which allows you to use JavaScript to define business rules that will run inside Relativity (but also in custom DA Servers).

The Add Event drop down provides quick access to stubs for all the available events, in this case the schema implements (well, pretends to) the beforeProcessDelta event, which gets called before any chunk of updates received from the client would be applied

When?

Work on DASM/Mac is well under way, and we’re confident it will ship with the upcoming “Summer 2010″ releases. The same is true for DA Scripting in DA/.NET and Relativity (the feature is completed as we speak).

Stay tuned to see both features in beta drops, very soon.

yours,
marc

by marc hoffman, April 14th, 2010

“Making Apps”

Both Olaf Monien and Daniel Magin have blogged about the first in their “Making Apps” series of events on iPhone development, which RemObjects Software had the great honor of sponsoring.

This first event last week was in Munich; from what i hear there might still be a couple of seats available for the encore in Hamburg, next week…

by marc hoffman, March 20th, 2010

RT: Kontra on Apple, Google and “Choice”

The Angry Drunk links to a worthwhile read taking apart the latest “draconian control” clamors from Google’s new spokesperson for the Android / against the iPhone, and IMHO sums it up very nicely:

The bottom line is, if you think that Google is somehow a bastion of ‘open’ computing you are either ignorant, delusional or a hypocrite.

(Via The Angry Drunk).

by marc hoffman, March 16th, 2010

Visualizing Data with Core-Plot

Core Plot is an open source graphing framework for Cocoa developers that makes it really easy to add graphs to your applications for the Mac and iPhone. Over the past half year, i’ve had the chance to use Core Plot in several of the internal applications i have been working on and have been very impressed with it, so i wanted to talk a little bit about how to use the library, in general, and how to make generate charts based on data in a Data Abstract for OS X application, in particular.

The example shown here is from a small iPhone application i wrote to keep track of the status of our Continuous Integration servers. The application shows the success (or errors) of any builds done on the machine, as well as – and this is the part where Core Plot comes in – graphs the number of tests that run and fail, over time:

In the application in question, the chart is being displayed in a cell within a UITableView, so our example starts out by implementing a custom UITableViewCell class that will host the graph. But the same general principles apply to show a chart anywhere else (and, replacing UIView with NSView, for using charts in a desktop app).

Core Plot uses a Core Animation as underlying technology, and as such draws itself in a specialized view class CPLayerHostingView. So we start out by crating this view, and adding it as sub-view:

- (id)initWithStyle:(UITableViewCellStyle)style 
{
  self = [super initWithStyle:style reuseIdentifier:@"MyChartCell"];
  if (self)
  {
    CGRect frame = self.contentView.bounds;
    CPLayerHostingView *chartView = [[CPLayerHostingView alloc] initWithFrame: frame];
    [self addSubview:chartView];
    //...

Once the CPLayerHostingView is created, we can add a graphs to it, and configure them. There are several graph types supported, but we want a regular line graph with X/Y coordinates, so we’ll choose a CPXYGraph class. We’ll add the graph to the view, and set a padding, to give us some room between the edges of the table cell:

  // create an CPXYGraph and host it inside the view
  CPTheme *theme = [CPTheme themeNamed:kCPPlainWhiteTheme];
  graph = (CPXYGraph *)[theme newGraph];	
  chartView.hostedLayer = graph;
 
  graph.paddingLeft = 10.0;
  graph.paddingTop = 10.0;
  graph.paddingRight = 10.0;
  graph.paddingBottom = 10.0;

The next step is to set up a plot space. A plot space defines the coordinate system for one or more charts in a graph, essentially mapping logical values to the area in the graph. For example, your chart might show values ranging from, say, 0 to 1000. The plot space defines the scale of those values in relation to the graph. It also defines the visual range of values that can be seen on screen.

To match our X/Y graph, we’ll create a CPXYPlotSpace, and set it to a range of 0-100 for both axis:

  CPXYPlotSpace *plotSpace = (CPXYPlotSpace *)graph.defaultPlotSpace;
  plotSpace.xRange = [CPPlotRange plotRangeWithLocation:CPDecimalFromFloat(0)
                                                 length:CPDecimalFromFloat(100)];
  plotSpace.yRange = [CPPlotRange plotRangeWithLocation:CPDecimalFromFloat(0)
                                                 length:CPDecimalFromFloat(100)];

Finally, we’ll set up to axis, for the X and Y coordinates. Axis provide labels and tick-marks for the viewer, to give context to the values being shown.

The X/Y graph already provides a set of axis in for of a – you guessed it – CPXYAxisSet class. There are several properties on the axis’ that are worth looking at at tweaking to to suite out needs:

  • majorIntervalLength defines the number of units between “big” ticks on the axis. In this case it’s set to show one very 10 units. (if it were not for the exclusionRanges, discussed below, a numeric label would show for each major tick, as well).
  • minorTicksPerInterval specified how many small ticks will be shown between each major one. in this case, a value of 2 indicated that small would show for 5, 15, 25, etc. (a value of 9 would show ticks for every single unit).
  • Finally, exclusionRanges allows to define areas where no axis labels will be drawn. In my app, i don’t want to see any labels, so i set a range to exclude the entire visible graph
  CPXYAxisSet *axisSet = (CPXYAxisSet *)graph.axisSet;
 
  CPXYAxis *x = axisSet.xAxis;
  x.majorIntervalLength = length:CPDecimalFromFloat(10);
  x.constantCoordinateValue = length:CPDecimalFromFloat(2);
  x.minorTicksPerInterval = 2;
  x.borderWidth = 0;
  x.labelExclusionRanges = [NSArray arrayWithObjects:
                              [CPPlotRange plotRangeWithLocation:CPDecimalFromFloat(-100) 
                                                          length:CPDecimalFromFloat(300)], 
							  nil];;
 
  CPXYAxis *y = axisSet.yAxis;
  y.majorIntervalLength = length:CPDecimalFromFloat(10);
  y.minorTicksPerInterval = 1;
  y.constantCoordinateValue = length:CPDecimalFromFloat(2);
  y.labelExclusionRanges = [NSArray arrayWithObjects:
                              [CPPlotRange plotRangeWithLocation:CPDecimalFromFloat(-100) 
                                                          length:CPDecimalFromFloat(300)], 
							  nil];

Our graph is now fully set up – except for one important part: we still need to define one or more charts to display actual values. My application needs to show two graphs on top of each other – one showing the total number of tests that ran, and another showing the number of failures.

Core Plot supports a variety of chart (or, plot) types, such as bar and pie chart, but for this scenario, we want a simple line graph, which is done via a CPScatterPlot. (A scatter plot actually allows to draw a plot with points scattered all over the place, not just as string y = f(x) values, but we will use it in the more simplistic case).

We’ll create a new CPScatterPlot instance, configure some properties such as its lineWidth and lineColor, and most importantly, set the dataSource to the object that will provide the plot with its data – in this case, self. To round things off, we also tell the plot to fill the area underneath it with a gradient, going from almost-transparent green to nothing. This gives the plot some depth, without hiding the other plots.

  CPScatterPlot *dataSourceLinePlot = [[[CPScatterPlot alloc] init] autorelease];
  dataSourceLinePlot.identifier = @"AllTests";
  dataSourceLinePlot.dataLineStyle.lineWidth = 3.f;
  dataSourceLinePlot.dataLineStyle.lineColor = [CPColor greenColor];
  dataSourceLinePlot.dataSource = self;
  [graph addPlot:dataSourceLinePlot];
 
  // Put an area gradient under the plot above
  CPColor *areaColor = [CPColor colorWithComponentRed:0.3 
                                                green:1.0
                                                 blue:0.3
                                                alpha:0.3];
  CPGradient *areaGradient = [CPGradient gradientWithBeginningColor:areaColor 
                                                        endingColor:[CPColor clearColor]];
  areaGradient.angle = -90.0f;
  CPFill *areaGradientFill = [CPFill fillWithGradient:areaGradient];
  dataSourceLinePlot.areaFill = areaGradientFill;
  dataSourceLinePlot.areaBaseValue = CPDecimalFromString(@"1.75");

We repeat the above to add a second plot, except this time we use red for the line color and gradient, and set the identifier to @"FailedTests".

This was quite a bit setup, but our graph is now ready plot, and will ask its data source (our cell) for data by sending it messages from the CPPlotDataSource protocol. This protoco will be familiar to any Cocoa developer who worked with, for example, a UITableView or NSTableView, or any other control using a data source.

To the protocol, two methods need to be implemented. The first is numberOfRecordsForPlot: which will, simply enough, return the number of items (in case of a scatter plot, points, the chart will contain. The second can be one of three methods that return the actual data. We’ll implement numberForPlot:field:recordIndex:.

The data to display is retrieved via Data Abstract, using a simple DA SQL request on a table that contains all test runs:

	NSString *sql = [NSString stringWithFormat:@"SELECT TOP 50 ID, _TotalTestCount, \
                                     _FailedTestCount, FROM TestRuns ORDER BY ID DESC"];
	testRuns = [[rda getDataTableWithSQL:sql] retain];

The _TotalTestCount and _FailedTestCount fields are server-calculated based on a relation table and contain the total number of run and failed tests for each test run. We can Key Path operators to get the maximum value, and adjust our plot space accordingly, so that the entire graph fits into the chart:

  int count = [[testRuns rows] count];
  int maxTests = [[[testRuns rows] valueForKeyPath:@"@max._TotalTestCount"] intValue];
 
  CPXYPlotSpace *plotSpace = (CPXYPlotSpace *)graph.defaultPlotSpace;
  plotSpace.xRange = [CPPlotRange plotRangeWithLocation:CPDecimalFromFloat(0)
                                                 length:CPDecimalFromFloat(count)];
  plotSpace.yRange = [CPPlotRange plotRangeWithLocation:CPDecimalFromFloat(0)
                                                 length:CPDecimalFromFloat(maxTests)];

This is of course a bit redundant with the values we set earlier, but in real life application, the data access will usually happen in a different place than the initial setup (and data might get changed or refreshed during the course of the application), so i find it important to set up the graph properly to begin with, but then adjust the range to the real data.

With the data retrieved and the plot space adjusted, we can now implement the two data source methods.

numberOfRecordsForPlot: will simply return the number of rows in our table. numberForPlot:field:recordIndex will be called twice per data point, once for the X value (since we’re drawing a “non-scattered” plot, we’ll simply return the indexes) and one for the Y value, which we’ll retrieve from our data table. The different identifiers we assigned to the two plots will serve to distinguish which field to use for the Y value.

-(NSUInteger)numberOfRecordsForPlot:(CPPlot *)plot 
{
  return [rows count];
}
 
-(NSNumber *)numberForPlot:(CPPlot *)plot 
                     field:(NSUInteger)fieldEnum 
               recordIndex:(NSUInteger)index 
{
  switch (fieldEnum)
  {
    case CPScatterPlotFieldX: 
    {
        // inverse numbers, so first (latest) test run is on the right.
        int x = [rows count]-recordIndex; 
        return [NSDecimalNumber numberWithInt:x];
    }
    case CPScatterPlotFieldY:
    {
      if ([plot.identifier isEqual:@"AllTests"])
      {
        float v = [[[rows objectAtIndex:index] valueForKey:@"_TotalTestCount"] floatValue];
        return [NSNumber numberWithFloat:v];
      }
      else
      {
        float v = [[[rows objectAtIndex:index] valueForKey:@"_FailedTestCount"] floatValue];
        return [NSNumber numberWithFloat:v];
      }
    }
  }
  return nil;
}

And that’s it! You can find more info on Core Plot on their Google code page; i also recommend joining their mailing list for latest news and support.

by marc hoffman, January 26th, 2010

Incremental Data Fetching in Data Abstract for OS X

Bugs 7, the new bug-tracking application i have been working on over the past month for internal use here at RemObjects, employs several different data access paradigms (all based on Data Abstract for OS X), to accommodate the different nature of the data in individual tables.

The most interesting one is the main Issues table that contains all the bugs and tasks that are logged in the system. That is due to the fact that (a) it is a huge table (with over 25,000 records as of now, sized at about 11 MB when transferred over the wire, compressed) and that (b) by the very nature of a bug tracking system, this table changes frequently, and needs to be updated on the clients, often.

Rather then downloading the entire table anew every time, we optioned for a solution that allowed us to incrementally fetch only those records that have changed, and integrate them with the local dataset. This way, only minimal traffic is occurred for the regular refresh (which our client, by default, does every two minutes).

In the following post, i want to give you a quick glimpse at how this was accomplished, and how you can leverage the same technology in your own Data Abstract applications.

The Server

A couple things happen on the middle tier server (written using Data Abstract for .NET), to support the incremental refresh. Like all tables in the Bugs database, our Issues table has an UpdatedDate field, which gets automatically adjusted by the business logic code on the server. Every time a new issue is created, or an existing issue is updated, the server puts the current UTC time into the UpdatedDate field, clearly marking the order in which issues have been touched.

This is handled by a simple BeforeProcessChange event handler on the server, which simply adjusts the received delta, as such:

method BugsDataService.bp_Issues_BeforeProcessChange(aSender: BusinessProcessor; ea: DeltaChangeEventArgs);
begin
  ea.DeltaChange.NewValues['UpdatedDate'] := DateTime.Now;
  ea.DeltaChange.NewValues['UpdatedByID'] := Session['UserID'];
end;

(Of course the actual code in our server performs a lot more checks and changes, to enforce business logic for our database – but that’s beyond the scope of this post.)

Also, our Issues table does not permit deleting of records (only closing of issues, which sets their status accordingly, but does not remove the rows from the database). This alleviates the problem of worrying about rows disappearing from the table altogether.

The Client

On the client, a bit more custom logic is necessary, to perform the incremental updating.

When the client application (“Bugs 7”) is first started, it checks whether a briefcase file with a local copy of the data is found from a previous run, or not.

If not, the client will start a request to download the complete set of data from the server. This is a one-time process, and will download the entire table with it’s (currently) 11MB across the wire. Once downloaded, it is stored in a briefcase file, so on next application start, the data can be loaded locally. After the download is finished, the application also takes note of the latest UpdatedDate value it can find in the table, for future reference. This is made easy by Cocoa’s KVC and Key Paths:

- (void)downloadIssueData 
{
	// Initial download can take long, server side, to gather data. 
	// allow for a longer timeout.
	[(ROHTTPClientChannel *)[[rda dataService] channel] setTimeout:360];
 
	// fetch full table from server	
	issues = [[rda getDataTable:@"Issues"] retain];
	maxDate = [[[issues rows] valueForKeyPath:@"@max.UpdatedDate"] retain];
 
	// save table to briefcase
	[self saveIssues];
}

If instead a briefcase was found, the Issues table simply gets loaded from that file, alongside the stored maxDate:

- (void)loadIssueDataFromBriefcase
{
	DABriefcase *briefcase = [DABriefcase briefcaseWithFolder:briefcasePath];
	issues = [[briefcase tableNamed:@"Issues"] retain];
	maxDate = [[briefcase.properties valueForKey:@"BugsMaxUpdateDate"] retain];
}

Whichever path was taken, the application now holds a local copy of the Issues table it can work with. The next step it to schedule the regular refreshes, and for that an NSTimer is configured, to fire at regular intervals, on a background thread.

This NSTimer will trigger our beginRefreshBugs method, which uses asynchronous requests to start checking for new issues. It uses the previously stored maxDate and a feature of Data Abstract called DA SQL, to fetch only those issues that have newly changed:

- (void)beginRefreshBugs
{
	// prevent two refreshs happening at the same time, if the NSTimer
	// triggers again before the previous refresh has finished.
	if (refreshingBugs) return; 
	refreshingBugs = YES;
 
	// build the DA SQL query
	NSString *sql = [NSString stringWithFormat:"SELECT * FROM Issues WHERE UpdatedDate >= '%d'",
						(int)[maxDate timeIntervalSince1970]];
 
	//and start an Async Request to the server
	DAAsyncRequest *ar = [rda beginGetDataTable:@"Issues" withSQL:sql start:NO];
	[ar setDelegate:self]; 
	[ar setContext:@"RefreshIssues"];
	[ar start];
}

The DAAsyncRequest, once started, will communicate with the server in a background thread, without blocking the caller. beginRefreshBugs will return right away, and not wait for the request to complete (or fail).

Once the request did complete, it will call back to a delegate method (in this case we assigned self as the delegate, above), called asyncRequest:didReceiveTable:. It is here that we handle integrating the received data back with our big issues table by sending it the mergeTable:withPrimaryKey: message. This will replace the data in any rows that have changed, as well as add any new rows to the table:

- (void)asyncRequest:(DAAsyncRequest *)request didReceiveTable:(DADataTable *)table
{
	// our class may handle access to more tables. check the context
	// to make sure we handle the right requests.
	if ([[request context] compare:@"RefreshBugs"] == NSOrderedSame)
	{
		// nothing received? nothing to do!
		if ([table rowCount] > 1) 
		{
			@try
			{
				[issues mergeTable:table withPrimaryKey:@"ID"];
				maxdate = [[[table rows] valueForKeyPath:@"@max.UpdatedDate"] retain];
				[self saveBugs];
			}
			@finally 
			{
				refreshingBugs = NO;
			}
		}
	}
}

Of course a refresh might also fail (for example due to a broken internet connection, for this case, we implement a second delegate method, called asyncRequest: didFailWithException:, as follows:

- (void)asyncRequest:(ROAsyncRequest *)request didFailWithException:(NSException *)exception
{
	if ([[request context] compare:@"RefreshBugs"] == NSOrderedSame)
	{
		refreshingBugs = NO;
	}

The View

The last step after receiving new issues is to update any affected views. This happens more or less automatically, as every view that shows one one more issues (whether it’s the regular grid view of issues, a chart visualizing issue data, or an individual issue’s detail view) will be have registered itself to observe DA_NOTIFICATION_TABLE_CHANGED notifications on issues. And like any other change to a data table, mergeTable:withPrimaryKey: will send such a notification if changes happened, allowing all views to update themselves.

In Bugs, all of this happens in the background, so over time the view(s) presented to the user just seamlessly adjust themselves, as changes happen – new issues come into views; issues resolved by other users disappear on their own, etc.

This topic just touches on a very small aspect on Bugs 7, which itself is part of a mch larger project, comprised of four different client applications (Mac and iPhone, based on DA/OS X, for Windows, based on DA/.NET and Gtk# and the Web) as well as a middle tier server. We will blog more about different aspects of this project over the next few months, ands we’re also working on a bigger case study, to appear at bugsapp.com, soon. Stay tuned to this space, for more.
by marc hoffman, January 16th, 2010

Introducing Plateau for MonoTouch

Introducing Plateau

I’ve got a new personal Open Source project – Plateau. It’s a component library for MonoTouch. See my blog post at yamtu.net for more information.

by R. Brian Lindahl, November 10th, 2009

iPhone & Desktop Pairing with RemObjects SDK

One common scenario when developing iPhone apps is that you have a desktop application of some sorts that you want your iPhone to pair with – be it to sync data, remote control one with the other, or establish any other kind of communication.

In this scenario, the desktop app would be the “server”, in RemObjects SDK terminology, and the iPhone would be the client.

For the next release of RemObjects SDK for OS X, coming next month, we’ve created a nice sample that illustrates how to easily achieve this, and i figured i would post a small screencast of the sample in action, below.

What you see is a desktop app written in Delphi (although .NET will do just fine, as well), which for sample purposes has a pretty minimal UI, but in real life would be your full-fledged desktop UI app. When the iPhone client is a launched, it automatically detects any instance of your desktop app running on the local network and shows them in a list.

(The UI for this would be up to the application designer, obviously; for simplicity we simply show them all in a UITableView. In real life, if only one server was found you’d probably want to forgo the list altogether and just ask the user if he wants to connect).

Once the user picks a server, communication is initiated, by the iPhone calling a service method and passing it a unique GUID it generated. Since this is the first time these two are talking to each other, the server rejects the phone, but displays a random 4-digit code in it’s UI (this code is not known to the phone, at this stage). The phone asks the user to enter the code and, once done, sends it to the server to confirm the pairing. Once successful, the server stores the client’s GUID for future reference – and any future calls (even after the phone app has been restarted) will work right away.

Of course instead of showing an UIAlertView, your application would instead commence doing it’s real work.

Here’s the app in action:

by marc hoffman, September 18th, 2009