Find ALL cocoa classes that are singleton? - ios

There are several system classes in Cocoa that are singletons, such as UIApplication, NSNotificationCenter. Now, I want to find all classes that are singleton, any suggestion that how could I quickly find them all?
I'm working on a huge codebase, and I need to separate the system singleton object from the customized singleton.

Objective-C runtime hackery! Fun!
Now, before I continue, I will present the disclaimer that I'd never recommend putting anything like this in actual shipping code, and that if you do, it's totally not my fault. This can be fun/interesting to do for educational purposes, though.
This isn't going to be an exact science, since the language itself doesn't have any actual concept of a "singleton". Basically, we're just looking for Objective-C classes that have class methods with certain giveaway prefixes. If we find one of those, there's a good chance that we have a singleton.
With that in mind:
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
static BOOL ClassIsSingleton(Class class) {
unsigned int methodCount = 0;
Method *methods = class_copyMethodList(object_getClass(class), &methodCount);
#try {
for (unsigned int i = 0; i < methodCount; i++) {
Method eachMethod = methods[i];
// only consider class methods with no arguments
if (method_getNumberOfArguments(eachMethod) != 2) {
continue;
}
char *returnType = method_copyReturnType(eachMethod);
#try {
// only consider class methods that return objects
if (strcmp(returnType, #encode(id)) != 0) {
continue;
}
}
#finally {
free(returnType);
}
NSString *name = NSStringFromSelector(method_getName(methods[i]));
// look for class methods with telltale prefixes
if ([name hasPrefix:#"shared"]) {
return YES;
} else if ([name hasPrefix:#"standard"]) {
return YES;
} else if ([name hasPrefix:#"default"]) {
return YES;
} else if ([name hasPrefix:#"main"]) {
return YES;
} // feel free to add any additional prefixes here that I may have neglected
}
}
#finally {
free(methods);
}
return NO;
}
int main(int argc, const char * argv[]) {
#autoreleasepool {
NSMutableArray *singletons = [NSMutableArray new];
int classCount = objc_getClassList(NULL, 0);
Class *classes = (Class *)malloc(classCount * sizeof(Class));
#try {
classCount = objc_getClassList(classes, classCount);
for (int i = 0; i < classCount; i++) {
Class eachClass = classes[i];
if (ClassIsSingleton(eachClass)) {
[singletons addObject:NSStringFromClass(eachClass)];
}
}
}
#finally {
free(classes);
}
NSLog(#"Singletons: %#", singletons);
}
return 0;
}

Related

How would I back a bunch of class properties with a dictionary?

I have a class that holds attributes in a dictionary where the keys are well defined. I would like to replace this attribute dictionary with a class, let's call it AttributeSet. Where there were defined keys:
extern NSString *const Foo;
I would like to have properties:
#interface AttributeSet : NSObject
#property(strong) NSString *Foo;
...a ton more
#end
I would actually like the AttributeSet object to use a dictionary behind the scenes because for backwards compatibility reasons. So when this happens:
attributeSet.Foo = #"bar";
I actually want this to happen:
- (void)setFoo:(NSString *)foo {
self.attributes[Foo] = foo; //Foo is the extern variable Foo
}
but I don't want to have to define getters and setters for all of the properties.
I know that I can use key-value observing but that will 1) require me to have a mapping of (property name) #"Foo" --> (variable name) Foo and 2) result in both the property being set and the dictionary value being set when in reality I just want the dictionary to be set.
I know that I can do something like this: https://github.com/iosptl/ios6ptl/blob/master/ch28/Person/Person/Person.m
but that would 1) still require me to have a mapping and 2) require me to have an #dynamic for every property.
Is there a more automatic way to do this?
Thanks
To use the dynamically-generated accessor approach, as illustrated in the Person code you linked, without requiring #dynamic, you can declare the properties in a category on your class rather than the class itself:
#interface AttributeSet : NSObject
// ... no properties here ...
#end
#interface AttributeSet (YourPropertiesCategoryName)
#property(strong) NSString *Foo;
...a ton more
#end
The compiler will auto-synthesize properties declared in the class itself or in a class extension (which looks like a category with no category name), but not for a category.
Note that you don't need to and shouldn't provide an implementation for the category. (If you do, the compiler will complain about the lack of implementation for the properties. It won't auto-synthesize them, but you'll still need to use #dynamic to silence the warnings.)
After a bit of time, I think I've come up with quite the extensible solution for you. All it requires of you is to simply create your objects using the following helper class, like this:
#import "DictionaryBackedObject.h"
extern NSString *const foo;
NSString *const foo = #"Foo";
#interface Foo : NSObject
#property NSString *foo;
#end
#implementation Foo
#end
int main() {
Foo *object = [DictionaryBackedObject dictionaryBackedObjectOfType:[Foo class]
backingDictionary:#{ foo: #"Bar" }
mutable:NO];
NSLog(#"%#", [object foo]);
}
Note: This implementation is far from perfect, and it does use the 'dreaded' dlsym API, meaning, that you cannot strip your symbols from the executable should you wish to use this class. Also, it may cause rejection should this be submitted to the app store. There are other ways to automatically determine the key to use along with the dictionary, however, should you wish to find a workaround.
This implementation does support struct properties, as well as weak, copy, and atomic ones as well. It will be significantly slower than setting the property on a normal object, as this goes through objective-c's forwarding API (required to support struct returns).
Hopefully this helps you out, I certainly had a lot of fun making it.
DictionaryBackedObject.h
#interface DictionaryBackedObject : NSObject
+(id) dictionaryBackedObjectOfType:(Class) kls backingDictionary:(NSDictionary *) dictionary mutable:(BOOL) isMutable;
#end
DictionaryBackedObject.m
#import "DictionaryBackedObject.h"
#include <stdalign.h>
#include <dlfcn.h>
#import ObjectiveC.runtime;
#import ObjectiveC.message;
__attribute__((noinline))
static SEL property_getGetterSelector(objc_property_t property) {
char *getter = property_copyAttributeValue(property, "G");
if (getter) {
SEL result = sel_registerName(getter);
free(getter);
return result;
}
return sel_registerName(property_getName(property));
}
__attribute__((noinline))
static SEL property_getSetterSelector(objc_property_t property) {
char *setter = property_copyAttributeValue(property, "S");
if (setter) {
SEL result = sel_registerName(setter);
free(setter);
return result;
}
char buffer[512];
char propertyName[512];
strncpy(propertyName, property_getName(property), 512);
propertyName[0] = toupper(propertyName[0]);
snprintf(buffer, 512, "set%s", propertyName);
return sel_registerName(buffer);
}
struct objc_property_attributes_t {
union {
struct {
int nonatomic : 1;
int copy : 1;
int weak : 1;
int strong : 1;
};
int memory_mode;
};
int is_readonly;
int is_dynamic;
};
static inline BOOL property_isAttributeNull(objc_property_t property, const char *attr) {
void *value = property_copyAttributeValue(property, attr);
BOOL results = value == NULL;
free(value);
return results;
}
static struct objc_property_attributes_t property_getPropertyAttributes(objc_property_t property) {
struct objc_property_attributes_t attrs;
attrs.nonatomic = !property_isAttributeNull(property, "N");
attrs.copy = !property_isAttributeNull(property, "C");
attrs.strong = attrs.copy || !property_isAttributeNull(property, "&");
attrs.weak = !property_isAttributeNull(property, "W");
attrs.is_readonly = !property_isAttributeNull(property, "R");
attrs.is_dynamic = !property_isAttributeNull(property, "D");
return attrs;
}
static objc_property_t class_getPropertyForSelector(Class kls, SEL cmd) {
#define VALID_PROPERTY(property) \
(property != NULL && (property_getGetterSelector(property) == cmd || property_getSetterSelector(property) == cmd))
const char *selName = sel_getName(cmd);
objc_property_t results = class_getProperty(kls, selName);
if (VALID_PROPERTY(results))
return results;
if (strstr(selName, "set") == selName) {
char lowercaseSel[512];
strncpy(lowercaseSel, strstr(selName, "set"), 512);
lowercaseSel[0] = tolower(lowercaseSel[0]);
results = class_getProperty(kls, lowercaseSel);
if (VALID_PROPERTY(results)) return results;
}
// Easy paths exhausted, go the 'hard' way of looping over all of the properties available
results = NULL;
unsigned propertyCount = 0;
objc_property_t *properties = class_copyPropertyList(kls, &propertyCount);
for (unsigned propertyIndex = 0; propertyIndex < propertyCount; propertyIndex++) {
if (VALID_PROPERTY(properties[propertyIndex])) {
results = properties[propertyIndex];
break;
}
}
free(properties);
return results;
#undef VALID_PROPERTY
}
#implementation DictionaryBackedObject
-(id) initWithDictionary:(NSDictionary *) dictionary mutable:(BOOL) isMutable {
return nil;
}
+(Class) dictionaryBackedSubclassOfClass:(Class) kls {
#synchronized (kls) {
NSString *className = [NSStringFromClass(kls) stringByAppendingFormat:#"_dictionaryBacked"];
Class subclass = Nil;
if ((subclass = NSClassFromString(className))) {
return subclass;
}
subclass = objc_allocateClassPair(kls, [className UTF8String], 0);
class_addIvar(subclass, "_backingDictionary", sizeof(NSDictionary *), _Alignof(NSDictionary *), #encode(NSDictionary *));
class_addIvar(subclass, "_backingDictionaryIsMutable", sizeof(NSNumber *), _Alignof(NSNumber *), #encode(NSNumber *));
unsigned propertyCount = 0;
objc_property_t *properties = class_copyPropertyList(kls, &propertyCount);
for (unsigned i = 0; i < propertyCount; i++) {
objc_property_t property = properties[i];
char *type = property_copyAttributeValue(property, "T");
SEL getterSel = property_getGetterSelector(property);
SEL setterSel = property_getSetterSelector(property);
char getterTypeBuffer[512];
snprintf(getterTypeBuffer, 512, "%s#:", type);
char setterTypeBuffer[512];
snprintf(setterTypeBuffer, 512, "v#:%s", type);
NSUInteger typeSize;
NSUInteger typeAlignment;
NSGetSizeAndAlignment(type, &typeSize, &typeAlignment);
BOOL isStret = (typeSize * CHAR_BIT) > (WORD_BIT * 2);
class_addMethod(subclass, getterSel, isStret ? _objc_msgForward_stret : _objc_msgForward , getterTypeBuffer);
class_addMethod(subclass, setterSel, _objc_msgForward, setterTypeBuffer);
free(type);
}
free(properties);
Ivar backingDictionaryIvar = class_getInstanceVariable(subclass, "_backingDictionary");
Ivar backingDictionaryMutableIvar = class_getInstanceVariable(subclass, "_backingDictionaryIsMutable");
class_addMethod(subclass, #selector(forwardingTargetForSelector:), imp_implementationWithBlock(^id (id self) {
return nil;
}), "##:");
class_addMethod(subclass, #selector(forwardInvocation:), imp_implementationWithBlock(^void (id self, NSInvocation *invocation) {
SEL _cmd = [invocation selector];
objc_property_t property = class_getPropertyForSelector([self class], _cmd);
if (property == NULL) {
[self doesNotRecognizeSelector:_cmd];
return;
}
BOOL isGetter = (_cmd == property_getGetterSelector(property));
struct objc_property_attributes_t attributes = property_getPropertyAttributes(property);
NSString *propertyType = (__bridge_transfer NSString *) CFStringCreateWithCStringNoCopy(
NULL, property_copyAttributeValue(property, "T"), kCFStringEncodingUTF8, NULL
);
NSUInteger propertySize;
NSGetSizeAndAlignment([propertyType UTF8String], &propertySize, NULL);
void *dlsymKey = dlsym(RTLD_MAIN_ONLY, property_getName(property));
id dictionaryKey = *(__unsafe_unretained id *) dlsymKey;
NSMutableDictionary *backingDictionary = object_getIvar(self, backingDictionaryIvar);
NSNumber *isMutable = object_getIvar(self, backingDictionaryMutableIvar);
// Performing synchronization on nil is a no-op, see objc_sync.mm:306.
#synchronized (attributes.nonatomic ? nil : self) {
if (isGetter) {
id value = backingDictionary[dictionaryKey];
if (attributes.strong) {
[invocation setReturnValue:&value];
} else if (attributes.weak) {
value = [value nonretainedObjectValue];
[invocation setReturnValue:&value];
} else {
void *buffer = alloca(propertySize);
[value getValue:buffer];
[invocation setReturnValue:buffer];
}
} else {
if ((attributes.is_readonly || ![isMutable boolValue])) {
[self doesNotRecognizeSelector:_cmd];
return;
}
id dictionaryValue = nil;
void *newValue = alloca(propertySize);
[invocation getArgument:newValue atIndex:2];
if (attributes.strong) {
dictionaryValue = (__bridge id) newValue;
if (attributes.copy) {
dictionaryValue = [dictionaryValue copy];
}
} else if (attributes.weak) {
dictionaryValue = [NSValue valueWithNonretainedObject:(__bridge id) newValue];
} else {
dictionaryValue = [NSValue valueWithBytes:newValue objCType:[propertyType UTF8String]];
}
if (dictionaryValue == nil) {
[backingDictionary removeObjectForKey:dictionaryKey];
} else {
[backingDictionary setObject:dictionaryValue forKey:dictionaryKey];
}
}
}
}), "v#:#");
class_addMethod(subclass, #selector(initWithDictionary:mutable:), imp_implementationWithBlock(^id (id self, NSDictionary *dictionary, BOOL mutable) {
object_setIvar(self, backingDictionaryIvar, dictionary);
object_setIvar(self, backingDictionaryMutableIvar, #(mutable));
return self;
}), "##:#c");
objc_registerClassPair(subclass);
return subclass;
}
}
+(id) dictionaryBackedObjectOfType:(Class)kls backingDictionary:(NSDictionary *)dictionary mutable:(BOOL)isMutable {
Class subclass = [self dictionaryBackedSubclassOfClass:kls];
return [[subclass alloc] initWithDictionary:dictionary mutable:isMutable];
}
#end
Rob Napier's example is a good option; the compiler's going to generate accessors for you unless you tell it not to, and the way you tell it that is with the #dynamic directive.
Another option would be automated code generation: write a script to emit ObjC code for your setters.
The third that I can think of is overwriting the accessors during runtime. In your class's +initialize, you can get the list of its properties from the runtime library and use class_replaceMethod() to insert your own accessors that use your dictionary instead of the ivars. This will require some string mangling to get the accessor names and keys from each other.
Here's a gist with a demo of that last option: https://gist.github.com/woolsweater/4fb874b15449ee7fd7e8

NSString containsString crashes

I'm trying to filter an array according to one of it's string fields.
Both nameLower and filterLower has NSString value inside, and yet i keep getting:
__NSCFString containsString:]: unrecognized selector sent to instance 0x7f876b79e160
-(void) filterFriendsArray:(NSString*)filter {
[_filteredFriendsArray removeAllObjects];
for (FacebookUser* user in _friendsArray)
{
NSString* nameLower = [user.user.name lowercaseString];
NSString* filterLower = [filter lowercaseString];
if ([nameLower containsString:filterLower])
[_filteredFriendsArray addObject:user];
}
_displayedFriendsArray = _filteredFriendsArray;
}
If you want your code to work on iOS 7 as well as iOS 8 you should use one of the rangeOfString calls instead. Basically if the range returned has a length of zero, the substring is not there.
/* These methods return length==0 if the target string is not found. So, to check for containment: ([str rangeOfString:#"target"].length > 0). Note that the length of the range returned by these methods might be different than the length of the target string, due composed characters and such.
*/
- (NSRange)rangeOfString:(NSString *)aString;
- (NSRange)rangeOfString:(NSString *)aString options:(NSStringCompareOptions)mask;
- (NSRange)rangeOfString:(NSString *)aString options:(NSStringCompareOptions)mask range:(NSRange)searchRange;
- (NSRange)rangeOfString:(NSString *)aString options:(NSStringCompareOptions)mask range:(NSRange)searchRange locale:(NSLocale *)locale NS_AVAILABLE(10_5, 2_0);
Obviously it's trivial to implement containsString yourself in a category using rangeOfString:
#implementation NSString (Contains)
- (BOOL)myContainsString:(NSString*)other {
NSRange range = [self rangeOfString:other];
return range.length != 0;
}
#end
compare rangeOfString with NSNotFound
NSRange range = [self rangeOfString:other];
if(range.location != NSNotFound){
//do something
}
Use following:
if (![[NSString class] respondsToSelector:#selector(containsString)])
{
//ios 7
NSRange range = [mystring rangeOfString:other];
if(range.location != NSNotFound){
//do something
}
}
else //for iOS 8
{
if ([mystring containsString: other])
{
//do something
}
}
For those who encountered this in XLForm, make sure when you install XLForm using pods
platform :ios, '7'
pod 'XLForm'
It is already fixed in 3.1
from
if ([cellClassString contains:#"/"]) {
}
to
if ([cellClassString rangeOfString:#"/"].location != NSNotFound) {
}
I encapsulate my solution in YJKit, and you can call -[NSString containsString:] even for old version which below iOS 8.
bool _yj_streq(const char *str1, const char *str2, size_t length) {
for (int i = 0; i < length; i++) {
if (*str1++ != *str2++) {
return false;
}
}
return true;
}
- (BOOL)yj_containsString:(NSString *)string {
NSAssert(string != nil, #"*** -[%# containsString:] can not use nil argument.", [self class]);
size_t len1 = (size_t)self.length;
size_t len2 = (size_t)string.length;
if (len1 == 0 || len2 == 0 || len1 < len2) {
return NO;
}
const char *str1 = self.UTF8String;
const char *str2 = string.UTF8String;
for (size_t i = 0; i <= len1 - len2; i++) {
const char *substr1 = str1 + i;
if (_yj_streq(substr1, str2, len2)) {
return YES;
} else {
continue;
}
}
return NO;
}
Here is my source code:
https://github.com/huang-kun/YJKit/blob/master/YJKit/Base/Foundation/Categories/Generics/NSString%2BYJCompatible.m
Swift version of the answer given by w0mbat:
extension NSString {
func compatibleContainsString(string: NSString) -> Bool{
let range = self.rangeOfString(string as String)
return range.length != 0
}
}

IOS How record midi file with Midi input callback?

I try to record midi file with an Ipad.
My Ipad is pluged with the usb output of my electric piano.
I have read the apple core midi documentation and I have understand that :
For record a file, I should create a MusicSequence. So that I try to do but It doesn't work :(
Here is my code:
Firstly, I setup my midi connection:
-(void) setupMIDI {
MIDIClientRef client = nil;
MIDIClientCreate(CFSTR("Core MIDI to System Sounds Demo"), MyMIDINotifyProc, (__bridge void *)(self), &client);
inputPort = nil;
MIDIInputPortCreate(client, CFSTR("Input port"), MyMIDIReadProc, (__bridge void *)(self), &inputPort);
sequence = nil;
NewMusicSequence(&(sequence));
unsigned long sourceCount = MIDIGetNumberOfSources();
[self appendToTextView:[NSString stringWithFormat:#"%ld sources\n", sourceCount]];
for (int i = 0; i < sourceCount; ++i) {
MIDIEndpointRef src = MIDIGetSource(i);
CFStringRef endpointName = NULL;
OSStatus nameErr = MIDIObjectGetStringProperty(src, kMIDIPropertyName, &endpointName);
if (noErr == nameErr) {
[self appendToTextView: [NSString stringWithFormat:#" source %d: %#\n", i, endpointName]];
}
MIDIPortConnectSource(inputPort, src, NULL);
MusicSequenceSetMIDIEndpoint(sequence, src);
}
}
After that, I receive my Midi event with MyMIDIReadProc which is a callback function of my input port :
static void MyMIDIReadProc(const MIDIPacketList *pktlist, void *refCon, void *connRefCon)
{
AppViewController *vc = (__bridge AppViewController*) refCon;
MIDIPacket *packet = (MIDIPacket *)pktlist->packet;
for (int i=0; i < pktlist->numPackets; i++) {
Byte midiStatus = packet->data[0];
Byte midiCommand = midiStatus >> 4;
// is it a note-on or note-off
if ((midiCommand == 0x09) ||
(midiCommand == 0x08)) {
Byte note = packet->data[1] & 0x7F;
Byte velocity = packet->data[2] & 0x7F;
NSLog(#"midiCommand=%d. Note=%d, Velocity=%d\n", midiCommand, note, velocity);
MIDINoteMessage noteMessage;
noteMessage.releaseVelocity = 0;
noteMessage.velocity = velocity;
noteMessage.note = note;
MusicTrackNewMIDINoteEvent(vc->musicTrack, packet->timeStamp, &noteMessage);
packet = MIDIPacketNext(packet);
}
}
I try to transform MIDIPklist on MIDINoteMessage to add it on my track.
When I have finished that, I create the file with this function :
-(void) createMidiFile
{
// init sequence
NewMusicSequence(&sequence);
CFURLRef pathUrl = (__bridge CFURLRef)[NSURL fileURLWithPath:self.path];
//set track to sequence
MusicSequenceNewTrack(sequence, &musicTrack);
// write sequence in file
MusicSequenceFileCreate(sequence,
pathUrl,
kMusicSequenceFile_MIDIType,
kMusicSequenceFileFlags_EraseFile,
0);
}
The file has been created but the data aren't correct. It have every time the same size.
Thanks if you can help me to debug that ! I don't understand what I have to do to fill the track and sequence object for create a good mid file...
Sorry for my english guys.. :)
I'm trying to solve same problem. From what I can see - the MIDINoteMessage needs to have a duration which corresponds to the delta of note on and subsequent note off call. You have to keep track of this.
The callback should be performed on main thread and you need to be using CACurrentMediaTime to stash the times midi timestamps prior to dumping out the midi file. Some of the code below.
The other alternative approach was sourced from apple forums
"Create a MusicSequence, add a MusicTrack to it, add some midi events to the track via MusicTrackNewMidiNoteEvent, set that MusicSequence on a newly created MusicPlayer, and start the player. Now that you have that player playing you can query it for the current time in beats via the MusicPlayerGetTime function. Set that time for the MusicTimeStamp for midi messages you send to MusicTrackNewMidiNoteEvent.
**Important Note - You MUST populate the MusicTrack for the MusicPlayer that you are querying for the time stamp, or it won't play! You'll get an error (probably (-50) depending on if you set everything else up correctly). I did this with a loop, adding a message with a time stamp starting at zero, going up to four. I would guess that you don't even have to go that high, but the Track has to have something for the player to play. Don't worry about it running off the end since all we want It for is the MusicTimeStamp. The MusicPlayer will continue playing until you tell it to stop."
This code is the closest I've come to answer - although this is targeting IOS.
https://github.com/benweitzman/ReTune/blob/ee47009999298c2b03527302c3fb6d7be17b10e2/Return4/ViewController.m
#interface NoteObject : NSObject
#property (nonatomic) int time;
#property (nonatomic) int note;
#property (nonatomic) int velocity;
#property (nonatomic) bool noteOn;
#end
- (void) midiSource:(PGMidiSource*)midi midiReceived:(const MIDIPacketList *)packetList
{
[self performSelectorOnMainThread:#selector(addString:)
withObject:#"MIDI received:"
waitUntilDone:NO];
const MIDIPacket *packet = &packetList->packet[0];
for (int i = 0; i < packetList->numPackets; ++i)
{
//[self performSelectorOnMainThread:#selector(addString:)
// withObject:[self StringFromPacket:packet]
// waitUntilDone:NO];
if (packet->length == 3) {
if ((packet->data[0]&0xF0) == 0x90) {
if (packet->data[2] != 0) {
[self noteOn:packet->data[1] withVelocity:packet->data[2]];
} else {
[self noteOff:packet->data[1]];
}
} else if ((packet->data[0]&0xF0) == 0x80) {
[self noteOff:packet->data[1]];
}
}
packet = MIDIPacketNext(packet);
}
}
- (void) noteOff:(int)noteValue {
//NSLog(#"off");
if (noteValue>=0 && noteValue<127) {
ALSource * source = [sources objectAtIndex:noteValue];
ALSource *loopSource = [loopSources objectAtIndex:noteValue];
if (source.playing || loopSource.playing) {
[[fadingOut objectAtIndex:noteValue] release];
[fadingOut replaceObjectAtIndex:noteValue withObject:[[NSNumber alloc] initWithBool:YES]];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
float timeDone = 0;
float duration = 0.2;
float timeStep = 0.01;
float valStep = source.gain*timeStep/duration;
float loopStep = loopSource.gain*timeStep/duration;
while (timeDone < duration) {
if (![[fadingOut objectAtIndex:noteValue] boolValue]) break;
source.gain -= valStep;
loopSource.gain -= loopStep;
[NSThread sleepForTimeInterval:timeStep];
timeDone += timeStep;
}
if ([[fadingOut objectAtIndex:noteValue] boolValue]) {
[source stop];
[loopSource stop];
}
//source.gain = 1;
});
if (recording) {
double currentTime = CACurrentMediaTime();
int deltaTime = (int)(currentTime*1000-recordTimer*1000);
NoteObject * recordedNote = [[NoteObject alloc] init];
recordedNote.note = noteValue;
recordedNote.time = deltaTime;
recordedNote.noteOn = false;
[recordedNotes addObject:recordedNote];
recordTimer = currentTime;
}
}
}
}
- (void) finishFadeIn:(ALSource*)source {
}
- (void) noteOn:(int)noteValue withVelocity:(int)velocity {
if (noteValue>=0 && noteValue<127) {
if (recording) {
double currentTime = CACurrentMediaTime();
int deltaTime = (int)(currentTime*1000-recordTimer*1000);
NoteObject * recordedNote = [[NoteObject alloc] init];
recordedNote.note = noteValue;
recordedNote.time = deltaTime;
recordedNote.noteOn = true;
[recordedNotes addObject:recordedNote];
recordTimer = currentTime;
}
while(loadingScale || changingPitch);
float pitchToPlay = [[ratios objectAtIndex:noteValue] floatValue];
[[fadingOut objectAtIndex:noteValue] release];
[fadingOut replaceObjectAtIndex:noteValue withObject:[[NSNumber alloc] initWithBool:NO]];
ALSource * source = [sources objectAtIndex:noteValue];
[source stop];
source.gain = velocity/127.0f;
source.pitch = pitchToPlay;
[source play:[buffers objectAtIndex:noteValue]];
if ([loopBuffers objectAtIndex:noteValue] != (id)[NSNull null]) {
ALSource *loopSource = [loopSources objectAtIndex:noteValue];
[loopSource stop];
loopSource.gain = 0;
loopSource.pitch = source.pitch;
[loopSource play:[loopBuffers objectAtIndex:noteValue] loop:YES];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
float timeDone = 0;
float duration = [(ALBuffer*)[buffers objectAtIndex:noteValue] duration]-.4;
float timeStep = 0.01;
float valStep = source.gain*timeStep/duration;
float loopStep = valStep;
while (timeDone < duration) {
if ([[fadingOut objectAtIndex:noteValue] boolValue]) break;
source.gain -= valStep;
loopSource.gain += loopStep;
[NSThread sleepForTimeInterval:timeStep];
timeDone += timeStep;
}
/*if ([[fadingOut objectAtIndex:noteValue] boolValue]) {
[source stop];
[loopSource stop];
}*/
//source.gain = 1;
});
}
/*
[source play];*/
//[[sources objectAtIndex:noteValue] play:toPlay gain:velocity/127.0f pitch:pitchToPlay pan:0.0f loop:FALSE];
}
}

Is there api for get infor about network usage of an app?

i want to create an app that show all app installed on the device and info about network usage of that app. Is there api for get infor about network usage?
Your own app can find out how much data the device as a how as used by using the code below, but there's no way of knowing how much each particular app has used. Apps like My Data Manager used to give you a breakdown per app but a) that wasn't totally accurate and b) it now longer does this now with iOS 7. There's another app that sends the data through VPN to its server and in the screenshots on the app store makes it look like it can give a breakdown per app, but that's spin but in reality it just can't except for a few and with some interaction from the user. Basically there is no guaranteed accurate way of doing it per every app.
Also there is no guaranteed accurate way of knowing all installed apps. There are some mechanisms to do a good guess of what is installed based against a database but no way of knowing definitely what is installed
#include <arpa/inet.h>
#include <net/if.h>
#include <ifaddrs.h>
#include <net/if_dl.h>
- (void) getDataUsage
{
NSTimeInterval elapsedTime = [[NSDate date] timeIntervalSinceDate: self.timeThatDataMonitoringStarted];
NSLog(#"********* GETTING DATA USAGE. Elapsed time: %f **************",elapsedTime);
NSArray *data = [self getDataCounters];
NSNumber *wifiSentSinceBoot = (NSNumber*)data[0];
NSNumber *wifiReceivedSinceBoot = (NSNumber*)data[1];
NSNumber *wwanSentSinceBoot = (NSNumber*)data[2];
NSNumber *wwanReceivedSinceBoot = (NSNumber*)data[3];
int wifiSentSinceBootAsInt = [wifiSentSinceBoot intValue];
int wifiReceivedSinceBootAsInt = [wifiReceivedSinceBoot intValue];
int wWanSentSinceBootAsInt = [wwanSentSinceBoot intValue];
int wWanReceivedSinceBootAsInt = [wwanReceivedSinceBoot intValue];
static int initialWifiSent;
static int initialWifiReceived;
static int initialWWanSent;
static int initialWWanReceived;
if (!self.initialDataValuesSet)
{
self.initialDataValuesSet = YES;
initialWifiSent = wifiSentSinceBootAsInt;
initialWifiReceived = wifiReceivedSinceBootAsInt;
initialWWanSent = wWanSentSinceBootAsInt;
initialWWanReceived = wWanReceivedSinceBootAsInt;
}
int wifiSentSinceLastRetrieval = wifiSentSinceBootAsInt - initialWifiSent;
int wifiReceivedSinceLastRetrieval = wifiReceivedSinceBootAsInt - initialWifiReceived;
int wWanSentSinceLastRetrieval = wWanSentSinceBootAsInt - initialWWanSent;
int wWanReceivedSinceLastRetrieval = wWanReceivedSinceBootAsInt - initialWWanReceived;
uint dataUsed = wifiSentSinceLastRetrieval + wifiReceivedSinceLastRetrieval + wWanSentSinceLastRetrieval + wWanReceivedSinceLastRetrieval;
NSLog(#"Total data: %d", dataUsed);
}
- (NSArray *) getDataCounters
{
BOOL success;
struct ifaddrs *addrs;
const struct ifaddrs *cursor;
const struct if_data *networkStatisc;
int WiFiSent = 0;
int WiFiReceived = 0;
int WWANSent = 0;
int WWANReceived = 0;
NSString *name=[[NSString alloc]init];
success = getifaddrs(&addrs) == 0;
if (success)
{
cursor = addrs;
while (cursor != NULL)
{
name=[NSString stringWithFormat:#"%s",cursor->ifa_name];
// NSLog(#"ifa_name %s == %#\n", cursor->ifa_name,name);
// names of interfaces: en0 is WiFi ,pdp_ip0 is WWAN
if (cursor->ifa_addr->sa_family == AF_LINK)
{
if ([name hasPrefix:#"en"])
{
networkStatisc = (const struct if_data *) cursor->ifa_data;
WiFiSent+=networkStatisc->ifi_obytes;
WiFiReceived+=networkStatisc->ifi_ibytes;
// NSLog(#"WiFiSent %d ==%d",WiFiSent,networkStatisc->ifi_obytes);
// NSLog(#"WiFiReceived %d ==%d",WiFiReceived,networkStatisc->ifi_ibytes);
}
if ([name hasPrefix:#"pdp_ip"])
{
networkStatisc = (const struct if_data *) cursor->ifa_data;
WWANSent+=networkStatisc->ifi_obytes;
WWANReceived+=networkStatisc->ifi_ibytes;
// NSLog(#"WWANSent %d ==%d",WWANSent,networkStatisc->ifi_obytes);
// NSLog(#"WWANReceived %d ==%d",WWANReceived,networkStatisc->ifi_ibytes);
}
}
cursor = cursor->ifa_next;
}
freeifaddrs(addrs);
}
return [NSArray arrayWithObjects:[NSNumber numberWithInt:WiFiSent], [NSNumber numberWithInt:WiFiReceived],[NSNumber numberWithInt:WWANSent],[NSNumber numberWithInt:WWANReceived], nil];
}

How to get my IP address programmatically on iOS/macOS?

I would like to obtain my iPad's IP address programmatically.
How can I query the networking subsystem to find out what my IPv4 (and IPv6) addresses are?
PS: Can I disable IPv6 somehow?
The following code finds all IPv4 and IPv6 addresses on an iOS or OSX device. The first getIPAddress method acts more or less as the older code in this answer: you can prefer either one or the other type address, and it always prefers WIFI over cellular (obviously you could change this).
More interestingly it can return a dictionary of all addresses found, skipping addresses for not up interfaces, or addresses associated with loopback. The previous code as well as other solutions on this topic will not properly decode IPv6 (inet_ntoa cannot deal with them). This was pointed out to me by Jens Alfke on an Apple forum - the proper function to use is inet_ntop (look at the man page, and or refer to this inet_ntop article also provided by Jens.
The dictionary keys have the form "interface" "/" "ipv4 or ipv6".
#include <ifaddrs.h>
#include <arpa/inet.h>
#include <net/if.h>
#define IOS_CELLULAR #"pdp_ip0"
#define IOS_WIFI #"en0"
//#define IOS_VPN #"utun0"
#define IP_ADDR_IPv4 #"ipv4"
#define IP_ADDR_IPv6 #"ipv6"
- (NSString *)getIPAddress:(BOOL)preferIPv4
{
NSArray *searchArray = preferIPv4 ?
#[ /*IOS_VPN #"/" IP_ADDR_IPv4, IOS_VPN #"/" IP_ADDR_IPv6,*/ IOS_WIFI #"/" IP_ADDR_IPv4, IOS_WIFI #"/" IP_ADDR_IPv6, IOS_CELLULAR #"/" IP_ADDR_IPv4, IOS_CELLULAR #"/" IP_ADDR_IPv6 ] :
#[ /*IOS_VPN #"/" IP_ADDR_IPv6, IOS_VPN #"/" IP_ADDR_IPv4,*/ IOS_WIFI #"/" IP_ADDR_IPv6, IOS_WIFI #"/" IP_ADDR_IPv4, IOS_CELLULAR #"/" IP_ADDR_IPv6, IOS_CELLULAR #"/" IP_ADDR_IPv4 ] ;
NSDictionary *addresses = [self getIPAddresses];
NSLog(#"addresses: %#", addresses);
__block NSString *address;
[searchArray enumerateObjectsUsingBlock:^(NSString *key, NSUInteger idx, BOOL *stop)
{
address = addresses[key];
if(address) *stop = YES;
} ];
return address ? address : #"0.0.0.0";
}
- (NSDictionary *)getIPAddresses
{
NSMutableDictionary *addresses = [NSMutableDictionary dictionaryWithCapacity:8];
// retrieve the current interfaces - returns 0 on success
struct ifaddrs *interfaces;
if(!getifaddrs(&interfaces)) {
// Loop through linked list of interfaces
struct ifaddrs *interface;
for(interface=interfaces; interface; interface=interface->ifa_next) {
if(!(interface->ifa_flags & IFF_UP) /* || (interface->ifa_flags & IFF_LOOPBACK) */ ) {
continue; // deeply nested code harder to read
}
const struct sockaddr_in *addr = (const struct sockaddr_in*)interface->ifa_addr;
char addrBuf[ MAX(INET_ADDRSTRLEN, INET6_ADDRSTRLEN) ];
if(addr && (addr->sin_family==AF_INET || addr->sin_family==AF_INET6)) {
NSString *name = [NSString stringWithUTF8String:interface->ifa_name];
NSString *type;
if(addr->sin_family == AF_INET) {
if(inet_ntop(AF_INET, &addr->sin_addr, addrBuf, INET_ADDRSTRLEN)) {
type = IP_ADDR_IPv4;
}
} else {
const struct sockaddr_in6 *addr6 = (const struct sockaddr_in6*)interface->ifa_addr;
if(inet_ntop(AF_INET6, &addr6->sin6_addr, addrBuf, INET6_ADDRSTRLEN)) {
type = IP_ADDR_IPv6;
}
}
if(type) {
NSString *key = [NSString stringWithFormat:#"%#/%#", name, type];
addresses[key] = [NSString stringWithUTF8String:addrBuf];
}
}
}
// Free memory
freeifaddrs(interfaces);
}
return [addresses count] ? addresses : nil;
}
EDIT1: Code updated on May 16, 2014 (bug pointed out by lhunath, see comments). Loopback addresses now returned, but its easy for you to uncomment the test to exclude them yourself.
EDIT2: (by some unknown person): Improved further March 13, 2015: In case the user uses a VPN (regardless over WiFi or Cellular), the previous code would have failed. Now, it works even with VPN connections. VPN connections are given precedence over WiFi and Cell because that's how the device handles it. This should even work for Macs as the VPN connection on a Mac is also using IF utun0 but not tested.
EDIT3: (9/8/2016) Given the problems experienced by #Qiulang (see comments) with the VPN code (which someone else added), I've commented it out. If anyone knows definitively how to specify a user VPN please chime in with a comment.
In your implementation file .m ,
#import <ifaddrs.h>
#import <arpa/inet.h>
// Get IP Address
- (NSString *)getIPAddress {
NSString *address = #"error";
struct ifaddrs *interfaces = NULL;
struct ifaddrs *temp_addr = NULL;
int success = 0;
// retrieve the current interfaces - returns 0 on success
success = getifaddrs(&interfaces);
if (success == 0) {
// Loop through linked list of interfaces
temp_addr = interfaces;
while(temp_addr != NULL) {
if(temp_addr->ifa_addr->sa_family == AF_INET) {
// Check if interface is en0 which is the wifi connection on the iPhone
if([[NSString stringWithUTF8String:temp_addr->ifa_name] isEqualToString:#"en0"]) {
// Get NSString from C String
address = [NSString stringWithUTF8String:inet_ntoa(((struct sockaddr_in *)temp_addr->ifa_addr)->sin_addr)];
}
}
temp_addr = temp_addr->ifa_next;
}
}
// Free memory
freeifaddrs(interfaces);
return address;
}
Many existing solutions only consider wireless interfaces, which won't work for wired connections via an Ethernet adapter (ie. no Wifi or 3G); see this more recent solution which considers IP addresses obtained through wired interfaces as well.
iPad: How to get IP address programmatically WIRED (not via wireless)
Get IP address using Swift 3:
func getIPAddress() -> String {
var address: String = "error"
var interfaces: ifaddrs? = nil
var temp_addr: ifaddrs? = nil
var success: Int = 0
// retrieve the current interfaces - returns 0 on success
success = getifaddrs(interfaces)
if success == 0 {
// Loop through linked list of interfaces
temp_addr = interfaces
while temp_addr != nil {
if temp_addr?.ifa_addr?.sa_family == AF_INET {
// Check if interface is en0 which is the wifi connection on the iPhone
if (String(utf8String: temp_addr?.ifa_name) == "en0") {
// Get NSString from C String
address = String(utf8String: inet_ntoa((temp_addr?.ifa_addr as? sockaddr_in)?.sin_addr))
}
}
temp_addr = temp_addr?.ifa_next
}
}
// Free memory
freeifaddrs(interfaces)
return address
}
#DavidH's answer works fine till I got this result from some 4G cellular network:
{
"lo0/ipv4" = "127.0.0.1";
"lo0/ipv6" = "fe80::1";
"pdp_ip0/ipv4" = "10.132.76.168";
"utun0/ipv6" = "fe80::72c3:e25e:da85:b730";
}
I am not using vpn so I have no idea why I had a utun0/ipv6.
--- Updated ---
I further debug this issue and found that I can get an fake vpn address even in other 4G networks (is this iOS bug??),
{
""awdl0/ipv6"" = ""fe80::c018:9fff:feb2:988"";
""en0/ipv6"" = ""fe80::181a:2e43:f91b:db2b"";
""lo0/ipv4"" = ""127.0.0.1"";
""lo0/ipv6"" = ""fe80::1"";
""pdp_ip0/ipv4"" = ""10.48.10.210"";
""utun0/ipv4"" = ""192.168.99.2"";
}
If I did use vpn I will get this:
{
"lo0/ipv4" = "127.0.0.1";
"lo0/ipv6" = "fe80::1";
"pdp_ip0/ipv4" = "10.49.187.23";
"utun0/ipv6" = "fe80::5748:5b5d:2bf0:658d";
"utun1/ipv4" = "192.168.99.2"; //the real one
}
So it is utun1 NOT utun0
Without figuring out why I will just have to drop vpn check :(
---- update ----
I raised a bug (28131847) to apple and replied with "Not all utun interfaces are for VPN. There are other OS features that use utun interfaces."
But when I asked how to get a valid vpn IP address then, their answer was rather disappointed, "You can go into Settings -> VPN and look at your VPN configuration to see if the VPN is active. In some cases you can see the assigned IP address there as well. We are now closing this bug report." :(
---- update 2016/11/04 ----
I hit the problem again and I need to further modify #DavidH's answer to fix it:
I was in 4G network and I got this address:
addresses: {
"awdl0/ipv6" = "fe80::98fd:e6ff:fea9:3afd";
"en0/ipv6" = "fe80::8dd:7d92:4159:170e";
"lo0/ipv4" = "127.0.0.1";
"lo0/ipv6" = "fe80::1";
"pdp_ip0/ipv4" = "10.37.212.102";
"utun0/ipv6" = "fe80::279c:ea56:a2ef:d128";
}
With his original answer I will get the wifi IP fe80::8dd:7d92:4159:170e, which was fake and connection failed.
So I modified the code to like,
[searchArray enumerateObjectsUsingBlock:^(NSString *key, NSUInteger idx, BOOL *stop)
{
if ((internetReach.isReachableViaWiFi && [key hasPrefix:IOS_WIFI]) ||
(internetReach.isReachableViaWWAN && [key hasPrefix:IOS_CELLULAR])) {
address = addresses[key];
if(address) *stop = YES;
}
} ];
The current solution doesn't return the en0 device on OS X, the following code uses the System Configuration Framework to get the interfaces then uses standard C functions to get the IP address.
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <sys/ioctl.h>
#include <net/if.h>
#define IFT_ETHER 0x6
#include <SystemConfiguration/SCDynamicStore.h>
+(void)getInterfaces
{
SCDynamicStoreRef storeRef = SCDynamicStoreCreate(NULL, (CFStringRef)#"FindCurrentInterfaceIpMac", NULL, NULL);
CFPropertyListRef global = SCDynamicStoreCopyValue (storeRef,CFSTR("State:/Network/Interface"));
id primaryInterface = [(__bridge NSDictionary *)global valueForKey:#"Interfaces"];
for (NSString* item in primaryInterface)
{
if(get_iface_address([item UTF8String]))
{
NSString *ip = [NSString stringWithUTF8String:get_iface_address([item UTF8String])];
NSLog(#"interface: %# - %#",item,ip);
} else
NSLog(#"interface: %#",item);
}
}
static char * get_iface_address (char *interface)
{
int sock;
uint32_t ip;
struct ifreq ifr;
char *val;
if (!interface)
return NULL;
/* determine UDN according to MAC address */
sock = socket (AF_INET, SOCK_STREAM, 0);
if (sock < 0)
{
perror ("socket");
return NULL;
}
strcpy (ifr.ifr_name, interface);
ifr.ifr_addr.sa_family = AF_INET;
if (ioctl (sock, SIOCGIFADDR, &ifr) < 0)
{
perror ("ioctl");
close (sock);
return NULL;
}
val = (char *) malloc (16 * sizeof (char));
ip = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr.s_addr;
ip = ntohl (ip);
sprintf (val, "%d.%d.%d.%d",
(ip >> 24) & 0xFF, (ip >> 16) & 0xFF, (ip >> 8) & 0xFF, ip & 0xFF);
close (sock);
return val;
}
This answer was inspired by #DavidH's answer. I fixed some issues, replaced inet_ntop with getnameinfo which allows a cleaner approach. Note that this yields a dictionary that maps an interface name to an array of IP addresses (an interface can have multiple IPv4 and IPv6's associated with it, technically). It does not distinguish between IPv4 and IPv6:
// Get all our interface addresses.
struct ifaddrs *ifAddresses;
if (getifaddrs( &ifAddresses ) != 0) {
NSLog( #"Couldn't get interface addresses: %d", errno );
return nil;
}
int error;
char host[MAX( INET_ADDRSTRLEN, INET6_ADDRSTRLEN )];
_ipAddressesByInterface = [NSMutableDictionary dictionaryWithCapacity:8];
for (struct ifaddrs *ifAddress = ifAddresses; ifAddress; ifAddress = ifAddress->ifa_next) {
if (!(ifAddress->ifa_flags & IFF_UP) || (ifAddress->ifa_flags & IFF_LOOPBACK))
// Ignore interfaces that aren't up and loopback interfaces.
continue;
if (ifAddress->ifa_addr->sa_family != AF_INET && ifAddress->ifa_addr->sa_family != AF_INET6)
// Ignore non-internet addresses.
continue;
if ((error = getnameinfo( ifAddress->ifa_addr, ifAddress->ifa_addr->sa_len, host, sizeof( host ), NULL, 0, NI_NUMERICHOST )) != noErr) {
// Couldn't to format host name for this address.
NSLog( #"Couldn't resolve host name for address: %s", gai_strerror( error ) );
continue;
}
NSString *ifName = [NSString stringWithCString:ifAddress->ifa_name encoding: NSUTF8StringEncoding];
NSMutableArray *ifIpAddresses = _ipAddressesByInterface[ifName];
if (!ifIpAddresses)
ifIpAddresses = _ipAddressesByInterface[ifName] = [NSMutableArray arrayWithCapacity:2];
[ifIpAddresses addObject:[NSString stringWithCString:host encoding: NSUTF8StringEncoding]];
}
freeifaddrs( ifAddresses );
return _ipAddressesByInterface;
Great solution for swift in This file which serves all the details.
In One of my app I need to fetch wifi IP address. I have used answers above, in swift 3 like this:
let WIFI_IF = "en0"
let UNKNOWN_IP_ADDRESS = ""
var addresses: [AnyHashable: Any] = ["wireless": UNKNOWN_IP_ADDRESS, "wired": UNKNOWN_IP_ADDRESS, "cell": UNKNOWN_IP_ADDRESS]
var interfaces: UnsafeMutablePointer<ifaddrs>? = nil
var temp_addr: UnsafeMutablePointer<ifaddrs>? = nil
var success: Int = 0
success = Int(getifaddrs(&interfaces))
if success == 0 {
temp_addr = interfaces
while temp_addr != nil {
if temp_addr?.pointee.ifa_addr == nil {
continue
}
if temp_addr?.pointee.ifa_addr.pointee.sa_family == UInt8(AF_INET) {
if (String(utf8String: (temp_addr?.pointee.ifa_name)!) == WIFI_IF) {
addresses["wireless"] = String(utf8String: inet_ntoa(((temp_addr?.pointee.ifa_addr as? sockaddr_in)?.sin_addr)!))
}
}
temp_addr = temp_addr?.pointee.ifa_next
}
}
In this code, It crashes because I have to check for nil in each statement I have used as optional with ?. So it is better for me to use given linked file in my class. It becomes easy for me to check now like:
class func getWifiIPAddress() -> String {
var wifiIp = ""
let WIFI_IF = "en0"
let allInterface = Interface.allInterfaces()
for interf in allInterface {
if interf.name == WIFI_IF {
if let address = interf.address {
if address.contains(".") {
wifiIp = address
break
}
}
}
}
return wifiIp
}
I have parsed string for "." because Interface Class returns two interface in my iPhone for en0 address like "fb00::" and address like "101.10.1.1"
I created a simple file for getting the ip address. I based this solution on # lundhjem's, #DavidH's and #Ihunath's answers. It considers wired connections. I haven't included VPN in this solution though.
PCNetwork.h
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
#interface PCNetwork : NSObject
+ (NSString *)getIPAddress; // Prefers IPv4
+ (NSString *)getIPAddress:(BOOL)preferIPv4;
+ (NSDictionary *)getIPAddresses;
#end
NS_ASSUME_NONNULL_END
PCNetwork.m
#import "PCNetwork.h"
#include <ifaddrs.h>
#include <arpa/inet.h>
#include <net/if.h>
#define IP_UNKNOWN #"0.0.0.0"
#define IP_ADDR_IPv4 #"ipv4"
#define IP_ADDR_IPv6 #"ipv6"
#implementation PCNetwork
#pragma mark - IP
+ (NSString *)getIPAddress {
return [self getIPAddress:YES];
}
+ (NSString *)getIPAddress:(BOOL)preferIPv4 {
NSArray *searchArray = [self getAllIFSearchArray:preferIPv4];
NSDictionary *addresses = [self getIPAddresses];
DLog(#"addresses: %#", addresses);
__block NSString *address = nil;
[searchArray enumerateObjectsUsingBlock:^(NSString *key, NSUInteger idx, BOOL *stop) {
address = addresses[key];
if(address) *stop = YES;
}];
return address ?: IP_UNKNOWN;
}
+ (NSDictionary *)getIPAddresses {
NSMutableDictionary *addresses = [NSMutableDictionary dictionary];
struct ifaddrs *interfaces;
BOOL success = !getifaddrs(&interfaces); // Retrieve the current interfaces : returns 0 on success
if (success) {
struct ifaddrs *temp_interface;
for (temp_interface = interfaces; temp_interface; temp_interface = temp_interface->ifa_next) { // Loop through linked list of interfaces
if (!(temp_interface->ifa_flags & IFF_UP) || (temp_interface->ifa_flags & IFF_LOOPBACK)) { // Ignore interfaces that aren't up and loopback interfaces.
continue;
}
if (!temp_interface->ifa_addr) {
continue;
}
const struct sockaddr_in *temp_addr = (const struct sockaddr_in*)temp_interface->ifa_addr;
if (temp_addr->sin_family == AF_INET || temp_addr->sin_family == AF_INET6) {
char addrBuf[MAX(INET_ADDRSTRLEN, INET6_ADDRSTRLEN)];
NSString *name = [NSString stringWithUTF8String:temp_interface->ifa_name];
NSString *type = nil;
if (temp_addr->sin_family == AF_INET) {
if (inet_ntop(AF_INET, &temp_addr->sin_addr, addrBuf, INET_ADDRSTRLEN)) {
type = IP_ADDR_IPv4;
}
} else {
const struct sockaddr_in6 *addr6 = (const struct sockaddr_in6*)temp_interface->ifa_addr; // AF_INET6
if (inet_ntop(AF_INET6, &addr6->sin6_addr, addrBuf, INET6_ADDRSTRLEN)) {
type = IP_ADDR_IPv6;
}
}
if (type) {
NSString *key = [NSString stringWithFormat:#"%#/%#", name, type];
addresses[key] = [NSString stringWithUTF8String:addrBuf];
}
}
}
freeifaddrs(interfaces); // Free memory
}
return addresses.count ? addresses.copy : nil;
}
#pragma mark - Inter Frame Spacing
+ (NSArray *)getAllIFSearchArray:(BOOL)preferIPv4 {
NSArray *KNOWN_WIFI_IFS = #[#"en0"];
NSArray *KNOWN_WIRED_IFS = #[#"en1",#"en2",#"en3",#"en4"];
NSArray *KNOWN_CELL_IFS = #[#"pdp_ip0",#"pdp_ip1",#"pdp_ip2",#"pdp_ip3"];
NSMutableArray *searchArray = [NSMutableArray array];
// Add wifi
[searchArray addObjectsFromArray:[self getIFSearchArrayWith:KNOWN_WIFI_IFS preferIPv4:preferIPv4]];
// Add cell
[searchArray addObjectsFromArray:[self getIFSearchArrayWith:KNOWN_CELL_IFS preferIPv4:preferIPv4]];
// Add wired
[searchArray addObjectsFromArray:[self getIFSearchArrayWith:KNOWN_WIRED_IFS preferIPv4:preferIPv4]];
return searchArray.copy;
}
+ (NSArray *)getIFSearchArrayWith:(NSArray *)iFList preferIPv4:(BOOL)preferIPv4 {
NSMutableArray *searchArray = [NSMutableArray array];
for (NSString *iFType in iFList) {
if (preferIPv4) {
[searchArray addObject:[NSString stringWithFormat:#"%#/%#", iFType, IP_ADDR_IPv4]];
[searchArray addObject:[NSString stringWithFormat:#"%#/%#", iFType, IP_ADDR_IPv6]];
} else {
[searchArray addObject:[NSString stringWithFormat:#"%#/%#", iFType, IP_ADDR_IPv6]];
[searchArray addObject:[NSString stringWithFormat:#"%#/%#", iFType, IP_ADDR_IPv4]];
}
}
return searchArray.copy;
}
#end
in iOS 13.4.1 is not work for me .
i use this fix it.
+ (NSString *)getIPAddress{
NSArray *searchArray =
#[ IOS_VPN #"/" IP_ADDR_IPv4, IOS_VPN #"/" IP_ADDR_IPv6, IOS_WIFI #"/" IP_ADDR_IPv4, IOS_WIFI #"/" IP_ADDR_IPv6, IOS_4_3G #"/" IP_ADDR_IPv4, IOS_4_3G #"/" IP_ADDR_IPv6, IOS_CELLULAR #"/" IP_ADDR_IPv4, IOS_CELLULAR #"/" IP_ADDR_IPv6];
__block NSDictionary *addresses = [self getIPAddressArray];
__block NSString *address;
[searchArray enumerateObjectsUsingBlock:^(NSString *key, NSUInteger idx, BOOL *stop)
{
address = addresses[key];
if ([key rangeOfString:#"ipv6"].length > 0 && ![[NSString stringWithFormat:#"%#",addresses[key]] hasPrefix:#"(null)"] ) {
if ( ![addresses[key] hasPrefix:#"fe80"]) {
// isIpv6 = YES;
*stop = YES;
}
}else{
if([self isValidatIP:address]) {
*stop = YES;
}
}
} ];
return address ? address : #"error";
}
+ (NSString *)getIPType{
NSString *ipAddress = [self getIPAddress];
if ([self isValidatIP:ipAddress]) {
return #"04";//ipv4
}else{
return #"06";//ipv6
}
}
+ (NSDictionary *)getIPAddressArray{
NSMutableDictionary *addresses = [NSMutableDictionary dictionaryWithCapacity:8];
// retrieve the current interfaces - returns 0 on success
struct ifaddrs *interfaces;
if(!getifaddrs(&interfaces)) {
// Loop through linked list of interfaces
struct ifaddrs *interface;
for(interface=interfaces; interface; interface=interface->ifa_next) {
if(!(interface->ifa_flags & IFF_UP) /* || (interface->ifa_flags & IFF_LOOPBACK) */ ) {
continue; // deeply nested code harder to read
}
const struct sockaddr_in *addr = (const struct sockaddr_in*)interface->ifa_addr;
char addrBuf[ MAX(INET_ADDRSTRLEN, INET6_ADDRSTRLEN) ];
if(addr && (addr->sin_family==AF_INET || addr->sin_family==AF_INET6)) {
NSString *name = [NSString stringWithUTF8String:interface->ifa_name];
NSString *type;
if(addr->sin_family == AF_INET) {
if(inet_ntop(AF_INET, &addr->sin_addr, addrBuf, INET_ADDRSTRLEN)) {
type = IP_ADDR_IPv4;
}
} else {
const struct sockaddr_in6 *addr6 = (const struct sockaddr_in6*)interface->ifa_addr;
if(inet_ntop(AF_INET6, &addr6->sin6_addr, addrBuf, INET6_ADDRSTRLEN)) {
type = IP_ADDR_IPv6;
}
}
if(type) {
NSString *key = [NSString stringWithFormat:#"%#/%#", name, type];
addresses[key] = [NSString stringWithUTF8String:addrBuf];
}
}
}
// Free memory
freeifaddrs(interfaces);
}
return [addresses count] ? addresses : nil;
}
+ (BOOL)isValidatIP:(NSString *)ipAddress {
if (ipAddress.length == 0) {
return NO;
}
NSString *urlRegEx = #"^([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\."
"([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\."
"([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\."
"([01]?\\d\\d?|2[0-4]\\d|25[0-5])$";
NSError *error;
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:urlRegEx options:0 error:&error];
if (regex != nil) {
NSTextCheckingResult *firstMatch=[regex firstMatchInString:ipAddress options:0 range:NSMakeRange(0, [ipAddress length])];
if (firstMatch) {
NSRange resultRange = [firstMatch rangeAtIndex:0];
NSString *result=[ipAddress substringWithRange:resultRange];
//输出结果
NSLog(#"%#",result);
return YES;
}
}
return NO;
}

Resources