Tuesday, March 21, 2017

CloudKit! Oh, Wait... I Guess It's Time To Enroll In The Apple Developer Program

This blog was originally supposed to be about CloudKit and using Apple's cloud services for use in your apps, but after snagging Ray Wenderlich's starter project from his site and launching it in Xcode I was met with this:



O dam.  I guess it's about time to fork up some do$h for a Paid Developer Account with Apple.  The day has finally come.  But what do I actually get for my 99 US Dollars?  Why do even have to pay for this thing at all?  Well, let's dig in.  For those of you that are on the cusp of making the plunge along with me, I hope this article will be helpful on your journey.

BETA RELEASES

The first perk of being a part of the Apple Developer Program is access to the Beta versions of all of their operating systems, including iOS, macOS, tvOS, and watchOS.  This ensures that as a developer you are always in the know about which features are going to be hitting consumers in the next software updates.  Apple also hosts beta releases of Xcode on their developer site, so you'll always have access to the most updated version of the powerful IDE for your Swifting.

KEY APPLE TECHNOLOGIES

There are a bunch of key Apple technologies that become to available to you after you enroll as well. Let's go over some of them.

  • CloudKit: Provides user authentication, database storage, and cloud assets to your program using Apple's iCloud service.  For free, it starts with 10GB Assets Storage, 100MB Database Storage, 2GB of Data Transfers, and allows 40 requests per second.  It even scales up as your user-base increases.
  • Game Center: Apple's social network for gaming.  Tracks high-scores, game invites, and online matchmaking.
  • Wallet: Integrate into your app to track gift cards, concert tickets, boarding passes, and more using PassKit.
  • Apple Pay: Simplifies the payment system of your app by allowing the user to utilize the cards they already have saved in their Apple Pay preferences and verifying their identity using Touch ID.
  • In-App Purchase: Using the StoreKit framework, you can offer premium content, virtual goods, and subscriptions to your user-base.
  • Maps: Gain access to Apple Maps integration, including custom annotations, highlighted regions, overlays, 3D views, and more.
  • Keychain Sharing: Allows the use of Apple Keychain to share user passwords between your apps.
  • App Groups: Lets you share containers between your apps and grants additional features for the apps to communicate with each other.
  • Data Protection: Encrypts the data you are storing locally on your users' devices for increased security.
  • HomeKit: Grants the ability to control Network-enabled devices with your app.  HomeKit compatible door locks, lightbulbs, thermostats, window shades, and more are already on the market.
  • HealthKit: Connects your app to Apple's Health app and lets you use the user data stored there to implement your own features.
  • Wireless Accessory Configuration: Enables your app to connect to and configure external Wi-Fi accessories.
  • Personal VPN: Create and manage custom VPN configurations.
  • Inter-App Audio: Export audio that can be used between other inter-app audio enabled apps.
  • Background Modes:  Define background functionality for your app while the user is doing other things.
  • Associated Domains: Associate your app with specific web domains to connect it to specific services.

APP TESTING

Another key benefit of the Apple Developer Program are the features it provides you for testing your products.  You are permitted to add up to 100 devices to your developer account from each product family (iPhone, iPad, Apple TV, etc.) to directly install your applications on for testing purposes.  This is a great way to give test builds to friends, family, testers, to get some feedback on your app.

A much more robust testing platform is TestFlight.  Think of TestFlight as an official beta release of your app that you can invite up to 2,000 participants to try by simply adding their email to the invite list.  The really cool thing about TestFlight is that it's an officially managed platform by Apple, meaning that once you've submitted your app and it's been approved Apple takes care of all additional device management.  Apple includes all necessary user instructions to get started directly in the invite email.  If you have a new build ready to go out, just let Apple know and all testers will get a notification informing them to download the new build.  Pretty sweet!


APP SUPPORT

Think of this like AppleCare for your apps.  As part of your membership in the Developer program, you are granted up to two sessions a year for code-level support by Apple Technical Support.  These sessions are designed to speed up your development process and get you to deployment as fast as possible.  Not a bad little perk!

DISTRIBUTION

At the end of the day it's all about that do$h, innit?  The single most important thing about the Apple Developer Program is that it lets you host your products on The App Store, accessible from the millions and millions of Macs, iPhones, iPods, Apple TVs, and Apple Watches out there in the wild.  It's important to know that Apple takes a 30% cut of all profits you make from your apps (hey, it could be worse), so prepare for a good chunk of change to be scraped off the top of each check that swings your way.

Specifically for macOS, Apple lets you circumvent the store by providing you with a Developer ID that you can use to register your apps with Apple.  This let's macOS users open your apps after downloading them without having to worry about any security warning or privacy concerns that Apple has built into the OS.  If you are interested in developing extensions for Safari, enrollment in the program also allows you to host your extensions in the Safari Extensions Browsers.

CONCLUSION

This whole article probably comes across as an ad for Apple's Developer services, but guess what?  If you have any hope of seeing your app running on someone's phone other than yours or a close friend's, you've got no choice but to pay up!  Seems aggressive, but that's the way Apple does business and will almost certainly continue to doing business.  At least Apple recognizes the need to continually attract new developers, so has added value to the program by offering the services listed here.  So from my perspective $99 a year is not all that unreasonable.  

Or maybe I've just been drinking the Apple Juice for way too long!

Friday, March 10, 2017

Parsing Mad XML To Snag Some Sweet, Sweet Board Game Data Using BGG's API

Check it.

API: Application Programming Interface
XML: Extensible Markup Language
BGG: Board Game Geek

Now that you're caught up on all of the initialisms in the title of this article, let's write a simple app that's lets us use some of that data.  But what should it do?  Well, I don't know about you doofuses, but I spend almost as much time trying to figure out which game in my collection I'd like to play next as I do playing them.  So here's what we're going to do: make our phones decide for us.  Let's do it.

Let's start with the User Interface.

Game Grabber?  Eh, that's cool.  I can live with that. That text field there is where we can plug in any BGG username to tell our app whose collection to choose the game from. The Game label toward the bottom is going to be what we use to display the name of the game that our phone decides we should play.  Don't worry, its going to stay hidden until the game is actually chosen.

Aight, lets jump into our View Controller and get coding.



Okay, we know we have to make our View Controller a delegate for our text field, but what in the heck is an XMLParserDelegate?  It's a Protocol we're adopting to become a delegate for an XML Parser, you dingus!  All will make sense soon.  Let's move on.



Okay, what do we have we here.  Outlets for our text field and label, an array of strings for the game titles we're going to snag from BGG, a blank string called currentTitle, and a bool keeping track of whether or not our XML document is currently being parsed.  Not bad, not bad.  Let's set up our viewDidLoad next.



Coolio.  Here we set our usernameField delegate to the View Controller, and set up a gesture recognizer for out view and give it an action called dismissKeyboard.  Still no sign of that XMLParserDelegate, though.  Hmmm, I'm sure it will come into play later.  For now let's take a look at dismissKeyboard in more detail.



Well, I don't know what I expected... it dismisses the on-screen keyboard.  Got it.  I wonder what's going on with that text field anyways?



Very nifty.  First thing we do when the user presses the return button and starts their search is check if the gameTitles array is empty.  This is to make sure that if we've already got some game data from a previous API request we don't have to make another one.  We can just take a game out of the titles we already nabbed.

After that is where we are ensuring that randomGameLabel is staying hidden until we put a game title in it.  We also set documentBeingParsed to true because we are about to start digging into some XML!  There are a few checks to make sure of first though: first that the usernameField actually has some text in it, and secondly that there are no spaces in it.  You cannot have spaces in your BGG username!  Unless I missed a memo or something, I probably did.

Whatever, lets get on the net.



There you go, hitting that API like a sledgehammer.  We're snagging the username from our text field and passing that in as part of the URL, but what's that little ?own=1 doohickey?  That's a parameter we're passing in called own with a value of 1.  What that does is ensure that the game our phone spits back out to us is a game we actually own.  The whole point here is to be able to actually play the game that shows up on our screen, remember?  I supposed you could pass in a value of 0 if you wanted your phone to tell you what game you should bur next or something, but I digress.



Pretty standard session and dataTask stuff.  There are two more guards in there making sure we're actually getting a response and usable data back.  The only real standout is that sick switch statement right in the middle of the task.  Finally, our XMLParserDelegate is coming into play.  Notice, however, that this is only the case if the statusCode we get back from BGG is exactly 200, which indicates the data is ready for our perusal.  Otherwise, we break out of the closure.

The funny thing about the BGG API is that it always fails the first time you make a new request.  This is because their servers work on a queuing system.  Think of your first request like knocking on their door, and it isn't until you make another, slightly more polite request, that you're actually let inside.  We'll handle this in a little more detail later, but for now let's look more deeply into our XML Parser.

First things first, we create an XMLParser called xmlParser and we tell it that it's going to be chowing down on the response data we wrestled out of the API.  Then we assign the View Controller as its delegate, and lastly we tell it to GO.  TO.  TOWN.  Let's look into more detail at what our parser is actually doing.



Our first parser delegate function is didStartElement.  This bad simply tells the currentTitle string we declared way up above to empty out its contents every single time it hits a new Element in our XML file.  An element in XML looks like this:

<name>Agricola</name>

So this function fires off every time it hits that first set of brackets.  The reason we want this is to happen is to make sure we can snag every element's String individually and keep our data nice and tidy.



The foundCharacters function continually fires off as it makes its way down the XML file.  Get it now?  That's why we have to make sure at the start of each new element we're clearing our currentTitle string.  Otherwise this function will just keep adding more and more characters to the same darn string!



At the END of each element we check to see if it is a "name" element, if it is we set the currentTitle string equal to a gameTitle variable and append it to our array of gameTitles.  Now you should understand the true nature of XML Parsing!



Lastly, we have this parserDidEndDocument function that switches the documentBeingParsed bool back to false.  This has adds no functionality whatsoever to our app, but I used it for debugging and forgot to take it out.  Sue me!

Okay, we are almost done.  Our dataTask is finished, now we just have to do something with all that data we just got.



If at this point our gameTitles array is still zero, we throw a message up onto the screen to let the user know to try again in a couple of seconds (a bad search will have been caught by an earlier guard statement, so this is in case BGG's API stubbornly decides not to give us any data like it's been known to do).  Otherwise, we got some game titles now to play with!  Heck yes!  Let's randomly select one and spit it up to the screen.

There is one more thing we have to watch out for though.  What if you want to switch between multiple BGG collections by searching different users back to back?  We need an elegant way to flush out the gameTitles array and get it prepared for some new ones.  We didn't include that in the actual search function because we wanted to avoid calling the API over and over again for the same user's collection.  How about this:



There we go.  If the user decides to change the text in the text field, we'll flush the array out then.

That's it, buddy!  Let's see this bad boy in action.  Bear in mind, in the following gif I'm doing searches I've already done several times before which is why I'm not getting hit with the message saying to try again.  Typically, the first request will tell you to cool off and wait for a bit, but that's ok I still love you BGG :)




I love it.  I love everything.  I love board games.  I love Swift.

Fare thee well, bozos.

Wednesday, March 8, 2017

Using Functional Recursion To Create A Self-Playing Version Of 6 Nimmt!

This experiment seeks to fly in the face of the single responsibility principle.  Not because I should, but because I can.  Call me Icarus, if you wish, but my wings are molded from stronger material than wax.  ANYWAYS, nothing in this article is indicative of good coding practices, or is even particularly elegant or efficient in its design.  I do think it gives some interesting perspective on the value recursive functions bring to the programmatic table however, so let's use that as my defense mechanism against the pundits.

What you are about to see is the entire game of 6 Nimmt!, a wonderfully German card game designed by Wolfgang Kramer, defined within a single function.  Set up, tear down, and all of the mechanics will be entirely contained within one function.  Furthermore, unlike my previous posts this one actually runs!  But first we're gonna need some classes and structs and what have you to get us started.

Let's start with the most rudimentary element of any card game, the cards:


Here we have a struct with three properties: number, bulls, and playedBy.  Number is the numeric value of the card, bulls are how many points the card is worth (points are BAD in this game!), and playedBy will help us keep track of which player uses the card in the game.

Next we need a deck to store these cards:












Pretty simple.  An array to store our cards and a function to generate our deck and shuffle it up.  Each card gets assigned a value between 1 and 104, and playedBy is nil for now.  Bulls are a computed property so we don't have to worry about that one at all.

Now we need some players to deal these cards to!


Our Player class has three properties: name, hand, and score.  All pretty self-explanatory.  We also got a neat little function for getting a Player their starting hand in there.

Lastly, we need a class for our game itself.

An array of Players, an array of Card arrays (representing the table where we're playing the game), a roundCounter to keep track of which round of the game we're in, and a simple bool to make sure we don't keep repeatedly setting up a game we're already playing.

Great, we're good to go.  If you don't know how to play 6 Nimmt! already the following function will make very little sense to you.  Too bad, brush up on your European card game history, bro.


Here's what we start with.  A pitiful, disgusting, empty, useless function.  Let's cram it with some functionality.


Our function starts innocently enough with a conditional checking if the game needs to be set up.  If it does, it creates a deck, five players, deals them ten cards each, and starts off each of the four card rows that our game is actually played on.  It ends by telling our game that it doesn't need to be set up anymore, remember we're going to be going through this game over and over again so we don't want to set up a new game every time a new round begins.

Take note that Deck.createDeck() and player.createHand() are the only external function calls in the entire game.  I decided to include those as methods because otherwise this screen shot looked like complete shit.


Next we create an empty array for all the cards players will be playing this round.  Note that it's totally safe to start it as empty because we want a brand new array for each round of the game we go through.  After that each player randomly chooses a card from their hand to play (proper A.I. is for another article, dude), then the cards are sorted from lowest to highest.  This is done because in 6 Nimmt! players take their turns in ascending card value order.

Now, we must find out which row each player's card will be placed in.


We start by calculating the difference between the player's card and the last card in each row.  If this creates a negative number we are setting the difference to a number higher than the highest possible value difference between cards to make the our conditionals much easier to implement.  

Now, to understand the following game logic, keep in mind the following rules of 6 Nimmt!
  1. A player's card value must be higher than the value of the rightmost card in the row they are placing it.
  2. If their card value is higher than the value of the rightmost card in multiple rows they must place it next to the card which has the least difference.
  3. If the row a card must go into already has five cards in it, the player scores all the Bulls in that row and replaces the row with their card.  Remember, players don't want Bulls!
  4. If their card value is lower than the rightmost card values in all four rows they can choose which row they want to score and replace with their card.
Cool, so we already have our player card and rightmost card value differences so lets start playing the game!


This is what happens if a player's card value is lower than all four rows rightmost card value.  The  player scores the row worth the least amount of Bulls and replaces it with their card.





These four conditional statements check to see which row the player's card should go in, then to see if that row already has five cards in it.  If it does, the player must score the row and replace it with their card.  If it doesn't, the player simply adds their card to the row. 

That's actually the entire game!  We're already done.  Not too crazy, right?  The only thing left to do is check to see who won:


This block of code only triggers if we just completed the tenth and final round of the game.  It sorts the players by their scores, prints some stuff to the console, and determines who the winner is.  After that it wipes clean all of our structs and class properties to prepare for another game.  There's only one small thing left to do, and that's make this bad boy recursive!


BAM, son.  We are done.  I could've made the game automatically play another game after finishing the clean up process, but infinite recursion is a terrifying thing and not to be trifled with.  Therefore the only thing left to do is call this functions as many times as we can handle and play 6 Nimmt! over and over and over again.  


Wow, what a journey.  What a world we live in where such great people can meet and bond over dozens of games of 6 Nimmt! each lasting about a second.  I am truly grateful that I was able to make such a meeting of minds happen inside magical the world of Swift.

Until next time!