i3Factory World

Your Iphone, iPad & Android Application Factory

Browsing Posts tagged tutorial

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

Icona Newsstand Icon

The iOS5 revolution

A lot of water has flowed under the bridge since our first part of this tutorial. We’re sorry for the delay, but at the time of writing we were aware of the new featured introduced with iOS5 but we were still under NDA and not authorized to disclose anything about the SDK. Finally we’re now able to provide our example magazine app with the double iOS4/iOS5 compatibility.

 

I will not spend my time explaining all the Newsstand features, all in all we’re creating a magazine app here and the Newsstand implementation details go beyond our original purposes. I have written a two parts tutorial in my personal blog that you can read here and here, and that covers all Newsstand aspects and the required (by Apple reviewers) subscriptions. In short, Newsstand is both a new way to present magazines in the iPad and iPhones, where the original icon is replaced by a cover of the magazine or newspaper and is placed under a special group dedicated to collect all Newsstand icons installed by the user. For the developer it is also a framework, called Newsstand Kit, that introduces a new methodology to organize, download and install the app content.

The example app

The screenshot shows the final appearance of the app. A set of nine magazines with their nine fruity cover. You can download a magazine, see the progress bar change while the operation is in progress and finally read it. The other screenshot shows the appearance in the Newsstand. The typical icon has been replaced by a magazine-like representation inside the Newsstand group. The same, if run on an iPad with iOS4 installed will show the classical icon instead.



The full app code is available on GitHub. Don’t consider it as a production code, so please don’t try to reuse as it is with your clients unless you have tested it in all possible corner cases. Anyway it can be considered as a valid starting point for real apps. Essentially the principles behind the code architecture have been depicted in Part I of this tutorial; if you haven’t read it yet I recommend to jump to that article before entering in this one, at least to understand the key components of such an app. The driving aim is to keep the issues management (controller, don’t confuse it with “view controller”) separated as much as possible from the UI concerns. This means that in theory the whole code behind the Store Manager and Issue Models part can be reused even in a Mac OSX app, as they relationship with UI stuff is minimum.

I have setup this app using the basic single window Xcode template and then added the two main components in the application delegate startup method:

– (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// here we create the “Store” instance
_store = [[Store alloc] init];
[_store startup];self.shelf = [[[ShelfViewController alloc] initWithNibName:nil bundle:nil] autorelease];
_shelf.store=_store;self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease];
self.window.rootViewController = _shelf;
[self.window makeKeyAndVisible];
return YES;
}

The two components are:

  • the Store class, which represents the Store Manager block in the app architecture; this Class is a subclass of NSObject and is not linked to the user interface.
  • the ShelfViewController represents the UI of the application. It is linked to the store using a specific property. Note that the controller will not access the store exposed properties, but will get the needed information via a simple API. We could have used a delegate pattern instead, but as both blocks are essentially custom blocks in a custom app, there is not much need to define a protocol for their interaction. We can say that the controller part has been split in two parts, one for the UI, the shelf, and one for the back-end, the Store. Their link is such that the Shelf depends on the Store (strict link), not viceversa. Any Store to Shelf communication is done using lazy methods, based on notifications.

The app has then been integrated with the new Newsstand requirements in the info plist. Again have a look at Apple docs or my tutorial for a detailed guide.

Models and Controllers

We have one model in this app, it is the Issue model which represents a single magazine issue in the store or in the user library. There is also one controller, which is the Store. As I said this controller is not a UI component but the app back-end is self sufficient with these two components. In theory we can retrieve the store status and download magazines even without any user interface. This is a basic concept in magazine apps, where many events happen in the background are not related to direct user interaction: this means that the app should be capable to perform several tasks even if the user interface has not been loaded at all.

The role of the Issue class is to represent all relevant magazine properties, that is a unique identifier, a title, a release date, a link to the cover image, obviously the link (URL) to the content (which can be a pdf file, an epub file or a zip file for complex packages). In particular the unique identifier is a piece of data that must be maintained along the full life of the issue: it is the only way to uniquely identify a particular issue, independently of localization issues (e.g. the title can change in different countries, but not the unique ID). Besides it is the link to the Newsstand way to identify issues (the NKIssue‘s name field) and it can be also used to link the product with the App Store if we’re going to implement In App Purchases.

Other than this role, the Issue class will have a central role in the downloading of a magazine. Essentially the Store class will schedule a download but it will be monitored (progress) and then finished (effective magazine installation) by the Issue class.

Finally this class has limited capabilities to represent a magazine already downloaded and available in the user library. For this we provide the simple isIssueAvailableForRead that will be used by the user interface to know which action is possible with an issue (read or download) and eventually display the contents.

The Store class is the app controller. It is initialized at the very beginning of the app and its instance is never released. Besides immediately after initialization this class will fetch the store contents from the publisher server. In the example we decided to implement the store contents as a simple property list, and we instruct the store to retrieve this property list, decode it and create the Issue objects, and finally download their cover images. All this is done asynchronously using the Grand Central Dispatch and at the end a notification will be sent to inform all interested objects (in particular the view controller) that the store contents are ready and the UI can be safely displayed. Note that the store status is represented by a property status. We have overridden its setter method to post a notification that informs any interested observer of the new state. For simplicity we decided to limit the possible statuses to “not initialized”, “downloading”, “ready” and “error”. A more complex app can introduce extra states if needed. Finally as a fallback in case of missing connection (the user must be able to access his/her content even if not connected to the Internet) we reload a locally saved copy of the store.

The central piece of the code is in the downloadStoreIssues method of the class, that we report below:

-(void)downloadStoreIssues {
self.status=StoreStatusDownloading;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH,0), ^{
NSArray *_list = [[NSArray alloc] initWithContentsOfURL:[NSURL URLWithString:@”http://www.viggiosoft.com/media/data/iosblog/magazine/store.plist”]];
if(!_list) {
// let’s try to retrieve it locally
_list = [[NSArray alloc] initWithContentsOfURL:[self fileURLOfCachedStoreFile]];
}
if(_list) {
// now creating all issues and storing in the storeIssues array
[_list enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
NSDictionary *issueDictionary = (NSDictionary *)obj;
Issue *anIssue = [[Issue alloc] init];
anIssue.issueID=[issueDictionary objectForKey:@”ID”];
anIssue.title=[issueDictionary objectForKey:@”Title”];
anIssue.releaseDate=[issueDictionary objectForKey:@”Release date”];
anIssue.coverURL=[issueDictionary objectForKey:@”Cover URL”];
anIssue.downloadURL=[issueDictionary objectForKey:@”Download URL”];
anIssue.free=[(NSNumber *)[issueDictionary objectForKey:@”Free”] boolValue];
[anIssue addInNewsstand];
[storeIssues addObject:anIssue];
[anIssue release];
// dispatch cover loading
if(![anIssue coverImage]) {
dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSData *imgData = [NSData dataWithContentsOfURL:[NSURL URLWithString:anIssue.coverURL]];
if(imgData) {
[imgData writeToURL:[anIssue.contentURL URLByAppendingPathComponent:@”cover.png”] atomically:YES];
}
});
}
}];
// let’s save the file locally
[_list writeToURL:[self fileURLOfCachedStoreFile] atomically:YES];
[_list release];
self.status=StoreStatusReady;
} else {
ELog(@”Store download failed.”);
storeIssues = nil;
self.status=StoreStatusError;
}
});
}

Note that in the code above for simplicity I have queued the cover downloading immediately after the property list download. Probably this is not the best way to do this, as we are adding extra networking steps before presenting the app status, with possible delays if network conditions are not good. We can do this in an example where we know that the number of issues is limited, probably in a real app it makes more sense to do this in a background job and then notify the UI to update its status every time a new cover is ready to be displayed.

The view controller

The user interface will wake up immediately, and will change its appearance based on the notifications coming from the store. When the notification center will deliver the store “I’m ready” message to the UI, this one will load all UI issue representations (the CoverView class in the code, a basic view with really minimum application logic) and show the shelf to the user.

At this point the app terminated its back-end processing and is waiting for user commands. Here we have two possibilities:

  • A magazine has been downloaded, the user will see a “READ” button, clicking on it he will be able to read it. In our example all our content is made of pdf files, we decided to use the Quick Look framework in iOS, it is enough for our purposes.
  • A magazine has not been downloaded yet, the user will see a “DOWNLOAD” button, clicking on it the download will start and we’ll show a progress bar. At the end we need to replace the button label and hide the progress. We’ll see this part in more detail.

The view controller is dependent on the store. In order to get the issues information (number of issues, detail of each issue) it will not access directly to the store properties but will use a very simple API, made of three methods:

/* “numberOfIssues” is used to retrieve the number of issues in the store */
-(NSInteger)numberOfStoreIssues;/* “issueAtIndex:” retrieves the issue at the given index */
-(Issue *)issueAtIndex:(NSInteger)index;/* “issueWithID:” retrieves the issue with the given ID */
-(Issue *)issueWithID:(NSString *)issueID;

Based on this information, and on the Issue properties, it will create the Issue view representation (CoverView) and place it on the screen.

Downloading a magazine

In a magazine app there are three critical sections: retrieve and display of the store contents, download the contents and finally read them (a good PDF or epub reader is mandatory in an app like that; it’s not the purpose of this tutorial, aimed to explain the architecture and the techniques needed to make such an app, but it is a relevant part in the user experience).

My approach in the magazine download is that the user must be completely free to do any action and such actions shouldn’t interfere with the result of the download. Now many apps take a shortcut: they put a spinner in the center of the screen, then block any user interaction with the UI objects behind the spinner and ask the user to wait. It’s easy to do this, but it is not the better user experience. While waiting the user can read another issue, can navigate inside the store or decide to switch temporarily to another application or finally can temporarily lose the network. Newsstand Kit provides a system level methodology that simplifies (in some cases) the developer life but at the same time removes any excuse on not doing a good job in term of user experience.

As soon as the user triggers a new download, the view controller will send its download request to the store class. In the code below you can see the scheduleDownloadOfIssue: code. This method will prepare the network request and will send it in the background. Note how we split the app behavior based on the operating system version we’re on. If it is iOS5 then we must use the Newsstand approach – where the downloading will be managed by the system in a Newsstand queue – , if we are in iOS4 we’ll follow a classic methodology based on NSOperation: in this case I missed for simplicity to get the content length of the content to be downloaded, so in the iOS4 case the progress bar will not be displayed. This information instead if provided “for free” by Newsstand.

-(void)scheduleDownloadOfIssue:(Issue *)issueToDownload {
NSString *downloadURL = [issueToDownload downloadURL];
NSURLRequest *downloadRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:downloadURL]];
if(isOS5()) {
// iOS5 : use Newsstand
NKIssue *nkIssue = [issueToDownload newsstandIssue];
NKAssetDownload *assetDownload = [nkIssue addAssetWithRequest:downloadRequest];
[assetDownload downloadWithDelegate:issueToDownload];
} else {
// iOS4 : use NSOperation
NSURLConnection *conn = [NSURLConnection connectionWithRequest:downloadRequest delegate:issueToDownload];
NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(startDownload:) object:conn];
if(!downloadQueue) {
downloadQueue = [[NSOperationQueue alloc] init];
downloadQueue.maxConcurrentOperationCount=1;
}
[downloadQueue addOperation:op];
[downloadQueue setSuspended:NO];
}
}// iOS4 only
-(void)startDownload:(id)obj {
NSURLConnection *conn = (NSURLConnection *)obj;
[conn start];
}

In the two cases I’d like to emphasize the fact that the Store initiates the download operation but then it will delegate any further processing to the interested part, the is the Issue to be downloaded. So it will be the Issue class that will monitor the download and then will finalize it.

The Issue class will behave as delegate of the download operation triggered by the Store class. The delegate protocol changes if you’re using Newsstand or not. In the former case you will need to be compliant with the NSURLConnectionDownloadDelegate protocol while in the latter the protocol to be respected will be the un-documented (or: documented in the header files only!) NSURLConnectionDataDelegate which is a “spin-off” of the classic NSURLConnectionDelegate. The difference between the two protocols in that one will write in the file system, the other in memory. Again don’t use the “data” approach for production purposes, here we’re loading the whole content in memory and then saving on disk when the content has been completely downloaded: really not the best approach if your content is hundred of megabytes, your app will certainly crash.

In order to visually track the progress and then update the user interface, we decided, being coherent of our principles, to keep the application store controller independent on any UI choice. To do this we make use of KVO and Notifications. Essentially when the view controller starts a download, it will set itself and the magazine view as notification observers of the download result, in order to update its status as soon the download terminates (with success or with error):

-(void)downloadIssue:(Issue *)issue updateCover:(CoverView *)cover {
cover.progress.alpha=1.0;
cover.button.alpha=0.0;
[issue addObserver:cover forKeyPath:@”downloadProgress” options:NSKeyValueObservingOptionNew context:NULL];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(issueDidEndDownload:) name:ISSUE_END_OF_DOWNLOAD_NOTIFICATION object:issue];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(issueDidFailDownload:) name:ISSUE_FAILED_DOWNLOAD_NOTIFICATION object:issue];
[[NSNotificationCenter defaultCenter] addObserver:cover selector:@selector(issueDidEndDownload:) name:ISSUE_END_OF_DOWNLOAD_NOTIFICATION object:issue];
[[NSNotificationCenter defaultCenter] addObserver:cover selector:@selector(issueDidFailDownload:) name:ISSUE_FAILED_DOWNLOAD_NOTIFICATION object:issue];
[_store scheduleDownloadOfIssue:issue];
}

Both the cover view and the view controller will need to unregister from the notification center as soon as the download operation terminates. Besides to allow the cover view to keep track of the progress status, as the download starts it will be registered as a KVO observer of the downloadProgress property of the Issue. This means that for each change of this property, that will occur during the downloading phase, the cover view will be informed of the change to update the progress bar (so we have here a separation again of the back-end property, the download progress, from the UI, the UIProgressBar). The cover view will unregister itself as soon as download terminates.

When the download terminates, the Issue instance will copy the downloaded content to the final destination. In the Newsstand framework this destination is specified by the system, in iOS4 will copy it in the Caches directory to be compliant with iCloud requirements (yes, iOS4 doesn’t work with iCloud but our app is the same for both generations of the OS, so we must be good citizens in any case).

In the Newsstand case we’ll also need to update the cover image for the Newsstand icon. We’ll do it with a simple command inside the code that manages the download termination (at the end we also send the end of download notification we mentioned previously):

-(void)connectionDidFinishDownloading:(NSURLConnection *)connection destinationURL:(NSURL *)destinationURL {
// copy the file to the destination directory
NSURL *finalURL = [[self contentURL] URLByAppendingPathComponent:@”magazine.pdf”];
ELog(@”Copying item from %@ to %@”,destinationURL,finalURL);
[[NSFileManager defaultManager] copyItemAtURL:destinationURL toURL:finalURL error:NULL];
[[NSFileManager defaultManager] removeItemAtURL:destinationURL error:NULL];
// update Newsstand icon
[[UIApplication sharedApplication] setNewsstandIconImage:[self coverImage]];
// post notification
[self sendEndOfDownloadNotification];
}

Conclusions

This is the conclusion of this tutorial. We decided after a long interval to make an effort to propose a complete app valid for both iOS4 and iOS5 environments. In the code you will find more interesting stuff, e.g. a “hook” to the Store Kit: this is a typical issue not considered by developers when they make applications that sell magazines. In such case storing the issue price in the publisher server is not of help as we must retrieve the pricing information from the only relevant source, that is the App Store. To do this our app must – asynchronously – query the iTunes store for the prices of all the publications and then display them once retrieved. I’m not adding this extra step in the code, just the hook as placeholder for future extensions, but of course if required by our readers I can extend this tutorial further. I would appreciate any suggestion and any contribution in the GitHub hosted code, I would like to see the example code provided with this tutorial as a good skeleton for new apps. If you didn’t enjoy this article, at least you can enjoy the literature classics (and a Django manual…) that I used to create my PDF files.

Carlo Vigiani

Leggendo un’interessantissimo articolo di Marc Edwards su Smashing Magazine ho deciso di citarne e tradurne le parti piu’ significative dato che spesso mi trovo a dover parlare con i clienti della effettiva necessità di progettare le proprie app tenendo conto che le tecnologie audiovisive sono in costante evoluzione.
L’iPhone 4 dispone di una risoluzione di gran lunga superiore (614.400 pixel) rispetto ai modelli iPhone precedenti e ha di fatto un display quadruplicato di 153.600-pixel  anche se lo schermo ha le stesse dimensioni fisiche, così quei punti in più sono utili per ulteriori dettagli – due volte il dettaglio in orizzontale, e due volte in verticale.

Lo Scaling dell’interfaccia utente (UI) per i display a  maggiore dettaglio – o piu’ spesso un aumento della dimensione display – non è un problema nuovo. Le interfacce che sono in grado di scalare sono dette: ” indipendenti dalla risoluzione” (resolution independence).

In un recente articolo, Neven Mrgan ha descritto l’ indipendenza dalla risoluzione: “RI [risoluzione indipendente] come obiettivo, e non come una tecnica. Significa avere contenuti e risorse che potranno essere significative in dimensioni diverse. “Se è un obiettivo, non una tecnica specifica, quindi quali tecniche esistono? Come Apple ha risolto il problema di Ios?

Layout Fluidi (Fluid Layout)

Mentre le applicazioni che sfruttano elementi  nativi dell’interfaccia utente di Apple richiedono molto meno lavoro quando le si progetta per la visualizzazione Retina, ciò che ci interessa altamente personalizzati, applicazioni grafiche-driven che necessitano di una buona dose di lavoro per sfruttare al massimo le Retina display.

Sebbene non sia strettamente indipendente dalla risoluzione, l’utilizzo di un layout liquido può aiutare a gestire un app per approfittare di uno schermo più grande  operando sul padding o modificando il layout dinamico. Moltissime applicazioni Mac, Windows e  per Linux utilizzano questo metodo, cosi’ come fanno alcuni siti web.

Questo è parzialmente cio’ che Apple ha gestito con la differenza di risoluzione da iPhone a iPad – molti elementi dell’interfaccia utente hanno la stessa dimensione in pixel, ma “padded” in modo da utilizzare la parte reale dello schermo. La barra di stato è un buon esempio di questo metodo. Funziona perché la densità dei pixel del 3GS iPhone e iPad sono simili (163ppi vs 132  ppi).

Layout fluido e’ utile o quando il cambiamento di densità di pixel è minore, ma non sono di aiuto quando si passa da un display iOS non-Retina ad uno Retina (163 ppi a 326 ppi). L’immagine seguente mostra cosa sarebbe successo se una applicazione per iPhone è stata semplicemente ridimensionata per soddisfare i display ad alta risoluzione di iPhone 4. Pulsanti e zone di touch sarebbero della stessa dimensione in pixel, ma la metà in termini di dimensioni fisiche a causa della maggiore densità di pixel, rendendo le cose più difficili da leggere e da toccare.

Just-in-time Resolution Independence

Un altro approccio per la gestione di differenti risoluzioni e densità dei pixel è di disegnare tutto utilizzando il codice o le immagini vettoriali (come i PDF), in fase di runtime. L’utilizzo del vettoriale Ti permette di designare o scrivere codice una sola volta, e per display a qualsiasi risoluzione, anche a scala frazionata.

Purtroppo, utilizzando immagini vettoriali si tende ad essere più avidi di risorse e mancanza di controllo livello di pixel. L’aumento delle risorse non può essere un problema per un desktop, ma è un problema rilevante per un sistema operativo mobile. La mancanza di controllo del livello di pixel è un vero e proprio problema per gli elementi più piccoli . Modificando delle dimensioni di un’icona di un solo pixel  si perde chiarezza.

Neven Mrgan sottolinea in suo famoso articolo che abbiamo gia citato  che:

“… Semplicemente non è possibile creare ottimi, icone dettagliate che possono essere arbitrariamente ridotte a dimensioni molto piccole, mantenendo la chiarezza. Piccole icone sono delle caricature: esagerano alcune caratteristiche, basta allineare le forme su una griglia. Anche se tutte le icone possono essere eseguite come vettori, la dimensione più grande non sarebbe mai scalata in basso bene “.

Anche se qui si sta parlando esclusivamente di icone, descrizione di Neven Mrgan è adatta per la maggior parte degli elementi dell’interfaccia utente. Le decisioni di immagini in scala sono un lavoro da creativo, non da meccanico. Gli elementi vettoriali non sono adatti per tutte le risoluzioni, se si da valore alla qualità del risultato.

Ahead-of-time Resolution Independence

I migliori risultati di qualità – e il metodo che Apple ha scelto per la transizione dal iPhone 3G a iPhone 4  – proviene da immagini pre-renderizzate, costruite per i dispositivi particolari, su risoluzioni specifiche: sviluppate  progetti su misura per ogni formato richiesto, se volete. E ‘più lavoro, ma le immagini in pre-rendering assicurno sempre il miglior risultato possibile.

Apple ha scelto di raddoppiare esattamente la risoluzione del iPhone 3GS  per l’iPhone 4, rendendo il fattore scala ancora più semplice (diverso dall’ approccio di Google e Microsoft – si noti che questo articolo non è rilevante per l’ultima versione di Microsoft sistema operativo mobile – dimostrando ancora una volta che controllare l’intero stack ha dei vantaggi enormi).


Attualmente, ci sono tre risoluzioni IOS:

* 320 × 480 (iPhone / iPod touch)
* 640 × 960 (4 iPhone e iPod con display a Retina)
* 768 × 1024 / 1024 × 768 (iPad)

In pochi anni, sembra altamente probabile che la line-up saranno:

* 640 × 960 (iPhone / iPod touch con display retina)
* 1536 × 2048 / 2048 × 1536 (iPad con display retina)
* Qualche tipo di periferica IOS desktop iMac dimensioni con un display Retina

Ci sono differenze significative tra la progettazione di applicazioni per iPhone  e per iPad, il layout delle app deve essere in parte o completamente rielaborato  in ogni caso – non si può semplicemente scalare per  iPad la vostra app per iPhone , e aspettarsi che tutto funzioni bene o sche abbia un usabiliyà su iPad. La differenza di dimensione dello schermo e il fattore di forma significano che ogni dispositivo dovrebbe essere trattato separatamente. La dimensione del dell’iPad permette di visualizzare più informazioni sullo schermo, mentre le applicazioni per iPhone in genere hanno bisogno di essere più profonde, con diversi livelli per poter fruire bene di tutte le informazioni.

Questa secondo articolo dedicato alla progettazione del layout per il retina display proviene sempre dalla lettura e traduzione dell’interessante articolo articolo di Marc Edwards su su Smashing Magazine

Costruire progetti scalabili

Costruire applicazioni per l’iPhone 4 display Retina prevede la creazione di due serie di immagini, un a 163 ppi e un altr a 326 ppi. Le immagini A 326  ppi dovra’ includere @ 2x alla fine del nome del file, per indicare che essi, sono al doppio della risoluzione.

Quando si tratta di costruire elementi di un interfaccia utente in grado di scalare facilmente con Adobe Photoshop, le bitmap sono il tuo nemico, perché “pixelizzano” o si sfocano quando se ne modifica la scala. La soluzione è quella di creare un colore a tinta unita, modelli o layers con maschere vettoriali (solo assicurarsi di avere “snap to pixel” attivo quando possibile).  A volte, il passaggio a tutti i vettori non ha vantaggi significativi.

Prima che qualcuno ne parli non si stanno suggerendo dei metodi nuovi, e sicuamente molti progettisti di icone è lavorano in questo modo da anni.

Di norma e’ meglio disegnare elementi semplici direttamente in Photoshop utilizzando il Tool col Rettangolo arrotondato. Disegnare cerchi utilizzando il Rounded Rectangle Tool con un raggio d’angolo molto ampio, perché lo strumento ellisse non può fare lo snap  di pixel.
I gruppi di layer possono avere maschere vettoriali , e si utlizzano per il compositing complesso (opzione-trascinare una maschera da un altro livello per creare una maschera di gruppo).

oggetti più complessi vengono trascinati in Adobe Illustrator per l’esatta dimensione dei pixel, e poi incollate in Photoshop come un shape layer. Bisogna fare attenzione quando si incolla in Photoshop, il risultati non si allineano sempre come dovrebbero – è spesso un mezzo  pixel fuori l’asse x, asse y o entrambi. La soluzione è lo zoom, bisogna scorrere il documento con lo strumento Mano e incollare di nuovo. Ripetere l’operazione fino a quando si allinea tutto. E’ esasperante, e il metodo funziona dopo alcuni tentativi. Un’altra opzione è quella di ingrandire fino al 200%, selezionare il percorso con la selezione diretta di utensili, e spostarla di una volta, fino a che si muoverà tutto esattamente 0.5px.

Anche gli oggetti più complessi, che richiedono più colori vengono disegnati in Illustrator per l’esatta dimensione dei pixel, e quindi incollati in Photoshop come oggetto avanzato. E ‘l’ultima risorsa, anche se – gradienti non sono dithered, e la modifica successiva risulterà più difficile.

Se avete bisogno di usare un bitmap per una texture, ci sono tre opzioni: usare un pattern layer, un pattern layer style, o costruire un layer bitmap con le dimensioni 2 × e poi trasformarlo in un oggetto avanzato. E’ preferibile usare pattern layer styles, nella maggior parte dei casi, ma con attenzione: i patterns vengono scalati con l’interpolazione bicubica quando si scala l’intero documento, in modo che diventino “softer “.  La soluzione è creare due versioni di ogni pattern (modello), quindi modificare manualmente il “pattern layer styles “per il modello corretto dopo la scalatura – un poco noioso, ma del tutto pratico.


Scalare l’immagine (Scaling Up)

A questo punto, il progetto dovrebbe essere in grado di scalare al doppio esattamente e senza intoppi.

Normalmente si prepara prima  una Action di Photoshop che permette di fare uno snapshot della cronologia, poi la scala al 200%. Ciò significa, che si puo’ì avere la risoluzione al display retina con solo un clic. Se Vi senti sicuri e  avete costruito tutto bene, dovreste essere in grado di scalare, modificare, poi ri-scala verso il basso e continuare a modificare senza alcun tipo di degradazione. Se avete problemi, uno snapshot è lì pronta. Utilizzando un unico documento per entrambe le risoluzioni, significa non dover tenere due documenti in sincronia – un enorme vantaggio.

Una avvertimento: i “layer styles” possono contenere solo valori interi. Se si modifica un ombra offset da 1 px con il documento a 2 × dimensioni, e poi scala verso il basso, il valore finirà come 1 px, perché non può essere pari allo 0,5 px (un valore non intero). Se si richiede modifiche specifiche per la versione 2 × del file di Photoshop, dovrete salvare quella versione in un file separato.

Esportazione, exporting.

Ora,  qualche brutta notizia: il processo di esportazione di tutte le immagini per costruire un’applicazione può essere estremamente noioso, e non ci sono molti consigli in questa fase che possano aiutarvi.

Il miglior metodo di esportazione sembra essere: attivare il layer che si desidera visibile, fare un perimetro di selezione dell’elemento, quindi utilizzare Copia elementi uniti e incollare la selezione in un nuovo documento – non è molto divertente quando si hanno centinaia di immagini da esportare.

Il problema è amplificato quando si salva per l’esposizione Retina, dove ci sonoil doppio delle immagini ; ad ogni immagine a 1 × deve corrispondere un  immagini 2 ×  con massima precisione.

La soluzione migliore sembra essere la seguente:

* Crea il tuo design a 1 ×
* Utilizzare Copia elementi uniti per salvare tutte le 1 × immagini
* Duplicare l’intera cartella contenente le immagini a 1 ×
* Utilizzare Automator per aggiungere @ 2x a tutti i nomi dei file
* Aprire ogni @ 2x immagine ed eseguire la “Scala del 200%” azione di Photoshop. Questo ti dà un file con il corretto nome del file e dimensione, ma con contenuti upscaled
* Scala il tuo  documento di progettazione principale Photoshop del 200%
* Utilizzare Copia elementi uniti (Copy Merged) per incollare gli elementi di qualità superiore in ogni @ 2x documento, disattivare il livello di qualità inferiore, quindi salvare per il Web, sovrascrivendo il file.

In alcuni casi, l’opzione di Photoshop “Esporta i livelli per i file” (Export Layers To Files) può aiutare. Lo script può essere trovato nel menu File.

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