📜 ⬆️ ⬇️

A couple of words about debugging applications from the App Store

Despite the fact that the emulator supplied as part of the Marmalade tool environment is quite convenient and allows you to debug all conceivable and inconceivable situations, there are a number of tasks with which it cannot cope. Such tasks include, for example, debugging the purchase of products from a developed application (via the App Store or Android Market). Testing the purchase itself must be performed on the device, but it often happens that the application logic is associated with the purchase, which would be nice to debug under the emulator. How to do this, below:


As an example, consider the s3e / s3eIOSAppStoreBilling application, supplied as examples to Marmalade. If you run it, then all that we will be able to see is a modal window, with the error message "This is a case of purchase." This is of course correct, but we want to debug the application before we post it on the IPhone. It is not at all difficult to do. We will develop a stub that simulates (in the debug build) the behavior of the S3E iOS App Store Billing . Let's start by adding our files to the mkb file:

s3eIOSAppStoreBilling.mkb
#!/usr/bin/env mkb files { s3eIOSAppStoreBilling.cpp AppStoreStub.h AppStoreStub.cpp } ... 


The stub code itself is primitive. In the h-file for each function of App Store Billing, we add a stub with the suffix "Stub" and if the assembly is a debug build, we substitute calls with the #define directive:
')
AppStoreStub.h
 #ifndef _APPSTORESTUB_H_ #define _APPSTORESTUB_H_ #include <string.h> #include "s3eTypes.h" #include "s3eIOSAppStoreBilling.h" #if defined IW_DEBUG #define s3eIOSAppStoreBillingAvailable \ s3eIOSAppStoreBillingAvailableStub #define s3eIOSAppStoreBillingGetInt \ s3eIOSAppStoreBillingGetIntStub #define s3eIOSAppStoreBillingInit \ s3eIOSAppStoreBillingInitStub #define s3eIOSAppStoreBillingStart \ s3eIOSAppStoreBillingInitStub #define s3eIOSAppStoreBillingTerminate \ s3eIOSAppStoreBillingTerminateStub #define s3eIOSAppStoreBillingStop \ s3eIOSAppStoreBillingTerminateStub #define s3eIOSAppStoreBillingRequestProductInformation \ s3eIOSAppStoreBillingRequestProductInformationStub #define s3eIOSAppStoreBillingCancelProductInformationRequests \ s3eIOSAppStoreBillingCancelProductInformationRequestsStub #define s3eIOSAppStoreBillingRequestPayment \ s3eIOSAppStoreBillingRequestPaymentStub #define s3eIOSAppStoreBillingCompleteTransaction \ s3eIOSAppStoreBillingCompleteTransactionStub #define s3eIOSAppStoreBillingRestoreCompletedTransactions \ s3eIOSAppStoreBillingRestoreCompletedTransactionsStub #endif s3eBool s3eIOSAppStoreBillingAvailableStub(); int32 s3eIOSAppStoreBillingGetIntStub(s3eIOSAppStoreBillingProperty property); s3eResult s3eIOSAppStoreBillingInitStub(s3eProductInformationCallbackFn infoCallback, s3ePaymentTransactionUpdateCallbackFn updateCallback, void* userData); void s3eIOSAppStoreBillingTerminateStub(); s3eResult s3eIOSAppStoreBillingRequestProductInformationStub(const char** productIdentifiers, uint32 numProductIdentifiers); void s3eIOSAppStoreBillingCancelProductInformationRequestsStub(); s3eResult s3eIOSAppStoreBillingRequestPaymentStub(s3ePaymentRequest* paymentRequest); s3eResult s3eIOSAppStoreBillingCompleteTransactionStub(s3ePaymentTransaction* transaction, s3eBool finalise); s3eResult s3eIOSAppStoreBillingRestoreCompletedTransactionsStub(); void appStoreStubUpdate(); #endif // _APPSTORESTUB_H_ 


The implementation of these functions also does not contain anything supernatural:

AppStoreStub.cpp
 #include "AppStoreStub.h" #define PRODUCT_INFO_REQUESTED 0x0001 #define TRANSACTION_REQUESTED 0x0002 s3eProductInformationCallbackFn productInfoCallback = NULL; s3ePaymentTransactionUpdateCallbackFn transactionCallback = NULL; void* billingData = NULL; s3eProductInformation productInformation; s3ePaymentTransaction transaction; s3eTransactionReceipt receipt; int eventMask = 0; s3eBool s3eIOSAppStoreBillingAvailableStub() { return S3E_TRUE; } int32 s3eIOSAppStoreBillingGetIntStub(s3eIOSAppStoreBillingProperty property) { switch (property) { case S3E_IOSAPPSTOREBILLING_CAN_MAKE_PAYMENTS: return 1; default: return 0; } } s3eResult s3eIOSAppStoreBillingInitStub(s3eProductInformationCallbackFn infoCallback, s3ePaymentTransactionUpdateCallbackFn updateCallback, void* userData) { productInfoCallback = infoCallback; transactionCallback = updateCallback; billingData = userData; return S3E_RESULT_SUCCESS; } void s3eIOSAppStoreBillingTerminateStub() {} s3eResult s3eIOSAppStoreBillingRequestProductInformationStub(const char** productIdentifiers, uint32 numProductIdentifiers) { if (numProductIdentifiers != 1) return S3E_RESULT_ERROR; memset(&productInformation, 0, sizeof(productInformation)); strcpy(productInformation.m_ProductID, *productIdentifiers); strcpy(productInformation.m_LocalisedTitle, *productIdentifiers); strcpy(productInformation.m_LocalisedDescription, *productIdentifiers); strcpy(productInformation.m_FormattedPrice, "0.00"); strcpy(productInformation.m_PriceLocale, "0.00"); productInformation.m_Price = 0; productInformation.m_ProductStoreStatus = S3E_PRODUCT_STORE_STATUS_VALID; eventMask |= PRODUCT_INFO_REQUESTED; return S3E_RESULT_SUCCESS; } void s3eIOSAppStoreBillingCancelProductInformationRequestsStub() {} s3eResult s3eIOSAppStoreBillingRequestPaymentStub(s3ePaymentRequest* paymentRequest) { memset(&transaction, 0, sizeof(transaction)); transaction.m_TransactionStatus = S3E_PAYMENT_STATUS_PURCHASED; transaction.m_Request = paymentRequest; transaction.m_Retain = S3E_FALSE; transaction.m_TransactionReceipt = &receipt; strcpy(transaction.m_TransactionID, "1"); strcpy(transaction.m_OriginalTransactionID, "1"); memset(&receipt, 0, sizeof(receipt)); receipt.m_ReceiptSize = 0; eventMask |= TRANSACTION_REQUESTED; return S3E_RESULT_ERROR; // ??? } s3eResult s3eIOSAppStoreBillingCompleteTransactionStub(s3ePaymentTransaction* transaction, s3eBool finalise) { return S3E_RESULT_SUCCESS; } s3eResult s3eIOSAppStoreBillingRestoreCompletedTransactionsStub() { return S3E_RESULT_SUCCESS; } void appStoreStubUpdate() { #if defined IW_DEBUG if (eventMask != 0) { if (eventMask & PRODUCT_INFO_REQUESTED) { productInfoCallback(&productInformation, billingData); } if (eventMask & TRANSACTION_REQUESTED) { transactionCallback(&transaction, billingData); } eventMask = 0; } #endif } 


Here we should mention the appStoreStubUpdate function. The fact is that the App Store Billing API uses asynchronous calls to work with the store. This means that calls to callback functions must be made outside the context of calling functions for requesting product information s3eIOSAppStoreBillingRequestProductInformation and making a purchase s3eIOSAppStoreBillingRequestPayment. We will execute these calls in appStoreStubUpdate, calling the latest of the update functions of the Maramalade application.

Also, the return code s3eIOSAppStoreBillingRequestPaymentStub may raise issues. Frankly, I myself do not understand this moment. Logically, if successful, it should return S3E_RESULT_SUCCESS (equal to 0), but the example uses the following validation of the query formation:

s3eIOSAppStoreBilling.cpp
  if (s3eIOSAppStoreBillingRequestPayment(&g_PaymentRequest)) SetStatus("Purchasing %s...", g_ProductID2); else SetStatus("Purchasing %s FAILED", g_ProductID2); 


That is, a nonzero value is expected. Apparently, the developers of the example made a mistake, but at this point you should pay attention when debugging the application on the iPhone.

The last thing to mention is a check. In s3eIOSAppStoreBillingRequestPaymentStub, we form an empty check:

AppStoreStub.cpp
  ... memset(&receipt, 0, sizeof(receipt)); receipt.m_ReceiptSize = 0; ... 


But this may not be entirely correct if the application must verify the receipt or use some of the information contained in it (for example, the date of purchase). About how to build the correct check can be found here . It describes how to verify a check.

There are very few. In the example code, we add #include AppStoreStub.h (it must go last) and call appStoreStubUpdate, to emulate asynchronous calls:

s3eIOSAppStoreBilling.cpp
 bool ExampleUpdate() { appStoreStubUpdate(); ... 


By compiling the example and running it under the debugger, we can make sure that now we can request product information and make purchases without disturbing the App Store at all.

Source: https://habr.com/ru/post/157677/


All Articles