Andrés Ibañez | 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 In A Few Weeks From Now, This Old Website is Going Away. https://www.andyibanez.com/in-a-few-weeks-from-now-this-old-website-is-going-away/ https://www.andyibanez.com/in-a-few-weeks-from-now-this-old-website-is-going-away/#respond Mon, 19 Aug 2019 22:07:57 +0000 https://www.andyibanez.com/?p=586 Hello all. First, I want to start this post saying “thank you all”. I started this website in 2011 when I started earning some little money when I started college, and it has been a fun trip since then. I blogged mostly about iOS and occasionally covered different topics throughout the years. Sadly, it’s time […]

The post In A Few Weeks From Now, This Old Website is Going Away. appeared first on Andy Ibanez.

]]>
Hello all.

First, I want to start this post saying “thank you all”. I started this website in 2011 when I started earning some little money when I started college, and it has been a fun trip since then. I blogged mostly about iOS and occasionally covered different topics throughout the years. Sadly, it’s time for this specific website to go away.

You may be wondering why I’m taking this measure now. First, you need to remember that I haven’t blogged anything since 2017. My website has almost become irrelevant in terms of content as I haven’t put enough effort in keeping it updated, mostly because I’ve been busy with other things.

When I started this website, I was very surprised by how many people started sharing its content. Not only did people share it with others, people started referencing the content of my blog as a reference for what they were writing. Throughout the years my posts have been linked in StackOverflow answers, and I have even seen it linked from university websites! Makes me think that some tutors thought my content was worth it to share with their students. Seeing my content being spread that way brought big smiles to my face, especially when I noticed sudden spikes in my website’s traffic. Even more surprising, some people actually grabbed my original content and translated it to their own native language to share knowledge even further. I want to thank the people who did that as it made me feel that my content was appreciated.

Unfortunately, right now the vast majority of my content is outdated, and it’s not very relevant anymore. Also I think my writing style has changed throughout the years – I’ve read my older content and I just can’t believe I used to write the way I used to do. Despite being a college student at the time, my style is immature at best, and childish at worst. This kind of thing leaves a wrong impression in my image, and I want it to go away to pursue other endeavors.

Got ya! This is not a goodbye!

I’m not actually thinking in killing my website entirely. Rather, I think it’s time for a new beginning. I want this old website to go away so I can start anew. I do not only want to start writing higher-quality content, but I also want to have a bigger level of compromise with the people who have been reading me in the past 8 years.

And this is I want to do: I want to start a new website, with this very same domain, but with a different format, and with higher quality content. The new website will still be a blog, but I will be using a static site generator instead of WordPress (which frankly I’ve wanted to get rid of for a long time now, but I’m just getting into it).

You might be wondering why such a dramatic post if this website is going to continue in some form anyway. There’s a few reasons for that. First, I wanted to give a big and legit thank you to all the people who have read my blog in the past years. I started blogging as a personal log of things I learned, but it eventually turned into this big thing that got shared around more than I had expected. The second reason is that I want to start with new fresh content, and that means that all my blog posts are going away. They are not being migrated to the new site. Save for a very few exceptions (it’s probably about 10% of my content), everything is going to be deleted, and I wanted to give my readers a change to archive any blog posts that they don’t think are gonna make the cut.

If you want to ask me what content is gonna be carried over to the new site, the answer is “I don’t know yet”. I have the WordPress data that shows me what posts are still very popular, but I do not want to carry all of those to the new site. Also popularity is not related to quality or even relevancy. For example, the article “Are You Storing Sensitive Data in NSUserDefaults? Stop Doing That!” is one of my most popular posts to date. This one has been shared and translated many times. The problem with it is that, while the core idea of the post is still very true today, many things have changed since I posted it – you cannot really access the plist file as easily anymore, so I’d have explore things again and see to what level is still relevant, and whether it deserves such many words to express the main idea. Also, I absolutely HATE the writing style on that one. It makes me want to pull my hair off. It’s very popular, but I feel embarrassed it has been read so many times. Another article that is popular is Multithreading on iOS And Mac OS X Using NSOperations.. This a popular article (not as popular as the other one, though) and the content on it is still very relevant today. The problem with it is, again, my awful writing style. I might carry this one over but not as it currently is. Instead, I might rewrite it from scratch.

Some posts will disappear completely altogether, and the others will be rewritten from scratch. I’m still deciding which ones will make the cut.

As for when this website will go away for sure, I cannot really give a solid date yet. I’m currently working on the new website and exploring static generator tools (I think I’m gonna stick with Hugo). Once I choose the static gen tool, I will go through my old content and choose the ones that will be carried over to the new website. All this could be done for this Friday as it could be done next Friday, or in six weeks from now or more (or less). The new website will not be launched with empty content, and I will do my best to map their URLs to the new website so they don’t break across the vast ocean that is the internet. Also, as part of my compromise to you guys, I will start blogging more often. I will try to organize myself better to write more often. In the past months I have followed many other Swift Devs and I’ve been inspired to involve myself with the community more. I have a desire, and that desire is to be relevant in the Swift and iOS communities again, and I cannot do it with my old content.

So, if you care about still having access to the old content from this website, make sure you save what you care about it. If you are an archivist, please do me a favor and save my website in a way that can be accessed (I can’t be bothered to take such measures myself) by others in the future.

For updates on the upcoming website, follow me on Twitter (@AndyIbanezK), and once again, thank you all for your support.

The post In A Few Weeks From Now, This Old Website is Going Away. appeared first on Andy Ibanez.

]]>
https://www.andyibanez.com/in-a-few-weeks-from-now-this-old-website-is-going-away/feed/ 0 586
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
WWDC Keynote Thoughts https://www.andyibanez.com/wwdc-keynote-thoughts/ https://www.andyibanez.com/wwdc-keynote-thoughts/#respond Mon, 13 Jun 2016 16:39:17 +0000 https://www.andyibanez.com/?p=528 (Yes, I just copied Erica Sadun’s idea). This post will be update as the keynote goes by. (I will have lunch midway through the keynote, so I will be at least 15 minutes late at some point). ——— All times are EST. 12:52 PM: The stream seems to be finally up. 1:02 PM: Yes, talking […]

The post WWDC Keynote Thoughts appeared first on Andy Ibanez.

]]>
(Yes, I just copied Erica Sadun’s idea).

This post will be update as the keynote goes by.

(I will have lunch midway through the keynote, so I will be at least 15 minutes late at some point).

———

All times are EST.

12:52 PM: The stream seems to be finally up.
1:02 PM: Yes, talking about the Orlando tragedy is needed. I’m sure it hit Tim harder than others.
1:05 PM: That’s a lot of scholarships. I wish I could get that scholarship myself. Welp just gonna save up and go there someday…
1:09 PM: It was probably a good idea to avoid talking about the numbers. Jumping into OS features less than 10 minutes in.
1:11 PM: You are telling me watchOS 3.0 will make my watch usable beyond notifications? MADNESS. Neat change to the side button too.
1:12 PM: Doing this live is hard, so I will just edit this post later haha.

———–

5:20 PM:

SiriKit

So out of everything that has been announced, I’m excited for a lot of it.

Siri APIs of course. This is what I have been hoping for for years. Unfortunately, after further reading in the docs, it looks like it’s not possible to do much with it right now. SiriKit is designed to work with certain domains:

  • Audio or video calling
  • Messaging
  • Sending or receiving payments
  • Searching photos
  • Booking a ride
  • Managing workouts

At least at first, apps that adopt SiriKit will be limited to those domains.

Proactive Suggestions

Craig previewed a feature where QuickType is more useful and proactive than ever. It’s neat, and apps can expand on this. If your app has conceptual data it can provide to other apps, you can do so with these APIs. They are introduced in new properties of the UITextInputTraits class.

Apple Opens App Messages to Developers

I will go ahead and say meh to this one. All the visual woo is not too attractive for me. I could live without it. Stickers have always been a gimmick that I have never used. The potential of actual third party app integration is definitely neat, but I live in a country where WhatsApp is the norm, and nobody utilises iMessage. If I had to rate the new Messages announcements, I’d give them a 4/10. 4 being the only actual helpful stuff for developers (no stickers, no fancy features, just SDKs).

User Notifications

Apple is killing the old notification APIs in favor of this. I have no complaints. They are incredibly cool. This is probably one of the first things I will start playing with.

Speech Recognition

Very nifty framework. You can recognise speech from your own voice and even recorded audio. Writing a quick app to do this for me will me finish a lot of college of college homework.

Widget Enhancements

Absolutely cool. I like the idea of having my own widgets on search. Also how they appear when peek and popping on app icons. I see a lot of potential for this.

There’s a Whole Bunch of Extension Points

The amount of extension points has at least doubled since iOS 8:

  • Call Directory
  • Intents
  • Intents UI
  • Messages
  • Notification Content
  • Notification Service
  • Sticker Pack

Some of these are outright meh and the others do look helpful. I have yet to read an them though. That Call Directory Extension Points looks interesting. It might be something I asked Apple for back in 2011.

5:48 PM EDIT: Right, Call Directory is most likely the thing Craig showed where you can hook into calls to see if they are spam, or really extend them to do anything else with whatever number you grabbed.

Misc

Developers will finally have access to RAW camera data from the iOS camera. I love taking mobile pictures, and I currently use a Samsung Galaxy S7 Edge for most of my pictures. It looks like that could be changing soon.

That’s it for now. This post may or may not be edited later.

The post WWDC Keynote Thoughts appeared first on Andy Ibanez.

]]>
https://www.andyibanez.com/wwdc-keynote-thoughts/feed/ 0 528
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
Eliminating the C For Loop Is Not Bad for Swift. https://www.andyibanez.com/why-eliminating-c-style-for-loops-is-not-a-bad-thing-for-swift/ https://www.andyibanez.com/why-eliminating-c-style-for-loops-is-not-a-bad-thing-for-swift/#comments Fri, 18 Dec 2015 19:44:30 +0000 https://www.andyibanez.com/?p=417 Eliminating C-Style for loops is not a bad thing for Swift, as there are other kind of loops that can be used and even alternatives to loops themselves.

The post Eliminating the C For Loop Is Not Bad for Swift. appeared first on Andy Ibanez.

]]>
The C-style for ;; loop simply has no place in Swift.

Recently, it has been announced by Chris Lattner himself that Swift will do away with the classical C-style for ;; loop. The suggestion was done by Erica Sadun, one of the most respected members of the iOS Development Community, and her proposal has been accepted for Swift 3.0. This for loop is used in almost every programming language, from Java to Objective-C (of course, it’s just a superset of C after all) and beyond.

It sparked this Reddit discussion about the decision. Many people support the removal of the for ;; loop and many of them seem to be worried. In this post I want to explain why I support the elimination of the C for loop, and why people shouldn’t be worried about its removal in the future.

In this post, I want to urge you to write code with as little loops as possible, and to embrace higher order functions whenever possible. At no point will I suggest you stop using loops altogether. If anything, use the other loops (for-in and repeat-while) instead of for ;;.

Everything that can be done with a loop, can be done without it.

Swift will keep some of its loops, like repeat-while and for-in, so it’s not like Swift is getting rid of the ability to loop altogether.

But even if it were, there are more, cleaner, alternatives to loops. Let’s talk a little about functional programming, which Swift seems to embrace a lot, to see why.

Functional programming is the programming paradigm that embraces immutability and the reduction of side-effects. Think about the difference between let and var. If you are reading this, you are familiar with how these keywords work. Anything you declare with let cannot be changed. Anything declared with var is fair game to be changed in the future.

In a pure functional coding style, you would avoid using var because everything is immutable. This means that there cannot be the concept of a counter variable, because they are incremented or decremented at some point, and that’s not immutability. Now of course, in the real world it is impossible to achieve full functional code, since any user interaction that happens must cause something to happen somewhere in your program. But still, I believe coding “functionally” whenever possible is going to help you write much better code, and using as few little loops as possible is going to help you do that. Getting rid of the classic C-style loop is a very good beginning to start writing functional code.

Like I said in the Reddit thread I mentioned above, I took a functional programming class in Scala in which we didn’t write a single loop, despite the fact that Scala for loops don’t technically violate the principle of immutability. Can you imagine writing a lot of code without a single loop, despite tasks that may need them? It’s entirely possible using recursion and higher-order functions.

Recursion

Recursion is an important topic, and arguably one of the hardest topics for newcomers in programming to understand. One of the reasons people are hating the removal of the C-style for ;; loop is that they say transitioning to Swift from whatever your first language is will be hard, and if you have been programming for a while, it will be hard to get used to. This is not so necessarily true because the concept of recursion is the same everywhere, so even if learning the Swift loops and higher order functions was too complicated, you could still achieve the same results using recursion. This is a big even if, because Swift’s loops are easy, and higher order functions are not too complicated to understand. Really, the only reason you would want to keep for ;; around is to make it easier than it already is to transition from any other language to Swift, and for legacy reasons. I imagine a programmer who has been writing code for the last 20 years has written many for ;; loops, but programming paradigms change, some are introduced, and it is all for the better for Software Development. We simply cannot cling to all idioms and features of old languages for too long.

I think recursion has its place. Some algorithms are better with it, and some of them are more complicated than they should be. Use recursion when it makes sense. I am not saying you should ditch loops altogether after all, and I definitely don’t want people to write over complicated algorithms with recursion when they could have been done with a Swift loop (as long as the loop in question is not a for ;;).

Higher Order Functions

Higher order functions can replace loops for around 90% of tasks that require them, I’d say. The Wikipedia page on Higher-Order Functions defines a higher order function as:

In mathematics and computer science, a higher-order function (also functional, functional form or functor; not to be confused with the functor concept in category theory) is a function that does at least one of the following:

  • takes one or more functions as arguments,
  • returns a function as its result.[disputed – discuss]

This is the definition copied and pasted from Wikipedia as of December 18, 2015 at 2:35 PM – Please note that the second bullet point is marked as disputed.

In short, a higher order function can take one or more functions (if you really are an old school programmer, think of it as a function taking a pointer to a function in C++. If you have worked with C# before, think of lambdas. And, if you are writing in Java 8 and above, think of anonymous functions) and then execute them. Note that the Wikipedia article says that a higher order function returns a function and it is marked as disputed. And rightly so – I’d probably say that a higher order function takes one or more functions as parameters and it might return another function – That is, I wouldn’t say returning a function is required for it to be considered a higher order function. Swift includes many higher order functions for collections that help you reduce the usage of loops.

If you are new to programming, this discussion may be hurting your head a little bit. The good news is I have code examples ready to illustrate how you can reduce the use of loops altogether.

For this example, I will adapt some old code of mine to loops, and then I will show you how to reduce it to using HOFs. This code comes from my app Mignori, and it’s purpose is to run through a JSON response from a web service and to map each JSON object to a class manually with the help of SwiftyJSON. I am not using an object mapper for this as I consider it to be overkill for my specific purpose, but I digress.

You could start with the “naïve” approach. Suppose you just talked to a web server and got a lot of JSON back and you want to map it to your own class. You could do this (I need to state I never used this for loop in the app, but I adapted it from the second snippet):

let json = JSON(data: data)
var createdPosts = [Post]()

if let posts = json.array {			
	for var i = 0; i < posts.count; i++ {
		let post = try Post(json: posts[i], server: self.server)
		createdPosts += [post]
	}
}

Simple enough right? We got the results as a JSON array, and we are going through each element to create a Post with it. The details of a Post object are not important in this article.

If you have been programming for a while, particularly in Swift, you may notice that we can do the same thing, in a cleaner manner, using a for-in loop.

let json = JSON(data: data)
var createdPosts = [Post]()
            
if let posts = json.array {
	for post in posts {
		let post = try Post(json: post, server: self.server)
		createdPosts += [post]
	}
}

This will iterate through each element and then create a Post object with it, without needing to use the array index.

But this can be even better, by using the map higher order function. This function will iterate through each element of the array, do something with it, and return it. In this case, we are converting a raw JSON object to a Post object. That is, we are mapping JSON to Post.

let json = JSON(data: data)
let createdPosts = try json.array?.map({ (jsonPost) -> Post in
	return try Post(json: jsonPost, server: self.server)
})

The map higher order function takes a function. The function you pass as a parameter is the operation that will be done to each object of the JSON array. The map function itself will return a new array – In this case, a new array of Post objects.

This is much, much better and more functional than the approaches described above. The best part is you can still simplify it further without hurting readability:

let json = JSON(data: data)       
let createdPosts = try json.array?.map { return try Post(json: $0, server: self.server)}

Terrific! Something that took us over 5 lines of code before, now takes us two! The $0 is a placehorder for the value the function is giving you – In this case, the JSON Post. If the function had two parameters, you could use $0 and $1 – and so on.

Important Higher Order Functions

If I managed to show you how powerful higher order functions are, then there’s a few of them you should know about. I am not going to explain to you how they work, and I will leave them as an exercise for yourself. Keep in mind you can apply these functions to any collection that conforms to the CollectionType protocol, and as such you can create your own collection types that support these HOFs.

  • map: Map is the function we just saw, which allows you to to operate over the elements of a collection and then return a new array with the results.
  • filter: The filter function will take a function that returns a boolean. You will decide what objects should be part of the new array and which ones won’t. For example, if you have an array of numbers that go from 1 to 200 sequentially, you can filter the array to return a new one that contains only even numbers.
  • reduce: The reduce function will collapse a collection into something else. For example, suppose you have an array of numbers, and you wanted to add them up and return that result. You could achieve this using the reduce function..

There’s many other higher order functions to explore, like flatMap, but they are not as widely used so if you want to know more about them, check the Swift documentation.

When to use Loops

To end this discussion, let’s talk about when you may actually want to use loops. Like I said before, my intention is not to destroy the usage of loops altogether, but rather to decrease their usage as modern programming languages have better alternatives for many cases.

First, don’t use C-style for ;; loops. They are not needed, not at all, and they are going to be removed for Swift 3.0 anyway.

Second, in the case you need to iterate through a collection and you need the index in each operation (basically the only valid usage for for ;; loops I can think of), then you can use the enumerate() method:

for (index, value) in shoppingList.enumerate() {
    print("Item \(index + 1): \(value)")
}

(Code taken from the official Swift guide)

Third, even non-enumerated for-in loops have their uses. I use them to print quick debugging info:

for tag in tagsArray {
	print("tag is \(tag)")
}

Finally, the repeat-while and while loops. These loops can have their use cases, but DO NOT use them to iterate through collections. There’s simply no need to do that. To be honest I haven’t needed to write one of these loops in a long time. I would say their uses are limited, but you would use them for things like this:

while !coffee.empty {
	coffee.drink()
}

Use these loops when you need to do something as long as a condition is true. I repeat, do NOT use them for iterating through collections. Because while you can do that, semantically it doesn’t make any sense. You have all the alternatives for that above.

Conclusion

I welcome the removal of the C-style for ;; loop, because modern languages have better and more robust alternatives to do what you would do with them. We cannot stick to legacy conventions forever, and removing the for ;; loop is a great first step to get rid of them. This may not apply to all languages, but it definitely applies to Swift.

I encourage every developer to adopt the usage of functional paradigm philosophies when it makes sense, and to decrease the usage of loops throughout their code whenever possible.

The post Eliminating the C For Loop Is Not Bad for Swift. appeared first on Andy Ibanez.

]]>
https://www.andyibanez.com/why-eliminating-c-style-for-loops-is-not-a-bad-thing-for-swift/feed/ 4 417
Automatic App and WatchKit App Build Numbers with Xcode. https://www.andyibanez.com/automatic-app-and-watchkit-app-build-numbers-with-xcode/ https://www.andyibanez.com/automatic-app-and-watchkit-app-build-numbers-with-xcode/#respond Sun, 04 Oct 2015 01:54:27 +0000 https://www.andyibanez.com/?p=401 Build Numbers in Xcode The “Build Number”, also known as “Bundle Version” is a field of your info.plist file in your app project. This help helps you build and sort different builds of your app. This field is pretty much free from. You can set your build numbers as you want. I used to use […]

The post Automatic App and WatchKit App Build Numbers with Xcode. appeared first on Andy Ibanez.

]]>
Build Numbers in Xcode

The “Build Number”, also known as “Bundle Version” is a field of your info.plist file in your app project. This help helps you build and sort different builds of your app.

This field is pretty much free from. You can set your build numbers as you want. I used to use a YYMMDD format – Before releasing an app, I would set this field manually and then submit it to iTunes. This is why Mignori has funny bundle numbers: The first version was something like 131229, indicating I completed it and submitted it to iTunes on December 29, 2013. The third build number was 140309, indicating I submitted that version on April 9, 2014, and so on. What I like about doing things like this is that you know exactly when you finished a version and uploaded it to iTunes.

But, more often than not, the build number is either the number of times you built your project – That is, the number of times you clicked the “Play” button on Xcode, or the number of Commits on your Git repo. I eventually started doing the Git repo way, because sometimes I would forget to update the build number manually and it would be a little bit annoying to fix.

Specially with WatchKit apps now. Things are slightly more complicated, but nothing too extreme. Your main app’s Build Number and your WatchKit app’s Build Number have to match. If they don’t watch, Xcode will refuse to compile your project. So save yourself a little bit of trouble it’s easier to have this field update automatically instead of setting it twice.

You can use this bash script to set the build numbers to the number of commits on your git repo:

# Set the build number to the count of Git commits
buildNumber=$(git rev-list --count HEAD)
/usr/libexec/PlistBuddy -c "Set :CFBundleVersion $buildNumber" "${PROJECT_DIR}/${INFOPLIST_FILE}"

I graciously got this snippet from StackOverflow. But it was a long time ago, so unfortunately I cannot properly credit the author. If you know the author of this script, please let me know so I can update this post. Thanks!

To automatically update your build numbers to the number of git commits in your repo, add the script as following:

  1. Click on your project’s “blue print”.
  2. Select your main app target.
  3. Select “Build Phases” on the tab at the top.
  4. Click the + button, and select “New Run Script Phase”
  5. Copy and paste the script there.
  6. Repeat steps 5 – 6 for your WatchKit app (Select your WatchKit target first).

Xcode Build Numbers

(Click to enlarge).

Now, whenever you click the “Play” button on Xcode, the Build Number will be updated automatically.

If you prefer, you can adapt this to make your Build Number the number of times you physically built your app. The gist is to grab the current build number, add one, and then put it in the appropriate environment variable.

The post Automatic App and WatchKit App Build Numbers with Xcode. appeared first on Andy Ibanez.

]]>
https://www.andyibanez.com/automatic-app-and-watchkit-app-build-numbers-with-xcode/feed/ 0 401
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