ip address change notifications - ios6

I'm using the Reachability class to detect wifi availability.
There are some situations when an ipad will be connected to one wifi network,and a user will connect to another available wifi network.
During these network transitions reachable->unreachable->reachable notifications are not generated.
This connection change where the ip address of the ipad changes is what Im trying to listen for.
Do notifications for local wifi connection changes exist or will I have to just poll my ip periodically?

I would personally just poll the IP however frequently you find appropriate (once a second should be fine), using the code found here, and just store the previous occurrences result, see if it changes.
What I would really recommend is setting up a simple delegate class that does this for you and will send a custom event to whatever class may need those updates. It should be pretty straight forward, especially considering it seems like you have some experience.
UPDATE
I have posted some code below that will create a delegate that will call back to whatever class once it detects any change in IP. Note there may be some errors/typos as I am not in front of a computer with XCode currently, but you should get the general idea.
IPChangeNotifier.h
#import <UIKit/UIKit.h>
#protocol IPChangeNotifierDelegate;
#interface IPChangeNotifier : NSObject {
NSString *prevIP;
NSTimer *changeTimer;
id changeDelegate;
}
-(id) initWithTimer:(float)time andDelegate:(id)del;
-(NSString*)getIPAddress;
-(void) checkForChange;
#end
#protocol IPChangeNotifierDelegate <NSObject>
#optional
-(void) IPChangeDetected:(NSString*)newIP previousIP:(NSString*)oldIP;
#end
IPChangeNotifier.m
#import IPChangeNotifier.h
#import <ifaddrs.h>
#import <arpa/inet.h>
#implementation IPChangeNotifier
-(id) initWithTimer:(float)time andDelegate:(id)del {
changeTimer = [NSTimer scheduleTimerWithTimeInterval:time target:self selector:#selector(checkForChange) userInfo:nil repeats:YES];
prevIP = #"";
changeDelegate = del;
}
-(void) checkForChange {
NSString *currentIP = [self getIPAddress];
if (![currentIP isEqualToString:prevIP]) {
if ([changeDelegate respondsToSelector:#selector(IPChangeDetected:)]){
[changeDelegate IPChangeDetected:currentIP previousIP:prevIP];
}
prevIP = currentIP;
}
}
- (NSString *)getIPAddress {
struct ifaddrs *interfaces = NULL;
struct ifaddrs *temp_addr = NULL;
NSString *wifiAddress = nil;
NSString *cellAddress = nil;
// retrieve the current interfaces - returns 0 on success
if(!getifaddrs(&interfaces)) {
// Loop through linked list of interfaces
temp_addr = interfaces;
while(temp_addr != NULL) {
sa_family_t sa_type = temp_addr->ifa_addr->sa_family;
if(sa_type == AF_INET || sa_type == AF_INET6) {
NSString *name = [NSString stringWithUTF8String:temp_addr->ifa_name];
NSString *addr = [NSString stringWithUTF8String:inet_ntoa(((struct sockaddr_in *)temp_addr->ifa_addr)->sin_addr)]; // pdp_ip0
NSLog(#"NAME: \"%#\" addr: %#", name, addr); // see for yourself
if([name isEqualToString:#"en0"]) {
// Interface is the wifi connection on the iPhone
wifiAddress = addr;
} else
if([name isEqualToString:#"pdp_ip0"]) {
// Interface is the cell connection on the iPhone
cellAddress = addr;
}
}
temp_addr = temp_addr->ifa_next;
}
// Free memory
freeifaddrs(interfaces);
}
NSString *addr = wifiAddress ? wifiAddress : cellAddress;
return addr ? addr : #"0.0.0.0";
}
#end
You can then simply make whatever class you want a delegate by adding <IPChangeNotifierDelegate> to your interface file and then initialize the notifier by doing something simple like below.
IPChangeNotifier *ipChecker = [[IPChangeNotifier alloc] initWithTimer:1.0 andDelegate:self]
Also make sure you have the following method included to make sure you can get the change events and do whatever you need.
-(void) IPChangeDetected:(NSString*)newIP previousIP:(NSString*)oldIP {
// Do what you need
}

Related

GKMatchRequest invitation not showing in other device

Thanks to the updates to GameKit API in iOS 6, I am finally able to implement my turn-based board game the way it should be, complete with turn timeouts and better programmatic creation of matches. However, I am running into an issue that I cannot seem to solve. My desire is to have Game Center running entirely invisible to the end-user, so that everything is programmatic and uses my own custom interfaces. Therefore, I use my own custom table view to display matches, not the default GKTurnBasedMatchmakerViewController. Right now, I have no problem displaying open matches using the -loadMatchesWithCompletionHandler: method. I also use a custom screen to create a match, with a direct creation for auto-match (not a problem) and a table view that loads Game Center friends of the localPlayer for invitation. Since the playersToInvite attribute can now be filled with playerID's, this is possible in iOS 6.
My main problem is handling the invitation on the recipient's side. I send the invitation with the following code
-(void)invitarAmigoMio:(NSArray*)losAmigos
{
GKMatchRequest *request = [[GKMatchRequest alloc] init];
request.minPlayers = 2;
request.maxPlayers = 2;
request.defaultNumberOfPlayers=2;
request.playersToInvite = losAmigos;
request.inviteMessage = #"Your Custom Invitation Message Here";
request.inviteeResponseHandler = ^(NSString *playerID, GKInviteeResponse
response)
{
if (response==GKInviteeResponseAccepted)
{
NSLog(#"INVITACION ACEPTADA");
}
else
{
NSLog(#"INVITACION RECHAZADA");
}
};
}
I have a delegate that handles the notifications. The following code is launch when the user is authenticated
-(void)registroNotificaciones
{
NSLog(#"registroNotificaciones");
[GKMatchmaker sharedMatchmaker].inviteHandler = ^(GKInvite* acceptedInvite, NSArray *playersToInvite)
{
if(acceptedInvite != nil)
{
// Get a match for the invite we obtained...
[[GKMatchmaker sharedMatchmaker] matchForInvite:acceptedInvite completionHandler:^(GKMatch *match, NSError *error)
{
if(match != nil)
{
NSLog(#"match != nil: ");
}
else if(error != nil)
{
NSLog(#"ERROR: From matchForInvite: %#", [error description]);
}
else
{
NSLog(#"ERROR: Unexpected return from matchForInvite...");
}
}];
}
};
NSLog(#"FIN registroNotificaciones");
}
Why notifications are not send?Or notifications are not received?Is there another way to send notification to invite to play a game?
I checked and my sandbox accounts of Game Center allow invitations, and I dont know what´s going on
You did pretty much all that is needed to create the invite; you just need to send it with this code:
[[GKMatchmaker sharedMatchmaker] findMatchForRequest:request withCompletionHandler:^(GKMatch* match, NSError *error) {
if (error)
{
//Invite has not been sent
}
else if (match != nil)
{
//whatever you want to do when the receiver accepts the invite
}
}];
I know you are looking to use custom displays but I haven't made my own view controllers for this, instead I used GKMatchmakerViewController which handles the 'send' invites for me and I think that's what's missing in your code.
Here is my code for that, maybe you can try it and check how GKMatchmakerViewController works internally (if possible):
GKMatchRequest *request = [[[GKMatchRequest alloc] init] autorelease];
request.minPlayers = minPlayers;
request.maxPlayers = maxPlayers;
request.playersToInvite = pendingPlayersToInvite;
GKMatchmakerViewController *mmvc = [[[GKMatchmakerViewController alloc] initWithMatchRequest:request] autorelease];
mmvc.matchmakerDelegate = self;
[myPresentingViewController presentModalViewController:mmvc animated:YES];
I have almost the same code you have that handles the notifications (registroNotificaciones) and it gets executed as soon as the user authenticates with GameCenter and is one of the first things the app does when launched too, from what you say, it looks like this code is working properly, just . Hope it helps!

Accessing an array by index passed by function call

I have written a specific init-override-function where i want to pass an index number to be called in an array. The index number itself is defined by selecting a tablerow in a tableview by the user. So.. the rownumber that is selected shall be passed into the init function and used there for further processing.
Well.. now there is my problem.. In my opinion the methods I've created are all correctly coded. But when I click my defined Connect Button an error message appears in the console, that, the index is out of bounds. So.. I've checked the array for entries and there are all available. So the indexnumber should be fine.
Maybe fyi: I've created a copy of the array in the TableViewController that is originally located in the PortConnection file.
Here are the necessary files. Can anyone give me a helping hand, where to search for?
PORTTABLEVIEWCONTROLLER.M
- (IBAction)pushConnect:(id)sender {
NSInteger selectedRow = [tableView selectedRow];
[portConnection initPort:selectedRow];
}
- (id)init {
self = [super init];
if (self) {
// Initialization of port Connection instance
portConnection = [[PortConnection alloc] init];
// Fill array in Portconnection.m with devices
[portConnection listDevices];
// Fill tableView Data Array with data from portConnection array
self.tableViewDataArray = [NSMutableArray arrayWithArray:portConnection.portArray];
}
return self;
}
PORTCONNECTION.H
#
interface PortConnection : NSObject {
// Instance of AMSerialPort
AMSerialPort *port;
// Port Array to be filled with found ports
NSMutableArray *portArray;
}
// List Devices into an given array
- (void)listDevices;
// Connect to selected port
- (void)initPort:(NSInteger)selectedRow;
#property (nonatomic, retain) NSMutableArray *portArray;
#property (nonatomic, retain) AMSerialPort *port;
#end
PORTCONNECTION.M
#implementation PortConnection
#synthesize port;
#synthesize portArray;
#pragma mark -
#pragma mark Serial Port Access
- (void)listDevices {
// get an port enumerator
NSEnumerator *enumerator = [AMSerialPortList portEnumerator];
AMSerialPort *aPort;
while ((aPort = [enumerator nextObject])) {
[portArray addObject:[PortItem portItemWithTitle:[aPort name] andPath:[aPort bsdPath]]];
}
}
- (void)initPort:(NSInteger)selectedRow {
//Create object of selected port searched in array
PortItem *portSelected = [portArray objectAtIndex:selectedRow];
NSString *deviceNameSelected = [portSelected valueForKey:#"bsdPath"];
// Start Connection
if (![deviceNameSelected isEqualToString:[self.port bsdPath]]) {
[self.port close];
[self setPort:[[[AMSerialPort alloc] init:deviceNameSelected withName:deviceNameSelected type:(NSString *)CFSTR(kIOSerialBSDModemType)] autorelease]];
[self.port setDelegate:self.port];
if ([self.port open]) {
NSLog(#"Connected...");
[self.port setSpeed:B38400];
[self.port readDataInBackground];
} else {
NSLog(#"error connecting");
[self setPort:nil];
}
}
}
#pragma mark -
#pragma mark initialization / deallocation
- (id)init {
self = [super init];
if (self) {
portArray = [NSMutableArray array];
}
return self;
}
- (void)dealloc {
portArray = NULL;
[super dealloc];
}
#end
Well.. my idea is, that something is wrong with the method INITPORT:(NSINTEGER)SELECTEDROW
but I am not sure at all....
Thanks so much for giving me advice again!
Sebastian
Your problem is this line,
portArray = [NSMutableArray array];
Although it is a retained variable, it will retain the value when you use the property setter method. This is a direct assignment of an autoreleased object will be released in a while and baring any other object retaining it (which doesn't happen), it should get deallocated. This is something which you don't want. Remedy this by using the property setter,
self.portArray = [NSMutableArray array];

QTMovie backed by a Movie works initially, but not in another method

What follows is an initializer method. It creates a Movie structure from a file, with an eye to extracting the raw pixels for processing, as per Apple QA1443. This is then wrapped in a QTMovie – sourceMovie = [QTMovie movieWithQuickTimeMovie: ...] – for convenience.
After initializing such an object, the second method is called, and we attempt to use sourceMovie. However, it turns out that any attempt to access sourceMovie here results in EXC_BAD_ACCESS For instance, NSLog(#"%#", sourceMovie) is impossible. The movie appears to have been deallocated, although it is not clear why.
Furthermore, in this second method, the usual methods to obtain a CVPixelBufferRef do not properly function: QTVisualContextIsNewImageAvailable seems always to return NO, and QTVisualContextCopyImageForTime always gives you an imageBuffer pointing at 0x00. (I guess this is not surprising if the QTMovie, Movie or visualContext are somehow being deallocated.)
The question then is, why is sourceMovie inaccessible from the second method? Is it being deallocated as it seems? If so, why?
And now the code, and sorry in advance for the length.
// From the .h, we have the following inst var declarations:
Movie myMovie;
QTMovie *sourceMovie;
QTVisualContextRef visualContext;
CVPixelBufferRef imageBuffer;
pixels_xy sourceSize;
MovieAnalyzer *analyzer;
// And now to the #implementation...
-(id)initWithFileString:(NSString *)file {
if (self = [super init]) {
NSError *e;
// Bit of a hack - get the movie size using temporary QTMovie.
sourceMovie = [QTMovie movieWithFile:file error:&e];
if (e) {
[self release];
NSLog(#"Could not open movie.");
return nil;
}
NSSize movieSize = [[sourceMovie posterImage] size];
CGRect bounds = CGRectMake(0, 0, movieSize.width, movieSize.height);
// Save the size in pixels.
sourceSize.x = (int)movieSize.width, sourceSize.y = (int)movieSize.height;
CFStringRef movieLocation = (CFStringRef) file;
// Get a QT Visual Context, and create a movie inialized to use it for output.
visualContext = NULL;
OSStatus status = CreatePixelBufferContext(k32ARGBPixelFormat, &bounds, &visualContext);
if (noErr != status && NULL == visualContext) {
[self release];
NSLog(#"Problem initializing QT Visual Context.");
return nil;
}
/*** Instantiate the Movie ***/
myMovie = NULL;
Boolean trueValue = true;
QTNewMoviePropertyElement newMovieProperties[3] = {0};
// Setup movie location
newMovieProperties[0].propClass = kQTPropertyClass_DataLocation;
newMovieProperties[0].propID = kQTDataLocationPropertyID_CFStringPosixPath;
newMovieProperties[0].propValueSize = sizeof(CFStringRef);
newMovieProperties[0].propValueAddress = &movieLocation;
// Assign the visual context - could also be NULL
newMovieProperties[1].propClass = kQTPropertyClass_Context;
newMovieProperties[1].propID = kQTContextPropertyID_VisualContext;
newMovieProperties[1].propValueSize = sizeof(visualContext);
newMovieProperties[1].propValueAddress = &visualContext;
// Make the movie active
newMovieProperties[2].propClass = kQTPropertyClass_NewMovieProperty;
newMovieProperties[2].propID = kQTNewMoviePropertyID_Active;
newMovieProperties[2].propValueSize = sizeof(trueValue);
newMovieProperties[2].propValueAddress = &trueValue;
status = NewMovieFromProperties(3, newMovieProperties, 0, NULL, &myMovie);
if (status != noErr || myMovie == NULL) {
NSLog(#"Problem initializing theMovie"); // problem
[self release];
return nil;
}
// Create a new QTMovie with the Movie as its backing object
sourceMovie = [QTMovie movieWithQuickTimeMovie:myMovie disposeWhenDone:NO error:&e];
if (e) {
NSLog(#"Could not initialize QTMovie from Movie.");
[self release];
return nil;
}
[sourceMovie stepForward];
analyzer = [[MovieAnalyzer alloc] initWithColorString:"BGRA" andSourceSize:sourceSize];
}
return self;
}
-(NSImage*) supplyCalibrationImage {
NSLog(#"%x", &sourceMovie);
[sourceMovie stepForward]; // This fails!
....
}
[QTMovie movieWithQuickTimeMovie:disposeWhenDone:error:] is returning an autoreleased movie. If you want it to stick around past that one method, you need to retain it.

mac screensaver start event

Is there an event fired when screensaver starts? Like for keychain locking:
OSStatus keychain_locked(SecKeychainEvent keychainEvent, SecKeychainCallbackInfo *info, void *context){...}
Finally found it — the solution is to use NSDistributedNotificationCenter and observe folowing events
com.apple.screensaver.didstart
com.apple.screensaver.willstop
com.apple.screensaver.didstop
com.apple.screenIsLocked
com.apple.screenIsUnlocked
Like
[[NSDistributedNotificationCenter defaultCenter]
addObserver:self
selector:#selector(screensaverStarted:)
name:#"com.apple.screensaver.didstart"
object:nil];
While there is no Carbon event for this, you can get notified when the current application changes, and then check to see if the new application is the screen saver process.
// Register the event handler for when applications change
{
EventTypeSpec es;
es.eventClass = kEventClassApplication;
es.eventKind = kEventAppFrontSwitched;
InstallApplicationEventHandler(&appChanged, 1, &es, NULL, NULL);
}
OSStatus appChanged(EventHandlerCallRef nextHandler, EventRef anEvent, void* userData)
{
ProcessSerialNumber psn;
GetEventParameter(anEvent, kEventParamProcessID, typeProcessSerialNumber,
NULL, sizeof(psn), NULL, &psn);
// Determine process name
char procName[255];
{
ProcessInfoRec pInfo;
Str255 procName255;
FSRef ref;
pInfo.processInfoLength = sizeof(ProcessInfoRec);
pInfo.processName = procName255;
pInfo.processAppRef = &ref;
GetProcessInformation(&psn, &pInfo);
const unsigned int size = (unsigned int)procName255[0];
memcpy(procName, procName255 + 1, size);
procName[size] = '\0';
}
if(strcmp(procName, "ScreenSaverEngine") == 0)
{
NSLog(#"Found %s\n", procName);
}
return noErr;
}
This isn't exactly an answer to the question, but I spent a lot of time looking in vain for a list of the notifications posted by macOS, so I wanted to post some code I wrote for notification discovery.
The code simply signs up to listen to all notifications, and prints some info for each as it comes in.
import Foundation
let distCenter = CFNotificationCenterGetDistributedCenter()
if distCenter == nil {
exit(1)
}
CFNotificationCenterAddObserver(distCenter, nil, { (center, observer, name, object, userInfo) -> Void in
print("Event occurred: \(name) User info: \(userInfo)")
}, nil, nil, .DeliverImmediately)
CFRunLoopRun()

Obj-C methods: calling with parameters

I've been using C-style functions, but I just learned they can't see instance variables. So I was advised to convert them to Objective-C methods.
NSString* myfunc ( int x )
becomes:
- (NSString *)myfunc:(int)x
and
myString = myfunc(x);
becomes
myString = [myString myfunc:x];
??
This compiles with ominous warnings, but does not work. What have I misunderstood?
It looks like your call is incorrect. Perhaps try:
NSString *myString = [self myfunc:x];
As far as I understand, you send the -myfunc: message to a NSString object. So the -myfunc: method should be declared in NSString class (or a category of NSString).
If this is what you want to do, you don't need to return the object itself as the result of the method: you can modify its instance variables in the method implementation. The method call (or message sending) looks like:
[myString myfunc:x];
If you want to send the message to an object of another class and return a string, your method declaration is correct but must appear in your class implementation and the receiver of the message (this is the item on the left in the square brackets) must be of that class:
#implementation MyClass
-(NSString *)myfunc:(int)x
{
NSString * returnString;
...// do something with x, returnString and instance variables
return returnString;
}
#end;
...
MyClass * myobj = [[MyClass alloc] init];
NSString * myString = [myobj myfunc:42];
As a second answer, I am trying to understand your problem through all your recent questions.
At the beginning, there was a C function returning a pointer to a NSString object:
NSString * myfunc( int x )
{
... // Do something with x
NSString * myString = #"MYDATA";
... // Do something with myString
return myString;
}
Then, you wanted to add in that function some code about an UIImage object:
image1.image = [UIImage imageNamed:#"image1.png"];
You were advised to convert the function to a method. If you want to access .image instance variable, this method has to belong to the class of image1 object (let's say this is AlanImage class). Something like this:
#interface AlanImage : NSObject {
UIImage image;
}
- (NSString *) myfuncWithParam: (int) x;
#end;
#implementation AlanImage
- (NSString *) myfuncWithParam: (int) x
{
NSString * myString = #"MYDATA";
image = [UIImage imageNamed:#"image1.png"];
return myString;
}
#end
Third, you didn't know what was the receiver of the method. My investigations tend to lead to your image object as a good candidate:
aNiceString = [image myfunc:aNiceInteger];
Finally (this question), not getting a satisfying answer, you reworded your third question, with success this time as it happens.
I am curious to get a more complete view of your project in order to give you some hints. Anyway, it seems that you are learning Objective-C and object oriented concepts: congratulations and stay motivated!
You haven't worked out what Object Oriented Programming is. With [theObject method] you can only call methods belonging to the specific instance.
I am not sure that following trick correctly work for a "general" objective-c, but in apple implementation you can do such:
#interface SomeClass: NSObject {
int m_someVariable;
...
};
- (NSString *) someMethod;
...
#end
#implementation SomeClass
...
//pure c function with extra one parameter
//for accessing to instance variables
static NSString privatePlainCeeMethod(SomeClass *my, int fortyTwo) {
NSString *str;
//access to a instance variable as for a usual
//cee structure field: my->fieldName
...
return [NSString stringWithFormat:#"someVariable:%d, fortyTwo:%d",
my->m_someVariable, fortyTwo];
};
- (NSString *) someMethod {
...
return privatePlainCeeMethod(self,42);
};
...
#end
I use such trick to divide a big objc method on observable private simple functions. These functions (a) do not pollute class interface and (b) are invoked faster than objc method.

Resources