4 ms·
Thanks for the info. Is your obj-c good enough to explain what Ionic is actually doing in the file that triggers the problem? https://github.com/driftyco/ionic
by Sujan 11y ago
Thanks for the info.
Is your obj-c good enough to explain what Ionic is actually doing in the file that triggers the problem? https://github.com/driftyco/ionic-plugin-keyboard/blob/master/src/ios/UIWebViewExtension.m https://github.com/driftyco/ionic-plugin-keyboard/blob/maste... Are they really doing it in a 'bad' way?
- Sephiroth87 11y agoThey are accessing an internal subview of UIWebView to hide the inputAccessoryView from the keyboard. When you use a normal view that uses the keyboard (UITextView, etc.), you can set it to any custom view you want, but because UIWebView is supposed to be used to display websites and not full apps, that property is not accessible, so they access the internal hierarchy of the webView, find the actual view that triggers the keyboard and modify it's property.
- masklinn 11y ago> They are accessing an internal subview An internal undocumented subview which is part of a private framework, aka they're digging into UIWebView's private implementation details[0] in order to do whatever they want to. Which may or may not be possible without that, but sadly that's besides the point. It's interesting to see that below the now-offending bits are even hackier bits which are currently commented "until [they] know it's app store safe" [0] which can't be formally private due to the architecture of Cocoa and objective-c I guess
- Sujan 11y agoThanks for the explanatin to you both! So removing this code would mean that all Ionic apps would display this thing here on all keyboards: https://raw.githubusercontent.com/clrung/PrevNextInputAccessoryView/master/README%20images/nativePrevNext.png https://raw.githubusercontent.com/clrung/PrevNextInputAccess... Did I get this right?
- Sephiroth87 11y agoYes, that's the one
- nogbit 11y agoI just wasted 10 minutes of my life to get to this which should be upvoted.
- afoxtron 11y agoJust wanted to chime in and say that's actually really bad code. Accessing a views subviews like that and looking for a specific instance of a class is just begging for a crash.
- EugeneOZ 11y agoAfter reading lines 5 and 6 it does not surprising me.
- kennywinker 11y agoCounterpoint: they have all the nil checks needed for this to fail silently if no UIWebBrowserView is found. It's a fragile implementation in that it might stop working if Apple changes the internal implementation of UIWebView, but it doesn't look like it'll crash because of it. That said, clearly it's not good for Apple to have large numbers of apps relying on private implementation details. It hamstrings them when it comes time to improve the OS. That translates to a harm for users. I'm not advocating for this hack. I wonder if this could be avoided by switching to WKWebView?
- masklinn 11y ago> I wonder if this could be avoided by switching to WKWebView? Nope, the keyboard UI is pretty much the same and the API does not provide any more support for disabling the accessory bar.
- Benjammer 11y ago- (UIView *)hackishlyFoundBrowserView { UIScrollView *scrollView = self.scrollView; UIView *browserView = nil; for (UIView *subview in scrollView.subviews) { if ([NSStringFromClass([subview class]) hasPrefix:@"UIWebBrowserView"]) { browserView = subview; break; } } return browserView; } This iterates through the view hierarchy until it finds a view with class name starting with "UIWebBrowserView". - (void)ensureHackishSubclassExistsOfBrowserViewClass:(Class)browserViewClass { if (!hackishFixClass) { Class newClass = objc_allocateClassPair(browserViewClass, hackishFixClassName, 0); IMP nilImp = [self methodForSelector:@selector(methodReturningNil)]; class_addMethod(newClass, @selector(inputAccessoryView), nilImp, "@@:"); objc_registerClassPair(newClass); hackishFixClass = newClass; } } This creates a new class at runtime that is a subclass of UIWebBrowserView (not accessible at compile time), but with the getter method for the inputAccessoryView overridden to return nil instead of the accessory view. - (void) setHackishlyHidesInputAccessoryView:(BOOL)value { UIView *browserView = [self hackishlyFoundBrowserView]; if (browserView == nil) { return; } [self ensureHackishSubclassExistsOfBrowserViewClass:[browserView class]]; if (value) { object_setClass(browserView, hackishFixClass); } else { Class normalClass = objc_getClass("UIWebBrowserView"); object_setClass(browserView, normalClass); } [browserView reloadInputViews]; } This takes the UIWebBrowserView object and overrides it's class at runtime with the aforementioned subclass with the nil accessory view getter. (Side note, this could also maybe have been done by swizzling out the method on the internal class instead of subclassing at runtime)
- ryanbertrand 11y agoI agree with the swizzling preference. I wonder if they can go around the Apple issue by using something like https://github.com/UrbanApps/UAObfuscatedString https://github.com/UrbanApps/UAObfuscatedString to generate the "UIWebBrowserView" string. static NSString browserViewClassName = Obfuscate.U.I.W.e.b.B.r.o.w.s.e.r.V.i.e.w; - (UIView *)hackishlyFoundBrowserView { UIScrollView *scrollView = self.scrollView; UIView *browserView = nil; for (UIView *subview in scrollView.subviews) { if ([NSStringFromClass([subview class]) hasPrefix:browserViewClassName]) { browserView = subview; break; } } return browserView; } This generates the "UIWebBrowserView" string at runtime which may get around Apple's checks.