Table of Contents

Reading results and handling errors

RetrieveData works asynchronously: it sends the request and returns immediately, then raises OnRetrieveData when rows arrive or OnRetrieveDataError when the request fails. This chapter explains the shape of the returned data, how to read it in the result handler, the limits that cause errors, and a complete live-dashboard example that ties authentication, requesting, and reading together.

The result structure

Results land in Data. Its Data list holds one entry per result row, and each row is a TTMSFNCCloudGoogleAnalyticsStringArray — an array of column values as strings. Columns appear in the order the metrics and dimensions were requested, so a request for smSessions with tdDate and udUserType yields rows of three columns: session count, date, and user type. Use Data.Data.Count for the row count and Length(Row) for the column count; convert numeric metric strings with StrToFloatDot when you need values rather than text.

Reading rows in the result handler

Read Data inside OnRetrieveData; that handler runs once the rows are available. Pair it with OnRetrieveDataError so transport failures and rejected requests surface to the user instead of failing silently.

procedure TForm1.AnalyticsRetrieveData(Sender: TObject;
  const ARequestResult: TTMSFNCCloudBaseRequestResult);
var
  Row: TTMSFNCCloudGoogleAnalyticsStringArray;
  I, J: Integer;
begin
  // Data.Data holds one entry per result row; each row is an array of column values
  // in the order the metrics and dimensions were requested.
  for I := 0 to FAnalytics.Data.Data.Count - 1 do
  begin
    Row := FAnalytics.Data.Data[I];
    for J := 0 to Length(Row) - 1 do
      Memo1.Lines.Add(Row[J]);
  end;
end;

procedure TForm1.AnalyticsRetrieveDataError(Sender: TObject; const AError: string);
begin
  // Fires for transport errors and for metric/dimension limit violations.
  ShowMessage('Analytics error: ' + AError);
end;

Limits and the ErrorMessages property

Analytics caps how many metrics and dimensions one request may combine. When a request exceeds those limits the component reports the problem through OnRetrieveDataError rather than returning rows. The ErrorMessages property holds the text used for those metric and dimension limit messages, so you can localize or reword them. If a report comes back empty or errors out, trim the number of requested categories first — that is the most common cause.

Putting it together: a live dashboard

This capstone combines all three chapters: it connects with persisted tokens, builds a realtime request once OnConnected confirms the tokens are valid, and prints each returned row in OnRetrieveData. Drive it from a timer that calls RetrieveData on an interval to keep the dashboard current.

procedure TForm1.StartDashboard;
begin
  FAnalytics := TTMSFNCCloudGoogleAnalytics.Create(Self);
  FAnalytics.Authentication.ClientID := '<your-client-id>.apps.googleusercontent.com';
  FAnalytics.Authentication.Secret := '<your-client-secret>';
  FAnalytics.Authentication.CallBackURL := 'http://127.0.0.1:8888';
  FAnalytics.PersistTokens.Location := plIniFile;
  FAnalytics.PersistTokens.Key := TTMSFNCUtils.AddBackslash(TTMSFNCUtils.GetDocumentsPath)
    + FAnalytics.ClassName + '.ini';
  FAnalytics.LoadTokens;
  FAnalytics.ViewID := '<your-view-id>';

  FAnalytics.OnConnected := DashboardConnected;
  FAnalytics.OnRetrieveData := DashboardData;
  FAnalytics.OnRetrieveDataError := DashboardError;
  FAnalytics.Connect;
end;

procedure TForm1.DashboardConnected(Sender: TObject);
var
  Metrics: TTMSFNCCloudGoogleAnalyticsRealtimeMetricsArray;
  GeoDims: TTMSFNCCloudGoogleAnalyticsRealtimeGeoDimensionArray;
begin
  // Build the request only once the connection (and tokens) are confirmed.
  SetLength(Metrics, 1);
  Metrics[0] := rtActiveUsers;
  SetLength(GeoDims, 2);
  GeoDims[0] := rtgdCountry;
  GeoDims[1] := rtgdCity;

  FAnalytics.RequestData.RealtimeMetrics := Metrics;
  FAnalytics.RequestData.RealtimeGeoDimension := GeoDims;
  FAnalytics.RetrieveData;
end;

procedure TForm1.DashboardData(Sender: TObject;
  const ARequestResult: TTMSFNCCloudBaseRequestResult);
var
  I: Integer;
begin
  for I := 0 to FAnalytics.Data.Data.Count - 1 do
    Memo1.Lines.Add(string.Join(' | ', FAnalytics.Data.Data[I]));
end;

procedure TForm1.DashboardError(Sender: TObject; const AError: string);
begin
  Memo1.Lines.Add('Error: ' + AError);
end;

Common mistakes

  • Reading Data outside OnRetrieveData. The call is asynchronous; the rows are only guaranteed to be present inside the handler.
  • Requesting too many categories at once. Exceeding the metric or dimension limit produces an error instead of data — reduce the request.
  • Forgetting to reset previous selections. RequestData keeps its arrays between calls, so clear the categories you no longer want before reissuing.
  • A stale or empty ViewID. Requests target the configured reporting view; an unset or wrong ViewID returns no usable rows.

See also