應(yīng)用很早就上線了,歡迎大家下載使用:http://itunes.apple.com/app/id1206687109
源碼已經(jīng)公開,大家可以去https://github.com/Inspirelife96/ILDiligence下載。 喜歡的話Fork或者給個Star,非常感謝。
下面是這一系列的全部帖子:
想法和原型
勤之時 - 架構(gòu)與工程組織結(jié)構(gòu)
勤之時 - 數(shù)據(jù)持久層的實現(xiàn)
勤之時 - 網(wǎng)絡(luò)層的實現(xiàn)
勤之時 - 業(yè)務(wù)邏輯層
勤之時 - Info.plist的改動
勤之時 - 表示層(一)
勤之時 - 表示層(二)
勤之時 - 表示層(三)
勤之時 - 表示層(四)
勤之時 - 表示層(五)
事實上,【勤之時】一個本地應(yīng)用,基本上沒有什么網(wǎng)絡(luò)請求,或者需要從服務(wù)器端下載數(shù)據(jù)。不過為了美觀,每天的背景圖片會變化,并且會有一個每日故事的分享頁面,也就是說,每天一圖。
那么具體這個圖片怎么來?當(dāng)然可以自己搭建服務(wù)器,然后提供圖片。不過這樣相對來說比較復(fù)雜,不但要搭建服務(wù)器,還要自己準備圖片。所以最簡單的是看有沒有現(xiàn)成的API 服務(wù),我找到了兩個:
Bing的每日一圖并不是API服務(wù),不過通過抓包,可以發(fā)現(xiàn)http://www.bing.com/HPImageArchive.aspx?format=js&idx=0&n=1 可以獲取到無水印的圖片。
{
"images": [{
"startdate":"20170319",
"fullstartdate":"201703191600",
"enddate":"20170320",
"url":"/az/hprichbg/rb/TingSakura_ZH-CN14945610051_1920x1080.jpg",
"urlbase":"/az/hprichbg/rb/TingSakura_ZH-CN14945610051",
"copyright":"一只在櫻花樹上嬉戲的綠繡眼(? Reece Cheng/500px)",
"copyrightlink":"http://www.bing.com/search?q=%E6%98%A5%E5%88%86&form=hpcapt&mkt=zh-cn",
"quiz":"/search?q=Bing+homepage+quiz&filters=WQOskey:%22HPQuiz_20170319_TingSakura%22&FORM=HPQUIZ",
"wp":true,
"hsh":"c4afb61170efde716ed371c02f8db13f","drk":1,"top":1,"bot":1,"hs":[]
}],
"tooltips": {
"loading":"正在加載...",
"previous":"上一個圖像",
"next":"下一個圖像",
"walle":"此圖片不能下載用作壁紙。",
"walls":"下載今日美圖。僅限用作桌面壁紙。"
}
}
從返回的Json中可以看到,我們可以獲取到urlbase,經(jīng)過測試,bing提供了1080x1920的圖片,其后綴為_1080x1920.jpg,所以最后無水印圖片的下載地址為http://www.bing.com/urlbase_1080x1920.jpg。
那么當(dāng)前圖片對應(yīng)的故事在哪里呢?同樣發(fā)現(xiàn)了API接口http://cn.bing.com/cnhp/coverstory/,不過這里的圖片是有水印的。
{
"date":"March 20",
"title":"碧玉妝 綠絲絳",
"attribute":"枝頭嬉戲的綠繡眼",
"para1":"古代的春分分為三候:“一候玄鳥至,二候雷乃發(fā)聲,三候始電。”便是說春分日后,燕子便從南方飛來了,下雨時天空便要打雷并發(fā)出閃電。春分后,中國南方大部分地區(qū)越冬作物進入春季生長階段,氣溫也繼續(xù)回升,真正的春天來了,你也開始一個全新的自己吧!",
"para2":"",
"provider":"? Reece Cheng/500px",
"imageUrl":"/th?id=OSA.pfFS41jcab0yuA&pid=SatAns&w=100&h=100&c=8&rs=1","primaryImageUrl":"http://hpimges.blob.core.chinacloudapi.cn/coverstory/watermark_tingsakura_zh-cn14945610051_1920x1080.jpg",
"Country":"",
"City":"",
"Longitude":"",
"Latitude":"",
"Continent":"亞洲",
"CityInEnglish":"Enter keyword",
"CountryCode":""
}
所以綜合這兩個,我們就可以得到每日背景圖片及故事。
至于Unsplash,大家可以參考Unsplash官網(wǎng)的說明去試試。【勤之時】暫時使用的是Bing的每日一圖及故事來作為背景。
知道了需求,了解了內(nèi)容的來源,接下來就是如何實現(xiàn)了。同樣,iOS應(yīng)用架構(gòu)談 網(wǎng)絡(luò)層設(shè)計方案探討了詳細的方案。
最終的方案
- 使用delegate來做數(shù)據(jù)對接
- 交付NSDictionary給業(yè)務(wù)層,使用Const字符串作為Key來保持可讀性
此外,【勤之時】的需求涉及到同步問題,可以發(fā)現(xiàn),我們的操作為
- 訪問http://www.bing.com/HPImageArchive.aspx?format=js&idx=0&n=1 獲取Json數(shù)據(jù)
- 根據(jù)Json數(shù)據(jù)拼接獲得每日圖片的下載地址
- 下載圖片
- 訪問http://cn.bing.com/cnhp/coverstory/獲取每日故事的內(nèi)容。
可以發(fā)現(xiàn),我們會發(fā)送3個HTTP異步請求,只有當(dāng)三個請求全部成功返回后,我們才能認為這個下載任務(wù)完成。因此,需要使用dispatch_group_t, dispatch_group_enter, dispatch_group_leave,dispatch_group_notify來完成這個組合。
最終代碼:
// ILDNetwork.h
#import <Foundation/Foundation.h>
@protocol ILDNetworkDelegate <NSObject>
- (void)fetchStoryDataSuccess:(NSDictionary *)storyDataDictionary;
- (void)fetchStoryDataFail:(NSError * _Nonnull)error;
@end
@interface ILDNetwork : NSObject
@property (nonatomic,weak) id<ILDNetworkDelegate> delegate;
- (void)downloadStoryData;
@end
](http://upload-images.jianshu.io/upload_images/1771779-541fcb4c8382a9f2.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)// ILDNetwork.m
#import "ILDNetwork.h"
#import "ILDNetworkConstants.h"
#import <UIKit/UIKit.h>
#import <AFNetworking/AFNetworking.h>
@interface ILDNetwork()
@property (nonatomic, strong) dispatch_group_t requestGroup;
@property (nonatomic, strong) NSString *storyTitle;
@property (nonatomic, strong) NSString *storyAttribute;
@property (nonatomic, strong) NSString *storyPara1;
@property (nonatomic, strong) UIImage *storyImage;
@property (nonatomic, strong) NSError *error;
@end
@implementation ILDNetwork
- (void)downloadStoryData {
[self downloadImageData];
[self downloadOtherData];
dispatch_group_notify(self.requestGroup, dispatch_get_main_queue(), ^{
if (!self.error) {
[self.delegate fetchStoryDataFail:self.error];
} else {
NSDictionary *storyDataDictionary = @{
kBingStoryURLTitle:self.storyTitle,
kBingStoryURLAttribute:self.storyAttribute,
kBingStoryURLPara1:self.storyPara1,
kBingImageURLImageData:UIImageJPEGRepresentation(self.storyImage, 0.5)
};
[self.delegate fetchStoryDataSuccess:storyDataDictionary];
}
});
}
- (void)downloadImageData {
dispatch_group_enter(self.requestGroup);
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:configuration];
NSURL *URL = [NSURL URLWithString:kBingImageURL];
NSURLRequest *request = [NSURLRequest requestWithURL:URL];
NSURLSessionDataTask *dataTask = [manager dataTaskWithRequest:request completionHandler:^(NSURLResponse *response, id responseObject, NSError *error) {
if (error) {
self.error = error;
} else {
NSDictionary *responseDict = (NSDictionary *)responseObject[kBingImageURLImages][0];
NSString *imageURLString = [NSString stringWithFormat:kBingImageFullPathFormat, responseDict[kBingImageURLUrlBase], kImageTypes];
[self downloadImageData:imageURLString];
}
dispatch_group_leave(self.requestGroup);
}];
[dataTask resume];
}
- (void)downloadImageData:(NSString *)imageUrlString {
dispatch_group_enter(self.requestGroup);
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:configuration];
manager.responseSerializer = [AFImageResponseSerializer serializer];
NSURL *URL = [NSURL URLWithString:imageUrlString];
NSURLRequest *request = [NSURLRequest requestWithURL:URL];
NSURLSessionDataTask *dataTask = [manager dataTaskWithRequest:request completionHandler:^(NSURLResponse *response, id responseObject, NSError *error) {
if (error) {
self.error = error;
} else {
self.storyImage = responseObject;
}
dispatch_group_leave(self.requestGroup);
}];
[dataTask resume];
}
- (void)downloadOtherData {
dispatch_group_enter(self.requestGroup);
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:configuration];
NSURL *URL = [NSURL URLWithString:kBingStoryURL];
NSURLRequest *request = [NSURLRequest requestWithURL:URL];
NSURLSessionDataTask *dataTask = [manager dataTaskWithRequest:request completionHandler:^(NSURLResponse *response, id responseObject, NSError *error) {
if (error) {
self.error = error;
} else {
NSDictionary *responseDict = (NSDictionary *)responseObject;
self.storyTitle = responseDict[kBingStoryURLTitle];
self.storyAttribute = responseDict[kBingStoryURLAttribute];
self.storyPara1 = responseDict[kBingStoryURLPara1];
}
dispatch_group_leave(self.requestGroup);
}];
[dataTask resume];
}
- (dispatch_group_t)requestGroup {
if (!_requestGroup) {
_requestGroup = dispatch_group_create();
}
return _requestGroup;
}
@end