ログインのためにWebサービスを呼び出すiOSアプリを開発していますが、その時点でログイン資格情報をベンダー識別子(identifierForVendor)とともにWebサーバーに送信し、それらの資格情報に対してデバイスを一意に識別します。 。
IdentifierForVendorを取得しました
NSString *uuid = [[UIDevice currentDevice] identifierForVendor].UUIDString
この識別子は、ウェブサーバーのデータベースとデバイスデータベースに保存されます。次回ユーザーがアプリケーションを開き、ウェブサーバーからデータをダウンロードしようとすると、まずユーザーデバイスのローカルidentifierForVendorがウェブサーバーに保存された識別子と比較されます。
ユーザーがアプリをアンインストールして再インストールすると問題が発生し、identifierForVendorが変更されていることがわかりました。そのため、ユーザーはこれ以上先に進むことができません。
Apple documentation IDevice Documentation
そこに記載されているように、同じベンダーのすべてのアプリがデバイスからアンインストールされた場合、そのベンダーのアプリの新規インストール時に新しいidentifierForVendorが使用されます。
私の場合、これにどう対処するのですか?
あなたはKeyChainでそれを保持することができます
-(NSString *)getUniqueDeviceIdentifierAsString
{
NSString *appName=[[[NSBundle mainBundle] infoDictionary] objectForKey:(NSString*)kCFBundleNameKey];
NSString *strApplicationUUID = [SSKeychain passwordForService:appName account:@"incoding"];
if (strApplicationUUID == nil)
{
strApplicationUUID = [[[UIDevice currentDevice] identifierForVendor] UUIDString];
[SSKeychain setPassword:strApplicationUUID forService:appName account:@"incoding"];
}
return strApplicationUUID;
}
通常、identifierForVendor
は使用しないでください。代わりに、NSUUID
を使用してカスタムUUIDを生成し、それをキーチェーンに保存します(アプリを削除して再インストールしてもキーチェーンは削除されないため)。
@nerowolfeの answer に追加。
SSKeychainは、デフォルトの同期モードとしてkSecAttrSynchronizableAny
を使用します。おそらくidentifierForVendor
を複数のデバイス間で同期させたくないので、ここにコードを示します。
// save identifierForVendor in keychain without sync
NSError *error = nil;
SSKeychainQuery *query = [[SSKeychainQuery alloc] init];
query.service = @"your_service";
query.account = @"your_account";
query.password = [[[UIDevice currentDevice] identifierForVendor] UUIDString];
query.synchronizationMode = SSKeychainQuerySynchronizationModeNo;
[query save:&error];
KeyChainを使用してVendorIdentifierを保存してみてください。アプリをアンインストールしても、デバイスがリセットされるまで存在します。
OK。私はサードパーティ、つまりSSKeychainを使いたくありませんでした。だからこれは私が試したコードで、かなりシンプルでうまく機能しています:
NSString *bundleId = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleIdentifier"];
KeychainItemWrapper *keychainItem = [[KeychainItemWrapper alloc] initWithIdentifier:bundleId accessGroup:nil];
if(![keychainItem objectForKey:(__bridge id)(kSecValueData)]){
NSString *idfa = [[[UIDevice currentDevice] identifierForVendor] UUIDString];
[keychainItem setObject:idfa forKey:(__bridge id)(kSecValueData)];
NSLog(@"saving item %@", [keychainItem objectForKey:(__bridge id)(kSecValueData)]);
}else{
NSLog(@"saved item is %@", [keychainItem objectForKey:(__bridge id)(kSecValueData)]);
}
迅速なバージョン
func UUID() -> String {
let bundleName = NSBundle.mainBundle().infoDictionary!["CFBundleName"] as! String
let accountName = "incoding"
var applicationUUID = SAMKeychain.passwordForService(bundleName, account: accountName)
if applicationUUID == nil {
applicationUUID = UIDevice.currentDevice().identifierForVendor!.UUIDString
// Save applicationUUID in keychain without synchronization
let query = SAMKeychainQuery()
query.service = bundleName
query.account = accountName
query.password = applicationUUID
query.synchronizationMode = SAMKeychainQuerySynchronizationMode.No
do {
try query.save()
} catch let error as NSError {
print("SAMKeychainQuery Exception: \(error)")
}
}
return applicationUUID
}
一意の番号をデバイスにリンクする明確な方法はもうありません。これはAppleプライバシーガイドラインでは許可されていません。
キーチェーンに独自の一意のIDを保存することもできますが、ユーザーがデバイスをクリアすると、このIDもなくなります。
デバイスをユーザーにリンクする が一般的に間違っているのは、ユーザーではなくデバイスを識別するためです。そのため、ユーザーが再ログインでき、ベンダーIDがユーザーアカウントにバインドされるように、APIを変更する必要があります。
また、ユーザーがiPhoneやiPadなどのデバイスを複数持ち、両方でアプリを使用するとどうなりますか?認証は一意のIDに基づいているため、これは実行できません。
この問題には KeychainAccess podを使用していました。
ポッドファイルで:
pod 'KeychainAccess', '~> 2.4' //If you are using Swift 2.3
pod 'KeychainAccess' //Defaults to 3.0.1 which is in Swift 3
キーチェーンでUUIDを設定するファイルにKeychainAccess
モジュールをインポートします
import KeychainAccess
以下のコードを使用して、キーチェーンからUUIDを設定および取得します。
注:BundleIdはキーでUUIDは値です
var bundleID = NSBundle.mainBundle().bundleIdentifier
var uuidValue = UIDevice.currentDevice().identifierForVendor!.UUIDString
//MARK: - setVenderId and getVenderId
func setVenderId() {
let keychain = Keychain(service: bundleID!)
do {
try keychain.set(venderId as String, key: bundleID!)
print("venderId set : key \(bundleID) and value: \(venderId)")
}
catch let error {
print("Could not save data in Keychain : \(error)")
}
}
func getVenderId() -> String {
let keychain = Keychain(service: bundleID!)
let token : String = try! keychain.get(bundleID!)!
return token
}