I need to send a distributed notification from my cocoa app to my firebreath project, so I need to create an observer and a selector in my firebreath code.
I changed the class extension to ".mm" in order to support objective-c code. I already have objective-c code in my firebreath project and is working ok. But when I try to create an observer I get errors in my code and I don't know how to resolve it.
Here is my source code from firebreath project:
//This is the selector
- (void)receiveAppConfirmationNotification:(NSNotification*)notif{
//The application is alive.
NSLog(#"The application is alive!!!!!!!!");
}
std::string MyProjectAPI::bgp(const std::string& val)
{
//Add an observer to see if the application is alive.
NSString *observedObject = #"com.test.net";
NSDistributedNotificationCenter *center = [NSDistributedNotificationCenter defaultCenter];
[center addObserver: self
selector: #selector(receiveAppConfirmationNotification:)
name: #"App Confirmation Notification"
object: observedObject];
}
Here are my errors:
...firebreath/../projects/MyProject/MyProjectAPI.mm:133: error: expected unqualified-id before '-' token. This is the line where I defined the "receiveAppConfirmationNotification" method.
...firebreath/../projects/MyProject/MyProjectAPI.mm:157: error: 'self' was not declared in this scope.
How can I do to define the selector?
How can I do to add the observer as the class itself?
A selector has to be part of an objective-c++ class; you can't just throw it in the middle of nowhere there, it should be in the #implementation section of the class.
To keep things as compatible as possible, I recommend putting both #interface and #implementation sections in the .mm file so that the .h file is compatible with C++, but that's up to you; it'll just be easier if you do it that way. You can use the pimpl pattern to help with this if you want.
I did the interface and the implementation and the code is without errors. The problem is that I am able to send notifications to my cocoa application, but I am not able to recibe a notification from the application to the plugin.
Here is header file:
#ifdef __OBJC__
#interface FBMyProject : NSObject {
NSString *parameter_val;
}
#property (nonatomic, retain) NSString *parameter_val;
-(void) receiveAppConfirmationNotification:(NSNotification*)notif;
#end
#endif
class MyProjectAPI : public FB::JSAPIAuto
{
public:
...
}
#endif
Here is my source file:
#implementation FBMyProject
#synthesize parameter_val;
-(void) receiveAppConfirmationNotification:(NSNotification*)notif{
//The application is alive.
NSLog(#"The application is alive!!!!!!!!");
}
- (id)init
{
self = [super init];
if (self) {
NSString *observedObject = #"test.com";
NSDistributedNotificationCenter *center = [NSDistributedNotificationCenter defaultCenter];
[center addObserver: self
selector: #selector(receiveAppConfirmationNotification:)
name: #"App Confirmation Notification"
object: observedObject];
}
return self;
}
- (void)dealloc
{
// unregister notification
[[NSDistributedNotificationCenter defaultCenter] removeObserver: self
name: #"App Confirmation Notification"
object: nil];
[self.parameter_val release];
[super dealloc];
}
#end
std::string MyProjectAPI::bgp(const std::string& val)
{
FBMyProject *my_project = [[FBMyProject alloc] init];
my_project.parameter_val = [NSString stringWithUTF8String:val.c_str()];
[my_project release];
return val;
}
Here is my source from the cocoa application:
NSDictionary *data = [NSDictionary dictionaryWithObjectsAndKeys:
#"OK", #"confirmation",
nil];
//Post the notification
NSString *observedObject = #"test.com";
NSDistributedNotificationCenter *center = [NSDistributedNotificationCenter defaultCenter];
[center postNotificationName: #"App Confirmation Notification"
object: observedObject
userInfo: data
deliverImmediately: YES];
Related
I have an app that sends out the following NSDistributed Notification, parts of which I eventually want to "grab" with a CLI and pipe it to a shell script:
{name = TheApplicationNotification; object = TheApplicationNotification; userInfo = {
path = "/path/to/a/file";
}}
I don't really know anything about programming, but I managed to grab the whole notification with the following code for a small CLI, shamelessly put together from two older stackoverflow comments:
#import <Foundation/NSObject.h>
#import <Foundation/NSNotification.h>
#import <Foundation/NSString.h>
#import <Foundation/NSDistributedNotificationCenter.h>
#import <Foundation/NSRunLoop.h>
#import <stdio.h>
#interface monitorcli: NSObject {}
-(id) init;
-(void) receive: (NSNotification*) notification;
#end
#implementation monitorcli
-(id) init {
NSDistributedNotificationCenter * center
= [NSDistributedNotificationCenter defaultCenter];
[center addObserver: self
selector: #selector(receive:)
name: #"TheApplicationNotification"
object: nil
];
fprintf(stderr,"Listening...\n");
[[NSRunLoop currentRunLoop] run];
fprintf(stderr,"Stopping...\n");
return self;
}
-(void) receive: (NSNotification*) notification {
NSLog(#"%#", notification);
}
#end
int main( int argc, char ** argv) {
[[monitorcli alloc] init];
return 0;
}
I have also managed to just get the notification name by substituting
NSLog(#"%#", notification);
for
fprintf(stderr,"%s\n", [[notification name] UTF8String] );
My question now is: what changes in the code are necessary to only print the "path" key in userInfo?
Try:
NSLog(#"%#", notification.userInfo[#"path"]);
The .userInfo Gets the value of the userInfo property, this value is a dictionary. The [#"path"] then gets the value associated with the #"path" key in the dictionary.
Note: all code typed directly into answer, expect minor typos.
I am trying to create a plugin for Unity using Objective-C for an app running on Mac. I need to get the URL when launching my app from a link using an url protocol. I haven't used Objective-C before, so I am having trouble trying to make it work.
I am using an example provided by Unity (download example) and changing the methods to the ones I need to get the URL, but my app crashes on the line nsApplication = [[NSApplication alloc] init]; on the _GetUrl method. I have no idea what I am missing/doing wrong. Also, _GetUrl is the method called from Unity when I want to ask for the url (which is called at the first frame), but I am afraid it might be called after applicationWillFinishLaunching. So where should I actually set the delegate so that applicationWillFinishLaunching happens after the delegate is set?
I use an .h and a .m script and then compile the bundle and import it into Unity as a plugin. This is my code:
PluginUrlHandler.h
#import <Foundation/Foundation.h>
#import <AppKit/AppKit.h>
#interface NSApplicationDelegate : NSObject
{
NSString* urlString;
}
// NSApplication delegate methods
- (void)applicationWillFinishLaunching:(NSNotification *)aNotification;
- (void)handleGetURLEvent:(NSAppleEventDescriptor *)event withReplyEvent:(NSAppleEventDescriptor *)replyEvent;
//Other methods
- (NSString *)getUrl;
#end
PluginUrlHandler.m
#import <Foundation/Foundation.h>
#import "PluginUrlHandler.h"
#implementation NSApplicationDelegate
- (id)init
{
self = [super init];
urlString = #"nourl";
return self;
}
- (void)applicationWillFinishLaunching:(NSNotification *)aNotification
{
NSAppleEventManager *appleEventManager = [NSAppleEventManager sharedAppleEventManager];
[appleEventManager setEventHandler:self
andSelector:#selector(handleGetURLEvent:withReplyEvent:)
forEventClass:kInternetEventClass andEventID:kAEGetURL];
}
- (void)handleGetURLEvent:(NSAppleEventDescriptor *)event withReplyEvent:(NSAppleEventDescriptor *)replyEvent
{
[event paramDescriptorForKeyword:keyDirectObject] ;
NSString *urlStr = [[event paramDescriptorForKeyword:keyDirectObject] stringValue];
urlString = urlStr;
}
- (NSString *)getUrl
{
return urlString;
}
#end
static NSApplicationDelegate* delegateObject = nil;
static NSApplication* nsApplication = nil;
// Helper method to create C string copy
char* MakeStringCopy (const char* string)
{
if (string == NULL)
return NULL;
char* res = (char*)malloc(strlen(string) + 1);
strcpy(res, string);
return res;
}
#if c__plusplus
extern "C" {
#endif
const char* _GetUrl ()
{
if (delegateObject == nil)
delegateObject = [[NSApplicationDelegate alloc] init];
if (nsApplication == nil)
nsApplication = [[NSApplication alloc] init];
[nsApplication setDelegate:delegateObject];
return MakeStringCopy([[delegateObject getUrl] UTF8String]);
}
#if c__plusplus
}
#endif
If your application is crashing, you need to provide some information, such as a stack trace or error message so we can best help. I'm assuming the error you're receiving looks like this:
2016-03-06 10:07:14.388 test[5831:230418] *** Assertion failure in -[NSApplication init], /Library/Caches/com.apple.xbs/Sources/AppKit/AppKit-1404.34/AppKit.subproj/NSApplication.m:1980
2016-03-06 10:07:14.391 test[5831:230418] An uncaught exception was raised
2016-03-06 10:07:14.391 test[5831:230418] Creating more than one Application
You shouldn't create your own NSApplication object. Just use the system one by referencing [NSApplication sharedApplication].
Generally speaking, you shouldn't need an NSApplication (or NSApplicationDelegate) for a plugin, though. The program that's loaded you should already have one, and you don't want to mess with that. Just create a custom NSObject subclass to be your AppleEvent handler. You don't need NSApplication (or it's delegate) at all for this. Any object can be the target of an AppleEvent.
You can't use things like applicationDidFinishLaunching:withOptions: from a plugin in any case. It's too late. The application has long since launched. You'll need to add your AppleEvent handler in some function called from Unity. I'm not particularly familiar with Unity's plugin engine, so I don't know if there's a particular "load" function that gets called automatically (I don't see one in the sample code). You may have to call something yourself. It would have to occur after the plugin is loaded, but before the Get URL Apple Event happens (it's unclear what you expect to generate that).
Just curious what you're trying to pull off with this. I've never seen a protocol handler used this way.
Creation of NSApplication instance looks very suspicious. Normally you don't create it as it is a singleton by definition.
So instead of this:
if (nsApplication == nil)
nsApplication = [[NSApplication alloc] init];
you should have rather this (getting current NSApplication instance):
if (nsApplication == nil)
nsApplication = [NSApplication sharedApplication];
I made a tutorial on this, see Override app delegate in Unity for iOS and OSX (4/4) Inject code to Mach-O binary.
It uses code injection to set an Objective-C class to respond the corresponding Apple Event.
I have an iOS 9.0 app that I am trying to add In-App Purchase to; (I have never used IAP before and found some code online that will hopefully get me started).
I decided to make the code reside in a Category for maintenance simplicity (I have only used Categories once before). That said, I'm having problems with the actual structure of the Category. This is the .h file's code in the category:
#import "SettingsViewController.h"
#import <StoreKit/StoreKit.h>
#interface SettingsViewController (Purchases)
#end
#define kInAppPurchaseManagerProductsFetchedNotification
#"kInAppPurchaseManagerProductsFetchedNotification"
#interface InAppPurchaseManager : NSObject <SKProductsRequestDelegate> {
SKProduct *proUpgradeProduct;
SKProductsRequest *productsRequest;
}
#end
This is the .m code:
#import "SettingsViewController+Purchases.h"
#implementation SettingsViewController (Purchases)
- (void)requestProUpgradeProductData {
NSSet *productIdentifiers = [NSSet setWithObject:#"com.runmonster.runmonsterfree.upgradetopro" ];
productsRequest = [[SKProductsRequest alloc] initWithProductIdentifiers:productIdentifiers];
productsRequest.delegate = self;
[productsRequest start];
// we will release the request object in the delegate callback
}
#pragma mark -
#pragma mark SKProductsRequestDelegate methods
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response {
NSArray *products = response.products;
proUpgradeProduct = [products count] == 1 ? [[products firstObject] retain] : nil;
if (proUpgradeProduct)
{
NSLog(#"Product title: %#" , proUpgradeProduct.localizedTitle);
NSLog(#"Product description: %#" , proUpgradeProduct.localizedDescription);
NSLog(#"Product price: %#" , proUpgradeProduct.price);
NSLog(#"Product id: %#" , proUpgradeProduct.productIdentifier);
}
for (NSString *invalidProductId in response.invalidProductIdentifiers)
{
NSLog(#"Invalid product id: %#" , invalidProductId);
}
// finally release the reqest we alloc/init’ed in requestProUpgradeProductData
[productsRequest release];
[[NSNotificationCenter defaultCenter] postNotificationName:kInAppPurchaseManagerProductsFetchedNotification object:self userInfo:nil];
}
#end
The problem is the SKProduct and SKProductsRequest are not available to the .m file, which I'm sure is caused by the way I set up the .h file. Help would be greatly appreciated.
It looks like you're trying to use a category for an unsuitable purpose. In particular, it looks like your category is supposed to contain a property declaration. But, as the documentation says:
Categories can be used to declare either instance methods or class methods but are not usually suitable for declaring additional properties. It’s valid syntax to include a property declaration in a category interface, but it’s not possible to declare an additional instance variable in a category. This means the compiler won’t synthesize any instance variable, nor will it synthesize any property accessor methods. You can write your own accessor methods in the category implementation, but you won’t be able to keep track of a value for that property unless it’s already stored by the original class.
I have searched a lot but didn't find useful code or tutorial.
In my application, I have an mutable array which update in every 60 seconds.
The objects in array is being displayed by table view in multiple view controllers.
I want to reload table view automatically when only when values in array changes or updated.
For this, I want to add observer on mutable array i.e when values in array changes then it should call a particular method for e.g
-(void)ArrayUpdatedNotification:(NSMutableArray*)array
{
//Reload table or do something
}
Thanks in advance.
You can abstract the array into a data container class with accessor methods, and then use key-value observing to observe when the array that backs the container object is changed (you cannot use KVO on an NSArray directly).
A simple example of a class used as an abstraction on top of an array follows. You use its insertObject:inDataAtIndex: and removeObjectFromDataAtIndex: methods instead of directly accessing the with addObject: and removeObject:.
// DataContainer.h
#interface DataContainer : NSObject
// Convenience accessor
- (NSArray *)currentData;
// For KVC compliance, publicly declared for readability
- (void)insertObject:(id)object inDataAtIndex:(NSUInteger)index;
- (void)removeObjectFromDataAtIndex:(NSUInteger)index;
- (id)objectInDataAtIndex:(NSUInteger)index;
- (NSArray *)dataAtIndexes:(NSIndexSet *)indexes;
- (NSUInteger)countOfData;
#end
// DataContainer.m
#interface DataContainer ()
#property (nonatomic, strong) NSMutableArray *data;
#end
#implementation DataContainer
// We'll use automatic notifications for this example
+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)key
{
if ([key isEqualToString:#"data"]) {
return YES;
}
return [super automaticallyNotifiesObserversForKey:key];
}
- (id)init
{
self = [super init];
if (self) {
// This is the ivar which provides storage
_data = [NSMutableArray array];
}
return self;
}
// Just a convenience method
- (NSArray *)currentData
{
return [self dataAtIndexes:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, [self countOfData])]];
}
// These methods enable KVC compliance
- (void)insertObject:(id)object inDataAtIndex:(NSUInteger)index
{
self.data[index] = object;
}
- (void)removeObjectFromDataAtIndex:(NSUInteger)index
{
[self.data removeObjectAtIndex:index];
}
- (id)objectInDataAtIndex:(NSUInteger)index
{
return self.data[index];
}
- (NSArray *)dataAtIndexes:(NSIndexSet *)indexes
{
return [self.data objectsAtIndexes:indexes];
}
- (NSUInteger)countOfData
{
return [self.data count];
}
#end
The reason that we do this is so we can now observe changes made to the underlying array. This is done through Key Value Observing. A simple view controller that instantiates and observes a data controller is shown:
// ViewController.h
#interface ViewController : UIViewController
#end
// ViewController.m
#interface ViewController ()
#property (nonatomic,strong) DataContainer *dataContainer;
#end
#implementation ViewController
static char MyObservationContext;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Instantiate a DataContainer and store it in our property
_dataContainer = [[DataContainer alloc] init];
// Add self as an observer. The context is used to verify that code from this class (and not its superclass) started observing.
[_dataContainer addObserver:self
forKeyPath:#"data"
options:(NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew)
context:&MyObservationContext];
}
return self;
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
// Check if our class, rather than superclass or someone else, added as observer
if (context == &MyObservationContext) {
// Check that the key path is what we want
if ([keyPath isEqualToString:#"data"]) {
// Verify we're observing the correct object
if (object == self.dataContainer) {
NSLog(#"KVO for our container property, change dictionary is %#", change);
}
}
}
else {
// Otherwise, call up to superclass implementation
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Insert and remove some objects. Console messages should be logged.
[self.dataContainer insertObject:[NSObject new] inDataAtIndex:0];
[self.dataContainer insertObject:[NSObject new] inDataAtIndex:1];
[self.dataContainer removeObjectFromDataAtIndex:0];
}
- (void)dealloc
{
[_dataContainer removeObserver:self forKeyPath:#"data" context:&MyObservationContext];
}
#end
When this code runs, three changes to the data are observed by the view controller and logged to the console:
KVO for our container property, change dictionary is {
indexes = "<NSIndexSet: 0x8557d40>[number of indexes: 1 (in 1 ranges), indexes: (0)]";
kind = 2;
new = (
"<NSObject: 0x8557d10>"
);
}
KVO for our container property, change dictionary is {
indexes = "<NSIndexSet: 0x715d2b0>[number of indexes: 1 (in 1 ranges), indexes: (1)]";
kind = 2;
new = (
"<NSObject: 0x71900c0>"
);
}
KVO for our container property, change dictionary is {
indexes = "<NSIndexSet: 0x8557d40>[number of indexes: 1 (in 1 ranges), indexes: (0)]";
kind = 3;
old = (
"<NSObject: 0x8557d10>"
);
}
While this is somewhat complex (and can get much more involved), this is the only way to be notified automatically that a mutable array's contents were changed.
What is can do is - After updating your Array send a Notification (NSNotificationCenter) and this notification will be received by all the controllers. On receiving the notificaiton the controller should do [tableview reloaddata].
Code example:
// Adding an observer
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(updateTable:) name:#"arrayUpdated" object:nil];
// Post a notification
[[NSNotificationCenter defaultCenter] postNotificationName:#"arrayUpdated" object:nil];
// the void function, specified in the same class where the Notification addObserver method has defined
- (void)updateTable:(NSNotification *)note {
[tableView reloadData];
}
If you want to use shiny blocks you can do this
// Create an instance variable for your block holder in your interface extension
#property (strong) id notificationHolder;
// Listen for notification events (In your TableView class.
self.notificationHolder = [[NSNotificationCenter defaultCenter] addObserverForName:#"NotificationName"
object:nil
queue:[NSOperationQueue mainQueue]
usingBlock:^(NSNotification *note) {
NSLog(#"Received notification");
}];
Then in dealloc (or when you don't use it anymore)
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self.notificationHolder];
}
Then in some other class
// Send a notification
[[NSNotificationCenter defaultCenter] postNotificationName:#"NotificationName" object:nil];
Ask if something is not clear! Hope it helps!
EDIT DUE TO COMMENT
The "YourEvent" is the name of the notification, this means that you can name it to whatever you want. (Perhaps "UpdateArrayNotification could be a good name?)
Something to think about: Note that you can have several observers for the same notification. This means that one 'post' will be snapped up by all observers.
I have two viewControllers accessing a NSNumber on the AppDelegate. One of them can see it, and the other can't. I am totally confused by this.
The one with the problem has this code.
AppDelegate *dataStore = (AppDelegate *)[[UIApplication sharedApplication] delegate];
dataStore.downHUD = [NSNumber numberWithFloat:(float)progress];
The other has this.
AppDelegate *dataStore = (AppDelegate *)[[UIApplication sharedApplication] delegate];
dataStore.downHUD = [NSNumber numberWithFloat:(float)0];
Both imports the AppDelegate in the .m file but I end up with
Property 'downHUD' not found on object of type 'AppDelegate *'
with the first one.
Anyone that can help me see what's wrong?
I copied and pasted a lot of code into the AppDelegate by mistake, that has been corrected. Is there some sort of link that could got broken?
Maybe there is no such property in your AppDelegate class.
In your AppDelegate.h under interface declaration you need to have
#property (nonatomic, retain) NSNumber* downHUD;
In your AppDelegate.m under implementation declaration you need to have
#synthesize downHUD;
In this manner you define accessors (getter and setter) to access an instance variable called downHUD. This accessors are public and you can do
dataStore.downHUD = ...
Maybe this could be the error. But without AppDelegate code it's difficult to understand what is going on.
Hope it helps.
Edit:
It's no a good strategy to access data within the application delegate. I suggest you to use singletons like singletons-appdelegates-and-top-level.html
Edit 2:
#interface SingletonModel : NSObject {
NSNumber* downHUD_;
}
+ (id)sharedInstance;
#property (nonatomic, retain) NSNumber* downHUD;
#end
#import "SingletonModel.h"
#implementation SingletonModel
#synthesize downHUD = downHUD_;
static SingletonModel *sharedInstance = nil;
+ (SingletonModel *)sharedInstance {
if (sharedInstance == nil) {
sharedInstance = [[super allocWithZone:NULL] init];
}
return sharedInstance;
}
- (id)init
{
self = [super init];
if (self) {
}
return self;
}
-(void)dealloc
{
[super dealloc];
}
+ (id)allocWithZone:(NSZone*)zone {
return [[self sharedInstance] retain];
}
- (id)copyWithZone:(NSZone *)zone {
return self;
}
- (id)retain {
return self;
}
- (NSUInteger)retainCount {
return NSUIntegerMax;
}
- (oneway void)release {
}
- (id)autorelease {
return self;
}
#end
To set your model:
SingletonModel* model = [SingletonModel sharedInstance];
model.downHUD = ...
To read your model:
SingletonModel* model = [SingletonModel sharedInstance];
NSNumber* n = model.downHUD;
For other info read iphone-code-snippet-the-singleton-pattern and singleton-classes. About Singletons you can find in apple documentation at Cocoa Fundamentals Guide and at Singleton.
Your two view controllers may refer to different AppDelegate code. Even though the Xcode Project Navigator shows only one set of AppDelegate files, and Jump to Definition shows the same AppDelegate class definition in both cases, one of the view controllers may actually have different delegate code.
I had this very problem with a delegate class definition, where some member variables were only available in one view controller but not in the other.
Right-click on each ViewController.m file in the Project Navigator, and use Show in Finder to see whether they are both in the same location as the desired AppDelegate files. If not, move the VC files to the correct location and add them to the project.
If you're using expo this is how to get rid of the bug.
"reactDelegate" was introduced in sdk 44.0.0 so if you're using sdk 43.0.0
here's is how your code should look in your "AppDelegate.m" from line 35 - 46.
RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions];
RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge moduleName:#"main" initialProperties:nil];
rootView.backgroundColor = [UIColor whiteColor];
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
UIViewController *rootViewController = [UIViewController new];
rootViewController.view = rootView;
self.window.rootViewController = rootViewController;
[self.window makeKeyAndVisible];
[super application:application didFinishLaunchingWithOptions:launchOptions];
If you have imported the header files then it should work. Did you try to clean and re-build your project? You can do that with CMD + Shift + K (or by selecting Clean from the Project menu).