Pre-fetching Screens

This section provides details on how to pre-fetch Screens and display them later

Purchasely, by default, shows the Screen with a loading indicator while fetching the Screen from the network and preparing it for display.

Using the PLYPresentation builder with its preload method (iOS: PLYPresentationBuilder), you can pre-fetch the Screen from the network before displaying it. This provides the following benefits:

  • Display the Screen only after it has been loaded from the network
  • Handle network errors gracefully
  • Show a custom loading screen
  • Pre-load the Screen while users navigate through your app, such as during onboarding screens
  • Choose not to display a Screen for a specific placement
  • Display your own Screen

Implementation

Build a PLYPresentation (Android) / PLYPresentationBuilder (iOS) for a placement or with a presentation id, then call preload

  1. An error may be returned if the presentation could not be fetched from the network.
  2. If successful, you will have a PLYPresentation instance containing the following properties:
class PLYPresentation {
  let screenId: String?
  let height: Int?
  let language: String
  let flowId: String?
  let placementId: String?
  let audienceId: String?
  let abTestId: String?
  let abTestVariantId: String?
  let type: PLYPresentationType
  let controller: PLYPresentationViewController?
  let plans: [PLYPresentationPlan]
  let metadata: PLYPresentationMetadata?
  let backgroundColor: UIColor?
}
data class PLYPresentation(
    val screenId: String?,
  	val height: Int?,
		val flowId: String?,
    val placementId: String?,
    val audienceId: String?,
    val abTestId: String?,
    val abTestVariantId: String?,
    val language: String?,
    val type: PLYPresentationType,
    val plans: List<PLYPresentationPlan>,
    val metadata: PLYPresentationMetadata?,
    val backgroundColor: String?
}
export type PurchaselyPresentation = {
  screenId: string;
  height: int | null;
  placementId?: string | null;
  audienceId?: string | null;
  abTestId?: string | null;
  abTestVariantId?: string | null;
  language?: string | null;
  type?: PLYPresentationType | null;
  plans?: PLYPresentationPlan[] | null;
  metadata: PLYPresentationMetadata;
};

A presentation can be one of the following types:

  • Normal: The default behavior, a Purchasely Screen created from our console.
  • Fallback: A Purchasely Screen, but not the one you requested, as it could not be found.
  • Deactivated: No Screen associated with that placement, possibly for a specific A/B test or an audience.
  • Client: You created a Custom Screen in the Purchasely Console and should display it. Use the list of plans to determine which offers to display to your users.
// fetch presentation for placement
PLYPresentationBuilder
    .forPlacementId("onboarding")
    .build()
    .preload { presentation, error in
         // closure to get presentation and display it
         guard let presentation = presentation, error == nil else {
             print("Error while fetching presentation: \(error?.localizedDescription ?? "unknown")")
             return
         }
         
         if presentation.type == .normal || presentation.type == .fallback {
           // Display directly
           presentation.display(from: myUIViewController)
           
           // alternatively: get the UIViewController to manage the transition yourself
					// note: this method won't work with Flows
					let purchaselyController = presentation.controller

             
         } else if presentation.type == .deactivated {
             
             // nothing to display
             
         } else if presentation.type == .client {
             let presentationId = presentation.screenId
             let planIds = presentation.plans
             
             // display your own Screen
             
         }
    }
PLYPresentation {
  placementId("onboarding")
  onCloseRequested {
    // TODO remove view from your layout
  }
}.preload { presentation, error ->
  if(error != null) {
    Log.d("Purchasely", "Error fetching Screen", error)
    return@preload
  }

  when(presentation?.type) {
    PLYPresentationType.NORMAL,
    PLYPresentationType.FALLBACK -> {
      
			// Easy: just call display
  		presentation?.display()
      
      // Intermediate: build the view to display it yourself in your layout for specific use cases
			// Note: this won't work with Flows
      val screenView = presentation.buildView(this@MainActivity) { outcome ->
        // Screen is closed, check result to know if a purchase happened
        when(outcome.purchaseResult) {
          PLYPurchaseResult.PURCHASED -> Log.d("Purchasely", "User purchased ${outcome.plan?.name}")
          PLYPurchaseResult.CANCELLED -> Log.d("Purchasely", "User cancelled purchased")
          PLYPurchaseResult.RESTORED -> Log.d("Purchasely", "User restored ${outcome.plan?.name}")
          null -> {}
        }
      }

      // Display Purchasely Screen by adding screenView to your layout
    }
    PLYPresentationType.DEACTIVATED -> {
      // Nothing to display
    }
    PLYPresentationType.CLIENT -> {
      val paywallId = presentation.screenId
      val planIds = presentation.plans
      // Display your own Screen
    }
    else -> {
      //No Screen, it means an error was triggered
    }
  }
}
try {
  // Build a request for the placement, then preload its Screen
  const request = Purchasely.presentation.placement('onboarding').build()

  const presentation = await request.preload()

  if (presentation.type === PLYPresentationType.DEACTIVATED) {
    // No Screen to display
    return
  }

  if (presentation.type === PLYPresentationType.CLIENT) {
    // Display my own Screen
    return
  }

  // Display Purchasely Screen, resolves when it is dismissed
  const outcome = await request.display()

  switch (outcome.purchaseResult) {
    case 'purchased':
    case 'restored':
      if (outcome.plan != null) {
        console.log('User purchased ' + outcome.plan.name);
      }

      break;
    case 'cancelled':
      console.log('User cancelled');
      break;
    case null:
      console.log('User dismissed: ' + outcome.closeReason);
      break;
  }

} catch (e) {
  console.error(e);
}
try {
  final request = PLYPresentationBuilder.placement("ONBOARDING").build();

  final presentation = await request.preload();

  if (presentation.type == PLYPresentationType.deactivated) {
    // No Screen to display
    return;
  }

  if (presentation.type == PLYPresentationType.client) {
    // Display my own Screen
    return;
  }

  //Display Purchasely Screen

  final outcome = await request.display();

  switch (outcome.purchaseResult) {
    case PLYPurchaseResult.cancelled:
      {
        print("User cancelled purchased");
      }
      break;
    case PLYPurchaseResult.purchased:
      {
        print("User purchased ${outcome.plan?.name}");
      }
      break;
    case PLYPurchaseResult.restored:
      {
        print("User restored ${outcome.plan?.name}");
      }
      break;
    case null:
      {
        print("User dismissed: ${outcome.closeReason}");
      }
      break;
  }
} catch (e) {
  print(e);
}
private PurchaselyRuntime.Purchasely _purchasely;

...
_purchasely.FetchPresentation("presentationId",
			OnFetchPresentationSuccess,
			Log,
			"contentId");		
...

private void OnFetchPresentationSuccess(Presentation presentation)
{
	Log("Fetch Presentation Success.");
	LogPresentation(presentation);

	switch (presentation.presentationType)
	{
		case PresentationType.Normal:
		case PresentationType.Fallback:
				_purchasely.PresentContentForPresentation(
          	presentation,
						OnPresentationResult,
						OnPresentationContentLoaded,
						OnPresentationContentClosed,
						true
				);
			break;
		case PresentationType.Unknown:
		case PresentationType.Deactivated:
			Log($"Fetched presentation with type: {presentation.presentationType}. Will not show content.");
			break;
		case PresentationType.Client:
			paywall.Show(presentation);
			break;
	}
}