Tutorials | Andy Ibanez https://www.andyibanez.com Teaching you cool stuff since 2012. Mon, 19 Aug 2019 22:08:54 +0000 en-US hourly 1 https://wordpress.org/?v=4.9.10 39653125 HTTP/2 Apple Push Notification Service Introduction. https://www.andyibanez.com/http2-apple-push-notification-service-introduction/ https://www.andyibanez.com/http2-apple-push-notification-service-introduction/#respond Tue, 02 May 2017 18:00:33 +0000 https://www.andyibanez.com/?p=562 A few years ago, Apple announced new APIs to interact with their push notification services. In late 2015, these APIs were finally made available for developers to use. In this post we will talk about the very basics of using Apple’s new Push Notification services, with basic bash cURL code. 1. Advantages Let’s talk about […]

The post HTTP/2 Apple Push Notification Service Introduction. appeared first on Andy Ibanez.

]]>
A few years ago, Apple announced new APIs to interact with their push notification services. In late 2015, these APIs were finally made available for developers to use.

In this post we will talk about the very basics of using Apple’s new Push Notification services, with basic bash cURL code.

1. Advantages

Let’s talk about why Apple decided to make new APIs for push notifications in the first place.

Those who have worked with the first iteration of the system – the one that has existed since the iOS 3.0 days – know how daunting it was to get this to work. You had to provision two different push notification certificates, one for development, and one for production, and you had to keep track of them. Not only that, but the API was entirely socket based, and it was very unreliable. You had to take into account what could happen if you sent a lot of push notifications at once – if you sent more than one notification per second, the notification would often get lost, and many of your users would never get the notification. So developers had to create timers and let the socket “rest” for a little while before sending another notification. For small sets of notifications, this was no issue, but imagine having to send 30,000 push notifications at once – waiting one second per notification would make it take 8 hours to send all the notifications to all the devices!

The new APIs are now HTTP/2 based, and it’s expected that the socket-based implementation will get deprecated soon and eventually removed, so you should start using the new APIs now.

One of the advantages to the new system is that you only need to create one certificate that can be used for production and development deployments of your app. Yes, that’s right, you no longer need to keep track of two sets of public key-pairs to achieve this anymore.

You can simply generate a Production push notification certificate and it will work for both cases. In my tests, creating a Development push notification certificate did not seem to work (I got an error message in the lines of curl: (16) Error in the HTTP2 framing layer).

And yes, if you are using Fastlane as part of your development flow, you can simply use the pem command to generate a production certificate. You never have to specify you want a development certificate because you will never need it anymore. Woot!

Type pem, fill in your Apple ID and app bundle identifier, and you are ready to go. You can use that set of certificates for both development and production push notifications.

And to make things even better, you don’t actually need a push notification certificate for this anymore. It now supports JWT-based authentication tokens. I will try to explore this possibility in a future post. This post will focus on using certificates for your notifications still, but keep that in mind when you decide to implement your own push notifications system.

Another advantage is that the new system is based on HTTP/2. Most developers know how to work with the HTTP protocol instead of using pure raw sockets. This makes it very easy to write a push notification implementation and to test them easily (you can use cURL in your terminal for this).

Finally, one of the biggest advantages is that you no longer need to implement the feedback calls to get a list of devices that no longer exist. The new system gives you back standard HTTP codes. If you are sending a push notification to a device that is no longer valid, the service will return a 410 error, indicating that you can deal with that token in your database accordingly.

All the HTTP status codes return something meaningful to you. Other than 410, you can also receive 413 when your payload is too large, or 403 when there is something wrong with your certificate or authorization token. Thanks to this, debugging your own APNS systems is much easier to do, and this was not very easy to do with the old one.

A full list of status codes can be found Apple’s Developers Website, scrolling down to Table 8-4.

3. A Test Environment for Push Notifications

In order to take advantage of these new APIs in your development environment, you need to install cURL v7.38 or newer, and you need to specify the -with-nghttp2 flag in order to support HTTP/2.

You can do this easily on your Mac by installing Homebrew and typing:

brew install curl --with-nghttp2
brew link curl --force

While macOS does come with cURL installed, it comes with an old version that does not support HTTP/2.

Then you need your push notification certificate. Using Fastlane, you can just write

pem

And follow the next prompts to generate your push notification certificate. The next two prompts will only ask you for your Apple ID and bundle identifier.

That’s it! With this we can send push notifications to your device.

Remember to ensure your deployment server also has a supported version of cURL in order to avoid issues. At the time of this writing, HTTP/2 is not widely used yet, so you may need to compile cURL with the -with-nghttp2 flag yourself.

#4. Playing Around with new Push Notificaitons

Once you have a push notification token received from your app, you can test this on your terminal.

We will use the token 34828574985 here (very pseudo, yeah) and the bundle identifier com.andyibanez.APNSFoobers for this example:

curl -d '{"aps":{"alert":"YOUR ALERT","sound":"default"}}' --cert "PATH_TO_YOUR_PUSH_CERTIFICATE.pem":"" -H "apns-topic: com.andyibanez.APNSFoobers" --http2 https://api.development.push.apple.com/3/device/34828574985

The push notification payload is the same one. It needs the "aps" key. Everything else you need to specify is your bundle ID, device token, and push notification certificate (PEM file).

The above example will send a development push notification. For production, simply change api.development.push.apple.com to api.push.apple.com and that’s it. You can use the same PEM file, and the only thing that could change is the token ID.

You can easily adapt this code to any language. I will try to write the PHP cURL based version of this in the future.

The post HTTP/2 Apple Push Notification Service Introduction. appeared first on Andy Ibanez.

]]>
https://www.andyibanez.com/http2-apple-push-notification-service-introduction/feed/ 0 562
rclone and Encryption Tutorial https://www.andyibanez.com/rclone-encryption-tutorial/ https://www.andyibanez.com/rclone-encryption-tutorial/#comments Sat, 11 Feb 2017 19:52:00 +0000 https://www.andyibanez.com/?p=552 rclone is a command line tool, similar to rsync, with the difference that it can sync, move, copy, and in general do other file operations on cloud services. You can use rclone to create backups of your servers or personal computers or to store your files in the cloud, optionally adding encryption to keep them […]

The post rclone and Encryption Tutorial appeared first on Andy Ibanez.

]]>
rclone is a command line tool, similar to rsync, with the difference that it can sync, move, copy, and in general do other file operations on cloud services. You can use rclone to create backups of your servers or personal computers or to store your files in the cloud, optionally adding encryption to keep them safe from prying eyes, which is what this post is about.

rclone supports the most popular cloud storage services including but not limited to Dropbox, Amazon Cloud Drive, Google Drive, and OneDrive. You can get unlimited Amazon Cloud Storage for $60 a year (update – this is no longer true for US-based accounts and it now costs $us 60 per TB per year), and unlimited Google Drive Storage if you pay for a G Suite account (around $10 for a domain per year) for $10 per month. G Suite lists unlimited storage starting when you have 5 users or more, but the guys at reddit.com/r/datahoarder state that, while the limit is there, Google doesn’t check for your usage so you can have unlimited storage as well (you do get “official” unlimited storage once you have 5 people in your G Suite account). I cannot verify the validity of this claim currently (Update – It’s real, guys).

Getting Started With rclone

Using the command line can be daunting, but it doesn’t have to be. rclone’s commands are nicely structured and are very easy to do. The initial configuration is straight forward and it doesn’t take too long.

Installation

You can skip this section if you have experience with rclone.

The Installation Page has instructions to install on your favorite Operating System. rclone runs in Linux, macOS, and Windows.

If you use macOS, you can install it with a single command using Homebrew:

brew install rclone

If you don’t use Homebrew, refer to the link above to install rclone without it.

Configuring and Using a Normal rclone Remote

You can skip this section if you already have experience with rclone, if you know how to configure a remote, and if you know how to use the basic rclone commands (copy, move, etc).

Before you can use an encrypted remote, you need to understand how to use normal ones.

What is a remote, anyway?

I have been saying this word a lot, so it might not be obvious what it means. rclone’s base concept is the remote, and it’s simply a cloud service that you have configured to use with rclone. For example, if you configure your Dropbox account, then it becomes a remote for rclone. The same with your Google account – Configure your Google account, and it will be a remote for rclone.

rclone config

rclone config is the command you use for configuration purposes, including creating remotes. In this example, I will configure a standard Gmail account. Go ahead and type this in your terminal:

rclone config

If you already have some remotes, they will be listed at the top. The output of the command will be something like this:

Name                 Type
====                 ====
AmazonCloudDrive  amazon cloud drive
EncryptedACD      crypt

e) Edit existing remote
n) New remote
d) Delete remote
s) Set configuration password
q) Quit config
e/n/d/s/q>

Naturally, you won’t see any remotes at the top if you don’t have any yet. Go ahead and press n to create a new remote, followed by enter. The first thing it will do is to prompt you for a name. The name of the remote can be anything you recognize. Should not contain spaces. I will name mine aibanezDrive.

Following the name, you will be prompted for a Storage. I am configuring Google Drive, so I will choose option 7.

Many providers will prompt you for a client_id and a client_secret. You don’t need these most of the time, and you should only use them if you created a custom app for your remotes. In my experience, I haven’t had the need to do that. You can press enter without writing anything at both prompts.

Later you will be asked if you want to use auto config. You will say yes to this if you are not running a headless system (no GUI). By choosing this option, rclone will launch a browser window prompting you to log in to your account (in this case, Gmail). You will see the standard login flow for your provider. After you are done with the credentials and allowing rclone to use your storage account, you will see a screen saying “Success!” and it will ask you to look back at your rclone terminal window.

Success

You will see something like this:

[aibanezDrive]
client_id = 
client_secret = 
token = {...}

Press y to save the remote. Then press q and enter to exit the configuration prompt.

To configure that your remote was saved successfully, you can run rclone listremotes. This will show you all your remotes, with the name you gave them. On my system, this prints:

AmazonCloudDrive
EncryptedACD
aibanezDrive

Trying out our rclone remote

Now that we have created a remote, we need to try it out! In this example we will create a list of files and directories and upload them to our Google Drive account using rclone. I need to remind you that we are currently playing with a non encrypted remote, so don’t choose any files you wouldn’t store unencrypted in the cloud. We will create the encrypted remote at the end of this tutorial.

For this part, I will go to ~Desktop and create a directory called Cards there. I will create a plain file and two directories which contain more plain files there. Essentially, I will be creating this:

Cards
 - Sakura
   - The Mirror
   - The Fly
   - The Sword
   - The Arrow
 - Shaoran
   - The Time
   - The Storm
   - The Return
   - The Freeze
 - None.txt

Following the convention of using Card Captor Sakura examples, I will create two folders for both main characters along with a few cards they caught in the show, and a None.txt file that would contain a list of cards none of them caught.

If you want to do the same, just run these commands below to create the same folder structure:

mkdir ~/Desktop/Cards && cd ~/Desktop/Cards
mkdir Sakura Shaoran
touch Sakura/The\ Mirror
touch Sakura/The\ Fly
touch Sakura/The\ Sword
touch Sakura/The\ Return
touch Shaoran/The\ Time
touch Shaoran/The\ Storm
touch Shaoran/The\ Return
touch Shaoran/The\ Freeze
touch None.txt

Copying the local directory to Google Drive

The command to copy files and directories is very straight forward.

Copy to local directory
rclone copy LOCAL_DIRECTORY_OR_FILE REMOTE_DIRECTORY

Note that you can copy both entire directories or just files within them.

The remote path starts with REMOTE_NAME:. Since in this example we are copying to the aibanezDrive remote, you would write aibanezDrive:, followed by the full path to copy to (starting with a /).

Copy from the remote directory

Just like you can copy files to your remote, you can also copy from the remote to your local computer. Otherwise it would be quite useless!

rclone copy REMOTE_DIRECTORY_OR_FILE LOCAL_DIRECTORY

rclone Copying Example

If you are using the same folder as me, you can copy and paste this command to see how it works. If not, you will have to modify REMOTE_DIRECTORY_OR_FILE and LOCAL_DIRECTORY as needed.

So to copy our Cards directory to /Cards, you would write this:

rclone copy ~/Desktop/Cards aibanezDrive:/Cards

After the operation finishes, you will see a small report on the operation:

Transferred:      0 Bytes (0 Bytes/s)
Errors:                 0
Checks:                 0
Transferred:            9
Elapsed time:       28.9s

(Please disregard the Elapsed Time for a few files. It is known the internet in my country sucks and there’s no really no way to get a better upload than 1mbps).

You can now go to your Google Drive account using your web browser. You will see the Cards directory at the root of your account.

Cards

Going inside that directory you will see the other two folders and the None.txt file.

Inside Cards

Finally going inside either the Sakura or Shaoran directories you can see their respective files as well.

Sakura

Now, if you want to copy something From your Google Drive account to your local computer, the steps are just as easy, with the only change being that the order of the local and remote folders are switched in the command.

Go ahead and upload any file to your Google Drive account. I uploaded a file called “IMG_1434.JPG” and I uploaded it to my root. If I wanted to copy this file from my Drive account to my Local computer, inside the ~/Desktop/Cards directory using rclone, I’d use this command:

rclone copy aibanezDrive:/IMG_1434.JPG ~/Desktop/Cards

And that’s it! Remember you can use the copy command for both directories and files.

Other Commands

rclone Supports a whole lot of commands as listed in their Documentation. Some useful ones are sync (it keeps a remote directory in sync with a local one), move, delete (remove the contents of a path), purge (remove a path and all its contents), ls (List all the objects in the specified path), lsd (list all the directories under the path), mkdir, and rmdir.

There is also a whole lot of interesting flags you can use with your commands, like --bwlimit to limit the amount of bandwidth rclone will use and --transfers to limit the amount of files that get transferred in batch, and others.

Other Notes

It’s important to note that some commands may not work or may behave differently based on what kind of remote you are using. The Storage Systems Overview page has a helpful table and a few notes for how commands may behave. There’s also a page for every supported remote that lists their quirks, features, and possible different behaviors for common commands.

Configuring and Using an Encrypted Remote.

We are finally at the main point of this post. Hooray!

Due to the nature of cloud storage, you may not want to store your files in a plain format, because that implies that they are storied unsafely in some other guy’s computer and they might be visible to someone else. If someone breaks into your cloud storage account, they can see your files. That wouldn’t be pretty.

At the same time, there’s some files that may be fine to store unencrypted. Maybe a list of groceries you need to buy, or other kind of files that there is no issue if other people see them or you just feel save storing them as is.

rclone supports the use of encryption, and it can be used in such a way that a remote can hold both encrypted and non-encrypted files.

The crypt Remote.

To use encryption, you create a crypt remote. This is an special kind of remote. If you configure any other kind of remote, you are creating a direct connection between your computer and another remote, and that’s what makes crypt different. Unlike the other remotes, crypt is placed on top of an existing remote in order to do its job, and is not a direct connection like the others.

The remote we created in this tutorial, aibanezDrive was a direct remote between your computer and Google Drive. We will now create a crypt remote that uses this remote as an underlying requirement for the encrypted one. So the crypt remote will encrypt the files, pass them over to your standard remote, and this one will end the encrypted files to the cloud.

Creating a crypt Remote.

Before we create an account, delete the /Cards folder from your Google Drive (if you created it), as we will be using this same directory to show how crypt works.

With that out of the way, we need to run rclone config again. Press n to create a new remote.

The one thing I don’t like about rclone is that, when listing your remotes, you can’t see what underlying remote a crypt one is using, so when prompted for the name, I recommend giving it a name that helps you identify both the crypt and the underlying remote it’s using. I will be naming it aibanezDrive_Crypt.

After the name, enter 5 to create a crypt remote.

This is where the configuration takes a weird turn, unlike the others. When prompted for a remote, you specify an existing remote, along with its path. The path you will choose will be the root of the remote.

Suppose you choose the root of your remote to be /Archive/Encrypted. This will cause the crypt remote to store all the encrypted files there, and also, when you refer to the root of the remote – say, aibanezDrive_Crypt:/ -, you will be referring to the whole /Archive/Encrypted directory in your storage of choice. In other words, your remote won’t be able to see anything outside this path you specified. If you are a UNIX user, you can think that your remote is chrooted to /Archive/Encrypted.

In this example, I want to archive encrypted files in aibanezDrive:/Archive/Encrypted. Note that we are not referring to the remote we are creating itself, but rather the underlying one.

Next you will be prompted if you want to encrypt the file names. The way I see it, yes is the only right choice here but you might not mind it if the file names are visible. Choosing yes has some complications all listed in the crypt section of the documentation. There’s issues with long file names and paths. But, in general, if your file names are below 156 characters, you should be fine on all providers, granted that some provides may not have this issue. Please refer to the documentation to see if you can find any info on filename length.

Choose 2.

Next you will be asked to create a password or to have one generated for you. Both options are strong and it depends on how much you trust the RNG of your system.

I will be writing my own.

Next you will be asked for the salt. In cryptography and security, a salt modifies a string so it’s hash is entirely different. The salt is what makes your password cat to be stored as entirely different strings in databases that store passwords.

You have three options this time: To provide one, have one generated for you, or to not use a hash at all (not recommended). Keep in mind that whether you provide one or have one generated for you, you will have to store in a safe place, as rclone will ask you for both when interacting with a crypt remote. Not providing the salt when required will result in you not having access to these files later.

I will be providing my own hash.

After providing the salt you will see the newly created remote:

[aibanezDrive_Crypt]
remote = aibanezDrive:/Archive/Encrypted
filename_encryption = on
password = *** ENCRYPTED ***
password2 = *** ENCRYPTED ***

Press y at the prompt and then q.

You now have your newly created remote and you can use it exactly the same you would use any other remote. For this example, I will be running exactly the same example as the one I used to demonstrate how copying works with a normal Google Drive remote, except I will just change the remote names and paths.

rclone copy ~/Desktop/Cards aibanezDrive_Crypt:/

After the script is done, you can verify if they exist in your Drive account, but you won’t be able to see their contents (or filenames) at all! Remember that /Archive/Encrypted is the root of the remote, so you your Google Drive will have this same path where you will see the encrypted files.

Crypt Results

Note that, unlike a normal remote, you cannot just add files and restore them. They will not get magically encrypted and you will just add normal files without encryption if you uploaded them directly. So if you want to encrypt the file names, you can only do it with your crypt remote with rclone

And since they are encrypted, the only way to download them is with rclone itself as well. So normally, you would want to list the files that are available in the controller in order to do that. You can do that with the ls and lsd commands:

rclone ls aibanezDrive_Crypt:/
Andys-iMac:Cards andyibanez$ rclone ls aibanezDrive_Crypt:/
   358768 IMG_1434.JPG
        0 None.txt
        0 Shaoran/The Freeze
        0 Shaoran/The Return
        0 Shaoran/The Storm
        0 Shaoran/The Time
        0 Sakura/The Sword
        0 Sakura/The Mirror
        0 Sakura/The Return
        0 Sakura/The Fly

Note that rclone really tries to list everything when just using the ls command. You an list the directories only using lsd (this would not print IMG_1434.JPG and None.txt in this case), or if you just want to view the files (not directories) at the top level, you can run the ls command with the --max-depth=1 flag:

Andys-iMac:Cards andyibanez$ rclone ls --max-depth=1 aibanezDrive_Crypt:/
   358768 IMG_1434.JPG
        0 None.txt

Now suppose I want to download the Sakura folder to my computer:

rclone copy aibanezDrive_Crypt:/Sakura ~/Desktop/restored

This will download the contents from Sakura into ~/Desktop/restored.

Conclusion

rclone is a fantastic tool for creating backups and for using cloud storage. If you want to use rsync but would rather store your files someplace else, rclone is the perfect tool to do it. It supports many cloud providers, it’s open source, it’s under active development, and it supports cryptography out of the box.

The post rclone and Encryption Tutorial appeared first on Andy Ibanez.

]]>
https://www.andyibanez.com/rclone-encryption-tutorial/feed/ 9 552
Introducing fsduplicates https://www.andyibanez.com/introducing-fsduplicates/ https://www.andyibanez.com/introducing-fsduplicates/#comments Sun, 26 Jun 2016 22:53:15 +0000 https://www.andyibanez.com/?p=546 Just very recently, I wrote a blog post where I suggest people use audio fingerprinting techniques to better search and identify for duplicate songs in their libraries instead of using metadata. I also provided a half-assed script to attempt to do it easily. That script simply did not work as expected, and ultimately writing something […]

The post Introducing fsduplicates appeared first on Andy Ibanez.

]]>
Just very recently, I wrote a blog post where I suggest people use audio fingerprinting techniques to better search and identify for duplicate songs in their libraries instead of using metadata. I also provided a half-assed script to attempt to do it easily.

That script simply did not work as expected, and ultimately writing something that would scan for duplicates was slightly more complicated.

Introducing fsduplicates

Today I’m introducing a better tool that I have built since I wrote that blog post. The tool is called fsduplicates, and it is a command line tool that interacts directly with the AcoustID database.fsduplicates is incomplete, but it is pretty usable for now. It is written in Swift 3 and it is fully open source.

fsduplicates has two main functions: To scan and fetch the AcoustIDs of each song in the AcoustID database (called the indexing process), and duplicate indentification. Using the tool is really easy.

The indexing content is done using the -f flag. Note that you can issue the -v flag to all commands to trigger verbosity.

fsduplicates -f DIR_TO_SEARCH DIR_TO_OUTPUT

An example:

fsduplicates -f /Volumes/iTunes/Music/Nightwish ~/Documents/fsduplicates_nightwish

This will recursively index the contents of the folder you passed as DIR_TO_SEARCH. Do note that this process can take a long time, not only due to the fingerprinting process but to play well with AcoustID’s rules. All the results will be dropped in DIR_TO_OUTPUT (also called a Library). fsduplicates will not move or delete any files. The Library directory will contain three plain text files with info about the songs:

  • library contains a list of all the songs it indexed. This is a simple list of file paths to songs. This is used by fsduplicates to prevent reindexing songs and save you time. The advantage of having this file is that, if you are indexing a large directory and you want to stop, the reindexing process can be restarted later without losing progress.
  • fps_library contains a list of AcoustIDs and the files that match them. If you wanted to analyse your duplicates manually, you would use this pair. Each line stores the data as acoustid:filepath to make it easier to parse using standard Bash tools.
  • no_fps_library contains a list of file paths that did not have a matching fingerprint in AcoustID’s database. Consider contributing the fingerprints of these songs to their service to help them improve.

After the indexing process is done, you can show results using the -s flag.

fsduplicates -s LIBRARY

Example:

fsduplicates -s ~/Documents/fsduplicates_nightwish

This will group fingerprints with file paths that matched them to make it easier to see which songs are duplicated.

-----------------------------------
Showing Duplicates for 08fcc296-7d3f-483f-86ea-cfbe725d291d:
1. /Volumes/iTunes/Music/Nightwish/Bless The Child/02 The Wayfarer.m4a
2. /Volumes/iTunes/Music/Nightwish/Century Child/12 The Wayfarer.m4a
3. /Volumes/iTunes/Music/Nightwish/Ever Dream/03 The Wayfarer.m4a
4. /Volumes/iTunes/Music/Nightwish/Highest Hopes/2-01 The Wayfarer.m4a
5. /Volumes/iTunes/Music/Nightwish/Tales From The Elvenpath/15 Wayfarer.m4a
6. /Volumes/iTunes/Music/Nightwish/Wishsides/2-03 The Wayfarer.m4a
-----------------------------------

-----------------------------------
Showing Duplicates for e9ffe05f-ad4a-4906-afca-26cbbf628787:
1. /Volumes/iTunes/Music/Nightwish/Bless The Child/09 Lagoon.m4a
2. /Volumes/iTunes/Music/Nightwish/Century Child/11 Lagoon.m4a
3. /Volumes/iTunes/Music/Nightwish/Highest Hopes/2-08 Lagoon.m4a
4. /Volumes/iTunes/Music/Nightwish/Tales From The Elvenpath/14 Lagoon.m4a
5. /Volumes/iTunes/Music/Nightwish/Wishsides/2-07 Lagoon.m4a
-----------------------------------

If you pass in the -i flag, you will be able to choose an action for each group. Currently, there’s only the option to create symbolic links of each file in the group in the directory or to skip the group. In the future I will add more actions, like the ability to directly delete the duplicates or move them entirely to the Library path.

fsduplicates -s -i ~/Documents/fsduplicates_nightwish

Sample output:

-----------------------------------
Showing duplicates for 08fcc296-7d3f-483f-86ea-cfbe725d291d:
1. /Volumes/iTunes/Music/Nightwish/Bless The Child/02 The Wayfarer.m4a
2. /Volumes/iTunes/Music/Nightwish/Century Child/12 The Wayfarer.m4a
3. /Volumes/iTunes/Music/Nightwish/Ever Dream/03 The Wayfarer.m4a
4. /Volumes/iTunes/Music/Nightwish/Highest Hopes/2-01 The Wayfarer.m4a
5. /Volumes/iTunes/Music/Nightwish/Tales From The Elvenpath/15 Wayfarer.m4a
6. /Volumes/iTunes/Music/Nightwish/Wishsides/2-03 The Wayfarer.m4a
-----------------------------------
What do you want to do?:
(s)ymbolic link all to Library       (i)gnore

OPTION: 

Creating symbolic links is useful, because you can then drop them into a music player to listen to them.

Downloading fsduplicates

Head over to the project page on Github and download it from there. The install and more complete usage instructions are on the README.md. The project is open source and I welcome contributions, even if they are just cleaning the hacky code I wrote.

Warnings and other notes

I consider this product to be mostly incomplete, but it’s complete enough for my needs. You agree to use this tool under your own risk and not blame me if it creates a black hole in your computer.

The post Introducing fsduplicates appeared first on Andy Ibanez.

]]>
https://www.andyibanez.com/introducing-fsduplicates/feed/ 3 546
Cleaning your music library of duplicates with Audio Fingerprinting. https://www.andyibanez.com/cleaning-music-library-duplicates-audio-fingerprinting/ https://www.andyibanez.com/cleaning-music-library-duplicates-audio-fingerprinting/#respond Wed, 22 Jun 2016 22:37:41 +0000 https://www.andyibanez.com/?p=540 Those Nasty Duplicate Songs If you are anything like me, you have probably spent the last decade or so building and maintaining a digital music library. I have spent many years searching, finding, buying, and ripping actual music CDs that were really hard to find. I have scouted the internet (in my teen years) looking […]

The post Cleaning your music library of duplicates with Audio Fingerprinting. appeared first on Andy Ibanez.

]]>
Those Nasty Duplicate Songs

If you are anything like me, you have probably spent the last decade or so building and maintaining a digital music library. I have spent many years searching, finding, buying, and ripping actual music CDs that were really hard to find. I have scouted the internet (in my teen years) looking or rare albums that very few people have put their hands on. I have spent tons of money on iTunes buying music. And of course, I have also spent time buying and ripping many thousands of non-rare CDs.

I have purchased digital versions of albums before I was able to get them in physical form in a CD and re-ripping them. Naturally, my iTunes Library has grown gigantically, not only file size wise (which is very big, because most of my collection is in ALAC), but also in the amount of songs I actually have. All of my songs are neatly organised, with their shiny metadata and album artwork.

At the moment of this writing, my iTunes Library currently has 72,197 songs! And with so many ripped albums, digital purchases, and even sessions of music sharing with my friends in my teen years, I have accumulated tons of duplicate songs.

This is a big problem, and the time to clean my library has finally come.

Existing solutions and why they don’t work

If you also have the problem of having thousands of duplicate songs, chances are you have tried to use some existing tools to do this job.

I am an Apple user, and I use iTunes (I don’t have as many complaints about this software like many people do in this year and age, but I digress). iTunes has a feature that lets you view all your “duplicate” songs in the standard song view. To do this, go to File > Library > Show Duplicate Items. You will get something similar to this:

*Duplicate Song List View*

If you take a look at this list, you will see that everything is simply wrong. In fact, these “duplicate” songs list is so bad, I find it absurd Apple actually shipped this poorly implemented feature on iTunes. What’s worse is that it has been around for years, and it’s one of the least helpful features I have ever seen in a software.

I want to use the song “Nemo” by Nightwish as an example of how badly implemented this feature is. Like you can see, I have three copies of that song on my library, but only two of the ones you see there are true duplicates. The one in the album “Once” and the one in the single of the same name, “Nemo”. “Nemo (Live)” is, surprise surprise, a Live version of this song. They are technically the same song, but performed differently and in different contexts. In other words, there’s no need for iTunes to classify it as a different song. Most people I know enjoy listening to Live and Studio versions of songs, so they will keep both with them.

The problem with iTunes and many software that attempts to solve this problem is that they try to deduce the equality of songs based on Metadata only. If two songs sound “similar” by iTunes standards, then it will mark the mas duplicates. Song titles are the biggest culprits, but it will also disregard different versions of the song based on different versions. For example, I currently have versions of Nemo with the Genre metadata “Symphonic Metal”, but I could add the orchestral version of the song and change the genre to “Orchestral” or anything other than Symphonic Metal, and iTunes would still consider it the same song. This is, presumably, a reason why Apple’s Music matching algorithm is a disgrace, whereas iTunes Match’s works better.

This is the general problem that most software attempting to solve this problem has. They can’t listen to the songs to really determine if they are the same thing or not.

Audio Fingerprinting to the rescue!

If you have built your library over a long period of time and tried to keep it organised, chances are you have used some sort of music database to help you keep your library clean and organised. Music Brainz is popular, but there’s others.

When you rip a CD with a software that integrates with Music Brainz, they try to use all kinds of information available to populate the songs’ metadata within the album. The thing about Music CDs is that they don’t contain the metadata inside them, just raw audio data, so the software can’t populate they info with it.

Maybe you have downloaded an album online and you noticed that the songs have virtually no metadata at all. Those criminal “Track 1”, “Track 2”, “Track X” are everything you can see. But you can throw all the songs to Music Brainz Picard and make it get all the metadata for the album, without having anything else to help it.

How does it do this?

This is achieved through a technology called Audio Fingerprinting. Like its name implies, Audio Fingerprinting is a technology that can uniquely identify a song. Like real life fingerprints in humans, they are unique. No other song or interpretation of it will ever have the same fingerprinting as another. For the mathematically inclined, Audio Fingerprinting is an application of signal analysis. You can tell when two songs are the same based on raw data with little to no metadata available with a very small chance of error.

Music Brainz itself is just a music database. The actual song recognition magic is done through a service called AcoustID. This Audio Fingerprinting service is nothing more than a simple audio fingerprints database. I’m being redundant because AcoustID doesn’t really do anything other than keep track of existing audio fingerprints. What software like Music Brainz Picard do is calculate the audio fingerprint of a song, send it off to AcousticID, and wait for it return with the song information it matched to. Music Brainz and AcousticID work really close together, as the information from the fingerprint in the database is usually tied to a Music Brainz’ release.

Cool! So how do I use this awesome technology?

AcoustID created a tool called Chromaprint. This is a simple C library that can generate the audio fingerprints for you. It also includes a tool called fpcalc which is a command line tool for calculating fingerprints.

Bro, I’m not a developer.

I’m still working on a good script to help me solve this issue, but I have written this quick mockup you can use on your Mac (and probably on Linux too – Sorry Windows users). I cannot help you learn Bash, but this may be useful for you if you are willing to put in the effort. Fellow developers, excuse this ugly code:

#! /bin/bash

mkdir ~/song_dupes &> /dev/null
touch ~/song_dupes/nightwish
touch ~/song_dupes/sorted

find /Volumes/iTunes/Music/Nightwish -name '*' | while IFS=$'\n' read -r FILE; do
	if [ -f "$FILE" ]; then
		calculated_hash=`fpcalc "$FILE" -hash | grep "HASH" | cut -d'=' -f2`
		if [[ -z "$calculated_hash" ]]; then # Test if calculated_hash is empty.
			echo "Unable to calculate fingerprint of $FILE: $calculated_hash"
		else
			echo "$calculated_hash:$FILE" >> ~/song_dupes/nightwish
			echo "Calculated HASH of $FILE: $calculated_hash"
		fi
	fi
done

You may not be a developer, but you will need to use the Terminal if you want to use this script. The good news is that I’m willing to help you. You will need to do the following (and I will guide you through this, if you are using a Mac):

  1. To install Chromaprint
  2. Create a script file with the code above.
  3. Give it the right permissions to be able to execute.

The easiest way to install Chromaprint is to install Homebrew. So click that link and follow the instructions there. They are easy to follow. Come back here when you are done.

To install Chromaprint, simply open your Terminal app and type:

brew install chromaprint

If your internet is slow, it can take a while.

Now download the Script above. Simply copy it, open your Terminal type “cd ~/Desktop”, then type “vim” on it, then press the letter “i” (you will see the bottom left part of the Terminal now says “INSERT”), and press ctrl + v to paste the script there (or right click paste, if you prefer). You may need to modify some things, like changing /Volumes/iTunes/Music/Nightwish to the actual path of the folder you want to analyse. You can also change the names it will generate, but they are just pedantic details and they won’t affect the flow of the script in any way. The script will generate a directory called song_dupes inside your Home directory, and the results will be in a file called nightwish. Nightwish is the band I’m analysing for duplicates, so that will stay there. When you are done, press the ESC key, write “wq”, and press ENTER. It will prompt you for a name. Give it one and press ENTER. The script will be created in your Desktop.

Now on your Terminal write `chmod 755 ~/Desktop/NAME_OF_THE_SCRIPT” and press enter.

You can now drag the script to your Terminal window and watch it execute. You should see output similar to this as it executes:

*Watch it execute*

After the script is done executing, write cat nightwish | sort. This will sort and print the list ordered by audio fingerprinting hash values:

*Dem Hashes!*

This is ugly, but it can help you get some job done. Songs that have the same hashes are likely to be duplicates, but you should check to make sure. This very barebones script is full of failures and the right thing to do would be to send the fingerprints to AcoustID to see if they are really the same song. For instance, if you search for the song Eva nearing the end, you will see that I have many different versions of the song, and more or less has the same problems as iTunes (but it’s more accurate than iTunes, still). I have actually checked those songs with Music Brainz Picard to see what would happen it correctly identified four different versions of the song, whereas this script will be a little bit misleading, showing those four versions with the same hash.

Once I finish this script to use the AcoustID database, it will be doing a much better work identifying real duplicates.

The post Cleaning your music library of duplicates with Audio Fingerprinting. appeared first on Andy Ibanez.

]]>
https://www.andyibanez.com/cleaning-music-library-duplicates-audio-fingerprinting/feed/ 0 540
Writing “Lite” And “Pro” Versions Of Your App (In Swift) https://www.andyibanez.com/writing-a-lite-and-pro-versions-of-your-app-in-swift/ https://www.andyibanez.com/writing-a-lite-and-pro-versions-of-your-app-in-swift/#comments Wed, 10 Feb 2016 04:17:32 +0000 https://www.andyibanez.com/?p=440 Learn to use Targets to build a "Lite" and a "Pro" version of your app.

The post Writing “Lite” And “Pro” Versions Of Your App (In Swift) appeared first on Andy Ibanez.

]]>
I recently decided to monetise the upcoming version of Mignori as a free “Lite” app and a paid “Pro” app. This was a prevalent business model before IAPs were a thing, and it almost died off completely when they were introduced. Despite all that, some apps still monetise this way, and I think it’s worth talking about how to do it properly.

Before we get started, I hate writing iOS SDK tutorials and specifying I’m going to be using an specific language. SDK tutorials should be written to be language-agnostic (while the code snippets can be in an specific language), because anyone who knows Objective-C can pick up Swift and vice versa. Despite all that, I’m explicitly stating that we will be using Swift here because to achieve this, you need to know about macros, and many people think Swift doesn’t support macros the way Objective-C does, so many people think that what I’m going to show you in this tutorial is simply not possible. Let me say beforehand that the techniques here apply in both languages, but the things you have to tinker with will be slightly different depending on what language you are writing your app in.

The techniques described here will be useful for many other things. Some apps release different “versions” of their apps that are not necessarily “Lite” and “Pro” only (MoneyWiz comes to mind). This post will walk you through doing exactly that.

So let’s get started!

What Not To Do

If you have been writing for iOS for a long time, chances are you have attempted to create both versions of your app, and you did it wrong. Let me tell how you shouldn’t approach the idea of writing a “Lite” and “Pro” versions of your app.

Mulitple Projects

So you just finished writing the “Pro” version of your app and you decided to build the “Lite” version of it. No easier way than to Right Clicking your project in Finder and hitting “duplicate”, right?

This will cause an unnecessary mess because you will have two projects to maintain. Even if the projects are exactly the same at first, as you “numb down” the second project to make it the “Lite” version, chances are you are going to break things in the existing codebase. Not only that, but when you find a bug in one of your versions, you will have to fix it in both projects. And not only that, but if your Lite version is heavily modified, what guarantees you the same fix will work in both projects?

Subversion Branches

Another chaotic way of doing things is to maintain one project, but to branch out to different versions of your app.

Subversion tools like Git do a great job trying to keep your codebase consistent across different branches, so you may be thinking that keeping a common “core” branch and then two different branches for your app versions is a good idea. It isn’t. It will still be chaotic to maintain.

What To Do Instead

Enter Targets

Targets are the right way to create “Lite” and “Pro” versions of your app. Targets, amongst other things, allow you to compile different projects with the same sources.

From Apple’s Documentation:

A target specifies a product to build and contains the instructions for building the product from a set of files in a project or workspace. A target defines a single product; it organizes the inputs into the build system—the source files and instructions for processing those source files—required to build that product. Projects can contain one or more targets, each of which produces one product.

So each Target produces exactly one product. In this case, your “Lite” app will be a target, and the “Pro” one will be another. If you have written App Extensions or Apple Watch apps, you have worked with Targets before.

From here on out I will show specific techniques and configurations I did on Mignori, as well as related techniques that, while I personally did not use, they may be useful to you in your projects.

Getting Started

We are going to assume you just finihed your “Pro” app, and are ready to numb it down to “Lite”. Click your Project’s “Blueprint” and then take a look at your targets (if your target’s don’t show up, click the Screenshot 2016-02-09 22.24.56 button to show them). Depending on how you configured your project (whether you added unit tests or not, UI tests, etc), you may have more than just your main app target.

Project Blue Print Targets

In the unlikely case you don’t know what these configurations are, you basically configure your targets, and the Bundle Identifier must be unique for each app target, otherwise your users won’t be able to have both versions installed at the same time. Doing this mistake was and is still pretty common. Please don’t do it. If you plan on having exportable data from the “Lite” version to the “Pro” version, you are gonna have a hard time if you make that mistake.

Viewing your targets, right-click your app target and click “duplicate”. This will do exactly what you expect it to do.

Initial Target Configurations

We have been through this already, so the first thing you should do is to change the bundle identifier of the new app.

I personally follow the standard reverse DNS format, and then the last “period” contains the app name, along with a version identifier.

com.fairese.ios.NewMignori-ProVersion
com.fairese.ios.NewMignori-LiteVersion

You can also rename the target itself, by slowly double-clicking it. My two targets are named “Mignori” and “MignoriLite” respectively. If your target name contains a space, it won’t compile – I have searched heavens and oceans to find the solution to this, but couldn’t. By default, apps will have the same name of your Target. I do not want the “Lite” app to be called “MignoriLite” in my users’ screens, but I’d rather call it “Mignori Lite”. To fix this, open the “Info” tab of the target you want to rename and search for the “Bundle name” key. You can type your app name with spaces here to add the “Lite”.

The app is called “NewMignori” because it has been rewritten from scratch. That’s besides the point and don’t worry about giving such words to your own apps.

App Icon

With that essential configuration out of the way, the easiest thing to change is the app icon. To change the icon of your targets (you may want to do this to add a “Lite” label, for example), open the graphics assets file (Assets.xcassets), click the “+” icon to the bottom, hover over “App Icon & Launch Images”, and select “New iOS App Icon”. You can rename the assets here as you wish. My “Pro” app’s icon is simply called “AppIcon”, and the “Lite” apps’s icon is called “AppIcon-Lite”.

Go back to your Targets and select your new “Lite” target from the previous step. Find the “App Icons and Launch Images” section. When you click the drop down in “App Icons Source”, you will see all your app icons sources (if you named your icons like mine, you will see “AppIcon” and “AppIcon-Lite”. You know which one to select. Easy!

Screenshot 2016-02-10 00.12.37

Target-Specific Sources

If you select a file – any file (other than the Blueprint) – you can select what target it belongs to. Take a look at the Utilities window and find the “Target Membership” section. This allows you to create target-specific resources and even code files. Creating target-specific code files is probably going to be overkill in most cases. I will show you how to execute code specific to each target in just one file later, but the option to do it is there, if you ever really need to. Some valid uses for this may be, for example, different “License.txt” files, or even different demo data sources.

Screenshot 2016-02-09 23.08.30

The Fun Part – Target-Specific Code, In The Same File

Like I said, creating whole new Code files for targets can be overkill and won’t be necessary 99% of the time. If you were to do that for every source file, you would have the same problem as if you created different projects for both versions of your app. Think about it – You would be creating redundant code that you would have to maintain in at least two different places!

Compiler Custom Flags

If you have written lots of Objective-C and even pure C or C++ code before, you know you can create conditionalyl-compilated code. You do this using #define macros:

#define DEBUG 1

#if DEBUG
// Conditional code to run when the DEBUG flag is set.
#endif

This code would only be run when the macros “DEBUG” exists. As you may know, before the compiler actually compiles anything, it searches for and replaces appearances of #define macros with their current value. Chances are you have seen header guards in good ol’ C or C++ (or even on bridge files from Objetive-C to Swift!).

#ifndef Bridge_h
#define Bridge_h

#import "SWRevealViewController.h"

#endif /* Bridge_h */

This prevents the #import statements from being run more than once, as multiple same includes wouldn’t compile (technically they would, as #import and #include work differently, but when you create a bridging header, the header guard is there). In C and C++, anything that starts with a pound sign (#) is known as a preprocessor directive, and it’s code that gets “replaced” before the compiler tries to do its job.

Why all this discussion on the C and C++ macros? Because as Objective-C is a superset of C, it is expected you can use them with Objective-C too. And you can. What many people don’t know about is that they (sorta) work with Swift, too. At least for our intents and purposes, they work.

I’m telling you all this because there’s a particular limitation on how Swift deals with preprocessor macros vs. Objective-C (or its subset). In Objective-C, you can grab the actual value of the macros to use it later. Consider this example:

#define MY_STRING @"OH EM GEE"

NSLog(MY_STRING)

This would work because the preprocessor will actually replace MY_STRING with @”OH EM GEE”, then the code will be handed to the compiler to do its job.

But in Swift, this is not the case. The most useful thing you can do with macros is to check if a definition has taken place. You can do pretty insane stuff with macros, so it makes sense the people behind Swift chose not to fully support them.

Just cut to the chase already…

Basically, you can do this in Swift (and in Objective-C):

#if PRO_VERSION
// Code to compile if the app is the pro version
// Adapt your UI, do anything that you need for your app's full version.
#endif

#if LITE_VERSION
// Code to compile if the app is the lite version
// Limit functionality, add (annoying) reminders to buy the pro version, etc..
#endif

How do we achieve this? For each target, open the “Build Settings” tab and search for “Swift Compiler – Custom Flags”. You set the “flags” (in the example above, LITE_VERSION and PRO_VERSION are flags) you want to use here, and not only can you set them per target, but also per build configuration (debug or release).

For example, this is what my app’s Lite version looks like:

Screenshot 2016-02-09 23.45.29

And for my Pro version:

Screenshot 2016-02-09 23.46.26

Each flag, like you can see, needs to be preceded by a “-D”. This tells your app that Swift will provide those definitions to it.

Slightly irrelevant to this post, you can also see that I have -DDEBUG and -DRELEASE flags. I tend to do more verbose logging when running in debug mode, and these flags help me do just that.

Finally…

You need to compile and run each project separately, so keep that in mind if you use autoincrementing build numbers (so you don’t wonder why the build numbers of your main and lite apps are so different). You also need to archive them individually for App Store submission. But even with these extra steps, it’s much easier and faster than keeping track of separate projects or subversion branches.

I hope you also realised there’s a lot you can do with the techniques described here. Each target has their own extensive configuration, so you can play around and see what else you can do. You can even change the deployment target of each app, so if you ever need to support older iOS versions but the chances of breaking new code are high, you can just create a new target and work from there. The options are vast, and I hope you learned something new.

Photo Feb 09, 11 48 58 PM

I based the initial draft of this post on http://www.raywenderlich.com/68613/create-paid-lite-version-iphone-app about the same topic. However, not only is that tutorial outdated, but I also personally think it’s overkill to create a whole demo project to talk about this. Tutorials that cover everything, from the “File > New Project” Step to the point of the tutorial, can be helpful for newcomers, but they can be overly verbose for people who are already familiar with the topic in hand (in this case, Xcode and iOS development). That said, I apologise for pointing out the obvious, but a post can sound weird if it doesn’t have certain words or instructions (like “right-click” and “duplicate”).

The post Writing “Lite” And “Pro” Versions Of Your App (In Swift) appeared first on Andy Ibanez.

]]>
https://www.andyibanez.com/writing-a-lite-and-pro-versions-of-your-app-in-swift/feed/ 1 440
Embracing the Swift Standard Library. https://www.andyibanez.com/embracing-the-swift-standard-library/ https://www.andyibanez.com/embracing-the-swift-standard-library/#respond Fri, 03 Jul 2015 23:10:35 +0000 https://www.andyibanez.com/?p=378 Swift is a truly powerful language. But a powerful language is nothing if it doesn’t have a powerful standard library. In this post we will get started with the Swift Standard library (which is small compared to the standard library of other languages, but by no means does that mean it isn’t powerful). The Swift […]

The post Embracing the Swift Standard Library. appeared first on Andy Ibanez.

]]>
Swift is a truly powerful language. But a powerful language is nothing if it doesn’t have a powerful standard library. In this post we will get started with the Swift Standard library (which is small compared to the standard library of other languages, but by no means does that mean it isn’t powerful). The Swift Standard library goes hand in hand with Apple’s new programming paradigm – Protocol-Oriented Programming. If you are acquainted with Protocol-Oriented Programming, you know how different it is compared to the so-known OOP. Swift’s Standard Library adopts Protocol-Oriented Programming really well (and that’s to be expected, since they created this paradigm!)

To be more specific, we will explore three protocols from Swift’s Standard Library here – GeneratorType, SequenceType and ArrayLiteralConvertible. We are going to build a Shopping List that behaves like an array – You will be able to create a Shopping List passing it an array of Shopping Items, and you will be able to fast-iterate (using for-in) over this list to get its elements as if it were a normal array. But this shopping list also has its own methods, so we are not just creating a new array with a fancy name.

A Simple Shopping List

If you wanted to create a shopping list of things, you would probably start by creating a class or an struct (in this post, we will use an struct to also embrace the power of value types in Swift). This struct would probably represent a simple shopping item, with a name and a price.

struct ShoppingItem {
    let name: String
    let price: Float
    
    init(name: String, price: Float) {
        self.name = name
        self.price = price
    }
}

And if you wanted to keep a list of these items, you would probably just put them in an array.

let apples = ShoppingItem(name: "Apples", price: 12.76)
let carrots = ShoppingItem(name: "Carrots", price: 15.43)
let bananas = ShoppingItem(name: "Bananas", price: 32.53)

let shoppingList = [apples, carrots, bananas]

Then whenever you need, you would just operate things “on the fly”. Need to calculate the total? Let’s just iterate over the array, and add the values. Same if you want to calculate the average price of the items on your list.

var total: Float = 0
for item in shoppingList {
    total += item.price
}

var avg: Float = total / Float(shoppingList.count)

This works fine. It does it’s job. But it’s not ideal. If you want to calculate the calculate the total or average in different places, you will end up having duplicate code everywhere.

But you already know how to fix that, don’t you?

Let’s Just Encapsulate Everything

If you thought of making a new struct, called ShoppingList or something like that, you are already in the right path. You would create a ShoppingList struct that would encapsulate an array of ShoppingItems. In that way, you could just create computed properties to get the total and average price of everything on the list. You’d end up with something like this.

struct ShoppingList {
    
    private let items: [ShoppingItem]

    var count: Int {
        get {
            return items.count
        }
    }

    var total: Float {
        get {
            var total: Float = 0
            for item in self.items {
                total += item.price
            }
            return total
        }
    }
    
    var average: Float {
        get {
            return self.total / Float(self.count)
        }
    }

    subscript(index: Int) -> ShoppingItem {
        return self.items[index]
    }
    
}

That works pretty well, right? the ShoppingItems are nicely encapsulated inside the ShoppingList. We don’t need to access the underlying array because we have a subscript operator that lets us get the elements of the array directly. We can count the number of elements in said list without querying the underlying array directly. We forgot to add an initialisator though, so let’s go ahead and create an initialisator that takes an array:

struct ShoppingList {
    
    private let items: [ShoppingItem]

    var count: Int {
        get {
            return items.count
        }
    }

    var init(array: [ShoppingItem]) {
        self.items = array
    }

    var total: Float {
        get {
            var total: Float = 0
            for item in self.items {
                total += item.price
            }
            return total
        }
    }
    
    var average: Float {
        get {
            return self.total / Float(self.count)
        }
    }

    subscript(index: Int) -> ShoppingItem {
        return self.items[index]
    }
    
}

You could create a better initialisator by using the elipsis (…) operand, so you could pass in a variable amount of shopping items without putting them in array.

init(items: ShoppingItem...) {

}

But later we are going to do something much neater. Trust me on this one!

Fine and dandy. Let’s create a new ShoppingList now:

let apples = ShoppingItem(name: "Apples", price: 12.76)
let carrots = ShoppingItem(name: "Carrots", price: 15.43)
let bananas = ShoppingItem(name: "Bananas", price: 32.53)

let shoppingList = ShoppingList([apples, carrots, bananas])

Getting the total and average of the items on your list is much better now.

print("\(shoppingList.total)")
print("\(shoppingList.average)")

What’s that? You want to iterate over the items? Err. I mean, you can. But you will have to do it with an old fashioned for loop (not even for-in!). We need to get the number of elements in the underlying array, using our count property, and then use a for loop like we all know.

for var i = 0; i < shoppingList.count; i++ {
    print("\(shoppingList[i].name) cost \(shoppingList[i].price)")
}

And with that, you can do anything you want with each item of the shopping list. Yay!

A Better Way of Doing Things

But of course, this code is slightly awkward, and many of us have written a variation of it at some point. Wouldn’t it be nice if we could treat the ShoppingList as an array, and create it using Array-like Syntax? And wouldn’t it be nice to be able to iterate through it using a for-in loop, instead of having to grab the count and then use an old-fashioned for-loop on it?

The good news is that you can! Thanks to the Swift Standard Library and Swift’s focus on Protocols, we can make our code elegantly blend with the features of the language.

Elegant Array Syntax

First, we will talk about one of the simplest Protocol’s in Swift’s Standard Library – ArrayLiteralConvertible.

This protocol allows your own structs, classes, and even enums to be initialised with array-like syntax.

For example, Foundation’s NSSet object, does this:

let mySet: NSSet = ["foo", "bar", "cheese"]

We can create a set by assigning it an array. In this case, Type Annotation is important, otherwise Swift will think we are creating an Array instead.

We can do with ArrayLiteralConvertible. This protocol has only one requirement:

init(arrayLiteral elements: Self.Element...)

First we make ShoppingList comply with this protocol. Then we implement that only method, which is just one assignment. I will also delete the old init method from before:

struct ShoppingList : ArrayLiteralConvertible {
    //...

    /// MARK: - ArrayLiteralConvertible
    
    init(arrayLiteral: ShoppingItem...) {
        self.items = arrayLiteral
    }

    //...
    
}

Like you can see, it’s really simple – We assign the protocol, we implement the init, and assign the passed array in that init.

With this, you can already create shopping lists neater:

let apples = ShoppingItem(name: "Apples", price: 12.76)
let carrots = ShoppingItem(name: "Carrots", price: 15.43)
let bananas = ShoppingItem(name: "Bananas", price: 32.53)

let shoppingList: ShoppingList = [apples, carrots, bananas]

Just like Apple’s NSSet, the only catch is we need to use type annotation.

Making our Struct Iterable

Finally, we need to make the shopping list enumerable so we can treat it as an array. The work around from above works, but we can definitely make that neater.

Making the struct enumerable is slightly more complicated and involved than adding the natural array initialisation syntax. I will try to explain everything that is going on. You will need to make your struct comply with both SequenceType and GeneratorType

SequenceType

SequenceType’s only job is to make sure you can use your objects in for-in loops. If you check SequenceType’s documentation (linked above), you will see that it has many methods and one typealias. We do not need to comply with all, and in this post, I won’t. You just need to comply with the typealias, and the filter and generate methods:

struct ShoppingList : SequenceType, ArrayLiteralConvertible {
    //...
    
    /// MARK: - SequenceType
    
    typealias Generator = ShoppingList
    
    func filter(includeElement: (ShoppingItem) -> Bool) -> [ShoppingItem] {
        var fItems = [ShoppingItem]()
        for itm in self.items where includeElement(itm) == true {
            fItems += [itm]
        }
        return fItems
    }
    
    func generate() -> Generator {
        return self
    }

    //...
}

According to Swift’s docs, “A generator is a sequence that is consumed when iterated”. To be completely honest with you, I couldn’t make total sense of that definition of generator, but after playing with this for a while, I came with my own definition of generator: A generator gives you the items that are being iterated through. Each time an iteration passes, you are given one item.

The typealias needs to be defined. Like you can see, it is part of the signature of the generate() method, and we are returning self. Why? Well, we can say ShoppingList is a generator because it contains the ShoppingItems, and we want to receive one shopping item each time for-in goes through it.

The filter method is the interesting part. Not all items “match” the filter for-in uses when iterating (I will talk about why later – bear with me for now). So we need to manually check all the items against the filter closure that this method gives us. We are using slight pattern matching here to do just that. At the end of this method, we return an array of elements that match our filter.

GeneratorType

Finally, the last protocol to make our struct enumerable is GeneratorType. Again it has very few requirements, and it’s one of the easiest to implement. You can think of it’s only job to give whoever is requesting it’s data the corresponding element.

    //...
    /// MARK: - GeneratorType
    
    var currentElement = 0
    
    mutating func next() -> ShoppingItem? {
        if currentElement < self.items.count {
            let curItem = currentElement
            currentElement++
            return self.items[curItem]
        }
        return nil
    }

    //...

next() is declared as mutating, because it alters the internal state of our struct. We need to manually keep track of all the iterated items so we can give the right ones at all times.

And that’s it! Making your objects comply with those protocols make it possible to have a nice object that can be initialised an array, has it’s own methods, and can be iterated through with the natural for-in syntax.

Wrapping Up

Before I finish this post, let me tell you what the filter of SequenceType is for – Pattern Matching. for-in, in Swift 2, supports Pattern Matching. When you are using for-in with pattern matching in Swift, the filter block has all the logic to make sure you don’t include incorrect elements.

For example, we can use pattern matching to iterate only through the items whose cost is under 13:

for item in shoppingList where item.price < 13 {
    print("\(item.name) cost \(item.price)")
}

This prints: “Apples cost 12.76” and nothing else, and this behaviour was given for free by just correctly implementing the filter method of SequenceType.

You can download the code for this post from here and copy-paste it in a new Playground.

And with that, I encourage to take a look at Swift’s Standard Library in more depth. There’s many interesting protocols you can adopt to make your code neater and more Swift-like. Currently, there is not much documentation about them besides Apple’s official documentation, so if you find an interesting one, I encourage you to write about it.

The post Embracing the Swift Standard Library. appeared first on Andy Ibanez.

]]>
https://www.andyibanez.com/embracing-the-swift-standard-library/feed/ 0 378
Installing Ghost In an Ubuntu Server with Virtualmin and Apache https://www.andyibanez.com/ghost-ubuntu-virtualmin-apache/ Wed, 20 Aug 2014 22:30:27 +0000 http://www.andyibanez.com/?p=325 Learn to install Ghost in a VPS you manage with Virtualmin and Apache.

The post Installing Ghost In an Ubuntu Server with Virtualmin and Apache appeared first on Andy Ibanez.

]]>
Ghost

I was arguing with myself whether I should post this or not. For one, it is the second Linux-related tutorial I write, and that strays from the main topic of my site (my awesome self and iOS development). But because installing Ghost was a big hassle on my current setup, I decided to write this for those who are struggling with the same thing as me. After all, I have said this before – one of the reasons I decided to start blogging was so I could also start documenting things for myself. Most, if not all, of my tutorials, have been things I have written shortly after having learned about them. So I know that my own site is a good go-to reference when I need to use those topics again.

At the time of this writing, Ghost 0.5 has just been released to the public. There are many changes, and there aren’t too many sources you can find to help you install Ghost on Ubuntu, using Virtualmin (Webmin, in case you only have access to that – this tutorial should work for both). In fact, in all my searches, the total amount of resources I found that talked about Ghost and Virtualmin or Webmin in the same page was a grand total of zero.

For this reason, I will document how I managed to install Ghost on my server, which is running Ubuntu server with a Virtualmin setup. If you are like me, you just hate to do server administration by hand using the Terminal and sending off the commands, and installed Virtualmin to make your life easier.

Most Ghost related tutorials use NGinx. But personally I am an Apache guy, so for me it’s important to make this work in Apache.

In short, this tutorial assumes that you have access to Virtualmin (or Webmin), and are using Apache.

Finally, this is not a tutorial for beginners. Even when using Webmin, you need to know the command line, and know your way around virtual sites.

With all that said, I will try to make this as easy as possible.

1. Preparing Your Web Server For Ghost.

The first thing you will probably want to do is to set up a subdomain using Virtualmin for your Ghost Blog. If you are using a shared host that has Webmin and can’t do that, don’t worry, you can set Ghost up in your main domain as well.

(Virtualmin only) Go to Virtualmin, click Create Virtual Server. At this point, the settings of the server are irrelevant so just configure it as you need it. The Ghost blog I created is located at http://linguist.andyibanez.com

Grab your username and password for FTP and SSH purposes.

2. Installed The Packages Required for Ghost

After you have your site created, go to Webmin > System > Software Packages.

2.1 Build Essentials and NPM

Select the radio button that says “Package from APT”. Then click the button that says “Search Apt…”. Type in build-essentials, select the only package that shows up, and install it. This package is needed because it includes a few useful development tools, including g++, which is needed to compile the other tools needed for Ghost.

APT

Do the exact same thign with NPM, writing “npm” instead of build-essentials.

2.2 node.js

Sadly, I lied. Sorta. We will need to access our site SSH after all to install the remaining component to install Ghost. I, too, thought it was a bummer when I realised I had to do that.

Technically, you can install node.js installed build-essentials and PM, but at the time of this writing, the APT packages for node.js are outdated. Ghost needs nose.js 0.10.x to work, and at the time of this writing it installs 0.6.x. So we have no other option but to download them and compile it from source.

Virtualmin Users

If you have access to Virtualmin because you have full access over your VPS, follow these instructions.

1. Login to your root account via SSH in the terminal (this is NOT the username and password you created above – you should have gotten this when you created your VPS).
2. Once logged in, type in the following commands *:

cd /usr/src
wget http:/nodejs.org/dist/v0.10.18/node-v0.10.18.tar.gz
tar zxf node-v0.10.18.tar.gz
cd node-v0.10.18
./configure
make
make install

The moment you send the make command, your server will be doing a big task. It can take a while to complete (in my case, it took 8 – 10 minutes).

Once that is done, verify node.js is installed by typing this command:

node -v

It should print the node.js version you’re running.

If you are using a shared hosting and don’t have access to Virtualmin but only to Webmin, you may need to ask the manage of the server to install that version of node.js, npm, and build-settings.

3. Installing Ghost

Once all data is done, everything we have left is to install Ghost itself. Go to http://ghost.org/download and download the package to your computer. Next, grab the SSH details we obtained in the first step.

Once Ghost is downloaded, open your favorite FTP program and login with the credentials. Navigate to the public_html folder, unzip the Ghost package, and upload it (you can also upload the zip as it is, or even download it directly there with wget and unzip with more command line commands).

Once that is uploaded via FTP, we need to log in via SSH again, but this time using the credentials from the first step instead of root. Do that and navigate to the place where you just uploaded the Ghost files. Once you are there, run the following command to install Ghost:

npm install --production

And watch out for any possible errors. If we are doing everything correctly, everything you will see at most are a few warnings.

4. Configuring Ghost

We are nearing the end of this process.

Ghost has a file called “config.js”. If you don’t see it, you may see the example “config.example.js” file. You can just copy the latter one and rename it to config.js. Open it with your favorite editor (I do all my FTP operations with Cyberduck, so I can download the files locally and open them with TextMate – But this is just personal preference.

The config file is here, you can also just steal this one.

// # Ghost Configuration
// Setup your Ghost install for various environments
// Documentation can be found at http://support.ghost.org/config/

var path = require('path'),
    config;

config = {
    // ### Development **(default)**
    development: {
        // The url to use when providing links to the site, E.g. in RSS and email.
        url: 'http://my-ghost-blog.com',

        // Example mail config
        // Visit http://support.ghost.org/mail for instructions
        // ```
        //  mail: {
        //      transport: 'SMTP',
        //      options: {
        //          service: 'Mailgun',
        //          auth: {
        //              user: '', // mailgun username
        //              pass: ''  // mailgun password
        //          }
        //      }
        //  },
        // ```

        database: {
            client: 'sqlite3',
            connection: {
                filename: path.join(__dirname, '/content/data/ghost-dev.db')
            },
            debug: false
        },
        server: {
            // Host to be passed to node's `net.Server#listen()`
            host: '127.0.0.1',
            // Port to be passed to node's `net.Server#listen()`, for iisnode set this to `process.env.PORT`
            port: '2368'
        },
        paths: {
            contentPath: path.join(__dirname, '/content/')
        }
    },

    // ### Production
    // When running Ghost in the wild, use the production environment
    // Configure your URL and mail settings here
    production: {
        url: 'http://my-ghost-blog.com',
        mail: {},
        database: {
            client: 'sqlite3',
            connection: {
                filename: path.join(__dirname, '/content/data/ghost.db')
            },
            debug: false
        },
        server: {
            // Host to be passed to node's `net.Server#listen()`
            host: '127.0.0.1',
            // Port to be passed to node's `net.Server#listen()`, for iisnode set this to `process.env.PORT`
            port: '2368'
        }
    },

    // **Developers only need to edit below here**

    // ### Testing
    // Used when developing Ghost to run tests and check the health of Ghost
    // Uses a different port number
    testing: {
        url: 'http://127.0.0.1:2369',
        database: {
            client: 'sqlite3',
            connection: {
                filename: path.join(__dirname, '/content/data/ghost-test.db')
            }
        },
        server: {
            host: '127.0.0.1',
            port: '2369'
        },
        logging: false
    },

    // ### Testing MySQL
    // Used by Travis - Automated testing run through GitHub
    'testing-mysql': {
        url: 'http://127.0.0.1:2369',
        database: {
            client: 'mysql',
            connection: {
                host     : '127.0.0.1',
                user     : 'root',
                password : '',
                database : 'ghost_testing',
                charset  : 'utf8'
            }
        },
        server: {
            host: '127.0.0.1',
            port: '2369'
        },
        logging: false
    },

    // ### Testing pg
    // Used by Travis - Automated testing run through GitHub
    'testing-pg': {
        url: 'http://127.0.0.1:2369',
        database: {
            client: 'pg',
            connection: {
                host     : '127.0.0.1',
                user     : 'postgres',
                password : '',
                database : 'ghost_testing',
                charset  : 'utf8'
            }
        },
        server: {
            host: '127.0.0.1',
            port: '2369'
        },
        logging: false
    }
};

// Export config
module.exports = config;

Search for the line that says “// ### Production” near line 47 and do a few edits.

  • Edit url to be the URL that will be used to visit your blog.
  • (Optionally) change the port number (only do this if you’re installing a second instance of Ghost).

Finally, go back to Webmin, select your server, and go to Configure Website > Edit Directies. You will be greeted with a bunch of directives.

Paste in these three lines, right before the first “Directory” directive:

ProxyRequests off
ProxyPass / http://127.0.0.1:2368/
ProxyPassReverse / http:/127.0.0.1:2368/

If you are installing a second instance of Ghost, you would need to change the port number to match the port of the instance.

Now you need to start the service (Ghost runs as a service instead of an actual website).

npm start --production > output.log &

Please note you will need to do this again if you restart your server. There are ways to keep Ghost “Alive forever”, but none of them have worked for me so I am omitting them from this tutorial.

That’s a Wrap

You can now visit the URL of your Ghost website. visit it’s /ghost page to create the first user and start blogging!

You can install various instances of Ghost with this method. Simply change the port number in both the configuration and proxy settings when you do.

Enjoy!

The post Installing Ghost In an Ubuntu Server with Virtualmin and Apache appeared first on Andy Ibanez.

]]>
325
Using The iOS Keychain. https://www.andyibanez.com/using-ios-keychain/ https://www.andyibanez.com/using-ios-keychain/#comments Wed, 11 Jun 2014 23:00:56 +0000 http://andyibanez.com/?p=295 Learn how to enable your app to use the iOS Keychain. The iOS Keychain is essential to keep sensitive data secure.

The post Using The iOS Keychain. appeared first on Andy Ibanez.

]]>
Keychain

If you’re a developer, you know you will, you know you will have to store sensitive data in your app sooner or later. Storing sensitive data in NSUserDefaults is a big no-no, so you just can’t store sensitive data there. Instead, just like OS X, iOS provides a keychain your app can use to store all sorts of sensitive data. The keychain is they key place to store all those user passwords or bank information your app may need to store in the app. If you are just starting with iOS development, or just mid-level, the idea of having to store sensitive data is scary. The good news is, iOS has a very easy API to use with the keychain. Interacting with the keychain on iOS is very simple as the API is composed of just four essential functions.

The Keychain Essentials.

The iOS Keychain Is Much Different Than The OS X Keychain

The OS X keychain is much, much more complex than the iOS keychain. This tutorial is specifically for the iOS keychain.

First, in OS X, any app can request access to any info currently stored in the keychain. If you’re an OS X user, you may recall those prompts notifying you that a given app would like to access data in your keychain. They look like this:

mac-os-x-105-leopard-keychain-access-password-dialogue-box

 

Once you put in your password, the keychain will open and grab the data it needs, and hand it over to the app requesting access to it.

Second, the OS X Keychain has one more feature you cannot find in iOS, and this is one of the reasons the iOS Keychain is so simple: In OS X, the user can create more than one keychain for different things. One for certificates, one for passwords, one for anything else that may be sensitive (not many people do this, but this keychain feature is there for those who need it).

In iOS, each app has their own keychain and this keychain cannot be accessed by any other apps, unless  both apps that want to access this keychain are made and maintained by you, have the Keychain Sharing entitlement enabled, and they are both in the same Keychain Access Group(s). This is a completely different paradigm than the OS X Keychain, because now other apps cannot tamper with your private keychain. Also, each app in iOS has one keychain only, where you can add one or more keychain items.

Also, unlike OS X, the user may not even be aware the keychain exists in iOS. When you’re dealing with the iOS Keychain, the user never gets any prompts and nothing gets in the way of your user for interacting with your app. The keychain is completely hidden for the end user, and you are responsible to make the user interaction details as abstract as possible.

iOS Keychain Concepts And Structure

The keychain is the storage, secure and encrypted your app can use to store sensitive data. It is important that your app and all subsequent versions of it are all signed by the same mobile provision profile. If they aren’t, you will have many troubles later on.

A Keychain Item is the unit of sensitive data stored in your app. If you visualize a keychain, like the one your grandma has for all her keys, a keychain item is an ‘key’. Keychain items have the sensitive data you want to store, and are accompanied by one or more attributes. The attributes describe the keychain item, and what attributes you can use depend on the item class of the keychain item. The item class is “what kind” of data you are going to store. This can be a user/password combination, a certificate, a generic password, and things like that.

Keychain Search Dictionaries.

Search dictionaries specify the attributes for the keychain item you want to update or create.

All dictionary items have a very similar structure. First, they contain the key-value class pair, to specify “what” you’re going to store (again, this can be a user/password combination, a generic password, a certificate, etc).

Second, they contain one or more key-value pairs that set the attributes for the item. This can be a label to give the item a name, the creation time, and anything other that may describe the item.

Third, they specify search attributes and return attributes, to tell your app how it should retrieve this item from the keychain.

Search dictionaries are also known as queries.

Getting Our Hands Dirty

Once you have absorbed all that theory, let’s build a small app that is going to store a few user/password pairs in the keychain and retrieve them.

1. Create a new XCode project for iPhone only, and make it a single-view application. Make sure you are using Storyboards and ARC.
2. The Keychain Services API is part of Security.framework. Go ahead and add it to your project (yourAppBlueprint > Build Phases > Link Binary with Libraries).
3. Add text fields and buttons like this:

Screenshot 2014-06-10 19.39.53

(The “Password” field would normally be set to hide the characters you type with circles. I am not using this field as a password field so you can see what you type on it – Naturally don’t do this in a real world app).

This app will be very simple. It will allow users to save and modify their user/password combination. The bottom part (everything after the “Fetch Data” label) will be used to get the password, and to delete the keychain item.

Create outlets for all the text fields, actions for all the buttons, and an extra outlet for the “Password” label at the bottom. Your header should look like this (nwUsernameTxtFld refers to the top most username text field):

#import <UIKit/UIKit.h>

@interface TKViewController : UIViewController

@property (weak, nonatomic) IBOutlet UITextField *nwUsernameTxtFld;
@property (weak, nonatomic) IBOutlet UITextField *passwordTxtFld;
@property (weak, nonatomic) IBOutlet UITextField *usernameTxtFld;

@property (weak, nonatomic) IBOutlet UILabel *passwordLbl;

- (IBAction)saveButtonPressed:(id)sender;
- (IBAction)updateButtonPressed:(id)sender;
- (IBAction)getPasswordPressed:(id)sender;
- (IBAction)deleteItemPressed:(id)sender;


@end

4. Now go to the view controller implementation file (.m) and import Security.framework.

#import <Security.framework>

5. Add the following code to your saveButtonPressed method. I will explain everything below:

- (IBAction)saveButtonPressed:(id)sender
{
    //Let's create an empty mutable dictionary:
    NSMutableDictionary *keychainItem = [NSMutableDictionary dictionary];
    
    NSString *username = self.nwUsernameTxtFld.text;
    NSString *password = self.passwordTxtFld.text;
    NSString *website = @"http://www.myawesomeservice.com";
    
    //Populate it with the data and the attributes we want to use.
    
    keychainItem[(__bridge id)kSecClass] = (__bridge id)kSecClassInternetPassword; // We specify what kind of keychain item this is.
    keychainItem[(__bridge id)kSecAttrAccessible] = (__bridge id)kSecAttrAccessibleWhenUnlocked; // This item can only be accessed when the user unlocks the device.
    keychainItem[(__bridge id)kSecAttrServer] = website;
    keychainItem[(__bridge id)kSecAttrAccount] = username;
    
    //Check if this keychain item already exists.
    
    if(SecItemCopyMatching((__bridge CFDictionaryRef)keychainItem, NULL) == noErr)
    {
        
        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:NSLocalizedString(@"The Item Already Exists", nil)
                                                        message:NSLocalizedString(@"Please update it instead.", )
                                                       delegate:nil
                                              cancelButtonTitle:NSLocalizedString(@"OK", nil)
                                              otherButtonTitles:nil];
        [alert show];
        
    }else
    {
        keychainItem[(__bridge id)kSecValueData] = [password dataUsingEncoding:NSUTF8StringEncoding]; //Our password
        
        OSStatus sts = SecItemAdd((__bridge CFDictionaryRef)keychainItem, NULL);
        NSLog(@"Error Code: %d", (int)sts);
    }
}

The first thing we need to do is to create a search dictionary. We are creating a mutable dictionary to which we will add all our keychain item related data. Then we grab all our data, such as username and the server the password is for, and put it in variables to make our life easier.

We are creating a keychain item with the item class (kSecClass) kSecClassInternetPassword. We need to do a bridge cast to the keys and all constants in ARC, otherwise the compiler is going to complain. The Keychain Services API works with Core Foundation objects, so throughout this tutorial you will see bridge casts and direct casts of Objective-C objects to their CF counterparts.

When we specify the class we are going to use, we need to check what keys can we give to the search dictionary. To do that, we head over to Apple’s Keychain Item Class Keys and Values reference and we look for the key we are using. We are using kSecClassInternetPassword, so we search for kSecClassInternetPassword and then we find that this dictionary can have the following keys:

kSecAttrAccessible
kSecAttrAccessGroup
kSecAttrCreationDate
kSecAttrModificationDate
kSecAttrDescription
kSecAttrComment
kSecAttrCreator
kSecAttrType
kSecAttrLabel
kSecAttrIsInvisible
kSecAttrIsNegative
kSecAttrAccount
kSecAttrSecurityDomain
kSecAttrServer
kSecAttrProtocol
kSecAttrAuthenticationType
kSecAttrPort
kSecAttrPath

You do not need to add them all. What kind of data these keys expect can also be found on the same page I linked you to.

One important key is kSecAttrAccessible. We are giving it kSecAttrAccessibleWhenUnlocked. This keys specify when should the data be accessible. The key we gave it specifies that the data is only accessible when the user unlocks the device. You can also make the keychain be accessible only on the specific device the user is using, make it always accessible, and a few other options. Check Apple’s Keychain Accessibility Constants to learn what other constants you can use with this key.

The kSecAttrServer and kSecAttrAccount keys can be pretty much anything you want, but it’s better to keep them relevant. They are strings, and it makes sense to make them the website a user login is used for and the username for it.

You may have noticed that we are not using the password yet. Before we store the password, it is better to make sure it doesn’t exist yet. To do that, we use the SecItemCopyMatching() function. This function takes, as the first parameter, a search dictionary, or query, to look for an existing keychain item. We have specified the search criteria, and you can add a few more keys if you want, such as kSecAttrProtocol and kSecAttrAuthenticationType. The second parameter is a pointer that will store the returned data, where the keychain will populate the search results. This parameter can be NULL, meaning that we don’t need to have the result handed back to us. In this case, we only want to know if an item with the specified criteria already exists and we don’t care about returning it.

This function searches our app’s keychain for items that match the criteria. You can optionally give it a Search Key to refine the search more. Note that not all these keys will work for the Internet Password class. The search keys also allow you to specify wether the function should return more than one matching keychain item or all of them. This is useful if you’re building an app for a web service, where the user can have various different usernames and passwords, and you want to fetch them all.

You can also give the dictionary a Return Type Keys, specifying how the return dictionary should be formed. You can make a dictionary return the value stored, all it’s attributes, whether it should be persistent, and so on. You can find the documentation for this in the last few links I posted.

All keychain-related functions (a grand total of 4) have OSStatus as their return type. It’s very important you check for this value. In the example above, we are checking if SecItemCopyMatching()’s return code is noErr. For this tutorial, we are going to assume that noErr means no error at all. But in a real-world scenario, you may want to check for Keychain Services Result Codes to properly debug your program. It is very common to get a errSecParam return code. It usually means the search dictionary you’re providing to your function is malformed. If you get this code, check that your search dictionary only has acceptable keys for its item class.

Now you know that after creating the initial parts of the dictionary, you check whether you have a keychain item with that criteria or not. If a keychain item exists with this criteria, we will show a alert view asking the user to update the data instead. If there isn’t, we will attempt to insert the keychain item into our keychain. First we add the last search dictionary item, whose key is kSecValueData (depending on how you need to store data, the key may change – check the last part of the Keychain Services Programming Guide). This is the actual password, and you need to convert it to NSData (failing to do this step results in the Sec functions to return errSecParam).

Finally, we create an OSStatus and assign the return value of the SecItemAdd() function. This function takes the search dictionary we want to add into the keychain, and additionally (yep, can be NULL), a second nil dictionary that will contain the resulting dictionary of the operation.

After this line is done, we will print into the console the result code of SecItemAdd(). It should print 0. If it doesn’t, compare your status result code with the table I linked to above and debug accordingly (errSecParam is -50 – If you have this code, check your dictionary).

Add a few dictionary items and make sure the console logs 0. If it does, you can now move on to implement the update method.

5.

- (IBAction)updateButtonPressed:(id)sender
{
    //Let's create an empty mutable dictionary:
    NSMutableDictionary *keychainItem = [NSMutableDictionary dictionary];
    
    NSString *username = self.nwUsernameTxtFld.text;
    NSString *password = self.passwordTxtFld.text;
    NSString *website = @"http://www.myawesomeservice.com";
    
    //Populate it with the data and the attributes we want to use.
    
    keychainItem[(__bridge id)kSecClass] = (__bridge id)kSecClassInternetPassword; // We specify what kind of keychain item this is.
    keychainItem[(__bridge id)kSecAttrAccessible] = (__bridge id)kSecAttrAccessibleWhenUnlocked; // This item can only be accessed when the user unlocks the device.
    keychainItem[(__bridge id)kSecAttrServer] = website;
    keychainItem[(__bridge id)kSecAttrAccount] = username;
    
    //Check if this keychain item already exists.
    
    if(SecItemCopyMatching((__bridge CFDictionaryRef)keychainItem, NULL) == noErr)
    {
        //The item was found.
        
        //We can update the keychain item.
        
        NSMutableDictionary *attributesToUpdate = [NSMutableDictionary dictionary];
        attributesToUpdate[(__bridge id)kSecValueData] = [password dataUsingEncoding:NSUTF8StringEncoding];
        
        OSStatus sts = SecItemUpdate((__bridge CFDictionaryRef)keychainItem, (__bridge CFDictionaryRef)attributesToUpdate);
        NSLog(@"Error Code: %d", (int)sts);
    }else
    {
        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:NSLocalizedString(@"Item Doesn't Exist.", nil)
                                                        message:NSLocalizedString(@"The item you want to update doesn't exist.", nil)
                                                       delegate:nil
                                              cancelButtonTitle:NSLocalizedString(@"OK", nil)
                                                           otherButtonTitles:nil];
        [alert show];
    }
}

You should be able to understand everything up to the if. In the if we check for the existence of the keychain item, giving the user an error if the keychain item they want to update doesn’t exist.

Afterwards we need to create a dictionary with all the attributes we want to update. In this case, we only want to update the password for a given user, so we pass in the value data key to update it.

The SecItemUpdate() function takes two parameters: The search query that brings the records you want to update, and a dictionary with the attributes you want to use to replace the ones already stored. Like all other Sec functions, it returns an status code, which you can check to see if the operation was successful or if it found any problems.

Add some new user/password combinations and edit them. The keychain should update the data correctly. After that you can move on to the fetch data method.

6.

- (IBAction)getPasswordPressed:(id)sender
{
    //Let's create an empty mutable dictionary:
    NSMutableDictionary *keychainItem = [NSMutableDictionary dictionary];
    
    NSString *username = self.usernameTxtFld.text;
    NSString *website = @"http://www.myawesomeservice.com";
    
    //Populate it with the data and the attributes we want to use.
    
    keychainItem[(__bridge id)kSecClass] = (__bridge id)kSecClassInternetPassword; // We specify what kind of keychain item this is.
    keychainItem[(__bridge id)kSecAttrAccessible] = (__bridge id)kSecAttrAccessibleWhenUnlocked; // This item can only be accessed when the user unlocks the device.
    keychainItem[(__bridge id)kSecAttrServer] = website;
    keychainItem[(__bridge id)kSecAttrAccount] = username;
    
    //Check if this keychain item already exists.
    
    keychainItem[(__bridge id)kSecReturnData] = (__bridge id)kCFBooleanTrue;
    keychainItem[(__bridge id)kSecReturnAttributes] = (__bridge id)kCFBooleanTrue;
    
    CFDictionaryRef result = nil;
    
    OSStatus sts = SecItemCopyMatching((__bridge CFDictionaryRef)keychainItem, (CFTypeRef *)&result);
    
    NSLog(@"Error Code: %d", (int)sts);
    
    if(sts == noErr)
    {
        NSDictionary *resultDict = (__bridge_transfer NSDictionary *)result;
        NSData *pswd = resultDict[(__bridge id)kSecValueData];
        NSString *password = [[NSString alloc] initWithData:pswd encoding:NSUTF8StringEncoding];
        
        self.passwordLbl.text = password;
    }else
    {
        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:NSLocalizedString(@"The Item Doesn't Exist", nil)
                                                        message:NSLocalizedString(@"No keychain item found for this user.", )
                                                       delegate:nil
                                              cancelButtonTitle:NSLocalizedString(@"OK", nil)
                                              otherButtonTitles:nil];
        [alert show];
    }
}

Fetching data is arguably the hardest thing to do when it comes to keychain items. One of the few things you will notice is that this search dictionary has two new keys, kSecReturnData and kSecReturnAttributes, and that they accept core foundation booleans. The first key will specify that it wants to return the securely stored data only, whereas the second key tells the API that it wants to fetch all the attributes. This is very important: If you only have kSecReturnData as true but not kSecReturnAttributes, the second parameter of the SecItemCopyMatching() function will only return the encrypted data and store it in a CFDataRef object. Whereas, if you also set kSecReturnAttributes to true, then the second parameter of the function will return a dictionary, that is, a CFDictionaryRef. They are different types and if you accidentally give it the wrong pointer to the second parameter, your app may crash eventually.

If the if evaluates to true, then it’s all nice and dandy. We create a new NSDictionary to which we transfer ownership of our old CFDictionaryRef while casting it as a NSDicitonary, and then we can just use it like a normal Objective-C object. We grab the NSData with the kSecValueData key, and then we convert it into a string, that later we give it the password label.

When you type in a username in the second username textfield, the user’s password should show up in the password label below. Try it and play with it. When you are certain it works, let’s move to the last method.

7.

- (IBAction)deleteItemPressed:(id)sender
{
    //Let's create an empty mutable dictionary:
    NSMutableDictionary *keychainItem = [NSMutableDictionary dictionary];
    
    NSString *username = self.usernameTxtFld.text;
    NSString *website = @"http://www.myawesomeservice.com";
    
    //Populate it with the data and the attributes we want to use.
    
    keychainItem[(__bridge id)kSecClass] = (__bridge id)kSecClassInternetPassword; // We specify what kind of keychain item this is.
    keychainItem[(__bridge id)kSecAttrAccessible] = (__bridge id)kSecAttrAccessibleWhenUnlocked; // This item can only be accessed when the user unlocks the device.
    keychainItem[(__bridge id)kSecAttrServer] = website;
    keychainItem[(__bridge id)kSecAttrAccount] = username;
    
    //Check if this keychain item already exists.
    
    if(SecItemCopyMatching((__bridge CFDictionaryRef)keychainItem, NULL) == noErr)
    {
        OSStatus sts = SecItemDelete((__bridge CFDictionaryRef)keychainItem);
        NSLog(@"Error Code: %d", (int)sts);
    }else
    {
        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:NSLocalizedString(@"The Item Doesn't Exist.", nil)
                                                        message:NSLocalizedString(@"The item doesn't exist. It may have already been deleted.", nil)
                                                       delegate:nil
                                              cancelButtonTitle:NSLocalizedString(@"OK", nil)
                                              otherButtonTitles:nil];
        [alert show];
    }
}

This is the easiest method to implement. We do what we have been doing all along: Create a search dictionary and then check if we have items with that criteria. If it finds a dictionary, we simply call SecItemDelete(), which only takes one parameter that is the search dictionary, and like all the previous 3 Sec functions you have seen, it returns an OS Status.

Play with the whole app now and see if it behaves like you would expect it to behave.

Wrapping Up

You should know have a firm understanding of how the iOS Keychain works.

Keep in mind that the keychain can be used to store other data, even private keys and certificates. The iOS keychain, while keeping the API simple, is really powerful with all its features. The way search dictionaries work makes a lot of sense with the whole API.

You have seen the 4 functions that come with the Keychain Services API:

  • SecItemCopyMatching
  • SecItemAdd
  • SecItemUpdate
  • SecItemDelete

And you also learned how to use them.

As usual, any comments and feedback are very welcome. You may also download this project’s source code from here.

How to tell what each error code means:

If you get an error code (like -50), and you can’t tell what it means on the internet, you can open up the Terminal in your Mac and type:

security error -50

That will tell you what the error is so you can go about fixing it. In the case of error -50, the Terminal will tell you:

Error: 0xFFFFFFCE -50 One or more parameters passed to a function were not valid

The post Using The iOS Keychain. appeared first on Andy Ibanez.

]]>
https://www.andyibanez.com/using-ios-keychain/feed/ 26 295
Making A Linux File Server That Interacts With OS X Mavericks. https://www.andyibanez.com/making-linux-file-server-interacts-os-x-mavericks/ https://www.andyibanez.com/making-linux-file-server-interacts-os-x-mavericks/#comments Mon, 28 Oct 2013 22:55:30 +0000 http://andyibanez.com/?p=130 Learn to build a File Server that interacts with OS X Mavericks. Mavericks will talk to the File Server via SMB.

The post Making A Linux File Server That Interacts With OS X Mavericks. appeared first on Andy Ibanez.

]]>
Making A Linux File Server That Interacts With OS X Mavericks.

This is a non-development tutorial, but with Mavericks being really new there are no tutorials to make your file server interact with the OS smoothly. Because of this, I’m writing this small tutorial to setup your server to share files with OS X Mavericks. This may or may not work with OS X Lion and OS X Mountain Lion, but you lose nothing trying, right?

Last weekend I decided to ditch my Windows Desktop altogether and to make my beautiful 27” Inch iMac my main computer. The reason I stuck with Windows for so long is that I just cannot stash thousands of Gigabytes in my iMac. In other words, storage was the only reason I was using Windows. But then I thought, wouldn’t it be great to kill my Windows setup altogether, and convert all this hardware into a file server? I said yes, and that’s exactly what I did.

I’m incredibly illiterate when it comes to Linux and file servers. This was my first time setting a file server, actually. I was going to use FreeNAS. I looked at many OS alternatives for this, but I decided to stick with Ubuntu. If you’re in a similar situation, follow along this tutorial and hopefully by the end of it you will have a working Ubuntu file server that can communicate painlessly with your Mac.

1. Things to Consider: Your File Server Won’t use AFP.

If you don’t have much of a technical background and just want to have your file server up and running, feel free to skip this paragraph.

It will use SMB (yes, Windows’ – ironic in my case, isn’t it?). Why? Apple has the nasty habit of killing many of their technologies in the background with no formal announcement, but this was spotted in some technical documents. Basically, Apple will shift from AFP file sharing to SMB2 in OS X 10.9 Mavericks. It may come as a surprise to some but many people have had problems with AFP in Mountain Lion and below. Apple is dropping support for their own protocol in favor of Windows’ because it is more secure (or so they say).

There were many problems I had when I was first setting my file server up. I actually had no idea what this AFP protocol was, much less that it was being deprecated. When I first set the file server up, I used this tutorial to make Ubuntu announce and interact with OS X via AFP. I had no problems following that tutorial, but at the time of testing the problems were obvious. I was able to connect to my file server exactly once – then I restarted the server, and OS X Mavericks refused to connect anymore, burping this error message instead:

“The version of the server you’re trying to connect to is not supported. Please contact your system administrator to solve this problem.”

Googling tirelessly for hours I never found a solution to get that setup to work. I learned AFP was being dropped, and no one ever suggested a working around (there were a few workarounds, but none of them worked for me). So I just started over with a new Ubuntu Installation, and luckily, I succeeded putting my file server up.

Requirements for the File Server

I will not talk about the hardware requirements here. Personally I transformed an old computer into a file server. I assume you already have some old hardware lying around that you can use. But in case you’re curious, this is my server’s setup:

  • 5 internal 1TB hard drives.
  • 12 GBs RAM
  • VGA 1680 x 1020 Monitor

You can probably tell I won’t guide you through installing SSH to remotely control the server, as personally I control the server physically since it is next to my main computer anyway.

As for the software requirements:

  • Ubuntu Server. It probably works with the “normal” Ubuntu Desktop, but this is what I use. At the time of this writing, the latest version is Ubuntu Server 13.10, and you can get it here.
  • Samba. We need this so our server can be talked to with SMB. I will guide you through this.
  • Avahi (Optional). This isn’t needed but it does something really cool – announce the server via Bonjour. If you want to install this, I will guide you through.

1.1 So no Netatalk, then?

If you’re one of the poor souls who couldn’t get the Ubuntu File Server work properly with OS X Mavericks or (Mountain) Lion, you’re probably asking yourself why we aren’t using Netatalk.

Like I said at the beginning of this tutorial, we will be using SMB instead of AFP. Netatalk provided the required tools to talk to your server via AFP. Since Apple will most likely get rid of AFP completely at some time, we don’t need to use it at all.

2. Preparing the File Server

Installing and Configuring Ubuntu Server

How to install the server from scratch is out of this tutorial. I will focus mostly in Samba to make sure your Ubuntu can actually talk to your OS X. So go ahead and install it, as barebones as you can/want. I didn’t do any different than clicking “next” until the installation started.

Just make sure you use a different computer and account name. There seems to be some sort of conflict when OS X has the same account name as the file server.

Additionally, you will need to give Ubuntu an static IP address or use a .local name. Personally I gave the Ubuntu machine an static IP. Much easier to work with with my home network.

Installing and Configuring Samba: A Very Important Piece of the File Server

This is the most monotonous and longest part, but it isn’t so bad. Make sure you’re ready to spend 15 minutes of your time. To get this right I actually had to follow two different tutorials, and I have compiled them into one for you:

Step 1: Open Up The Terminal

I hate silly steps like this but let’s put them in for the sake of completeness. Open up a terminal window. We’re going to do everything as root to save ourselves the constant “sudo”. Type in:

sudo su

We’re going to be editing a few files later on. I like to use Linux’s included gedit to edit said files. Others may prefer, to use Nano, Vi, Vim, etc.. In case you’re like me and prefer a nice GUI, please install gksudo. This is irrelevant to the tutorial, but you shouldn’t execute GUI programs via the command line with sudo, even when you do need superuser permissions. Instead, to execute GUI programs via the command line with sudo permissions, execute them with gksu (or gksudo) instead. To install gksudo:

apt-get install gksu

To use it when you aren’t using the command line as root:

gksu gedit somefile

Or:

gksudo gedit somefile

When you want to share new directories via Samba later on, you will need to edit a file, and if you like gedit, you better use this to launch it instead of sudo from now on. So maybe it is a very little relevant after all!

Step 2: Install and Configure Samba for the File Server

With your shiny super user permissions, it’s time to install Samba:

apt-get install libcups2 samba samba-common

Once it’s done installing, we have to do a couple of configurations. We need to edit

/etc/samba/smb.conf

now:

gedit /etc/samba/smb.conf

This will launch gedit with Samba’s configuration file. Press Ctrl + F and type in “security =” so you’re taken to this area:

[...]
# "security = user" is always a good idea. This will require a Unix account
# in this server for every user accessing the server. See
# /usr/share/doc/samba-doc/htmldocs/Samba3-HOWTO/ServerType.html
# in the samba-doc package for details.
#  security = user
[...]

Remove the comment (the pound sign) from the last line, and add

username map = /etc/samba/smbusers

below it, so you have:

[...]
# "security = user" is always a good idea. This will require a Unix account
# in this server for every user accessing the server. See
# /usr/share/doc/samba-doc/htmldocs/Samba3-HOWTO/ServerType.html
# in the samba-doc package for details.
  security = user
  username map = /etc/samba/smbusers
[...]

Save (Ctrl + S) the file and quit gedit so you have control over the terminal window with su.

Restart Samba when done:

service smbd restart

Step 3: Creating Samba Users

Samba users are needed to interact with the file server.

The steps to create Samba users are simple as 1,2, which I will guide you through:

  1. Create a password for the user you want to create.
  2. Add that user to to the Samba users file.

To create the password for the Samba user:

smbpasswd -a <username>

Where username is the name of the new Samba account you want to create. For example:

smbpasswd -a Andy

Then open the

/etc/samba/smbusers

file:

gedit /etc/samba/smbusers

And add the username like this:

<username> = “<username>”

To the left you put the name of the Ubuntu account that will have to this Samba account, and to the right, the name of the Samba account it has access to.

So if my Ubuntu account is called “andyfileserver” and the Samba account is called Andy, it would be like this:

andyfileserver = "Andy"

Save the file and restart Samba once again.

service smbd restart

And that’s it! You can now connect to your file server with your OS X Mavericks by going to Finder > Go > Connect to Server…

Sharing Folders

This is really out of the scope of the tutorial. There are many configurations for shared folders, but to get you started, you add some entries to your

/etc/samba/smb.conf

file. Open it and add your entries at the end of the file, like this:

[theroot]

comment = File Server
path = /media/andy
read only = no
writable = yes

[bluray]

comment = Optical Drive
path = /cdrom
read only = yes
writable = no

You essentially mount directories. What is in the first [] brackets is what your Mac will see as their name in the file server. I have “theroot” and “bluray”, so for me this looks like this:

Screen Shot 2013-10-28 at 6.49.00 PM

Again, there are many configurations so you may want to look how to configure Samba share folders for your needs. you can even assign different permissions and everything.

3. (Optional) File Server Announcing to the Network

You have finished the most important part and basically you can use your file server now, but this little extra step is cool in my opinion. Instead of having to do Finder > Go > Connect to Server… Every time we want to connect to the server, why don’t we show up in Finder’s left bar?

Screen Shot 2013-10-28 at 6.35.52 PM

To do that we need to install Avahi. Avahi is a simple file server daemon that announces to the network when it has connected, basically Bonjour. With this instead of telling OS X Mavericks to find the server, we can tell the server to tell everyone when it’s connected. In that way our server will appear as extractable media in the left bar of any finder Window, and it’s much easier and pleasant to interact with it using this method.

Luckily, installing and configuring Avahi is really fast and simple.

If you still have your su terminal window, type this in:

apt-get install avahi-daemon avahi-utils

Then open and edit the file

/etc/avahi/services/smb.service

(the file will be blank and that’s fine – you’re creating it from scratch):

gedit /etc/avahi/services/smb.service

And copy and paste this into it:

<?xml version="1.0" standalone='no'?>
<!DOCTYPE service-group SYSTEM "avahi-service.dtd">
<service-group>
 <name replace-wildcards="yes">%h</name>
 <service>
   <type>_smb._tcp</type>
   <port>445</port>
 </service>
 <service>
   <type>_device-info._tcp</type>
   <port>0</port>
   <txt-record>model=RackMac</txt-record>
 </service>
</service-group>

And that’s it! You don’t even need to restart Avahi. It will automatically announce to the network and your Mac will see your file server in Finder to the left.

And that’s it! I hope you find this tutorial useful, as personally I had a hard time configuring my file server to talk properly with OS X Mavericks.

Linklist:

These are the original tutorials I used:

http://www.howtoforge.com/ubuntu-13.04-samba-standalone-server-with-tdbsam-backend
http://www.howtogeek.com/howto/ubuntu/install-samba-server-on-ubuntu/
http://www.macdweller.org/2012/05/13/samba-bonjour-with-avahi/

The post Making A Linux File Server That Interacts With OS X Mavericks. appeared first on Andy Ibanez.

]]>
https://www.andyibanez.com/making-linux-file-server-interacts-os-x-mavericks/feed/ 58 130
Creating Aggregate Projects on Theos: A Configurable Tweak. https://www.andyibanez.com/creating-aggregate-projects-theos-configurable-tweak/ https://www.andyibanez.com/creating-aggregate-projects-theos-configurable-tweak/#comments Sat, 11 May 2013 02:32:08 +0000 http://andyibanez.com/?p=122 Learn how to create aggregate projects with Theos, while writing a configurable mobile substrate tweak.

The post Creating Aggregate Projects on Theos: A Configurable Tweak. appeared first on Andy Ibanez.

]]>
Creating Aggregate Projects on Theos: A Configurable Tweak.

More often than not, you will want to create a Theos project that is composed of one or various subprojects. A very common case for this would be to write a Tweak that can be configured by the user (guess what we’re writing today…): You’d create a Tweak project that contains a PreferencesLoader subproject that is in charge of writing the tweak’s behaviour. Aggregrate projects with Theos are very easy to build.

In this tutorial we’re going to write an Aggregate project that does just that: We will write a very simple tweak with a simple PreferencesLoader that will decide whether the tweak should alert the user when the app has been launched or not.

You have seen this simple tweak in action before: I wrote it in my Writing a MobileSubstrate Tweak for iOS tutorial. In fact to save us the time, I will assume you followed that tutorial, and that you managed to compile it and run it. We will write the same tweak with a twist. In that way we will save us the trouble of writing a MobileSubstrate tweak and get straight to the point of this tutorial.

In this tutorial…

  • You’ll learn the basics of a PreferencesLoader project (Note that this is NOT the same a Settings.bundle found in App Store apps, but they can share similarities).
  • You will learn to write aggregate projects with Theos. An aggregate project is a project that contains one or more sub projects.

The following assumptions are made:

  • You know how to write iOS apps. You know the SDK and you have written an app. It doesn’t matter if said app has been published. You have the necessary skills to write an iOS app.
  • You know how a Settings.bundle works and its limitations for iOS Apps. You also know how to make bundles and edit their plists manually.
  • You know how to write Mobile Substrate tweaks. If you don’t, head over to my tutorial to get started…

Alright, without further ado, let’s get started!

Writing The Mobile Substrate Tweak: Aggregate Projects Don’t Come Alone!

Since we’re going to build this project on top of an old tweak from an old tutorial, go build the tweak described in the tutorial linked above. It shouldn’t take you long (it’s literally less than 15 lines long), so go ahead and build it. I will wait for you.

Got it up and running? Cool, let’s continue.

Discussion on PreferencesLoader: The Most Common Use For Aggregate Projects

PreferencesLoader was written by DHowett as a way to let developers create settings pages for their apps and tweaks in the Settings app. PreferencesLoader can be very different than the standard Settings.bundle you’re used to.

When it comes to PreferencesLoader, there are two approaches to fetch to create a settings page for your app: The simple approach and the PreferencesLoader approach.

The Simple Approach

The simple approach is very similar to a Settings.bundle in that the cells are loaded from a plist file stored in /Library/PreferenceLoader/Preferences/. This plist contains the specifiers that your app requires. These can be simple cells that contain switches, text, text fields, and so on. The specifiers are loaded from their respective files and using the simple approach your bundle doesn’t execute code. It just loads and saves the values in the modifiable fields of the bundle.

The PreferenceBundle Approach.

Unlike the static settings the Simple Approach offers, this approach actually allows your settings bundle to execute code. The entry plist for this approach must have the same name of the PreferenceBundle to run and define it’s isController option to true. You can show your own view and everything you need. I will cover the PreferenceBundle Approach in another tutorial.

For more information on PreferenceLoader, refer to the iPhoneDevWiki page on the topic.

Creating Aggregate Projects: Getting Our Hands Dirty.

Now you have the tweak that alerts the user when an app has been launched. This is cool, very cool, but what if we wanted the user to have the ability to turn it off and on as he pleases? We need to write a settings page for it. But it wouldn’t make sense to write a different project for it. Does it make sense to ask the user to install the tweak, and then separately install it’s settings page? I don’t think so.

Aggregate projects come to the rescue. We’re going to create the settings project for our tweak, and they will be installed at the same time, since the settings project will be a subproject of the main project! But how do we do that? There is no template or anything to simplify the job of creating an aggregate project.

You don’t really need to do much to create an aggregate project. In fact, it’s just a matter of adding two extra lines to your makefile and that’s it.

But we don’t have the preferences project set up yet. In your terminal window, cd to your current project’s directory and run Theos’ nic.pl to create a new project. Select “iphone/preference_bundle” and finish the wizard.

If you’re lucky, Theos will automatically detect you created a project inside another one, and it will modify the makefile for now. If it doesn’t, don’t worry. Simply add the following two lines to it and you will be fine:

SUBPROJECTS += notifiersettings
include $(THEOS_MAKE_PATH)/aggregate.mk

It’s important the SUBPROJECTS specifier is before the include of the aggregate makefile.

Wiring Things Up: The Core Of Aggregate Projects.

make package install your project now. Open the settings app. Apart from our nice alert that tells the user Settings has been launched, if you scroll down a bit you should see a cell containing our settings preferences name for our tweak. Open it up and you should see this:

If you change the switch and go back, you will see its current state was automatically saved by our good friend PreferenceLoader. At least with saving settings, you don’t have to worry about that if you use the simple approach.

Naturally now we gotta modify its strings so it makes sense to the user.

Go to the PreferencesLoader subproject. Now open its Resources folder and open the plist there (NOT the one called Info.plist – the other one that has then name of your project). Since you know how to modify these, just change the name of the label to something like “Alert user on app launch”:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>items</key>
	<array>
		<dict>
			<key>cell</key>
			<string>PSGroupCell</string>
			<key>label</key>
			<string>Notifier Settings.</string>
		</dict>
		<dict>
			<key>cell</key>
			<string>PSSwitchCell</string>
			<key>default</key>
			<true/>
			<key>defaults</key>
			<string>com.AndyIbanez.NotifierSettings</string>
			<key>key</key>
			<string>alertLaunch</string>
			<key>label</key>
			<string>Alert User on App Launch</string>
		</dict>
	</array>
	<key>title</key>
	<string>NotifierSettings</string>
</dict>
</plist>

Now we just have to modify the actual tweak to read that value and alert the user if he so wishes (AKA if alertLaunch is YES).

With jailbreak apps you don’t have many “commodities” as you do with App Store apps, so you can’t use the same methods you’d use in App Store apps to read settings from a plist.

Instead, we have to read the plist manually, fetch the values we have to check for manually, and do whatever we want with those values.

You should know this by now, but in case you don’t remember, just create an NSDictionary object and use initWithContentsOfFile or dictionaryWithContentsOfFile. The full path to your settings plist is /var/mobile/Library/Preferences/YOUR_PLIST.plist.

In other words, your reading code would look like this:

	NSMutableDictionary *settings = [NSMutableDictionary dictionaryWithContentsOfFile:
									[NSString stringWithFormat:@"%@/Library/Preferences/%@", NSHomeDirectory(), @"com.AndyIbanez.NotifierSettings.plist"]];

(Note the use of NSHomeDirectory(). The iPhoneDev Wiki which is maintained by the guys behind our awesome jailbreak development tools recommend we never refer to /var/mobile directly, and that we should use NSHomeDirectory() instead.)

With that in mind, then you just read the value you want (alertLaunch in my case and probably yours too), and use it accordingly. When done, your whole method should look like this:

-(void)launch
{
    NSString *appName = [self displayName];
    
	NSMutableDictionary *settings = [NSMutableDictionary dictionaryWithContentsOfFile:
									[NSString stringWithFormat:@"%@/Library/Preferences/%@", NSHomeDirectory(), @"com.AndyIbanez.NotifierSettings.plist"]];
	NSNumber* shouldNotify = [settings objectForKey:@"alertLaunch"];
	
	if([shouldNotify boolValue] == YES)
	{
	    NSString *message = [NSString stringWithFormat:@"The app %@ has been launched", appName, nil];
	    UIAlertView *alert1 = [[UIAlertView alloc] initWithTitle:appName message:message delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil];
	    [alert1 show];
	    [alert1 release];
    }
    %orig;
}

And you’re done! Try it now. When the switch is on, you’ll be alerted when an app has launched. When it’s off, it will do the opposite.

You can downloade the source code for the project in my Github here. Study it and improve it, break it, etc..

If you have any questions or comments, you can as usual use the comments section below.

CHANGELOG:
_____________
May 10, 2013 at 6:00 PM [America/La_Paz]: First version of this post.
May 12, 2012 at 2:00 PM [America/La_Paz]: I forgot to add the source code for the settings plist. It’s added now.

The post Creating Aggregate Projects on Theos: A Configurable Tweak. appeared first on Andy Ibanez.

]]>
https://www.andyibanez.com/creating-aggregate-projects-theos-configurable-tweak/feed/ 25 122