Simulator's permissions
Enabling the permissions to the PhotoLibrary.framework
, Photo.framework
or any other framework which need authorization for the user can be problematic during running your unit tests. It might be nice to be able to pre-allow or disallow access for applications without user intervention to run the tests.
There are several ways in which this problem can be solved. One of the most simple is stubbing the access to these frameworks using dependency injection. I’m not going to go further in that aspect as isn’t the purpose of this article.
In this article, I’ll cover how we can disable the permissions when we run our test in the simulator in a completely different way.
TCC.db
TCC.db
is an SQLite3 database, located in the user’s developer library in the simulator. This database contains between other information the allowance/disallowance to these frameworks.
Let’s take a look with the built-in sqlite3 command-line tool inside the database:
There are several tables inside the database, but we are going to focus specifically on the access
table. Let’s make a query to the table to see its content:
There are several columns in that table but let’s pay attention specifically to three of them:
service
: Represents name of the service (Camera, Photos, Contacts, etc).client
: Represents the Bundle id of your app.allowed
: Represents if the service is allowed/disallowed using integer values.
It’s pretty trivial to update this database ourselves directly if we want to enable the access to any of these services, but we will talk about that later.
Services
In the service
column we can find several services that normally we use daily:
kTCCServiceAddressBook
kTCCServicePhotos
kTCCServiceCalendar
kTCCServiceWillow
kTCCServiceContacts
kTCCServiceCamera
kTCCServiceMicrophone
kTCCServiceReminders
kTCCServiceTwitter
kTCCServiceAll
kTCCServiceMotion
kTCCServiceMediaLibrary
kTCCServiceSiri
As you might be already figured out the names are pretty intuitive and represent the name of the most used services in any iOS app. I didn’t list all the available services above, there are several more 🧐 out there.
Allowing access to the services.
To be able to modify the access
table I modified a small script in this SO question.
Let’s go through the steps in the script:
- We get the current logged username with
id -un
. - We remove any trailing string in the saved
$currentUserID
. - We get all the folder locations in which exist a
TCC.db
- We apply regex to match the database name and then we modify the database table
access
to allow access to thePhotos.framework
.
To run the script from the console you should make it executable running:
I used the name disable-permissions
, but you can feel free to put anyone you want.
Run Script Phase
Now we have the script we can run it from the console and it will enable/disable any permission we want. But would be cool if this script could run as part of our build process but only when we run our tests 🤔 ?. Guess what? We can do it using the Run Script Phase
in Xcode.
In my previous article, I explained how to set different configurations for your testing environment. Once we have defined the Testing
configuration in our project we can create a new Run Script Phase
to run our defined code as part of the build process.
Go to the Project’s Navigator and select your project target. Select Build Phases and click the + button to add a new Run Script Phase
:
As we want to disable the permissions only for the testing configuration we need to include the following before running the script:
I keep my scripts in a scripts
folder in the root directory of the app to keep everything organized, pretty much you need to change the locations regarding the one you created. Now every time we build our project before running our unit tests the script would be executed and the permissions enabled for each service we defined in the script.
Libraries
While I was researching I found two good libraries using similar approaches. There are both written in Objective-C, but who knows maybe soon we can see a Swift port for any of these:
Conclusion
We covered how to pre-allow the permissions in an app to be able to run the unit tests and how to execute it automatically as part of a Run Script Phase
.
Thanks for reading! 🎉.