EA showBluetoothAccessoryPicker not showing in SwiftUI - ios

I have created a small program that needs to connect to an external accessory. I have been able to do so successfully with UIKit and the EA framework.
The problem I am having is that I have a SwiftUI based app that needs to use the External Accessory but when I call the showBluetoothAccessoryPicker function, the picker window does not show.
What I have tried so far:
I have read that you need to have the app delegate with
var window: UIWin
setup correctly to see the picker window. So I created a class
class AppDelegate: UIApplicationDelegate
{
var window: UIWindow?
}
I then associated the app delegate with my swiftui "App" struct like this:
#main
struct POC: App {
#UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
var body: some Scene {
WindowGroup {
TabSelectionView()
}
}
}
Nevertheless, when I call showBluetoothAccessoryPicker the picker window does not show.
I see the following in the output log:
2021-09-20 13:16:31.804796-0500 POC[2150:1514447] IAPDHasLaunched: kIAPAvailableNotification iapdAvailableState 0 -> 0
2021-09-20 13:16:31.805791-0500 POC[2150:1514447] IAP2DHasLaunched: kIAP2AvailableNotification iap2dAvailableState 0 -> 0
2021-09-20 13:16:31.807219-0500 POC[2150:1514447] -[EAAccessoryManager _initFromSingletonCreationMethod] isRunningOnMac
This log appears correct and matches the output from a working demo app that uses UIKit. It's just that the picker window does not show.
Does anybody know what needs to be done to get the picker window to appear correctly for SwiftUI apps? Is there any SwiftUI examples of connecting to an External Accessory that anybody could point me towards?
Thanks in advance for any help.

UPDATE: It's also broken in UIKit! Feedback reported as FB9856371.
Apparently the new UIScene-based mechanism kills it (and since SwiftUI is based on the new scene-based mechanism, it only looks like it's a problem with SwiftUI).
If you roll back to the UIApplicationDelegate-based lifecycle, it works again.
OLD ANSWER:
It's just broken in SwiftUI right now.
Further experimentation shows that the EAExternalAccessory picker seems to query the UIApplicationDelegate's window property to find out where to display itself in.
Alas, that being an optional property, which is not implemented in the SwiftUI's version of the AppDelegate, it only gets nil and does not show up.
It also seems that the #UIApplicationDelegateAdaptor approach does not work in this case, because it does not really act as a stand-in for the real app delegate, but rather gets the methods forwarded.
A way to work around this is to use the UIKit application lifecycle and present your SwiftUI app via the UIHostingController.
It might also be possible to hack this via method swizzling, but if so, I don't know how.
Please open a bug report.

Related

Using undo manager with mac catalyst

I'm working on adding a mac build target to an iOS application. I have the basics working and would like to implement undo/redo functionality.
In a traditional AppKit application you get this for free. When you create a new application, the prepopulated main menu has the Edit item and Undo and Redo under it. The view controller has an undoManager, you just registerUndo on it (preferably setActionName as well) and everything works. Hotkeys, menu item title changes and state changes (disable redo when at the top of the stack etc.) all work out of the box.
Adding a catalyst build target to an iOS project also creates a default menu with a top level Edit menu and Undo/Redo menu items. These do not seem to adopt the built-in functionality. Do I really need to manually recreate all that is free with AppKit or is there something I'm missing?
Are you calling becomeFirstResponder() on the view that you're registering undo events? This was tripping me up for awhile.
I wasn't able to get action names to show up in the Catalyst Edit menu, but I did see the Undo/Redo stack working properly from UIKit code without having to dip into AppKit.
Registering undo on NSWindow.undoManager works for me. But I had to use a hidden/private API to access the NSWindow instance using Dynamic library:
let nsWindow = Dynamic.NSApplication.sharedApplication.delegate.hostWindowForUIWindow(view.window)
let undoManager: UndoManager? = nsWindow.undoManager
I was able to get this working in my Catalyst app only by calling becomeFirstResponder() in viewDidAppear and no sooner. I was originally calling it in viewDidLoad and also tried viewWillAppear, neither of which correctly register my undo manager with macOS.
Relevant view controller sample code:
class MyViewController: UIViewController {
override var undoManager: UndoManager? {
return myCustomUndoManager
}
override var canBecomeFirstResponder: Bool {
true
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
becomeFirstResponder()
}
}

Why does adding iOS AdMobs GADBannerView cause sigabrt in AppDelegate (Swift)?

I'm having trouble getting AdMob advertising working in my iOS app.
I'm getting a specific error which I will show you below, but first here is what I've tried.
Here's What I've Tried
Once I got the error in my main app I retrieved the official BannerExample and got it running successfully. BannerView example app runs and I see the ad appear in the app while it's running on my iPhone 5s simulator.
I've done all manner of pod update to insure I have the exact new versions of all required packages and keep in mind that the app builds with no problems.
I've debugged it down to a single line of code that I will show you below.
Now that you know I've done quite a bit of work, take a look at what is happening.
The App Crash
The exact error is :
CYaPass[13155:433036] [MC] Reading from private effective user
settings. Google Mobile Ads SDK version: afma-sdk-i-v7.15.0 2016-12-04
14:37:26.046 CYaPass[13155:432944] -[UIView loadRequest:]:
unrecognized selector sent to instance 0x7f8bf4546090 2016-12-04
14:37:26.055 CYaPass[13155:432944] *** Terminating app due to uncaught
exception
This is not the normal sigabrt on AppDelegate error.
I have tried all the normal things related to StoryBoard and here is an image which shows you I don't have any undefined items that are blowing this up. Plus, I'll show you that when i remove the one line of AdMob code the app will run normally.
You can see that I do have two outlets defined on the UIView that I'm attempting to use for the BannerView. That's so I could test it when I remove the one line of AdMob code.
You can also see those outlets on the SettingsController
You can see them in my actual controller code which looks like the following:
#IBOutlet weak var AdMobs: GADBannerView!
#IBOutlet weak var SuperView: UIView!
func loadAd(){
AdMobs.adUnitID = "ca-app-pub-3940256099942544/2934735716"
AdMobs.rootViewController = self
AdMobs.load(GADRequest())
SuperView.backgroundColor = UIColor.black;
}
I separated those lines into the loadAd() func so I could test them from different locations, but it's the same code that the official BannerExample uses and calls in viewDidLoad() func.
Small Test On UIView
Then, you see I also do a little work on the exact same UIView (SuperView) where I set the backgroundColor to black.
That was to convince myself that it wasn't just becuase I'd attached an outlet to that UIView. But that code works fine, if I simply comment out the three lines before it.
Here, I'll run it and show you the output. Just before I do that, here's my AppDelegate application constructor which doesn't change. It is really a copy of the exact official BannerExample:
import Firebase
import UIKit
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate{
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
FIRApp.configure()
GADMobileAds.configure(withApplicationID: "ca-app-pub-myapp-hiddenforpublicConsumption")
return true
}
Now, I'll change change loadAd() to look like the following and run again:
func loadAd(){
//AdMobs.adUnitID = "ca-app-pub-3940256099942544/2934735716"
//AdMobs.rootViewController = self
//AdMobs.load(GADRequest())
SuperView.backgroundColor = UIColor.black;
}
Obviously, the only change is that I commented those three lines. Here's what it looks like when it runs.
Tabbed View Application
By the way, this is a tabbed app. So the app comes up, I click on the Settings tab and you can see that the altered code in loadAd() runs and turns the background of the UIView black.
Main Point : Specific Code Causes Problem
Now, let me show you that if I uncomment just that first line of Banner Ad code then the app crashes in the AppDelegate.
Here's the altered loadAd() again:
func loadAd(){
AdMobs.adUnitID = "ca-app-pub-3940256099942544/2934735716"
//AdMobs.rootViewController = self
//AdMobs.load(GADRequest())
SuperView.backgroundColor = UIColor.black;
}
Again, you can see that I've only uncommented the one line that sets the adUnitId on the GADBannerView. This is the exact same code that the BannerExample uses. It even uses the same test unitId which is free for everyone's testing. Also, even if you change that unitId to a invalid value the app does not crash, the ad just doesn't show up.
Here's The Result
Debugging Shows Exact Line of Code
I can step into the code and it is exactly on that loadAd() line we just added when this error occurs (shown below for clear reference):
AdMobs.adUnitID = "ca-app-pub-3940256099942544/2934735716"
Only Difference : Tabbed View -- Is That the Issue?
The only effective difference is that my app is based upon the tabbedview.
Does anyone know why this occurs?
I've been trying everything for almost 2 days now. Any help is appreciated.
Oh, the agony...I finally found the answer.
First of all, here you can see I've gained success.
Type of View Must Be Changed
If you go to storyboard and examine that UIView you will see it looks like the following:
However, it kind of makes sense that because it is not a GADBannerView that the error we got looks like:
[UIView setAdUnitID:]: unrecognized selector sent to instance
That was the clue that got me to the answer.
You have to change the value to a GADBannerView (just type it in the box over UIView):
I also confirmed that in the official example that the type is set properly there.
That's about 36 hours I'll never get back.
I missed that step in the initial documentation so this is "error between chair and keyboard".
If anyone else has this same problem and doesn't know what's going on, I had a different problem with a similar error.
Go to the View for the ad, and go down to where it says Label. Change that to your view's name, it worked for me.
Also make sure module is empty, and inherit module from target is unticked.

JSQMessages keyboard not pushing up view

When I go to type a message into my JSQMessagesViewController, the view is not being pushed up when the keyboard appears.
I can't send a message and I can't see the message I'm typing.
I don't even see any code in my VC that would be causing this / that would have changed this so I don't know where to start looking or what code to supply. I've downloaded examples using JSQMessages and I feel like this is just built in functionality behind the scenes.
Any ideas?
If you have viewDidAppear/viewDidDissapear being used you have to use
super.viewDidAppear(true)
and
super.viewDidDissapear(true)
also called respectively.
Adding those in fixed the issue.

Rendering UIView in Swift with #IBDesignable, crash

I have a graph built in Paintcode that I am showing in a UIView. It works fine in the App but will not render in storyboard. I have read a host of different posts about overriding init frame, and init coder. However, I can't find a specific example that helps me with my problem, by showing the practical use of these overrides.
The graph takes 14 variables to produce a bar chart. Here is the class code
import UIKit
#IBDesignable
class graphRisk: UIView {
override func drawRect(rect: CGRect) {
StyleKitGraph.drawRiskGraph(ehsScale: ehsScale, ciScale: ciScale, stratScale: stratScale, qaScale: qaScale, delScale: delScale, leadScale: leadScale, peopScale: peopScale, hrmScale: hrmScale, pmScale: pmScale, costScale: costScale, finScale: finScale, assScale: assScale, itsScale: itsScale, engScale: engScale)
}
}
It looks good in the App, and it is no big deal if it doesn't render as the variables are coded, and the design is fine, however, I removed the #IBDesignable tag and it still errors the same.
Finally, how do I get the UIView to update when I enter the view controller it resided in?
So the questions are;
1 How do I get render to work for this code example?
2 Is there a way to not render the view, and stop the error that way?
3 Do I need to worry about the error as the App runs fine?
4 How do I get the UIView to refresh. What is the syntax for .setNeedsDisplay() etc? I tried lots of things and none updated or reloaded or refreshed.
As always I truly welcome all help, and thank you.
Not sure what version of Xcode you are using. I am using Xcode 6.4.
I, like you, have researched for days on many websites and tried many solutions. None of them worked. At last, I removed the #IBDesignable to my custom UI class. Not only did the error went away, the instance of the custom UI class previewed fine in Main.storyboard.
Perhaps, in the new development of Xcode, #IBDesignable is no longer necessary.

Don't want to dismiss popover in Titanium when user touches outside of the popover

I'm just started working in Titanium. And now working on popover in titanium, using the following code.
var myPopover = Ti.UI.iPad.createPopover({ height: 150, width: 150 });
popover.add(view);
popover.show({ view: button });
My issue is when user touches outside of the popover the popover is dismissing (default behaviour), I don't want that.
In iOS there is a delegate method called: - (BOOL)popoverControllerShouldDismissPopover:(UIPopoverController *)popoverController . If we don't want to dismiss the popover when user touches outside just return NO, is there any way to do this in Titanium ?
I read the Titanium.UI.iPad.Popover documentataion, but couldn't find any such methods.
Please help me.
Thanks in advance.
Unfortunately without significant work yourself you won't be able to in the short-term.
Some tips:
Don't trust the api docs to have everything in. Instead either look through the SDK in Github or find the SDK on your system and search through the iOS (in the iPhone folder) classes for this method.
If you go to appcelerator's JIRA account, you can request this to be implemented. Go to the Q&A and make some noise about it to get other people to watch the JIRA issue so it is prioritised by Appcelerator.
You could write a module that extends the popover functionality, in fact with modules you can extend anything and create the javascript bridge yourself so you can access any properties or anything you want. There's guides for that on the Wiki..
Happy hunting ;)

Resources