Making a Share Extension that accepts text and URLs in combination with CoreData Swift 3
As I mentioned in my previous blog on action extensions I was creating a replica of the default Apple notes application which also included creating a share extension that would allow my notes app to accept URLs and text from within other apps. The main challenge here is that I wanted my share extension to be able to create or add to my existing notes that are being saved in CoreData.
The entire project can be located here (disclaimer I am still adjusting it):
Contribute to CustomNotes development by creating an account on GitHub.github.com
I did quite a bit of looking online and never really found a great example of what I was trying to do. Some of them included a way of using NotificationCenter within your CoreDataStack’s init and using that to pass data, but I found a way that is much easier and seems to work great!
Share Extensions, like all others, are basically their own application, however they need to be included as part of a project instead of a stand alone one. To make a share extension in a project go to File -> New -> Target and select Share Extension.
A popup will appear after creation asking if you want to make this active. All this means is when you build the application it’s going to build the extension not the application. You can select no or yes and manually change what you want to run in the top left of Xcode :
As I mentioned before I made my share extension in my notes application. To allow both of these applications to communicate with each other the first thing that needs to be done is to connect your application and extension to the same app group. To do this go to the project capabilities and turn App Groups to ON for both your app and extension and add them to the same group:
Within the share extension itself you’ll notice it comes with a ShareViewController of type SLComposeServiceViewController with three functions inside:
isContentValid() -> Bool
configurationItems() -> [Any]!
To explain what each of these does you can quickly run your extension with Safari and then when you click on the activity button you should see your extension loaded like so:
After you click on it without writing any code you will see this come up:
As you can see it’s like it’s own little app within Safari. Going back to those three functions as you might have guessed didSelectPost() gets fired when you click on the post button. isContentValid() is actually checking to see if the content we are trying to share with our application is the proper type. This function determines whether or not the Post Button is enabled. Lastly the configurationItems() function is there to enable us to post our info to a specific object. In this example I want to be able to create or add to an existing note in my CustomNotes application via Core Data.
Before we start writing code in our extension lets go back to my customNotes app to provide some context and start the process of sharing my coreData with my extension.
There are other resources out there on Core Data so I won’t go too much into it aside from what’s needed in relation to the share extension. I personally like to make a core data stack that handles all of my core data needs. In this example my data model is called “NotesModel” which has an Entity of type “Note” and two attributes. One called “title” of type String and another called “date” of type Date, which in CoreData is actually of type NSDate. Below is my CoreDataStack:
Now everything here looks pretty standard aside from something you may have noticed when I initialize my NSPersistentContainer. You can see that I’ve created my own custom container called CustomPersistentContainer. The reason I am doing this is because I want to set the directory url to that of my app group. This is important to allow my share extension and my application to store data in the same location.
One last thing before we got back to our extension is we want to add our extension to our NotesModel’s Target Membership which you can see when you select your model and open the utilities inspector. Just check the box and the magic happens. Doing this replaces the need to Notifications or other methods people use to link the core data.
Now in this example I simply want to allow my notes app to accept URLs and text. The first step here is to go into your extension’s plist file and change the NSExtensionActivationRule to a dictionary and then add NSExtensionActivationSupportsWebPageWithMaxCount as a Number with a value of 1 and NSExtensionActivationSupportsText as a Boolean with a value of YES. Also while we are here you can change the Bundle display name to whatever you want as this name is what actually shows up when the activity controller is set.
Now in our ShareViewController let’s create a few variables to hold onto our data. For one thing I want a variable to hold onto my url info, in my example I just want the url as a String. Also if I’m just trying to share text I want a variable to hold onto whatever that text is.
To actually check to see if what I am trying to share is of type URL or text I need to add the following code to viewDidLoad() in my ShareViewController:
As you can see when we find the proper types we set our string properties to the values found. Now we can use those properties to see if our content is valid to allow us to post to our application:
Now that we have our content we want to post it, but where do we want to post it? To our CustomNotes app of course! Ultimately we want our extension to look like this when we open it to be able to choose from our existing notes or create a new note.
Do enable this little select note option on the bottom we need to play around with our configurationItem function, but we want to populate it with our notes from CoreData so lets add a few properties to our ShareViewController. One to store all our notes, one to create our instance of CoreDataStack and another to hold the value of the note we’ve selected to post to.
var selectedNote: Note!
let store = CoreDataStack.store
var notes = [Note]()
Also let’s add these to our viewDidLoad() so we can populate notes:
notes = store.fetchedNotes
When we click on “New Note” above it will actually open up another viewController that allows us to select the note we want to add to. To do this we want to create another ViewController and since it’s going to be a list of current notes lets go ahead and make it a TableViewController.
As you might have guessed in our tableview we can select the note we want to change, but we need to pass it back to our ShareViewController so let’s go ahead and create a protocol and have our ShareViewController conform to it.
So now let’s go into our configurationItems() function to set up our little bottom zone:
Last thing we need to do in our ShareViewController is actually post our item back to core data:
And now your share extension and application can communicate and work together!!
One last item is to have your notes application update with any new notes or changes when it’s already open in the background of your phone. To do this just add the below code to your initial view controller that displays your notes:
I hope you found this helpful. If you have any further insight or questions please feel free to comment.
I will say that while building this extension I came across an interesting issue where when someone was trying to add to an existing note in the extension and then opened the note app from the background and then went into that existing note it would not show the addition. To work around this instead of changing a note I would create a new note and delete the old one. You can see this on lines 12–20 on my didSelectPost.swift gist (2nd to last one posted).