iOS實(shí)現(xiàn)模擬定位功能

作者:HDB_Li
鏈接:https://juejin.im/post/5dd7553b51882573530949fe

前言

App中越來越多的功能依賴用戶實(shí)際的位置囊卜,例如基于用戶位置提供推薦數(shù)據(jù)厨钻、基于定位判斷某些功能是否可用,但是在開發(fā)調(diào)試中XCode卻沒有提供自定義的模擬定位的功能激蹲,所以本文主要的目的是現(xiàn)實(shí)一個(gè)可以在開發(fā)調(diào)試過程中隨時(shí)模擬定位的功能扫沼。

PS: 當(dāng)你的app在脫離了XCode的調(diào)試測(cè)試階段崇呵,仍想修改定位的話缤剧。

思路

我們?cè)趇OS的app開發(fā)中通常采用的是CLLocationManager來獲取用戶當(dāng)前的位置,當(dāng)然也可以采用MKMapViewshowUserLocation來獲取用戶的位置域慷,所以我們分別針對(duì)這兩種情況分析荒辕。

CLLocationManager

采用CLLocationManager獲取定位時(shí),是根據(jù)CLLocationManagerDelegate- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray<CLLocation *> *)locations的回調(diào)來獲取到定位的犹褒。我們只需要在系統(tǒng)回調(diào)這個(gè)方法傳遞給業(yè)務(wù)代碼的中間抵窒,插入一部分代碼,來修改locations參數(shù)叠骑。原本的邏輯為系統(tǒng)回調(diào)->業(yè)務(wù)代碼李皇,現(xiàn)在變?yōu)?code>系統(tǒng)回調(diào)->模擬定位模塊->業(yè)務(wù)代碼,就實(shí)現(xiàn)了無侵入式的實(shí)現(xiàn)模擬定位功能宙枷。為了實(shí)現(xiàn)這個(gè)邏輯掉房,可以有以下幾個(gè)思路。

1慰丛、 Runtime swizzle

因?yàn)?code>業(yè)務(wù)代碼是根據(jù)- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray<CLLocation *> *)locations方法來接受回調(diào)的卓囚,所以可以采用Runtime swizzle這個(gè)方法,來實(shí)現(xiàn)模擬定位的功能璧帝,但是我們中間組件是不知道業(yè)務(wù)代碼中具體是哪個(gè)類捍岳,所以無法具體指定runtime swizzle哪個(gè)類,所以只能遍歷所有的類睬隶,判斷當(dāng)前類的方法列表中是否有locationManager:didUpdateLocations:這個(gè)方法,如果存在則swizzle页徐。

  • 優(yōu)點(diǎn):便于理解苏潜。
  • 缺點(diǎn):需要遍歷所有的類和類的方法列表。

2变勇、中間代理對(duì)象

這種思路是Swizzle了CLLocationManagersetDelegate:方法恤左,當(dāng)調(diào)用setDelegate時(shí),將真實(shí)的delegate object保存下來搀绣,再將我們定義的中間代理類swizzle delegate對(duì)象設(shè)置為CLLocationManagerdelegate飞袋,這樣當(dāng)系統(tǒng)回調(diào)CLLocationManagerDelegate,會(huì)先回調(diào)到中間代理類swizzle delegate中链患,再由swizzle delegate將事件傳遞到真實(shí)的delegate object巧鸭。

  • 優(yōu)點(diǎn):相對(duì)于第一種方法,不需要遍歷類和類的方法列表麻捻,只需swizzle CLLocationManager中的setDelegate:方法即可纲仍。
  • 缺點(diǎn):在中間代理類swizzle delegate中需要實(shí)現(xiàn)全部的CLLocationManagerDelegate方法呀袱,如果后續(xù)增加代理方法,仍需要修改這個(gè)類郑叠。

3夜赵、采用NSProxy實(shí)現(xiàn)中間代理對(duì)象

Objective-C中有2個(gè)基類,常用的就是NSObject乡革,另一個(gè)就是NSProxy寇僧,NSProxy主要用于消息轉(zhuǎn)發(fā)處理,所以采用NSProxy我們可以更好的處理方法二中的缺點(diǎn)沸版。

創(chuàng)建一個(gè)新的類MockLocationProxy嘁傀,集成自NSProxy

// MockLocationProxy.h
#import <CoreLocation/CoreLocation.h>

@interface MockLocationProxy : NSProxy

@property (nonatomic, weak, readonly, nullable) id <CLLocationManagerDelegate> target;

- (instancetype)initWithTarget:(id <CLLocationManagerDelegate>)target;

@end
復(fù)制代碼
// MockLocationProxy.m
#import "MockLocationProxy.h"

@implementation MockLocationProxy

- (instancetype)initWithTarget:(id<CLLocationManagerDelegate>)target {
    _target = target;
    return self;
}

@end
復(fù)制代碼

接著就來處理消息轉(zhuǎn)發(fā)的邏輯推穷,首先我們要知道我們想要的是什么效果心包,系統(tǒng)回調(diào)給MockLocationProxyMockLocationProxy只處理locationManager:didUpdateLocations:馒铃,其他的消息都仍然交給原target蟹腾。

所以我們?cè)?code>MockLocationProxy.m中添加以下方法:

// MockLocationProxy.m
@implementation MockLocationProxy

- (instancetype)initWithTarget:(id<CLLocationManagerDelegate>)target {
    _target = target;
    return self;
}

- (BOOL)respondsToSelector:(SEL)aSelector {
    if (aSelector == @selector(locationManager:didUpdateLocations:)) {
        return YES;
    }
    return [self.target respondsToSelector:aSelector];
}

- (void)forwardInvocation:(NSInvocation *)invocation {
    SEL sel = invocation.selector;
    if ([self.target respondsToSelector:sel]) {
        [invocation invokeWithTarget:self.target];
    }
}

- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel {
    return [self.target methodSignatureForSelector:sel];
}

#pragma mark - CLLocationManagerDelegate
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray<CLLocation *> *)locations {
    if ([self.target respondsToSelector:_cmd]) {
        // 模擬定位代碼
        CLLocation *mockLocation = [[CLLocation alloc] initWithLatitude:39.908722 longitude:116.397499];
        locations = @[mockLocation];
        [self.target locationManager:manager didUpdateLocations:locations];
    }
}
@end
復(fù)制代碼

當(dāng)消息發(fā)送給MockLocationProxy時(shí),判斷當(dāng)前方法是否是locationManager:didUpdateLocations:区宇,如果是娃殖,則MockLocationProxy響應(yīng)事件,否則直接傳遞給原本的target议谷。到此已經(jīng)可以隨時(shí)處理模擬定位炉爆。你只需要在模擬定位的代碼做一些處理,就可以隨時(shí)修改定位卧晓。

One more.

上述方法雖然可以模擬定位芬首,但是每次修改模擬值都需重新build,那么有沒有辦法在運(yùn)行時(shí)隨時(shí)修改這個(gè)值呢逼裆?

LLDebugTool

當(dāng)然可以郁稍,你只需要在你的項(xiàng)目中集成LLDebugTool,調(diào)用其中的Location模塊胜宇,LLDebugTool提供了一個(gè)UI來隨時(shí)修改這個(gè)模擬值耀怜,讓你在調(diào)試時(shí),隨時(shí)模擬定位桐愉,LLDebugTool仍提供了很多其他的功能财破,如果你只需要模擬定位的功能,則只需要集成LLDebugTool/Location這個(gè)subspec就可以了从诲。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末左痢,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌抖锥,老刑警劉巖亿眠,帶你破解...
    沈念sama閱讀 221,695評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異磅废,居然都是意外死亡纳像,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,569評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門拯勉,熙熙樓的掌柜王于貴愁眉苦臉地迎上來竟趾,“玉大人,你說我怎么就攤上這事宫峦〔砻保” “怎么了?”我有些...
    開封第一講書人閱讀 168,130評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵导绷,是天一觀的道長(zhǎng)犀勒。 經(jīng)常有香客問我,道長(zhǎng)妥曲,這世上最難降的妖魔是什么贾费? 我笑而不...
    開封第一講書人閱讀 59,648評(píng)論 1 297
  • 正文 為了忘掉前任,我火速辦了婚禮檐盟,結(jié)果婚禮上褂萧,老公的妹妹穿的比我還像新娘。我一直安慰自己葵萎,他們只是感情好导犹,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,655評(píng)論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著羡忘,像睡著了一般谎痢。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上卷雕,一...
    開封第一講書人閱讀 52,268評(píng)論 1 309
  • 那天舶得,我揣著相機(jī)與錄音,去河邊找鬼爽蝴。 笑死,一個(gè)胖子當(dāng)著我的面吹牛纫骑,可吹牛的內(nèi)容都是我干的蝎亚。 我是一名探鬼主播,決...
    沈念sama閱讀 40,835評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼先馆,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼发框!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起煤墙,我...
    開封第一講書人閱讀 39,740評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤梅惯,失蹤者是張志新(化名)和其女友劉穎宪拥,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體铣减,經(jīng)...
    沈念sama閱讀 46,286評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡她君,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,375評(píng)論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了葫哗。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片缔刹。...
    茶點(diǎn)故事閱讀 40,505評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖劣针,靈堂內(nèi)的尸體忽然破棺而出校镐,到底是詐尸還是另有隱情,我是刑警寧澤捺典,帶...
    沈念sama閱讀 36,185評(píng)論 5 350
  • 正文 年R本政府宣布鸟廓,位于F島的核電站,受9級(jí)特大地震影響襟己,放射性物質(zhì)發(fā)生泄漏引谜。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,873評(píng)論 3 333
  • 文/蒙蒙 一稀蟋、第九天 我趴在偏房一處隱蔽的房頂上張望煌张。 院中可真熱鬧,春花似錦退客、人聲如沸骏融。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,357評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)档玻。三九已至,卻和暖如春茫藏,著一層夾襖步出監(jiān)牢的瞬間误趴,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,466評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工务傲, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留凉当,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,921評(píng)論 3 376
  • 正文 我出身青樓售葡,卻偏偏與公主長(zhǎng)得像看杭,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子挟伙,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,515評(píng)論 2 359

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