應(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的改動
勤之時 - 表示層(一)
勤之時 - 表示層(二)
勤之時 - 表示層(三)
勤之時 - 表示層(四)
勤之時 - 表示層(五)
關(guān)于數(shù)據(jù)持久層的設(shè)計悲靴,首先推薦一篇大神的文章iOS應(yīng)用架構(gòu)談 本地持久化方案及動態(tài)部署
我們一點點來看, 首先根據(jù)需求決定持久化方案
那么需求是什么臭胜?
【勤之時】有一個任務(wù)列表,每個任務(wù)有自己的一些配置癞尚,并可以增耸三,刪任務(wù),和修改任務(wù)配置浇揩。另外一個比較重要的是仪壮,任務(wù)執(zhí)行狀況的統(tǒng)計分析,這意味著每次執(zhí)行任務(wù)時胳徽,需要記錄下來积锅。此外爽彤,還有每日圖片分享,這個圖片以及圖片介紹是每日從網(wǎng)上下載的缚陷,若沒有網(wǎng)絡(luò)的情況下适篙,則用前一次的圖片及內(nèi)容。
綜上所述箫爷,有3個需要持久化的內(nèi)容
- 任務(wù)列表及其配置嚷节, 若以數(shù)據(jù)庫表結(jié)構(gòu)來描述一個任務(wù)(Task Data Table),如下 )(需要增虎锚,刪硫痰,改,查)
字段名 | 屬性 | 描述 |
---|---|---|
id | string | 任務(wù)ID窜护,主鍵效斑,也可以為數(shù)字類型 |
name | string | 任務(wù)名字 |
color | string | 任務(wù)配色 |
diligenceTime | number | 專注時長 |
isRestMode | bool | 是否為休息模式 |
restTime | number | 休息時長 |
isFocusMode | bool | 是否為沉浸模式 |
musicName | string | 背景音樂 |
isMusicEnabled | bool | 背景音樂是否打開 |
isAlertEnabled | bool | 是否打開任務(wù)提醒 |
alertTime | date | 任務(wù)提醒時間 |
- 任務(wù)列表及其配置, 若以數(shù)據(jù)庫表結(jié)構(gòu)來描述一個專注(Diligence Data Table)柱徙,如下 (需要增缓屠,刪,查)
字段名 | 屬性 | 描述 |
---|---|---|
id | string | 任務(wù)ID坐搔,外鍵藏研,與Task Data Table 的id關(guān)聯(lián) |
startDate | date | 專注開始時間 |
endDate | date | 專注結(jié)束時間 |
breakTimes | number | 中斷次數(shù) |
diligenceTime | number | 專注時長 |
- 每日圖片及故事, 若以數(shù)據(jù)庫表結(jié)構(gòu)來表述 (Todays Story)概行,如下蠢挡,(僅需保留最近一次的值即可)。
字段名 | 屬性 | 描述 |
---|---|---|
dateToday | date | 主鍵凳忙,日期 |
title | string | 描述 |
attribute | string | 來源 |
para1 | string | 今日背景圖故事 |
image | data | 專注時長 |
有哪些方案业踏?
下面內(nèi)容摘抄自大神的文章
在有需要持久化需求的時候,我們有非常多的方案可供選擇:NSUserDefault涧卵、KeyChain勤家、File,以及基于數(shù)據(jù)庫的無數(shù)子方案柳恐。因此伐脖,當(dāng)有需要持久化的需求的時候,我們首先考慮的是應(yīng)該采用什么手段去進行持久化乐设。
- NSUserDefault
一般來說讼庇,小規(guī)模數(shù)據(jù),弱業(yè)務(wù)相關(guān)數(shù)據(jù)近尚,都可以放到NSUserDefault里面蠕啄,內(nèi)容比較多的數(shù)據(jù),強業(yè)務(wù)相關(guān)的數(shù)據(jù)就不太適合NSUserDefault了。
- keychain
Keychain是蘋果提供的帶有可逆加密的存儲機制歼跟,普遍用在各種存密碼的需求上和媳。另外,由于App卸載只要系統(tǒng)不重裝哈街,Keychain中的數(shù)據(jù)依舊能夠得到保留留瞳,以及可被iCloud同步的特性,大家都會在這里存儲用戶唯一標識串叹卷。所以有需要加密撼港、需要存iCloud的敏感小數(shù)據(jù),一般都會放在Keychain骤竹。
- 文件存儲
文件存儲包括了Plist、archive往毡、Stream等方式蒙揣,一般結(jié)構(gòu)化的數(shù)據(jù)或者需要方便查詢的數(shù)據(jù),都會以Plist的方式去持久化开瞭。Archive方式適合存儲平時不太經(jīng)常使用但很大量的數(shù)據(jù)懒震,或者讀取之后希望直接對象化的數(shù)據(jù),因為Archive會將對象及其對象關(guān)系序列化嗤详,以至于讀取數(shù)據(jù)的時候需要Decode很花時間个扰,Decode的過程可以是解壓,也可以是對象化葱色,這個可以根據(jù)具體<NSCoding>中的實現(xiàn)來決定递宅。Stream就是一般的文件存儲了,一般用來存存圖片啊啥的苍狰,適用于比較經(jīng)常使用办龄,然而數(shù)據(jù)量又不算非常大的那種。
- 數(shù)據(jù)庫存儲
數(shù)據(jù)庫存儲的話淋昭,花樣就比較多了俐填。蘋果自帶了一個Core Data,當(dāng)然業(yè)界也有無數(shù)替代方案可選翔忽,不過真正用在iOS領(lǐng)域的除了Core Data外英融,就是FMDB比較多了。數(shù)據(jù)庫方案主要是為了便于增刪改查歇式,當(dāng)數(shù)據(jù)有狀態(tài)和類別的時候最好還是采用數(shù)據(jù)庫方案比較好驶悟,而且尤其是當(dāng)這些狀態(tài)和類別都是強業(yè)務(wù)相關(guān)的時候,就更加要采用數(shù)據(jù)庫方案了贬丛。因為你不可能通過文件系統(tǒng)遍歷文件去甄別你需要獲取的屬于某個狀態(tài)或類別的數(shù)據(jù)撩银,這么做成本就太大了。當(dāng)然豺憔,特別大量的數(shù)據(jù)也不適合直接存儲數(shù)據(jù)庫额获,比如圖片或者文章這樣的數(shù)據(jù)够庙,一般來說,都是數(shù)據(jù)庫存一個文件名抄邀,然后這個文件名指向的是某個圖片或者文章的文件耘眨。如果真的要做全文索引這種需求,建議最好還是掛個API丟到服務(wù)端去做境肾。
從需求上看剔难,需求1,2需要對兩張表進行增刪改查奥喻,而且規(guī)則并不復(fù)雜偶宫,數(shù)據(jù)量也不會很大,且并不需要加密环鲤,所以這里考慮用文件存儲纯趋,以Plist的方式去持久化。
Task Data Table 以 字典的形式 (task id -> task data dictionary)存儲在Document文件夾下的taskData.plist冷离。
Diligence Data Table 以字典的形式 (task id -> diligence data dictionary)存儲在Document文件夾下的diligenceData.plist吵冒。
需求3僅需要保留最近一次的結(jié)果,但里面有一個圖片數(shù)據(jù)西剥,鑒于他有且僅有一個圖片數(shù)據(jù)痹栖,這里還是繼續(xù)使用Plist文件,存儲在Document文件夾下的storyData.plist瞭空。
和數(shù)據(jù)庫形式的持久層不同揪阿,Plist這里僅提供了讀取和保存的功能。其他的例如增匙铡,刪图甜,改的功能,上移到業(yè)務(wù)邏輯層去實現(xiàn)了鳖眼。
最終代碼, 以ILDTaskDataPersistence為例黑毅,其他的請參考Github里的ILDPersistenceLayer工程(https://github.com/Inspirelife96/ILDiligence)
ILDTaskDataPersistence Class
// ILDTaskDataPersistence.h
#import <Foundation/Foundation.h>
@interface ILDTaskDataPersistence : NSObject
+ (NSDictionary *)readTaskData;
+ (void)saveTaskData:(NSDictionary *)taskDataNSDictionary;
@end
// ILDTaskDataPersistence.m
#import "ILDTaskDataPersistence.h"
#import "ILDPersistenceFilePathHelper.h"
NSString *const kTaskDataPersistanceFile = @"taskData.plist";
@implementation ILDTaskDataPersistence
+ (NSDictionary *)readTaskData {
NSString *taskDataFilePath = [ILDPersistenceFilePathHelper persistenceFilePath:kTaskDataPersistanceFile];
NSDictionary *taskDataDictionary = [NSDictionary dictionaryWithContentsOfFile:taskDataFilePath];
if (!taskDataDictionary) {
NSString *defaultTaskDataFilePath = [[NSBundle mainBundle] pathForResource:kTaskDataPersistanceFile ofType:nil];
taskDataDictionary = [NSDictionary dictionaryWithContentsOfFile:defaultTaskDataFilePath];
[ILDTaskDataPersistence saveTaskData:taskDataDictionary];
}
return taskDataDictionary;
}
+ (void)saveTaskData:(NSDictionary *)taskDataNSDictionary {
NSString *taskDataFilePath = [ILDPersistenceFilePathHelper persistenceFilePath:kTaskDataPersistanceFile];
[taskDataNSDictionary writeToFile:taskDataFilePath atomically:YES];
}
@end