Closed jordynmackool closed 5 years ago
Hi, I'm happy to see this has moved to review. There's much that I agree with here, but I do have a number of notes:
The name of "overlay controller" may be misleading. When doing the review of the Core Certification guidelines, it was argued that some of these operations may not be done in an overlay style like Ford's Gen 3 (specifically, I think AudioPassThru was called out, as it's difficult to imagine others occurring without an overlay, though they could replace the template wholesale as well). I believe I originally criticized the name "Modal" for this very reason, however, I think it's better than "Overlay," and would prefer it if nothing better can be provided. At least "Modal" is describing various "modes of operation" rather than a UI overlay, which may or may not happen based on the OEM implementation.
I'm not sure that storing pending overlay controllers is better than just failing when one is already presented. I can see both being useful at different times, and the dev could implement either manually if they wanted it. Personally, I think I'd prefer failing. 🤷♂️ I've re-read the paragraph describing this a few times, but I think I'm still missing the purpose of allowing it, could you elaborate?
When a view controller wants to present an overlay controller it must call the presentOverlayController:completion: method of the VC manager. The presenting VC must be listed in the view controller stack but does not need to be the top view controller.
I'm not sure if this is the best policy. Isn't presenting an overlay from somewhere other than the top VC on the stack more likely to lead to bugs than to be useful? Apple does allow this in my testing, however, they log a message to the console whenever it happens:
Presenting view controllers on detached view controllers is discouraged
If really needed, a VC can already grab the VC from the top of the stack and send it a message, right? Can we cut off this possible avenue for bugs?
Should the method - (void)presentOverlayController:(SDLOverlayController *)overlayController completion:(SDLOverlayControllerCompletionHandler)completion;
be on the UIViewController
equivalent as it is in UIKit?
If the overlay controller has detected the end of the presentation it should notify the VC manager. This can also be done through an encapsulated overlay controller completion handler.
How would this work? With at least some of these (such as Alert), the AlertResponse
is sent immediately, not when the overlay closes. Would it need an internal timer set to the timeout and hope they line up, plus listeners for the soft buttons since the alert closes once one is hit?
As it inherits SDLViewController it is possible to manipulate the head unit during a presentation. To do that the app developer must create a new view and place it in the view property of the overlay controller.
I'm not sure I'm understanding this point. It's possible to manipulate the view controller underneath the overlay? Or to manipulate the overlay?
Depending on the subclass (and the min/max values of the parameter) the duration value will be ignored if it exceeds the value range.
I agree with points 1 & 2, however, might it be better that if it's set to 0
(or not set at all due to an initializer not including that parameter) to set it to the default value? e.g. for ScrollableMessage
to set it to 30 seconds instead of 65.535 seconds?
duration
(and all other properties on the subclasses) be readonly
to set developer expectations about changing it during presentation as well as to prevent bugs from us. We certainly will not be expecting them to change properties during presentation and it could cause significant issues that we don't want to have to account for. This is how many objects are designed in the SDL iOS library (e.g. SDLChoiceSet for the Choice Set Manager). There can be an initializer that takes the duration, or not and it's set to the default.The main reason for this desire is these SDL UI controllers work fundamentally differently than UIKit ones. When you set those properties on UIKit, the values change on screen while it's on screen, with SDL, that will not happen. To help set expectations, I would prefer developers to understand they can set these values once. If they want something different, they need to make a new object.
SDLAlertController
buttons
should perhaps note that it should fail to initialize anything with buttons containing more than one state?
@property (nonatomic, copy) (^onAudioData)(NSData *audioData);
The name of this block and corresponding initializer names probably shouldn't include on
, since that's an SDL RPC paradigm and not an iOS one? Perhaps audioDataProcessor
, or audioDataHandler
?
Is @property (nonatomic, nonnull, copy) SDLAudioPassThruCapabilities *capabilities;
intended to be nullable
?
Am I crazy if I think muteAudioSource
should actually default to true
? The vast majority of the time the developer will prefer to pause the audio to make sure they get clear voice, right?
I know I've made this point several times, but I'd be remiss not to note it in the review: I strongly disagree with the use of the value
property of the SDLSliderController
and SDLValuePickerController
. I understand that's how UIKit does it, but the SDL version works entirely differently. A UISlider
is an interface item in UIKit, not a time-limited overlay controller.
The slider's value therefore ought to be a value, as nothing else makes sense. We cannot KVO observe the slider value over time as we can in UIKit. However, because our Sliders are time-limited controllers, they ought to pass back the selected value in a completion handler style block, not require the developer to remember to check the value on the view controller after the response occurs. In the current proposal, there's no way to know except by repeatedly checking the value
in a timed loop or checking in the SDLOverlayControllerCompletionHandler
which doesn't even pass back the view controller, so they'd have to store it themselves or even if it was modified to include it, they'd have to cast it themselves.
It just makes sense to have a completion handler here that returns the value.
minimumValue
and maximumValue
make much sense here. They can set it to 25
and 50
but those values won't be reflected on the overlay, so what's the point?What I'm thinking of for these classes is something like this:
@interface SDLSliderController : SDLOverlayController
@property (nonatomic, nonnull, copy) NSString *title;
@property (nonatomic, nullable, copy, readonly) NSString *message;
// must not exceed "maxvalue" of "Slider.numTicks"
@property (nonatomic, readonly) NSInteger numValues;
// maps to "Slider.position"
@property (nonatomic, readonly) NSInteger initialIndex;
// Passes back the selectedIndex
@property (nonatomic, readonly) SDLSliderCompletionHandler completionHandler;
- (instancetype)initWithTitle:(nonnull NSString *)title numPositions:(NSInteger)numPositions initialPosition:(NSInteger)initialPosition completionHandler:(SDLSliderCompletionHandler)completionHandler;
- (instancetype)initWithTitle:(nonnull NSString *)title message:(nonnull NSString *)message numPositions:(NSInteger)numPositions initialPosition:(NSInteger)initialPosition completionHandler:(SDLSliderCompletionHandler)completionHandler;
@end
@interface SDLValuePickerController : SDLOverlayController
@property (nonatomic, nonnull, copy, readonly) NSString *title;
@property (nonatomic, nonnull, copy, readonly) NSArray<NSString *> *valueLabels;
@property (nonatomic, readonly) NSInteger initialIndex;
// Passes back the selectedIndex (NSInteger) and selectedValue (NSString)
@property (nonatomic, readonly) SDLValuePickerCompletionHandler completionHandler;
- (instancetype)initWithTitle:(nonnull NSString *)title valueLabels:(nonnull NSArray<NSString *> *)valueLabels initialIndex:(NSInteger)initialIndex completionHandler:(SDLValuePickerCompletionHandler)completionHandler;
@end
something like that.
SDLOverlayControllerCompletionHandler
should include the SDLOverlayViewController
to which it corresponds.I don't think it makese sense to discuss all items in this chat. I think a workshop over phone makes the most sense. After the workshop we can share the minutes with conclusions in this chat.
The Steering Committee deferred this proposal on 2018-10-23 based on the request from the author to schedule a workshop in this comment to further discuss the feedback that was given in this comment. It was concluded that additional next steps will be determined after the workshop takes place and the meeting minutes are shared with the SDLC Members.
The Steering Committee had a workshop regarding this proposal on 2018-11-14. The author is to make changes based on conclusion of the meeting minutes that was sent out on 2018-11-19 and resubmit to be reviewed.
Main screen overlay As it inherits SDLViewController it is possible to manipulate the head unit during a presentation. To do that the app developer must create a new view and place it in the view property of the overlay controller.
What does this really mean? Does this mean that a new template will be presented prior to the overlay being presented (e.g. a loading template) as discussed in the workshop? If so, how are you mitigating the large downsides of such an approach as discussed in that same workshop? If it's manipulating the existing view, how is that happening with a new SDLViewController
? I think this section needs to be fleshed out considerably.
SDLOverlayController
should not have any publicly accessible initializers. Instead, the subclasses should include duration
only on their own initializers (as you do currently).
On SDLSliderController
, if the message
maps to sliderFooter
(it's a bit unclear), then perhaps message
should be named footerMessage
or footerText
instead, for clarity?
We discussed in the workshop that having the minimumValue
and maximumValue
might not be best for this overlay, because those two values won't actually mean anything (they won't be reflected on the UI) which would be confusing for developers. I see that you noted that the position values would be mapped and displayed on the UI:
If the app does not provide a message the overlay controller creates localized numbers to translate position value to the user facing value using NSNumberFormatter and localizedStringFromNumber:numberStyle:.
However, I think this conflicts with what you said in other places:
As per mobile API the property Slider.sliderFooter is an array used for two purposes. Either it's a footer text (single item) or a list of names representing slider values (number of items must match .numTicks). This controller is used for the "single item" case.
The message property maps to Slider.sliderFooter as a singleton array. As per mobile API this will result in a descriptive message explaining the meaning of the slider value.
There is no (and can be no) footerText
(or equivalent) property on SDLSliderController
because the footerText
is automatically being handled to show the footer number values. Also, wouldn't message
map to sliderHeader
.
I believe SDLValuePickerController
is missing a message
parameter to map to sliderHeader
.
What was the result of your thoughts on how to retrieve the value from SDLSliderController
and SDLValuePickerController
? Did you decide to stick with referencing the controller within its own completion handler, which could lead to a potential retain cycle?
My apologies. I have to admit that I did not spend enough time on this item. Below text were notes/thoughts but I have missed to spec them out for the propoal.
I was thinking about adding an opaque
flag (non-opaque is by default) to views that will solve all the "large downsides" discussed in the workshop. This flag should allow views from the underlying view controller to stay visible while the overlay controller is presenting. The opaque mode is view type related.
Top view controller's view hierarchy (all views are non-opaque): view
App presents following overlay controller (present list of albums) with view hierachy: view (opaque)
The resulting screen will be:
four lines of text ("loading", "artist", "album", "genre") because root view and text view are opaque one button ("cancel") because button view is non-opaque primary graphic because overlay's root view is opaque an no image view is contained.
The big pros are:
I understand that documentation is missing. It'll need much more text for explanation and of course a well written page in developer guides with examples. Therefore this proposal should be returned for revisions.
I disagree. Unfortunately there's no "protected" access modifier or abstract classes in objc which would perfectly fit but I do want to keep possibilities for customized overlays created by app partners or providers of utility code/libraries. Your suggestion is too restricting with no benefit.
I disagree. It's a bit unclear as you knwo the mobile API so well. I understand your point but what I'm trying to do here is to redesign all overlays and unify them as good as possible. Just as any other overlay message properties Slider.sliderFooter
is also multi line capable. I don't think header or footer are really good names at all as they are too specific to the purpose. In my backlog I wanted to write a proposal to add title
to Alert based on the overlays and your template title proposal. With all that I think title and message fits best.
I'm not sure if I can get your point. If priority is not clear: Internally message
is of higher priority than automatically generated numbers which fits to what I wrote "If the app does not provide a message ...". Either a message or numbers will be visible.
Do you want a pure value based slider controller? Do you want to force presenting numbers? This approach removes the ability of single message sliders but does not give any benefit. Do you really want to enforce apps for reduced functionality?
Depends on 4. Again I want to note that we will loose the ability of single message sliders.
So the value properties are read only to the app developer but internally they can be set to the response's value.
Regarding retain cycles I want to refer to https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/WorkingwithBlocks/WorkingwithBlocks.html and https://developer.apple.com/documentation/uikit/uiviewcontroller/1621380-presentviewcontroller
I don't see a retain cycle for temporarily presented overlays.
Regarding 1. Main screen overlay
I think I understand what you're saying, but I'm not 100% confident, so I don't want to comment too much on this. I noted the cancel
soft button, which I'm assuming was a mistake because we can't currently cancel an overlay before or while it's presented without user input, right? Unless I'm not understanding correctly.
Regarding 2. base class accessibility I disagree. Unfortunately there's no "protected" access modifier or abstract classes in objc which would perfectly fit but I do want to keep possibilities for customized overlays created by app partners or providers of utility code/libraries. Your suggestion is too restricting with no benefit.
Even if we wanted developers to create their own "customized overlays" – though I don't understand what that means or why we'd want to allow that (it will only lead to pain on everybody's part) – they could still do so even if the initializer is not there. I think there's a very clear benefit. Developers should not create an instance of the base class, therefore, no initializers should be available.
Regarding 3. message/slider footer
I think I'm following you here and I think I can get on board with that.
Regarding 4. min/max values
I think this was a misunderstanding on my part. I missed the "if there is no message
" part. It's a bit concerning to me that there's "hidden" functionality. We know that 99.9% of developers don't read the documentation, so knowing the limits of minimumValue
and maximumValue
, and the differences for having a message
and not having it, are concerning to me.
I'm unsure about this myself, but what do you think about yet another SDLSliderController
overlay class? One for having a message and one for having number values?
Regarding 5. value picker message
I missed the title
🙄
Regarding 6. the actual value
The issue I have here is that the overlay class itself has no way of knowing when the value is set by SDL. The only way the caller can know about when the value
becomes available is through SDLOverlayControllerCompletionHandler
which does not provide the overlay itself or the value in the parameters it passes. Therefore, the caller would look like:
self.slider = [[SDLSliderController alloc] initWithTitle:"Hello" value:4 minimumValue:3 maximumValue:6]
[self presentOverlayController:slider completion:^(SDLResult result, NSError *error) {
[self doSomethingWith:self.slider.value];
}];
You're right that there's no retain cycle if the code is written like this, but if slider
is assigned to a property of the class, then there is (because of reference to self
), which is what I was thinking of with the retain cycle. My suggestion is to change the SDLOverlayControllerCompletionHandler
to:
typedef void (^SDLOverlayControllerCompletionHandler)(SDLViewController *presentingViewController, SDLOverlayController *overlay, SDLResult result, NSError *error);
in order to avoid that case. Devs will still have to contend with the retain cycle if they reference self
themselves, but it would help, e.g. the above case would become:
self.slider = [[SDLSliderController alloc] initWithTitle:"Hello" value:4 minimumValue:3 maximumValue:6]
[self presentOverlayController:slider completion:^(SDLViewController *presentingViewController, SDLOverlayController *overlay, SDLResult result, NSError *error) {
[(MySDLMusicViewController *)presentingViewController doSomethingWith:(SDLSliderController *)overlay.value];
}];
SDLValuePickerController
or SDLSliderController
with invalid values (e.g. > 26 values, the value is outside the min/max range). We can either throw an exception or return nil
from the initializer. I'd recommend the exception route.The Steering Committee voted to return this proposal for revisions on 2018-12-18 to allow the author to add more details as discussed in this comment and this comment.
Hey Kujtim, thanks for the update. Some notes and thoughts:
SDLView
opaque
property is going to end up being confusing to developers because the opaque
property only matters if it's on an overlay controller, right? Am I right to think that e.g. Setting a view to transparent on a standard SDLViewController
will have no effect? I can't think of a better way to do it without it being "all or nothing."In addition, your updates have not addressed my concern (2) above. I'm unsure of what you mean by "customized overlays." I'm assuming you mean developer-created replacements for our overlay classes, which seems like it will only end with pain for us and them (e.g. MOBILE_API changes, incompatibility bugs, them not reporting bugs on the standard framework back to us, etc.).
(4) above seems possibly partly addressed in the "alternatives considered," but my specific concern or proposed solution wasn't addressed, and I'm still fairly concerned about the non-obvious behaviors. What do you think about another overlay class to fix this issue? I don't like splitting it out so many times, but it seems like it's the best way to make the functionality clear.
(6) I'm not in full agreement here, but neither is it the hill I want to die on. Either way works.
Additional Question (1) was also not addressed. I would like to see an exception thrown if an out of bounds size is created for the sliders.
Hello Joel, thank you for the review. Good to have you back 👍
opaque
property is supposed to be used anywhere, not only for overlays.message
overrides the localized value list. The alternative solution was to concat the localized value with the message so that we don't need a third value class. I have another option in mind. The SDLSliderController
requires a message string. The SDLValuePickerController
should take over the localized value list. See below code change:@interface SDLSliderController : SDLOverlayController
// This is the only initialiser for this controller. A single message string will be used for the slider footer
- (instancetype)initWithTitle:(nonnull NSString *)title
message:(nonnull NSString *)message
value:(NSInteger)value
minimumValue:(NSInteger)minimumValue
maximumValue:(NSInteger)maximumValue;
@end
@interface SDLValuePickerController : SDLOverlayController
// this initialiser automatically creates localized value labels
- (instancetype)initWithTitle:(nonnull NSString *)title
value:(NSInteger)value
minimumValue:(NSInteger)minimumValue
maximumValue:(NSInteger)maximumValue;
// This initialiser allows apps to specify their own value labels
- (instancetype)initWithTitle:(nonnull NSString *)title
valueLabels:(nonnull NSArray<NSString *> *)valueLabels
value:(NSInteger)value;
@end
Above actually moves the init method from the slider controller to the value picker controller. If I'm not mistaken it solves your concern of the message override and my concern of three classes 👍
Additional Question 1. I'm not OK with throwing exceptions. I understand that app developers will receive crashlogs when using exceptions but the user will be faced with a bad experience. I guess it's worse to have the app to crash compared to not perform the requested action. The user can continue using other areas of the app if we use error messages. The initialiser should log an error message. I'm not sure if we can throw exceptions in DEBUG but errors in RELEASE? ...
Additional question 1: Well, the main reason I advocate for an exception is that an out of bounds scenario shouldn't happen in release if it doesn't happen in development. The other reason is that it's most likely going to be an unhandled and undefined scenario for the developer if creating the overlay fails, and the only real way to get the dev's attention of a failure in production is a crash or a user emailing about something not working. We could also NSAssert
, which would happen in debug, but not release by default. I wish we had swift so we could throw
and the dev could choose to try!
or catch
themselves.
SDLView
in a view controller is transparent, and another view controller overlays it that is transparent or opaque, then nothing in the transparent view should show through, right? The Steering Committee voted to keep this proposal in review to allow the author to respond to the outstanding questions in this comment and this comment discussing transparent/opaque behavior.
@joeljfischer @NicoleYarroch
The other 1. :) There is no limit to the stack. I believe it's the best choice to have views opaque by default and let the developer make them transparent. Then the developers know's what they do.
- @NicoleYarroch I'm afraid I don't understand your question.
I hope this modification of your example can help explain my question regarding overlaying transparent views.
App presents the following overlay controller with view hierarchy:
OR
Thanks @NicoleYarroch The resulting screen will be * ("loading", "artist", "album", "genre") because toe overlay's textView is transparent.
The Steering Committee voted to return this proposal for revisions to allow the author to update the proposal. The author is to include the revisions discussed in this comment and expand on his response in the first point in this comment.
@kshala-ford please advise if you will be able to take action on this proposal within the next two weeks. If not, we will plan to close as inactive on 2019-05-08. Thank you!
Closing as inactive. The issue will remain in a returned for revisions
status and unlocked so the author can notify the Project Maintainer when it is ready to be revisited.
Hello SDL community,
The review of "SDL 0201 - High level interface: Overlay Controllers" begins now and runs through February 19, 2019. The proposal is available here:
https://github.com/smartdevicelink/sdl_evolution/blob/master/proposals/0201-high-level-interface-overlay-controllers.md
Reviews are an important part of the SDL evolution process. All reviews should be sent to the associated Github issue at:
https://github.com/smartdevicelink/sdl_evolution/issues/606
What goes into a review?
The goal of the review process is to improve the proposal under review through constructive criticism and, eventually, determine the direction of SDL. When writing your review, here are some questions you might want to answer in your review:
More information about the SDL evolution process is available at
https://github.com/smartdevicelink/sdl_evolution/blob/master/process.md
Thank you, Jordyn Mackool
Program Manager - Livio jordyn@livio.io