Skip to content

[modelio] Update for iOS 10.3 beta 1 - #1601

Merged
VincentDondain merged 5 commits into
dotnet:xcode8.3from
VincentDondain:modelio-b1
Feb 6, 2017
Merged

[modelio] Update for iOS 10.3 beta 1#1601
VincentDondain merged 5 commits into
dotnet:xcode8.3from
VincentDondain:modelio-b1

Conversation

@VincentDondain

Copy link
Copy Markdown
Contributor
  • Added MDLAssetTest (copy from MDLObjectTest).
  • Indexers tests.

@spouliot spouliot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I'm not sure we want to expose Protocol in the API

public void IndexerTest ()
{
using (var obj = new MDLObject ()) {
var key = new Protocol ("MDLComponent");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I'm not sure we want to expose Protocol like this in the API. At best it's not very type safe, I'd better use

var key = new Protocol (typeof (MDLComponent));

My first thought was to abuse [BindAs] but a conversion from Protocol to System.Type, with additional checks to ensure it's a protocol, can be costly if done repetitively...

Maybe we can just provide a [Wrapper] that calls a conversion method ? and also expose that as a new Protocol.ctor(System.Type) ?

@rolfbjarne @dalexsoto thoughts ?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think just a [Wrapper] that calls a conversion method (or a new Protocol ctor) would be fine.

BTW your example is wrong 😄 It should be:

var key = new Protocol (typeof (IMDLComponent));

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Yep, agree on just the Wrapper 👍

Comment thread src/ModelIO/MDLAsset.cs Outdated
return ObjectForKeyedSubscript (key);
}
set {
SetObject (value, key);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

that's confusing because your new SetObject (even if internal) is not symmetric with the existing GetObject

Comment thread src/modelio.cs Outdated
[iOS (10,3), TV (10,2), Mac (10,12,4)]
[Export ("componentConformingToProtocol:")]
[return: NullAllowed]
IMDLComponent IsComponentConforming (Protocol protocol);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Is makes it sound boolean, but this is not what it returns. Seems to me that Get is the action (not question) being done.

Also it seems to be related with the previous method, so GetComponent would match.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@spouliot I totally agree with you but I wanted to keep it close to MDLObject's IsComponentConforming...

Most of the changes in this PR are about aligning MDLObject and MDLAsset.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Let's fix the bad one with an [Obsolete] and add a new, accurate, name :)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Maybe GetConformingComponent? That's a bit more descriptive and closer to what ObjC calls it (which makes finding it easier).

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Ah, I see the SetComponent method now, GetComponent would be the opposite method (I assume at least, since Apple named them quite differently).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yea so in the headers setComponent:forProtocol: and componentConformingToProtocol: have the exact same description:

Extensible component support that allows user of ModelIO to customize MDLAssets to fit their format and workflow.

So I'm going for GetComponent instead of IsComponentConforming.

Comment thread src/ModelIO/MDLAsset.cs Outdated
}

[iOS (10,3), TV (10,2), Mac (10,12,4)]
public IMDLComponent this [Protocol key] {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

there's already a this that returns something else... check FxDG but I don't think we should expose it this way. That can get confusing source wise when the argument type is not clear.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

FxDG (Framework Design Guideline)?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

yup :)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Mmh here's the guideline: https://msdn.microsoft.com/en-us/library/ms229006(v=vs.110).aspx

AVOID indexers with parameter types other than System.Int32, System.Int64, System.String, System.Object, or an enum.

So Protocol is definitely not recommended.

DO NOT provide more than one family of overloaded indexers in one type.

I guess that validates your point @spouliot. As I understand it it's fine to "overload" an indexer (as in have an indexer that's using an other indexer in the same type) but it's wrong to create a 2nd and different family of indexers.

Comment thread src/ModelIO/MDLObject.cs Outdated
namespace XamCore.ModelIO {
public partial class MDLObject {
[iOS (10,3), TV (10,2), Mac (10,12,4)]
public IMDLComponent this [Protocol key] {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

should match how MDLAsset turns out...

Comment thread src/modelio.cs Outdated

[Internal]
[Export ("setObject:forKeyedSubscript:")]
void SetObject ([NullAllowed] IMDLComponent obj, Protocol key);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

are those just doing the same as the setComponent:forProtocol: and componentConformingToProtocol: above ? in that case maybe we should not expose them ?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yes so objectForKeyedSubscript:'s description is:

Allows shorthand [key] syntax for componentConformingToProtocol:

@spouliot correct me if I'm wrong but this is basically the indexer I implemented initially (public IMDLComponent this [Protocol key] which was sentenced to die because there's already one).

Comment thread src/modelio.cs Outdated

[Internal]
[Export ("setObject:forKeyedSubscript:")]
void SetObject ([NullAllowed] IMDLComponent obj, Protocol key);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

same (not sure we need those, tests would confirm)

Comment thread src/modelio.cs

[Export ("componentConformingToProtocol:")]
[return: NullAllowed]
IMDLComponent IsComponentConforming (Protocol protocol);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

that's old, but badly named :(

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Ah well you found the origin of my IsComponentConforming...

Comment thread src/modelio.cs Outdated
void RemoveObject (MDLObject @object);

[iOS (10,3), TV (10,2), Mac (10,12,4)]
[Abstract]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

breaking change, you cannot add abstract methods as existing subclasses won't have them

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Ah damn, good point I missed that.

Comment thread src/modelio.cs Outdated
MDLObject GetObjectAtIndexedSubscript (nuint index);

[iOS (10,3), TV (10,2), Mac (10,12,4)]
[Abstract]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

same

@monojenkins

Copy link
Copy Markdown
Contributor

Build failure

@VincentDondain

Copy link
Copy Markdown
Contributor Author

Introspection-mac

[FAIL] Selector not found for ModelIO.MDLAsset : objectForKeyedSubscript:
[FAIL] Selector not found for ModelIO.MDLAsset : setObject:forKeyedSubscript:
[FAIL] Selector not found for ModelIO.MDLObject : objectForKeyedSubscript:
[FAIL] Selector not found for ModelIO.MDLObject : setObject:forKeyedSubscript:

@VincentDondain

VincentDondain commented Jan 31, 2017

Copy link
Copy Markdown
Contributor Author

Unrelated tests failures (due to the move to Xcode8.3):
apitest - https://bugzilla.xamarin.com/show_bug.cgi?id=51799
monotouch-test - https://bugzilla.xamarin.com/show_bug.cgi?id=51801
mini watchOS - https://bugzilla.xamarin.com/show_bug.cgi?id=50419
don’t link watchOS - https://bugzilla.xamarin.com/show_bug.cgi?id=50419

Comment thread src/modelio.cs Outdated
[iOS (10,3), TV (10,2), Mac (10,12,4)]
[Abstract]
[Export ("objectAtIndexedSubscript:")]
MDLObject GetObjectAtIndexedSubscript (nuint index);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Just GetObject would be better.

[Test]
public void IndexerTest ()
{
using (var obj = new MDLAsset ()) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think you need a version check here, for Xcode 8.3.

public void IndexerTest ()
{
using (var obj = new MDLObject ()) {
var key = new Protocol ("MDLComponent");

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think just a [Wrapper] that calls a conversion method (or a new Protocol ctor) would be fine.

BTW your example is wrong 😄 It should be:

var key = new Protocol (typeof (IMDLComponent));

@VincentDondain

Copy link
Copy Markdown
Contributor Author

Alright so:

  1. I'm open to suggestions on how to assert that Type is a Protocol in ObjCRuntime/Protocol.cs (not sure at all I'm doing the right thing but it works :P)
  2. I removed the Indexers I added for both MDLAsset and MDLObject because they did not follow the Framework Design Guidelines, therefore I removed MDLObject.cs that was new in this PR and the tests I created since they were related to the new manual code.

For MDLObject, based on the description, I'm really not sure that objectAtPath should become an indexer like MDLAsset's objectAtIndex.

The first one being:

Return the object at the specified path, or nil if none exists there

The second being:

Returns the top-level object at the specified index in the asset.
index: An index in the asset’s list of top-level objects; between zero and the value of the count property.

@monojenkins

Copy link
Copy Markdown
Contributor

Build failure

@rolfbjarne rolfbjarne left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Also please add tests for the new Protocol constructor.

Comment thread src/ObjCRuntime/Protocol.cs Outdated

public Protocol (Type type)
{
this.handle = Class.GetHandle (type);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This is not correct.

  1. First check that the type is an interface, otherwise it can't be a protocol:

     if (!type.IsInterface)
         throw new ArgumentException (string.Format ("'{0}' is not a protocol.", type.FullName));
    
  2. Then get the Protocol attribute, and get the name of the protocol from there:

     var protocols = type.GetCustomAttributes (typeof (ProtocolAttribute), false);
     if (protocols.Count == 0)
         throw ...
    
     var protocol = protocols [0];
     var protocolName = protocol.Name;
     handle = objc_getProtocol (protocolName);
     if (handle == IntPtr.Zero)
         throw ...
    

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Mmh didn't realized I could dive into the managed type like that! Much better thanks.

@monojenkins

Copy link
Copy Markdown
Contributor

Build failure

Comment thread src/ObjCRuntime/Protocol.cs Outdated

public Protocol (Type type)
{
var exception = new ArgumentException (string.Format ("'{0}' is an unknown protocol", type.FullName));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This creates an exception instance for every protocol created (thru this .ctor). This is not efficient, use something like:

if (type.IsInterface) {
   foreach (var pa in type.GetCustomAttributes<ProtocolAttribute> (false)) {
      handle = objc_getProtocol (pa.Name);
      if (handle != IntPtr.Zero)
         return;
   }
}
if (handle == IntPtr.Zero)
   new ArgumentException (string.Format ("'{0}' is an unknown protocol", type.FullName));

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Also having different exception messages for each condition gives you valuable information (which check failed?).

Comment thread src/modelio.cs Outdated
[Export ("components", ArgumentSemantic.Copy)]
IMDLComponent[] Components { get; }

[Internal]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

maybe [Advanced] but not [Internal]. The main reason is that Type is easier (and type safe) to use, but it would be costly in a loop, e.g.

foreach (var component in list)
   x.SetComponent (type);

vs

var p = new Protocol (type);
foreach (var component in list)
   x.SetComponent (p);

It also makes it possible to override the method in a subclass.

Comment thread src/modelio.cs Outdated
[Wrap ("SetComponent (component, new Protocol (type))")]
void SetComponent (IMDLComponent component, Type type);

[Internal]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

same

Comment thread src/modelio.cs Outdated
#if XAMCORE_4_0
[Internal]
#endif
[Obsolete ("Use SetComponent (Type protocol)")]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

remove both [Obsolete] and [Internal]

Comment thread src/modelio.cs Outdated
#if XAMCORE_4_0
[Internal]
#endif
[Obsolete ("Use GetComponent (Type protocol)")]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Obsolete IsComponentConforming but expose GetComponent (Protocol protocol) for the same reasons as above

@spouliot spouliot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

👍

throw new ArgumentException (String.Format ("'{0}' is an unknown protocol", name));
}

public Protocol (Type type)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

❤️

@monojenkins

Copy link
Copy Markdown
Contributor

Build failure

@monojenkins

Copy link
Copy Markdown
Contributor

Build failure

@VincentDondain

Copy link
Copy Markdown
Contributor Author

Unrelated test failures

@rolfbjarne add one simple test in MDLObjectTest covering the new Protocol constructor.

Also fixed the introspection-mac tests.

@VincentDondain
VincentDondain merged commit 23ba6df into dotnet:xcode8.3 Feb 6, 2017
@VincentDondain
VincentDondain deleted the modelio-b1 branch February 6, 2017 20:20
spouliot pushed a commit to spouliot/xamarin-macios that referenced this pull request Mar 28, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants