Inlines
Overview
As of v6.0.0, the Granify SDK will support display of inline campaigns.
Inline campaigns display via views embedded in your application’s user interface. Initially, these views are hidden, but are expanded with an animation to reveal the campaign. They will usually contain a headline and a message. The below image shows an example inline campaign in the red box.
Implementation
To enable the Granify SDK to display inline campaigns you must add at least one GranifyInlineView
to your app. Doing so requires the following steps:
- Add the
parentView
parameter to yourGranify.trackPageView(...)
calls - Add the
GranifyInlineView
to your UI - Set the expand and collapse handlers on the
GranifyInlineView
throughGranifyInlineView.setExpandHandler(expandHandler:)
andGranifyInlineView.setCollapseHandler(collapseHandler:)
- Set the
GranifyInlineViewLabel
throughGranifyInlineView.setLabel(label:)
The following sections will go through these steps in greater detail
Add the parentView parameter to your trackPageView calls
Inlines require a parent UIView
to be passed through Granify.trackPageView(...)
in the parentView
parameter. This parent view must be a superview containing all the subviews on the current page. We require this view to locate all the GranifyInlineView
on the page.
Adding a GranifyInlineView to your UI
Inline campaign support requires clients to add a GranifyInlineView
to every position on a page where it is desired for an inline to appear. The GranifyInlineView
class extends UIView
and can be added to your user interface in the same way. In the most common case, a GranifyInlineView
will not initially be visible in your UI. If you are using interface builder and storyboards the easiest way to achieve this is by setting the height constraint to be 0.
The following are recommended constraints if you are using interface builder and storyboards.
- Height constraint of 0
- Top of the
GranifyInlineView
constrained to the bottom of the view above - Bottom of the
GranifyInlineView
constrained to the top of the view below
If you are adding the GranifyInlineView
to a UIStackView
then only the height constraint of 0 is needed
Please note that the above are only recommendations. The only requirements for an inline to be displayed in a GranifyInlineView
are that they are initially 0 height and can be expanded/collapsed via a handler. Your user interface may require different constraints to meet these conditions.
Setting GranifyInlineView Expand And Collapse Handlers
As mentioned in the previous section, a GranifyInlineView
will not initially be visible in your UI. As a result, Granify requires clients to pass in two handlers:
- An expand handler of type
GranifyInlineViewExpander
that will expand theGranifyInlineView
allowing an inline campaign to appear. All views below theGranifyInlineView
must accommodate the expansion and must be updated accordingly. The expanded height of theGranifyInlineView
will be determined by Granify. - A collapse handler of type
GranifyInlineViewCollapser
that will collapse theGranifyInlineView
back to its initial state
Expand handlers are passed through GranifyInlineView.setExpandHandler(expandHandler:)
. Collapse handlers are passed through GranifyInlineView.setCollapseHandler(collapseHandler:)
. We recommend that GranifyInlineView.setExpandHandler(expandHandler:)
and GranifyInlineView.setCollapseHandler(collapseHandler:)
are called in viewDidLoad()
of the current view controller. If handlers are not set in the GranifyInlineView
, inlines cannot be displayed. The Granify SDK handles animation of the inlines internally. Do NOT add any additional animation handling.
Alternatively, if a GranifyInlineView
is created programatically, these handlers can be passed in during initialization.
If you are using the recommended constraints from above, we recommend using the handlers below. However, depending on your application’s views you may need more complex handlers.
extension UIView {
var heightConstraint: NSLayoutConstraint? {
return constraints.first(where: {
$0.firstAttribute == .height && $0.relation == .equal
})
}
// expand handler (pass into `GranifyInlineView.setExpandHandler(expandHandler:)`)
func expandZeroHeightConstraint(toHeight: CGFloat) {
self.heightConstraint?.constant = toHeight
self.frame = CGRect(x: self.frame.minX, y: self.frame.minY, width: self.frame.width, height: toHeight)
}
// collapse handler (pass into `GranifyInlineView.setCollapseHandler(collapseHandler:)`
func collapseZeroHeightConstraint() {
self.heightConstraint?.constant = 0
self.frame = CGRect(x: self.frame.minX, y: self.frame.minY, width: self.frame.width, height: 0)
}
}
Setting the GranifyInlineViewLabel
Additionally, a GranifyInlineViewLabel
must be applied to each GranifyInlineView
via GranifyInlineView.setLabel(label:)
.
A GranifyInlineViewLabel
has three fields:
- name: Required field. Identifies the associated
GranifyInlineView
. This field must be unique per location on a parent view. If a parent view containing aGranifyInlineView
is reused in several places only one unique name is required. - productId: The ID of the associated product. Required when the
GranifyInlineView
is located on a product page, or on any page where it is associated with a specific product (ie. inlines associated with a cart product). - sku: Field that is required when the associated
GranifyInlineView
is associated with a specific productID/SKU combination.
For example, a GranifyInlineView
located on a product page must use the name
and productId
parameters.
On a cart page, a GranifyInlineView
not associated with any product (for example at the top of the page) would only require the name
field in the GranifyInlineViewLabel
. However, if the GranifyInlineView
was associated with a product in the cart, then the name
and productId
fields would be required. The sku
field would also be required if there are multiple skus for that productId
. If these labels are not properly created, inline display will not work properly.
For a dynamically created GranifyInlineView
, such as those in a UITableView
or UICollectionView
, the GranifyInlineViewLabel
must be reapplied any time the view content is updated to account for recycling of the GranifyInlineView
. In essence, if the contents of the superview of a GranifyInlineView
change, the GranifyInlineView
should be assigned a new GranifyInlineViewLabel
to reflect the new content of its superview (e.g. product id, sku).
A list of names used in GranifyInlineViewLabel
must be given back to a Granify Product Manager or Account Manager so that we can create campaigns targeted at those labels.
Testing your GranifyInlineView
Because a GranifyInlineView
is embedded into your user interface it is imperative that they be tested extensively to make sure that expanding them does not interfere with any of your other views.
The Granify.overrideGranifyInlineView(label:)
function can be used to test inline campaigns. This function takes in a JSON object specifying the details of a GranifyInlineViewLabel
. Example JSON objects for a product page or cart page can be seen below.
Product Page
{ "name": "aboveProductPageProductPhoto", "product_id": <product id> }
Cart Page Inline associated with product
{ "name": "bottomOfListedProduct", "product_id": <cart product id>, "sku": <cart product sku>}
Inline not associated with a product
{ "name": "aboveCartPageTotal"}
Ideally, you would have a text box in a debug menu that calls Granify.overrideGranifyInlineView(label:)
when the JSON object is input. Using this override makes any matched inline campaign appear in the GranifyInlineView
with the GranifyInlineViewLabel
you specified. Using this in conjunction with Granify.showConcept(conceptId:)
can be used to display a specific campaign in your desired GranifyInlineView
.
Inlines and Restricted States
Please note that inlines are not hidden when a restricted state is set; they persist regardless of restricted states.
Troubleshooting
- Using Interface Builder to change a
UIView
to custom classGranifyInlineView
results inGranifyInlineView
not being found.- Use
GFYGranifyInlineView
instead ofGranifyInlineView
in theCustom Class -> Class
field in interface builder to avoid this error.
- Use