Last active
March 5, 2023 22:07
-
-
Save 0xced/3035167 to your computer and use it in GitHub Desktop.
Hack to choose the displayed carrier name in the iOS simulator
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// Copyright (c) 2012-2015 Cédric Luthi / @0xced. All rights reserved. | |
// | |
#import <Foundation/Foundation.h> | |
#if TARGET_OS_SIMULATOR | |
static const char *fakeCarrier; | |
static const char *fakeTime; | |
#import <objc/runtime.h> | |
@interface XCDFakeCarrier : NSObject | |
@end | |
@implementation XCDFakeCarrier | |
/* | |
Runtime offsets for the -[UIStatusBarComposedData rawData] struct | |
struct { | |
BOOL itemIsEnabled[27]; | |
BOOL timeString[64]; | |
int gsmSignalStrengthRaw; | |
int gsmSignalStrengthBars; | |
BOOL serviceString[100]; | |
... | |
} | |
See https://github.com/nst/iOS-Runtime-Headers/blob/master/Frameworks/UIKit.framework/UIStatusBarComposedData.h | |
*/ | |
static NSUInteger timeStringOffset; | |
static NSUInteger timeStringSize; | |
static NSUInteger gsmSignalStrengthBarsOffset; | |
static NSUInteger gsmSignalStrengthBarsSize; | |
static NSUInteger serviceStringOffset; | |
static NSUInteger serviceStringSize; | |
+ (void) load | |
{ | |
fakeCarrier = getenv("FAKE_CARRIER"); | |
if (!fakeCarrier) | |
{ | |
NSLog(@"You must set the FAKE_CARRIER environment variable"); | |
return; | |
} | |
fakeTime = getenv("FAKE_TIME"); | |
const Class UIStatusBarComposedData = objc_getClass("UIStatusBarComposedData"); | |
const Method rawData = class_getInstanceMethod(UIStatusBarComposedData, sel_getUid("rawData")); | |
const char *typeEncoding = method_getTypeEncoding(rawData); | |
if (typeEncoding) | |
{ | |
NSRegularExpression *typeEncodingRegularExpression = [NSRegularExpression regularExpressionWithPattern:@"\\^(\\{\\?=\\[\\d+[Bc]\\]\\[\\d+c\\]ii\\[\\d+c\\])" options:(NSRegularExpressionOptions)0 error:NULL]; | |
NSString *typeEncodingString = @(typeEncoding); | |
NSTextCheckingResult *typeEncodingMatch = [typeEncodingRegularExpression firstMatchInString:typeEncodingString options:(NSMatchingOptions)0 range:NSMakeRange(0, typeEncodingString.length)]; | |
if (typeEncodingMatch.numberOfRanges > 1) | |
{ | |
NSString *partialTypeEncoding = [[typeEncodingString substringWithRange:[typeEncodingMatch rangeAtIndex:1]] stringByAppendingString:@"}"]; | |
NSMethodSignature *methodSignature = [NSMethodSignature signatureWithObjCTypes:partialTypeEncoding.UTF8String]; | |
NSString *parsedTypeEncoding = methodSignature.debugDescription; | |
NSRegularExpression *parsedTypeEncodingRegularExpression = [NSRegularExpression regularExpressionWithPattern:@"^ memory \\{offset = (\\d+), size = (\\d+)\\}" options:NSRegularExpressionAnchorsMatchLines error:NULL]; | |
NSArray<NSTextCheckingResult *> *offsetMatches = [parsedTypeEncodingRegularExpression matchesInString:parsedTypeEncoding options:(NSMatchingOptions)0 range:NSMakeRange(0, parsedTypeEncoding.length)]; | |
if (offsetMatches.count == 5) | |
{ | |
timeStringOffset = [[parsedTypeEncoding substringWithRange:[offsetMatches[1] rangeAtIndex:1]] integerValue]; | |
timeStringSize = [[parsedTypeEncoding substringWithRange:[offsetMatches[1] rangeAtIndex:2]] integerValue]; | |
gsmSignalStrengthBarsOffset = [[parsedTypeEncoding substringWithRange:[offsetMatches[3] rangeAtIndex:1]] integerValue]; | |
gsmSignalStrengthBarsSize = [[parsedTypeEncoding substringWithRange:[offsetMatches[3] rangeAtIndex:2]] integerValue]; | |
serviceStringOffset = [[parsedTypeEncoding substringWithRange:[offsetMatches[4] rangeAtIndex:1]] integerValue]; | |
serviceStringSize = [[parsedTypeEncoding substringWithRange:[offsetMatches[4] rangeAtIndex:2]] integerValue]; | |
BOOL isTimeStringOK = timeStringOffset > 0 && timeStringSize > 0; | |
BOOL isSignalStrengthBarsOK = gsmSignalStrengthBarsOffset > 0 && gsmSignalStrengthBarsSize == sizeof(int); | |
BOOL isServiceStringOK = serviceStringOffset > 0 && serviceStringSize > 0; | |
if (isTimeStringOK && isSignalStrengthBarsOK && isServiceStringOK) | |
{ | |
const SEL fakeSelector = @selector(fake_rawData); | |
const Method fakeRawData = class_getInstanceMethod(self, fakeSelector); | |
if (class_addMethod(UIStatusBarComposedData, fakeSelector, method_getImplementation(fakeRawData), typeEncoding)) | |
{ | |
method_exchangeImplementations(rawData, class_getInstanceMethod(UIStatusBarComposedData, fakeSelector)); | |
NSLog(@"Using \"%s\" fake carrier", fakeCarrier); | |
return; | |
} | |
} | |
} | |
} | |
} | |
NSLog(@"XCDFakeCarrier failed to initialize"); | |
} | |
- (void *) fake_rawData | |
{ | |
char *rawData = [self fake_rawData]; | |
BOOL isEmptyCarrier = [@(fakeCarrier ?: "") stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]].length == 0; | |
BOOL isEmptyTime = [@(fakeTime ?: "") stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]].length == 0; | |
if (isEmptyCarrier && isEmptyTime) | |
{ | |
bzero(rawData, serviceStringOffset + serviceStringSize); | |
return rawData; | |
} | |
strlcpy(rawData + serviceStringOffset, fakeCarrier, serviceStringSize); | |
if (fakeTime) | |
strlcpy(rawData + timeStringOffset, fakeTime, timeStringSize); | |
int gsmSignalStrengthBars = 5; | |
memcpy(rawData + gsmSignalStrengthBarsOffset, &gsmSignalStrengthBars, gsmSignalStrengthBarsSize); | |
BOOL enableSignalBars = YES; | |
memcpy(rawData + 3, &enableSignalBars, sizeof(enableSignalBars)); | |
BOOL enableBatteryLevelPercentage = YES; | |
memcpy(rawData + 8, &enableBatteryLevelPercentage, sizeof(enableBatteryLevelPercentage)); | |
/* | |
0 center Time | |
1 right Do not Sisturb | |
2 left Airplane | |
3 left Signal Bars | |
4 left Carrier | |
5 left Wi-Fi | |
6 right Time | |
7 right Battery Logo | |
8 right Battery Level Percentage | |
9 right Battery Level Percentage | |
10 right Bluetooth Battery | |
11 right Bluetooth | |
12 right TTY | |
13 right Alarm | |
14 right Plus | |
15 unknown | |
16 right Location Service | |
17 right Rotation Lock | |
18 unknown | |
19 right AirPlay | |
20 right Siri | |
21 left Play | |
22 left VPN | |
23 left Call Forward | |
24 left Network Activity Indicator | |
25 left Black Square | |
*/ | |
return rawData; | |
} | |
@end | |
#endif |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
https://github.com/shinydevelopment/SimulatorStatusMagic