iOS NewsStand 功能

           NewsStand是苹果公司专门对数字出版做的一个新功能。它能把所有用户订阅的报刊和杂志类的app都放在一个组图标里。实际上,News Stand相当于一个特制的文件夹专门放置报纸,杂志类应用程序。


新的api能为开发者提供以下杂志出版提供的常用功能。

1,后台下载,因为杂志的数据量比较大,通常要程序员处理什么时候下载的问题。而以前这些下载只能在杂志程序运行的时候工作。
现在,可以提交给专用的下载程序进行。而且下载的内容如果是zip和其他压缩格式,还可以自动解压。

2,badge通知。以前用户需要打开app才能知道版本更新。现在由于统一在newstand里面,下载完成能自动通知读者有新一期可以阅读了。

3,阅读界面,这方面各家需求不一样,还是要自己实现。暂时没有办法统一。
使用newstand另一个好处是新一期的封面可以用来代替原来app程序的图标

4,Newsstand 工作流程, 服务器端要先发送一个push notification给客户端的newsstand程序。客户端程序连服务器,取得要下载的url地址。
交给Newsstand后台下载打包的内容,然后进行必要的解压。最后客户端从newsstand里面拿回下载的内容,再把它们搬到程序的数据区。
剩下就交给阅读程序handle了。

 

详细介绍

首先介绍的就是怎样把一个应用程序改变成一个News Stand程序,这实际上有两步工作,一是让程序运行于News Stand,二是改变程序的图标。

1. 让程序运行于News Stand内

直接在Xcode中更改Info.plist(如图):

 

就这么简单,运行!你的程序就运行在News Stand中了。如图:

iOS NewsStand 功能_第1张图片

不过,出现在News Stand中的是一个系统的icon。如果我们想自定义我们自己的icon。那么,我们需要第二步。

 iOS NewsStand 功能_第2张图片

2. 为你的News Stand程序添加图标

应用程序仍需定义标准图标,这些图标用于settings,search,Push等,(而且你的程序有可能运行于iOS 5以前的版本)。Newsstand 图标可以反应应用的内容,可以动态更新,另外还可以加一些修饰,使其看上去就像真正的杂志或者报纸。


 iOS NewsStand 功能_第3张图片

关于BindingType和BindingEdge应该很容易理解,我就不累赘了。另外Newsstand中的图标不一定是正方形,只是不知有没有尺寸上的限制。

 

显示和下载杂志

        现在是时候开始编写一些代码,将我们的项目加入NewsstandKit框架。

 1、先导入NewsstandKit标题的分类,需要它

 #import<NewsstandKit/NewsstandKit.h>

   

2、为简单起见,我们假设列表中可用的问题(所有免费的)是在一个plist文件,可以从服务器下载的出版商。所以为了我们定义一个类称为出版商将照顾下载当前的项目列表。一旦项目被下载他们将提交给用户store.plist是相当简单的:它是一个数组,数组的对象都是一个字典,每个字典词典代表一个问题通过提供识别名称(独特的)、标题(对用户可见),出版日期和url缩略图封面图片和内容(PDF文件)下载。

必须有一个配置表,这个配置表要从提供者下载。配置表是一个数组存储字典,字典中Name必须唯一,Title是显示的名字,Date(发布日期),封面,内容(PDF,html等)

 iOS NewsStand 功能_第4张图片

3、制作一个列表页,展示配置表中所有的杂志,然后点击某个封面后下载当前点击的杂志。

4、系统提供NewsstandKit.framework来支持newsstand类型的程序,就是在sprint board上看到在书架中的程序。

提供有NKLibrary, NKIssue和NKAssetDownload的类。其中NKLibrary用来管理Newsstand的内容(比如,当前阅读的issue,当前所有 的issue等);

NKIssue用来表示一期刊物,您可以将刊物的URL,包装成NSURLRequest,set给NKIssue的对象。NKIssue还可以很方便的管理刊物的状态(比如None, Downloading-下载中,Available-可用)。

NKAssetDownload,可用于刊物的下载。它的 delegate符合NSURLConnectionDownloadDelegate的协议,这个协议中有三个方法:

connection:didWriteData:totalBytesWritten:expectedTotalBytes://这个方法可以用来做进度管理。
connectionDidResumeDownloading:totalBytesWritten:expectedTotalBytes://方法可以 用来做续传。
connectionDidFinishDownloading:destinationURL://方法表明下载已完成,可以更新界面的 Issue状态。

NewsstandKit是属于系统级别的,因此在app切换到后台或退出的时候,也会由系统选择继续下载。

如果使用ASIHttpRequest来下载的话也是没有问题的,但是没有后台下载,issue状态管理也需要自己来做。

5、 后台下载只能使用Newsstand Kit的framework实现。关键是自动下载。Newsstand类型的app可由push notification来触发下载流程。前提是注册push功能的时候,加上Newsstand的key。

像正常的APNS一样,app在前台,后台,或退出状态下都可以收到。前台的很简单,在didReceiveRemoteNotification的方法中,弹一个alert,问用户是否需要下载;或者直接下载都可以(我之前的做法是会将要下载的issue的信息包装到push notification的message body中)。如果app是退出状态下的话,收到Newsstand的APNS,app会直接在后台启动(这个你是看不到的),然后会走didFinishLaunchingWithOptions的方法,option会带入参数,您可以通过它获取信息。然后就可以启动下载。

首先读取,看看有没有此名字的期刊

NKIssue * nkIssue = [nkLib issueWithName:name];
if (!nkIssue) {
    
    如果没有的话,通过下面的方法就可以加进NSLibrary了
    nkIssue = [nkLib addIssueWithName:namedate:[(NSDictionary *)obj objectForKey:@"Date"]];
}

ps: 注意 NSIssue的状态


6、下载相关问题。

 6-1、首先介绍程序在前端显示时怎么下载,得调用NKAssetDownload

 6-1-1、每一个issue都是由一个或者多个asset组成,asset可以是pdf,images,videos,thumbnails,但是推荐打包所有资源成zip包,然后下载这一个文件

 6-1-2、 最终的文件本地路径,被Newsstand framework指定,并且被指定到沙盒中的cache目录,所以也遵循icloud要求

 6-1-3、本地路径可以通过 NSIssue 的contentURL属性找回

 6-1-4、我们可以创建自己最终的路径,通过在contentURL后添加我们自定义的文件名字

-(NSString *)downloadPathForIssue:(NKIssue *)nkIssue {
     return [[nkIssue.contentURL path] stringByAppendingPathComponent:@"magazine.pdf"]; 
}

        所以当我们点击图标时,如果没有下载,我们就要询问服务器去哪里下载杂志,也就是给我们下载的url(复杂点的app,我们也许会牵扯到一个内收费的问题,也要在这做控制了),最终,我们NSIssue调用NSAssetDownload去排队下载。注意更新下载的进度条

-(void)startDownloadIssue:(NSIndexPath *)index{
    
    NSDictionary * publisher = [_booksAryobjectAtIndex:index.row];
    
    NKLibrary * nkLib = [NKLibrary sharedLibrary];
    NKIssue * nkIssue = [nkLib issueWithName:[publisherobjectForKey:@"Name"]];
    
    NSLog(@"issue . name = %@",nkIssue.name);
    // 获取publish的severURL
    
    NSURL * downloadURL = [NSURL URLWithString:[publisherobjectForKey:@"Content"]];
    
    if (!downloadURL) {
        return;
    }
    
    NSURLRequest * req = [NSURLRequestrequestWithURL:downloadURL];
    
    
    NKAssetDownload * assetDownload = [nkIssueaddAssetWithRequest:req];
    [assetDownload setUserInfo:@{@"index":[NSNumber numberWithInt:index.row]}];
    
    [assetDownload downloadWithDelegate:self];
    
}
 

注意 NSURLConnectionDownloadDelegate代理

- (void)connection:(NSURLConnection *)connectiondidWriteData:(long long)bytesWritten totalBytesWritten:(longlong)totalBytesWritten expectedTotalBytes:(long long) expectedTotalBytes{
    
    NKAssetDownload * dnl = [connectionnewsstandAssetDownload];
    
    NSLog(@"第%@个 下载了 百分之%lld",[dnl.userInfoobjectForKey:@"index"],bytesWritten/totalBytesWritten);
    
    //在此更新进度条
}
但主要的还是其他两个代理方法

6-2、后台下载

6-2-1、newsstand 一个有意思的特征,就是一旦一个asset已经开始下载时,不管程序是在暂停,还是终止,Newsstand都将继续下载。

        当然,在你的app暂定状态,它不会收到任何的状态更新,但是它会在后台被唤醒去处理下载的完成操作。在这个过程中,你可以copy文件到你的目标目录下,并且对zipped的文件进行解包。

         如果在newsstand下载过程中被终止,情况就不一样了。实际上,下载过程中被终止,我们就不能简简单单的唤醒,连接 delegate完成下载就行了。因为当一个app被终止后,App delegate也就不存在了。所以在这种情况下,我们将会在background重新 relaunch 当前app。然后  UIApplicationDelegate application:didFinishLaunchingWithOptions: 会被调用,launchOptions 会带上 UIApplicationLaunchOptionsNewsstandDownloadsKey这样的key

         如果这个key存在的话,这个key对应的value应该包含一个包含asset的数组。(这个没做实验,不做考证)

         另外一个比较重要的是,当程序每次relaunch时,我们需要检查时候存在等待下载的。如果有的话,我们就必须重新附上 download delegate 完成下载,否则程序或者认为他们是被抛弃的,可能会引起崩溃。

         我们要在  Appdelegate  中的 application: didFinishLaunchingWithOption: 当界面加载完后检查

// inside the app delegate'sapplication:didFinishLaunchingWithOptions: and after the view controller hasbeen initialized
NKLibrary *nkLib = [NKLibrary sharedLibrary];
for(NKAssetDownload *asset in [nkLib downloadingAssets])
{
    [assetdownloadWithDelegate:store];
}

6-3、新内容推送

      另外一个newsstand的好处是,当有新的内容推送时,会收到push notification:这就意味着这个app会自动在后台下载新的内容,并更新在newsstand group的杂志的封面。

 但是  Newsstand KIt 添加了一种新的push 通知类型,叫做UIRemoteNotificationTypeNewsstandContentAvailability通知。通过此类型的通知,告诉有新的内容要更新。newsstand应用也许对其他类型的通知不感兴趣,所以把上边注册的通知类型用

[[UIApplication sharedApplication]registerForRemoteNotificationTypes:UIRemoteNotificationTypeNewsstandContentAvailability];


代替。

 

注册这个通知之后,会弹出一个 提示“** 想要给你发送新的杂志”,如果这个出错的话,再

- (void)application:(UIApplication *)applicationdidFailToRegisterForRemoteNotificationsWithError:(NSError *)error method.


处理。

         当一个通知发送到终端时,会有两种状态,在前台跑或者不在。当程序在前台时,我们必须响应application:didReceiveRemoteNotifation:开发者可以仅仅提示一个alert,或者自动开始一个下载或者找服务器要新一期的杂志数据(title,cover,等等)。另外一种情况,收到通知后会激活application:didFinishLaunchingWithOptions:还有这个方法必须做的。然后检查  launch option中的UIApplicationLaunchOptionRemoteNotificationKey对应的值并检查负荷。 这种情况下,将要被完成是会下载最新的issue从获取issue列表的服务器。正常情况下后台任务也就跑个几秒钟,因为之前你还有检查服务器,所以我们就要设置beginBackgroundTaskWithExpirationHandler 来获取10minutes完成这个任务。

 

 

下面是一个后台完成下载的代码

NSDictionary *payload = [launchOptionsobjectForKey:UIApplicationLaunchOptionsRemoteNotificationKey];
    if(payload) {
        // schedule for issue downloading in background
        NKIssue *issue4 = [[NKLibrary sharedLibrary]issueWithName:@"Magazine-4"];
        if(issue4) {
            NSURL *downloadURL = [NSURLURLWithString:@"http://www.viggiosoft.com/media/data/blog/newsstand/magazine-4.pdf"];
            NSURLRequest *req = [NSURLRequestrequestWithURL:downloadURL];
            NKAssetDownload *assetDownload = [issue4addAssetWithRequest:req];
            [assetDownload downloadWithDelegate:store];
        }
    }


6-4、最后一个就是更新newsstand的图标了。

       苹果建议我们都用最新一期报刊的封面来更新app的icon,我们经常见的应该是带了一个newi的腰带的图标。这个也不难,我们通过以下代码就可以实现,不过在这一期杂志不可用的时候永远不要去更新封面图标

// update the Newsstand icon
UIImage *img = [publishercoverImageForIssue:nkIssue];
if(img) {
    [[UIApplication sharedApplication]setNewsstandIconImage:img];
    [[UIApplication sharedApplication]setApplicationIconBadgeNumber:1];
}

做完上面的所有事情,我们在bulid我们的app,然后就可以看到下面的图片:

iOS NewsStand 功能_第5张图片

我们点击上面的任意一个按钮下载,悲剧的是崩溃了,我们做错了什么?少做了什么?看看崩溃日志吧。

2014-06-21 18:23:34.490 NewsStandDemo[5493:60b] ***Terminating app due to uncaught exception 'NSInvalidArgumentException', reason:'you do not have background download privileges: add 'newsstand-content' tomainBundle.infoDictionary.UIBackgroundModes'
*** First throw call stack:
(
      0   CoreFoundation                      0x018c71e4__exceptionPreprocess + 180
      1   libobjc.A.dylib                     0x016468e5objc_exception_throw + 44
      2   CoreFoundation                      0x018c6fbb +[NSExceptionraise:format:] + 139
      3   NewsstandKit                        0x000d4427 -[NKIssueaddAssetWithRequest:] + 326
      4   NewsStandDemo                       0x00005940 -[StorescheduleDownloadOfIssue:] + 320
      5   NewsStandDemo                       0x00003866-[ViewController downloadIssue:updateCover:] + 630
      6   NewsStandDemo                       0x000034a2-[ViewController coverSelected:] + 226
      7   NewsStandDemo                       0x00006b34 -[CoverViewbuttonCallback:] + 68
      8   libobjc.A.dylib                     0x01658880 -[NSObjectperformSelector:withObject:withObject:] + 77
      9   UIKit                               0x003083b9-[UIApplication sendAction:to:from:forEvent:] + 108
      10  UIKit                               0x00308345-[UIApplication sendAction:toTarget:fromSender:forEvent:] + 61
      11  UIKit                               0x00409bd1-[UIControl sendAction:to:forEvent:] + 66
      12  UIKit                               0x00409fc6-[UIControl _sendActionsForEvents:withEvent:] + 577
      13  UIKit                               0x00409243-[UIControl touchesEnded:withEvent:] + 641
      14  UIKit                               0x00347ddd-[UIWindow _sendTouchesForEvent:] + 852
      15  UIKit                               0x003489d1-[UIWindow sendEvent:] + 1117
      16  UIKit                               0x0031a5f2-[UIApplication sendEvent:] + 242
      17  UIKit                               0x00304353_UIApplicationHandleEventQueue + 11455
      18  CoreFoundation                      0x0185077f__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 15
      19  CoreFoundation                      0x0185010b__CFRunLoopDoSources0 + 235
      20  CoreFoundation                      0x0186d1ae __CFRunLoopRun+ 910
      21  CoreFoundation                      0x0186c9d3CFRunLoopRunSpecific + 467
      22  CoreFoundation                      0x0186c7eb CFRunLoopRunInMode + 123
      23  GraphicsServices                    0x049f45ee GSEventRunModal+ 192
      24  GraphicsServices                    0x049f442b GSEventRun + 104
      25  UIKit                               0x00306f9bUIApplicationMain + 1225
      26  NewsStandDemo                       0x0000462d main + 141
      27  libdyld.dylib                       0x04d7b701 start + 1
)
libc++abi.dylib: terminating with uncaught exception oftype NSException
根据报错的信息,我们可以看出,我们的App要更增加UIBackgroundModes,所以我们就在plist设置NewsStands UIBackgroundModes,

 


再次点击下载,ok 顺利下载完成。

再次点击第五个就可以完美阅读啦

iOS NewsStand 功能_第6张图片

参考 文档:

http://www.viggiosoft.com/blog/blog/2011/10/17/ios-newsstand-tutorial/

https://developer.apple.com/library/ios/documentation/StoreKit/Reference/NewsstandKit_Framework/_index.html

http://www.viggiosoft.com/blog/blog/2011/10/29/at-newsstand-and-subscriptions/

你可能感兴趣的:(iOS NewsStand 功能)