tumblr / xextensionitem Goto Github PK
View Code? Open in Web Editor NEWEasier sharing of structured data between iOS applications and share extensions
License: Apache License 2.0
Easier sharing of structured data between iOS applications and share extensions
License: Apache License 2.0
This library currently has a Core Location dependency as a “location” parameter seems commonly useful enough to warrant inclusion. I can understand developers not wanting to pull this in so am interested in suggestions on how to best structure this. Perhaps a XExtensionItem+Location
subspec that augments the core library with these capabilities?
Mutability should be avoided. A great way to provide an easy-to-use object creation API is by using the builder pattern.
Unfortunately, many Objective-C developers seem adverse to using builders. As such, the first version of this library includes the XExtensionItemMutableParameters
subclass, which allows one to create an XExtensionItemParameters
instance without using its initializer (which takes a large number of arguments).
I'd prefer a builder but want this library to be as approachable as possible. The introduction of a builder class means XExtensionItemMutableParameters
would be deleted altogether, and XExtensionItemParameters
's large initializer would be made private. The only way to get an instance would be to use the builder object and then call build
at the end.
First it should check CFBundleDisplayName
, then CFBundleName
. This gives the developer maximum control on how their application name is represented and matches Springboard.
This project was inspired by x-callback-url, and is clearly named as such. Is this a good name or does anyone have an idea for something better?
On one hand, XExtensionItem is:
x-
prefix has long been used for custom HTTP headers, and x-callback-url continued the tradition. It signifies an unofficial, user-land addition on top of a platform.NSExtensionItem
.On the other hand, XExtensionItem is:
Is two Xcode projects confusing? @Twigz seems to think so. The arguments I can remember him making:
pod lib create
I’ve been unable to figure out how to test the Weibo activities, so for the time being am assuming that they do not know how to parse incoming NSExtensionItem
input. But they may, in which case we’d want to pass them a fully populated item instead of just a placeholder object.
.cocoadocs.yml
file..cocoadocs.yml
(still currently showing the default styles)As per @prendio – allow a default subject to be provided rather than explicitly registering subjects for individual activity types using the registerSubject:forActivityType:
method added in this pull request.
(This isn’t particularly pressing since there isn’t public API for extensions to read this value, and only Mail and Messages support subjects out of the built-in activities (which only change once a year, at most), but we only just realized that the latter does yesterday, so who knows what else we could potentially be missing?)
A couple of options:
attributedTitle
value if it exists and a subject was not explicitly registered for a given activity type@property (nonatomic, copy) NSString *defaultSubject;
propertyregisterSubject:forActivityType:
with a nil
activity type will register it as the default. I think I like this one the best.Thoughts?
Consider the following use case: in general, you are sharing a URL but want to provide a custom HTML email if the Mail activity is chosen. You’d write code like the following:
[[XExtensionItemSource alloc] initWithURLProvider:^(NSString *activityType) {
if (activityType == UIActivityTypeMail) {
return HTMLEmailString;
}
else {
return URL;
}
}];
This wouldn’t compile though, since the URL provider block is typed to return an NSURL
. Should it be typed to return an id
instead? This way the developer can return non-URL types for activities that they know can take those non-URL types as inputs.
In the wiki we have written:
## Could be supported using custom parameters
* Custom URL slug
* Post type (for apps that specifically only want to create posts of a certain type)
* Post to Twitter (yes/no)
* Post to Facebook (yes/no)
I think it should be modified to read:
## Could be supported using custom parameters
* Suggested custom URL slug
* Post type (for apps that specifically only want to create posts of a certain type)
* Shareable to Twitter (yes/no)
* Shareable to Facebook (yes/no)
It would just pass a single item through rather than providing any of the benefits, but at least would make it easier for apps that still support iOS 7 to simply drop XExtensionItem in.
The current name makes this property sound a little more like a mapping and less like it actually has content inside of it!
Potential replacements: alternateContentRepresentations or alternateRepresentations, or if we want to maintain the typeIdentifier part, alternateRepresentationsByType, alternateContentRepresentationsByType, alternateRepresentationsByTypeIdentifier, etc
You can return an NSData
instance but there’s no way to specify a type identifier that differs from the one that the XExtensionItemSource
was initialized with.
To try and determine what modifications we may want/need to make to XExtensionItem in the short term, I think it’s worthwhile to consider how we’d want the library to function if we only needed to support iOS 9.
XExtensionItem for iOS 8 has two goals:
Therefore, XExtensionItem
usage on iOS 9 only could look something like the following:
// Application
// `XExtensionItem` no longer wraps your attachments, instead it’s just an additional metadata object
[[UIActivityViewController alloc] initWithActivityItems:
@[@"Apple homepage", [NSURL URLWithString:@"http://apple.com"], xExtensionItemSource]
applicationActivities:nil]
// Extension
// Rather than treat *every* item as something that can be wrapped by an `XExtensionItem`, just specifically look to see if one was passed in
for (NSExtensionItem *item in self.extensionContext.inputItems) {
if ([XExtensionItem isXExtensionItem:item]) {
XExtensionItem *xExtensionItem = [[XExtensionItem alloc] initWithExtensionItem:item];
// Access tags, app attribution, custom application parameters, etc.
}
else {
// Process regular, non-XExtensionItem item
}
}
Ideally, we’d release what we currently have (let’s call it 1.0) and add a section to the README documenting how we envision the library changing in order to eventually better support iOS 9 exclusively. Then at some point we cut a new release for developers who only support 9 (let’s call this 2.0), and those who continue to support 8 can just use the older version.
But the problem here is interoperability. An application that uses XExtensionItem
1.0 (e.g. provides a single XExtensionItem
instance as their lone activity item) wouldn’t be compatible with an extension that uses 2.0 (I need to think about it a little more but I think an app that uses 2.0 will still work just fine with an extension that uses 1.0).
How can we resolve this? It seems as though modifying the extension-facing API to support both types of input is the most future-proof course of action. Otherwise, we’d have to either:
As requested by @prendio2:
Certain built-in system activities (Facebook/Twitter/Vimeo/Flickr and possibly Weibo) can consume an NSExtensionItem
as input, and display the attributedContentText
value. XExtensionItem
does not currently provide a way to customize this attributedContentText
value on a per-activity item basis, however.
One could use the new registerItemProvidingBlock:forActivityType:
method added in this pull request, but if they wanted to pass an image/string/etc. to the Twitter activity as well, the caller would need to build an NSExtensionItem
themselves in order to provide a Twitter-specific attributedContentText
value. This is suboptimal as part of XExtensionItem
’s mission is to prevent users from having to construct NSExtensionItem
instances themselves.
This isn’t as useful for Flickr, Vimeo, or Facebook, since it’s safe to assume that anyone who shares using those extensions also has the apps installed – if so, the app’s extension takes precedence and promptly throws the attributedContentText
on the floor ¯_(ツ)_/¯
Probably safe to assume that Twitter will add an official share extension of their own at some point, but this would still be useful in the interim.
I'm not sure if this will be an issue or not, but does using @import
assume that the project that will be using this pod has modules enabled? I know that if you use modules #import
is automatically turned into an @import
, but I'm not sure if it goes the other way around.
Would this actually be useful? Not so sure that it would be, honestly.
[!] Invalid `Podfile` file: undefined method `source' for #<Pod::Podfile:0x007f91739ab850>. Updating CocoaPods might fix the issue.
# from /private/tmp/CocoaPods/Try/XExtensionItem/Example/Podfile:1
# -------------------------------------------
> source 'https://github.com/CocoaPods/Specs.git'
#
# -------------------------------------------
[!] Invalid `Podfile` file: undefined method `source' for #<Pod::Podfile:0x007fe470af43b0>. Updating CocoaPods might fix the issue.
# from /private/tmp/CocoaPods/Try/XExtensionItem/Example/Podfile:1
# -------------------------------------------
> source 'https://github.com/CocoaPods/Specs.git'
#
# -------------------------------------------
Shouldn’t be much work, I just haven’t put any thought into supporting usage on OS X yet.
The more I think about this, the more I think we would actually want to be able to key titles off of activity types, if we’re going to use them as the subject that the UIActivityItemSource
provides. I realized this by sharing from the Tumblr app to the Tumblr extension and realizing that our subjects written specifically for email don’t translate well to quote posts or link posts, where we consume the title
field in our extension.
Add this back to the .travis.yml
file:
- pod lib lint --quick
Once this command actually looks for JSON podspecs (CocoaPods/CocoaPods#3477)
I'm a little worried that XExtensionItemSource is a little bit confusing/difficult to use for newcomers. Normal users might not be familiar with what a "placeholder item" is or why it's here.
It seems confusing that you can initialize an XExtensionItemSource with an attachments array or use the registerItemProvidingBlock: method and what the difference is between those things. And how are people supposed to understand how attributedContentText
ties into those things? I worry that you have to be an expert in how UIActivityItemSource/NSExtensionItem/NSItemProvider work in order to use this API.
Compare that to UIActivityViewController, which (in the simple case) is easy as can be - literally just pass it whatever object you want. I wonder if we should be trying to offer a slightly higher-level API that abstracts away a few of these things. Particularly the placeholder item, the attachments array, and the attributedContentText/attributedTitle properties. I think that attributedTitle and subject are fundamentally the same concept and could be easily merged (since the activities that use subject never use attributedTitle)
Working on ideas for alternatives.
Talk with @chrismaddern to make sure we’re on the same page with regards to which parameters apps should use to attribute themselves.
Here’s what we have so far: https://github.com/tumblr/XExtensionItem/blob/master/XExtensionItem/XExtensionItemSourceApplication.h
(An architectural suggestion from @khanlou)
XExtensionItemParameters
implementation becomes a lot simpler. Adding new fields doesn’t require modifying description
, isEqual
, hash
, etc.XExtensionItemMutableParameters
wouldn't be able to read from or write to the NSExtensionItem
instance internal to its superclass. As @khanlou put it “they would both be standalone classes basically.”Currently leaning towards “no” but getting rid of the mutable subclass in favor of a builder would certainly make this suggestion more appealing.
Otherwise an application has no way to determine which representation is the “canonical” one (as per @AriX).
https://iosdevelopers.slack.com/files/arix/F04LGFWRQ/rdar20599026.txt
Provide hooks for giving specific items to specific activities, instead of simply passing the same fully-populated NSExtensionItem
everywhere (e.g. to send different representations to Twitter, Facebook, Mail.app, etc.)
I can think of three different ways to register alternative items off the top of my head (these methods would all be on XExtensionItemSource:
@implementation XExtensionItemSource
...
// Simplest – just provide a value that should be used in place of the default `NSExtensionItem` if a specific activity is picked
- (void)registerItem:(id)item forActivityType:(NSString *)activityType;
// More complex – provide a block that returns a value, to avoid necessarily creating values for activities that may never be selected
- (void)registerItemProvider:(void (^)())itemProvider forActivityType:(NSString *)activityType;
// Most flexible – provide an object that determines what item should be returned for an activity type
@property (nonatomic) id <XExtensionItemProvider> alternativeItemProvider;
@end
@protocol XExtensionItemProvider
- (id)itemForActivityType:(NSString *)activityType;
@end
Usage would look something like:
[itemSource registerItem:twitterLengthSummaryString forActivityType: UIActivityTypePostToTwitter];
[itemSource registerItemProvider:^{
// Lazily compute the Twitter-specific summary
return twitterLengthSummaryString;
} forActivityType: UIActivityTypePostToTwitter];
@implementation MyAppCustomActivityItemProvider <XExtensionItemProvider>
- (id)itemForActivityType:(NSString *)activityType {
if (activityType == UIActivityTypePostToTwitter) {
return self.twitterLengthSummaryString;
}
// Returning `nil` would mean the activity/extension gets the default `NSItemProvider` instead of something special
return nil;
}
@end
itemSource.alternativeItemProvider = [[MyAppCustomActivityItemProvider alloc] init];
I feel like @AriX will have opinions on this one.
To match what the NSExtensionItem
APIs themselves call these things.
Future-proof things a lil' bit.
To provide more robust sharing to activities like mail (subject) and Facebook/Twitter (thumbnail image):
- (UIImage *)activityViewController:(UIActivityViewController *)activityViewController thumbnailImageForActivityType:(NSString *)activityType suggestedSize:(CGSize)size
- (NSString *)activityViewController:(UIActivityViewController *)activityViewController dataTypeIdentifierForActivityType:(NSString *)activityType
- (NSString *)activityViewController:(UIActivityViewController *)activityViewController subjectForActivityType:(NSString *)activityType
The .sketch
file is in the repo, should be easy enough to re-export.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.