实施特定应用功能的策略
不同的应用程序有不同的需求,但是一些是在许多类型的应用程序常见的行为,以下部分提供有关如何在应用程序中实现特定类型的功能的指导。
隐私策略
在设计应用程序时,保护用户的隐私是重要的考虑因素。隐私保护包括保护用户的数据,包括用户的身份和个人信息。系统框架已经提供隐私控制来管理诸如联系人之类的数据,但是您的应用程序应该采取措施保护您在本地使用的数据。
使用磁盘加密保护数据
数据保护使用内置硬件以磁盘上的加密格式存储文件,并根据需要进行解密。当用户的设备被锁定时,受保护的文件是无法访问的,甚至是创建它们的应用程序。在应用程序可以访问其受保护的文件之前,用户必须解锁设备(通过输入相应的密码)。
大多数iOS设备都提供数据保护,并符合以下要求:
用户设备上的文件系统必须支持数据保护。大多数设备支持此行为。
用户必须为设备设置一个活动的密码锁。
要保护文件,请向文件添加一个属性,指示所需的保护级别。使用NSData类或NSFileManager类添加此属性。编写新文件时,可以使用具有相应保护值的writeDataFile:options:NSData的error:method作为写入选项之一。对于现有文件,可以使用NSFileManager的setAttributes:ofItemAtPath:error:method来设置或更改NSFileProtectionKey的值。使用这些方法时,请为文件指定以下保护级别之一:
无保护 - 文件已加密,但不受密码保护,并在设备锁定时可用。指定NSDataWritingFileProtectionNone选项(NSData)或NSFileProtectionNone属性(NSFileManager)。
完成 - 设备被锁定时,文件被加密并且不可访问。指定NSDataWritingFileProtectionComplete选项(NSData)或NSFileProtectionComplete属性(NSFileManager)。
完成,除非已经打开 - 文件已加密。当设备被锁定时,无法访问关闭的文件。用户解锁设备后,您的应用程序可以打开该文件并使用该文件。如果用户在文件打开时锁定设备,您的应用程序可以继续访问它。指定NSDataWritingFileProtectionCompleteUnlessOpen选项(NSData)或NSFileProtectionCompleteUnlessOpen属性(NSFileManager)。
完成,直到第一次登录 - 文件被加密,无法访问,直到设备启动并且用户已解锁一次。指定NSDataWritingFileProtectionCompleteUntilFirstUserAuthentication选项(NSData)或NSFileProtectionCompleteUntilFirstUserAuthentication属性(NSFileManager)。
如果您保护文件,您的应用程序必须准备好丢失对该文件的访问。启用完整的文件保护后,当用户锁定设备时,您的应用程序将失去读取和写入文件内容的能力。您可以使用以下技术之一跟踪对受保护文件状态的更改:
应用程序委托可以实现applicationProtectedDataWillBecomeUnavailable:和applicationProtectedDataDidBecomeAvailable:methods。
任何对象都可以注册UIApplicationProtectedDataWillBecomeUnavailable和UIApplicationProtectedDataDidBecomeAvailable通知。
任何对象都可以检查共享UIApplication对象的protectedDataAvailable属性的值,以确定文件当前是否可访问。
对于新文件,建议您在写入任何数据之前启用数据保护。如果使用writeToFile:options:error:方法将NSData对象的内容写入磁盘,则会自动进行。对于现有文件,添加数据保护将使用新的受保护版本替换未受保护的文件。
识别您的应用程序的唯一用户
只有在这样做时,才能识别应用程序的用户,才能为该用户提供明确的收益。如果您只需要将应用的一个用户与其他用户区分开来,则iOS提供可帮助您执行此操作的标识符。但是,如果您需要更高级别的安全性,您可能需要自己做更多的工作。例如,提供金融服务的应用程序可能希望提示用户登录凭据,以确保用户有权访问特定帐户。
重要提示:在识别用户时,始终要透明了解您获取的任何信息的意图。识别用户是不可接受的,防止您可以偷偷跟踪它们。
以下是一些常见的情况,可能需要您识别用户,以及如何实现它们的解决方案。
您希望将用户链接到服务器上的特定帐户。包括登录屏幕,要求用户安全地输入帐户信息。通过以加密形式存储,始终保护您从用户收集的帐户信息。
您想区分不同设备上运行的应用程序的实例。使用UIDevice类的identifierForVendor属性来获取一个区分一个设备上的用户与其他设备上的用户的ID。现在,该技术可以让您识别特定的用户。单个用户可以具有多个设备,每个设备具有不同的ID值。
您想要为广告目的识别用户。使用ASIdentifierManager类的advertisingIdentifier属性获取用户的ID。
由于用户可以在所有iOS设备上运行应用程序,因此Apple不提供在多个设备上识别同一用户的方法。如果您需要识别特定用户,则必须使用普遍唯一的ID(UUID),登录帐户或其他类型的身份识别系统提供您自己的解决方案。
评级限制
用户可以设置限制,指定要在应用程序中使用的媒体的评级。如果您的应用程序根据限制播放媒体或修改其行为,则需要确定当前设置并在设置更改时进行响应。
要获取当前设置,请获取共享的standardUserDefaults对象,并使用objectForKey:方法查看以下项的值:
媒体评级键值
com.apple.content-rating.ExplicitBooksAllowed
布尔。如果此键的值为NO,则不允许显式书籍
com.apple.content-rating.ExplicitMusicPodcastsAllowed
布尔。如果此键的值为NO,则不允许显示音乐,电影和播客。
com.apple.content-rating.AppRating
的NSNumber。该键的值范围为0到1000.不允许其评级高于当前键值的应用程序。
com.apple.content-rating.MovieRating
的NSNumber。该键的值范围为0到1000.不允许其分级高于当前键值的影片。
com.apple.content-rating.TVShowRating
的NSNumber。该键的值范围为0到1000.不允许其等级高于当前键值的电视节目。
注意:如果objectForKey:为特定键返回nil,则表示有关此特定限制的信息不可用。在这种情况下,您的应用程序可以使用自己的策略来确定适当的评级。
要检测用户何时更改限制,请注册通知NSUserDefaultsDidChangeNotification。共享的standardUserDefaults对象在检测到位于其中一个持久域中的首选项的更改时,会将此通知发送到您的应用程序。
应用程序评级是针对我们的国家/地区代码定义的,并被普遍应用。表5-1显示了与每个美国应用程序评级相关联的值。
表5-1应用程序评分
评分名称
数值
4+
100
9+
200
12+
300
17+
600
电影和电视的评分因国家而异。如果一个国家或地区没有为电影或电视节目指定评级系统,您的应用程序应使用自己的策略来确定适当的评级。虽然大多数地区定义电影评级,但只有少数人定义电视节目评分。
区域可以定义多个评级级别,每个级别与描述评级的名称和范围为0到1000的数字相关联。例如,美国使用字符串“G”,数字100指定最低级别电影评级等级。
即使您的应用没有播放媒体,也可能需要将自己的评分系统映射到电影或电视节目评分系统。例如,只有当美国电影评级“R”被允许时,游戏才能启用某些功能。要查看当前的评分列表,请下载此文档的伴随文件(链接位于页面顶部)。
支持多种版本的iOS
支持最新版本的iOS以及一个或多个早期版本的应用程序必须使用运行时检查来防止在旧版本的iOS上使用较新的API。运行时检查会阻止您的应用程序在尝试使用当前操作系统上不可用的功能时崩溃。
您可以进行几种类型的检查:
要确定一个类是否存在,请查看它的Class对象是否为nil。链接器对于任何未知的类对象返回nil,使得可以使用类似于以下的条件检查:
if ([UIPrintInteractionController class]) {
// Create an instance of the class anduse it.
}
else {
// The print interaction controller isnot available so use an alternative technique.
}
要确定方法是否可用于现有类,请使用instancesRespondToSelector:class方法或respondToSelector:instance方法。
要确定基于C的函数是否可用,请执行函数名为NULL的布尔比较。如果符号不为NULL,则可以调用该函数。例如:
if(UIGraphicsBeginPDFPage!= NULL){
UIGraphicsBeginPDFPage();
}
有关如何编写支持多个部署目标的代码的更多信息和示例,请参阅SDK兼容性指南。SDK Compatibility Guide
保持你的应用程序的视觉外观Across Launches
即使您的应用程序支持后台执行,也不能永久运行。在某些时候,系统可能需要终止您的应用程序以释放当前前台应用程序的内存。但是,用户不应该关心应用程序是否已在运行或终止。从用户的角度来说,退出应用程序似乎暂时中断。当用户返回到应用程序时,该应用程序应始终将用户返回到最后一个使用点,以便用户可以继续执行任何正在进行的任务。这种行为为用户提供了更好的体验,并且与UIKit内置的状态恢复支持相对容易实现。
UIKit中的状态保存系统提供了一个简单但灵活的基础设施,用于保存和恢复应用程序视图控制器和视图的状态。基础设施的工作是在适当的时候推动维护和恢复过程。为此,UIKit需要您的应用程序的帮助。只有您了解您的应用程序的内容,因此只有您可以编写保存和还原该内容所需的代码。当您更新应用程序的UI时,只有您知道如何将较旧的保留内容映射到界面中的较新对象。
你有三个地方必须在你的应用程序中考虑状态保存:
您的应用程序委托对象,它管理应用程序的顶级状态
您的应用程序的视图控制器对象,用于管理应用程序用户界面的整体状态
您的应用的自定义视图,可能有一些需要保留的自定义数据
UIKit允许您选择要保留的用户界面的哪些部分。如果您已经有处理状态保存的自定义代码,则可以继续使用该代码,并根据需要将部分迁移到UIKit状态保存系统。
在您的应用程序中启用状态保存和恢复
状态保存和恢复不是自动功能,应用程序必须选择使用它。应用程序通过在其应用程序委托中实施以下方法来表明他们对该功能的支持:
应用:shouldSaveApplicationState:
应用:shouldRestoreApplicationState:
通常,这些方法的实现只是返回YES,表示可以发生状态保存和恢复。但是,要在不发生操作的情况下,有条件地保留和恢复状态的应用程序可以返回NO。例如,在发布对您的应用程序的更新后,您可能希望从应用程序返回NO:shouldRestoreApplicationState:方法,如果您的应用程序无法从先前版本有效地还原状态。
保存和恢复过程
状态保存和恢复是一个选择加入功能,并在您的应用程序的帮助下工作。您的应用程序标识应该保留的对象,UIKit会在适当的时候执行保存和还原这些对象的工作。因为UIKit处理这么多的过程,所以它有助于了解它在幕后做的事情,以便您了解自定义代码如何适应整体方案。
在考虑状态保护和恢复时,首先有助于分离两个进程。 UIKit会在适当的时间保留应用的状态,例如当您的应用程序从前台移动到后台时。当UIKit确定需要新的状态信息时,它会查看应用程序的视图和视图控制器,以查看哪些应该保留。对于每个对象,UIKit将保存相关数据写入加密的磁盘文件。下一次您的应用程序从头开始,UIKit会查找该文件,如果存在,则使用它来尝试恢复应用程序的状态。因为文件是加密的,所以状态保存和恢复只有在设备解锁时才会发生。
在恢复过程中,UIKit使用保留的数据重新构建您的界面,但实际对象的创建将由您的代码来处理。因为您的应用程序可能会自动从故事板文件加载对象,只有您的代码知道需要创建哪些对象,哪些对象可能已经存在,并且可以简单地返回。在创建每个对象之后,UIKit将使用保留的状态信息来初始化它们。
在保存和恢复过程中,您的应用程序有很少的责任。
在保存期间,您的应用程序负责:
告诉UIKit它支持状态保存。
告诉UIKit应该保留哪些视图控制器和视图。
编码任何保留对象的相关数据。
在恢复期间,您的应用程序负责:
告诉UIKit它支持状态恢复。
提供(或创建)UIKit请求的对象。
解码保存对象的状态并使用它将对象返回到之前的状态。
在应用程序的责任方面,最重要的是告诉UIKit在后续启动期间保留哪些对象并提供这些对象。 这两个行为是设计应用程序的保存和恢复代码时应该花费大部分时间的地方。他们也是您对实际过程最有控制力的地方。 为了理解为什么会这样,它有助于看一个例子。
图5-1显示了用户与几个选项卡进行交互之后,选项卡栏界面的视图控制器层次结构。 您可以看到,一些视图控制器将自动加载作为应用程序的主要故事板文件的一部分,但是某些视图控制器已被呈现或被推送到不同选项卡中的视图控制器上。在没有状态恢复的情况下,仅在主故事板文件中的视图控制器将在后续启动期间恢复。 通过添加对状态恢复的支持,您可以保留所有的视图控制器。
图5-1示例视图控制器层次结构
UIKit仅保留那些具有分配的恢复标识符的对象。 恢复标识符是一个字符串,用于标识UIKit和您的应用程序的视图或视图控制器。此字符串的值仅对您的代码有意义,但是此字符串的存在告诉UIKit它需要保留标记的对象。
在保存过程中,UIKit会拖动应用程序的视图控制器层次结构,并保留具有恢复标识符的所有对象。如果视图控制器没有恢复标识符,则该视图控制器及其所有视图和子视图控制器都不会被保留。 图5-2显示了先前视图层次结构的更新版本,现在具有应用于大多数(但不是全部)视图控制器的恢复标识。
图5-2添加恢复标识查看控制器
根据您的应用程序,保留每个视图控制器可能或可能无意义。如果视图控制器显示临时信息,则可能不希望在恢复时返回到同一点,而是选择将用户返回到界面中更稳定的位置。
对于您选择保留的每个视图控制器,您还需要确定以后如何还原它。 UIKit提供了两种重建对象的方式。您可以让您的应用程序委托重新创建它,或者您可以将恢复类分配给视图控制器,并让该类重新创建它。恢复类实现UIViewControllerRestoration协议,并负责在还原时查找或创建指定的对象。以下是有关何时使用每一个的提示:
如果视图控制器在启动时始终从应用程序的主要故事板文件中加载,请不要分配恢复类。相反,让您的应用程序委托找到该对象,或者利用UIKit的支持来隐式查找恢复的对象。
对于在启动时未从主故事板文件加载的视图控制器,请分配恢复类。最简单的选择是使每个视图控制器都有自己的恢复类。
在保存过程中,UIKit识别要保存并将每个受影响对象的状态写入磁盘的对象。每个视图控制器对象都有机会写出任何要保存的数据。例如,选项卡视图控制器保存所选选项卡的标识。UIKit还将视图控制器的恢复类信息保存到磁盘。如果任何视图控制器的视图具有恢复标识符,UIKit也会要求它们保存状态信息。
下一次应用程序启动时,UIKit照常加载应用程序的主要故事板或nib文件,调用应用程序委托的应用程序:willFinishLaunchingWithOptions:方法,然后尝试恢复应用程序的上一个状态。它的第一件事是要求您的应用程序提供与保留的控件对象匹配的一组视图控制器对象。如果给定的视图控制器具有分配的恢复类,则要求该类提供对象;否则,应用程序代理被要求提供。
保存过程的流程
图5-3显示了在状态保存期间发生的高级事件,并显示了应用程序的对象如何受到影响。在保存甚至发生之前,UIKit会通过调用应用程序来发出应用程序委托:shouldSaveApplicationState:method。如果该方法返回YES,则UIKit将开始收集和编码应用程序的视图和视图控制器。完成后,将编码的数据写入磁盘。
图5-3高级流界面保存
下次应用程序启动时,系统会自动查找保留的状态文件,如果存在,则使用它来恢复您的界面。由于此状态信息仅与应用程序的上一个和当前启动周期相关,因此在应用程序完成启动后,该文件通常会被丢弃。只要恢复您的应用程序出现错误,该文件也会被丢弃。例如,如果您的应用程序在恢复过程中崩溃,系统会在下一个启动周期内自动丢弃状态信息,以避免再次崩溃。
恢复过程的流程
图5-4显示了状态恢复期间发生的高级别事件,并显示了应用程序的对象如何受到影响。标准初始化和UI加载完成后,UIKit会通过调用应用程序来调用应用程序:shouldRestoreApplicationState:method来请求您的应用程序委托状态恢复。这是您的应用程序代理机会检查保留的数据,并确定是否可以恢复状态。如果是,UIKit使用应用程序委托和恢复类来获取对应用程序的视图控制器的引用。然后向每个对象提供需要将其恢复到其先前状态的数据。
图5-4恢复用户界面的高级流程
虽然UIKit有助于还原各个视图控制器,但它不会自动恢复这些视图控制器之间的关系。相反,每个视图控制器负责编码足够的状态信息以将其自身恢复到其先前的状态。例如,导航控制器对其导航堆栈上的视图控制器的顺序的信息进行编码。然后,它稍后使用此信息将这些视图控制器返回到堆栈上的先前位置。具有嵌入式子视图控制器的其他视图控制器同样负责编码他们以后要还原他们的孩子所需的任何信息。
注意:并非所有视图控制器都需要对子视图控制器进行编码。例如,标签栏控制器不编码有关其子视图控制器的信息。相反,假设您的应用程序遵循在创建标签栏控制器本身之前创建适当的子视图控制器的常规模式。
由于您负责重新创建应用程序的视图控制器,因此您可以在恢复过程中更改您的界面。例如,您可以重新排序标签栏控制器中的选项卡,并仍然使用保留的数据将每个选项卡返回到之前的状态
。当然,如果您对视图控制器层次结构进行了显着的更改,例如在应用程序更新期间,您可能无法使用保留的数据。
当您排除视图控制器组时会发生什么?
当视图控制器的恢复标识符为零时,该视图控制器及其管理的任何子视图控制器不会自动保留。例如,在图5-5中,由于导航控制器没有恢复标识符,因此从保留的数据中省略它及其所有子视图控制器和视图。
图5-5从自动保存过程中排除视图控制器
即使您决定不保留视图控制器,这并不意味着所有这些视图控制器完全从视图层次结构中消失。 在启动时,您的应用程序仍然可以创建视图控制器作为其默认设置的一部分。例如,如果任何视图控制器从应用程序的故事板文件自动加载,它们仍将出现,尽管是默认配置,如图5-6所示。
图5-6加载默认视图控制器
还有一点需要注意的是,即使视图控制器没有自动保留,您仍然可以编码对该视图控制器的引用并手动保留。在图5-5中,第一导航控制器的三个子视图控制器具有恢复标识符,即使父导航控制器没有。如果您的应用程序委托(或任何保留的对象)编码对这些视图控制器的引用
,则会保留其状态。即使导航控制器中的命令未被保存,您仍然可以使用这些引用来重新创建视图控制器,并在后续的启动周期将它们安装在导航控制器中。
实施状态保护和恢复清单
支持状态保存和恢复需要修改您的应用程序委托并查看控制器对象以对状态信息进行编码和解码。如果您的应用程序有任何自定义视图也具有可保留的状态信息,那么您还需要修改这些对象。
在您的代码中添加状态保存和恢复时,请使用以下列表来提醒您需要编写的代码。
(必需)实现应用程序:shouldSaveApplicationState:和应用程序:shouldRestoreApplicationState:应用程序委托中的方法;请参阅在您的应用程序中启用状态保存和恢复。
(必需)通过为其restoreIdentifier属性分配一个非空字符串,将恢复标识符分配给要保留的每个视图控制器;请参阅标记您的视图控制器进行保存。
如果要保存特定视图的状态,请将非空字符串分配给它们的restoreIdentifier属性;看保持你的意见状态。
(必需)从应用程序显示应用程序的窗口:willFinishLaunchingWithOptions:应用程序委托的方法。状态恢复机构需要窗口,以便可以恢复应用程序界面的滚动位置和其他相关位。
将修复类分配给相应的视图控制器。 (如果不这样做,您的应用程序委托被要求在还原时提供相应的视图控制器。)请参阅在启动时恢复视图控制器。
(推荐)使用encodeRestorableStateWithCoder:和decodeRestorableStateWithCoder编码和解码视图的状态并查看控制器:这些对象的方法;请参阅编码和解码视图控制器的状态。
使用应用程序对应用程序的任何版本信息或其他状态信息进行编码和解码:willEncodeRestorableStateWithCoder:和应用程序:didDecodeRestorableStateWithCoder:应用程序委托的方法;请参阅保存应用程序的高级状态。
作为表视图和集合视图的数据源的对象应实现UIDataSourceModelAssociation协议。虽然不是必需的,但是该协议有助于在这些类型的视图中保留所选和可见的项目。请参阅实施易于维护的数据源。