Onesecondbefore's Analytics native mobile libraries for iOS and Android let you collect information about visitors in your apps.
It consists of a set of commands (e.g. send
or set
) and
a set of (configuration) options that set certain values or change the library's behaviour.
They are described in more detail below.
Creates an instance of a tracker. When you create a tracker you should also specify configuration parameters like the URL of the tracker and the account ID. An instance of a tracker should be created as part of the start-up code of the mobile app.
import OSB
let osb = OSB.instance
osb.config(accountId: String, trackerUrl: String)
osb.config(accountId: String, trackerUrl: String, siteId: String)
osb.config(accountId: String, trackerUrl: String, siteId: String, consentCallback: (([String:String]) -> Void)?)
import com.onesecondbefore.tracker.OSB;
// Add this code to your app's initialization code
OSB osb = OSB.getInstance();
osb.config(Context context, String accountId, String trackerUrl);
osb.config(Context context, String accountId, String trackerUrl, String siteId);
parameter name | type | required | description |
---|---|---|---|
context | Context | Android only | Context / Activity. |
accountId | string | yes | Account id which specifies where your hits will be stored. |
trackerUrl | string | yes | URL of the tracker used by the app |
siteId | string | no | Optional identifier used to group hits together |
consentCallback | function | no | Optional callback function that needs to be called when consent is known. Please also refer to the installation documentation of iOS and Android on this topic. |
// Minimal configuration for account "account". The siteId will be "development"
let osb = OSB.instance
osb.config(accountId: "account", trackerUrl: "https://myconsent.mydomain.com")
// Creates a tracker for "account" with siteId "mysite.app":
let osb = OSB.instance
osb.config(accountId: "account", trackerUrl: "https://myconsent.mydomain.com",
siteId: "mysite.app")
// Add a callback function when consent is known and Google Consent Mode has to
// be updated
func googleConsentCallback(consent: [String: String]) -> Void {
Analytics.setConsent(Dictionary(
uniqueKeysWithValues: consent.map {
t, s in (ConsentType(rawValue: t),
ConsentStatus(rawValue: s))
})
)
}
let osb = OSB.instance
osb.config(accountId: "account",
trackerUrl: "https://myconsent.mydomain.com",
siteId: "mysite.app",
consentCallback: googleConsentCallback
)
// Minimal configuration for account "account".
import com.onesecondbefore.tracker.OSB;
// this is Context / Activity
OSB osb = OSB.getInstance();
osb.config(this, "development", "https://myconsent.mydomain.com");
// Creates a tracker with site-id specified:
OSB osb = OSB.getInstance();
osb.config(this, "account", "https://myconsent.mydomain.com", "mysite.app");
Retrieves the profile of the user in real-time. Contact us to enable the profile service for your account.
import OSB
let osb = OSB.instance
let profile = osb.profile()
import org.json.JSONObject;
import com.onesecondbefore.tracker.OSB;
// Add this code to your app's initialization code
OSB osb = OSB.getInstance();
JSONObject profile = osb.profile();
Sends an event hit to the analytics server.
let osb = OSB.instance
osb.send(type: OSBHitType, subtype: String, data: [String: Any])
OSB osb = OSB.getInstance();
osb.send(OSB.HitType type, String subtype, Map<String, Object> data);
parameter name | type | required | description |
---|---|---|---|
type | enum | yes | Specifies the type to send. The type will be written in column hit_type , except for 'action' , where sub_type will be used. See below for the supported types |
subtype | string | yes if type == "action" | You can choose an action from the ga() command queue (detail , add , remove , checkout , checkout_option , purchase , refund or promo_click ), or use your own action as long as they only contain letters, numbers and/or underscores. The given subtype will be stored in the hit_type column. |
data | Map or Dictionary | no | Contains additional type specific data. Is saved depending on the hit type. Read all about it in the Data Options |
Below types are defined in the enum OSB.HitType
(Android) or OSBHitType
(iOS).
iOS type | Android type | description |
---|---|---|
OSBHitType.aggregate | OSB.HitType.AGGREGATE | tracks an aggregate event. All hits will be aggregated during sessionization and by pushed to the trash table. |
OSBHitType.event | OSB.HitType.EVENT | tracks an event. Table column hit_type will contain 'event' . |
OSBHitType.ids | OSB.HitType.IDS | tracks identifiers. |
OSBHitType.pageview | OSB.HitType.PAGEVIEW | tracks a pageview, column hit_type will contain 'pageview' . To be used if your app's navigation structure is similar to the website, e.g. a news app. |
OSBHitType.screenview | OSB.HitType.SCREENVIEW | tracks a screenview, column hit_type will contain 'screenview' . |
OSBHitType.viewable_impression | OSB.HitType.VIEWABLE_IMPRESSION | tracks a viewable_impression, column hit_type will contain 'viewable_impression' . |
let data = [
"category": "Video",
"action": "start",
"label": "Funny cat movie"
]
let osb = OSB.instance
osb.send(type: OSBHitType.event, data: data)
try osb.send(type: OSBHitType.aggregate, scope: "page", name: "scrolldepth", aggregateType:
OSBAggregateType.max, value: 0.8)
try osb.send(type: OSBHitType.event, category: "Category", action: "Action", label: "Label", value: "1",
data: [["extra1": "a", "extra2": 3.1415]])
osb.setIds(data: [["key": "a3", "value": "12345"], ["key": "a3-1", "value": "12345-1"]])
try osb.send(type: .pageview)
osb.setConsent(data: ["marketing", "social", "functional", "advertising"]);
osb.set(type: .page, data: [["article": 123]])
try osb.send(type: .pageview, data: [["b": 2]])
try osb.send(type: .pageview)
try osb.send(type: .viewable_impression, data: [["a": 1, "b": 2]])
osb.set(type: .item, data: [
["id": "sku123", "name": "Apple iPhone 14 Pro", "category": "mobile", "price": 1234.56,
"quantity": 1],
["id": "sku234", "name": "Samsung Galaxy S22", "category": "mobile", "price": 1034.56,
"quantity": 1]
])
//- try osb.send(type: .action, actionType: "purchase", data: [
//- ["id": "abcd1234", "revenue": "2269.12", "tax": "476.52", "shipping": 100,
//- "affiliation": "partner_funnel"]
//- ])
OSB osb = osb.getInstance();
Map<String, Object> data = new HashMap<>();
data.put("category", "Video");
data.put("action", "start");
data.put("label", "Funny cat movie");
osb.send(OSB.EventType.EVENT, null, data);
// or use
osb.sendEvent("Video", "start", "Funny cat movie");
Sends a pageview hit to the analytics server. Pageviews are a specific kind of event with a number of fixed parameters.
Accepts the data options for page.
let osb = OSB.instance
osb.sendPageView(url: String)
osb.sendPageView(url: String, title: String)
osb.sendPageView(url: String, title: String, referrer: String)
osb.sendPageView(url: String, title: String, referrer: String, data: [String: Any])
OSB osb = OSB.getInstance();
osb.sendPageView(String url);
osb.sendPageView(String url, String title);
osb.sendPageView(String url, String title, String referrer);
osb.sendPageView(String url, String title, String referrer, Map<String, Object> data);
Sends a pageview:
let osb = OSB.instance
osb.sendPageView("https://mysite.app/page/subpage", "Page Title")
OSB osb = osb.getInstance();
osb.sendPageView("https://mysite.app/page/subpage", "Page Title");
Sends an event hit to the analytics server.
Accepts the data options for event.
let osb = OSB.instance
osb.sendEvent(category: String)
osb.sendEvent(category: String, action: String)
osb.sendEvent(category: String, action: String, label: String)
osb.sendEvent(category: String, action: String, label: String, value: String)
osb.sendEvent(category: String, action: String, label: String, value: String, data: data)
osb.sendEvent(category: String, action: String, label: String, value: String, data: data, interaction: Bool)
OSB osb = OSB.getInstance();
osb.sendEvent(category: String)
osb.sendEvent(category: String, action: String);
osb.sendEvent(category: String, action: String, label: String);
osb.sendEvent(category: String, action: String, label: String, value: String);
osb.sendEvent(category: String, action: String, label: String, value: String, data: data);
osb.sendEvent(category: String, action: String, label: String, value: String, data: data, interaction: boolean);
Sends an event:
let osb = OSB.instance
try osb.sendEvent(category: "Category", action: "Action", label: "Label", value: "1", data: [["extra1": "a", "extra2": 3.1415]])
OSB osb = osb.getInstance();
Map<String, Object> data = new HashMap<>();
data.put("imdb rating", "5"):
osb.sendEvent("Video", "play", "Funny cat movie", 1, data);
Sends a screenview hit to the analytics server. Screenviews are a specific kind of event with a number of fixed parameters.
let osb = OSB.instance
osb.sendScreenView(name: String)
osb.sendScreenView(name: String, data: [String: Any])
OSB osb = OSB.getInstance();
osb.sendScreenView(String name);
osb.sendScreenView(String name, Map<String, Object> data);
parameter name | type | required | description |
---|---|---|---|
screenName | string | yes | Screen name. |
className | string | no | Class name. |
data | Map or Dictionary | no | Contains additional type specific data. Accepts the data options for screen. |
Sends a screenview:
let osb = OSB.instance
try osb.sendScreenView(screenName: "Homepage", className: "MainController", data: ["a":"1", "b":"2"])
OSB osb = osb.getInstance();
osb.sendScreenView("The screen name with helper", "The class name with helper");
Sets additional properties in the payload. The payload will not be sent after a set
command. Only a subsequent 'send'
will send the hit.
let osb = OSB.instance
osb.set(name: String, data: [String: Any])
OSB osb = OSB.getInstance();
osb.set(String name, Map<String, Object> data);
name | type | required | description |
---|---|---|---|
name | string | no | Name of object used to group additional data under in the JSON payload. See Data Options for more info. |
data | Map | yes | A Map (Android) or Dictionary (iOS) with additional data to store. |
let osb = OSB.instance
osb.set("ids", data: ["username": "john.doe@example.com"])
Map<String, Object> data = new HashMap<>();
data.put("username", "john.doe@example.com");
OSB osb = OSB.getInstance();
osb.set("ids", data);
Map<String, Object> data = new HashMap<>();
data.put("page_id", "123456");
data.put("other_id", "98765");
OSB osb = OSB.getInstance();
osb.set(data);
Only applies if you use our or a 3rd party IAB TCF certified Consent Gate.
Sets the additional consent of Google's Additional Consent as returned from the Consent Gate.
The response contains the IAB TC Data object. If this contains the addtlConsent
property, this method is automatically called.
The method sets the required storage and Lookup Key in NSUserDefaults (on iOS) and SharedPreferences (on Android): IABTCF_AddtlConsent
.
Use this only if you are setting all values in a custom way.
Accepts "Additional Consent" string of Google's Additonal Consent as described here.
The following storage and lookup keys are set in NSUserDefaults on iOS and SharedPreferences on Android.
key | type | description |
---|---|---|
IABTCF_AddtlConsent | String | AC string with spec version and consented Ad Technology Provider IDs. |
let osb = OSB.instance
// Set a single value
osb.setAdditionalConsent(data: ACString)
OSB osb = OSB.getInstance();
osb.setAdditionalConsent(new ACString(...));
let osb = OSB.instance
// set Additional Consent (AC) String
osb.setAdditionalConsent(data: "2~1.35.41.101~dv.9.21.81")
OSB osb = OSB.getInstance();
// set Additional Consent (AC) String
osb.setAdditionalConsent(new String("2~1.35.41.101~dv.9.21.81"));
Set the consent as returned from the WebView that contains the Consent Management Platform. This allows future events to know the consent that was given by the user.
Since version 7.5, setConsent will trigger the callback function that you entered as the consentCallback
configuration option.
Accepts the data options for consent
and described in the data options for consent.
The following storage and lookup keys are set in NSUserDefaults on iOS and SharedPreferences on Android. Only applies if you use our or a 3rd party IAB TCF certified Consent Gate. More info can be found here.
key | type | description |
---|---|---|
IABTCF_CmpSdkID | Number | The unsigned integer ID of CMP SDK |
IABTCF_CmpSdkVersion | Number | The unsigned integer version number of CMP SDK |
IABTCF_PolicyVersion | Number | The unsigned integer representing the version of the TCF that these consents adhere to. |
IABTCF_gdprApplies | Number | 1 GDPR applies in current context. 0 - GDPR does not apply in current context. Unset - undetermined (default before initialization). See the section "What does the gdprApplies value mean?" for more information. |
IABTCF_PublisherCC | String | Two-letter ISO 3166-1 alpha-2 code – Default: AA (unknown) |
IABTCF_PurposeOneTreatment | Number | 0 - no special treatment of purpose one1 - purpose one not disclosedUnset default - 0 .Vendors can use this value to determine whether consent for purpose one is required. |
IABTCF_UseNonStandardTexts | Number | 1 - CMP uses customized stack descriptions and/or modified or supplemented standard Illustrations0 - CMP did not use a non-standard stack desc. and/or modified or supplemented Illustrations |
IABTCF_TCString | String | Full encoded TC string |
IABTCF_VendorConsents | Binary String | The '0' or '1' at position n – where n's indexing begins at 0 – indicates the consent status for Vendor ID n+1; false and true respectively. eg. '1' at index 0 is consent true for vendor ID 1 |
IABTCF_VendorLegitimateInterests | Binary String | The '0' or '1' at position n – where n's indexing begins at 0 – indicates the legitimate interest status for Vendor ID n+1; false and true respectively. eg. '1' at index 0 is legitimate interest established true for vendor ID 1 |
IABTCF_PurposeConsents | Binary String | The '0' or '1' at position n – where n's indexing begins at 0 – indicates the consent status for purpose ID n+1; false and true respectively. eg. '1' at index 0 is consent true for purpose ID 1 |
IABTCF_PurposeLegitimateInterests | Binary String | The '0' or '1' at position n – where n's indexing begins at 0 – indicates the legitimate interest status for purpose ID n+1; false and true respectively. eg. '1' at index 0 is legitimate interest established true for purpose ID 1 |
IABTCF_SpecialFeaturesOptIns | Binary String | The '0' or '1' at position n – where n's indexing begins at 0 – indicates the opt-in status for special feature ID n+1; false and true respectively. eg. '1' at index 0 is opt-in true for special feature ID 1 |
IABTCF_PublisherRestrictions{ID} | String ['0','1', or '2'] | The value at position n – where n's indexing begins at 0 – indicates the publisher restriction type (0-2) for vendor n+1; (see Publisher Restrictions Types). eg. '2' at index 0 is restrictionType 2 for vendor ID 1 . {ID} refers to the purpose ID. |
IABTCF_PublisherConsent | Binary String | The '0' or '1' at position n – where n's indexing begins at 0 – indicates the purpose consent status for purpose ID n+1 for the publisher as they correspond to the Global Vendor List Purposes; false and true respectively. eg. '1' at index 0 is consent true for purpose ID 1 |
IABTCF_PublisherLegitimateInterests | Binary String | The '0' or '1' at position n – where n's indexing begins at 0 – indicates the purpose legitimate interest status for purpose ID n+1 for the publisher as they correspond to the Global Vendor List Purposes; false and true respectively. eg. '1' at index 0 is legitimate interest established true for purpose ID 1 |
IABTCF_PublisherCustomPurposesConsents | Binary String | The '0' or '1' at position n – where n's indexing begins at 0 – indicates the purpose consent status for the publisher's custom purpose ID n+1 for the publisher; false and true respectively. eg. '1' at index 0 is consent true for custom purpose ID 1 |
IABTCF_PublisherCustomPurposesLegitimateInterests | Binary String | The '0' or '1' at position n – where n's indexing begins at 0 – indicates the purpose legitimate interest status for the publisher's custom purpose ID n+1 for the publisher; false and true respectively. eg. '1' at index 0 is legitimate interest established true for custom purpose ID 1
|
let osb = OSB.instance
// Set a single value
osb.setConsent(data: String)
// Or set an array of values
osb.setConsent(data: [String])
OSB osb = OSB.getInstance();
osb.setConsent(new String[] { String, ... });
osb.setConsent(new String(...));
let osb = OSB.instance
// Purpose array
osb.setConsent(data: ["social", "analytics", "advertising", "functional"])
// IAB TCF string as returned from CMP
osb.setConsent(data: "COvFyGBOvFyGBAbAAAENAPCAAOAAAAAAAAA ... QmAgBC3ZAYzUw")
OSB osb = OSB.getInstance();
osb.setConsent(new String[] {"social", "analytics", "advertising", "functional"});
// or set IAB TCF string
osb.setConsent(new String("COvFyGBOvFyGBAbAAAENAPCAAOAAAAAAAAA ... QmAgBC3ZAYzUw"));
Set the ids. Use this to store user id's.
Accepts the data options for ids
and described in the data options for ids.
let osb = OSB.instance
// Set a single value
osb.setIds(data: [["key": String, "value": String, "label": String, hash: Boolean], ...])
Map<String, Object> ids1 = new HashMap<>();
ids1.put("key", String);
ids1.put("value", String);
ids1.put("label", String);
ids1.put("hash", Boolean);
ArrayList<Map<String, Object>> idsList = new ArrayList<>();
idsList.add(ids1);
...more ids can be added...
osb.setIds(idsList);
let osb = OSB.instance
osb.setIds(data: [["key": "a3", "value": "12345"], ["key": "a3-1", "value": "12345-1"]])
Map<String, Object> ids1 = new HashMap<>();
ids1.put("key", "a3");
ids1.put("value", "12345");
Map<String, Object> ids2 = new HashMap<>();
ids2.put("key", "b4");
ids2.put("value", "6789");
List<Map<String, Object>> idsList = new ArrayList<>();
idsList.add(ids1);
idsList.add(ids2);
osb.setIds(idsList);
The opposite of set. Resets all values to null
or their default values.
let osb = OSB.instance
osb.remove(name: String)
OSB osb = OSB.getInstance();
osb.remove(String name);
name | type | required | description |
---|---|---|---|
name | string | yes | Name of object used to group additional data under in the JSON payload. See Data Options for all the possible values. |
let osb = OSB.instance
osb.remove("ids")
OSB osb = OSB.getInstance();
osb.remove("ids");
Data options can be set along its corresponding command (event, ids, items or pageview) or set
with the set or add commands. With set
, no hit is sent to the server, whereas with send
a hit is sent to the server.
Contains the options of an action
. Actions are typically used to track ecommerce actions like purchase or checkout. It will populate the action object (Snowflake) or struct (BigQuery) in the Analytics hits table.
The following commands support the parameters:
name | type | default value | description |
---|---|---|---|
id | string | null | Order or transaction id. |
revenue | float | null | Total revenue amount. |
shipping | float | null | Shipping amount. |
tax | float | null | Tax amount. |
currency_code | string | null | Contains the currency code. We recommend using the ISO-4217 ISO codes. |
any other | string | null | Custom data key/value pairs (up to 50 per hit). Will be stored in the data array of the Analytics hits table. |
The following example shows the most common use-case of how to set and send an action with an item.
try osb.set(type: .item, data: [["id": "sku123",
"name": "Apple iPhone 14 Pro",
"category": "mobile",
"price": 1234.56,
"quantity": 1
],
["id": "sku234",
"name": "Samsung Galaxy S22",
"category": "mobile",
"price": 1034.56, "quantity": 1
]])
try osb.send(type: .action,
actionType: "purchase",
data: [["id": "abcd1234",
"revenue": 2269.12,
"tax": (2269.12 * 0.21),
"shipping": 100,
"affiliation": "partner_funnel"]])
Map<String, Object> item1 = new HashMap<>();
item1.put("id", "sku123");
item1.put("name", "Apple iPhone 14 Pro");
item1.put("category", "mobile");
item1.put("price", 1234.56);
item1.put("quantity", 1);
Map<String, Object> item2 = new HashMap<>();
item2.put("id", "sku234");
item2.put("name", "Samsung Galaxy S22");
item2.put("category", "mobile");
item2.put("price", 1034.56);
item2.put("quantity", 1);
List<Map<String, Object>> itemData = new ArrayList<>();
itemData.add(item1);
itemData.add(item2);
osb.set(OSB.SetType.ITEM, itemData);
Map<String, Object> data4 = new HashMap<>();
data4.put("id", "abcd1234");
data4.put("revenue", 2269.12);
data4.put("tax", (2269.12 * 0.21));
data4.put("shipping", 100);
data4.put("affiliation", "partner_funnel"); // Custom data item
try {
osb.send(OSB.HitType.ACTION, "purchase", data4);
} catch (IllegalArgumentException ex) {
showHitTypeError();
}
Contains the consent value.
The following commands support the parameters:
The value can be sent as a string or an array, not as a JavaScript object as with other payload data.
type | default value | description |
---|---|---|
array or string | null | Consent value like 'all' or ['necessary','advertising'] or a valid TC String (IAB Europe) from us as a provider, like: CPUP3OWPUP3OWFjAOBENChCsAP_-AH_-ABpaxK... |
Contains the options of an event. Will populate the event object (Snowflake) or struct (BigQuery) in the Analytics tables.
The following commands support the parameters:
hit_type=event
.name | type | default value | description |
---|---|---|---|
category | string | null | Contains the event category, e.g. 'video' . |
action | string | null | Contains the event action, e.g. 'play' . |
label | string | null | Contains the event label, e.g 'funny_cat_12345.mov' . |
value | number | null | Contains the event value, e.g. 0 . |
interaction | boolean | null (or true) | Contains whether the event should be considered an interaction. Only if true , will the hit be sessionized. |
any other | string | null | Custom data key/value pairs (up to 50 per hit). Will be stored in the data array of the Analytics hits table. |
Contains the options of ids. Will populate the ids
array in the Analytics tables.
The following commands support the parameters:
hit_type=ids
.name | type | default value | description |
---|---|---|---|
key | string | null | Contains the key of the id, e.g. _ga . |
value | string | null | Contains the id, e.g. 123456789.987654321 . |
label | string | null | Use this if you want to use the same key in different situations. E.g. a _ga cookie value for different accounts on the same web page. |
hash | boolean | null | Use this if you want Analytics to hash your value in-memory before storing it in the database, e.g. for
email addresses or phone numbers. Hashing is done with SHA256(salt + ':' + value) where
salt is account specific and can be retrieved from the
Onesecondbefore Console
(with proper permissions).
So if the salt is OSB and the email address is test@onesecondbefore.com, use this statement in BigQuery to calculate the hash: SELECT TO_HEX(SHA256('OSB:test@onesecondbefore.com'))
(which would be 'c2ddd2d48ea0889b95b6e2750624b9690deb4feacdc6ef81195b17138c989496' ).
|
Contains the options of items. Will populate the items
array in the Analytics tables.
The following commands support the parameters:
name | type | default value | description |
---|---|---|---|
id | string | null | Product id or sku. |
name | string | null | Product name. |
category | string | null | Product category. |
price | float | null | Product price. |
quantity | float | null | Product quantity. |
any other | string | null | Custom data key/value pairs (up to 50 per hit). Will be stored in the data array of the Analytics hits.action.items.data table. |
Contains the options of a page. Will populate the page object (Snowflake) or struct (BigQuery) in the Analytics tables.
The following commands support the parameters:
hit_type=pageview
.name | type | default value | description |
---|---|---|---|
title | string | JS: document.title | Title of the page. |
id | string | null | Unique id of the page in the Content Management System. |
url | string | JS: location.href | URL of the page, in case you want to override the default. If the value starts with the protocol (e.g. http), it is considered a full URL. If the value starts with a slash (/), everything from the path (including query string and fragment) will be overwritten. If the value starts with a question mark (?), everything after the query string (including the fragment) will be overwritten. If the value starts with a hash (#), everything after the fragment will be overwritten. |
referrer | string | JS: document.referrer | URL of the referrer, in case you want to override the default. If the value starts with the protocol (e.g. http), it is considered a full URL. If the value starts with a slash (/), everything from the path (including query string and fragment) will be overwritten. If the value starts with a question mark (?), everything after the query string (including the fragment) will be overwritten. If the value starts with a hash (#), everything after the fragment will be overwritten. |
osc_id | string | null | On-site campaign identifier. |
osc_label | string | null | An extra label in case the on-site campaign identifier alone is not enough. |
oss_keyword | string | null | On-site search query / keyword. |
oss_category | string | null | On-site search category. |
oss_total_results | integer | null | On-site search total number of search results. |
oss_results_per_page | integer | null | On-site search number of search results per result page. |
oss_current_page | integer | null | On-site search result current page number. |
any other | string | null | Custom data key/value pairs (up to 50 per hit). Will be stored in the data array of the Analytics hits table. |
Contains the options of a screen(view). Will populate the event object (Snowflake) or struct (BigQuery) in the Analytics tables.
The following commands support the parameters:
hit_type=screenview
.name | type | default value | description |
---|---|---|---|
sn | string | null | Screen name. |
cn | string | null | Class name. |
data | string | null | Custom data key/value pairs (up to 50 per hit). Will be stored in the data array of the Analytics hits table. |