Guia de Recuperação de Dados do Dispositivo
Guia completo para recuperar identificadores e parâmetros de dispositivo específicos de cada plataforma, necessários para atribuição precisa via API S2S e mensuração de campanhas.
Identificadores de dispositivo obrigatórios: A Singular exige identificadores de dispositivo específicos em todas as requisições da API para atribuição precisa.
Plataformas móveis:
- Android (Google Play): Google Advertising ID (GAID/AIFA) e App Set ID (ASID)
- Android (Amazon): Amazon Advertising ID (AMID) para dispositivos Fire
- Android (OEMs chineses): Open Advertising ID (OAID) para dispositivos sem o Google Play Services
- Android (fallback): Android ID (ANDI) somente quando nenhum outro identificador estiver disponível
- iOS: Identifier for Vendors (IDFV) e Identifier for Advertisers (IDFA) quando disponível
Parâmetros de dispositivo: Locale, marca do dispositivo, modelo do dispositivo e versão de build são obrigatórios para plataformas móveis
Os exemplos de código abaixo demonstram os métodos de recuperação para cada plataforma e tipo de identificador.
Aplicativos de exemplo
Implementações de referência
Exemplos completos e funcionais para iOS e Android demonstrando padrões de recuperação de dados do dispositivo.
Singular Device ID (SDID)
Se o seu app inclui um Singular SDK (uma integração híbrida), recupere o Singular Device ID (SDID) do SDK e envie-o como o parâmetro
sdid
em vez de coletar identificadores específicos da plataforma. O SDK gera e resolve o SDID automaticamente. Para saber como recuperar o SDID em cada SDK, consulte o guia de integração do SDK:
- SDK do iOS
- SDK do Android
- SDK do Unity
- SDK do Flutter
- SDK do React Native
- SDK do Cordova
- SDK do Unreal Engine
Para integrações server-to-server puras, sem SDK no dispositivo, gere um UUIDv4 no lado do cliente para usar como SDID, ou continue usando os identificadores específicos da plataforma documentados abaixo.
Identificadores de dispositivo iOS
Dispositivos iOS exigem IDFV (sempre) e IDFA (quando o usuário concede permissão de rastreamento), além do status de autorização do ATT, para atribuição precisa.
Identificadores iOS obrigatórios
Requisitos de identificador:
- IDFV: Obrigatório em todas as requisições S2S, independentemente da permissão de rastreamento
- IDFA: Deve ser fornecido se o consentimento do App Tracking Transparency for concedido pelo usuário
- Status do ATT: Código de status de autorização obrigatório em todas as requisições (0-3)
Guia de implementação
Identifier for Advertisers (IDFA)
O Identifier for Advertisers (IDFA) permite que os anunciantes rastreiem e atribuam as ações dos usuários (cliques em anúncios, instalações de apps) a campanhas específicas para segmentação e otimização precisas.
A partir do iOS 14.5, os usuários devem dar opt-in por meio do framework App Tracking Transparency (ATT) antes que os apps acessem o IDFA. Sem o consentimento do usuário, o IDFA retorna apenas zeros, limitando os recursos de rastreamento.
Identifier for Vendors (IDFV)
O Identifier for Vendors (IDFV) é um identificador exclusivo atribuído pela Apple ao dispositivo, específico do fornecedor/desenvolvedor. Permanece consistente em todos os apps do mesmo fornecedor no dispositivo, permitindo o rastreamento de comportamento entre apps sem identificação pessoal.
Etapas de implementação:
- Garanta que o prompt de ATT seja exibido e tratado antes de tentar acessar o IDFA
- Capture o IDFA (se autorizado) e envie ao servidor para as requisições da API
- Capture o IDFV e envie ao servidor para as requisições da API (sempre obrigatório)
- Inclua o status de autorização do ATT em todas as requisições
Exemplos de código
Solicitando a autorização do ATT e recuperando IDFA/IDFV
#import <AdSupport/AdSupport.h>
#import <AppTrackingTransparency/AppTrackingTransparency.h>
#import <UIKit/UIKit.h>
- (void)retrieveIdentifiers {
// Request ATT authorization (iOS 14.5+)
[ATTrackingManager requestTrackingAuthorizationWithCompletionHandler:^(ATTrackingManagerAuthorizationStatus status) {
dispatch_async(dispatch_get_main_queue(), ^{
switch (status) {
case ATTrackingManagerAuthorizationStatusAuthorized: {
// ATT authorized, retrieve IDFA
NSUUID *idfa = [[ASIdentifierManager sharedManager] advertisingIdentifier];
NSLog(@"IDFA: %@", [idfa UUIDString]);
NSLog(@"ATT Status: %ld", (long)status); // Status = 3
break;
}
case ATTrackingManagerAuthorizationStatusDenied:
NSLog(@"ATT Status: Denied (%ld)", (long)status); // Status = 2
break;
case ATTrackingManagerAuthorizationStatusRestricted:
NSLog(@"ATT Status: Restricted (%ld)", (long)status); // Status = 1
break;
case ATTrackingManagerAuthorizationStatusNotDetermined:
NSLog(@"ATT Status: Not Determined (%ld)", (long)status); // Status = 0
break;
default:
NSLog(@"Unknown ATT status.");
break;
}
// Retrieve IDFV (always available)
NSUUID *idfv = [[UIDevice currentDevice] identifierForVendor];
if (idfv != nil) {
NSLog(@"IDFV: %@", [idfv UUIDString]);
} else {
NSLog(@"Unable to retrieve IDFV.");
}
});
}];
}
// Call the method to retrieve identifiers
[self retrieveIdentifiers];
import AdSupport
import AppTrackingTransparency
import UIKit
func retrieveIdentifiers() {
// Request ATT authorization (iOS 14.5+)
ATTrackingManager.requestTrackingAuthorization { status in
DispatchQueue.main.async {
switch status {
case .authorized:
// ATT authorized, retrieve IDFA
let idfa = ASIdentifierManager.shared().advertisingIdentifier.uuidString
print("IDFA: \(idfa)")
print("ATT Status: \(status.rawValue)") // Status = 3
case .denied:
print("ATT Status: Denied (\(status.rawValue))") // Status = 2
case .restricted:
print("ATT Status: Restricted (\(status.rawValue))") // Status = 1
case .notDetermined:
print("ATT Status: Not Determined (\(status.rawValue))") // Status = 0
@unknown default:
print("Unknown ATT status.")
}
// Retrieve IDFV (always available)
if let idfv = UIDevice.current.identifierForVendor?.uuidString {
print("IDFV: \(idfv)")
} else {
print("Unable to retrieve IDFV.")
}
}
}
}
// Call the function to retrieve identifiers
retrieveIdentifiers()
Disponibilidade dos identificadores:
- IDFA: Requer autorização do ATT a partir do iOS 14.5+. Sem consentimento, retorna apenas zeros
- IDFV: Sempre disponível — inclua em todas as requisições da API da Singular
- Valores de status do ATT: 0=Indeterminado, 1=Restrito, 2=Negado, 3=Autorizado
Identificadores de dispositivo Android (Google Play)
Dispositivos Android com Google Play Services exigem o App Set ID (ASID) em todas as requisições, com o Google Advertising ID (GAID/AIFA) quando disponível.
Identificadores do Google Play obrigatórios
Requisitos de identificador:
- ASID: Obrigatório em todas as requisições S2S para dispositivos Google Play
- AIFA/GAID: Deve ser fornecido quando disponível (sem opt-out)
Guia de implementação
Google Advertising Identifier (GAID)
O Google Advertising Identifier (GAID), também conhecido como AIFA ou Android Advertising ID (AAID), é um identificador exclusivo e redefinível pelo usuário atribuído a dispositivos Android. Permite que anunciantes e desenvolvedores rastreiem e atribuam as ações dos usuários entre apps para segmentação e otimização de campanhas, mantendo a privacidade.
Dependências
Adicione a dependência necessária no seu
build.gradle
:
dependencies {
implementation 'com.google.android.gms:play-services-ads-identifier:18.0.1'
}
Permissões
Se o app tiver como alvo o Android 12/API nível 31+, adicione a permissão no
AndroidManifest.xml
:
<uses-permission android:name="com.google.android.gms.permission.AD_ID" />
Uso
AdIdUtils.getGoogleAdId(getApplicationContext());
Implementação
import android.content.Context;
import android.os.AsyncTask;
import android.util.Log;
import com.google.android.gms.ads.identifier.AdvertisingIdClient;
import com.google.android.gms.ads.identifier.AdvertisingIdClient.Info;
public class AdIdUtils {
public static void getGoogleAdId(Context context) {
AsyncTask.execute(new Runnable() {
@Override
public void run() {
try {
Info adInfo = AdvertisingIdClient.getAdvertisingIdInfo(context);
String adId = adInfo.getId();
boolean isLimitAdTrackingEnabled = adInfo.isLimitAdTrackingEnabled();
Log.d("GoogleAdID", "Advertising ID: " + adId);
Log.d("GoogleAdID", "Limit Ad Tracking: " + isLimitAdTrackingEnabled);
} catch (Exception e) {
Log.e("GoogleAdID", "Error retrieving GAID", e);
}
}
});
}
}
Dependências
dependencies {
implementation 'com.google.android.gms:play-services-ads-identifier:18.0.1'
}
Permissões
<uses-permission android:name="com.google.android.gms.permission.AD_ID" />
Uso
import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.launch
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
lifecycleScope.launch {
val googleAdId = AdIdUtils.getGoogleAdId(applicationContext)
Log.d("MainActivity", "Retrieved Google Ad ID: $googleAdId")
}
}
}
Implementação
import android.content.Context
import android.util.Log
import com.google.android.gms.ads.identifier.AdvertisingIdClient
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
object AdIdUtils {
suspend fun getGoogleAdId(context: Context): String? {
return withContext(Dispatchers.IO) {
try {
val adInfo = AdvertisingIdClient.getAdvertisingIdInfo(context)
val adId = adInfo.id
val isLimitAdTrackingEnabled = adInfo.isLimitAdTrackingEnabled
Log.d("GoogleAdID", "Advertising ID: $adId")
Log.d("GoogleAdID", "Limit Ad Tracking: $isLimitAdTrackingEnabled")
adId
} catch (e: Exception) {
Log.e("GoogleAdID", "Error retrieving GAID", e)
null
}
}
}
}
App Set ID (ASID)
O Android App Set ID oferece rastreamento entre apps com foco em privacidade para o mesmo desenvolvedor. Útil para análises e prevenção de fraude, mas não pode ser usado para publicidade personalizada.
Dependências
dependencies {
implementation 'com.google.android.gms:play-services-appset:16.1.0'
}
Uso
AppSetIdUtils.getAppSetId(getApplicationContext());
Implementação
import android.content.Context;
import android.util.Log;
import com.google.android.gms.appset.AppSet;
import com.google.android.gms.appset.AppSetIdClient;
import com.google.android.gms.appset.AppSetIdInfo;
import com.google.android.gms.tasks.Task;
public class AppSetIdUtils {
public static void getAppSetId(Context context) {
AppSetIdClient client = AppSet.getClient(context);
Task task = client.getAppSetIdInfo();
task.addOnSuccessListener(info - {
String appSetId = info.getId();
int scope = info.getScope();
Log.d("AppSetID", "App Set ID: " + appSetId);
Log.d("AppSetID", "Scope: " + (scope == AppSetIdInfo.SCOPE_DEVELOPER ? "Developer" : "App"));
}).addOnFailureListener(e - {
Log.e("AppSetID", "Failed to retrieve App Set ID", e);
});
}
}
Dependências
dependencies {
implementation 'com.google.android.gms:play-services-appset:16.1.0'
}
Uso
AppSetIdUtils.getAppSetId(applicationContext)
Implementação
import android.content.Context
import android.util.Log
import com.google.android.gms.appset.AppSet
import com.google.android.gms.appset.AppSetIdClient
import com.google.android.gms.appset.AppSetIdInfo
object AppSetIdUtils {
fun getAppSetId(context: Context) {
val client: AppSetIdClient = AppSet.getClient(context)
val task = client.appSetIdInfo
task.addOnSuccessListener { info -
val appSetId: String = info.id
val scope: Int = info.scope
Log.d("AppSetID", "App Set ID: $appSetId")
Log.d("AppSetID", "Scope: ${if (scope == AppSetIdInfo.SCOPE_DEVELOPER) "Developer" else "App"}")
}.addOnFailureListener { exception -
Log.e("AppSetID", "Failed to retrieve App Set ID", exception)
}
}
}
Identificadores de dispositivo Android (não Google Play)
Dispositivos Android sem o Google Play Services exigem identificadores alternativos com base no fabricante do dispositivo e no método de distribuição.
Identificador de dispositivo Amazon
AMID: O Amazon Advertising ID deve ser fornecido para dispositivos Amazon Fire sem o Google Play Services.
Amazon ID (AMID)
Amazon Advertising Identifier permite o rastreamento de publicidade redefinível pelo usuário em dispositivos Amazon Fire sem o Google Play Services, mantendo a privacidade do usuário e permitindo a atribuição.
Requisitos:
- Funciona em dispositivos Amazon Fire com Fire OS 5.1+
- Respeita a preferência de Limit Ad Tracking do usuário
- Pode não estar disponível em dispositivos sem Fire OS
Uso
AdvertisingIdHelper.getAmazonAdvertisingId(getContentResolver());
Implementação
import android.content.ContentResolver;
import android.provider.Settings;
import android.provider.Settings.SettingNotFoundException;
import android.util.Log;
public class AdvertisingIdHelper {
public static void getAmazonAdvertisingId(ContentResolver contentResolver) {
String advertisingID = "";
boolean limitAdTracking = false;
try {
limitAdTracking = Settings.Secure.getInt(contentResolver, "limit_ad_tracking") != 0;
advertisingID = Settings.Secure.getString(contentResolver, "advertising_id");
Log.d("AdvertisingID", "Amazon Advertising ID: " + advertisingID);
Log.d("LimitAdTracking", "Limit Ad Tracking: " + limitAdTracking);
} catch (SettingNotFoundException e) {
Log.e("AdvertisingID", "Advertising ID not supported on this device", e);
}
}
}
Uso
AdvertisingIdHelper.getAmazonAdvertisingId(contentResolver)
Implementação
import android.content.ContentResolver
import android.provider.Settings
import android.util.Log
object AdvertisingIdHelper {
fun getAmazonAdvertisingId(contentResolver: ContentResolver) {
try {
val limitAdTracking = Settings.Secure.getInt(contentResolver, "limit_ad_tracking") != 0
val advertisingID = Settings.Secure.getString(contentResolver, "advertising_id")
Log.d("AdvertisingID", "Amazon Advertising ID: $advertisingID")
Log.d("LimitAdTracking", "Limit Ad Tracking: $limitAdTracking")
} catch (e: Settings.SettingNotFoundException) {
Log.e("AdvertisingID", "Advertising ID not supported on this device", e)
}
}
}
Identificador de OEM chinês
OAID: O Open Advertising Identifier deve ser fornecido para dispositivos fabricados na China sem o Google Play Services.
Open Advertising ID (OAID)
O Open Advertising Identifier (OAID) é um identificador exclusivo e anônimo para publicidade em dispositivos Android fabricados na China. Introduzido pela Mobile Security Alliance (MSA) como alternativa ao GAID para dispositivos onde o Google Play Services está indisponível.
Dispositivos suportados: Huawei, Xiaomi, OPPO, Vivo e outros dispositivos Android fabricados na China
Acesse via MSA SDK ou Huawei Mobile Services (HMS).
Dependências
dependencies {
implementation 'com.bun.msa.sdk:msa:1.0.26'
}
Implementação
import android.os.Bundle;
import android.util.Log;
import androidx.appcompat.app.AppCompatActivity;
import com.bun.msa.sdk.DeviceId;
import com.bun.msa.sdk.DeviceIdSupplier;
import com.bun.msa.sdk.IIdentifierListener;
public class MainActivity extends AppCompatActivity {
private static final String TAG = "OAIDExample";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
getOAID();
}
private void getOAID() {
try {
DeviceId deviceId = new DeviceId(this);
deviceId.getDeviceIds(new IIdentifierListener() {
@Override
public void onSupport(boolean isSupport, DeviceIdSupplier supplier) {
if (isSupport && supplier != null) {
String oaid = supplier.getOAID();
Log.d(TAG, "OAID: " + oaid);
} else {
Log.e(TAG, "OAID not supported on this device");
}
}
});
} catch (Exception e) {
Log.e(TAG, "Error retrieving OAID", e);
}
}
}
Dependências
dependencies {
implementation 'com.bun.msa.sdk:msa:1.0.26'
}
Implementação
import android.os.Bundle
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
import com.bun.msa.sdk.DeviceId
import com.bun.msa.sdk.DeviceIdSupplier
import com.bun.msa.sdk.IIdentifierListener
class MainActivity : AppCompatActivity() {
companion object {
private const val TAG = "OAIDExample"
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
getOAID()
}
private fun getOAID() {
try {
val deviceId = DeviceId(this)
deviceId.getDeviceIds(object : IIdentifierListener {
override fun onSupport(isSupport: Boolean, supplier: DeviceIdSupplier?) {
if (isSupport && supplier != null) {
val oaid = supplier.oAID
Log.d(TAG, "OAID: $oaid")
} else {
Log.e(TAG, "OAID not supported on this device")
}
}
})
} catch (e: Exception) {
Log.e(TAG, "Error retrieving OAID", e)
}
}
}
Fallback de Android ID
Restrições do ANDI: O Android ID só pode ser fornecido se nenhum outro identificador estiver disponível E o app não for distribuído via Google Play Store. Proibido para apps do Google Play.
Android ID (ANDI)
O Android ID é um identificador exclusivo de 64 bits gerado quando o dispositivo é configurado pela primeira vez. A partir do Android 8.0 (Oreo), é limitado por app e por usuário — apps diferentes recebem Android IDs diferentes, a menos que compartilhem a mesma chave de assinatura.
Persistência: Permanece constante, a menos que o dispositivo seja restaurado de fábrica ou o app seja desinstalado/reinstalado após uma atualização OTA.
import android.provider.Settings;
import android.content.Context;
String androidId = Settings.Secure.getString(
context.getContentResolver(),
Settings.Secure.ANDROID_ID
);
import android.provider.Settings
val androidId: String = Settings.Secure.getString(
contentResolver,
Settings.Secure.ANDROID_ID
)
Identificadores web e multiplataforma
Aplicações web e implementações multiplataforma exigem o Singular Device ID (SDID) para rastreamento de atribuição preciso.
Identificador web obrigatório
SDID: O Singular Device ID é obrigatório em todas as requisições S2S para plataformas Web, PC, Console e CTV.
ID de dispositivo do Singular Web SDK
O Singular Device ID (SDID) oferece rastreamento consistente entre sessões para aplicações web e plataformas não móveis.
Pré-requisitos: O Singular Web SDK deve estar implementado e inicializado antes de recuperar o SDID.
Uso
// Retrieve SDID after Singular SDK initialization
const sdid = window.singularSdk.getSingularDeviceId();
console.log("Singular Device ID:", sdid);
Observação de implementação:
Chame
getSingularDeviceId()
somente após o Singular SDK ser inicializado com sucesso — tentar recuperá-lo antes da inicialização retorna null.
Parâmetros de dispositivo móvel
Os parâmetros de dispositivo obrigatórios fornecem contexto essencial para atribuição e análises em plataformas móveis.
Parâmetros obrigatórios
Plataformas móveis: Locale, marca do dispositivo, modelo do dispositivo e build são obrigatórios em todas as requisições S2S para iOS e Android.
Recuperação de parâmetros
Colete informações de locale, fabricante, modelo e build para um perfil completo do dispositivo.
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import <sys/sysctl.h>
// Retrieve Locale
NSString *retrieveLocale() {
NSString *locale = [[NSLocale currentLocale] localeIdentifier];
NSLog(@"Locale: %@", locale);
return locale;
}
// Retrieve Manufacturer (always Apple for iOS)
NSString *retrieveManufacturer() {
return @"Apple";
}
// Retrieve Device Model
NSString *deviceModel() {
size_t bufferSize = 64;
char model[bufferSize];
int status = sysctlbyname("hw.machine", model, &bufferSize, NULL, 0);
if (status == 0) {
NSString *deviceModel = [NSString stringWithCString:model encoding:NSUTF8StringEncoding];
NSLog(@"Device Model: %@", deviceModel);
return deviceModel;
} else {
NSLog(@"Unable to retrieve device model.");
return nil;
}
}
// Retrieve Build Version
NSString *buildVersion() {
size_t bufferSize = 64;
char build[bufferSize];
int status = sysctlbyname("kern.osversion", build, &bufferSize, NULL, 0);
if (status == 0) {
NSString *buildVersion = [NSString stringWithCString:build encoding:NSUTF8StringEncoding];
NSLog(@"Build Version: %@", buildVersion);
return buildVersion;
} else {
NSLog(@"Unable to retrieve build version.");
return nil;
}
}
import Foundation
import UIKit
// Retrieve Locale
func retrieveLocale() - String {
let locale = Locale.current.identifier
print("Locale: \(locale)")
return locale
}
// Retrieve Manufacturer (always Apple for iOS)
func retrieveManufacturer() - String {
return "Apple"
}
// Retrieve Device Model
func deviceModel() - String? {
var systemInfo = utsname()
uname(&systemInfo)
let machineMirror = Mirror(reflecting: systemInfo.machine)
let identifier = machineMirror.children.reduce("") { identifier, element in
guard let value = element.value as? Int8, value != 0 else { return identifier }
return identifier + String(UnicodeScalar(UInt8(value)))
}
print("Device Model: \(identifier)")
return identifier
}
// Retrieve Build Version
func buildVersion() - String? {
var size: Int = 0
sysctlbyname("kern.osversion", nil, &size, nil, 0)
var build = [CChar](repeating: 0, count: size)
sysctlbyname("kern.osversion", &build, &size, nil, 0)
let buildVersion = String(cString: build)
print("Build Version: \(buildVersion)")
return buildVersion
}
import android.os.Build;
import java.util.Locale;
// Locale (lc parameter)
String locale = Locale.getDefault().toString();
// Device Make (ma parameter)
String deviceMake = Build.MANUFACTURER;
// Device Model (mo parameter)
String deviceModel = Build.MODEL;
// Build (bd parameter)
String build = "Build/" + Build.ID;
import android.os.Build
import java.util.Locale
// Locale (lc parameter)
val locale: String = Locale.getDefault().toString()
// Device Make (ma parameter)
val deviceMake: String = Build.MANUFACTURER
// Device Model (mo parameter)
val deviceModel: String = Build.MODEL
// Build (bd parameter)
val build: String = "Build/" + Build.ID
Mapeamento de parâmetros:
- Locale (lc): Código de idioma e região (ex.: en_US, zh_CN)
- Make (ma): Fabricante do dispositivo (Apple, Samsung, Xiaomi)
- Model (mo): Modelo específico do dispositivo (iPhone14,2, SM-G991B)
- Build (bd): Versão de build do SO prefixada com "Build/"
Parâmetros de timestamp
Os parâmetros de timestamp opcionais descrevem o estado de instalação e atualização do dispositivo. Esses são valores reportados pelo SO, coletados no dispositivo e enviados na requisição SESSION.
Recuperação de install_time
install_time
é o timestamp Unix (segundos) reportado pelo SO de quando o app foi fisicamente instalado no dispositivo. No Android, leia
PackageInfo.firstInstallTime
(milissegundos) e divida por 1000. O iOS não expõe nenhuma API de horário de instalação, então use a data de criação do diretório Documents do app como a aproximação padrão.
#import <Foundation/Foundation.h>
// install_time: creation date of the Documents directory (proxy)
NSFileManager *fm = [NSFileManager defaultManager];
NSURL *docs = [[fm URLsForDirectory:NSDocumentDirectory
inDomains:NSUserDomainMask] firstObject];
NSDictionary *attrs = [fm attributesOfItemAtPath:docs.path error:nil];
NSDate *created = attrs[NSFileCreationDate];
long installTime = (long)[created timeIntervalSince1970];
NSLog(@"install_time: %ld", installTime);
import Foundation
// install_time: creation date of the Documents directory (proxy)
let docs = FileManager.default
.urls(for: .documentDirectory, in: .userDomainMask).first!
let attrs = try? FileManager.default.attributesOfItem(atPath: docs.path)
let created = attrs?[.creationDate] as? Date
let installTime = created.map { Int($0.timeIntervalSince1970) }
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
// install_time: PackageInfo.firstInstallTime is in milliseconds
long installTime = 0L;
try {
PackageInfo info = getPackageManager().getPackageInfo(getPackageName(), 0);
installTime = info.firstInstallTime / 1000L; // seconds
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
import android.content.pm.PackageManager
// install_time: PackageInfo.firstInstallTime is in milliseconds
val installMillis = packageManager
.getPackageInfo(packageName, 0)
.firstInstallTime
val installTime = installMillis / 1000 // seconds
Este é o horário de instalação do dispositivo, não o horário de instalação de atribuição da Singular. A atribuição usa o timestamp da sessão (
utime
) da primeira sessão (
install=true
).
Recuperação de update_time
update_time
é o timestamp Unix (segundos) reportado pelo SO de quando o app foi atualizado pela última vez no dispositivo. No Android, leia
PackageInfo.lastUpdateTime
(milissegundos) e divida por 1000. O iOS não expõe nenhuma API de horário de atualização, então use a data de criação do executável do bundle do app, que é reescrita a cada atualização, como a aproximação padrão.
#import <Foundation/Foundation.h>
// update_time: creation date of the app bundle executable (proxy)
NSString *execPath = [[NSBundle mainBundle] executablePath];
NSDictionary *attrs = [[NSFileManager defaultManager]
attributesOfItemAtPath:execPath error:nil];
NSDate *updated = attrs[NSFileCreationDate];
long updateTime = (long)[updated timeIntervalSince1970];
NSLog(@"update_time: %ld", updateTime);
import Foundation
// update_time: creation date of the app bundle executable (proxy)
let execPath = Bundle.main.executablePath!
let attrs = try? FileManager.default.attributesOfItem(atPath: execPath)
let updated = attrs?[.creationDate] as? Date
let updateTime = updated.map { Int($0.timeIntervalSince1970) }
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
// update_time: PackageInfo.lastUpdateTime is in milliseconds
long updateTime = 0L;
try {
PackageInfo info = getPackageManager().getPackageInfo(getPackageName(), 0);
updateTime = info.lastUpdateTime / 1000L; // seconds
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
import android.content.pm.PackageManager
// update_time: PackageInfo.lastUpdateTime is in milliseconds
val updateMillis = packageManager
.getPackageInfo(packageName, 0)
.lastUpdateTime
val updateTime = updateMillis / 1000 // seconds
Em um dispositivo onde o app nunca foi atualizado, isso equivale a
install_time
. Reflete atualizações do app, não atividade de atribuição.