i'm java programmer that 'must' move on to obj-C for a while,
i got some confuse when generating random alphanumeric code... here my javacode:
PS: i want to generate code like this :Gh12PU67, AC88pP13, Bk81gH89
private String generateCode(){
String code = "";
Random r = new Random();
char[] c = new char[]{'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z'};
for(int i = 0; i<4; i++){
int uplow = r.nextInt(2);
String temp = ""+ c[r.nextInt(c.length)];
if(uplow==1)
code = code + temp.toUpperCase();
else
code = code + temp;
if((i+1)%2==0){
code += r.nextInt(10);
code += r.nextInt(10);
}
}
return code;
}
then i create on OBJ-C
-(void)generateCode{
NSString *alphabet = #"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXZY0123456789";
NSMutableString *s = [NSMutableString stringWithCapacity:4];
for (NSUInteger i = 0U; i < 4; i++) {
u_int32_t r = arc4random() % [alphabet length];
unichar c = [alphabet characterAtIndex:r];
[s appendFormat:#"%C", c];
}
NSLog(#"s-->%#",s);
}
but i got "HpNz" for result AC88pP13 insted that hve pattern String,string, numeric,numeric, lowescase string,numeric,numeric...
that case screw my life for 3 days...
Your Objective-C code looks good, but (as #Wain correctly said in a comment above),
the Java function function contains logic to insert 2 digits after 2 letters, which you
have not replicated in the Objective-C method.
I would make that logic slightly less obscure and write it as
- (void)generateCode
{
static NSString *letters = #"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXZY";
static NSString *digits = #"0123456789";
NSMutableString *s = [NSMutableString stringWithCapacity:8];
for (NSUInteger i = 0; i < 2; i++) {
uint32_t r;
// Append 2 random letters:
r = arc4random_uniform((uint32_t)[letters length]);
[s appendFormat:#"%C", [letters characterAtIndex:r]];
r = arc4random_uniform((uint32_t)[letters length]);
[s appendFormat:#"%C", [letters characterAtIndex:r]];
// Append 2 random digits:
r = arc4random_uniform((uint32_t)[digits length]);
[s appendFormat:#"%C", [digits characterAtIndex:r]];
r = arc4random_uniform((uint32_t)[digits length]);
[s appendFormat:#"%C", [digits characterAtIndex:r]];
}
NSLog(#"s-->%#",s);
}
Remark (from the man page):
arc4random_uniform(length) is preferred over arc4random() % length,
as it avoids "modulo bias" when the upper bound is not a power of two.
Remark: A more verbatim translation of the Java code code += r.nextInt(10);
to Objective-C would be
r = arc4random_uniform(10);
[s appendString:[#(r) stringValue]];
which creates a NSNumber object #(r) from the random number, and then
converts that to a string.
if you want a secure random string you should use this code:
#define ASCII_START_NUMERS 0x30
#define ASCII_END_NUMERS 0x39
#define ASCII_START_LETTERS_A 0x41
#define ASCII_END_LETTERS_Z 0x5A
#define ASCII_START_LETTERS_a 0x61
#define ASCII_END_LETTERS_z 0x5A
-(NSString *)getRandomString:(int)length {
NSMutableString *result = [[NSMutableString alloc]init];
while (result.length != length) {
NSMutableData* data = [NSMutableData dataWithLength:1];
SecRandomCopyBytes(kSecRandomDefault, 1, [data mutableBytes]);
Byte currentChar = 0;
[data getBytes:¤tChar length:1];
NSString *s = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
if (currentChar > ASCII_START_NUMERS && currentChar < ASCII_END_NUMERS) { // 0 to 0
[result appendString:s];
continue;
}
if (currentChar > ASCII_START_LETTERS_A && currentChar < ASCII_END_LETTERS_Z) { // A to Z
[result appendString:s];
continue;
}
if (currentChar > ASCII_START_LETTERS_a && currentChar < ASCII_END_LETTERS_z) { // a to z
[result appendString:s];
continue;
}
}
return result;
}
I spent much too much time trying to find an implementation for base 62 conversion for Objective-C. I am sure this is a terrible example and there must be an elegant, super-efficient way to do this, but this works, please edit or answer to improve it! But I wanted to help people searching for this to have something that will work. There doesn't appear to be anything specific to be found for an Objective-C implementation.
#implementation Base62Converter
+(int)decode:(NSString*)string
{
int num = 0;
NSString * alphabet = #"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
for (int i = 0, len = [string length]; i < len; i++)
{
NSRange range = [alphabet rangeOfString:[string substringWithRange:NSMakeRange(i,1)]];
num = num * 62 + range.location;
}
return num;
}
+(NSString*)encode:(int)num
{
NSString * alphabet = #"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
NSMutableString * precursor = [NSMutableString stringWithCapacity:3];
while (num > 0)
{
[precursor appendString:[alphabet substringWithRange:NSMakeRange( num % 62, 1 )]];
num /= 62;
}
// http://stackoverflow.com/questions/6720191/reverse-nsstring-text
NSMutableString *reversedString = [NSMutableString stringWithCapacity:[precursor length]];
[precursor enumerateSubstringsInRange:NSMakeRange(0,[precursor length])
options:(NSStringEnumerationReverse |NSStringEnumerationByComposedCharacterSequences)
usingBlock:^(NSString *substring, NSRange substringRange, NSRange enclosingRange, BOOL *stop) {
[reversedString appendString:substring];
}];
return reversedString;
}
#end
Your code is fine. If anything, make it more generic. Here is a recursive version for any base (same code):
#import <Foundation/Foundation.h>
#interface BaseConversion : NSObject
+(NSString*) formatNumber:(NSUInteger)n toBase:(NSUInteger)base;
+(NSString*) formatNumber:(NSUInteger)n usingAlphabet:(NSString*)alphabet;
#end
#implementation BaseConversion
// Uses the alphabet length as base.
+(NSString*) formatNumber:(NSUInteger)n usingAlphabet:(NSString*)alphabet
{
NSUInteger base = [alphabet length];
if (n<base){
// direct conversion
NSRange range = NSMakeRange(n, 1);
return [alphabet substringWithRange:range];
} else {
return [NSString stringWithFormat:#"%#%#",
// Get the number minus the last digit and do a recursive call.
// Note that division between integer drops the decimals, eg: 769/10 = 76
[self formatNumber:n/base usingAlphabet:alphabet],
// Get the last digit and perform direct conversion with the result.
[alphabet substringWithRange:NSMakeRange(n%base, 1)]];
}
}
+(NSString*) formatNumber:(NSUInteger)n toBase:(NSUInteger)base
{
NSString *alphabet = #"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; // 62 digits
NSAssert([alphabet length]>=base,#"Not enough characters. Use base %ld or lower.",(unsigned long)[alphabet length]);
return [self formatNumber:n usingAlphabet:[alphabet substringWithRange:NSMakeRange (0, base)]];
}
#end
int main(int argc, char *argv[]) {
#autoreleasepool {
NSLog(#"%#",[BaseConversion formatNumber:3735928559 toBase:16]); // deadbeef
return EXIT_SUCCESS;
}
}
A Swift 3 version: https://gist.github.com/j4n0/056475333d0ddfe963ac5dc44fa53bf2
You could improve your encode method in such a way that reversing the final string is not necessary:
+ (NSString *)encode:(NSUInteger)num
{
NSString *alphabet = #"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
NSUInteger base = [alphabet length];
NSMutableString *result = [NSMutableString string];
while (num > 0) {
NSString *digit = [alphabet substringWithRange:NSMakeRange(num % base, 1)];
[result insertString:digit atIndex:0];
num /= base;
}
return result;
}
Of course, this could also be generalized for arbitrary bases or alphabets, as suggested by #Jano in his answer.
Note that this method (as well as your original encode method) returns an empty string for num = 0, so you might want to consider this case separately (or just replace while (num > 0) { ... } by do { ... } while (num > 0).
For more efficiency, one could avoid all intermediate NSString objects altogether, and work with plain C strings:
+ (NSString *)encode:(NSUInteger)num
{
static const char *alphabet = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
NSUInteger base = 62;
char result[20]; // sufficient room to encode 2^64 in Base-62
char *p = result + sizeof(result);
*--p = 0; // NULL termination
while (num > 0) {
*--p = alphabet[num % base];
num /= base;
}
return [NSString stringWithUTF8String:p];
}
I need to parse an NSString to a byte array and am having some trouble doing it. I have a padded byte array in a method and convert that into a mutablestring, then I have a method that needs to place those numbers back into a byte array.
In C# it would be as simple as:
do
{
val = byte.Parse(str.Substring(i, 3));
byteArr[j++] = val;
i += 3;
}
Here is the code snippit Note** Ive been trying a lot of different things in the do loop so its a mess in there right now:
-(NSData*) StrToByteArray: (NSString*)str
{
NSLog(#"StrToByteArray. String: %#", str);
if([str length]==0)
NSLog(#"Invailid String");
int val;
Byte byteArr[[str length]/3];
int i = 0;
int j = 0;
NSRange range;
do {
range = NSMakeRange(i, 3);
val = (int)[str substringFromIndex:i];
NSLog(#"StrToByteArray. VAR: %i", val);
byteArr[j++] = val;
NSLog(#"byteArr: %i",byteArr[i]);
i+=3;
}while(i<str.length);
NSData* wrappedByteArr = [NSData dataWithBytes:&byteArr length: sizeof(byteArr)];
return wrappedByteArr;
}
Here is the loop that makes the padded string:
for(int i = 0; i<=len;i++)
{
val = byteArr[i];
NSLog(#"byteArr to string original: %i", val);
if(val<(Byte)10)
{
[tempStr appendString:(#"00")];
[tempStr appendString:[NSString stringWithFormat:#"%d",val]];
}
else if(val<(Byte)100)
{
[tempStr appendString:(#"0")];
[tempStr appendString:[NSString stringWithFormat:#"%d",val]];
}
else {
[tempStr appendString:[NSString stringWithFormat:#"%d",val]];
}
}
NSLog(#"string: %#", tempStr);
return tempStr;
Take 2
Now that I know what the data looks like and how you want to parse it, I would approach it like this:
- (NSData *) parseStringToData:(NSString *) str
{
if ([str length] % 3 != 0)
{
// raise an exception, because the string's length should be a multiple of 3.
}
NSMutableData *result = [NSMutableData dataWithLength:[str length] / 3];
unsigned char *buffer = [result mutableBytes];
for (NSUInteger i = 0; i < [result length]; i++)
{
NSString *byteString = [str substringWithRange:NSMakeRange(i * 3, 3)];
buffer[i] = [byteString intValue];
}
return result;
}
Edit:
Your padding method could be simplified as well by providing the correct format specifier that automatically pads integers.
for(int i = 0; i<=len;i++)
{
val = byteArr[i];
NSLog(#"byteArr to string original: %i", val);
[tempStr appendFormat:#"%03d", val];
}
I am using Image Magick on ios and I have converted some command lines used on my server into a ConvertImageCommand. Everything work well.
I have added the -monitor as an argument to the command so I can see every tiny progress on my image (loading, resizing, cropping, etc)
However, I would like to display a progress bar to inform the user on the progress of the image process.
I am looking for a very simple example on how to use the progress monitor function...
SetImageProgressMonitor(Image *,const MagickProgressMonitor,void *),
SetImageInfoProgressMonitor(ImageInfo *,const MagickProgressMonitor,void *);
Can somebody help me ?
Here is the code:
- (void)ExecuteCommand {
/*
command is an array which contains all the elements of the ImageMagick command.
example:
command (
convert,
imgSource.jpg,
"-blur",
"0x2.5",
"-paint",
5,
imgSaved.jpg
)
*/
ImageInfo *imageInfo = AcquireImageInfo();
int nbArgs = command.count;
char **argv = (char **)malloc((nbArgs + 1) * sizeof(char*));
for (unsigned i = 0; i < nbArgs; i++)
{
NSString *argString = [command objectAtIndex:i];
argv[i] = strdup([argString UTF8String]);
}
argv[nbArgs] = NULL;
progress_monitor_method = SetImageInfoProgressMonitor(imageInfo, &MonitorProgress, self);
ConvertImageCommand(imageInfo, nbArgs, argv, NULL, AcquireExceptionInfo());
if (argv != NULL)
{
for (unsigned index = 0; argv[index] != NULL; index++) {
free(argv[index]);
}
free(argv);
}
}
MagickBooleanType MonitorProgress(const char *text,const MagickOffsetType offset,const MagickSizeType extent,void *client_data) {
IM_TestViewController *IMVC = client_data;
float prog = offset;
float tot = extent;
NSNumber *value = [NSNumber numberWithFloat:prog/tot];
[IMVC performSelectorInBackground:#selector(updateProgressBar:) withObject:value] ;
NSLog(#"Action : %# %lld on %lld", [NSString stringWithCString:text encoding:NSUTF8StringEncoding], offset, extent);
return MagickTrue;
}
- (void)updateProgressBar:(NSNumber *)value {
self.progressBar.progress = [value floatValue];
}
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;
}