Fujifilm SPA iOS SDK Tutorial
Introduction
This project is a sample application to demonstrate how to integrate with the Fujifilm SPA iOS SDK.
This document provides a tutorial to use the Fujifilm SPA iOS SDK library in your iOS application.
What is it?
The Fujifilm Smart Publishing API SDK is a native library that you can include in your existing iOS application to enable photo product output through Fujifilm. This provides you with a new revenue stream while providing a new valuable service to your application users.
The Fujifilm SPA SDK gives you access to over 100 popular photo gift products and allows you to control the availability and pricing of each product through our web portal.
Please visit the Fujifilm Developer Network portal to sign-up and obtain an API key, set product pricing, and configure your application. The portal is available at http://www.fujifilmapi.com/.
Requirements
- iOS version 8.0 or later
- Developers using the Fujifilm SPA iOS SDK need to sign up for an account on Fujifilm Developer Network (http://fujifilmapi.com), create an application, obtain an Api Key, and setup catalog products and pricing.
Integration Instructions
Step 1: Get API Key
Sign up for an account on Fujifilm Developer Network (http://fujifilmapi.com), create an application, obtain an API Key, and setup catalog products and pricing.
Step 2: Include Fujifilm SPA SDK
Include the Fujifilm SPA SDK in your Xcode project. To add Fujifilm SPA SDK to your Xcode project, you may install via Cocoapods or add it manually.
Using CocoaPods
For more information on Cocoapods, including instructions on adding a pod to your Xcode project, visit https://guides.cocoapods.org/using/the-podfile.html.
This section assumes you have CocoaPods installed on your system.
In your Podfile, include the SPA SDK pod like so:
pod 'Fujifilm-SPA-SDK', '~> 1.10.12'
Install the pod by navigating to the project directory in a terminal and running $ pod install
. If you have already installed the SDK and would like to update to the latest version, run $ pod update
instead.
Upon successful installation, close your Xcode project if it is open, and open the workspace that was generated by Cocoapods.
Manual Installation
Skip this section if you are using CocoaPods!
- Download files
- Download the libFujifilm_SPA_SDK_iOS.a, Fujifilm.SPA.SDK.h, and Fujifilm_SPA_SDK_iOS_AppSwitch.h.
- Add files to project
- Open your project in Xcode. Select File > Add Files To “MyApp” and select the files you just downloaded. Check “Copy items if needed” under Destination and select “Create groups” under Added Folders. Make sure your target is checked in the “Add to targets” section. Click Add.
- Link with frameworks
- Add the following frameworks to your project:
- Accelerate
- AddressBook
- AddressBookUI
- AVFoundation
- AudioToolBox
- CoreMedia
- MobileCoreServices
- SystemConfiguration
- AssetsLibrary
- ImageIO
- Photos
- To add frameworks, select your project in the Xcode file explorer. In the main window, the top left corner has a dropdown menu with a list of your projects and targets. Make sure your target is selected (not your project) and switch to the Build Phases tab. Expand the Link Binary With Libraries section and add the frameworks listed above.
- In your
TARGETS
Build settings, add-lc++
to theOther Linker Flags
section - In your
TARGETS
Build settings, add-ObjC
to theOther Linker Flags
section
Step 3: Updating info.plist file
In order to use the SDK you will need to add keys to your project's info.plist
file.
You can enter all of the keys manually using the Xcode UI, or you can open the info.plist
file in a text editor and copy/paste the following in the inside the <plist> <dict>
tag.
<key>NSLocationWhenInUseUsageDescription</key>
<string>Please enable this feature to search for stores near you when creating personalized prints and gifts.</string>
<key>NSContactsUsageDescription</key>
<string>Please enable this feature to access your contacts when creating personalized prints and gifts.</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>Please enable this feature to be able to upload your photos and create personalized prints and gifts.</string>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
<key>NSExceptionDomains</key>
<dict>
<key>fujifilmesys.com</key>
<dict>
<key>NSExceptionAllowsInsecureHTTPLoads</key>
<true/>
<key>NSExceptionRequiresForwardSecrecy</key>
<false/>
<key>NSIncludesSubdomains</key>
<true/>
</dict>
<key>paypal.com</key>
<dict>
<key>NSExceptionRequiresForwardSecrecy</key>
<false/>
<key>NSIncludesSubdomains</key>
<true/>
</dict>
</dict>
</dict>
Step 4: PayPal Payment Option
We offer 2 payment options to users, Credit Card and PayPal. Please follow the steps below to enable PayPal as you'll typically see a higher conversion rate with it.
Setup for app switch using the CFBundleURLTypes key in info.plist
To accept payments from PayPal, you must register a URL type and configure your app to return from app switches.
Register a URL type
- In Xcode, click on your project in the Project Navigator and navigate to App Target > Info > URL Types
- Click [+] to add a new URL type
- Under Identifier, enter your app's Bundle ID
- Under URL Schemes, enter your app switch return URL scheme. This scheme must start with your app's Bundle ID and be dedicated to Fujifilm' SDK app switch returns. For example, if the app bundle ID is com.your-company.Your-App, then your URL scheme could be com.your-company.Your-App.FujifilmSDK.Payments.
- If your app is built using iOS 9 as its Base SDK, then you must add URLs to a whitelist in your app's
info.plist
:
<key>LSApplicationQueriesSchemes</key>
<array>
<string>com.paypal.ppclient.touch.v1</string>
<string>com.paypal.ppclient.touch.v2</string>
</array>
- Test your URL type set in step 4 above: Open up Mobile Safari on your iOS Device or Simulator and enter in the URL that you set in step 4 (e.g. com.your-company.Your-App.FujifilmSDK.Payments://test). This should launch your app.
Important: If you have multiple app targets, be sure to add the URL type for all of the targets.
Step 5: Integrate with SDK - Objective-C
If your app is Swift, you can following the instructions found here: Swift Integration.
In your view controller header file, import the SDK:
#import "Fujifilm.SPA.SDK.h"
In your view controller header file, ensure it implements the FujifilmSPASDKDelegate
protocol:
@interface ViewController : UIViewController <FujifilmSPASDKDelegate>{}
In your view controller, create a Fujifilm_SPA_SDK_iOS
object. Initialize it using the initWithApiKey:environment:images:userID:retainUserInfo:promoCode:launchPage:extraOptions
method:
Fujifilm_SPA_SDK_iOS *fujifilmSDKOrderController = [[Fujifilm_SPA_SDK_iOS alloc]
initWithApiKey: @"YOUR_API_KEY" //REPLACE with YOUR ApiKey
environment: @"Preview"
images: NS_ARRAY_OF_FFIMAGES
userID: @"" //optional
retainUserInfo: YES
promoCode: @"" //optional
launchPage: kHome
extraOptions: nil];
Parameters
Name | Type | Description |
---|---|---|
apiKey |
NSString* |
Fujifilm SPA apiKey you receive when you create your app at http://fujifilmapi.com. This apiKey is environment specific |
environment |
NSString* |
Sets the environment to use. The apiKey must match your app’s environment set on http://fujifilmapi.com. Possible values are “preview” or "production". |
images |
FFImage* |
An NSArray of FFImage which can be initialized with a PHAsset or NSURL. Images must be jpeg, png , or heic format and smaller than 20MB. A maximum of 100 images can be sent in a given Checkout process. If more than 100 images are sent, only the first 100 will be processed. |
userID |
NSString* |
Optional parameter. Send in an empty string @"" if you don't use it. This can be used to link a user with an order. Maximum length is 50 alphanumeric characters. |
retainUserInfo |
BOOL |
Save user information (address, phone number, email) for when the app is used a second time. |
promoCode |
NSString* |
Optional parameter to add a promo code to the order. Contact us through http://fujifilmapi.com for usage and support. |
launchPage |
enum |
The page that the SDK should launch when initialized. Valid values are kHome and kCart . Defaults to kHome |
extraOptions |
NSDictionary<NSString*, id>* |
A dictionary with key/value pairs. All key/value pairs are optional; extraOptions may be empty or nil if no options are desired. See section "Extra Initialization Options" for more information. |
Next, set the Fujifilm_SPA_SDK_iOS object’s delegate to the view controller:
fujifilmSDKOrderController.delegate = self;
Next, create a new FujifilmSPASDKNavigation Controller with the orderController as its root
FujifilmSPASDKNavigationController *fujifilmSDKNavigationController = [[FujifilmSPASDKNavigationController alloc] initWithRootViewController:fujifilmSDKOrderController];
Finally, present the Fujifilm_SPA_SDK_iOS object:
[self presentViewController:fujifilmSDKNavigationController animated:YES completion:nil];
Extra Initialization Options (Optional)
Key | Value | Description |
---|---|---|
kSiteDeepLink |
NSString* |
Specifies which page the user is first presented with when launching the SDK. You can send the user to the cart, category, product details, or product builder. To send the user to the cart, set the value to Cart . To send the user to a category use the follow pattern: mailorder/CATEGORY_NAME . Make sure to change the CATEGORY_NAME to the name of the category, for example, mailorder/WallArt . To send the user to a product details screen use the following pattern: mailorder/CATEGORY_NAME/PRODUCT_NAME . Make sure to change CATEGORY_NAME to the name of the category and the PRODUCT_NAME to the name of the product, for example, mailorder/canvas/11x14gallerywrappedcanvas . To send the user to a product builder screen use the following pattern: mailorder/CATEGORY_NAME/PRODUCT_NAME/builder . Make sure to change CATEGORY_NAME to the name of the category and the PRODUCT_NAME to the name of the product, for example, mailorder/canvas/11x14gallerywrappedcanvas/builder . |
kEnableAddMorePhotos |
BOOL* |
By default this is set to YES . To disable the "Add More Photos" feature set this to value No . If YES (or omitted), the user will be able to add more photos from his or her local Photos gallery on the Compose screen and the Prints screen. |
kPreRenderedOrder |
FFOrder* |
See section "Providing Pre-rendered Products" for more information. |
Providing Pre-rendered Products (Optional)
Pre-rendered (custom) products can be used to create your own custom content that we will print on either a 4x8 Greeting card, 5x7 Greeting Card, 5x7 Stationery Card or 8x8 Photo book custom product. Pre-rendered products can only be used with pre-rendered (custom) product codes. You first have to enable the custom product(s) on your pricing page in the portal, which can be found under the Cards > Custom Cards and Photo Books > Custom Photo Books categories in the left navigation. After you enable the custom product(s) you can then use one of the 4 product codes:
4x8 Greeting Card: "PRGC;823"
5x7 Greeting Card: "PRGC;830"
5x7 Stationery Card: "PRGift;4121"
8x8 Photobook: "PRGift;5212"
In order to include pre-rendered products with your order, you may pass an instance of the FFOrder
class into the extraOptions
parameter. This class contains a list of products (instances of FFLine
class) to be added to the order. Each FFLine
contains a product code field which corresponds to the product code found on http://fujifilmapi.com, as well as a list of FFPage
objects. Each FFPage
object contains a list of FFAsset
objects, each of which contains a url to the Hi-Res image to be printed.
The following is an example function showing how to create an order with a pre-rendered Stationery Card:
-(FFOrder *)createPrerenderedOrderWithStationeryCard {
//create a FFAsset for the front of the card
FFAsset *assetFront = [FFAsset assetWithHiResImageURL:@"http://test.com/prerenderedCardFront.jpg"];
//create a FFAsset for the back of the card
FFAsset *assetBack = [FFAsset assetWithHiResImageURL:@"http://test.com/prerenderedCardBack.jpg"];
//create a FFPage for the front of the card. Add the front asset
FFPage *pageFront = [FFPage page];
[pageFront addAsset:assetFront];
//create a FFPage for the back of the card. Add the back asset
FFPage *pageBack = [FFPage page];
[pageBack addAsset:assetBack];
//create a FFLine with product code which represents the desired product
FFLine *line = [FFLine lineWithProductCode:@"PRGift;4121"];
//add the front and back pages to the Line
[line addPage:pageFront];
[line addPage:pageBack];
//Create a new order containing the Line
FFOrder *order = [FFOrder order];
[order addLine:line];
//return the FFOrder to be added to extraOptions
return order;
}
Finishing SPA SDK
The FujifilmSPASDKDelegate requires your view controller to implement the method fujifilmSPASDKFinishedWithStatus:(int) statusCode andMessage: (NSString*) message
.
When the Fujifilm SPA SDK is finished, it will return to the parent app, calling fujifilmSPASDKFinishedWithStatus:andMessage
. You must implement like so:
#pragma mark -
#pragma mark Fujifilm SPA SDK delegate
-(void) fujifilmSPASDKFinishedWithStatus: (int) statusCode andMessage: (NSString*) message{
NSString *msg;
/**
Status codes that may be sent from Fujifilm SPA SDK. This may require updates if any new codes are added. See documentation for list of status codes.
*/
switch (statusCode){
case kFujifilmSDKStatusCodeFatal:
msg = @"Fatal Error";
break;
case kFujifilmSDKStatusCodeNoImagesUploaded:
msg = @"No Images Uploaded";
break;
case kFujifilmSDKStatusCodeNoInternet:
msg = @"No Internet";
break;
case kFujifilmSDKStatusCodeInvalidAPIKey:
msg = @"Invalid APIKey";
break;
case kFujifilmSDKStatusCodeUserCanceled:
msg = @"User Canceled";
break;
case kFujifilmSDKStatusCodeNoValidImages:
msg = @"No Valid Images";
break;
case kFujifilmSDKStatusCodeTimeout:
msg = @"Timeout Error";
break;
case kFujifilmSDKStatusCodeOrderComplete:
msg = message;
break;
case kFujifilmSDKStatusCodeUploadFailed:
msg = @"Upload Failed";
break;
case kFujifilmSDKStatusCodeInvalidUserIDFormat:
msg = @"Invalid User ID Format";
break;
case kFujifilmSDKStatusCodeInvalidPromoCodeFormat:
msg = @"Invalid Promo Code Format";
break;
case kFujifilmSDKStatusCodeRequiresPhotoPermission:
msg = @"Photo Permission Required";
break;
default:
msg = @"Unknown Error";
}
NSLog(@"fujifilmSPASDKFinishedWithStatus: statusCode: %u message: %@", statusCode, msg);
}
The status code will be one of the following values:
statusCode |
meaning |
---|---|
0 | Fatal Error |
1 | No Images Uploaded |
2 | No Internet |
3 | Invalid API Key |
4 | User Cancelled |
5 | No Valid Images |
6 | Time Out |
7 | Order Complete |
8 | Upload Failed |
9 | User ID Invalid Format |
10 | Promo Code Invalid Format |
11 | Requires Photo Permission |
12 | Partner Closed SDK |
It is up to your view controller to handle any/all of these cases in fujifilmSPASDKFinishedWithStatus:andMessage
as seen above. The status codes and messages are for internal use only; please do not present these to the user.
In addition, the FujifilmSPASDKDelegate
has an optional method promoCodeDidFailValidationWithError: (int) error
This method is called when a promotion code that is passed in fails validation in the SDK. The error parameter is an int corresponding to the cause of the failure. Possible error values are provided as an enum (FFPromotionError
). Though not required, it is recommended that this method is implemented in order to log issues and/or inform users that their promotion is invalid. An example implementation is given below:
-(void) promoCodeDidFailValidationWithError:(int)error {
NSString *errorReason;
switch(error) {
case 0:
errorReason = @"Promotion Expired";
break;
case 1:
errorReason = @"Promotion Not Activated";
break;
case 2:
errorReason = @"Invalid Discount";
break;
case 3:
errorReason = @"Promotion Disabled";
break;
case 4:
errorReason = @"Promotion Does Not Exist";
break;
case 5:
default:
errorReason = @"Fatal Error";
break;
}
NSLog(@"Promotion is invalid. Cause: %@",errorReason);
}
PayPal Payment Option
In addition to updating your info.plist
in Step 4 above, you must also update your application delegate.
Update your application delegate
In your AppDelegate's application:didFinishLaunchingWithOptions
implementation, use setReturnURLScheme:
with the value you set above in Step 4:PayPal Payment Option. It's important that the url you pass in here matches the url that you set in your info.plist
for Step 4 above.
For example:
#import "AppDelegate.h"
#import "Fujifilm_SPA_SDK_iOS_AppSwitch.h"
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[Fujifilm_SPA_SDK_iOS_AppSwitch setReturnURLScheme:@"com.your-company.Your-App.FujifilmSDK.Payments"];
return YES;
}
Then in your application delegate, pass the payment URL that you set above in Step 4:PayPal Payment Option. Make sure that you change the "com.your-company.Your-App.FujifilmSDK.Payments" from the example below to the url that you set in your info.plist
for Step 4 above.
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 90000
- (BOOL)application:(UIApplication *)application
openURL:(NSURL *)url
options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options {
if ([url.scheme localizedCaseInsensitiveCompare:@"com.your-company.Your-App.FujifilmSDK.Payments"] == NSOrderedSame) {
return [Fujifilm_SPA_SDK_iOS_AppSwitch handleOpenURL:url options:options];
}
return NO;
}
#endif
// If you support iOS 7 or 8, add the following method.
- (BOOL)application:(UIApplication *)application
openURL:(NSURL *)url
sourceApplication:(NSString *)sourceApplication
annotation:(id)annotation {
if ([url.scheme localizedCaseInsensitiveCompare:@"com.your-company.Your-App.FujifilmSDK.Payments"] == NSOrderedSame) {
return [Fujifilm_SPA_SDK_iOS_AppSwitch handleOpenURL:url sourceApplication:sourceApplication];
}
return NO;
}
Receiving analytic events (Optional)
If you're interested in seeing what behavior your users are taking in our SDK, we provide a way for you to listen to events from us when users take certain actions. To receive these events, implement the receivedAnalyticsEvent:withAttributes:
delegate method.
The attributes are a NSArray
of NSDictionary
. In each NSDictionary
, the keys (defined below) will correspond to either an NSString
value for strings, or NSNumber
value for booleans and numbers. Below is an example of how data is passed to this method.
Example Code
-(void) receivedAnalyticsEvent:(NSString *)event withAttributes:(NSArray *)attributes{
if (!event) {
return;
}
if ([event isEqualToString:kAnalyticsEventItemPurchased]) {
[self processItemPurchasedEventWithAttributes:attributes];
}
// Handle any other desired events here
}
-(void) processItemPurchasedEventWithAttributes:(NSArray *)attributes {
NSString *productName = nil;
NSString *productCode = nil;
NSNumber *quantity = @0;
NSNumber *unitPrice = @0.0;
for (NSDictionary *attribute in attributes) {
NSObject *eventValue = [attribute valueForKey:valueKey];
if (eventValue == nil || [eventValue isKindOfClass:[NSNull class]]) {
continue;
}
NSString *attributeName = [attribute valueForKey:attributeKey];
if ([attributeName isEqualToString:kAnalyticsAttributePurchasedProduct]) {
productName = [attribute valueForKey:valueKey];
}
else if ([attributeName isEqualToString:kAnalyticsAttributeProductCodePurchased]) {
productCode = [attribute valueForKey:valueKey];
}
else if ([attributeName isEqualToString:kAnalyticsAttributePurchasedQuantity]) {
quantity = [attribute valueForKey:valueKey];
}
else if ([attributeName isEqualToString:kAnalyticsAttributePurchasedUnitPrice]) {
unitPrice = [attribute valueForKey:valueKey];
}
}
NSLog(@"Received purchased event for product %@ (%@) with quantity %@ and unit price %@", productName, productCode, quantity, unitPrice);
}
Events
Event Name | Event Value | Description |
---|---|---|
Exit | kAnalyticsEventExit |
Sent when the user exits the SDK |
Print Edited | kAnalyticsEventPhotoEdited |
Sent when the user edits a print product |
Product Edited | kAnalyticsEventProductEdited |
Sent when the user edits any non-print product |
Continue Shopping | kAnalyticsEventContinueShopping |
Sent when the user tapps "Continue Shopping" from the cart page and the thank you page |
Item Added to Cart | kAnalyticsEventItemAddedToCart |
Sent when a product is added to cart from either the compose page or the prints page |
Item Composed | kAnalyticsEventitemComposed |
Sent when the user goes to the compose page to edit a product |
Item Details Viewed | kAnalyticsEventDetailsViewed |
Sent when the user taps navigates to the product details page |
Item Purchased | kAnalyticsEventItemPurchased |
Sent for every item when the user successfully checks out |
Order Complete | kAnalyticsEventOrderComplete |
Sent when the user successfully checks out |
Checkout Started | kAnalyticsEventCheckoutStarted |
Sent when the user taps "Continue to Checkout" from the cart page |
Store Searched | kAnalyticsEventStoreSearched |
Sent when the user searches for a store on the store list page |
Item Removed from Cart | kAnalyticsEventRemovedFromCart |
Sent when a product is removed from the cart, either due to an error or when the user deletes the product themselves |
Store Favorited | kAnalyticsEventStoreFavorited |
Sent when the user favorites a store on the store search page or on the first step of checkout |
Exit Event Attributes
Event Attribute | Data Type | Description |
---|---|---|
kAnalyticsAttributeItemsPurchased |
NSNumber (integer) |
Number of items purchased |
kAnalyticsAttributeExitPoint |
NSString |
Page the user left on |
kAnalyticsAttributePromoCode |
NSString |
Comma separated list of promo codes applied |
kAnalyticsAttributeExitMethod |
NSString |
Done Button/Back Button/Cancel Button |
kAnalyticsAttributeDeliveryType |
NSString |
Retail Pickup/Mail Order |
kAnalyticsAttributePickupLocation |
NSString |
If this is a Mail Order order, N/A. Otherwise, it is the name of the retailer the user selected (Walmart, Sam's Club, etc.) |
kAnalyticsAttributeAddressValidationErrors |
NSNumber (integer) |
Number of address validation errors that have occurred during the session |
Print Edited Event Attributes
No event attributes
Product Edited Event Attributes
Not event attributes
Continue Shopping Event Attributes
Event Attribute | Data Type | Description |
---|---|---|
kAnalyticsAttributeScreen |
NSString |
the page in which the user tapped Continue Shopping |
Item Added to Cart Event Attributes
Event Attribute | Data Type | Description |
---|---|---|
kAnalyticsAttributeStatus |
NSNumber (BOOL) |
Whether add to cart succeeded or failed |
kAnalyticsAttributeDuration |
NSNumber (integer) |
The amount of time it took to add to cart |
kAnalyticsAttributeItemAdded |
NSString |
Name of the product added to cart |
kAnalyticsAttributeAddToCartDelivery |
NSString |
Retail Pickup/Mail Order |
kAnalyticsAttributeAddToCartPickup |
NSString |
If this is a Mail Order order, N/A. Otherwise, it is the name of the retailer the user selected (Walmart, Sam's Club, etc.) |
kAnalyticsAttributeAddedProductCode |
NSString |
The product code of the item added to cart |
Item Composed Event Attributes
Event Attribute | Data Type | Description |
---|---|---|
kAnalyticsAttributeComposedProduct |
NSString |
Name of the product composed |
kAnalyticsAttributeComposedSource |
NSString |
Where the user came from. One of: Cart Page, Order Prints Page, Product List |
kAnalyticsAttributeComposedDelivery |
NSString |
Retail Pickup/Mail Order |
kAnalyticsAttributeComposedPickup |
NSString |
If this is a Mail Order order, N/A. Otherwise, it is the name of the retailer the user selected (Walmart, Sam's Club, etc.) |
Item Details Viewed Event Attributes
Event Attribute | Data Type | Description |
---|---|---|
kAnalyticsAttributeDetailsProduct |
NSString |
Name of the product the user viewed |
kAnalyticsAttributeDetailsSource |
NSString |
Where the user viewed the product from. Currently only "Product List" |
kAnalyticsAttributeDetailsDelivery |
NSString |
Retail Pickup/Mail Order |
kAnalyticsAttributeDetailsPickup |
NSString |
If this is a Mail Order order, N/A. Otherwise, it is the name of the retailer the user selected (Walmart, Sam's Club, etc.) |
Item Purchased Event Attributes
Event Attribute | Data Type | Description |
---|---|---|
kAnalyticsAttributePurchasedProduct |
NSString |
Name of the product the user purchased |
kAnalyticsAttributePurchasedQuantity |
NSNumber (integer) |
Quantity of the line in the order |
kAnalyticsAttributePurchasedDelivery |
NSString |
Retail Pickup/Mail Order |
kAnalyticsAttributePurchasedPickup |
NSString |
this is a Mail Order order, N/A. Otherwise, it is the name of the retailer the user selected (Walmart, Sam's Club, etc.) |
kAnalyticsAttributeProductCodePurchased |
NSString |
Product code of the item purchased |
kAnalyticsAttributePurchasedUnitPrice |
NSNumber (float) |
Unit price of the product |
Order Complete Event Attributes
Event Attribute | Data Type | Description |
---|---|---|
kAnalyticsAttributeNumberOfItems |
NSNumber (integer) |
Number of lines in the order |
kAnalyticsAttributeNumberOfDistinctItems |
NSNumber (integer) |
Number of distinct products in the order |
kAnalyticsAttributeOrderPaymentType |
NSString |
Name of the payment method used by the user |
kAnalyticsAttributeOrderCurrencyType |
NSString |
ISO 4217 currency code for the type of currency used. Currently only USD. |
kAnalyticsAttributeOrderSubtotal |
NSNumber (float) |
Sub total of the order |
kAnalyticsAttributeOrderTax |
NSNumber (float) |
Tax the user was charged for |
kAnalyticsAttributeOrderShipping |
NSNumber (float) |
Total cost of shipping for the user |
kAnalyticsAttributeOrderDiscount |
NSNumber (float) |
Amount taken off the order due to discounts |
kAnalyticsAttributeOrderTotal |
NSNumber (float) |
Total amount of the order |
kAnalyticsAttributeOrderRetailer |
NSString |
If this is Mail Order, N/A. Otherwise, it is the name of the retailer the user selected (Walmart, Sam's Club, etc.) |
kAnalyticsAttributeOrderServiceType |
NSString |
Mail Order/Retail Pickup |
kAnalyticsAttributeOrderDeliveryMethod |
NSString |
The delivery level the user selected: Standard/Expidited/Rush. If this is a store pickup order, this is "Pay in Store". |
kAnalyticsAttributeStoreNumber |
NSString |
The store number of the store the user selected, if the order is store pickup. |
Checkout Started Event Attributes
Event Attribute | Data Type | Description |
---|---|---|
kAnalyticsAttributeStoreNumber |
NSString |
The store number of the store the user selected, if the order is store pickup. |
kAnalyticsAttributeNumberOfItems |
NSNumber (integer) |
Number of lines in the order |
kAnalyticsAttributeNumberOfDistinctItems |
NSNumber (integer) |
Number of distinct products in the order |
kAnalyticsAttributeOrderCurrencyType |
NSString |
ISO 4217 currency code for the type of currency used. Currently only USD. |
kAnalyticsAttributeOrderSubtotal |
NSNumber (float) |
Sub total of the order |
kAnalyticsAttributeIsPreservedCart |
NSNumber (BOOL) |
If this is a new order or a preserved cart |
kAnalyticsAttributeOrderRetailer |
NSString |
If is is Mail Order, N/A. Otherwise, it is the name of the retailer the user selected (Walmart, Sam's Club, etc.) |
kAnalyticsAttributeOrderServiceType |
NSString |
Mail Order/Retail Pickup |
Store Searched Event Attributes
Event Attribute | Data Type | Description |
---|---|---|
kAnalyticsAttributeSearchLatitude |
NSNumber (float) |
The latitude of the user when they tap "Find near me" on the store search page |
kAnalyticsAttributeSearchLongitude |
NSNumber (float) |
The longitude of the user when they tap "Find near me" on the store search page |
kAnalyticsAttributeSearchZip |
NSString |
The zip code the user entered on the store search page |
kAnalyticsAttributeSearchRadius |
NSNumber (integer) |
The radius the user had selected |
kAnalyticsAttributeSearchResultsCount |
NSNumber (integer) |
The number of stores found based on the users search criteria |
Item Removed from Cart Event Attributes
Event Attribute | Data Type | Description |
---|---|---|
kAnalyticsAttributeProductRemoved |
NSString |
The name of the product removed from the users cart |
kAnalyticsAttributeProductCodeRemoved |
NSString |
The product code of the product removed from the users cart |
Store Favorited Event Attributes
Event Attribute | Data Type | Description |
---|---|---|
kAnalyticsAttributeFavoritedStoreNumber |
NSString |
The store number of the store that the user favorited |
Override Image Picker (Optional)
If you're interested in having our SDK use your image picker when the user attempts to add more photos to their session, you can implement the optional requestForAdditionalPhotos function. Our SDK will call this function when a user attempts to add more photos from within our SDK. You can then call the completionHandler to send us the images the user selected.
Example Code
/**
Optional function (requestForAdditionalPhotos) to implement if you would like to use your own image picker. Our SDK will call this function when a user attempts to add more photos from within our SDK. You can then call the completionHandler to send us the images the user selected.
@param selectedImages - An array of FFImage objects that represents the images the user has in session. This should be referenced in your image picker to display to the user which images are already in their session (show the image as selected). The FFImage object has a uniqueidentifier property that is set to the PHAsset's identifier or the NSURL's path and can be accessed by calling getUniqueIdentifier, [myFFimageObject getUniqueIdentifier]. You can then use this identifier to compare it to the identifiers for the images in your image picker and display to the user the images already in their session.
@param notDeselectable - An array of FFImage objects that represents the images the user is not allowed to deselect because they are being used in a cart or a product builder. This should be referenced to prevent the user from deselecting images in your image picker. The FFImage object has a uniqueidentifier property that is set to the PHAsset's identifier or the NSURL's path and can be accessed by calling getUniqueIdentifier, [myFFimageObject getUniqueIdentifier]. You can then use this identifier to compare it to the identifiers for the images in your image picker and prevent the user from deselecting the image.
@param completionHandler - Call this completion handler to send us the images the user selected.
*/
- (void)requestForAdditionalPhotos:(NSArray<FFImage *> *)selectedImages lockedImages:(NSArray<FFImage *> *)notDeselectable withCompletionHandler:(void (^)(NSArray<FFImage *> * _Nonnull))completionHandler{
//self.requestForAdditionalPhotosCompletionHandler = completionHandler;
//open your image picker and then call the completion handler.
}
Full Example Code
Podfile
pod 'Fujifilm-SPA-SDK', '~> 1.10.12'
ViewController.h
#import "Fujifilm.SPA.SDK.h"
@interface ViewController : UIViewController <FujifilmSPASDKDelegate>{}
/**
Enum of status codes that may be sent from Fujifilm SPA SDK. This may require updates if any new codes are added. See documentation for list of status codes.
*/
typedef enum FujifilmSDKStatusCode {
kFujifilmSDKStatusCodeFatal= 0,
kFujifilmSDKStatusCodeNoImagesUploaded= 1,
kFujifilmSDKStatusCodeNoInternet= 2,
kFujifilmSDKStatusCodeInvalidAPIKey= 3,
kFujifilmSDKStatusCodeUserCanceled= 4,
kFujifilmSDKStatusCodeNoValidImages= 5,
kFujifilmSDKStatusCodeTimeout= 6,
kFujifilmSDKStatusCodeOrderComplete= 7,
kFujifilmSDKStatusCodeUploadFailed= 8,
kFujifilmSDKStatusCodeInvalidUserIDFormat = 9,
kFujifilmSDKStatusCodeInvalidPromoCodeFormat = 10,
kFujifilmSDKStatusCodeRequiresPhotoPermission= 11
} FujifilmSDKStatusCode;
@end
ViewController.m
- (IBAction)launchFujifilmSDK:(id)sender {
FFImage *newFFImage1 = [[FFImage alloc] initWithNSURL:[NSURL URLWithString:@"https://webservices.fujifilmesys.com/venus/imagebank/fujifilmCamera.jpg"]];
FFImage *newFFImage2 = [[FFImage alloc] initWithNSURL:[NSURL URLWithString:@"https://webservices.fujifilmesys.com/venus/imagebank/mustang.jpg"]];
NSMutableArray<FFImage *> *ffimages = [[NSMutableArray alloc] initWithObjects:newFFImage1,newFFImage2, nil];
/*
-------------------------------------------------------------------------------
Create a Fujifilm_SPA_SDK_iOS instance and present the Fujifilm SDK controller.
-------------------------------------------------------------------------------
- Go to http://www.fujifilmapi.com to register for an apiKey.
- Ensure you have the right apiKey for the right environment.
//MAKE SURE TO CHANGE YOUR_API_KEY TO YOUR APIKEY!
*/
Fujifilm_SPA_SDK_iOS *fujifilmSDKOrderController = [[Fujifilm_SPA_SDK_iOS alloc]
initWithApiKey: @"5cb79d2191874aca879e2c9ed7d5747c"
environment: @"Preview"
images: ffimages
userID: @"" //optional
retainUserInfo: YES
promoCode: @"" //optional
launchPage: kHome
extraOptions: nil];
fujifilmSDKOrderController.delegate = self;
//Create a new FujifilmSPASDKNavigation Controller with the orderController as its root
FujifilmSPASDKNavigationController *fujifilmSDKNavigationController = [[FujifilmSPASDKNavigationController alloc] initWithRootViewController:fujifilmSDKOrderController];
/*
---------------------------------------------------------------------------------------
Present the Fujifilm SPA SDK Navigation Controller
---------------------------------------------------------------------------------------
*/
[self presentViewController:fujifilmSDKNavigationController animated:YES completion:nil];
}
-(void) fujifilmSPASDKFinishedWithStatus: (int) statusCode andMessage: (NSString*) message{
NSString *msg;
/**
Status codes that may be sent from Fujifilm SPA SDK. This may require updates if any new codes are added. See documentation for list of status codes.
*/
switch (statusCode){
case kFujifilmSDKStatusCodeFatal:
msg = @"Fatal Error";
break;
case kFujifilmSDKStatusCodeNoImagesUploaded:
msg = @"No Images Uploaded";
break;
case kFujifilmSDKStatusCodeNoInternet:
msg = @"No Internet";
break;
case kFujifilmSDKStatusCodeInvalidAPIKey:
msg = @"Invalid APIKey";
break;
case kFujifilmSDKStatusCodeUserCanceled:
msg = @"User Canceled";
break;
case kFujifilmSDKStatusCodeNoValidImages:
msg = @"No Valid Images";
break;
case kFujifilmSDKStatusCodeTimeout:
msg = @"Timeout Error";
break;
case kFujifilmSDKStatusCodeOrderComplete:
msg = message;
break;
case kFujifilmSDKStatusCodeUploadFailed:
msg = @"Upload Failed";
break;
case kFujifilmSDKStatusCodeInvalidUserIDFormat:
msg = @"Invalid User ID Format";
break;
case kFujifilmSDKStatusCodeInvalidPromoCodeFormat:
msg = @"Invalid Promo Code Format";
break;
case kFujifilmSDKStatusCodeRequiresPhotoPermission:
msg = @"Photo Permission Required";
break;
default:
msg = @"Unknown Error";
}
//NSLog(@"fujifilmSPASDKFinishedWithStatus: statusCode: %u message: %@", statusCode, msg);
}
AppDelegate.m
Make sure to change the "com.your-company.Your-App.FujifilmSDK.Payments" to match the URL that you set above in Step 4:PayPal Payment Option. Make sure that you change the "com.your-company.Your-App.FujifilmSDK.Payments" from the example below to the url that you set in your info.plist for Step 4 above.
#import "AppDelegate.h"
#import "Fujifilm_SPA_SDK_iOS_AppSwitch.h"
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[Fujifilm_SPA_SDK_iOS_AppSwitch setReturnURLScheme:@"com.your-company.Your-App.FujifilmSDK.Payments"];
return YES;
}
- (void)applicationWillResignActive:(UIApplication *)application {
}
- (void)applicationDidEnterBackground:(UIApplication *)application {
}
- (void)applicationWillEnterForeground:(UIApplication *)application {
}
- (void)applicationDidBecomeActive:(UIApplication *)application {
}
- (void)applicationWillTerminate:(UIApplication *)application {
}
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 90000
- (BOOL)application:(UIApplication *)application
openURL:(NSURL *)url
options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options {
if ([url.scheme localizedCaseInsensitiveCompare:@"com.your-company.Your-App.FujifilmSDK.Payments"] == NSOrderedSame) {
return [Fujifilm_SPA_SDK_iOS_AppSwitch handleOpenURL:url options:options];
}
return NO;
}
#endif
// If you support iOS 8, add the following method.
- (BOOL)application:(UIApplication *)application
openURL:(NSURL *)url
sourceApplication:(NSString *)sourceApplication
annotation:(id)annotation {
if ([url.scheme localizedCaseInsensitiveCompare:@"com.your-company.Your-App.FujifilmSDK.Payments"] == NSOrderedSame) {
return [Fujifilm_SPA_SDK_iOS_AppSwitch handleOpenURL:url sourceApplication:sourceApplication];
}
return NO;
}
@end
Additional notes and debugging help
The following are some notes to help with integrating with Fujifilm SPA iOS SDK.
Use Requirements:
- The phone must have internet access to begin the SDK, and must retain access throughout the checkout process. If the connection is lost during the checkout process, an alert will be shown notifying the user that an internet connection is required
- A maximum of 100 images can be sent to the SDK in a given checkout process. If more than 100 images are sent, only the first 100 will be processed
- Only
jpeg
,png
, andheic
files are supported - The maximum size of a single file is 20MB
General Errors
- Ensure that you updated your
info.plist
with required data listed above (NSAppTransportSecurity
,NSLocationWhenInUseUsageDescription
,NSContactsUsageDescription
, andNSPhotoLibraryUsageDescription
)
Errors that prevent the SDK from Starting
- 0 valid images
- No internet access
- Invalid APIKey. Ensure the APIKey you are using matches the environment string you are passing in (“stage’, “preview”, "production")
- Missing required frameworks: AddressBook, AddressBookUI, MobileCoreServices, SystemConfiguration, AssetsLibrary, ImageIO, and Photos
Errors that will cancel the SDK
- Loss of network or internet access before all images have finished uploading
- All images fail to upload / no remaining images to checkout with
These errors will return control back to the parent app and call fujifilmSPASDKFinishedWithStatus:andMessage
with status code and message corresponding to the cause of the error.
Errors that will prevent a specific picture from uploading or being processed
- An image is over 20MB
- An image is of an unsupported file format Image file is corrupt or is uploaded unsuccessfully, making it corrupt
These Errors will not cancel the SDK, and as such, they will not directly return an error code to fujifilmSPASDKFinishedWithStatus:andMessage
. However, if enough images are removed such that 0 images are remaining then the SDK will be terminated.
Feedback
We’re very interested in your feedback! If you run into any trouble, have a suggestion, or want to let us know what worked well send us an email to [email protected] or use the web form at https://www.fujifilmapi.com/contact-us.
License
IMPORTANT - PLEASE READ THE FOLLOWING TERMS AND CONDITIONS CAREFULLY BEFORE USING THE FOLLOWING COMPUTER CODE (THE “CODE”). USE OF THE CODE IS AT YOUR OWN RISK. THE CODE IS PROVIDED “AS IS”, WITH ANY AND ALL FAULTS, DEFECTS AND ERRORS, AND WITHOUT ANY WARRANTY OF ANY KIND. FUJIFILM DISCLAIMS ALL WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, WITHOUT LIMITATION, ALL IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT, WITH RESPECT TO THE CODE OR DEFECTS IN OPERATION OR ANY PARTICULAR APPLICATION OR USE OF THE CODE. FUJIFILM DOES NOT WARRANT THAT THE CODE WILL MEET YOUR REQUIREMENTS OR EXPECTATIONS, THAT THE CODE WILL WORK ON ANY HARDWARE, OPERATING SYSTEM OR WITH ANY SOFTWARE, THAT THE OPERATION OF THE CODE WILL BE UNINTERRUPTED, FREE OF HARMFUL COMPONENTS OR ERROR-FREE, OR THAT ANY KNOWN OR DISCOVERED ERRORS WILL BE CORRECTED. FUJIFILM SHALL NOT BE LIABLE TO YOU OR ANY THIRD PARTY FOR ANY LOSS OF PROFIT, LOSS OF DATA, COMPUTER FAILURE OR MALFUNCTION, INTERRUPTION OF BUSINESS, OR OTHER DAMAGE ARISING OUT OF OR RELATING TO THE CODE, INCLUDING, WITHOUT LIMITATION, EXEMPLARY, PUNITIVE, SPECIAL, STATUTORY, DIRECT, INDIRECT, INCIDENTAL, CONSEQUENTIAL, TORT OR COVER DAMAGES, WHETHER IN CONTRACT, TORT OR OTHERWISE, INCLUDING, WITHOUT LIMITATION, DAMAGES RESULTING FROM THE USE OR INABILITY TO USE THE CODE, EVEN IF FUJIFILM HAS BEEN ADVISED OR AWARE OF THE POSSIBILITY OF SUCH DAMAGES.