You are browsing the archive for iPhone.

Avatar of marc

by marc

Creating a Turn-Based Game for iOS: Nougat Tic-Tac-Toe

March 11, 2013 in iOS, iPhone, non-tech, Nougat, Oxygene, Prism, Visual Studio, WWDC, Xcode

Over the past couple of weeks, i have been working on a new sample project for Oxygene “Nougat” on the side. After having gotten Browse500 into the App Store in January and showing how to reimplement a simple native UIKit control from Objective-C in Nougat last month, i wanted to create another example that illustrates the benefits of how Nougat interacts directly with the native APIs, and i picked a technology i knew nothing about at all to do so: Game Center.

I’m not a big gamer; i don’t find myself playing games much, and i have certainly never written one before, so this was a great opportunity to explore something new.

For those of you not familiar with it, Game Center is a technology Apple ships in iOS (and now also Mac OS X) that allows for games to interact across the network to create multi-player games, and to keep track of achievements. Game Center is not about graphics, or game UI, or anything of the sort (there’s other frameworks for that), it’s only for bringing players together.

I decided to create a simple turn-by-turn game, that is, a game where multiple players (in this case two) can interact, but only one player is active at a given time. Once a player has made their move, it’s the next player’s turn. To keep things simple, i picked a game that everybody knows and that is dead simple to implement, game playing wise: Tic Tac Toe. This way, having to explain the game itself won’t get in the way of the sample code.

To start with, i had to dig myself into the Game Center documentation and i watched a couple of videos on Turn-by-Turn gaming from WWDC 2011 (sessions i didn’t attend at the time). As it turns out, the API for interacting with Game Center for such a game is surprisingly easy and straightforward, and i was up and coding in no time.

Before we get started looking at the code, let’s look at the prerequisites:

  • Here’s the amount of effort anyone from the Oxygene team had spent on thinking about or working on making GameKit (the framework for interacting with Game Center) available to Nougat, before i started this endeavor: 0. Nada. Nil.

  • Here’s the amount of legwork i had to do before i could work with the GameKit APIs, just like every other Cocoa developer would: i added a reference to GameKit to my project. Done.

That’s it. No begging someone to translate header files, or waiting for someone to create a crappy abstraction wrapper class. Add a reference, and use the APIs as Apple intended them — that’s the way development should be.

The Code

Let’s dive into the code.

First of all, the full code of Tic-Tac-Toe is included in the latest Nougat beta, and it’s also available in my Github account for anyone to explore.

There’s really three separate parts that are of interest as a sample project in this app:

  1. The GameKit integration
  2. The game board, implemented as a custom UIControl
  3. The computer player algorithm, generously provided by my good friend and long-time Oxygene user, Jeremy Knowles.

Let’s look at GameKit first.

Working with GameKit

There’s four main classes or concepts that you will work with for GameKit.

The first is the local player, a singleton object that can be obtained via a call to GKLocalPlayer.localPlayer. This objects provides the game with information about the local player — most importantly their ID, and whether the local player is in fact registered and authenticated with Game Center or not. If the player is not set up for Game Center, Tic Tac Toe will simply disable the appropriate UI and just allow local vs. computer game play (although it would be possibly for a game to provide a signup UI, if so desired).

Next is the GKTurnBasedMatchmakerViewController, which provides access to the standard GameCenter UI. It gives the user access to any and all games they might already have going on, as well as the chance to start a new game. In Tic-Tac-Toe, i make this UI available via a button in the navigation bar, and all that’s needed to show the view is code like the following:

var request := new GKMatchRequest;
request.minPlayers := 2;
request.maxPlayers := 2;
 
var mmvc := new GKTurnBasedMatchmakerViewController withMatchRequest(request);
mmvc.turnBasedMatchmakerDelegate := self;
mmvc.showExistingMatches := true;
presentViewController(mmvc) animated(true) completion(nil);

This code brings up the Game Center UI, with the list of games (if any) or straight with the “Start New Game” (if not). Via the GKMatchRequest, we force the number of players to exactly two. (Game Center itself allows games for up to 16 players):

Tic Tac Toe Game Center

The way Game Center works for starting new games is that it lets you either invite a known friend who also plays the game or — much more interestingly — auto-match you with another random player. One thing that’s cool is that after starting a game, it will always be your turn — you will either be thrown into another game that was started earlier by a different user, or you will get to start a fresh game, depending on what games are currently live and waiting for players. If you start a fresh game, you get to make your first move, and then and only then does Game Center go out and try to find someone for you to play with.

Going back to the code level, what we are looking out for is a callback on the turnBasedMatchmakerDelegate that we assigned above. Our root view controller class implements some 4 methods from the IGKTurnBasedMatchmakerViewControllerDelegate interface, and the match-maker view controller will call us back on these when interesting things happen. This is a very common pattern in Cocoa, and is how the platform handles “events”.

In particular, the callback we care about for starting a new game is:

method turnBasedMatchmakerViewController(aViewController: GKTurnBasedMatchmakerViewController) 
       didFindMatch(aMatch: GKTurnBasedMatch);

This method actually gets called any time the user picks a game in the Game Center UI — whether they are starting a new game, or selecting an existing game. This is neat, because it means we can leave all the game management to GameKit, and don’t need to keep track of ongoing games ourselves.

This callback receives the third important class, a GKTurnBasedMatch. “All” we need to do when this method is called is load the passed match as the active game. In the Tic-Tac-Toe app, we do this by calling our own loadCurrentMatch() method, which does all the heavy lifting.

Every match contains a binary blob in the matchData property that describes the current game state. The content of this binary blob is entirely up to our game, but we can use it to store up to 4KB of data, and this data will automatically be passed between all the participants as they take their turns. In turn, our Game will read the data, let the active player make their move, and then store the updated game state, as we will see in a few moments.

Loading a Game

Loading a game is done with two method calls, first, a a small helper function loads the binary data into an NSDictionary, and then we ask the board to load its game state from that dictionary (the idea being that in a future version, our game might store additional info in the dictionary that the board does not care about — that’s why i decoupled the data the Board class reads/writes from the actual binary format).

var lDictionary := dictionaryFromData(fCurrentMatch.matchData);
fBoard.loadFromNSDictionary(lDictionary,);

The same happens in reverse when we later save the updated game state. Of course, when a new game is first started, there will be no data present (GameKit doesn’t know anything about how our game data is structured, after all), so our code will need to be prepared to find an empty NSData object in matchData.

The second part, in addition to loading the board data, is determining the active player (if any) and enabling the game play. Each GKTurnBasedMatch has a currentParticipant property of type GKTurnBasedParticipant which links to the current player (it also has a participants array that contains all (two) players). We can compare this participant’s playerID to that of the local player.

If they match, it’s the local player’s turn, so we display “your turn”, and enable the board. If they don’t match, it’s either the remote players’ turn (if currentParticipant is assigned), or the game has finished. In either case, we show the proper status message, and leave the board disabled.

Playing a Turn

If it was the local player’s turn and we enabled the board, we’re done for now, and await the player to make a move. This all happens inside the Board class, which will call us back via the board() playerDidSelectGridIndex() method, once the player made their move.

What we need to do then is determine the game status (the player might have made a winning move, for example), re-encode the game status, and pass control back to game center.

There are three scenarios we need to cover:

  1. The player has won by managing to place 3 “X” markers in a row.
  2. The board has been filled (and the game just tied).
  3. Any other case — the game is still on.

In the two game-over scenarios, we set the appropriate status (Won/Lost/Tied) on each of the game participants, and then call endMatchInTurnWithMatchData() completionHandler() to tell Game Center that the match has ended as part of the player taking his turn.

In the other case, we call endTurnWithNextParticipant() matchData() completionHandler() to let Game Center know that the local player is done, and game play can move to the next player.

In either case, we will pass the updated information on what our game board looks like now, via the matchData parameter.

It is worth noting that it’s up to our game to tell Game Center “who’s next”. In our case, that decision is trivial (it’s the player that’s not the local player), but Game Center allows for turns to be passed in arbitrary ways, depending on your game’s needs — it doesn’t always have to go around the (virtual) table.

Once we called either of the above fund methods, it’s no longer our turn, and all we can do is wait for the remote player to make a move.

Handing Remote Turns

Of course, eventually the remote player will make their move, and GameKit provides a second delegate interface for this case, IGKTurnBasedEventHandlerDelegate, which we have hooked up to the GKTurnBasedEventHandler.sharedTurnBasedEventHandler singleton.

What will happen once the remote player moved is that Game Center will show a notification banner to the user, saying something along the lines of “It’s Your Turn”, and play that annoying fanfare sound. When and only when the user taps that notification, will we receive a call to the

method handleTurnEventForMatch(aMatch: GKTurnBasedMatch) 
       didBecomeActive(didBecomeActive: Boolean);

method to let our application know that something happened. This is nice, because it saves us from worrying about showing any UI to the user to ask if they, say, want to switch from the game they are currently looking at to the one that has received a response. When we get the callback, we already know that the user has decided to view the game in question.

So all we really need to do is the same as we did in … didFindMatch() above: load the passed match and enable the board, if appropriate.

House Keeping

This basically takes care of all the Game Center interaction needed for our simple game. There’s a few more minor items that i won’t go into detail for this post; for example, there’s a callback you will want to handle to quit games if the user selects to delete them in the Game Center UI. There also are APIs for providing a custom UI for the list of games, instead of relying on the standard UI provided by Game Center (all Tic-Tac-Toe does with this is adjust the Game Center button to either read “Games” or “Start”, depending on whether there are any games ongoing or not).

In Tic-Tac-Toe, all the GameKit interaction is located in RootViewController.pas, which is only ~500 lines of code total, so it should give you a good overview and starting point for your own game.

Caveats

There are a couple of caveats you might want to be aware of when working with Game Center games; these had me stumped for a short while, so hopefully pointing them out here will help save you frustration:

  • In order for Game Center to work, your app needs to be registered on iTunes Connect with the appropriate app ID. Even for the simulator. This is counter-intuitive if you are used to App Store development, because normally you don’t have to go to iTunes Connect to register your app until you’re (almost) ready to go and submit it. For Game Center, you need to register it first. If you don’t, GKTurnBasedMatchmakerViewController will just come up looking all empty and dysfunctional.

  • As best as i can tell, you must specify “gamekit” in the UIRequiredDeviceCapabilities section of your Info.plist file for things to work properly.

  • Finally, and most importantly, notifications do not work in the Simulator. If you worked with Push Notifications before, this might not come as much of a surprise, but realize that callbacks such as handleTurnEventForMatch() … will never, ever be called on the Simulator — which seriously will impact how you test game interaction. (In essence, i found the only way to work around this was to manually invoke the GKTurnBasedMatchmakerViewController and re-select the active game to trigger the app to reload the game data and detect the changed game state.)

The Game Board

The second piece of interest in the game is the Board class, which implements all the UI of the game as a really simply UIControl descendant.

The implementation is fairly straight-forward; the class maintains a 3×3 array to keep track of which player has claimed a particular spot on the game board, and in which move (by keeping track of the move number, the game can show different versions of their “X” and “O” markers, making the board look more dynamic).

The begin/continue/endTrackingWithTouch() withEvent() methods are overridden to keep track of — you guessed it — the user’s finger, and they use animations to fade the user’s game piece in and out, and to move it to the next appropriate spot, as the finger moves. (You’ll notice that while the user can move their finger all over the board, the game piece will always “snap” to a fixed position in the closest matching grid spot with a smooth animation.)

All the game pieces (the grid, the “X” and “O” game pieces, etc.) are pre-created images, and the game pieces come in 5 versions (since 5 is the maximum number of moves any one player can make).

The entire Board class is really straightforward and there isn’t much worth covering here that won’t be clear from looking at the code. Worth pointing out is the makeComputerMove method, which invokes Jeremy’s mean ComputerPlayer class, which is used when not playing in Game Center mode.

Tic Tac Toe Board

Summary

I think that’s it (and it’s probably enough ;). Make sure to check out the code on GitHub, and let me know what you think!

Oh, and: even though the code is written in Oxygene, as are the code snippets here, i hope the general description of how to work with GameKit will also be useful to “regular” Cocoa developers using Objective-C. Another nice thing about Oxygene working directly against the standard Cocoa APIs without any abstractions!

Avatar of marc

by marc

“Nougat”, Beta 3

February 11, 2013 in iOS, iPad, iPhone, Nougat, Oxygene, Visual Studio, WWDC

Beta 3

On Friday, we shipped BETA 3 of Oxygene “Nougat”, a major milestone in our progress to bring the Oxygene language you know and love to the “Cocoa” platform for truly native Mac and iOS development.

The big benefit of Oxygene “Nougat” over other non-Apple tool chains for Mac and iOS is that it fully embraces the platform and is a native compiler for the Objective-C runtime (which is at the core of Mac OS X and iOS development) that works directly with the native Cocoa API and controls.

This means you get all the benefits of the platform and you can work with and access all the same APIs that Objective-C developers using Xcode can. You can, for example, attend WWDC or NSConference or read and watch any of the trillion online tutorials out there on iOS (and Mac) development — and all the things you see and learn apply directly to the code you write with “Nougat”.

My friend and college Jim has created a great new video to introduce “Nougat” and show you around Beta 3, which you can find here.

Beta 3 is a huge milestone for us, in that we believe it represents a state in the development of the product where we can call it “usable for production work”. That doesn’t mean we’re close to RTM yet — we have big and strict plans for how solid we want the product to be for “1.0″ this summer — but it does mean that it is solid enough that you can start doing serious work with it. I should know, as i already have my first app created with Nougat in the App Store: Browse500. (Full source is on GitHub, too).

What’s Next?

As mentioned above, we still have a long way to go before RTM. Beta 1 last October had the goal of, well, getting something out to you guys to play with, and we got a lot of great feedback. For Beta 2 we focused on getting all the basic tool chain support in place (debugging, deploying, etc.) and our main focus for Beta 3, which we just shipped, was stability, so we spent most of our time fixing bugs in the compiler, IDE and toolchain. For Beta 4 we are shifting focus back on features — there are still quite a few things missing in the language, and we have lots of improvements and enhancements planned for the tool chain and the Visual Studio IDE support.

“Nougat” is not standing still, and the next two months should be a whirl-wind of new stuff going into the product.

After Beta 4 (and possibly a Beta 5), we are still on track for an official “1.0″ release (actually, it will be 6.0) in late spring/early summer.

Get Nougat!

How do you get “Nougat”? If you bought or renewed Oxygene from us since last October, you already have Oxygene “Nougat” as part of your product portfolio. Simply head over to beta.remobjects.com and get your copy of Beta 3 (and don’t forget to participate in the beta forums as well to let us know what you think).

(If you have a Suite Subscription for Xcode, that of course includes access to Oxygene “Nougat”, as well).

If you have a license for Oxygene for Java or for Embarcadero Prism XE2 or XE3, you can renew to the full suite of Oxygene for all three platforms at $349 in our secure online shop.

If you own Delphi XE2 or later, you can take advantage of our cross-grade offer for $399 to get the full Oxygene package.

And finally, the full Oxygene package is available for new users at $499.

Avatar of marc

by marc

Introducing Browse500, the first Nougat app on the iOS App Store!

January 17, 2013 in iOS, iPad, iPhone, Nougat, Oxygene, Photography

I’m more than thrilled to announce the immediate availability of Browse500.app, the very first app written in Oxygene “Nougat” to be submitted to and approved for the iOS App Store.

Dog-fooding is important, and as you know, we do a lot of it here at RemObjects. In early December i started out creating a small “real life” iOS app with the goal of (a) putting Nougat thru its paces, test it and find bugs, (b) create a nice sample application that illustrates Nougat and core iOS and Cocoa concepts in a real application and (c) “test” App Store submission.

I did not expect any problems with app store approval — after all, Nougat creates 100% native Cocoa apps that are virtually indistinguishable from those created with Xcode and Objective-C, but i wanted too “prove” that a Nougat app gets accepted (even with “Oxygene” in the compiler meta data, for example).

I was not disappointed. I submitted Browse500 on December 28th, the day the iTunes Connect (the place where you submit apps) was back from its holiday shutdown, and version 1.0 was approved just a few days later. I did not put 1.0 live in the store for two reasons: for one i’ve been working on the app and kept improving it while 1.0 was waiting for review, and for another, there were a few bugs and leaks that would kill the app after some period of browsing because it ran out of memory. Those were easily fixed thanks to Instruments. 1.0.2 was submitted last week and approved last night.

So what is Browse500?

 

From the user’s perspective, it’s a nice little app to browse the 500px photo community. It lets you browse popular and, as they call it, “fresh” photos, view individual photos full screen, and drill into individual user’s portfolios to see all their photos.

It’s simple, it’s quick, and it turns out i actually use it all the time myself to browse photos.

I’ll be expanding it over time; i have a “bookmark” feature in the works that’s not active in 1.0.2 yet (but that i’ve been using frequently), i have more navigation ideas, and i want to add support for logging authenticating (right now the app browses anonymously only), voting, favoriting, etc.

 

From the curious developer’s perspective, Browse500 is an open source app, [available on GitHub], written in 100% Oxygene Nougat that illustrates a wide range of iOS technologies and concepts, including:

  • The awesome and fluid kind of standard UI you get by using the proper iOS frameworks rather that weird hack frameworks that aren’t native
  • Use of UITableViews and iOS 6′s awesome new UICollectionView, which i use to implement the main “album” view of the app
  • Using custom UICollectionViewCells and custom UIViews
  • Working with both XIB-based and all-in-code views
  • Using Grand Central Dispatch to write asynchronous code that loads images and data from the net on demand and in the background — you’ll see that the album view is an endless stream of photos that just keeps filling with more as you scroll until 500px runs out of photos to show you
  • Using iCloud to sync settings (1.0.2 as one setting) and data (the 1.1 will have the bookmark feature alluded to above)
  • Using third party Objectve-C APIs (the app uses the open source PXAPI library to talk to 500px.com)
    … and much more

As mentioned, the app is on the App Store now, so if you are mainly interested in playing around, go get it (it’s free). And of course it’s a Universal app for iPhone and iPad.

The app is also fully open source. You can get the full source code from the GitHub repository (you will need the latest Nougat beta), and i would appreciate feedback, comments and &mdashl of course — pull requests.

Also, don’t forget to rate the app on the App Store!

 




(Disclaimer: The photos in this view are not by me.)

Oxygene on the Big Screen

January 4, 2013 in Android, iOS, iPad, iPhone, Java, Mac, Metro, Oxygene, Windows, WP7

Android powered Ouya ConsoleYou already know Oxygene is the best choice for mobile development – Oxygene for Java on Android, Oxygene for .NET for Windows Phone and the Windows RT Surface and the beta “Nougat” already providing great support for iOS development. But what if you want to develop on the big screen? Like that 50 plus inch TV in your front room?

Enter the Ouya, the Android powered game console for your TV. They just released their ODK (Ouya Development Kit), and since it is Android powered, it is perfectly supported by Oxygene for Java right out of the box. Oxygene for Java is a completely native Android development tool – there are no forced abstraction layers or additional run-times to get in your way or require updating when new variations or versions of the platform come out.

Red Ant Games has just announced they are using Oxygene for Java to move their Subject 33 to Ouya and Android mobile devices. Subject 33 is currently an Alpha prototype on Windows. They also have plans to support iOS and Mac with “Nougat”.

Avatar of marc

by marc

Comparing Data Abstract and Core Data (with and without iCloud)

August 6, 2012 in iOS, iPad, iPhone, Relativity, ROFX, Xcode

People often ask us what the difference between Core Data and Data Abstract is, and why they should choose one over the other.

The answer to that second question frequently is “it depends”, because while on first glance Core Data and Data Abstract might seem like technologies that solve the same problem (“database access”), they are actually quite different technologies, and designed with very different goals.

Core Data is, in essence, a local data access technology and ORM. It is designed around the concept that your application has a certain data model, expressed as a suite of objects, and wants to persist that data between runs (or to save runtime memory footprint), locally. As such, Core Data is largely a front-end for SQLite (although it supports other storage options) that abstracts the developer from dealing with the actual database storage, table structure, and SQL queries, and exposes data as regular in-memory objects that magically persist.

Don’t get us wrong, that is an awesome feature for many scenarios, and Core Data is a great solution in that space.

Data Abstract on the other hand is designed around the concept of data stored on a “big” server somewhere on the network, off your iDevice or Mac, or “in the cloud”, and that the database on this server (or these servers) contains data that is shared between many users of the application (which of course does not mean that each user cannot have their own private set of data).

These servers could be anything, from an existing database in a large Oracle cluster that already provides the backbone for an enterprise, to a fresh database you just designed solely for your iOS app. The target application could be anything from — say — a view into said enterprise’s customer relations system, over an iOS front-end to your web site, to the client app for the next Social Network you are building.

To achieve this, Data Abstract is build around the so-called Multi-Tier concept, where the client applications you build using Data Abstract talk to a “middle tier” server that enforces data consistency and access restrictions.

 

The central difference between Core Data and Data Abstract is that the former is focused around simplifying how an app can persist its application state locally, while the latter is designed around accessing, presenting and working with data that exists elsewhere, usually (but not necessarily) in a context that is much larger than an individual application and an individual user.

So Core Data and Data Abstract aren’t really that much in competition, but target pretty different scenarios. In most cases where Core Data is a good solution, Data Abstract would be overkill or even useless, and in the cases where you need what Data Abstract provides, Core Data won’t do much good.

But What About Core Data in iCloud?

On first glimpse, it might seem as if the new Core Data Syncing via iCloud expands Core Data onto Data Abstract’s turf. And to a certain degree that is true; iCloud certainly expands what you can do with Core Data, but there are still some pretty severe limitations.

When Steve Jobs took the stage at WWDC in 2011 to announce iCloud, he gave the reason why iCloud was going to be better than previous syncing technologies: Rather than syncing information between devices he said that with iCloud the “truth, is in the cloud”, and each device — iOS or Mac — syncs against a well-known set of data in that single location.

That is the right way to do it, and that is how iCloud works in general, but: it is not how Core Data syncing via iCloud works, unfortunately.

At the time of this writing, Core Data syncing via iCloud consists of each client storing change logs in the cloud and these diffs getting transferred between devices. In other words, iCloud syncs Core Data between devices, without a single truth “in the cloud”. As Drew McCormack has outlined in his excellent series of articles on the topic, this comes with a myriad of problems, caveats and corner cases to look out for.

This is, of course, in sharp contrast to how data access with a multi-tier solution such as Data Abstract is handled. Data Abstract’s model (or any other database-centric multi-tier architecture) does put the truth in the cloud, just as Steve Jobs suggested it should be.

Another crucial limitation of Core Data is that any data it stores in iCloud is still an island: it can only be accessed by the applications that created it, and is isolated per user. That is fine for many scenarios (e.g. in cases where you’re 100% focused on in-app presentation), but imposes some serious limitations, such as:

  • not being able to provide a web-based front-end to user’s data outside of their iOS apps.
  • not being able to provide access to the user’s data on iOS/Mac on the one side, and client applications on other platforms (be it Android, Windows, or even just non-App Store Mac) on the other.
  • not being able to offer options for users to share data among themselves, or share data publicly.

Also, the fact that data is self-contained within each user’s store means that as the application developer, you have no access to it, for example to investigate if users are reporting problems. On the one hand, this is a win for user privacy, as users only need to trust Apple with their data, not you as the App Vendor, but on the other, it severely limits what apps can do and how you as an app developer can trouble-shoot or assist users when they have problems with their data.

So, Which Option Should You Choose?

Indicators that Core Data is the right solution would be:

  • You have a fairly straightforward data model and not more data than can easily it on the device.
  • Your data is internal to the application, and you never want to use it outside of the app (aside from, say, letting the user explicitly export it).
  • Your entire data is tied to a single user and you have no need to share (part of) the data between different users of your app.

Indicators that you might want to use Data Abstract include:

  • You are building a front end to already existing data managed in a database (e.g. an app for your Enterprise database).
  • Your app’s data is part of a larger data infrastructure shared (in parts) by all or some users of your app.
  • Your app’s data should be accessible (in parts) outside of the application itself, for example via a web site.
  • Your app’s data should be available on platforms not covered by iCloud, for example Windows or Android clients.
  • Users of your app should be able to share (parts of) their data, whether explicitly or implicitly.
  • Your application contains shared data common to many or all users.
  • You want or need full control and access to the data and how it is stored.

The one advantage of Core Data, of course, is that it works “out of the box” with just the user’s devices, and (optionally) iCloud, and you do not need to worry about setting and maintaining any database and server yourself.

If your application can live within these constraints, that can be a significant saving in both cost and management effort. But in most cases, you either need the extra flexibility of hosting the data yourself, or you don’t — and if you do, Core Data and iCloud give you absolutely no wiggle room and hosting your own data will be the only solution.

Data Abstract makes that easy and cost efficient too, and with our free Relativity Server that can be hosted on just about any co-located server and on many cloud services, such as Amazon EC2, you can be up and running with your own hosted middle-tier quickly.

Interested?

If this got you interested, please check out some of our other resources on Data Abstract for Xcode (and for our other platforms) to see what DA can do for you and how to get started.

In particular, i recommend reading the first available chapters of our “Data Abstract for Xcode Book” and to check out the videos on RemObjects TV, especially Introduction to Data Abstract for Xcode narrated by yours truly and Relativity Server Overview.

And of course we have a free 30-day trial versions of Data Abstract for all platforms.

We hope you will find Data Abstract useful — and don’t hesitate to contact me directly or our support team if you have any questions.

— marc

Tools and Abstractions on iOS

April 16, 2012 in iOS, iPad, iPhone

I just came upon this Hanselminutes episode from back in February with John Sonmez about MonoTouch and Mono for Android, and it even mentions PhoneGap. If you are interested in working on iOS with any tool, I recommend you listen to the podcast, as he discusses the pros and cons of the different levels of abstraction and how to get the most out of the level you choose.

What do you think about his points on the different levels of abstraction? Remember, whatever level you choose we’ve got you covered with Data Abstract and RemObjects SDK for Xcode, .NET or JavaScript.

Developing Database Applications for Mac and iOS (UPDATE: 6 chapters free)

February 24, 2012 in Books, iOS, iPad, iPhone, Mac, ROFX, Xcode

Developing Database Applications for Mac and iOS is a book about Data Abstract for Xcode that we started work on in 2011. Writing has been put on hold for a bit, but we are reviving the project and are planning to complete the book some time in 2012.

In the mean time, select portions of some chapters that we think are ready for consumption are being integrated here on the wiki for your benefit, while the remainder of the book is still being worked on.

The following topics are currently available:

Chapter 1

Chapter 2

Chapter 3

Chapter 4

Chapter 10

Chapter 12

Updating Data in iOS Client Applications

October 26, 2011 in Cocoa, iOS, iPhone, Mac, Relativity, ROFX, Uncategorized, Xcode

As promised earlier, I’m going to tell you how to change data in an iOS application and apply changes to the DA Server. As client application we will use the iOS Relativity Client we have previously created.

In the course of this session we will try to implement an editor for changing data from the Orders table. We will also use UIPickerView & UIDatePicker for changing the Order’s fields.
The source code can be obtained from here.
Let’s start!

The first thing we need to do is to add a new editor view and its controller.
After performing File->New…->New File, select the “UIViewController subclass” item. As the base class we will use the UITableViewController.
Now we need to add an edit button to the existing OrdersViewController. Clicking on this button should open an editor for the selected row.

- (void)viewDidLoad {
    [super viewDidLoad];
 
    // create & set Edit button    
    UIBarButtonItem *actionButton = 
     [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemEdit 
                                                   target:self 
                                                   action:@selector(edit:)];
    [[self navigationItem] setRightBarButtonItem:actionButton];
    [actionButton release];
    ...
}
 
- (IBAction)edit:(id)aSender {
 
    OrderEditorController *editor = 
      [[[OrderEditorController alloc] initWithStyle:UITableViewStyleGrouped] autorelease];
 
    // set OrderViewController as the delegate to be able to notify it about the results of the editing process
    [editor setDelegate:self];
 
    // find the currently selected row
    NSIndexPath *path = [self.tableView indexPathForSelectedRow];
    NSInteger idx = [path indexAtPosition:1];
    DADataTableRow *row = [orderRows objectAtIndex:idx];
 
    // and set it for our editor 
    [editor setOrderRow:row];
 
    // push the editor view to be a topmost view
    [[self navigationController] pushViewController:editor animated:YES];
}
 
// delegate the method that the editor will call for applying changes
-(void)endEditWithOk:(DADataTableRow *)row {
    // applying changes on Relativity server
    [[DataAccess sharedInstance] applyUpdatesForTable:[row table]];
    // reload view for synchronizing client-side data
    [[self tableView] reloadData];
}
 
// delegate the method that the editor will call for canceling changes
-(void)endEditWithCancel:(DADataTableRow *)row {
    // cancel the change
    [row cancel];
    // reload table
    [[self tableView] reloadData];
}

Now it is time to start implementing our OrderEditorController. The Orders table has several fields we can edit: OrderDate and OrderStatus, but we can also change the client of the order, though I don’t think we need to change the document in such a radical way ;) .

Note: There is one small problem we need to resolve first. Unfortunately, the current version of the PCTrade database doesn’t have an OrderStatus reference, so the Orders table only has its integer values (which by default are 0).
Let’s assume that we need to implement several available document statuses. A new uncompleted order usually has the “New” status. When it is completed but not payed yet, it has a “Pending” status. When we received the money, it changes to “Closed” and finally, there is the additional “Deleted” status. In the future we probably may want to track the document history, so we loose any of the orders, even if they were deleted. So our database will keep those orders, but with the “Deleted” status. In order to keep the correspondence between the available status numbers and their names, we will add a new NSArray into our DataAccess class as shown below.

/////////////////////////////
//  DataAccess.h
@interface DataAccess : NSObject <DARemoteDataAdapterDelegate, DAAsyncRequestDelegate>
{
    ...
    NSArray *orderStatusNames;
}
...
@property (readonly) NSArray *orderStatusNames;
-(NSUInteger)idForOrderStatusName:(NSString *)name;
-(NSString *)nameForOrderStatus:(NSUInteger)status;
@end
 
/////////////////////////////
//  DataAccess.m
 
- (id) init {
	self = [super init];
	if (self) {
		...
 
        orderStatusNames = [[NSArray alloc] initWithObjects:
                         @"New", 
                         @"Pending", 
                         @"Closed", 
                         @"Deleted", 
                         nil];
 
	}
	return self;
}
 
- (void) dealloc {
   ...  
   [orderStatusNames release], orderStatusNames = nil;
   [super dealloc];
}
 
 
-(NSString *)nameForOrderStatus:(NSUInteger)status {
 
    if (status >= [orderStatusNames count])
        return @"Unknown state";
 
    return [orderStatusNames objectAtIndex:status];
}
 
-(NSUInteger)idForOrderStatusName:(NSString *)name { 
 
    for (NSUInteger i = 0; i < [orderStatusNames count]; i++) {
        NSString *statusName = [orderStatusNames objectAtIndex:i];
        if ([statusName compare:name] == NSOrderedSame) 
            return i;
    }
    return 0;
}
 
-(NSArray *)orderStatusNames {
 
    return orderStatusNames;
}

Now we are ready to start the editor implementation. Please open the OrderEditorController.h file and add the following:

@interface OrderEditorController : UITableViewController {
 
    DADataTableRow *orderRow;
 
    UIBarButtonItem *saveButton;
    UIBarButtonItem *cancelButton;
 
    id delegate;
    NSDateFormatter *df; 
}
 
@property (retain) DADataTableRow *orderRow;
@property (assign) id delegate;
 
- (IBAction)save:(id)aSender;
- (IBAction)cancel:(id)aSender;
@end

Our editor should have references to the edited row, the delegate reference, the save & cancel buttons and the appropriate methods. This will implement them:

#pragma mark - View lifecycle
 
- (void)viewDidLoad {
    [super viewDidLoad];
 
    // create & add cancel button    
    cancelButton = [[UIBarButtonItem alloc] initWithTitle:@"Cancel" 
                                                    style:UIBarButtonItemStylePlain 
                                                   target:self action:@selector(cancel:)];
    self.navigationItem.leftBarButtonItem = cancelButton;
 
    // create & add save button    
    saveButton = [[UIBarButtonItem alloc] initWithTitle:@"Save" 
                                                  style:UIBarButtonItemStyleDone 
                                                 target:self action:@selector(save:)];
    self.navigationItem.rightBarButtonItem = saveButton;
 
    // we will also need this formatter for converting NSDate to NSString 
    df = [[NSDateFormatter alloc] init];
    [df setDateStyle:NSDateFormatterMediumStyle];
}
 
- (IBAction)save:(id)aSender {
 
    if ([delegate respondsToSelector:@selector(endEditWithOk:)])
        [delegate endEditWithOk:orderRow];
 
    [self.navigationController popViewControllerAnimated:YES];
 
}
 
- (IBAction)cancel:(id)aSender {
 
    if ([delegate respondsToSelector:@selector(endEditWithCancel:)])
        [delegate endEditWithCancel:orderRow];
 
    [self.navigationController popViewControllerAnimated:YES];
}

In the majority of cases, we can represent the editor view as the table view, where each cell represents the field name at the left side and the current value at the right side. We can also group certain fields together based on general meaning or application logic. To do that we have to initialize our OrderEditorController with the UITableViewStyleGrouped style. When the user taps on a certain cell, we will need to allow him to edit that value.

So now we need to configure our table view as shown below:

 
#pragma mark - Table view data source
 
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    // we want to have two groups, 
    // one for OrderDate & OrderStatus, and one for the Client field 
    return 2;
}
 
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    switch (section) {
        case 0:
            // the first section will have two cells (showing OrderDate & OrderStatus)
            return 2;
        case 1:
            // the second section will have a single cell (showing the Client field)
            return 1;
 
        default:
            return 0;
    }
}
 
static NSString *TABLE_CELL_ID = @"OrderEditorCell";
 
- (UITableViewCell *)tableView:(UITableView *)tableView 
         cellForRowAtIndexPath:(NSIndexPath *)indexPath {
 
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:TABLE_CELL_ID];
    if (cell == nil) {
      cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 
                                     reuseIdentifier:TABLE_CELL_ID] autorelease];
    }
 
    // Configure the cell...
    int section = [indexPath section];
    int index = [indexPath indexAtPosition:1];
    switch (section) {
      case 0: { // for the first section ...
        switch (index) {
          case 0: { // configure the cell to show the OrderDate field
                    [[cell textLabel] setText:@"Order Date:"];
                    NSDate *value = [orderRow valueForKey:@"OrderDate"];
                    NSString *valueAsString = value ? [df stringFromDate:value] : @"Click to select";
                    [[cell detailTextLabel] setText:valueAsString];
                    break;
          }
          case 1: { // configure the cell to show the OrderStatus field
                    [[cell textLabel] setText:@"Order Status:"];
                    NSNumber *value = [orderRow valueForKey:@"OrderStatus"];
                    NSString *valueAsString = value ? 
                         [[DataAccess sharedInstance] nameForOrderStatus:[value intValue]] : 
                         @"Click to select";
                    [[cell detailTextLabel] setText:valueAsString];
                    break;
          }
        }
        break;
      }
      case 1: { // for the second section
        switch (index) {
          case 0: { // configure the cell to show the Client field
                    [[cell textLabel] setText:@"Client:"];
                    NSString *value = [orderRow valueForKey:@"CustomerName"];
                    NSString *valueAsString = value ? value : @"Click to select";
                    [[cell detailTextLabel] setText:valueAsString];
                    break;
          }
        }
        break;
     }
     default:
       break;
    }
    [cell setNeedsDisplay];
	return cell;
}

Now, when you compile and run the project, you should see something like the following:

Next we can start to implement the editing for the OrderDate and OrderStatus fields. For editing dates we can use the really good NSDatePicker control with the appropriate configuration. For editing the OrderStatus we can use our custom picker populated with available statuses.
I also want the picker view to appear at the bottom of the current editor using animation, like the standard Contacts application does. Having spent some time with Google, I’ve found an interesting Data Cell sample with the required functionality. I’ve just slightly adjusted it for our purposes, namely to support animation for custom pickers.

- (void)viewDidLoad {
 
  ...
  // create & configure our DatePicker
  datePickerView = [[UIDatePicker alloc] init];
  [datePickerView setDatePickerMode:UIDatePickerModeDate];
  [datePickerView addTarget:self 
                     action:@selector(pickerDidChange) 
           forControlEvents:UIControlEventValueChanged];
 
  pickerDoneButton = [[UIBarButtonItem alloc] initWithTitle:@"Done" 
                                                      style:UIBarButtonItemStyleDone 
                                                     target:self action:@selector(pickerDone)];
  pickerCancelButton = [[UIBarButtonItem alloc] initWithTitle:@"Cancel" 
                                                        style:UIBarButtonItemStyleDone 
                                                       target:self action:@selector(pickerCancel)];
}
 
#pragma mark - Common picker routines
-(void)showPicker:(UIPickerView *)picker {
 
    if (!picker)
        return;
 
    if (activePicker) 
        [self hidePicker:activePicker];
 
    activePicker = picker;
 
    if (picker.superview == nil) {
        [self.view.window addSubview:picker];
 
        // size up the picker view to our screen and compute the start/end 
        // frame the origin for our slide-up animation
        // compute the start frame
        CGRect screenRect = [[UIScreen mainScreen] applicationFrame];
        CGSize pickerSize = [picker sizeThatFits:CGSizeZero];
        CGRect startRect = CGRectMake(0.0,
                                      screenRect.origin.y + screenRect.size.height,
                                      pickerSize.width, pickerSize.height);
        picker.frame = startRect;
 
        // compute the end frame
        CGRect pickerRect = CGRectMake(0.0,
                                       screenRect.origin.y + screenRect.size.height - pickerSize.height,
                                       pickerSize.width,
                                       pickerSize.height);
        // start the slide-up animation
        [UIView beginAnimations:nil context:NULL];
        [UIView setAnimationDuration:0.3];
 
        // we need to perform some post operations after the animation is complete
        [UIView setAnimationDelegate:self];
 
        picker.frame = pickerRect;
 
        // shrink the table's vertical size to make room for the date picker
        CGRect newFrame = self.tableView.frame;
        newFrame.size.height -= picker.frame.size.height;
        self.tableView.frame = newFrame;
        [UIView commitAnimations];
 
        // add the "Done" & "Cancel" buttons to the nav bar
        self.navigationItem.rightBarButtonItem = pickerDoneButton;
        self.navigationItem.leftBarButtonItem = pickerCancelButton;
    }
}
 
-(void)removePicker:(UIPickerView *)picker {
 
    if (!picker)
        return;
 
    if (picker.superview != nil) {
        [picker removeFromSuperview];    
    }
}
 
-(void)hidePicker:(UIPickerView *)picker {
 
    if (!picker)
        return;
 
    activePicker = nil;
 
    CGRect screenRect = [[UIScreen mainScreen] applicationFrame];
    CGRect endFrame = picker.frame;
    endFrame.origin.y = screenRect.origin.y + screenRect.size.height;
 
    // start the slide-down animation
    [UIView beginAnimations:nil context:picker];
    [UIView setAnimationDuration:0.3];
 
    // we need to perform some post operations after the animation is complete
    [UIView setAnimationDelegate:self];
    [UIView setAnimationDidStopSelector:@selector(animationDidStop:finished:context:)];
 
    picker.frame = endFrame;
    [UIView commitAnimations];
 
    // grow the table back again in vertical size to make room for the date picker
    CGRect newFrame = self.tableView.frame;
    newFrame.size.height += picker.frame.size.height;
    self.tableView.frame = newFrame;
 
    // remove the "Done" button and restore the nav bar
    self.navigationItem.rightBarButtonItem = saveButton;
    self.navigationItem.leftBarButtonItem = cancelButton;
 
    // deselect the current table row
    //NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
    //[self.tableView deselectRowAtIndexPath:indexPath animated:YES];
 
}
 
-(void)animationDidStop:(NSString *)animationID 
               finished:(NSNumber *)finished 
                context:(void *)context {
    [self removePicker:context];
}
 
// called when we click the Done button
-(void)pickerDone {
 
    // change the data
    if (activePicker == (UIPickerView *)datePickerView)
        [orderRow setValue:datePickerView.date forKey:@"OrderDate"];
 
    // hide picker
    [self hidePicker:activePicker];
    // reload table view to show changes
    [[self tableView] reloadData];
}
 
// called when we click on the Done button
-(void)pickerCancel {
    // just hide the picker
    [self hidePicker: activePicker];
    [[self tableView] reloadData];
}
 
// the following method will sync the current picker value with the value in the table view
- (void)pickerDidChange {
 
    NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
    UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];
 
    if (activePicker == (UIPickerView *)datePickerView)
        cell.detailTextLabel.text = [df stringFromDate:datePickerView.date];
}

Now we need to launch the UIDatePicker when a user taps on the OrderDate field:

- (void)tableView:(UITableView *)aTableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
 
    [self hidePicker:activePicker];
    int section = [indexPath section];
    int index = [indexPath indexAtPosition:1];
 
    switch (section) {
      case 0: {
        switch (index) {
          case 0: {
            datePickerView.date = [orderRow valueForKey:@"OrderDate"];
            [self showPicker:(UIPickerView *)datePickerView];
            break;
          }
          case 1: {
 
            break;
	  }
        }
      }
    }
}

When you compile and run the application, you should get something like the following:

Finally, we need to implement our custom UIPickerView, which is quite simple. At the beginning we need to create a custom UIPickerView and set its data source and delegate properties:

- (void)viewDidLoad {
    ...    
    statePickerView = [[UIPickerView alloc] init];
    [statePickerView setDataSource:self];
    [statePickerView setDelegate:self];
    [statePickerView setShowsSelectionIndicator:YES];
}

Then we need to add a support UIPickerViewDataSource protocol to our editor class and implement several methods, as shown below:

 
#pragma mark - Custom Picker routines
 
// callback method for filling content for our custom statuses picker
- (NSString *)pickerView:(UIPickerView *)pickerView 
             titleForRow:(NSInteger)row 
            forComponent:(NSInteger)component {
 
    if (pickerView == statePickerView)
        return [[DataAccess sharedInstance] nameForOrderStatus:row];
    return @"";
}
 
- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView{
 
    // it should be a simple picker with a single component
    if (pickerView == statePickerView)
        return 1;
    return 0;
}
 
- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component {
 
    if (pickerView == statePickerView)
        return [[[DataAccess sharedInstance] orderStatusNames] count];
    return 0;
}

Change the tableView:didSelectRowAtIndexPath: method to show our state picker:

#pragma mark - Table view delegate
 
- (void)tableView:(UITableView *)aTableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
   ...    
    switch (section) {
      case 0: {
        switch (index) {
          case 0: {
            ...
          }
          case 1: {
            [self showPicker:statePickerView];
            break;
          }
        }
      }
    }
}

That’s all. Now when you tap on the status cell of the editor, you should get something like this:

You can choose a new status, then click the done button and finally save the current order.

I’m slightly worried that you may find this blog post a little confusing and not easy to reproduce. We wrote quite a lot of code here, and I deliberately skipped some minor details, trying to make it easier, but you can download the whole source code from here and play with the project yourself.

Thank you for your attention ;)

Changes in the Data Abstract Samples Suite

August 17, 2011 in .NET, Delphi, iOS, iPad, iPhone, Mac, ROFX, Windows, Xcode

Before the next upcoming release, I would like to highlight several changes concerning our Data Abstract samples suite.

Provided sample domain with the Relativity Server:

With the next release, we will provide Relativity Server with a new sample domain named PCTrade Sample that will contain a single schema called PCTrade. This schema will use the PCTrade for SQLite connection. The default login and password for this domain are Data and Relativity, respectively.

With this sample domain, Relativity Server can now be used as the server part for most of our DA samples in addition to the existing DASampleServer.

For the Xcode samples, you can easily choose the server you want to connect to:

For the .NET & Delphi samples you will need to set the flag value to True in the sample code as shown in the picture below:

Please note that several .NET samples, like Business Processor Sample and Refresh Update Data Sample, do not support working with Relativity since they depend on server logic implementation.

New Login service for the DASample server:

From the beginning, our samples had a virtual login implementation used only for the identification of clients and their platforms. For the next release, we have added a new secure DataService and LoginService to the DASample server. In order to maintain compatibility, we left the existing DASampleService unsecure. So if you try to build a client using that service, you will not need a login. But we added the new DataService on the top of the DASampleService, and made it secure. Thus, if you request that service, you need to login first.

Avatar of marc

by marc

My iPhone 4 Tracking Data

April 21, 2011 in iPhone, non-tech

Before any “bad actors” make off with my iPhone tracking data (is Hollywood really running out of ideas that badly?), I figured I’d have a look at some of the tracking data my iPhone collected over the past year:
Screen shot 2011 04 21 at 11 08 06 AM
In San Jose for Delphi Live 2010, including a couple trips up to San Francisco, and a lengthy photo shoot drive down south towards Hearst Castle with my colleague Sebastian, our friend Jeroen and his wife. Several erratic false locations placing us further east that we ever where.
Screen shot 2011 04 21 at 11 09 05 AM
Austin, Texas for 360iDev (which was great, and I’m looking forward to this year’s, in Colorado). The location info seems rather sparse and grid-like. I’m not too worried they’re gonna use that to place me on that crime scene…
Screen shot 2011 04 21 at 11 09 54 AM
Las Vegas for our first DSConf. One does not get around that much in Las Vegas; in fact i don’t think i managed to even get outside the MGM Grand or see sunlight for the 3 core days of conference. Once again, the location info looks sparse and grid-like, close-up.
Screen shot 2011 04 21 at 11 11 06 AM
My travels thru europe to see the Sisters live this spring. You can see the train ride from Berlin to Dortmund, the drive from London to Leeds, and stops in Tilburg, NL and Brussels. I’m surprised those drives look so sparse; it’s as if the iPhone tracks less when it doesn’t a have 3G data connection. Weird. You can also see a stopover in Frankfurt/Main, from the trip to San Jose.
Screen shot 2011 04 21 at 11 12 10 AM
Finally, a semi-closeup of Berlin. Once again, the tracking data is all over the place and fairly useless, I’d say. I’ve certainly never been to Hohenschönhausen ;).
So the morale: don’t rely on this iPhone feature for geotagging your photos…
Update: Looks like the grid-ification is a feature of the sample app, the actual location data collected is not on that grid. A simple one-line change in the app reveals the real data – which still is all over the place and, I dare say, just useless and unconcerning:
Screen shot 2011 04 21 at 11 49 03 AM
This above is Berlin, without the grid. And no, I don’t get around that much ;). If anything, I’d say the gridded data looks cooler.