Skip to content
This repository was archived by the owner on Feb 22, 2023. It is now read-only.

Conversation

@amirh
Copy link
Contributor

@amirh amirh commented Jan 30, 2019

Platform implementation of the method channel API for adding and removing JavaScript channels.

#1116 adds the Dart side support, the current PR will land first.

flutter/flutter#24837

@amirh amirh requested a review from mklim January 30, 2019 20:25
@amirh amirh requested a review from cyanglaz January 30, 2019 22:01
Copy link
Contributor

@cyanglaz cyanglaz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Logic looks clean and I didn't find major issues. Added some thoughts about certain code.

_javaScriptChannelNames = [[NSMutableSet alloc] init];

WKUserContentController* userContentController = [[WKUserContentController alloc] init];
if (![args[@"javascriptChannelNames"] isKindOfClass:[NSNull class]]) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe check if [args[@"javascriptChannelNames"] is not nil as well?

if(![args[@"javascriptChannelNames"] && args[@"javascriptChannelNames"] isKindOfClass:[NSNull class]]) 

Or if we can be super careful here and add an NSArray type check as well, and then we can drop the NSNull check. Which i think is a better approach. I found some of the code in this classes that I did can be optimized this way as well.

Like below:

// initialize the object to be nil to avoid redundant NSNull check later.
NSArray * javaScriptChannelNames = nil;
// Check if the dictionary value is array class includes a nil check and a NSNull check as well as an array type check, which is cleaner.
if ([args[@"javascriptChannelNames"] isKindOfClass:[NSArray class]]) {
   javaScriptChannelNames = args[@"javascriptChannelNames"];
}
// Since we initialized `javaScriptChannelNames` to be nil, this value can either be nil or an array. hence the only nil check, dropping the NSNull check.
if (javaScriptChannelNames) {
   [_javaScriptChannelNames addObjectsFromArray:javaScriptChannelNames];
   [self registerJavaScriptChannels:_javaScriptChannelNames controller:userContentController];
}

can even make it better if logically we do not want to update the _javaScriptChannelNames and register at all when the [args[@"javascriptChannelNames"] is an empty array.

if (javaScriptChannelNames.count > 0) {
   [_javaScriptChannelNames addObjectsFromArray:javaScriptChannelNames];
   [self registerJavaScriptChannels:_javaScriptChannelNames controller:userContentController];
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks good point about checking for NSArray.
I believe [foo isKindOfClass:[NSArray class]] doesn't evaluate to true if foo is nil, so I guess this check covers all cases?

As for short-circuiting when the array is empty, I would think the performance gain is insignificant in this case(which doesn't happen many times) so I would choose a little simpler code in this case.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sounds good. and YES [foo isKindOfClass:[NSArray class]] covers all cases.

FlutterMethodChannel* _channel;
NSString* _currentUrl;
// The set of registered JavaScript channel names.
NSMutableSet* _javaScriptChannelNames;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can use a property and be lazily initialized.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the benefit over doing it this way?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lazy initializing the set can save memory (insignificant tho); In the case of this class, benefit is minimum since the set is used right after class initialization; I'm ok with not lazy initializing here.
Just a thought, if we do move the code [_javaScriptChannelNames addObjectsFromArray:javaScriptChannelNames]; out of the initiWithFrame method, then we can think about lazy initialize the _javaScriptChannelNames to save some memory.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can't move it out of initWithFrame, as a set of JavaScript channels is passed with the creation params... we immediately need this field initialized...

Copy link
Contributor

@mklim mklim left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some minor nits from me, otherwise LGTM modulo any feedback from @cyanglaz .

Is it worth adding unit tests for the obj-c side on this as well?

Copy link
Contributor Author

@amirh amirh left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks.

@cyanglaz PTAL

_javaScriptChannelNames = [[NSMutableSet alloc] init];

WKUserContentController* userContentController = [[WKUserContentController alloc] init];
if (![args[@"javascriptChannelNames"] isKindOfClass:[NSNull class]]) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks good point about checking for NSArray.
I believe [foo isKindOfClass:[NSArray class]] doesn't evaluate to true if foo is nil, so I guess this check covers all cases?

As for short-circuiting when the array is empty, I would think the performance gain is insignificant in this case(which doesn't happen many times) so I would choose a little simpler code in this case.

FlutterMethodChannel* _channel;
NSString* _currentUrl;
// The set of registered JavaScript channel names.
NSMutableSet* _javaScriptChannelNames;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the benefit over doing it this way?

@amirh
Copy link
Contributor Author

amirh commented Jan 31, 2019

@mklim for unit tests - ok if I wait to see in what form our e2e plugin test harness manifests?

@mklim
Copy link
Contributor

mklim commented Jan 31, 2019

@mklim for unit tests - ok if I wait to see in what form our e2e plugin test harness manifests?

Totally. Just a suggestion.

@cyanglaz
Copy link
Contributor

download

@amirh
Copy link
Contributor Author

amirh commented Jan 31, 2019

Looks like the build-ipas task has successfully completed on Cirrus but Github didn't pick that up for some reason.. going to land on "yellow" because this is really green.

@amirh amirh merged commit aedcc6f into flutter:master Jan 31, 2019
@amirh amirh deleted the webview_js_ios branch January 31, 2019 01:00
andreidiaconu pushed a commit to andreidiaconu/plugins that referenced this pull request Feb 17, 2019
iOS implementation of the method channel API for adding and removing JavaScript channels.

flutter#1116 adds the Dart side support, the current PR will land first.

flutter/flutter#24837
andreidiaconu added a commit to andreidiaconu/plugins that referenced this pull request Feb 17, 2019
@"Can't send a message to an unitialized JavaScript channel.");
NSDictionary* arguments = @{
@"channel" : _javaScriptChannelName,
@"message" : [NSString stringWithFormat:@"%@", message.body]
Copy link

@KennedyChiang KennedyChiang Oct 2, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if native send json string back flutter, it will got a string that hard to serialized like below.

{
    action = do_somethineg;
    data =     {
        key_1 = some_info;
        key_2 = "some_info";
    };
    timestamp = 1570005270866;
    token = "jwt_token";
}

is it possible to add code to serialize NSDictionary and NSArray before send message.body:

- (NSString *)messageFromBody:(NSString *)body {
    NSString *message;
    if ([body isKindOfClass:[NSDictionary class]] ||
        [body isKindOfClass:[NSArray class]]) {
        NSData *jsonData = [NSJSONSerialization dataWithJSONObject:body
                                                           options:NSJSONWritingPrettyPrinted
                                                             error:nil];
        message = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
    }
    if (message.length <= 0) {
        message = [NSString stringWithFormat:@"%@", body];
    }

    return message;
}

...

NSDictionary* arguments = @{
    @"channel" : _javaScriptChannelName,
    @"message" : [self messageFromBody: message.body]
  };

Thanks!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you mind filing this as an issue?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ya, i'm created HERE

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants