protected override IEnumerable<Datum> Poll(CancellationToken cancellationToken) { List<Datum> data = new List<Datum>(); if (HasValidAccessToken) { string[] missingPermissions = GetRequiredPermissionNames().Where(p => !AccessToken.CurrentAccessToken.Permissions.Contains(p)).ToArray(); if (missingPermissions.Length > 0) ObtainAccessToken(missingPermissions); } else ObtainAccessToken(GetRequiredPermissionNames()); if (HasValidAccessToken) { ManualResetEvent startWait = new ManualResetEvent(false); List<ManualResetEvent> responseWaits = new List<ManualResetEvent>(); Exception exception = null; // can't throw exception from within the UI thread -- it will crash the app. use this variable to check whether an exception did occur. Xamarin.Forms.Device.BeginInvokeOnMainThread(() => { try { GraphRequestConnection requestConnection = new GraphRequestConnection(); #region add requests to connection foreach (Tuple<string, List<string>> edgeFieldQuery in GetEdgeFieldQueries()) { NSDictionary parameters = null; if (edgeFieldQuery.Item2.Count > 0) parameters = new NSDictionary("fields", string.Concat(edgeFieldQuery.Item2.Select(field => field + ",")).Trim(',')); GraphRequest request = new GraphRequest( "me" + (edgeFieldQuery.Item1 == null ? "" : "/" + edgeFieldQuery.Item1), parameters, AccessToken.CurrentAccessToken.TokenString, null, "GET"); ManualResetEvent responseWait = new ManualResetEvent(false); requestConnection.AddRequest(request, (connection, result, error) => { if (error == null) { FacebookDatum datum = new FacebookDatum(DateTimeOffset.UtcNow); #region set datum properties NSDictionary resultDictionary = result as NSDictionary; bool valuesSet = false; foreach (string resultKey in resultDictionary.Keys.Select(k => k.ToString())) { PropertyInfo property; if (FacebookDatum.TryGetProperty(resultKey, out property)) { object value = null; if (property.PropertyType == typeof(string)) value = resultDictionary[resultKey].ToString(); else if (property.PropertyType == typeof(bool?)) { int parsedBool; if (int.TryParse(resultDictionary[resultKey].ToString(), out parsedBool)) value = parsedBool == 1 ? true : false; } else if (property.PropertyType == typeof(DateTimeOffset?)) { DateTimeOffset parsedDateTimeOffset; if (DateTimeOffset.TryParse(resultDictionary[resultKey].ToString(), out parsedDateTimeOffset)) value = parsedDateTimeOffset; } else if (property.PropertyType == typeof(List<string>)) { List<string> values = new List<string>(); NSArray resultArray = resultDictionary[resultKey] as NSArray; for (nuint i = 0; i < resultArray.Count; ++i) values.Add(resultArray.GetItem<NSObject>(i).ToString()); value = values; } else throw new SensusException("Unrecognized FacebookDatum property type: " + property.PropertyType.ToString()); if (value != null) { property.SetValue(datum, value); valuesSet = true; } } else SensusServiceHelper.Get().Logger.Log("Unrecognized key in Facebook result dictionary: " + resultKey, LoggingLevel.Verbose, GetType()); } #endregion if (valuesSet) data.Add(datum); } else SensusServiceHelper.Get().Logger.Log("Error received while querying Facebook graph API: " + error.Description, LoggingLevel.Normal, GetType()); SensusServiceHelper.Get().Logger.Log("Response for \"" + request.GraphPath + "\" has been processed.", LoggingLevel.Verbose, GetType()); responseWait.Set(); }); responseWaits.Add(responseWait); } #endregion if (responseWaits.Count == 0) exception = new Exception("Request connection contained zero requests."); else { SensusServiceHelper.Get().Logger.Log("Starting request connection with " + responseWaits.Count + " requests.", LoggingLevel.Normal, GetType()); requestConnection.Start(); } startWait.Set(); } catch (Exception ex) { exception = new Exception("Error starting request connection: " + ex.Message); } }); startWait.WaitOne(); // wait for all responses to be processed foreach (ManualResetEvent responseWait in responseWaits) responseWait.WaitOne(); // if any exception occurred when running query, throw it now if (exception != null) throw exception; } else throw new Exception("Attempted to poll Facebook probe without a valid access token."); return data; }