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

前言

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

思路

我們在iOS的app開發(fā)中通常采用的是CLLocationManager來獲取用戶當(dāng)前的位置盆犁,當(dāng)然也可以采用MKMapViewshowUserLocation來獲取用戶的位置命咐,所以我們分別針對這兩種情況分析。

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求晶、中間代理對象

這種思路是Swizzle了CLLocationManagersetDelegate:方法焰雕,當(dāng)調(diào)用setDelegate時(shí),將真實(shí)的delegate object保存下來芳杏,再將我們定義的中間代理類swizzle delegate對象設(shè)置為CLLocationManagerdelegate,這樣當(dāng)系統(tǒng)回調(diào)CLLocationManagerDelegate辟宗,會(huì)先回調(diào)到中間代理類swizzle delegate中爵赵,再由swizzle delegate將事件傳遞到真實(shí)的delegate object

  • 優(yōu)點(diǎn):相對于第一種方法泊脐,不需要遍歷類和類的方法列表空幻,只需swizzle CLLocationManager中的setDelegate:方法即可。
  • 缺點(diǎn):在中間代理類swizzle delegate中需要實(shí)現(xiàn)全部的CLLocationManagerDelegate方法容客,如果后續(xù)增加代理方法秕铛,仍需要修改這個(gè)類约郁。

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

Objective-C中有2個(gè)基類但两,常用的就是NSObject鬓梅,另一個(gè)就是NSProxyNSProxy主要用于消息轉(zhuǎn)發(fā)處理谨湘,所以采用NSProxy我們可以更好的處理方法二中的缺點(diǎn)绽快。

3.1

創(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
// MockLocationProxy.m
#import "MockLocationProxy.h"

@implementation MockLocationProxy

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

@end

接著就來處理消息轉(zhuǎn)發(fā)的邏輯坊罢,首先我們要知道我們想要的是什么效果,系統(tǒng)回調(diào)給MockLocationProxy擅耽,MockLocationProxy只處理locationManager:didUpdateLocations:活孩,其他的消息都仍然交給原target。

所以我們在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

當(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就可以了忧额。

后記

前言說過,定位除了CLLocationManager之外愧口,MKMapViewshowUserLocation也可以獲取定位信息睦番,那么如何解決這個(gè)問題呢? 你可以在LLDebugTool/Location中查看答案。

不要忘記點(diǎn)Star托嚣! ???

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末巩检,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子示启,更是在濱河造成了極大的恐慌兢哭,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,948評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件丑搔,死亡現(xiàn)場離奇詭異厦瓢,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)啤月,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,371評論 3 385
  • 文/潘曉璐 我一進(jìn)店門煮仇,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人谎仲,你說我怎么就攤上這事浙垫。” “怎么了郑诺?”我有些...
    開封第一講書人閱讀 157,490評論 0 348
  • 文/不壞的土叔 我叫張陵夹姥,是天一觀的道長。 經(jīng)常有香客問我辙诞,道長辙售,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,521評論 1 284
  • 正文 為了忘掉前任飞涂,我火速辦了婚禮旦部,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘较店。我一直安慰自己士八,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,627評論 6 386
  • 文/花漫 我一把揭開白布梁呈。 她就那樣靜靜地躺著婚度,像睡著了一般。 火紅的嫁衣襯著肌膚如雪官卡。 梳的紋絲不亂的頭發(fā)上蝗茁,一...
    開封第一講書人閱讀 49,842評論 1 290
  • 那天,我揣著相機(jī)與錄音寻咒,去河邊找鬼评甜。 笑死,一個(gè)胖子當(dāng)著我的面吹牛仔涩,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播粘舟,決...
    沈念sama閱讀 38,997評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼熔脂,長吁一口氣:“原來是場噩夢啊……” “哼佩研!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起霞揉,我...
    開封第一講書人閱讀 37,741評論 0 268
  • 序言:老撾萬榮一對情侶失蹤旬薯,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后适秩,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體绊序,經(jīng)...
    沈念sama閱讀 44,203評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,534評論 2 327
  • 正文 我和宋清朗相戀三年秽荞,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了骤公。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,673評論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡扬跋,死狀恐怖阶捆,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情钦听,我是刑警寧澤洒试,帶...
    沈念sama閱讀 34,339評論 4 330
  • 正文 年R本政府宣布,位于F島的核電站朴上,受9級特大地震影響垒棋,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜痪宰,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,955評論 3 313
  • 文/蒙蒙 一叼架、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧酵镜,春花似錦碉碉、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,770評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至靠粪,卻和暖如春蜡吧,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背占键。 一陣腳步聲響...
    開封第一講書人閱讀 32,000評論 1 266
  • 我被黑心中介騙來泰國打工昔善, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人畔乙。 一個(gè)月前我還...
    沈念sama閱讀 46,394評論 2 360
  • 正文 我出身青樓君仆,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子返咱,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,562評論 2 349