i3Factory World

Your Iphone, iPad & Android Application Factory

Browsing Posts published by carlo

IOS_OSX_maps_OFFLINE

New MapKit features

In WWDC 2013 Apple introduced many new MapKit features for iOS 7 and added this framework in OSX 10.9 (Mavericks). One of the major changes, which in my opinion didn’t get enough relevance in the developer community, has been the introduction of some base classes that allow full map customization and support for offline maps. In this article I’m going to describe the new MKTileOverlay class and present an example, for both iOS and OSX, that demonstrates the new capabilities.

Since the earlier iPhone OS versions there were many apps in the App Store that were supporting maps different from the ones provided by the operating system: consider for example Navigation apps that required support for offline navigation, that is the possibility to see the map even without internet connection. Another requirements for some special kind of applications that needed to show proprietary information (such as “Yellow Pages” apps) or technical information (e.g. when there was the requirement to show level curves for mountains or to represent the sea level).

There were several issues due to this limitation: first of all the overall mapping experience was completely different of these different approaches each other and in most cases they were subpar if compared with the OS maps performance (either with Google or Apple data). Besides from the point of view of the developer there were the problem of providing the right mapping code to support the map provider data: there were no a unique solution, but many. Some were commercial and expensive, other were open source but with lack of support and finally there were a lot of web-browser based solutions whose performances were far from the native maps other than difficult to integrate with Objective-C.

What we’re going to show in this article is how these things changed drastically and how it is easy to integrate your own map content inside the common MapKit framework.

Map overlays

At the base of our discussion there is the concept of “map overlay”. This is not new in MapKit, but with iOS7 things changed. Overlays are essentially parts of a map that can be overlayed over the base map, that is the part of the map representing the ground, the borders, the roads, and so on. Typically the usage of overlayed is to emphasize some regions of the map having a common property: e.g. to highlight a specific country or to represent the several intensities of an earthquake that occurred in a certain area or finally to highlight a road path in a navigation app.

From the point of view of the developer, an overlay is any object that conforms to the MKOverlay protocol. This protocol defines the minimum properties and methods required to define an overlay: their are the approximate center coordinate and the bounding box the fully encloses the overlay (boundingMapRect). These two properties allow MapKit to determine if a specific overlay is currently visible or not in the map so that the framework can take the actions needed to display this overlay. When an overlay object is added to the map using one of the MKMapView’s addOverlay: methods the control passes to the framework which, when determines that a specific overlay needs to be displayed, calls the map view delegate asking him to provide the graphical representation of the overlay. Before iOS7 Apple was providing a set of concrete MKOverlay compatible classes and they were associated to their corresponding MKOverlayView. E.g. to represent a circular overlay we could use the built-in MKCircle class and then provide, for rendering, the associated MKCircleView class, without the need to define our own object.

With iOS7 things changed: now the MKOverlayView has been replaced by the MKOverlayRenderer. Even if this changesdoesn’t require difficult refactoring to translate the code from pre-iOS7 to iOS7, thanks to the fact that Apple did a 1:1 mapping of methods from the old class to the new class, conceptually the change is significant: now the graphical representation of the overlay is no more provided by a UIView subclass, which is typically considered a heavy class, but it is provided by a class, MKOverlayRenderer, which is much more lightweight and descends directly from NSObject. However the mapping between the old and new class is complete, so in the circle example we can see MKCircleView replaced by MKCircleRenderer.

Finally overlays are stacked on the map, so they are given a Z-index that provides the relative positions of overlays each other and with the fixed parts of the map. Before iOS7 you could stack this overlays and define their positions as in an array, with iOS7 two stacks are defined in fact, and they are called “levels”: one level is the “above the roads level”, the other level is the “above the labels level”. This is an important and useful distinction because now we can change how the overlay rendering interacts with the map by specifying if it lies above or below the labels.

Tile overlays

Whatever is the complexity and size of the overlay, we have seen them up to now overlays as specific shapes. With the new MapKit provided with iOS 7 and OSX Mavericks, there is a new type of overlay called tiled overlay. You may consider this type of overlay as a particular layer the covers the whole map: due to its large dimensions this overlay is tiled, that is it is partitioned in bitmap areas to reduce the memory required to show the data and make the overlay rendering efficient. The purpose of this concrete implementation of the MKOverlay protocol, called MKTileOverlay (together with its rendering counterpart given by the MKTileOverlayRenderer class), is to efficiently represent the whole set of tiles across the map plane and for different zoom levels. This last point is important: when you’re displaying a map using bitmap drawing (to be compared with vector drawing) you can get an efficient implementation only if the specific bitmap representing a part an area of the map has the right details suitable for the current zoom level. This means that if we show the full Europe map we don’t need to present road and cities should be represented as points and only for the major ones; as soon as we zoom in in a specific area then we cannot continue to represent the area by scaling the same tile, because it doesn’t contain the required information and also because we would see evident scaling effects. The solution to this is to divide the continuous allowed zoom range in discrete levels and for each level provide the required set of tiles that will show the details appropriate for that levels. It is evident that if we keep the single bitmap tile size constant (e.g. 256 x 256 pixels) then for each zoom level we must increse the number of tiles by a factor of 4: you can see this in the picture below: the single european tile at zoom level 3, when zoom to zoom level 4 has been split, and furtherly details, with four new different tiles having all the same size of the original tile.

 

map_tiles_offline-mapping

URL templates

The tiled overlay class works efficiently as it does a lazy loading of the tiles: this means that a bitmap tile is looked for and loaded only when it needs to be displayed. In order to know the location of the tile, the developer must define in the tile overlay definition the so-called URL template. This is a string representing a template for the final URL that will be used to retrieve the tile: this template will contain some placeholders that will be replaced by effective values to get the final URL. Each tile can be characterized by 4 parameters: x and y for the tile indexes in the map plane, z for the zoom level and finally scale for the bitmap image resolution (scale factor). The corresponding placeholders for these parameters are: {x} {y} {z} {scale}. So as an example, the OpenStreetMap template URL will be http://c.tile.openstreetmap.org/{z}/{x}/{y}.png and then the tile with index X=547 Y=380 and zoom level Z=10, that fully encloses the city of Rome, will be represented by the URL: http://c.tile.openstreetmap.org/10/547/380.png (see below the image taken from our OSX demo app).

iOS_offline_map_rome_tile

Note that a URL template can be an http:// template to retrieve tiles from the internet, but it could also be a file:// template if we want to retrieve files from the disk: in this way we can save our tiles in the application bundle, or download and install a full tiles package for a certain city, and then display maps even if the device is not connected to the internet.

The mechanism that is used by the framework to translate a required tile coordinate (x;y;z;scale) to an effective bitmap is composed of several steps: this gives the developer the possibility to hook its own code to effectively customize the way the tiles are generated. This can be done by subclassing MKOverlayTile. Note that this is not required if setting the URL template is enough for you.

When the map framework needs a specific map tile, it calls the loadTileAtPath:result: of the MKOverlayTile class (or subclass):

1
- (void)loadTileAtPath:(MKTileOverlayPath)path result:(void (^)(NSData *tileData, NSError *error))result;

The first method argument is called path and is a MKTileOverlayPath structure which contains the tile coordinates:

1
2
3
4
5
6
typedef struct {
	NSInteger x;
	NSInteger y;
	NSInteger z;
	CGFloat contentScaleFactor; // The screen scale that the tile will be shown on. Either 1.0 or 2.0.
} MKTileOverlayPath;

The second method argument is a completion block that needs to be called when the tile data has been retrieved: this completion block will be called by passing the data and an error object. The MKTileOverlay default implementation will call the -URLForTilePath method to retrieve the URL and then it will use NSURLConnection to load the tile data asynchronously.

If we want to customize the tile loading behaviour we can easily subclass MKTileOverlay and redefine the loadTileAtPath:result: with our implementation of the loading mechanism. E.g. we can implement our own tiles caching mechanism (other than the one provided by the system via NSURLConnection) to return the cached data before triggering the network call; or we could watermark the default tile if we are shipping a freemium version of our offline map.

A more light way to hook into the tile loading mechanism is to redefine in our subclass the -URLForTilePath: method:

1
- (NSURL *)URLForTilePath:(MKTileOverlayPath)path;

The purpose of this method is to return the URL given the tile path. The default implementation is just to fill-out the URL template, as specified above. You need to redefine this method if the URL template mechanism is not sufficient for your needs. A typical case is when you want to pass in the URL a sort of “device identifier” to validate the eligibility of that specific app to access the URL (e.g. if you provide a limit to the quantity of data that can be accessed by a user on a given time or if you want to charge for this data), another case if you have multiple tile servers and you want to do a sort of “in-app” load balancing or regional-based API access (e.g. you have servers in multiple locations and based on the effective device location you want to access the closer server).

The tile renderer

As all overlays are associated to a renderer, also the tile overlay has its concrete renderer class: MKTileOverlayRenderer. Normally you don’t need to subclass this renderer so your map delegate’s -mapView:rendererForOverlay: method can simply instantiate the default tile overlay renderer initialized with your default or subclassed tile overlay instance. Possible applcations of a custom overlay renderer are when you need to further manipulate the bitmap image, e.g. adding a watermark or applying a filter, and this manipulation is independent from the tile source. In the demo code I defined a custom renderer to be used specifically for the Google map, whose effect is to add a sort of colored translucent mosaic on top of the map tiles.

The demo code

You can get the demo code from Github. This code works on both iOS 7 and OSX 10.9 and its purpose is to present a map and give the user the possibility to switch between different tile set: Apple (system), Google, OpenStreetMap and offline from a subset of OpenStreetMap tiles bundled within the app. In all cases I applied an extra overlay layer to show the tile grid with the x,y,z path associated to each grid. (Note: in OSX if you don’t code sign the app using your OSX Developer Program certificate, you will not be able to see the Apple tiles: the other three tile sets will be visible instead). You will see how you can fully take advantage of all features common to the MapKit (zoom, rotation, pan, custom overlays and also annotations which I didn’t include in the demo) and the only difference is in the tiles source and how they are rendered.

 

map-offline-tiles-demo_code

As you can see in the demo apps, there is a main view controller (iOS) and window controller (OSX). In both cases the main view contains an instance of MKMapView and a segmented control to switch between different visualizations. On the map I have instantiated two overlays. The first one is the grid overlay:

1
2
3
4
 // load grid tile overlay
 self.gridOverlay = [[GridTileOverlay alloc] init];
 self.gridOverlay.canReplaceMapContent=NO;
 [self.mapView addOverlay:self.gridOverlay level:MKOverlayLevelAboveLabels];

This is a tile overlay of subclass GridTileOverlay. It will not replace the map content (this means that is effectively overlayed on the map content) and its purpose is to draw, just above labels, the tiles grid.

The reloadOverlay method is called each time the overlay type selector is changed or when the view is loaded. It removes any existing tileOverlay and replaces it with a new one:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
-(void)reloadTileOverlay {

	// remove existing map tile overlay
	if(self.tileOverlay) {
		[self.mapView removeOverlay:self.tileOverlay];
	}

	// define overlay
	if(self.overlayType==CustomMapTileOverlayTypeApple) {
		// do nothing
		self.tileOverlay = nil;
	} else if(self.overlayType==CustomMapTileOverlayTypeOpenStreet || self.overlayType==CustomMapTileOverlayTypeGoogle) {
		// use online overlay
		NSString *urlTemplate = nil;
		if(self.overlayType==CustomMapTileOverlayTypeOpenStreet) {
			urlTemplate = @"http://c.tile.openstreetmap.org/{z}/{x}/{y}.png";
		} else {
			urlTemplate = @"http://mt0.google.com/vt/x={x}&y={y}&z={z}";
		}
		self.tileOverlay = [[MKTileOverlay alloc] initWithURLTemplate:urlTemplate];
		self.tileOverlay.canReplaceMapContent=YES;
		[self.mapView insertOverlay:self.tileOverlay belowOverlay:self.gridOverlay];
	}
	else if(self.overlayType==CustomMapTileOverlayTypeOffline) {
		NSString *baseURL = [[[NSBundle mainBundle] bundleURL] absoluteString];
		NSString *urlTemplate = [baseURL stringByAppendingString:@"/tiles/{z}/{x}/{y}.png"];
		self.tileOverlay = [[MKTileOverlay alloc] initWithURLTemplate:urlTemplate];
		self.tileOverlay.canReplaceMapContent=YES;
		[self.mapView insertOverlay:self.tileOverlay belowOverlay:self.gridOverlay];
	}
}

In the Apple maps case no extra overlay is added of course: we just use the base map. When we select to view the Google and OpenStreetMap we will use a standard MKTileOverlay class with the appropriate URL template. In both cases the overlay will be added with the canReplaceMapContent property set to YES: this replaces the Apple base maps completely and will avoid that those data will be loaded. Note that we add the tileOverlay just below the gridOverlay. Finally the offline case still uses a base overlay class but with a file URL template: note that we create the path from a hierarchical directory structure build inside the bundle. In this case too the new tiles replace the base ones and are inserted below the grid.

Our controller, being a delegate of MKMapView, responds to the -mapView:rendererForOverlay:. This is required by every application that uses overlays as this is the point where the app effectively tells the system how to draw an overlay that is currently visible in the map. In our case we just check that the overlay is a tile overlay (this is a general case to consider that fact that we might have other types of overlays) and based on the selection we use the standard MKTileOverlayRenderer or a custom renderer WatermarkTileOverlayRenderer. The latter is used to apply a randomly colored semi-transparent effect on top of the tiles, getting as a result a vitreous mosaic effect.

Conclusions

The possibility to easily switch between different map types but keeping the same “map navigation experience” is one of the most revolutionary features introduced with iOS 7, other than the longly awaited introduction of native maps inside OSX. This provides the same map infrastructure whatever is the content. Obviously the generation of custom map content is another huge and highly specialized task that we cannot cover here: but for developers this is a great step forward.

References

  • Location and Maps Programming Guide from Apple Developer Library
  • WWDC 2013 session 304 video: What’s new in Map Kit from Apple WWDC 2013 videos
  • MBXMapKit GitHub project by Mapbox – A simple library to intergrate Mapbox maps on top of MapKit, one of the first applications of tiled overlays

“viggiosoft github”

  • The GDAL project one of the main references for custom maps creation. Here is a link to a compiled version of the GDAL OSX Framework
  • Maperitive another great tool (for Windows only) to create custom maps and prepare them for offline usage
Posted by Carlo Vgiani

Two different concepts

In our previous article on the subject we explained how to create an app that supports the Newsstand feature introduced by Apple with iOS 5. The concepts explained in that post provide the technical references needed to build an app that is able to retrieve and download any new content, even in the background, and update the cover-like icon in the Newsstand app. Unfortunately while technically an app build in this way is completely functional, it wouldn’t be approved by Apple if sent for review. The reason for this is that Apple requires that each app compatible with Newsstand must offer its contens using an iTunes managed subscription.

Subscriptions are a rather new concept. They have been initially introduced with iOS 4 but their support has been greatly improved by Apple after a long discussion taken with publishers in order to build a business model acceptable for everybody. In particular the subscription concept has been enlarged by adding auto-renewable subscriptions and with the introduction of Newsstand – and its concept of issues auto-download – also free subcriptions have been added.

Before entering in the detail I would to emphasize the fact that one of the most interesting features of Newsstand, that is background auto-downloading of a new newspaper or magazine issue, is triggered by the special push notification I talked about in the previous part of this tutorial. The In App Purchase concepts are related to auto-download but there is no relation at all, from coding point of view, between the two concepts. As soon as the pubsliher has some new content, he can push this information to the application passing the content-available message together will all info required by the app to start automatic download. The In App Purchase subscriptions concept enters in this scheme from the publisher side by answering a simple question: “did the user subscribe to receive new content from us”? This means that the publisher is not allowed to push any new content to the user if he didn’t previously subscribed. That’s why Apple requires that each Newsstand application is paired with a corresponding In App Subcription service: because the customer must explicitly request for getting automatically the content (even if for free) and at the same time they want to reduce extra bandwith and device resources usage if the contents are not required.

Adding your Newsstand In App Purchases

I will not enter in a detailed explanation of how to setup your application for In App Purchase. This is well explained in Apple’s iTunesConnect Developer Guide and In App Purchase Programming Guide. I strictly recommend you read these guides, especially the second one, and exercise your developer account by creating a test app on iTunesAccount. In any case it is worth to summarize the main steps:

  • Enter in your application page inside the iTunesConnect portal and click on the Manage In-App Purchases button
  • For each type of subscription you want to setup, generate a specific In App Purchase product. In the following picture you can see that I listed three possible choices: one free subscription (note that free subscriptions don’t expire) and two auto-renewable subcriptions, both linked to the same product but with different duration and price.
  • Generate a shared secret and take note of it. You will need it later.
  • Note that the portal page has another sections called Newsstand. It will be used by the App Store only for marketing purposes and allows the publisher to keep his App Store page uptodate with the latest cover, consider for example the iTunes link of the New Yorker app: you will see the latest cover displayed instead of the app icon. An extra option given by Apple is the possibility to link this page to a Newsstand Atom Feed URL for automatic update. We’ll not talk anymore about this option in this tutorial.

A free subscription doesn’t require much data to be inserted: as it is an In App Purchase product, you must provide a unique product identifier; recommended practice is to use the reverse DNS convention, e.g. com.viggiosoft.NewsstandTutorial.subscription.free is enough self explaining; you will also add a display name (that is a title for the subscription) and a description, both localized. Auto-renewable subscriptions are a bit more complex. Infact when you define a subscription you will define a sort of reference product container that will open a family of similar In App Purchase products: this is an In App Purchase product that will not contain any reference to the duration of the subscription and will be used as a base for the single durations. This means that immediately after you have added your reference product, specifying its ID (still in reverse DNS), display name, description and publication name (all localized), you will be asked to add each specific product ID linked to the required duration: one week, one month, and so on up to one year. Note that the display name and description of these sub-products is taken directly from their reference, so the only information that you will be required to add is the duration, the price and of course the unique product ID.

Once this task has been accomplished, you can finally exit iTunesConnect and enter in the XCode domain.

Store Kit implementation

In our simple implementation we can imagine the app as in part I where we added three extra subscription buttons. Note that we are not implementing here any server-side capability, we’ll give some hints at the end and the app will emulate a basic receipt validation mechanism only for the purpose of completeness. The first button will allow the user to subscribe for free to a specific set of magazines. The other two options will ask the user to subscribe to the two one-month and one-year auto-renewable offers that we previously defined in iTunesConnect. Technically we are doing in all three cases a real In App Purchase transaction: in one case it will be free, in the other two cases a monetary transaction will occur.

Below you can see a set of snapshots taken from the app while used in the testing (sandbox) environment. You will notice that more or less the flow is the same of standard In App Purchase with a few noticeable differences:

  1. The title will change to Confirm Subscription
  2. Note that for the paid case, the 1 month term is automatically generated by the system based on the duration of the selected subscription. The only term customizable by the user is the Super Magazine Subscription definition that we set in iTunesConnect without any explicit reference to the duration.
  3. Immediately after the purchase, the system will ask for the explicit permission to send some personal data to the publisher. This is another compromise between Apple and the publishers that accepted to provide discounted subscriptions but in return of the possibility to control this kind of data (useful for advertising purposes).
  4. In case you already subscribed, the system will recall the user that he/she already subscribed and that the subsription will renew automatically at expiration. Read below to know how to avoid while developing the app to wait 1 month or 1 year to test your app after you did the first subscription…

At this point the code is not different from the usual code you may have written in the past. Let’s summarize here the main steps with the required code. For simplicity we have merged together the Store Kit communicationinside the main app view controller. Probably in a more complex application you want to separate the view controller from the store logic. As basic setup, don’t forget to link your code to the StoreKit library and import the framework header:

1
#import <StoreKit/StoreKit.h&gt> 

Besides our view controller will need to declare itself as conform to SKPaymentTransactionObserver, SKRequestDelegate and SKProductsRequestDelegate protocols. Finally our application delegate must register our view controller to be a transaction observer:

1
2
3
4
5
6
7
8
9
10
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { // other initialization code goes here // in particular we assume our "store" view controller has been already initialized here // StoreKit transaction observer [[SKPaymentQueue defaultQueue] addTransactionObserver:self.store]; // other initialization code } 

Immediately after clicking on one of the subscription buttons, we’ll call a method that will start the purchase procedure (eventually updating the UI) based on the product ID we pass to it: what we do here is to use a variable called purchasing to avoid two or more purchase operations going together (note that In App Purchase is highly asynchronous with unpredictable delays and trying to place together more purchases would be messy for the user). Then we create our StoreKit product request and submit to the App Store:

1
2
3
4
5
6
7
8
9
10
- (IBAction)subscription:(NSString *)productId { if(purchasing_==YES) { return; } purchasing_=YES; // product request SKProductsRequest *productsRequest = [[SKProductsRequest alloc] initWithProductIdentifiers:[NSSet setWithObject:productId]]; productsRequest.delegate=self; [productsRequest start]; } 

The request can fail, and in such case we reset our UI (not displayed here) and unset the purchasing flag. If the request succeeds, we’ll get the product IDs returned from the request and we’ll add them to the payment queue, effectively starting the operation. For completeness we should hilight the fact that in most cases the initial prouct request can be done independently on the effective user request of doing a purchase: the reason for this is that doing a product request is the only way to retrieve the exact product price localized to the user iTunes Store he’s currently connected. This means that we should never hard code a price in our app but we must retrieve it dynamically using a product request. In this case I simply skipped this extra step, not necessary for the purpose of the tutorial, by avoiding to mention any price in the subscription buttons! but be awave that showing the price together with a “buy” button is more than a welcome addition. Returning to our code, you will see below that we’ll loop through the returned responses (we shouldn’t get more than one due to the logic of the app) and for each of them we submit a payment request.

1
2
3
4
5
6
-(void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response { for(SKProduct *product in response.products) { SKPayment *payment = [SKPayment paymentWithProduct:product]; [[SKPaymentQueue defaultQueue] addPayment:payment]; } } 

As we previously registered this view controller instance as store kit transaction observer, we’ll get a few calls to the paymentQueue:updatedTransaction: protocol method. We must follow the right logic according to the effective state of the transaction. In our case we’re of course interested to the “success” case, managed via a call to our internal finishedTransaction: method:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
-(void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions { for(SKPaymentTransaction *transaction in transactions) { switch (transaction.transactionState) { case SKPaymentTransactionStateFailed: [self errorWithTransaction:transaction]; break; case SKPaymentTransactionStatePurchasing: break; case SKPaymentTransactionStatePurchased: case SKPaymentTransactionStateRestored: [self finishedTransaction:transaction]; break; default: break; } } } 

To terminate the transaction, we’ll inform the transaction queue that we declared our transaction finished. Now in a standard In App Purchase environment, based on typical consumable or non-consumable product, we should immediately provide the purchased product to the customer. For the subscription case infact we’re not providing a specific product but a subscription to a set of future products. Typically we’ll immediately unlock the access to the latest magazine (and as we’re in Newsstand doing this is quite easy…) but the most important stuff is that we must inform our server of the transaction data. The code below is a possible example of this, but we’ll explain the mechanism in more detail in the next paragraph. For the moment just notice that apart a few alerts displayed with some logging information we are sending the transaction receipt received from the store to a specific class called ReceiptCheck:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
-(void)finishedTransaction:(SKPaymentTransaction *)transaction { [[SKPaymentQueue defaultQueue] finishTransaction:transaction]; UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Subscription done" message:[NSString stringWithFormat:@"Receipt to be sent: %@\nTransaction ID: %@",transaction.transactionReceipt,transaction.transactionIdentifier] delegate:nil cancelButtonTitle:@"Close" otherButtonTitles:nil]; [alert show]; [alert release]; // save receipt [[NSUserDefaults standardUserDefaults] setObject:transaction.transactionIdentifier forKey:@"receipt"]; // check receipt [self checkReceipt:transaction.transactionReceipt]; } -(void)errorWithTransaction:(SKPaymentTransaction *)transaction { [[SKPaymentQueue defaultQueue] finishTransaction:transaction]; UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Subscription failure" message:[transaction.error localizedDescription] delegate:nil cancelButtonTitle:@"Close" otherButtonTitles:nil]; [alert show]; [alert release]; } -(void)checkReceipt:(NSData *)receipt { ReceiptCheck *checker = [ReceiptCheck validateReceiptWithData:receipt completionHandler:^(BOOL success,NSString *answer){ if(success==YES) { NSLog(@"Receipt has been validated: %@",answer); } else { NSLog(@"Receipt not validated! Error: %@",answer); }; [checker release]; }]; [checker retain]; } 

Server-side subscriptions management

We previously said that after a user completed a subscription purchase, we are not able to provide the full content immediately, but we still need to know when this content must be provided for the future. The current case is easily managed. If we call our 1-month subscription com.viggiosoft.NewsstandTutorial.subscription.1month then from the purchase receipt we know the purchase date and simply parsing the product ID we are able to define the expiration date as the transaction date + 1 month. This is easy and can be stored inside the app, at least for UI management. But there are many things that could happen in the subscription management and that the app is not necessarily aware: e.g. the subscription is renewed, or it has not been renewed by user choice, or it has not been renewed just because before the expiration we increased the price of our subscription (Apple stops any automatic renew in such event) or finally we defined a marketing bonus, that is provide e.g. 1 extra weekly subscription for each 1 month purchase (this is a possibility that is explicitly provided by iTunesConnect and is called marketing opt-in incentive. This means that a set of transactions may occur outside the control of the application: at this point the data we stored in the app immediately after the purchase couldn’t be reliable and we need to contact the App Store for the uptodate information. The best way to do this is to send the receipts to our server and let the server to systematically call Apple to maintain its subscription status for each user aligned with the App Store; in our case our app can simply use a basic API towards our server to lock or unlock certain magazines. But there is another reason why the server must take this information and it is strongly related to Newsstand: in order to push new content to the user device, the server must know for each new issue if the user is subscribed to it or not. So every time the publisher releases a new copy of his daily newspaper or a new issue of his weekly magazine, the subscription information of all users must be queried and the new content must be pushed to their device only if their subscription is still valid. Clearly we need to understand how this information is managed and can be retrieved.

In our example app we decided to implement a basis scheme of receipt validation directly inside the app. As we stated before, this is not the right method to do it and we simply added this extra code only for the needs of this tutorial. That’s why we decided to move this receipt validation scheme in an external class.

The end of the payment transaction is represented by an instance of the SKPaymentTransaction class which contains all the data required to keep track of the paymen: the ID of the purchased product, its quantity, the price paid, the date, a transaction identifier and most of important of all – for server based delivery of In App Purchases – the transaction receipt. This receipt is a chunk of data, internally coded by Apple, that can be decoded by sending an appropriate JSON message to iTunes’ receipt verification API (there are two different addresses, one for production and one for sandbox testing, see Apple documentation for a complete description of this API and the returned messages and error codes):

1
2
3
4
{ "receipt-data":"base64-data-encoding-of-receipt-data" "password":"your-autorenewable-shared-secret-from-itunesconnect" } 

In return Apple server will give us a full description of the receipt. A real example taken from the tutorial app of a possible receipt of the free subscription is shown below. .

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{ "status":0, "receipt":{ "bvrs":"1.0", "item_id":"473670104", "bid":"com.viggiosoft.tutorial.NewsstandTutorial", "product_id":"com.viggiosoft.tutorial.NewsstandTutorial.001", "purchase_date":"2011-10-29 19:49:02 Etc/GMT", "original_transaction_id":"1000000011535741", "quantity":"1", "original_purchase_date":"2011-10-29 17:14:33 Etc/GMT", "transaction_id":"1000000011544007" } } 

For a auto-renewable transaction the returned data contains more data. Initially you must check for the status key, if it is 0 then all went OK, if not 0 then you must understand the error message (the auto-renewable subscriptions possible status codes are summarized in Apple documentation). In addition you will get the expiration date (inclusive of the opt-in incentive) and the original transaction ID, that is the ID of the transaction that originated the subscription. In case the transaction has been auto-renewed and we are still sending to our server the original receipt, iTunes will return as an extra field, called latest_receipt_inf which will contain the latest renewed receipt together with the base64 version of this receipt. It makes sense in our case to replace our original receipt data with the latest one for the next queries.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
{ "status":0, "receipt":{ "bvrs":"1.0", "item_id":"476937514", "bid":"com.viggiosoft.tutorial.NewsstandTutorial", "product_id":"com.viggiosoft.tutorial.NewsstandTutorial.1month", "purchase_date":"2011-10-29 20:30:07 Etc/GMT", "expires_date_formatted":"2011-10-29 20:35:07 Etc/GMT", "expires_date":"1319920507692", "quantity":"1", "original_transaction_id":"1000000011540367", "original_purchase_date":"2011-10-29 18:41:32 Etc/GMT", "transaction_id":"1000000011546189" }, "latest_receipt_info":{ "bvrs":"1.0", "item_id":"476937514", "bid":"com.viggiosoft.tutorial.NewsstandTutorial", "product_id":"com.viggiosoft.tutorial.NewsstandTutorial.1month", "purchase_date":"2011-10-29 20:30:07 Etc/GMT", "expires_date_formatted":"2011-10-29 20:35:07 Etc/GMT", "expires_date":"1319920507692", "quantity":"1", "original_transaction_id":"1000000011540367", "original_purchase_date":"2011-10-29 18:41:32 Etc/GMT", "transaction_id":"1000000011546189"}, "latest_receipt":"base64-encode-of-latest-receipt" } } 

As you can see the receipt doesn’t contain any reference to the Apple ID used to make the purchase. The only way for us to match the specific device with the returned receipt is to send to our server both the Apple Push token (APNS token) and store both in the same record of our server-side database. In this way when we have new content available to be pushed to our customer Newsstand we’ll be able to process all of our APNS tokens, check the receipts and if the new content (magazine or newsspaper) is still covered by the user subscription we’ll then send the Newsstand push notification to trigger the download on the user device.

What happens if the user wants to use the same subscription data in another device? in such case we must provide the typical transaction restore process as required by the Store Kit guidelines. Typically the restore button is placed somewhere in your application UI and his only purpose is to call this method:

1
[[SKPaymentQueue defaultQueue] restoreCompletedTransactions]; 

All consumable and non-consumable transactions will be restored in the usual way: the SKPaymentTransactionStateRestored transaction update will be notified to our Store Kit observer and from the originalTransaction property of our SKPaymentTransaction we can get the product ID and then unlock it on the new device (don’t forget that the user can simply buy single issues from our app). For subscriptions the history is a little bit different, infact the system will return us the full set of transaction associated to the subscription history: infact each time a subscription is automatically renewed a new transaction is generated and it is stored in Apple servers. But why we need to know the whole history of user subscriptions? because as soon as we restore the subscription on a different device, we need to know for which dates the user was subscribed and only knowing the whole subscription history we’ll be able to unlock the right content: nothing more, nothing less than the user purchased for. Typically these receipt will be of the form explained above, that is a json message composed of the two fields: “receipt” and “latest_receipt_info”. The latter will all refer to the latest valid receipt, while the former will be different and related to each of the transactions: we must parse the purchase and expiration dates to create the valid subscription date range and provide the content accordingly.

Time compression

Before delivering our application we must carefully test all possible subscription cases: add a subscription, auto-renew, new content push notifications, subscription expiration, and so on. Clearly it would be impossible to wait for days or weeks or months to do these tests only. In Sandbox testing Apple compresses this time by giving you the possibility to test your application in minutes or hours. Looking at the receipts above, you will see that for a subscription purchased on Oct 29 at 20:30 (8:30 PM) there is a corresponding expiration date of only 5 minutes (same date at 20:35). This is because in the sandbox environment a 1 month subscription is compressed to a 5 minutes time scale. The full table of subscription duration / sandbox time scale is given in the figure. Besides auto-renewal will stop after 6 renewals thus giving you the possibility to manage the end-of subscription case (to avoid delivering content the user didn’t pay for): don’t forget that with a In App Purchase test account you cannot connect to the real iTunes store and so you have no way to manage subscriptions manually.

To better visualize this effect I added in my example application a “Receipts” button. After each purchase I store in a file the receipt data. When the user clicks this button, this file is opened and all stored receipts are sent to Apple for validation. The returned JSONs are then displayed as an alert. So if you try for example to purchase a 1 month subscription, and then immediately after the purchase you query for the receipts, you will get the purchase receipt only. Then try after 5/10 minutes (corresponding to 1 month in the time compression scale) to reopen the app and query for receipts again. The system will return you 2 or more receipts, each one corresponding to a different auto-renewal subscription: note the expiration date, it will be the same in the latest receipt field, but it will change in the receipt field as it is associated to each 1 month (5 minutes) renewal. Then close the app, wait for 40 minutes and try again: you will not get more than 6 receipts, as by convention after the 6th renewal attempt the subscription will automatically expire. You can improve this code by doing some real subscription date range detection and show it clearly to the user. Even more interesting is that as a transaction is automatically generated for each auto-renew, if you keep your app running in the foreground during an autorenewal, your app will be notified through the transaction observer mechanism and the receipt will be immediately validated.

Conclusions

This second part of the Newsstand Tutorial talks a bit of Newsstand and much more of In App Purchase and subscriptions. Of course you can stil make your app to be subscription based only, without any Newsstand integration, and probably you can manage all subscriptions directly inside your device by saving receipts locally and querying for their validity when needed. The ReceiptCheck class in my application example is done for this. What you cannot to do is to release a Newsstand app without In App Purchase. As one of the most appreciated Newsstand capabilities is to have the device download every new content in the background and at night-time, you must keep a note of each subscription and associate it to a push notification token: you can’t do this without a server, so in such case moving the whole receipt validation mechanism on your server is required.

The source code for this two part tutorial is hosted on GitHub. Note that this is not completely useful if you don’t do the whole setup for push notifications, In App Purchase, shared secrect and so on. My suggestion is: don’t try to run it, don’t use it as production code, but only as reference to better follow this tutorial and get some help if you’re stuck.

Carlo Vigiani

Il costo di un’ App per iPhone & iPad

This is the italian translation of the article Dear business people, an iOS app actually takes a lot of work!, written by Kent Nguyen. Thanks Kent for giving us permission to publish this translated article.

 

Il grande quesito: quanto costa un’app per iPhone?

Questa è una domanda estremamente comune che viene sempre chiesta da un sacco di persone che lavorano nel mondo degli affari oppure da clienti non molto esperti di tecnologia. Senza dubbio, ogni volta che ho fornito una stima iniziale prima ancora di formalizzare e analizzare in dettaglio le specifiche, ho potuto vedere sui loro volti l’espressione di shock a causa della inaspettatamente alta quotazione.

Inoltre nessuna delle mie quotazioni si è lontanamente avvicinata ai valori discussi in, nel quale vengono discussi i costi di sviluppo dell’app Twitterific. Nonostante il fatto che la domanda originale fosse stata posta nel 2008 e la migliore risposta (da uno degli sviluppatori di Twitterific) fosse arrivata nel 2010, è ancora molto precisa all’inizio del 2012 ed è facile prevedere che lo sarà almeno fino alla fine dell’anno.

Cosí, con il crescere del numero di imprese che desiderano avere un’app per iOS, penso che sia una buona idea spiegare perché i costi siano effettivamente alti cercando di dividere i vari passi e spiegare le variabili coinvolte. Spero che questo articolo sia di beneficio per i non sviluppatori e gli uomini d’affari che devono prendere delle decisioni o semplicemente vogliono comprendere il processo. Le idee in questo articolo ovviamente non sono ristrette al solo mondo iOS ma possono essere estese ad altre piattaforme (Android, Windows Mobile, forse Blackberry).

Checklist: prepararsi ad un’app per iPhone

Il procedimento non è affatto semplice e cerco innanzitutto di informare il cliente a fare tutte le considerazioni guidandolo attraverso questi passi:

PRIMO: Una delle maggiori scoperte che ottengo parlando con i clienti è quanto siano inconsapevoli delle grande infrastruttura necessaria per un’app per iPhone. Dato che essi assumono che un’app sia semplicemente un’app, si aspettano che gli venga fornito il prezzo di fare qualunque cosa con l’app senza tener conto di problematiche quali: avere un server di supporto col quale l’iPhone comunichi, dove immagazzinare i dati dell’utente. La prima volta che incontrai un tale cliente ero furioso ma in seguito ho realizzato il fatto che il concetto di client-server non deve essere dato per scontato tra i non programmatori. Avevo sbagliato, i manager di solito non hanno il senso-tecnico comune che noi programmatori ci attendiamo.

Perciò, cari lettori non tecnici: è necessario che disponiate di un server nel quale memorizzare i dati per qualunque tipo di app che come minimo debba fare qualche autenticazione (login) o qualunque tipo di personalizzazione che volete cambiare in seugito o comunque qualunque operazione che richieda il recepimento di informazioni dall’utente (sto cercando di usare il linguaggio il più semplice possibile).

SECONDO: Dato che avete bisogno di un server, bisogna fare in modo che l’iPhone possa comunicare con tale server, inviando e ricevendo dati. Non esiste una maniera standard, non esiste nessun componente plug-and-play per fare questo, ogni cosa va customizzata. Questo è analogo a creare il vostro linguaggio personalizzato: non volete che gli altri comprendano ciò che state dicendo ma le due estremità, telefono e server, devono capirsi.

Questo processo consiste nella creazione di API (Application Programming Interface) per la vostra app. Queste API devono esistere prima che si proceda con lo sviluppo dell’app. Perché? perché prima di cominciare a comunicare occorre definire il linguaggio! Questo ci porta al prossimo passo, come creare queste API.

TERZO: Non prendere questo passo alla leggera, le API hanno un’importanza pari al 50% dell’intera soluzione. Fare un’API è come mettere in piedi un sito web. Prima devi definire i dati, quindi la logica di business, quali sono i parametri di ingresso a tale logica, come interagiscono fra loro i vari moduli quando accade un evento. Per semplificare, il risultato finale è un sito web completo dove però le pagine non mostrano risultati grafici ma solamente del testo che verrà compreso dalla nostra app: ad esempio una pagina di autenticazione conterrà, in caso di successo, la semplice parola YES.

L’iPhone quindi farà una serie di richieste a questi punti terminali predefiniti (pagina di login) usando il formato di ingresso predefinito dall’API (nome utente + password) e quindi interpreterà il risultato fornito da queste pagine in risposta alla sua richiesta (YES/NO). L’app senza questo non potrà mai registrarsi e fare il login magicamente da sola.

Ci sono *un sacco* di variabili che devono essere prese in considerazione in questa fase, come preparare un server, selezionare il linguaggio di programmazione con cui scrivere le API, dove immagazzinare i dati per minimizzare i tempi di comunicazione, ecc.

QUARTO: Queste API o sono già pronte e ben documentate dal vostro team interno per essere fornite allo sviluppatore iPhone oppure preparatevi a pagare di più che solo per la pura app. In funzione delle conoscenze dello sviluppatore che avrete contattato per farvi l’app, egli/ella potrebbe avere o meno le capacità per fare ciò di cui avete bisogno. Se lo sviluppatore è in grado di fare questo lavoro, allora vi consiglio fortemente di affidargli anche questa parte del progetto dato che egli sa esattamente quali API servono per far funzionare l’app al meglio.

Nel caso abbiate già le API dovrete dare allo sviluppatore la possibilità di parlare apertamente e liberamente con il vostro team di back-end e non limitarvi a dargli la documentazione; questo perché il più delle volte egli richiederà che venga fatto del lavoro in più (più API) per supportare l’applicazione mobile in maniera appropriata.

Adesso, la parte iPhone

Caspita, tutto questo per essere appena pronti a sviluppare l’app e non siamo ancora partiti! In generale, qualunque cosa riguardo iOS è molto restrittiva. Dovete quasi sempre aver definito circa il 100% dello scopo e del design prima che lo sviluppatore possa partire con la programmazione; diversamente dallo sviluppo di siti web, lo sviluppo di un’app per iOS sotto contratto ha pochissimi margini di cambiamento:

Disegnare l’interfaccia: La scelta se si devono utilizzare i componenti grafici standard o personalizzati deve essere presa già dall’inizio, dato che l’architettura dell’intera app dipende da come si vuole l’interfaccia e da come la utilizzeranno gli utenti.Un esempio è la Tab Bar in basso: se si vogliono i bottoncini colorati invece di quelli monocromatici standard, la modifica al codice è sostanziale!

Il codice è fortemente integrato: Con i siti web voi potete aggiungere semplicemente una pagina in più, quindi create un link a quella pagina ove richiesto. Non potete fare questo con un’app per iOS, dato che ogni cosa va decisa all’inizio e ogni cambiamento può risultare in cambiamenti significativi in altre parti dell’app. Il modo in cui il codice iOS è strutturato è come quello di una breadboard (le basette sperimentali per circuiti elettrici), dove ogni cosa è collegata con fili: voi potete sempre cambiare delle cose qui e là ma se tocchi il filo sbagliato allora l’intero circuito smetterà di funzionare. Anche se viene usato del codice estremamente ben strutturato la flessibilità non aumenterà molto. L’aggiunta di un bottone email addizionale nella schermata “About” potrebbe richiedere un ridotto numero di linee di codice addizionali, ma l’aggiunto di un bottone “Facebook Like” sulla stessa pagina è una cosa completamente differente e non ci può attendere che venga fatto in poche ore.

Convertire un’app per iPhone in un’app iPhone/iPad universale: Questa è la peggiore ‘funzionalità aggiuntiva’ presente nei contratti di sviluppo di app per iPhone. Questo perché un’app per iPad non è una banale funzionalità aggiuntiva. Le app per iPad sono di solito molto più complesse delle app per iPhone e il più delle volte sono richiesti un’interfaccia e un meccanismo di interazione completamente differenti. Non solo quello, dato che anche le API potrebbero essere diverse: l’app Denso, che ho sviluppato (e quindi conosco), ha alcune funzionalità esclusive dell’app per iPad che richiedono dati addizionali dal server. Inoltre l’iPhone e l’iPad richiedono esperienze utente differenti.

Dunque siete pronti a partire?

Spero che dopo aver letto questo abbiate un’idea più chiara di quel che è necessario alla vostra ditta per sviluppare un’app per il mondo mobile. A meno che non facciate un’app completamente offline (come una Calcolatrice!) che non raccoglie dati dagli utenti, non potrete pretendere che questa vi venga sviluppata a un prezzo basso. Dopo aver considerato le variabili elencate sopra, se non potrete giustificare lo sviluppo a contratto allora l’altra opzione è assumere a tempo determinato dei progettisti a tempo pieno che lavorino a tempo pieno per l’intera durata del progetto.

D’altro canto, se decidete di andare avanti e affidare in outsourcing il lavoro, c’e’ un’altra cosa da aggiungere: le lungaggini burocratiche. Se lavorate in una grande azienda o in un ambiente molto regolamentato, il vostro lavoro sarà essenzialmente di aiutare lo sviluppatore ad evitare le lungaggini burocratiche che potrebbe incontrare lungo la strada e talvolta avrete necessità di aggirare un pochino le regole. Ho parlato con molti clienti e ho potuto notare il loro scetticismo quando ho cercato di saperne di più sulle loro API, dato che essi o non desideravano esporsi maggiormente per non violare le loro regole di segretezza, e non potevo biasimarli, oppure non possono portare certi dati al di fuori del perimetro aziendale, oppure semplicemente (e questa è la cosa peggiore) le regole di branding aziendale richiedono che ogni singola pagina dell’app presenti il logo dell’azienda (!!!). Alla fine ho preferito non lavorare con questi clienti dato che non potevo immaginare quando a lungo avrei penato per ottenere anche un minimo supporto sulle API di cui avrei avuto bisogno.

P.S. Avrete bisogno di tempo, un sacco, per cui siate pazienti.

 

Introduction

One of the most appreciated features by iPad users is the possibility to read books, magazines and newspapers. Practically all major publishers are in the App Store with apps dedicated to their products but there also many other minor publishers, in every country, that entered in the iOS world with one or more apps.

Applicazione iPad per riviste e Magazine

Developing iPad App for Magazine

Today a publisher that wants to enter in the App Store with his own magazine has several decisions he needs to make. Some of these are:

  • is it better to publish a specific app for the magazine or use a newsstand app as Zinio?
  • in case we decide to use our own app, should we contact an iOS dev to have a tailored product or use a web-based service that provides us in short time with a standard app?
  • should the magazine service be hosted by a third party or by the publisher itself?
  • is it better to re-use existing PDF or make a completely new digital magazine (e.g. if you use the Adobe Digital Publishing suite)?

Of course all these decisions will have impact on development costs, web services hosting and maintenance and finally the magazine design flow.

The author of this post has gained some experience by developing and releasing on the App Store several magazines.This series of articles is based on the experience acquired by developing some custom magazine apps and will try to depict what is the architecture of an iPad magazine app starting from the building blocks and entering into the coding challenges occurring on these blocks. We hope that any developer that should have the chance to build an iPad app can take some benefit reading these articles.

Part 1 – Architecture

The scheme below shows the three main screens of a typical magazine app.

note:Even if we’ll mainly refer the iPad device, all considerations can be applied to the iPhone too.



Main screens of a Magazine app

The first screen is the Store screen. This is typically the first view presented to the user (with the exception of the splash screen if required by the publisher) and provides the user the list of all issues that are currently available for purchase (with the term “purchase” we also mean “free” issues, which obviously are zero-priced).

note:In a typical magazine app we have only one kind of magazine, so the choice will be restricted to the last available issue and a set of older issues.

Of course for a more complex app, e.g. a book seller or multi-magazine app, this screen could be more complex as in such case products are organized by categories and then we could have a hierarchical representation of the same screen. Typically issues are represented with their cover and these covers are shown as a grid, or as a scrolling stand or using the well-know “coverflow” effect.

Together with the cover each issue is classified by its name, its release date and of course the price represented in the user currency. Besides a set of actions are associated to each issue, typically: purchase, download, preview.
Once an issue has been purchased, it should be automatically transferred to the Library view. This screen (which is usually accessible using the tab bar at the bottom) will show the list of issues that have been purchased. From this view the user can, for each issue, decide to read, delete, archive or download it.

Finally the Reader is the part of the app that allows the user to view the magazine: it can be a general purpose PDF or e-pub reader, or use (but this is not recommended as the capabilities are quite limited) the system Preview feature or finally be a custom reader: this depends on the format the issues are downloaded by the app.

Some publishers may ask to merge the Store and Library sections: this is a natural choice for those magazines which have a monthly (or lower) periodicity: in such case – due to the low number of issues available – it could be easier to show all covers in the same screen providing to the user different actions according to the fact that the magazine has been purchased or not.

A magazine app with the right architecture should be able to decouple the visual structure from the functional blocks. The reason for this is that both the publisher and the UI designer could come out with completely new ideas on how to represent the store on the screen, and it would be a nightmare for the developer to integrate each time his own back-end code with the specific needs of the new user interface. So to guarantee the maximum re-usability of the Store Manager and modeling structure it is good idea to architect the app following the MVC (Model View Controller) pattern as recommended by Apple for Cocoa apps.

Below you can see a high level functional blocks diagram.

The Publisher Server cloud symbol in the diagram represents all internet services, which are not part of the app. Normally this includes the server infrastructure used as storage for the magazines and the set of web services that will provide the magazine information to be seen on the store. This back-end can be hosted on the customer owned servers, or on specific sites such as Amazon S3 or finally on the developer server (but in such case be careful to provide a sufficient bandwidth and minimum downtime).

The Store Manager block is the central functional piece of the app. Its role is to communicate with the back-end server(s) and dispatch acquired data to the other parts of the app.

Initially the Store Manager needs to fetch from the back-end service the list of all available issues. How this can be done is specific of the implementation. We can use a simple XML or JSON file in case the list of issues is not huge, or we can establish a more complex protocol in case the publisher catalog is too large to be downloaded each time. E.g. we may provide access to several magazines which are organized by category or implement a search feature.

noteThe communication between the server and the app is one-way as data is transferred from the server to the app while the opposite flow could be limited to a minimum data exchange consisting of purchase transactions, simple http queries or analytics on the app usage.

From the point of view of the developer it makes sense to insert an intermediate communication layer (not shown in the diagram) between the server and the Store Manager: the advantage for this is that the Store Manager will expose a set of simple and general purpose APIs and at the same time the communication layer will take care of the specific implementation of back-end APIs.

Each time a new bunch of data is sent from the server to the Store Manager a set of Issue Models is created.

An Issue Model is the logical representation of each issue, and it consists of a unique identifier, the title, a cover image and the release date. Other information can be provided according to the application characteristics: e.g. a set of image previews, a short description of the issue, a table of contents, etc.

note:It is important to note that the Issue Model is characterized by a set of fields and only a subset of these is assigned by the store. Other information can be acquired from other sources: e.g. the price can be retrieved from the In App Purchase system, or the information that the product has been already purchased and downloaded from a local repository in the application data area. That’s why in the diagram below we decided to attach the Issue Models representation to a Local Storage block.

As soon as a new Issue Model is created by the Store Manager, it is annotated with few extra info collected from the Local Storage. The natural choice for the Local Storage would be to use the Core Data framework but of course a more simple approach based on plists or a serialized version of the data model.

The Store View is a view controller dedicated to provide the Store UI.

note:While the Store Manager is a highly reusable component, the Store View is customized according to publisher requests: we can have a shelf view (as in iBooks) or a sliding covers view, or a cover flow effect. In order to decouple the model data from the view the Store View can talk with the Store Manager using a delegate protocol.

Besides the Store View needs to listen to some Store Manager changes using a central notification mechanism or key-value observing (KVO). Why this requirement? because even if in most cases the Store View gets its data by querying the Store Manager block, using its delegate protocol (e.g. number of categories, name of categories, number of issues per category, name of issues, cover image and so on), it may happen that some events occur asynchronously with respect to the typical UI interaction (e.g.: a user is reading an issue but at the same the app finish to download another issue): in such case the Store View controller must be informed of this particular event in order to update the UI appropriately. Once the Store View knows, through notification or by KVO observation, that something changed in the Store Manager, it can start the delegate protocol based query cycle to update the UI.

The Library View is also a View Controller which is similar to the Store View but customized for the purpose of displaying only issues that have been purchased. It still needs to communicate with the Store Manager using the same protocol used by Store View and it still needs to listen to Store Manager changes, but the set of actions available to the user is completely different. As soon as a purchase is done, that is the transaction has been recorded, there are a set of operations that could be deferred or could simply fail due to temporary networking issues: they are the download phase (which could take several minutes especially if the issue a several hundred megabytes package) and the installation phase (typically unarchiving the package, generating thumbnails and so on). So the Library Manager needs to expose to the user the next required action: download, if the issue has been purchased but not downloaded, stop/cancel download if issue download is in progress, install if the package has been downloaded but not installed and finally read to start reading the magazine.

In the diagram we kept the Store and Library views separated to emphasize the different requirements and to be coherent with the 3-screens based app organization. But as stated before it may happen that the Store View and the Library View are merged in a unique view controller; this approach is common to many well-known magazine apps in the App Store.

note:The Store Manager maintains an interaction with two internet-based Apple services: In App Purchase (via Store Kit framework) and Newsstand (currently in beta, will be available with iOS5).

Due to App Store rules, In App Purchase is practically the only way to purchase magazines from the store. As there is no way at the moment to let the publisher own back-end server to communicate directly with the In App Purchase system, the Store Manager block must be able to annotate the Issue Model with the extra pricing info coming from the In App Purchase server.

The recommendation in such case is to insert an intermediate layer between the Store Manager and the Store Kit framework, whose purpose is to manage the communication with the highly asynchronous Store Kit protocol thus giving at the same time a simple interface to the Store Manager (e.g.: is this issue available in the store? what is the current price in this country? did the user already purchased the issue?) One example of well-known and excellent layer is the MKStoreKit open-source library available from GitHub.

Newsstand is a new feature coming with iOS5. As we are constrained by the NDA with Apple, we cannot disclose too many details and just refer to the marketing info available in the Apple web site. Essentially Newsstand will be a central hub for all subscription-based magazine apps: it is a special Springboard folder that will collect all information from the magazine apps and show and download all the latest issues. The Store Manager must provide the minimum required interface to provide the app the compatibility with this new feature.

In the next articles I will provide some more detail about the construction of the different blocks. We’ll see the details of the Store Manager delegate protocols, we’ll discuss the model behind each issue and we’ll see how to efficiently retrieve information from the network.

 


Introduction

One of the most appreciated features by iPad users is the possibility to read books, magazines and newspapers. Practically all major publishers are in the App Store with apps dedicated to their products but there also many other minor publishers, in every country, that entered in the iOS world with one or more apps.

Today a publisher that wants to enter in the App Store with his own magazine has several decisions he needs to make. Some of these are:

  • is it better to publish a specific app for the magazine or use a newsstand app as Zinio?
  • in case we decide to use our own app, should we contact an iOS dev to have a tailored product or use a web-based service that provides us in short time with a standard app?
  • should the magazine service be hosted by a third party or by the publisher itself?
  • is it better to re-use existing PDF or make a completely new digital magazine (e.g. if you use the Adobe Digital Publishing suite)?

Of course all these decisions will have impact on development costs, web services hosting and maintenance and finally the magazine design flow.

The author of this post has gained some experience by developing and releasing on the App Store several magazines.This series of articles is based on the experience acquired by developing some custom magazine apps and will try to depict what is the architecture of an iPad magazine app starting from the building blocks and entering into the coding challenges occurring on these blocks. We hope that any developer that should have the chance to build an iPad app can take some benefit reading these articles.

 

The platform i3F editorial rests on four foundation pillars:

  1. Development and reading documents in PDF format
  2. The use of web services and network queue
  3. The infrastructure for the Apple In App Purchase
  4. Web services using the editor

Let’s see in detail.

PDF Reader

The basis of the reading of the documents is the PDF Reader. To understand the work that is behind this technology, we begin by making reference to that IOS provides its developers.
PDF support and ‘native support within the framework Quartz, the 2D graphics framework installed in Mac OS X and successfully brought in IOS. To understand the importance of PDF in operating systems from Apple, suffice it to say that PDF is not ‘seen as any output format, but in fact any view graphics in Mac OS X and IOS can’ be reproduced as a PDF, which in fact turns out to be the prince format for printing based on Quartz. This explains why ‘is in Mac OS X support for PDF in Ios, both inbound and outbound, and’ natural and does not require the installation of external software (as is the case in Windows, where the input requires ‘installation of Adobe Reader, the output requires the use of appropriate plug-in).

That said, this’ does not mean that things are easy. In fact, the support provided by iOS is essentially limited to the possibility ‘to’ read ‘and understand a PDF file, but only to the functions of rendering on paper or video, while the interpretation of all other data (outline, thumbnails, annotations, etc. .) and ‘left to the programmer.
The PDF reader that comes with the app produced by i3F Editorial and ‘continuous work in progress, subject to continuous improvements and support of new possibilities’. Currently it offers the following features’ base:
– Support for iPhone and iPad
– Fast rendering of the page in portrait format (horizontal) and landscape (vertical, two-page spread), with caching for faster performance
– No limit on the number of pages supported (or at least no limit in addition to any provided by the platform IOS)
– Completely based on the rendering engine of IOS, so no surprises in the transition of the application across several operating system versions
– Loading thumbnails in multi-threading
– Mini-thumbnail (style iBooks)
– Function scrubbing of the page (with display of page number and / or thumbnail)
– Pre-loading outline (table of contents with all its hierarchical structure) and the comments (link)
– Intra-document annotation (jump page), and to external links
– In addition to standard support of external links (http:and mailto: within the application, any other url patterns to other app installed by the user, eg skype:) support links owners to stream video directly by the JPA and for displaying photo galleries, and this ‘means that the publisher will be’ able to create multimedia packages (PDF + multimedia) simply by defining the links within its graphical tool that generates the PDF, without having to weigh and implement all the technical complications due to the inclusion of media files directly into PDF (Recall that Quartz does not support these types of files, effectively ruling out this information.) Support for these links and ‘gradually increasing.
– Search function tests in multi-threading (ie the use of the document is not locked)
– Save the last page viewed (auto-bookmarks)

Library management

The interface defined by the library i3F Editorial is based on some commonly accepted standard templates (eg shelf covers iBooks style, window style or covers in the App Store), although i3F Editorial is a software package but a team of designers and developers able to implement any request is made by the user. The iPad and ‘a formidable creative platform, so they are well liked editors who want to bring this creativity’ even within applications.
What is ‘common hand and’ the technical aspect behind this approach. Currently, the composition of the publisher of the archive, and library users, are contained in files (in various formats, depending on the complexity ‘of the archive, ranging from XML to JSON actual SQLite database to , the most ‘complex). The files can be managed entirely by the editor through our web platform and can be installed on servers hosted by the owners or i3factory (which in turn uses deemed reliable provider ‘). At any time the publisher and ‘capable of varying the composition of its stock and make it instantly with a single click, after pre-testing always possible publication through our applications.
Once the file is defined, the application tries to start the last update was available. From this moment on, each publication can ‘overcome several states in the shop (if paid), to download (for free or if already’ purchased) to be installed, to read. The passage of these states and ‘carried out safely and fully multi-threaded: that’ means that you can ‘interact with the application (or even suspend it if the download) without having to wait for the download of the entire package. In addition, the media can be packaged into a single file – in this case, however, ‘the download time will be’ more ‘long – or can be downloaded on demand (this is where the media are considered optional to the use of the product).

In App Purchase

Following the latest news ‘introduced by Apple in the rules for approval of applications and content for a fee, we provide as a single solution for the purchase of In App Purchase, namely the ability’ to purchase publications via App Store (remember that these transactions Apple holds 30% of the price charged to the customer).
In any case i3factory and ‘willing to provide solutions to purchase additional wholesale App Purchase, based on external websites provided by the publisher. These solutions are not provided as standard and should be agreed from time to time, this peche ‘need’ from time to time consider all the complex ‘due to the security of transactions and payments made via the web. I3F does not make any kind of support on different forms of payment from the App Store, which will then be dealt with by the customer: in such cases will deal i3F ‘integration within the application only.

Web services for the publisher

The publisher will be ‘able to manage the archive of publications via web interface, based on standard Web 2.0 methodologies. This interface will allow ‘:
– The inclusion of new publications
– The definition of links and multimedia content loading
– Archive management (edit, delete, categorize and tested)
– The ability ‘to temporarily remove from sale certain publications
– Anti-hacking encrypted transactions
Recall that due to the presence of In App Purchase, sale prices and any content will be replicated within the service iTunesConnect Apple. By regulation we can not provide solutions that automate this task.
The entire package will be ‘provided in self-installing package based on PHP and requires the minimum server software support and which we believe is standard in the grand total’ of the equipment required to a publisher.

Con l’introduzione dell’iPad gli sviluppatori si son trovati di fronte al dilemma se sviluppare applicazioni separate per iPhone e iPad oppure rilasciare un’unica app “universale” valida per entrambi i dispositivi.

Indipendentemente da eventuali scelte di marketing, che potrebbero far propendere verso applicazioni separata col vantaggio di poterne vendere due al posto di una, in questo articolo spiegheremo quali sono i vantaggi che si possono ottenere dallo sviluppo di una singola applicazione e alcuni utili suggerimenti su come impostare il lavoro.

Prima di addentrarci nelle caratteristiche di un’applicazione universale, e’ bene rammentare come avviene il caricamento e il lancio di un’applicazione in iOS.

CARICAMENTO E LANCIO DELL’APPLICAZIONE

La figura in basso, tratta come sempre dall’eccellente documentazione Apple fornita col gratuitamente col pacchetto XCode dal sito http://developer.apple.com, mostra qual e’ la sequenza degli eventi che si scatena dal momento in cui l’utente tocca sull’icona dell’applicazione che intende lanciare.

app_life_cycle.jpg

Essendo Objective-C nient’altro che un’estensione del C, un’applicazione parte con l’esecuzione della funzione main(). Utilizzando i template forniti con XCode, la funzione main() contiene il seguente codice, che e’ bene non modificare mai a meno di esigenze molto particolari:

Quel che fa questo codice e’ essenzialmente lanciare la funzione UIApplicationMain() il cui compito consiste innanzitutto nell’instanziare l’oggetto UIApplication, il cui scopo e’ quello di creare e eseguire il motore delle applicazioni iOS, il cosiddetto Event Loop. Non entreremo in quest’articolo nella descrizione dell’Event Loop (lo faremo prossimamente) ma per ora basti sapere che ogni qualvolta l’utente tocca lo schermo del suo dispositivo, il tocco viene catturato dall’Event Loop e smistato alla porzione di codice che si occupera’ di gestirlo.

Dato che l’oggetto UIApplication e’ unico e gestito dal sistema operativo, come facciamo a inserire il nostro codice all’interno di questo meccanismo di start-up dell’applicazione? La risposta e’: tramite l'”application delegate”. Uno dei compiti della funzione UIApplicationMain e’ infatti quello di instanziare il nostro Application Delegate e, durante la fase di lancio, fornirgli il controllo tramite la ben nota chiamata al metodo “application:didFinishLaunchingWithOptions:”. In questo metodo noi inseriremo la nostra logica iniziale, creeremo i nostri view controller e comunque ci consentira’ di far partire la nostra app.

Riassumendo: l’utente tocca l’icona dell’applicazione, il sistema operativo carica l’app e chiama la funzione “main()”, la funzione main chiama la funzione UIApplicationMain, quest’ultima instanzia l’oggetto (unico) UIApplication, il quale avvia l’Event Loop e chiama il suo “delegate” affinche’ quest’ultimo esegua il codice di inizializzazione proprio dell’applicazione.

Quel che resta da definire a questo punto e’: come fa UIApplication a conoscere il suo “delegate”? la risposta e’: questa informazione viene fornita tramite il file Info.plist con la chiave “NSMainNibFile” (indicata in XCode come “Main nib file base name”). Questo nib file, che in genere nei template di XCode si chiama MainWindow.xib, conterra’ un oggetto AppDelegate che sara’ collegato all’outlet “delegate” del File’s Owner, che non e’ nient’altro che il nostro oggetto UIApplication.

Quindi UIApplicationMain svolgera’ oltre al compito sostanziale di instanziare UIApplication anche quello di caricare l’NSMainNibFile e collegare l’instanza di UIApplication (nota come “sharedApplication”) all’AppDelegate.

Questa lunga spiegazione e’ necessaria per capire come far partire un’app universale. In genere i template di XCode per le applicazioni univesali forniscono due diversi NSMainNibFile, uno denominato NSMainNibFile – e usato di default – un altro denominato NSMainNibFile~ipad che, se definito, verra’ caricato nel caso l’app venisse lanciata da un’iPad.

In genere le due chiavi devono puntare a due finestre differenti, dato che sono differenti le dimensioni degli schermi, e il programmatore potrebbe scegliere di instanziare i due UIViewController iniziali, che potrebbero essere profondamente diversi, gia’ nel nib file iniziale. Quel che pero’ fa il template di XCode e’ collegare all’UIApplication delegate outlet due diverse istanze di AppDelegate, denominate rispettivamente AppDelegate_iPhone e AppDelegate_iPad.

Da questo momento in poi le due app prenderanno strade sostanzialmente differenti e sara’ compito del programmatore gestire i due “rami” di codice e cercare di accomunare tra loro quelle porzioni di codice che sono indipendenti dall’interfaccia grafica.

Compito di questo articolo e’ indicare alcune regole pratiche che faciliteranno il compito del programmatore cercando di accomunare il piu’ possibile le porzioni di codice.

APPLICATION DELEGATE UNIFICATO

Il primo approccio rivolto all’unificazione del codice e’ quello di definire un unico application delegate. Come detto in precedenza il template “Universal app” di XCode tende a creare due app delegate praticamente identici, uno per iPad e l’altro per iPhone, e referenziarli in due MainWindow.xib differenti, che e’ bene rimangano tali dato che in questa sede si potrebbero gia’ cominciare a definire le peculiarita’ grafice delle due app (se non altro, le due Window hanno gia’ dimensioni differenti!).

Pertanto il nostro approccio sara’ cancellare uno dei due delegate dal nostro codice (ad esempio AppDelegate_iPad) e sostituire nel MainWindow ad esso associato (nell’esempio: MainWindow_iPad) l’oggetto corrispondente.
In questa maniera l’eventuale caricamento di dati essenziali all’applicazione e il codice richiesto dal protocollo di UIApplicationDelegate sara’ comune nei due casi.

ORGANIZZAZIONE DEL PROGETTO

Per mantenere un certo ordine nell’organizzazione del progetto, e’ buona norma definire tre gruppi all’interno di XCode, denominati Shared, iPhone  e iPad, rispettivamente utilizzati per i file comuni, specifici all’iPhone e specifici all’iPad. Nel nostro caso inseriremo l’AppDelegate comune nel gruppo Shared.

FORCHETTE NEL CODICE

E’ ovvio che tanto piu’ si condivideranno delle porzioni di codice, tanto piu’ sara’ necessario discriminare all’interno di tale codice se si sta eseguendo l’app in un iPad o in un iPhone/iPod Touch, dato che sarebbe presuntuoso pensare di poter scrivere del codice sempre indipendente dal device, specialmente nei View Controller e nelle View.

A tal fine e’ bene aggiungere nel proprio file “Prefix.pch” la definizione seguente:

La funzione isPad(), una volta chiamata, restituira’ YES se l’app viene lanciata in un iPad, NO altrimenti. Questo codice si basa sulla funzione “userInterfaceIdiom” definita nella classe UIDevice. Se questo metodo fornisce in risposta la costante UIUserInterfaceIdiomPad allora siamo in un iPad, altrimenti no. Pero’, dato che questa funzione e’ stata introdotta solamente con la versione 3.2 di iOS, ossia quella che ha introdotto l’iPad (in questo possiamo dire che in Apple non sono stati molto lungimiranti!), per poter garantire la compatibilita’ con versioni precedenti di iOS dobbiamo controllare che questa funzione esista e possa essere chiamata (se non lo facessimo, la nostra app andrebbe miseramente in crash). E’ ovvio che una risposta negativa a questo check implicherebbe una versione di iOS precedente alla 3.2 e quindi il dispositivo non puo’ essere un iPad.

Quindi, tutte le volte che dovremo gestire una qualche eccezione tra i due dispositivi, ci sara’ sufficiente creare una condizione if…else… su isPad() e gestire il comportamento dell’app nei due casi.

IL PATTERN MVC

La programmazione in iOS si basa profondamente sul paradigma Model-View-Controller (MVC).
Questo pattern architetturale prevede che un software sia articolato nelle tre unita’ fondamentali: Modello, Vista e Controllore. Il modello si occupa della gestione dei dati di un’applicazione, le viste gestiscono l’interfaccia utente e la rappresentazione grafica mentre il controller si occupa della logica dell’applicazione mettendo in comunicazione il modello con la vista.

E’ evidente che il Modello prescindera’ sempre dalla natura del dispositivo, in particolare nel nostro caso saremo facilitati dal fatto che sia l’iPhone che l’iPad condividono esattamente le stesse classi e le stesse funzioni per tutto cio’ che riguarda la gestione dei dati (le cosiddette “foundation classes”, ma anche il file system e i vari media framework o l’accesso ai sensori). Pertanto massima cura dovra’ essere data al progetto dell’app affinche’ i moduli comuni non direttamente legati alla rappresentazione grafica siano spostati all’interno del modello.

Talvolta si tende a integrare alcune funzionalita’ all’interno dei View Controller, questo per semplicita’ nella scrittura del codice: dato che il View Controller gestiste il flusso dell’applicazione perche’ non inserire certe funzionalita’, quali ad esempio quelle di networking, all’interno del View Controller? sebbene questo approccio continui ad essere sempre valido, occorre considerare il fatto che in taluni casi non e’ conveniente condividere tra i due dispositivi lo stesso View Controller, e in tal caso tali funzionalita’ andrebbero replicate con evidenti problemi di manutenzione del codice. Per cui si consiglia di spostare quanto piu’ possibile queste operazioni all’interno dei modelli, e sfruttare i meccanismi di comunicazione di Objective-C (protocolli, KVO, notifiche) per far colloquiare i modelli coi controller.

VISTE CONDIVISE

Condividere le viste, ossia le varie UIView e tutte le loro sottoclassi, e’ compito in genere abbastanza arduo. In genere si sconsiglia di costruire una vista per iPad partendo da quella per iPhone e “ingigantendola” di conseguenza: l’effetto e’ abbastanza sgradevole e comunque non renderebbe merito del magnifico display dell’iPad.

Quindi il consiglio che ci sentiamo di dare e’ quello di cercare di studiare l’interfaccia grafica tenendo conto delle caratteristiche dei due dispositivi e solamente in seguito pensare se e come unificare parti di essa. Vi saranno sicuramente dei casi in cui le viste possono essere condivise: si pensi ad esempio alle piccole miniature che si vedono in fondo al lettore di PDF integrato nell’app iBooks. Esse possono essere costruite (diciamo “possono” poiche’ non sappiamo se i progettisti di Apple abbiano seguito lo stesso approccio) usando un’unica vista che calcolando le dimensioni dello schermo sara’ in grado di conoscere il numero di miniature che potranno essere posizionate. Inoltre le stesse dimensioni delle miniature, dato che saranno diverse fra i due schermi, potranno essere discriminate grazie all’uso della nostra funzione isPad(). Quindi senza ombra di dubbio un’eventuale classe “ThumbnailsView” potra’ essere inserita nel gruppo Shared del nostro progetto.

VIEW CONTROLLER

Non c’e’ ombra di dubbio che i View Controller, rappresentando la logica dell’applicazione e quindi la parte piu’ complessa, richiedono un’attenta valutazione prima di effettuare la scelta della condivisione o no. Infatti tenere separati i View Controller tra i due rami del progetto (iPhone e iPad) puo’ implicare una quantita’ eccessiva di codice ridondante. Ma allo stesso tempo un’eccessiva unificazione, soprattutto per quei View Controller che piloteranno viste molto differenti per i due schermi, potrebbe comportare a una tale ramificazione intra-codice e un numero cosi’ grande di biforcazioni da rendere il codice difficilmente leggibile.

Si tenga conto inoltre che certi elementi molto comuni nell’iPhone, come gli UITabController, sono raramente utilizzati nell’iPad, e quindi questo gia’ in partenza implica una netta separazione delle due porzioni di codice. Chiaramente gli UITabController istanziano a loro volta dei UIViewController che potrebbero essere unificati tra loro. Si pensi per esempio al caso di una mappa: i percorsi per arrivare alla mappa possono essere molto diversi fra due dispositivi, ma alla fine la vista, a parte alcune differenze, sara’ sostanzialmente la stessa: una mappa a tutto schermo con una toolbar o navigation bar per accedere ad altre funzioni.

Il prossimo paragrafo, sui Popover, fornira’ una soluzione molto comoda per unificare il maggior numero di View Controller possibile.

I POPOVER

L’iPad e’ dotato di un insieme di view controller unici, quali gli UISplitViewController e gli UIPopoverController.

Questi ultimi sono quei “fumetti” che appaiono di tanto in tanto nell’interfaccia delle app per iPad e che consentono di accedere ad alcune funzionalita’ extra dell’applicazione. Apple suggerisce nelle sue Human Interface Guidelines (HIG) di utilizzare questo controller tutte le volte che si vuole accedere ad alcuni sezioni dell’app senza distogliere l’utente dalla pagina principale. Questo approccio e’ molto diverso dall’iPhone, in cui il progresso all’interno di un’applicazione avviene tipicamente tramite le “sliding windows”, ossia le finestre scorrevoli da destra verso sinistra e gestite da un UINavigationController. A ragione, i redattori dell’HIG ritengono che far scorrere tali finestre in un’app per iPad non sarebbe il massimo, poiche’ date le loro dimensioni il tutto  risulterebbe essere oneroso per il processore (rendering grafico) e visivamente pesante. In questo la soluzione suggerita e’: aprire un popover all’interno della finestra e inserire il view controller al suo interno. E’ vero che nel tempo gli sviluppatori si sono sbizzarriti nell’ottenere soluzioni alternative a questo approccio, ad esempio con viste scorrevoli parzialmente, ma l’approccio dei popover risulta essere il piu’ immediato, abbastanza elegante e comunque perfettamente in linea con le HIG.

I popover possono essere sfruttati per unificare i view controller. Si consideri ad esempio il caso di una funzione “Impostazioni” che si vuole introdurre nell’app. Ovviamente date le differenti dimensioni dello schermo, per l’iPhone si potrebbe impostare una classica vista 320×480 contenente una tabella e mostrata per scorrimento. Nell’iPad occorrerebbe costruire una macro vista 768×1024 da mostrare in qualche maniera e magari difficile da riempire (e quindi da rendere esteticamente gradevole) soprattutto se il numero di impostazioni risultasse ridotto. In questo caso la soluzione migliore sarebbe creare un popover e inserire al suo interno una finestra di dimensioni, guarda caso, 320×480.

Pertanto avremmo lo stesso ImpostazioniViewController, studiato per un iPhone, e le differenze fra i due device sarebbero:

  1. chiamata via NavigationController (“push”) per iPhone, via PopoverController per iPad
  2. presenza della navigation bar con “Back” button per iPhone, presenza opzionale della navigation bar ma senza “Back” button

LE MODAL VIEW

Le modal view sono tradizionalmente utilizzate nell’iPhone per interrompere il flusso dell’applicazione e porre l’utente di fronte ad un’attivita’ che richiede la sua immediata e unica attenzione. Con l’iPad tali viste sono state estese per consentire un approccio diverso all’interazione con l’utente, ed e’ grazie a tale scelta che possiamo pensare ad un’ulteriore forma di condivisione del codice.

La differenza sostanziale e’ che mentre nell’iPhone la modal view appare sempre e soltanto dal basso verso l’alto a riempire tutto lo schermo, nell’iPad e’ possibile scegliere tra diversi approcci. Nel nostro caso l’approccio piu’ confacente alla condivisione e’ quello di mostrare la vista modale in modalita’ “Form Sheet”, con la quale la modal view appare al centro dello schermo, con dimensioni ridotte, con un effetto di “dimming” nelle aree lasciate scoperte.

In questo caso la nostra vista comune non avra’ le stesse dimensioni (nell’iPad e’ comunque piu’ grande) per cui dovremo gestirla affinche’ il ridimensionamento risulti adeguato. Ad ogni modo queste eccezioni possono essere facilmente gestite nel nostro codice tramite l’uso della funzione isPad() senza dover necessariamente ricorrere all’uso di View Controller separati.

CONCLUSIONI

Senza dubbio scrivere una buona app universale rapprsenta sempre una sfida. Ma se non altro i vantaggi che si ottengono in riutilizzabilita’ e manutenzione valgono lo sforzo. Inoltre l’impegno in piu’ richiesto per la definizione di un’architettura che favorisca la condivisione del codice verra’ premiato da un software che risultera’ essere piu’ ordinato, leggibile e estendibile.

Chiaramente questo obiettivo puo’ non essere facilmente raggiungibile, e invitiamo gli sviluppatori a non cimentarsi in imprese disperate per rendere universali tutte le app, soprattutto in quei casi in cui le due interfacce sono profondamente diverse, oppure il contributo dovuto ai modelli e’ insignificante oppure si proviene da un’app iPhone gia’ esistente e progettata con tutt’altri criteri.

PhotoGrab

Screenshot di PhotoGrap per iPad

Abbiamo avuto il piacere di provare un’ottima app per iPad da poco presente sull’App Store: PhotoGrab (iTunes link, questa invece e’ la pagina di supporto).

Questa app permette di navigare entro la propria libreria di iPhoto senza alcun software aggiuntivo e senza dover scaricare fisicamente le vostre foto all’interno dell’iPad.

Questo e’ possibile poiche’ se si attiva la funzione “sharing” (“condivisione”) su iPhoto e ci si trova sulla stessa rete locale, allora l’app sara’ in grado di consultare l’elenco delle foto, con tanto di album, e lanciare degli slideshow.

Con essa quindi non solo potrete guardare le vostre foto comodamente seduti in poltrona oppure sdraiati nel letto, ma se andate a casa di amici vi sara’ sufficiente portare il vostro iPad e connettervi alla loro libreria.

L’app permette oltre al semplice browsing, anche di lanciare degli slideshow oppure di selezionare alcune foto per essere scaricate nella memoria dell’iPad.

La navigazione e’ molto veloce e di fatto lo slideshow non presenta ritardi dovuti a caricamenti. Il comportamento e’ fluido, l’interfaccia intuitiva e l’app stabile. Lo sviluppatore ci ha comunicato che e’ attualmente in revisione da parte di Apple un aggiornamento dell’app che portera’ diverse bug fix e alcune nuove potenzialita’.

Attualmente il prezzo sull’App Store italiano si aggira sui 5 Eur. E’ un prezzo sopra la media ma la comodita’ che porta questa app li vale senza dubbio. Inoltre lo sviluppatore, Matt Long, e’ molto attivo nella comunita’ dei developer su Apple e il suo lavoro di divulgatore e blogger e’ senz’altro da apprezzare.

DeTelegraaf for iPad

DeTelegraaf for iPad

Finalmente l’iPad e’ sbarcato anche in Italia, e l’App Store si e’ subito popolato delle applicazioni delle maggiori testate giornalistiche italiane, oltre che di qualche rivista.
Provate a scaricarne qualcuna, scegliendo tra Repubblica, Il Corriere della Sera, La Gazzetta dello Sport, Il Foglio, L’Unione Sarda, Mac Magazine (ma ve ne sono altre, che non abbiamo scaricato e provato dato che ci son bastati gli snapshot per capire dove saremmo andati a parare): sono tutte esattamente uguali (con La Repubblica che si distingue leggermente dal gruppo).
Non c’e’ nulla di male a che le applicazioni siano uguali, se non fosse che esse sono ben lontane da quel che dovrebbe essere un’app per iPad.
Escludiamo per ora dal gruppo l’app di Repubblica, che merita una trattazione a parte (e una stelletta in piu’ nel rating Apple, cosi’ permettendo di elevarsi dal mediocre 2 stelle alla sufficienza). Le altre si mostrano al pubblico come dei visualizzatori di PDF o meglio di PNG. Optiamo per la seconda scelta, dato che sono app abbastanza veloci (unico vantaggio) ma che di fatto si comportano come dei visualizzatori di un’unica grande immagine sulla quale sono impressionate tutte le pagine del giornale. Quindi la meravigliosa esperienza sensoriale che avrete leggendo questi giornali online sara’ semplicemente lo scroll e lo zoom lungo questo enorme foglio elettronico.
Un po’ poco, soprattutto se si confrontano queste app con quella del New York Times Editor’s Choice, quest’ultima si’ che rende giustizia all’iPad.
Si distingue leggermente La Repubblica +, forse un po’ piu’ lenta nel caricamento, ma che almeno permette di aprire l’articolo nella sua interezza (ahime’ senza immagini, ahime’ senza i link nel caso di rimando alla prima pagina, ahime’ con la disposizione a due colonne e scroll verticale, quando invece la lettura sarebbe facilitata se impostata a libro, si vedano nuovamente NYT o l’app iBooks) e con qualche contenuto multimediale (una bella galleria fotografica, un video).
A nostro avviso queste app cosi’ fatte avranno vita difficile quando i contenuti diventeranno a pagamento, cosa necessaria affinche’ il business dell’editoria sull’iPad diventi profittevole per gli editori. Attualmente forse il business l’hanno fatto i produttori di questa app (trattasi di una start-up italo-californiana) ma di certo non hanno un reso un gran servizio ne’ all’iPad ne’ alle testate nostrane. Questi ultimi da biasimare certamente, poiche’ presi dalla fretta di uscire per primi e del tutto incapaci di capire le potenzialita’ dell’iPad.
Il nostro auspicio e’ che queste applicazioni vengano aggiornate (oserei dire: rivoluzionate) il piu’ presto possibile e ben vengano le fotocopie, ma che non si presentino ancora sullo schermo dell’iPad le fotocopie delle pagine del giornale.

Il Foglio su iPad

Il Foglio su iPad

Un giroscopio all'interno di un telefonino smartphone

Un giroscopio all'interno di un telefonino smartphone

Si rumoreggia da tempo dell’imminente rilascio di uno smartphone dotato di un giroscopio costruito con tecnologia MEMS. Le speculazioni sul produttore del telefono vanno da Apple (con l’imminente iPhone 4G) a HTC e a ST-Ericsson, anche se il segreto e’ cosi’ ben mantenuto da rendere il tutto ancora molto incerto. Quel che e’ abbastanza chiaro invece e’ che entro il 2012 tutti i maggiori produttori di smartphone avranno integrato i giroscopi nei loro dispositivi, questo perche’ questi sensori permettono di introdurre nuove modalita’ nell’interazione utente-dispositivo, in particolare nei settori del gioco e della navigazione in interni (dove il segnale GPS e’ scarso o inesistente) oppure con l’invenzione di nuovi gesti per controllare le funzioni del telefono.
Attualmente una prova pratica del sensore a sei assi (tre assi forniti dall’accelerometro – ormai di serie su tutti gli smartphone – e tre dal giroscopio) e’ data dal controller MotionPlus fornito con la console Wii della Nintendo, permettendo un riconoscimento del movimento piu’ sensibile e preciso che con i soli 3 assi forniti dall’accelerometro, questo perche’ l’accelerometro – che rileva il movimento lineare – da solo non e’ in grado di stabilire la differenza tra gravita’ e movimento, mentre con l’ausilio del giroscopio – che rileva il momento angolare – e’ possibile distinguere le due componenti permettendo una risposta ai movimenti dell’interfaccia utente piu’ sensibile e priva di artifatti. Un altro uso molto comune dei giroscopi in tecnologia MEMS lo si trova nei dispositivi di stabilizzazione di molte macchine fotografiche reflex o cineprese digitali, dato che e’ possibile controbilanciare l’effetto del movimento della macchina tramite uno spostamento in direzione opposta del sensore.
Senza dubbio l’esperienza acquisita nel campo della fotografia verra’ immediatamente riversata, come prima applicazione, negli smartphone, in particolare in quei dispositivi che consentono scatti con risoluzioni superiori ai 5 megapixel, particolarmente sensibili alla stabilita’ dell’immagine.
Nel video un’interessante demo delle applicazioni del chip MPU-3000 di InvenSense sugli smartphone.

By continuing to use the site, you agree to the use of cookies. more information

The cookie settings on this website are set to "allow cookies" to give you the best browsing experience possible. If you continue to use this website without changing your cookie settings or you click "Accept" below then you are consenting to this.

Close