Рейтинговые книги
Читем онлайн iOS. Приемы программирования - Вандад Нахавандипур

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
1 ... 83 84 85 86 87 88 89 90 91 ... 165

2. Создайте в вашем приложении новый. plist-файл и назовите его Entitlements.plist. Вставьте содержимое файла с разрешениями из профиля инициализации — в точности как они есть — в ваш файл Entitlements.plist и сохраните.

3. Перейдите в настройки сборки и найдите разрешения подписывания кода (Code Signing Entitlements). Задайте для этого раздела значение $(TARGET_NAME)/Entitlements.plist. Оно означает, что Xcode должна найти файл Entitlements.plist в целевом каталоге, имеющем такое имя.

Опишу причины, по которым нам требуется это значение. Если создать проект под названием MyProject, Xcode создаст корневой каталог (SCROOT) под названием MyProject. В нем среда создаст еще один подкаталог, который также будет называться MyProject, а уже в этом каталоге будет находиться ваш исходный код. В каталоге SCROOT (то есть в вышестоящем каталоге под названием MyProject) будет создан еще один подкаталог под названием MyProjectTests. В этом подкаталоге будут модульные тесты, интеграционные тесты и тесты пользовательского интерфейса. Под этой структурой вы найдете ваш файл Entitlements.plist (он находится по адресу MyProject/MyProject/Entitlements.plist). Программа поиска разрешений подписывания кода ищет указанный. plist-файл в каталоге SRCROOT, поэтому, если вы предоставите ей значение MyProject/MyProject/Entitlements.plist, это ее вполне устроит! $(TARGET_NAME) в Xcode — это переменная, разрешаемая в имя вашей цели, которое по умолчанию совпадает с именем вашего проекта. Следовательно, в случае с MyProject значение $(TARGET_NAME)/Entitlements.plist будет разрешаться в MyProject/Entitlements.plist.

4. Соберите приложение и убедитесь, что все работает правильно.

Если вы получите сообщение об ошибке примерно следующего содержания:

error: The data couldn't be read because it isn't in the correct format[8]

это означает, что ваши разрешения записаны в неверном формате. Распространенная ошибка, которую совершают многие iOS-программисты, связана с заполнением файла с разрешениями следующим образом:

<plist version="1.0">

<key>Entitlements</key>

<dict>

<key>application-identifier</key>

<string>F3FU372W5M.com.pixolity.ios.cookbook.SecurityApp</string>

<key>com.apple.developer.default-data-protection</key>

<string>NSFileProtectionComplete</string>

<key>get-task-allow</key>

<true/>

<key>keychain-access-groups</key>

<array>

<string>F3FU372W5M.*</string>

</array>

</dict>

</plist>

Обратите внимание: этот файл с разрешениями оформлен неправильно, так как в верхней его части содержится висячий ключ Entitlements. Этот ключ потребуется удалить, чтобы ваш файл имел следующий вид:

<plist version="1.0">

<dict>

<key>application-identifier</key>

<string>F3FU372W5M.com.pixolity.ios.cookbook.SecurityApp</string>

<key>com.apple.developer.default-data-protection</key>

<string>NSFileProtectionComplete</string>

<key>get-task-allow</key>

<true/>

<key>keychain-access-groups</key>

<array>

<string>F3FU372W5M.*</string>

</array>

</dict>

</plist>

Итак, разработка записывающего приложения закончена. Вплотную займемся приложением, которое считывает данные. Это два совершенно самостоятельных подписанных приложения, каждое из которых обладает собственным профилем инициализации:

#import «AppDelegate.h»

#import <Security/Security.h>

@implementation AppDelegate

— (BOOL) application:(UIApplication *)application

didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{

NSString *key = @"Full Name";

/* Это не ID пакета того приложения, которое записывало информацию

в связку ключей. Это и не ID пакета данного приложения. Идентификатор

пакета данного приложения — com.pixolity.ios.cookbook.SecondSecurityApp */

NSString *service = @"com.pixolity.ios.cookbook.SecurityApp";

NSString *accessGroup = @"F3FU372W5M.*";

NSDictionary *queryDictionary = @{

(__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword,

(__bridge id)kSecAttrService: service,

(__bridge id)kSecAttrAccessGroup: accessGroup,

(__bridge id)kSecAttrAccount: key,

(__bridge id)kSecReturnData: (__bridge id)kCFBooleanTrue,

};

CFDataRef data = NULL;

OSStatus found =

SecItemCopyMatching((__bridge CFDictionaryRef)queryDictionary,

(CFTypeRef *)&data);

if (found == errSecSuccess){

NSString *value = [[NSString alloc]

initWithData:(__bridge_transfer NSData *)data

encoding: NSUTF8StringEncoding];

NSLog(@"Value = %@", value);

} else {

NSLog(@"Failed to read the value with error = %ld", (long)found);

}

self.window = [[UIWindow alloc]

initWithFrame: [[UIScreen mainScreen] bounds]];

self.window.backgroundColor = [UIColor whiteColor];

[self.window makeKeyAndVisible];

return YES;

}

Может потребоваться время, чтобы привыкнуть к тому, как работает связка ключей. Не волнуйтесь, если все не заработает с пол-оборота. Просто внимательно изучите все инструкции, данные в этой главе (особенно в ее введении), чтобы четко понять, что такое группа доступа к связке ключей и как она связана с разрешениями вашего приложения.

См. также

Разделы 8.0 и 8.2.

8.7. Запись и считывание информации связки ключей из iCloud

Постановка задачи

Требуется сохранить информацию в связке ключей, а также обеспечить хранение этой информации в пользовательской связке ключей, расположенной в облаке iCloud. Так пользователь сможет получать доступ к этой связке с любых имеющихся у него устройств с iOS.

Решение

При добавлении вашего элемента к связке ключей с помощью функции SecItemAdd добавьте ключ kSecAttrSynchronizable в словарь, передаваемый этой функции. В качестве значения этого ключа передайте kCFBooleanTrue.

Обсуждение

Когда элементы хранятся в связке ключей с ключом kSecAttrSynchronizable, имеющим значение kCFBooleanTrue, такие элементы сохраняются и в той пользовательской связке ключей, которая расположена в облаке iCloud. Таким образом, эти элементы будут доступны на всех пользовательских устройствах, если пользователь зашел в них под учетной записью для работы с iCloud. Если вы просто хотите считать значение, которое (как вам точно известно) синхронизировано с пользовательской связкой ключей из iCloud, необходимо указать вышеупомянутый ключ, а также задать для этого ключа значение kCFBooleanTrue. В таком случае iOS сможет получить требуемое значение из облака, если еще не сделала этого.

Пример, который мы рассмотрим в этом разделе, на 99 % совпадает с кодом, изученным в разделе 8.6. Разница заключается в следующем: когда мы сохраняем значение в связке ключей или пытаемся его оттуда считать, то указываем в нашем словаре ключ kSecAttrSynchronizable и задаем для этого ключа значение kCFBooleanTrue. Итак, давайте для начала рассмотрим, как сохранить значение в связке ключей:

#import «AppDelegate.h»

#import <Security/Security.h>

@implementation AppDelegate

— (BOOL) application:(UIApplication *)application

didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{

NSString *key = @"Full Name";

NSString *service = [[NSBundle mainBundle] bundleIdentifier];

NSString *accessGroup = @"F3FU372W5M.*";

/* Сначала удаляем имеющееся значение, если оно существует. Этого можно

и не делать, но SecItemAdd завершится с ошибкой, если имеющееся

значение окажется в связке ключей */

NSDictionary *queryDictionary = @{

(__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword,

(__bridge id)kSecAttrService: service,

(__bridge id)kSecAttrAccessGroup: accessGroup,

(__bridge id)kSecAttrAccount: key,

(__bridge id)kSecAttrSynchronizable: (__bridge id)kCFBooleanTrue

};

SecItemDelete((__bridge CFDictionaryRef)queryDictionary);

/* Затем запишем новое значение в связку ключей */

NSString *value = @"Steve Jobs";

NSData *valueData = [value dataUsingEncoding: NSUTF8StringEncoding];

NSDictionary *secItem = @{

(__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword,

(__bridge id)kSecAttrService: service,

(__bridge id)kSecAttrAccessGroup: accessGroup,

(__bridge id)kSecAttrAccount: key,

(__bridge id)kSecValueData: valueData,

(__bridge id)kSecAttrSynchronizable: (__bridge id)kCFBooleanTrue

};

CFTypeRef result = NULL;

OSStatus status = SecItemAdd((__bridge CFDictionaryRef)secItem, &result);

if (status == errSecSuccess){

NSLog(@"Successfully stored the value");

} else {

NSLog(@"Failed to store the value with code: %ld", (long)status);

}

self.window = [[UIWindow alloc]

initWithFrame: [[UIScreen mainScreen] bounds]];

self.window.backgroundColor = [UIColor whiteColor];

[self.window makeKeyAndVisible];

return YES;

}

Пожалуйста, внимательно изучите примечания из раздела 8.6. На данном этапе вы уже должны знать, что группа доступа, упоминаемая во всех приведенных примерах, у каждого разработчика будет называться по-своему. Обычно это командный идентификатор, генерируемый на портале Apple для разработки под iOS для каждого разработчика и обеспечивающий доступ к коду для его команды. Необходимо изменить это значение в ваших примерах и убедиться, что оно совпадает именно с вашим командным идентификатором.

1 ... 83 84 85 86 87 88 89 90 91 ... 165
На этой странице вы можете бесплатно читать книгу iOS. Приемы программирования - Вандад Нахавандипур бесплатно.
Похожие на iOS. Приемы программирования - Вандад Нахавандипур книги

Оставить комментарий