首先說一下我這個(gè)的實(shí)現(xiàn)思路,登錄支持多個(gè)賬號(hào),,也就是說可以保存多個(gè)微博賬號(hào)登錄的時(shí)候選擇其中一個(gè)登錄,。多個(gè)賬號(hào)信息保存在sqlite的數(shù)據(jù)庫中,每一個(gè)賬號(hào)信息就是一條記錄, 當(dāng)用戶啟動(dòng)微博客戶端的時(shí)候去取保存在sqlite數(shù)據(jù)庫中的賬號(hào)記錄信息,,然后把這些在界面中以列表的形式展示出來,,用戶可以點(diǎn)擊其中的一個(gè)賬號(hào)進(jìn)入微博,如果如果啟動(dòng)微博客戶端的時(shí)候檢查到sqlite數(shù)據(jù)庫中一個(gè)賬號(hào)都沒有的時(shí)候,,程序自動(dòng)顯示用戶授權(quán)認(rèn)證頁面,,本客戶端是基于oauth認(rèn)證實(shí)現(xiàn)的,所以使用新微博賬號(hào)前需要進(jìn)行授權(quán)認(rèn)證,,一個(gè)賬號(hào)只需要第一次做一下授權(quán)認(rèn)證然后會(huì)把user_id,、Access Token和Access Secret以及用戶名稱和頭像小圖標(biāo)一起保存到sqlite的數(shù)據(jù)庫中,下次登錄的時(shí)候可以不在需要輸入賬號(hào)和密碼,,直接在界面中點(diǎn)擊已經(jīng)選擇的用戶小圖標(biāo)進(jìn)入微博,。關(guān)于Access Token和Access Secret這個(gè)我就不解釋了,不懂的可以google一下oauth的知識(shí),,其實(shí)我前面做android版本的微博客戶端的時(shí)候?qū)戇^一篇比較詳細(xì)的介紹過關(guān)于oauth的隨筆可以翻出來參考一下,。
從上面的思路來看用戶登錄有兩種可能性,第一種數(shù)據(jù)庫中尚無任何賬號(hào)信息,;第二種數(shù)據(jù)庫中已經(jīng)包含了一個(gè)或以上的賬號(hào)信息,。這兩種情況顯示不同的用戶界面,兩種情況的實(shí)現(xiàn)過程看下面的描述,。
第一種情況實(shí)現(xiàn)過程:1.查詢sqlite數(shù)據(jù)庫 -->2.無賬號(hào)記錄顯示需要進(jìn)行授權(quán)認(rèn)證的提示信息界面(上圖6) -->3.在上一步界面中點(diǎn)擊開始按鈕進(jìn)入用戶授權(quán)界面-->4.用戶輸入自己的微博賬號(hào)和密碼點(diǎn)擊確定然后關(guān)閉用戶授權(quán)界面(上圖7)-->5.程序根據(jù)用戶的授權(quán)獲取這個(gè)賬號(hào)的信息保存到sqlite庫-->6.顯示用戶登錄選擇界面默認(rèn)賬號(hào)就是用戶剛剛授權(quán)的賬號(hào)
第二種情況實(shí)現(xiàn)過程:1.查詢sqlite數(shù)據(jù)庫 -->2.已有賬號(hào)記錄獲取所有賬號(hào)信息并且顯示用戶登錄選擇界面 -->3.讀取上一次登錄的賬號(hào)作為界面的默認(rèn)選擇用戶顯示(如上圖2)-->4.點(diǎn)擊添加按鈕顯示賬號(hào)添加界面(如上圖4)-->5.點(diǎn)擊切換按鈕顯示賬號(hào)選擇列表界面(如下圖3)-->6.點(diǎn)擊刪除按鈕顯示賬號(hào)刪除確認(rèn)界面(如下圖5)
從上面的實(shí)現(xiàn)過程看涉及到的主要知識(shí)點(diǎn):1.sqlite數(shù)據(jù)庫操作(創(chuàng)建數(shù)據(jù)庫,、創(chuàng)建數(shù)據(jù)表、插入數(shù)據(jù)記錄,、讀取數(shù)據(jù)記錄),;2、oauth授權(quán)認(rèn)證(已經(jīng)由sdk實(shí)現(xiàn)了調(diào)用相關(guān)方法即可),;3,、NSUserDefaults存取(用來記錄上一次的登錄賬號(hào))
現(xiàn)在正式開始動(dòng)手做一一些準(zhǔn)備工作:
一,、到 http://code.google.com/p/minblog4sina/(關(guān)于這個(gè)可以參看:iphone開發(fā)我的新浪微博客戶端-開篇)把我的這個(gè)sdk項(xiàng)目源代碼checkout到本地,,然后用xcode打開這個(gè)工程,在這個(gè)sdk工程的基礎(chǔ)上開發(fā)微博客戶端應(yīng)用,。
二,、設(shè)計(jì)sqlite數(shù)據(jù)庫用來保存用戶賬號(hào)信息,我是用firefox的一個(gè)名為SQLite Manager的插件來創(chuàng)建sqlite數(shù)據(jù)庫,這個(gè)使用非常簡單就不介紹了自己裝起來就知道怎么使用,,我用它創(chuàng)建了一個(gè)名為weibo.sqlite的數(shù)據(jù)庫,,然后這這個(gè)數(shù)據(jù)庫中創(chuàng)建了一個(gè)名為loginUser的表用來保存賬號(hào)信息,loginUser表的,,這個(gè)表包含5個(gè)字段分別用來保存user_id,、賬號(hào)昵稱、Access Token,、Access Secret,、賬號(hào)小圖標(biāo),具體如下圖:
三,、用xcode打開工程,,然后把上一步完成的weibo.sqlite文件添加到工程的Resources文件夾中.
四、在工程中添加類型為Objective-C class名為Sqlite的類用來負(fù)責(zé)sqlite庫的創(chuàng)建,、記錄的讀取,、記錄的添加等操作,同時(shí)在Xcode的左邊樹Frameworks右鍵添加名為libsqlite3.dylib的Framework,,這個(gè)類庫封裝了sqlite庫的操作,。
五,、打開Sqlite.h添加如下代碼,,在這個(gè)文件中首先import了sqlite3.h,這個(gè)就是Frameworks中的libsqlite3.dylib類庫,,然后又import了User.h,,這個(gè)是MinBlog4Sina的sdk中的用戶賬號(hào)對象類。又聲明的4個(gè)方法分別是數(shù)據(jù)庫初始化,、獲取用戶記錄列表,、添加用戶記錄、刪除用戶記錄,。
#import <Foundation/Foundation.h>
#import <sqlite3.h>
#import "User.h"
@interface Sqlite : NSObject {
sqlite3 *database;
}
-(Sqlite *)init;
-(NSMutableArray *)getUserList;
-(BOOL)addUser:(User *)user;
-(BOOL)delUser:(NSString *)name;
@end
六,、打開Sqlite.m添加如下代碼,這個(gè)文件里實(shí)現(xiàn)了上述的幾個(gè)方法,所謂的數(shù)據(jù)庫創(chuàng)建其實(shí)就是把上面第三個(gè)步驟中添加到工程的weibo.sqlite文件拷貝到本軟件的目錄中,,在下面的-(Sqlite *)init方法中進(jìn)行了拷貝操作,。還有就是用戶記錄讀取和添加中關(guān)于BLOB類型的數(shù)據(jù)處理,這里存儲(chǔ)的是圖片數(shù)據(jù)了,,在添加的時(shí)候把圖片轉(zhuǎn)換成bytes類型,,讀取的時(shí)候把bytes類型轉(zhuǎn)換成圖片。
#import "Sqlite.h"
@interface Sqlite(private)
-(void)createDatabaseIfNeeded:(NSString *)filename;
-(NSString *)dataFilePath;
-(BOOL)openDatabase;
-(void)closeDatabase;
@end
@implementation Sqlite
//初始化數(shù)據(jù)庫
-(Sqlite *)init
{
if (self= [super init]) {
[self createDatabaseIfNeeded:@"weibo.sqlite"];
}
return self;
}
-(BOOL)delUser:(NSString *)name
{
BOOL sucess=[self openDatabase];
if (sucess) {
NSString *sql=[[NSString alloc] initWithFormat:@"delete from loginUser where screen_name='%@'",name];
char *errorMsg;
if (sqlite3_exec(database, [sql UTF8String], NULL, NULL, &errorMsg)!=SQLITE_OK) {
NSLog(@"del User error:%s",errorMsg);
sqlite3_free(errorMsg);
}else {
}
[sql release];
}
[self closeDatabase];
return sucess;
}
//添加用戶記錄
-(BOOL)addUser:(User *)user
{
BOOL sucess=[self openDatabase];
if (sucess) {
NSString *sql=[[NSString alloc] initWithFormat:@"insert into loginUser(user_id,screen_name,key,secret,icon)values(?,?,?,?,?)"];
sqlite3_stmt *statement;
if (sqlite3_prepare_v2(database, [sql UTF8String], -1, &statement, nil)==SQLITE_OK)
{
NSString *uid=[NSString stringWithFormat:@"%@",user.userId];
const char *user_idChar=[uid UTF8String];
sqlite3_bind_text(statement, 1, user_idChar, strlen(user_idChar), NULL);
const char *screen_nameChar=[user.screen_name UTF8String];
sqlite3_bind_text(statement, 2, screen_nameChar, strlen(screen_nameChar), NULL);
const char *keyChar=[user.key UTF8String];
sqlite3_bind_text(statement, 3, keyChar, strlen(keyChar), NULL);
const char *secretChar=[user.secret UTF8String];
sqlite3_bind_text(statement, 4, secretChar, strlen(secretChar), NULL);
NSData *imageData = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:user.profile_image_url]];
sqlite3_bind_blob(statement, 5, [imageData bytes], [imageData length], NULL);
if(sqlite3_step(statement)==SQLITE_DONE)
{
NSLog(@"done");
}else {
NSLog(@"error");
}
sqlite3_finalize(statement);
}
[sql release];
}
[self closeDatabase];
return sucess;
}
//獲取用戶記錄列表
-(NSMutableArray *)getUserList
{
NSMutableArray *list;
BOOL sucess=[self openDatabase];
if (sucess) {
NSString *query=@"select * from loginUser";
sqlite3_stmt *statement;
if (sqlite3_prepare_v2(database, [query UTF8String], -1, &statement, nil)==SQLITE_OK)
{
list=[[[NSMutableArray alloc] init]autorelease];
while (sqlite3_step(statement)==SQLITE_ROW) {
char *user_idChar=(char *)sqlite3_column_text(statement, 0);
char *screen_nameChar=(char *)sqlite3_column_text(statement, 1);
char *keyChar=(char *)sqlite3_column_text(statement, 2);
char *secretChar=(char *)sqlite3_column_text(statement, 3);
int bytes = sqlite3_column_bytes(statement, 4);
const void *value = sqlite3_column_blob(statement, 4);
NSString *user_id=[[NSString alloc] initWithUTF8String:user_idChar];
NSString *screen_name=[[NSString alloc] initWithUTF8String:screen_nameChar];
NSString *key=[[NSString alloc] initWithUTF8String:keyChar];
NSString *secret=[[NSString alloc] initWithUTF8String:secretChar];
UIImage *icon;
if( value != NULL && bytes != 0 ){
NSData *data = [NSData dataWithBytes:value length:bytes];
icon=[UIImage imageWithData:data];
}
User *user=[[User alloc] init];
user.userId=user_id;
user.name=screen_name;
user.key=key;
user.secret=secret;
if (icon) {
user.icon=icon;
}
[list addObject:user];
[user release];
[user_id release];
[screen_name release];
[key release];
[secret release];
}
sqlite3_finalize(statement);
}
}
[self closeDatabase];
return list;
}
//根據(jù)是否已經(jīng)存在數(shù)據(jù)庫決定是否要?jiǎng)?chuàng)建數(shù)據(jù)庫
-(void)createDatabaseIfNeeded:(NSString *)filename
{
NSAutoreleasePool *pool=[[NSAutoreleasePool alloc] init];
NSFileManager *filemanage=[NSFileManager defaultManager];
NSArray *paths=NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory=[paths objectAtIndex:0];
NSString *writableDBPath=[documentsDirectory stringByAppendingPathComponent:filename];
BOOL sucess=[filemanage fileExistsAtPath:writableDBPath];
if (sucess) {
return;
}
NSString *defaultDBPath=[[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:filename];
NSError *error;
sucess=[filemanage copyItemAtPath:defaultDBPath toPath:writableDBPath error:&error];
if (!sucess) {
NSLog(@"create Database rror:%@",[error localizedDescription]);
}
[pool release];
}
//獲取數(shù)據(jù)庫文件路徑
-(NSString *)dataFilePath
{
NSArray *paths=NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentPath=[[paths objectAtIndex:0] stringByAppendingPathComponent:@"weibo.sqlite"];
return documentPath;
}
//打開數(shù)據(jù)庫
-(BOOL)openDatabase
{
int returnValue=sqlite3_open([[self dataFilePath] UTF8String], &database);
if (returnValue!=SQLITE_OK) {
sqlite3_close(database);
NSLog(@"open Database error:%@",sqlite3_errmsg(database));
return NO;
}else {
return YES;
}
}
//關(guān)閉數(shù)據(jù)庫
-(void)closeDatabase
{
if (sqlite3_close(database)!=SQLITE_OK) {
NSLog(@"close Database error:%@",sqlite3_errmsg(database));
}
}
@end
七,、在工程中添加類型為Objective-C class名為Global的類用來提供一些非常常用的公用方法,。
八、打開Global.h添加如下代碼,聲明了2個(gè)方法,,一個(gè)是獲取png的圖片資源,、另外一個(gè)是進(jìn)行時(shí)間格式化輸出的方法,這2個(gè)方法都是被聲明成+類型,,這樣方便使用,這2個(gè)方法在后面篇章中經(jīng)常會(huì)用到,。
#import <Foundation/Foundation.h>
@interface Global : NSObject
+(UIImage *) pngWithPath:(NSString *)path;
+(NSString *) dateInFormat: (time_t)dateTime format:(NSString*) stringFormat;
@end
九、打開Global.m添加如下代碼
#import "Global.h"
@implementation Global
+(UIImage *) pngWithPath:(NSString *)path
{
NSString *fileLocation = [[NSBundle mainBundle] pathForResource:path ofType:@"png"];
NSData *imageData = [NSData dataWithContentsOfFile:fileLocation];
UIImage *img=[UIImage imageWithData:imageData];
return img;
}
+(NSString *) dateInFormat: (time_t)dateTime format:(NSString*) stringFormat
{
char buffer[80];
const char *format = [stringFormat UTF8String];
struct tm * timeinfo;
timeinfo = localtime(&dateTime);
strftime(buffer, 80, format, timeinfo);
return [NSString stringWithCString:buffer encoding:NSUTF8StringEncoding];
}
@end
到處準(zhǔn)備篇的工作就已經(jīng)算是完成了,,主要的編碼工作就是sqlite的數(shù)據(jù)庫操作了,,在下一篇中我們會(huì)在這個(gè)基礎(chǔ)上開始用戶功能的開發(fā)。