I am working on implementing support for a CarPlay audio app, and am attempting to display listings in the simulator. I have implemented MPPlayableContentDataSource, but find that it is called inconsistently. It is called the first time the app is launched on a simulator, and if CarPlay is open on launch, I can make the first item display by scrolling up an otherwise empty listing to trigger a redraw.
CarPlay does not seem able to call the data source, however, and on a subsequent launch I see an empty screen or a spinner followed by the message Unable to connect to "AppName". I have tried different things but the main points are as follows:
In application: didFinishLaunchingWithOptions:
self.contentDataSource = [[MYContentDataSource alloc] init];
self.contentDelegate = [[MYContentDelegate alloc] init];
MPPlayableContentManager *contentManager = [MPPlayableContentManager sharedContentManager];
contentManager.dataSource = self.contentDataSource;
contentManager.delegate = self.contentDelegate;
[contentManager beginUpdates];
[contentManager endUpdates];
I've played around with beginUpdates endUpdates and reloadData methods of the content manager, but none of these result in the content datasource actually being called.
I've implemented numberOfChildItemsAtIndexPath and contentItemAtIndexPath in the datasource, which appear to be called correctly, although only on the first launch of the app on a fresh simulator.
The main points:
- (NSInteger)numberOfChildItemsAtIndexPath:(NSIndexPath *)indexPath {
return 3;
}
- (MPContentItem *)contentItemAtIndexPath:(NSIndexPath *)indexPath {
NSUInteger categoryId = [indexPath indexAtPosition:0];
MPContentItem *contentItem = [[MPContentItem alloc] initWithIdentifier:[NSString stringWithFormat:#"CAT-%lu", (unsigned long)categoryId]];
contentItem.title = [NSString stringWithFormat:#"Category %lu", (unsigned long)categoryId];
contentItem.subtitle = #"Subtitle";
contentItem.playable = NO;
contentItem.container = YES;
}
I've also tried retaining (or not) the reference to the MPPlayableContentManager.
I have the same behavior on an actual head unit. Any help would be appreciated.
After banging my head against the wall for quite a while, I got the following answer from Apple. Turns out that MPRemoteCommandCenter and MPNowPlayingInfoCenter are needed for CarPlay to work.
1. Start responding to MPRemoteCommandCenter events at app launch
2. Set the MPNowPlayingInfoCenter dictionary at app launch
These are required for MPPlayableContentDataSource to function correctly.
They are mentioned in the doc, but it isn't clear that they are needed for the catalog display to work. That solved the problem.
Related
I created my iMessage extension, when I try to open it, the first screen appears but it is totally frozen, and it does not react in any way.
I've put logs in the viewDidLoad of that first view and nothing appears there, after a few seconds I can already see those logs.
To make the application freezing lose that status, user has to slide screen left or right and back again.
I've tried looking all over the web for someone who happens to be the same, but I could not find anything.
It does not come to mind more screenshots or portions of code add, if you think I should provide some additional information, just let me know
Any help would be appreciated.
Thank you.
UPDATE:
This is my Project Structure.
This is my viewDidLoad code.
- (void)viewDidLoad
{
[super viewDidLoad];
NSLog(#"here viewDidLoad iMessage ext~~~!");
[self applyCornerRadiusToBtn];
[self registerPresentationAction];
NSDictionary *user = [self getUserInfoFromHostApp];
if (user) {
NSLog(#"Here != null user info");
//It is assumed that when you enter this point and run this log, the app should navigate to the next screen, but it does not.
[self performSegueWithIdentifier:#"goToYoutubeListIm" sender:nil];
} else {
NSLog(#"Here userInfo null");
}
}
- (NSDictionary *)getUserInfoFromHostApp
{
NSUserDefaults *myDefaults = [[NSUserDefaults alloc] initWithSuiteName:#"group.com.xxxxx"];
NSDictionary *userNameSaved = [myDefaults objectForKey:#"userInfoExt"];;
NSLog(#"userNameSaved in xxxx Ext ==> %#",userNameSaved);
NSURL *groupURL = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:#"group.com.xxxx"];
NSLog(#"groupURL ==> %#",groupURL);
return userNameSaved;
}
For all concerned I have found the problem or problems to be accurate.
1) I was creating my controllers type MSMessagesAppViewController. Apparently there should only be one controller of this type.
2) I had logic in the viewDidAppear in my MSMessagesAppViewController. For some strange reason this also caused the problem, I had to get the logic out there and force the user to interact with a button to execute the logic that was in the didAppear
I'm trying to test out the iOS 8.1 handoff feature with NSUserActivity between my iPhone and my iPad. For this, I tried both implementing my own solution, and to use Apple's PhotoHandoff project. However, it's not working.
If I provide a webpageURL, the handover works fine, but when I try to use userData or addUserInfoEntriesFromDictionary nothing works, and I can't for the life of me figure out what the catch is to make the data work.
Sample code:
NSUserActivity *activity = [[NSUserActivity alloc] initWithActivityType:#"com.company.MyTestApp.activity"];
activity.title = #"My Activity";
activity.userInfo = # {};
// activity.webpageURL = [NSURL URLWithString:#"http://google.com"];
self.userActivity = activity;
[self.userActivity becomeCurrent];
[self.userActivity addUserInfoEntriesFromDictionary:# { #"nanananan": #[ #"totoro", #"monsters" ] }];
(I'm also unable to make it work with a Mac app with a corresponding activity type)
I hope you found the solution already, but in case somebody stumbles upon this problem too, here is a solution. (Actually not very different from the previous answer)
Create user activity without userInfo, it will be ignored:
NSUserActivity *activity = [[NSUserActivity alloc] initWithActivityType:#"..."];
activity.title = #"Test activity";
activity.delegate = self;
activity.needsSave = YES;
self.userActivity = activity;
[self.userActivity becomeCurrent];
Implement the delegate to react to needSave events:
- (void)userActivityWillSave:(NSUserActivity *)userActivity {
userActivity.userInfo = #{ #"KEY" : #"VALUE" };
}
When needsSave is set to YES this method will be called and userActivity will be updated.
Hope this helps.
To update the activity object’s userInfo dictionary, you need to configure its delegate and set its needsSave property to YES whenever the userInfo needs updating.
This process is described in the best practices section of the Adopting Handoff guide.
For example, with a simple UITextView, you need to specify the activity type ("com.company.app.edit") identifier in the Info.plist property list file in the NSUserActivityTypes array, then:
- (NSUserActivity *)customUserActivity
{
if (!_customUserActivity) {
_customUserActivity = [[NSUserActivity alloc] initWithActivityType:#"com.company.app.edit"];
_customUserActivity.title = #"Editing in app";
_customUserActivity.delegate = self;
}
return _customUserActivity;
}
- (void)textViewDidBeginEditing:(UITextView *)textView
{
[self.customUserActivity becomeCurrent];
}
- (void)textViewDidChange:(UITextView *)textView
{
self.customUserActivity.needsSave = YES;
}
- (BOOL)textViewShouldEndEditing:(UITextView *)textView
{
[self.customUserActivity invalidate];
return YES;
}
- (void)userActivityWillSave:(NSUserActivity *)userActivity
{
[userActivity addUserInfoEntriesFromDictionary:#{ #"editText" : self.textView.text }];
}
FWIW, I was having this issue. I was lucky that one of my Activity types worked and the other didn't:
Activity: Walking
(UserInfo x1,y1)
(UserInfo x2,y2)
(UserInfo x3,y3)
Activity: Standing
(UserInfo x4,y4)
Activity: Walking
etc.
I got userInfo if the handoff occured when standing but not walking. I got other properties such as webpageURL in all cases; just userInfo came through null.
The fix for me was to invalidate & recreate the NSUserActivity object every time (e.g. when Walking to x2/y2 from x1/y1), instead of only when Activity type changed (e.g. from walking to standing). This is very much not the way the doc is written, but fixed the issue on iOS 9.
UPDATE: This workaround doesn't work on iOS 8. You need to implement this via the userActivityWillSave delegate, as gregoryM specified. Per Apple's doc:
To update the activity object’s userInfo dictionary efficiently,
configure its delegate and set its needsSave property to YES whenever
the userInfo needs updating. At appropriate times, Handoff invokes the
delegate’s userActivityWillSave: callback, and the delegate can update
the activity state.
This isn't a "best practice", it is required!
[Note: issue occurred on iOS 9 devices running code built on Xcode 6.x. Haven't tested Xcode 7 yet, and issue may not occur on iOS 8.]
Here's the scenario: I am using MWPhotoBrowser. I can push it fine once. The second time I try to push it the app crashes with nothing helpful. This is an ARC project, and this exact same code works fine in previous versions of the app. I am dumbfounded.
-(void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{
selectedIndexPath = indexPath;
if(browser == nil)
{
browser = [[MWPhotoBrowser alloc] initWithDelegate:self];
}
// Set options
browser.displayActionButton = YES; // Show action button to allow sharing, copying, etc (defaults to YES)
browser.displayNavArrows = YES; // Whether to display left and right nav arrows on toolbar (defaults to NO)
browser.zoomPhotosToFill = NO; // Images that almost fill the screen will be initially zoomed to fill (defaults to YES)
[browser setCurrentPhotoIndex:indexPath.row]; // Example: allows second image to be presented first
//browser.wantsFullScreenLayout = YES; // iOS 5 & 6 only: Decide if you want the photo browser full screen, i.e. whether the status bar is affected (defaults to YES)
// Present
[self.navigationController pushViewController:browser animated:YES];
}
Tap a cell once, MWPhotoBrowser opens fine. Go back, tap a cell again, the app crashes just with the debugger but no call stack or error reason:
(lldb):
browser is a strong member variable. It isn't being deallocated prematurely. I can also guarantee it is crashing exactly on the last line of the method.
Can someone please enlighten me on this? I'd solve it if the app at least gave me a reason for the crash and not just throw the debugger on my face.
I have used MWPhotoBrowser extensively and ran across the same problem once. The way I solved it was by checking the MWPhoto objects to see if they actually had been set properly. I realised that I had not created some of them, hence the crash. I would check the place where you store your MWPhotos and make sure they are all set. Hope this helps!
I'm using the UIDocumentInteractionController in iOS 7.1 and it's performing really badly.
I'm using it in a UICollectionViewController to view documents in a collection view.
On pressing an item in the collection view, it takes about around 6 (yes, that's six) seconds to appear. From a user experience perspective, they've pressed the screen a few more times before it appears because it takes so long.
I'm using the same code since iOS 6, but it seems particularly bad now. If anyone has any thoughts as to how I can speed things up, that would be greatly appreciated.
Essentially, I have the following in my header file:
interface MyViewController : UICollectionViewController <UIDocumentInteractionControllerDelegate>
{
UIDocumentInteractionController *docController;
}
#end
and in the implementation, I'm just doing the following:
In viewDidLoad (recently moved to here to see if it improves things):
docController = [[UIDocumentInteractionController alloc] init];
docController.delegate = self;
And then in the collectionView:didSelectItemAtIndexPath: I'm doing this:
NSURL *fileURL = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:document.Link ofType:#"" ]];
[docController setURL:fileURL];
PresentationViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"DocumentCell" forIndexPath:indexPath];
CGRect rect1 = cell.frame;
bool didShow = [docController presentOptionsMenuFromRect:rect1 inView:collectionView animated:YES];
where document is just a class with a string for the URL.
Let me know if you need any further detail.
Thanks in advance for any assistance anyone can provide.
-- Update:
After some NSLogs, I noticed that it's definitely the following line that's slow:
bool didShow = [docController presentOptionsMenuFromRect:rect1 inView:collectionView animated:YES];
TL;DR:
The method you are using is a synchronous request that uses your document data for find which apps are capable of reading your file. You need to swap with the asynchronous version that restricts the enumeration to only apps that can parse your file type.
Remove this method:
- (BOOL)presentOptionsMenuFromRect:(CGRect)rect
inView:(UIView *)view
animated:(BOOL)animated
And replace with this method:
- (BOOL)presentOpenInMenuFromRect:(CGRect)rect
inView:(UIView *)view
animated:(BOOL)animated
Excerpt from the Apple Docs:
This method is similar to the presentOptionsMenuFromRect:inView:animated: method, but presents a menu restricted to a list of apps capable of opening the current document. This determination is made based on the document type (as indicated by the UTI property) and on the document types supported by the installed apps. To support one or more document types, an app must register those types in its Info.plist file using the CFBundleDocumentTypes key.
If there are no registered apps that support opening the document, the document interaction controller does not display a menu.
This method displays the options menu asynchronously. The document interaction controller dismisses the menu automatically when the user selects an appropriate option. You can also dismiss it programmatically using the dismissMenuAnimated: method.
I was encountering a similar problem with:
UIDocumentInteractionController.presentPreviewAnimated
It would take an incredibly long time to complete. I found adding a brief delay between saving the file to be previewed and presenting the preview fixed the problem:
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(100 * NSEC_PER_MSEC)), dispatch_get_main_queue(), {
self.controller.presentPreviewAnimated(false)
})
Swift 4.2
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
self.controller.presentPreviewAnimated(false)
}
I am trying to display a file using QLPreviewController. The QL view displays correctly (is pushed on top of my Navigation Controller) but the content is blank. However, no errors are displayed and application doesn't crash.
Checks on existence of file return true. (A proof is that if I use [self.docInteractionController presentPreviewAnimated:YES]; where docInteractionController is a UIDocumentInteractionController the file is correctly shown).
The code is taken directly from Apple sample code DocInteraction.
previewController.dataSource = self;
previewController.delegate = self;
// start previewing the document at the current section index
previewController.currentPreviewItemIndex = 0; //I want the first (and only) document
[[self navigationController] pushViewController:previewController animated:YES];
[previewController release];
The current view is a QLPreviewControllerDataSource, QLPreviewControllerDelegate,, and the delegate methods are as follow:
- (NSInteger) numberOfPreviewItemsInPreviewController: (QLPreviewController *) controller
{
return self.documentURLs.count;
}
- (id)previewController:(QLPreviewController *)previewController previewItemAtIndex: (NSInteger)index
{
return [self.documentURLs objectAtIndex:index];
}
documentURLs is a NSArray that contains the fileURLs of the documents. The same fileURL passed to the UIDocumentInteractionController displays correctly. I don't necessarily have to use QuickLook, I may just rely on UIDocumentInteractionController, however the fact that it's not working is really annoying.
Thank you in advance
Giovanni
Make a sample that demoes the issue. If you find that it still occurs on iOS 7, pls file a bug report.
I reported a bug on this class (pass nil URL to get loading indicator) and it got fixed within 2 weeks.