基于iOS快遞查詢App的實(shí)現(xiàn)

功能介紹


  • 實(shí)現(xiàn)提供<b>快遞單號(hào)</b>和<b>快遞公司</b>查詢快遞軌跡
  • 實(shí)現(xiàn)<b>二維碼</b>和<b>條形碼</b>掃描查詢
  • 實(shí)現(xiàn)對(duì)個(gè)人查詢記錄保存
  • 實(shí)現(xiàn)市場常見的國內(nèi)與國際快遞電話的查詢與撥打
  • 運(yùn)行截圖


    運(yùn)行截圖

前期準(zhǔn)備


  • 快遞api的選擇:
    目前提供免費(fèi)的快遞api查詢的主要有快遞鳥股冗,快遞100等。在本文中掀亩,我選擇了快遞鳥作為我的快遞api查詢接口。主要原因是:快遞鳥對(duì)常見的快遞(順豐鳖敷,圓通丹泉,中通,韻達(dá)等)都提供免費(fèi)查詢馁菜,而快遞100則需要通過其他方式贡珊,自助定制性不強(qiáng)最爬。
    具體注冊(cè)步驟可以參見官網(wǎng)教程。
  • 快遞電話名稱信息的獲让挪怼:
    在App的第三個(gè)View中爱致,實(shí)現(xiàn)了對(duì)常見快遞客服電話的查詢撥打,這里的信息通過網(wǎng)絡(luò)爬蟲獲取固歪。
    快遞電話地址
  • 爬蟲用到的工具:urllib2+BeautifulSoup
  • 本文我這里為了圖省事蒜鸡,直接將上述網(wǎng)頁中的源代碼保存到代碼中存儲(chǔ)為html_doc進(jìn)行處理。
#-*- coding:utf-8 -*-
from bs4 import BeautifulSoup
html_doc="""這是上述網(wǎng)頁的源代碼"""
soup = BeautifulSoup(html_doc,"html.parser")
#用來存儲(chǔ)快遞名稱的數(shù)組
express = []
#用來存儲(chǔ)快遞電話號(hào)碼的數(shù)組
phoneNum = []
#快遞名稱都位于標(biāo)簽<h4>中
#去掉網(wǎng)頁中位于快遞名稱后的“電話”兩個(gè)字并保存在express中
for li in soup.find_all('h4'):
   data = str(li.get_text()).replace("電話","")
    express.append(data)
#快遞電話位于標(biāo)簽<b>中
for li in soup.find_all('b'):
    data = li.get_text()
    phoneNum.append(data)
expressLen = len(express)
#輸出到控制臺(tái)上
for i in range(expressLen):
    print('express'+str(i)+',',end="")
  • 處理中遇到的問題:
    • 開始使用Python2.7牢裳,中文處理總出問題逢防,編碼解碼網(wǎng)上也查了很多資料,最后還是放棄專用了3.4蒲讯,一試就成功忘朝;
    • Python2.7 和 Python3.4 可以在電腦上共存,由于mac os中某些系統(tǒng)程序會(huì)用到2.7判帮,因此將其保留局嘁。在控制臺(tái)中執(zhí)行<b>.py</b>程序時(shí)溉箕,只需要<b>Python3 xxx.py</b>即可。

程序?qū)崿F(xiàn)


代碼放在Github上了悦昵,歡迎folk肴茄,歡迎star
程序代碼

  • 程序示意圖


    iOS快遞模塊.png

程序界面

  • 查詢模塊:
    1. 用戶輸入:快遞單號(hào)(Required),快遞公司(Required)但指,快遞備注(Optional,用戶在快遞歷史模塊提供備注查看的作用)寡痰;
    2. 獲取用戶輸入的快遞單號(hào)和快遞公司信息,將請(qǐng)求的參數(shù)按照快遞鳥官方要求的格式處理(在這里會(huì)用到MD5加密以及Base64編碼)棋凳;
    3. 借助第三方庫:AFNetworking 拦坠,將第二步中處理好的數(shù)據(jù)POST給快遞鳥,快遞鳥返回Json數(shù)據(jù)剩岳;
    4. 處理返回的Json數(shù)據(jù)并且顯示物流信息贞滨;
    5. 用戶點(diǎn)擊“保存”按鈕,將本單的物流信息保存到plist文件當(dāng)中實(shí)現(xiàn)數(shù)據(jù)的持久化存儲(chǔ)拍棕。
    6. 用戶還可以使用二維碼或條形碼掃描功能晓铆,實(shí)現(xiàn)自動(dòng)識(shí)別快遞單號(hào)。
      利用AFNetworking POST網(wǎng)絡(luò)參數(shù)
//網(wǎng)絡(luò)請(qǐng)求的部分代碼
 -(IBAction)expressSearch:(id)sender {
    //1.編寫上傳參數(shù)
//獲取快遞公司對(duì)應(yīng)的編號(hào)
    NSString* shipperCode = shipperCodeUser;
//獲取快遞單號(hào)
    NSString* logisticCode = self.expressNum.text;
//根據(jù)快遞鳥api請(qǐng)求的要求格式進(jìn)行數(shù)據(jù)的處理
//其中eBusinessID,appKey是快遞鳥api提供
//reqURL:請(qǐng)求的網(wǎng)址绰播,同樣是由快遞鳥api提供
    NSString* requestData = [NSString stringWithFormat:@"{\"OrderCode\":\"\",\"ShipperCode\":\"%@\",\"LogisticCode\":\"%@\"}",shipperCode,logisticCode];
    NSMutableDictionary* params = [[NSMutableDictionary alloc]init];
    NSString* dataSignTmp = [[NSString alloc]initWithFormat:@"%@%@",requestData,appKey];
//這里使用到了MD5加密程序尤蒿,以及Base64編碼
    NSString* dataSign = [[MD5 md5String:dataSignTmp] base64String];
    [params setObject:[requestData stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding] forKey:@"RequestData"];
    [params setObject:eBusinessID forKey:@"EBusinessID"];
    [params setObject:@"1002" forKey:@"RequestType"];
    [params setObject:[dataSign stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding] forKey:@"DataSign"];
    [params setObject:@"2" forKey:@"DataType"];
    //2.上傳參數(shù)并獲得返回值
    AFHTTPSessionManager* manager = [AFHTTPSessionManager manager];
    manager.responseSerializer = [AFHTTPResponseSerializer serializer];
    [manager POST:reqURL parameters:params progress:^(NSProgress * _Nonnull uploadProgress) {
    } success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
        NSLog(@"請(qǐng)求成功:%@",responseObject);
        NSDictionary *json = [NSJSONSerialization JSONObjectWithData:responseObject options:0 error:nil];
    //3. 獲得網(wǎng)絡(luò)數(shù)據(jù)賦值給ExpressInfo對(duì)象
        NSMutableArray* expressTraces = [[NSMutableArray alloc]init];
        for (NSDictionary* traces in [json objectForKey:@"Traces"]) {
            [expressTraces insertObject:traces atIndex:0];
        }
        NSString* shipperCode = [json objectForKey:@"ShipperCode"];
        NSString* logisticCode = [json objectForKey:@"LogisticCode"];
        NSString* expressForUser = self.expressForUser.text;
        ExpressInfo* express = [[ExpressInfo alloc]initWitfShipperCode:shipperCode andlogisticCode:logisticCode andexpressForUser:expressForUser andexpressTraces:expressTraces];
    //4. 傳遞數(shù)據(jù)給ExpresstracesViewController
//獲取的物流數(shù)據(jù)在ExpressTracesViewController中顯示
        ExpressTracesViewController* expressTracesVC = [[ExpressTracesViewController alloc]init];
        expressTracesVC.express = express;
        self.hidesBottomBarWhenPushed = YES;
        [self.navigationController pushViewController:expressTracesVC animated:YES];
        self.hidesBottomBarWhenPushed = NO; 
    } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
        NSLog(@"請(qǐng)求失敗:%@",error.description);
    }];
}

向Plist中讀取并寫入數(shù)據(jù)

//保存數(shù)據(jù)到plist中的代碼
-(void)saveExpressTraces{
    //1. 獲得文件路徑
    NSString* path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject;
    NSString* fileName = [path stringByAppendingPathComponent:@"123.plist"];
    //2. 獲得本單快遞信息
    NSArray* array = [NSArray arrayWithObjects:shipperCode,logisticCode,expressForUser,expressTraces, nil];
    //3. 獲取已經(jīng)保存的快遞信息
    NSMutableArray* arrayCollection = [[NSMutableArray alloc]init];
    NSArray *result = [ NSArray arrayWithContentsOfFile:fileName];
    for (NSArray* obj in result) {
        [arrayCollection addObject:obj];
    }
    //4. 將本單快遞信息追加到已有的信息中
    [arrayCollection addObject:array];
    //5. 存儲(chǔ)
    [arrayCollection writeToFile:fileName atomically:YES];
    //6. 提示框
    UIAlertView* alert = [[UIAlertView alloc]initWithTitle:@"提示" message:@"保存成功" delegate:self cancelButtonTitle:@"確定" otherButtonTitles:nil, nil];
    [alert show];
}

選擇iOS系統(tǒng)相冊(cè)圖片

//獲取相冊(cè)照片
    // 1.判斷相冊(cè)是否可以打開
    if (![UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypePhotoLibrary]) return;
    // 2. 創(chuàng)建圖片選擇控制器
    UIImagePickerController *ipc = [[UIImagePickerController alloc] init];
    ipc.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
    // 3.設(shè)置代理
    ipc.delegate = self;
    // 4.modal出這個(gè)控制器
    [self presentViewController:ipc animated:YES completion:nil];
//實(shí)現(xiàn)相關(guān)協(xié)議
#pragma mark -------- UIImagePickerControllerDelegate---------
-(void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary<NSString *,id> *)info
{
    // 1.取出選中的圖片
    UIImage *pickImage = info[UIImagePickerControllerOriginalImage];
    NSData *imageData = UIImagePNGRepresentation(pickImage);
    CIImage *ciImage = [CIImage imageWithData:imageData];
    // 2.從選中的圖片中讀取二維碼數(shù)據(jù)
    // 2.1創(chuàng)建一個(gè)探測器
    CIDetector *detector = [CIDetector detectorOfType:CIDetectorTypeQRCode context:nil options:@{CIDetectorAccuracy: CIDetectorAccuracyLow}];
    // 2.2利用探測器探測數(shù)據(jù)
    NSArray *feature = [detector featuresInImage:ciImage];
    // 2.3取出探測到的數(shù)據(jù)
    for (CIQRCodeFeature *result in feature) {
        NSLog(@"%@",result.messageString);
        NSString *urlStr = result.messageString;
        [[UIApplication sharedApplication] openURL:[NSURL URLWithString:urlStr]];
    }
    // 注意: 如果實(shí)現(xiàn)了該方法, 當(dāng)選中一張圖片時(shí)系統(tǒng)就不會(huì)自動(dòng)關(guān)閉相冊(cè)控制器
    [picker dismissViewControllerAnimated:YES completion:nil];
}

利用攝像頭捕捉二維碼或者條形碼

#pragma mark --------AVCaptureMetadataOutputObjectsDelegate ---------
-(void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects fromConnection:(AVCaptureConnection *)connection
{
    AVMetadataMachineReadableCodeObject *object = [metadataObjects lastObject];
    if (object == nil) return;
    // 只要掃描到結(jié)果就會(huì)調(diào)用
    self.customLabel.text = object.stringValue;
    NSDictionary* userInfo = [NSDictionary dictionaryWithObject:object.stringValue forKey:@"userInfo"];
    [self clearLayers];
    [self dismissViewControllerAnimated:YES completion:^{
        [[NSNotificationCenter defaultCenter]postNotificationName:@"do" object:self userInfo:userInfo];
    }];
    /*
     將secendView dismissViewControllerAnimated掉幅垮,然后自動(dòng)注冊(cè)一個(gè)名為do的通知
     注冊(cè)了這個(gè)名為的通知,你就可以在任何.m文件里面通過以下代碼調(diào)用到了:
     NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
     [nc addObserver:self
     selector:@selector(handleColorChange:)
     name:@"do"
     object:nil];
     上面的代碼的意思就是尾组,先找到已經(jīng)注冊(cè)過的名為do的通知忙芒,然后再自動(dòng)調(diào)用handleColorChange去處理
     */
    // 2.對(duì)掃描到的二維碼進(jìn)行描邊
    AVMetadataMachineReadableCodeObject *obj = (AVMetadataMachineReadableCodeObject *)[self.previewLayer transformedMetadataObjectForMetadataObject:object];
    [self drawLine:obj];
}

  • 快遞歷史模塊:
    1. 獲取plist中的物流信息;
    2. 顯示到tableview中讳侨。
//獲取plist文件路徑
    NSString *path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject;
//得到123.plist文件
    NSString *fileName = [path stringByAppendingPathComponent:@"123.plist"];
//獲取123.plist文件中的返回值
    NSArray *result = [ NSArray arrayWithContentsOfFile:fileName];
//將返回值復(fù)制給(NSMutableArray* )expressHistory
    expressHistory = [NSMutableArray arrayWithArray:result];

  • 快遞電話模塊
    1. 新建一個(gè)快遞模型Class:ExpressPhoneNum.其中有兩個(gè)成員變量呵萨,快遞名稱和快遞電話;
    2. 將獲取到的快遞信息存儲(chǔ)為ExpressPhoneNum類型
    3. 將數(shù)據(jù)顯示到tableview
    4. 點(diǎn)擊某行實(shí)現(xiàn)撥打電話的功能
//點(diǎn)擊行打電話
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
//獲取點(diǎn)擊的Section
    NSArray* expressXX = self.expressArray[indexPath.section];
//獲取點(diǎn)擊的快遞信息
    ExpressPhoneNum* expressXXX = expressXX[indexPath.row];
    NSString* expressPhoneNumber = expressXXX.expressNum;
    NSMutableString* str = [[NSMutableString alloc]initWithFormat:@"tel:%@",expressPhoneNumber];
//實(shí)現(xiàn)撥號(hào)功能
    UIWebView* callWebView = [[UIWebView alloc]init];
    [callWebView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:str]]];
    [self.view addSubview:callWebView];
    NSLog(@"打電話翱缈纭潮峦!");
}

下一步的工作


  • 界面美化:程序當(dāng)中所有的控件都是系統(tǒng)默認(rèn)的,未做任何修改勇婴;
  • 程序整體功能執(zhí)行沒問題忱嘹,但存在個(gè)別bug,有待優(yōu)化

參考資料


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末耕渴,一起剝皮案震驚了整個(gè)濱河市拘悦,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌橱脸,老刑警劉巖础米,帶你破解...
    沈念sama閱讀 222,252評(píng)論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件分苇,死亡現(xiàn)場離奇詭異,居然都是意外死亡屁桑,警方通過查閱死者的電腦和手機(jī)医寿,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,886評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來蘑斧,“玉大人靖秩,你說我怎么就攤上這事∥谝叮” “怎么了盆偿?”我有些...
    開封第一講書人閱讀 168,814評(píng)論 0 361
  • 文/不壞的土叔 我叫張陵,是天一觀的道長准浴。 經(jīng)常有香客問我事扭,道長,這世上最難降的妖魔是什么乐横? 我笑而不...
    開封第一講書人閱讀 59,869評(píng)論 1 299
  • 正文 為了忘掉前任求橄,我火速辦了婚禮,結(jié)果婚禮上葡公,老公的妹妹穿的比我還像新娘罐农。我一直安慰自己,他們只是感情好催什,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,888評(píng)論 6 398
  • 文/花漫 我一把揭開白布涵亏。 她就那樣靜靜地躺著,像睡著了一般蒲凶。 火紅的嫁衣襯著肌膚如雪气筋。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,475評(píng)論 1 312
  • 那天旋圆,我揣著相機(jī)與錄音宠默,去河邊找鬼。 笑死灵巧,一個(gè)胖子當(dāng)著我的面吹牛搀矫,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播刻肄,決...
    沈念sama閱讀 41,010評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼瓤球,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼!你這毒婦竟也來了肄方?” 一聲冷哼從身側(cè)響起冰垄,我...
    開封第一講書人閱讀 39,924評(píng)論 0 277
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后虹茶,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體逝薪,經(jīng)...
    沈念sama閱讀 46,469評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,552評(píng)論 3 342
  • 正文 我和宋清朗相戀三年蝴罪,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了董济。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,680評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡要门,死狀恐怖虏肾,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情欢搜,我是刑警寧澤封豪,帶...
    沈念sama閱讀 36,362評(píng)論 5 351
  • 正文 年R本政府宣布,位于F島的核電站炒瘟,受9級(jí)特大地震影響吹埠,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜疮装,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,037評(píng)論 3 335
  • 文/蒙蒙 一缘琅、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧廓推,春花似錦刷袍、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,519評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至专缠,卻和暖如春居暖,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背藤肢。 一陣腳步聲響...
    開封第一講書人閱讀 33,621評(píng)論 1 274
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留糯景,地道東北人嘁圈。 一個(gè)月前我還...
    沈念sama閱讀 49,099評(píng)論 3 378
  • 正文 我出身青樓,卻偏偏與公主長得像蟀淮,于是被迫代替她去往敵國和親最住。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,691評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理怠惶,服務(wù)發(fā)現(xiàn)涨缚,斷路器,智...
    卡卡羅2017閱讀 134,711評(píng)論 18 139
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,310評(píng)論 25 707
  • 今天是我生命當(dāng)中獨(dú)一無二的僅有的一天策治,如果一切如我所愿脓魏,在未來我將還有21892天兰吟。在一去不返的今天,…… 陪兒子...
    成長無極限閱讀 259評(píng)論 0 0
  • 每篇都試著用自己的話去講給老婆聽茂翔,讓老婆成為我的第一個(gè)聽眾混蔼。
    成長時(shí)間線閱讀 98評(píng)論 0 0
  • 參考慕課網(wǎng)視頻 : http://www.imooc.com/view/506然后是實(shí)際操作最后做做筆記 界面設(shè)置...
    假裝會(huì)編程閱讀 318評(píng)論 0 0