NSMutableDictionary & dispatch_async Native Objects C# equivalent or similar

EstebanAlvaEstebanAlva USMember
edited March 2017 in Xamarin.iOS

I'm fairly new to xamarin and mobile dev. I'm currently migrating geofire library by Firebase to xamarin.ios. I came across the following objective-c code and trying to figure out what is actually doing? and its c# equivalent:

@property (nonatomic, strong) NSMutableDictionary *keyEnteredObservers;
...

GFQueryLocationInfo *info = self.locationInfos[key];

 [self.keyEnteredObservers enumerateKeysAndObjectsUsingBlock:^(id observerKey,
                                                                      GFQueryResultBlock block,
                                                                      BOOL *stop) {
            dispatch_async(self.geoFire.callbackQueue, ^{         // the callbackQueue is of type @property (nonatomic, strong) dispatch_queue_t callbackQueue;
                block(key, info.location);
            });
        }];

for the callbackQueue I am currently using type BlockingCollection

Any help is greatly appreciated.

Best Answer

  • JGoldbergerJGoldberger US Xamurai
    Accepted Answer

    @property (nonatomic, strong) NSMutableDictionary *keyEnteredObservers; is just a class member variable with type NSMutableDictionary. C# Equivalent:

    NSMutableDictionary keyEnteredObservers; 
    

    GFQueryLocationInfo *info = self.locationInfos[key] is juts assigning the value of self.locationInfos[key] to the info local variable of type GFQueryLocationInfo. C# Equivalent:

    GFQueryLocationInfo info = this.locationInfos[key]; // `self` in Obj-C is the same as `this` in C#.
    

    The next is a bit more complex. An Obj-C block is similar to a delegate (or lambda) in that is is a block of code that can be passed as an argument or held in a field or property. I will try to break it down:

     [self.keyEnteredObservers enumerateKeysAndObjectsUsingBlock:^(id observerKey,
                                                                          GFQueryResultBlock block,
                                                                          BOOL *stop) 
    

    So the above is calling the method enumerateKeysAndObjectsUsingBlock (likely this method on NSMutableDictionary is named differently in Xamarin, in fact I did not find anything analogous that takes a delegate but there are methods that get you all of the keys and/or all of the values in the NSMutableDictionary in Xamarin, I am just not sure what the intent here is) on self.keyEnteredObservers, or this.keyEnteredObservers in C# , and passing in the code block as an argument. The code block gets parameters id observerKey, GFQueryResultBlock block, and BOOL *stop passed in.

    {
                dispatch_async(self.geoFire.callbackQueue, ^{         // the callbackQueue is of type @property (nonatomic, strong) dispatch_queue_t callbackQueue;
                    block(key, info.location);
                });
            }];
    

    The above I really do not know how to translate directly. What seems to be happening is that they are setting up a queue of async methods that are executing the block of code, GFQueryResultBlock block that is passed in as a parameter to the above Obj-C block. What you might want to do here is just do the same with C# async methods. However I am not sure how you would handle the GFQueryResultBlock block, as that is an Obj-C block being passed in.

    All that said, can you try instead making a binding library to the geofire library? See our docs on making Obj-C bindings for Xamarin:
    https://developer.xamarin.com/guides/ios/advanced_topics/binding_objective-c/
    https://developer.xamarin.com/guides/cross-platform/macios/binding/objective-c-libraries/

Answers

  • JGoldbergerJGoldberger USMember, Forum Administrator, Xamarin Team, University Xamurai
    Accepted Answer

    @property (nonatomic, strong) NSMutableDictionary *keyEnteredObservers; is just a class member variable with type NSMutableDictionary. C# Equivalent:

    NSMutableDictionary keyEnteredObservers; 
    

    GFQueryLocationInfo *info = self.locationInfos[key] is juts assigning the value of self.locationInfos[key] to the info local variable of type GFQueryLocationInfo. C# Equivalent:

    GFQueryLocationInfo info = this.locationInfos[key]; // `self` in Obj-C is the same as `this` in C#.
    

    The next is a bit more complex. An Obj-C block is similar to a delegate (or lambda) in that is is a block of code that can be passed as an argument or held in a field or property. I will try to break it down:

     [self.keyEnteredObservers enumerateKeysAndObjectsUsingBlock:^(id observerKey,
                                                                          GFQueryResultBlock block,
                                                                          BOOL *stop) 
    

    So the above is calling the method enumerateKeysAndObjectsUsingBlock (likely this method on NSMutableDictionary is named differently in Xamarin, in fact I did not find anything analogous that takes a delegate but there are methods that get you all of the keys and/or all of the values in the NSMutableDictionary in Xamarin, I am just not sure what the intent here is) on self.keyEnteredObservers, or this.keyEnteredObservers in C# , and passing in the code block as an argument. The code block gets parameters id observerKey, GFQueryResultBlock block, and BOOL *stop passed in.

    {
                dispatch_async(self.geoFire.callbackQueue, ^{         // the callbackQueue is of type @property (nonatomic, strong) dispatch_queue_t callbackQueue;
                    block(key, info.location);
                });
            }];
    

    The above I really do not know how to translate directly. What seems to be happening is that they are setting up a queue of async methods that are executing the block of code, GFQueryResultBlock block that is passed in as a parameter to the above Obj-C block. What you might want to do here is just do the same with C# async methods. However I am not sure how you would handle the GFQueryResultBlock block, as that is an Obj-C block being passed in.

    All that said, can you try instead making a binding library to the geofire library? See our docs on making Obj-C bindings for Xamarin:
    https://developer.xamarin.com/guides/ios/advanced_topics/binding_objective-c/
    https://developer.xamarin.com/guides/cross-platform/macios/binding/objective-c-libraries/

  • EstebanAlvaEstebanAlva USMember

    @JGoldberger

    Thank you for the response. More or less here's what I got so far :

    var coll = keyEnteredObservers.KeysForObject(info).Cast<GFQueryLocationInfo>();
                    foreach (var data in coll)
                    {
                        var nonCapturedLocation = data.Location.Copy(); // location = CLLocation, do not capture entire dictionary by ref in lambda
                        this.GeoFire.CallbackQueue.Add(() =>
                        {
                            block(key, (CLLocation)nonCapturedLocation);
                        });
                    }
    

    I didn't mentioned but the binding library approach was my first attempt, I successfully created the library which I'm currently calling from a xamarin.ios project, but while testing, I found out sometimes some methods won't run or even fail this doesn't happen all the time. I find troubleshooting under this scenario a bit more difficult (at least for beginners) so I decided to port the library (like 70% ported already).

  • MaximeEMaximeE Member

    Hi Esteban,
    Did you translate GeoFire successfuly ? Is there a place where I could get your code please like GitHub or a package nuget ?

    I encounter problems to use GeoFire in Xamarin.Forms project : stackoverflow.com/q/51078938/4965913

Sign In or Register to comment.