Learn about sharing data between the app and the Photo Extension.
The post Video Tutorial: iOS App Extensions Part 4: Photo Extensions: Shared Settings appeared first on Ray Wenderlich.
Learn about sharing data between the app and the Photo Extension.
The post Video Tutorial: iOS App Extensions Part 4: Photo Extensions: Shared Settings appeared first on Ray Wenderlich.
Valentines Day is behind us but I’m still falling for all these sweet community apps!
The community has been hard at work pumping out new games and apps built with our tutorials. I’ve downloaded a ton of them and I’ve picked a few to show you.
I receive more than an app a day from readers like you busy working on games. I don’t have time to write about every app that hits my inbox, but hopefully the few I show you gets you excited for our community and inspires you to finish your app. Don’t miss the honorable mentions, plenty more inspiration waiting for you.
This month we have:
Keep reading to see the the latest apps released by raywenderlich.com readers like you!
Are you a know it all? More importantly are you a WikiKnowItAll? WOKWiki is made for you!
WOKWiki takes all of Wikipedia and turns it into a multiple choice quiz! Seriously, how awesome is that? You can play against your friends or the community. Race for time or go head to head on accuracy.
Any subject you can think of is covered. Historical Figures, Sports, Technology, China, Logic. Literally any random fact on Wikipedia is up for grabs. You can use tags to limit the questions you’ll receive unless you really are a know it all. ;]
Invisible Pix is the coloring book for the iPad.
Its got all the basic stuff you’d expect like pinch to zoom, adjustable brushes, auto saving, and more. You can color free hand or use the Magic Pen to stay perfectly in the lines as you color. Tilting the device reveals an light outline as your color. When you’re done of course you can share to Facebook and Twitter or save to your photos directly.
There are tons of themes from Animals to Dragons to Mandalas to Pirates and much more. They’re adding more series over time including holiday specials. Most series only cost a couple dollars and the entire experience is 100% ad free.
Do you travel so much you’ve forgotten where you’ve been? Then The Travelist is an app for you.
The Travelist will help you keep a log of all the countries and states you’ve visited. On the map of the world simply select where you’ve been to start compiling a list. You can mark where you’ve lived, visited, or would like to visit. As you enter your conquests you can see what percentage of the world or country you’ve seen. See a checklist of where you’d like to go.
You can also rank yourself against other travelers to see who’s seen the most of the world. Share your map, rank, and other travel stats with friends on Twitter, Facebook, iMessage, or even print it out.
Mr. Astroman is on a mission and needs your help. He’s flying into unexplored territory high above the planet’s surface.
The objective is simple: fly as high as you can while avoiding obstacles like falling spikes and force fields. Along the way you’ll encounter powerups and upgrades. Each level unlocks more equipment like shields and bullets. Use the bullets to blast holes in force fields and walls at just the right time.
See how high you can climb on the global GameCenter leaderboards or challenge your friends to beat your high scores. Collect achievements for your valor above the earth.
Its gonna take more than just a little tilting to make it at Roller Ball.
Roller Ball is based on the old school classic and its a ton of fun. There are a handful of balls rolling around the screen and a matching slot for each ball. Your goal is to make sure each ball finds its home. But its going to take precision and planning to make it. Once a ball finds its slot its not over, other balls can still knock it out as they roll around the screen.
There are nine randomly generated levels with 3 levels of difficulty. So take your time and work your way up to mastery. Maybe one day you’ll be as good as me. ;]
ThumberJack is more than a catchy puntastic name. Its also a super addicting yet incredibly simple game.
To become the best ThumberJack you can be you’ll need precision, agility, and a keen sense of hand eye coordination. Or maybe just your thumb and some luck.
An endless log is flowing down the screen and you’ve got to cut it all! Keep your thumb in the touch zone to avoid getting cut and keep it on the log to keep the cut going. Get points each time your clear a log joint. See if you can make it to the top of the GameCenter leaderboards and be the best ThumberJack in the land!
Do you love solved puzzles and putting that brain of yours to work? Good, time to download QuoteBreaker!
QuoteBreaker is a two part game. You start with a Nonogram puzzle. A grid with number hints for each row and column. You’ll fill a square for each number and leave the others blank. Each row’s rules must be satisfied. Its like sudoku on steroids. The second part is a Cryptogram built from the previous Nonogram. You’ll decipher a message using numbers as your guide.
QuoteBreaker is very challenging and extremely addicting. Once I started a puzzle I couldn’t put it down.
Have you ever wanted to just watch Twitter in Realtime? Stream is here to help!
Stream lets you set up watch lists of interests from #hashtags to @mentions to keywords. You get three for free and then pay $1.99 for unlimited interests. Everything on your watchlist ends up in your stream. Your stream does exactly what you think. Tweets slide in from the top in real time. You can swipe left to like or retweet.
You can also check some sweet charts to see what is making up your stream. The charts show each of your interests and measure the count of tweets. You can use this for some cool ideas like streaming two competing hashtags to see which is more popular. If you’re a Twitter addict, add this to your list of must have apps.
Every month I get way more submissions than I have time to write about. I download every app to try them out, but only have time to review a select few. I love seeing our readers through the apps submitted to me every month.
It’s not a popularity contest or even a favorite picking contest — I just try to share a snapshot of the community through your submissions. Please take a moment and try these other fantastic apps from readers like you.
Bouncy Buzz Blastoff
Cookie Shower
Joe Square
Searchr
WordHue
Chop’n’Bop
Trip Tracker Pro
8Calc
Analog Synth X
Desk Pad
cLrS (cOlOrs)
Myloco
cantoneseGame
plusclock HSB
Spiral Painter – Colorful Aura of Your Name
Each month, I really enjoy seeing what our community of readers comes up with. The apps you build are the reason we keep writing tutorials. Make sure you tell me about your next one, submit here.
If you saw an app your liked, hop to the App Store and leave a review! A good review always makes a dev’s day. And make sure you tell them you’re from raywenderlich.com; this is a community of makers.
If you’ve never made an app, this is the month! Check out our free tutorials to become an iOS star. What are you waiting for – I want to see your app next month.
The post Readers’ App Reviews – February 2016 appeared first on Ray Wenderlich.
This is just a quick heads up that we’ll be having some scheduled maintenance on raywenderlich.com tomorrow, Sunday Feb 28, around 9AM EST.
Our site is going to be moving to a new and modern forum, and a new authentication provider. Because of this, you’ll need to reset your password next time you log in.
I’ll share more about all the cool stuff in the new upgrade tomorrow. If you have any questions or issues, please contact support. Talk to you soon!
The post Scheduled Maintenance Tomorrow appeared first on Ray Wenderlich.
Today, we are pleased to announce two upgrades to raywenderlich.com:
We’re really excited about these changes, because the new cloud-based architecture should result in a snappier site and lay the foundation for some cool new features on the site in the months to come.
If you have an account on raywenderlich.com, you’ll need to reset your password. Here’s how:
That’s it! After that, you should have access to all your loot as usual – and feel free to play around with the new forums.
If you have any problems or questions, please contact us and we’ll lend you a hand.
Thanks all – and stay tuned for many more site improvements in the months ahead! :]
The post Announcing New Forums and Authentication System! appeared first on Ray Wenderlich.
If you’re an iOS developer, you should absolutely attend at least one iOS conference a year.
This is important so that you can keep your skills up-to-date, get a fresh burst of energy for your job, and to connect with the rest of the iOS community.
Luckily, there are tons of great iOS conferences to choose from. But which should you choose?
That’s what this post is about – a list of the the Top 10 iOS Conferences in 2016! To come up with this list, I surveyed over 300 developers, reached out to specific individuals to get their input, and merged this with my own opinions.
Please remember that this article is just my own personal opinion based on what I’ve heard from team members and the community. If I miss out on a conference you really like, please add your comments to this post, so everyone else can learn about it too.
Without further ado, let’s dive in to the top 10 iOS conferences of 2016!
By far, the #1 choice among developers is Apple’s official conference – WWDC.
The content at WWDC is great, but to me that’s not the selling point – you can always watch the videos at home. What’s great about WWDC is two things:
Getting a WWDC ticket isn’t easy – in recent years there has been a lottery system, with a smaller and smaller chance of getting a “golden ticket.”
However, even if you don’t get a ticket, you should consider coming to San Francisco anyway to socialize, and to attend an independent event that happens the same week – AltConf, which has a lot of great talks by members of the iOS community.
“WWDC – I love it. Highlight of my year – so much so that I’ve already booked flights, and plan to attend AltWWDC if I don’t get a ticket. San Francisco buzzes during one of the best weather-weeks of the year thanks to the influx of tech-visitors. Don’t miss The Bash on Thursday, the Design Review sessions and the lunchtime sessions – which have been increasingly impressive the last few years.” –Gemma Barlow
“WWDC is the conference that transcends all other iOS conferences. Developers from around the world make their pilgrimage to the the Moscone Center to not only hear what new devices, frameworks, and other Apple tech they’ll get this year, but to get rare face time with Apple designers and engineers. The conference is only half of the event, as during WWDC week there are countless meetups and parties for both those with and without tickets.” –Erik Kerber
“For the first developer conference I’ve attended, WWDC smashed all my expectations and turned me into a blob of hype and excitement. Apple engineers in the labs are both friendly and helpful so I would highly recommend you go talk to them if you get a chance. AltConf, which takes place across the street from WWDC, featured some extremely high quality talks from several well known individuals. If you don’t win the WWDC lottery, you can attend AltConf and still catch some Apple engineers in coffee shops all around the city.” –Jack Wu
“Dub-dub is amazing, exhausting, expensive, and a wonderful way to meet people. . . .However if you’re financially constrained, it may not be worth the price. The ticket alone is expensive, as are getting to and around SF. Hotels in San Francisco can be stupid expensive, kind of sketchtastic, or even both. If you’re watching your budget, it’s probably a lot more cost-efficient to buy an Apple TV, take the week off work, and watch videos as Apple puts them up to stream. You lose a lot of opportunities to talk to other people in person, but depending on your financial situation or personality type, that sacrifice may well be worth the several thousand dollars you get to hang on to instead of spending it on WWDC.” –Ellen Shapiro
Other than WWDC, 360iDev is the oldest – and biggest – iOS conference on this list.
I’ve been to 360iDev many times over the years, and always have a blast. John Wilker does a great job organizing the event, with a nice mix of technical, design, and non-technical talks – it has a little bit of everything for everyone.
“360iDev is the biggest independent iOS developer conference in the United States. If WWDC and AltConf are too spendy for you but you want a chance to reconnect with your colleagues, this is your next best bet. There are always a large variety of different talk topics. They accept a lot of first time speakers, so it’s always possible to discover someone you haven’t seen before. Plus, it’s the last remaining place for you to be able to participate in Stump the Experts.” –Janie Clayton
“A fantastic conference that mixes technical talks with discussions on working as an indie. The conference is located in downtown Denver, and the hosts work hard to integrate with the local area. The workshops and keynotes are definitely worth a mention, since they alone are worth attending for. The organizers have done a huge amount for the community, and really know how to put on a fantastic conference.” –Sam Davies
“360iDev is the biggest conference I go to every year. Its packed with indies and those that make up the community I love being a part of. Every year its a great chance to see old friends and make new ones while enjoying the Apple Community at large.” –Ryan Poolos
Full disclosure: our team runs RWDevCon, so we’re not impartial about the matter. However, the conference did very well on the poll, is quite different from the other options, and has sold out for two years straight, so I felt its spot on the list is warranted.
RWDevCon is different than the other iOS conferences, because instead of just listening to a talk, you participate.
RWDevCon has three simultaneous tracks of hands-on programming tutorials – all highly coordinated as a team, with tech editors and multiple rounds of practice. This way, you know the end result is the high quality style you know and love from our site!
“RWDevCon offers a unique mix of hands on tutorial style sessions and insightful ‘inspiration’ talks that really do inspire. There is something for everyone, with three tracks targeting different levels of expertise in a wide range of iOS topics that really stick thanks to the tutorial format of the talks. I was able to wet my feet with game development while also exploring advanced topics relevant to my work as an enterprise developer. Due to the more intimate setting, smaller size, and amazing inspiration talks, I made a lot of great friends and truly came home feeling inspired and excited to employ what I’d learned.” –Jeff Rames
“As a speaker, I saw firsthand how much preparation went into RWDevCon presentations: A whole, whole lot. What I was really impressed by as an attendee was how much that came through in the finished product – the tutorial sessions were thorough, every effort was made to make them easy for attendees to follow, and I was able to work through problems and ask questions of the speaker during the lab. The opportunity to meet many people I’d been following through the site for years in person was also really great.” –Ellen Shapiro
“This was a great way of actually learning a lot about a whole host of iOS-related topics, arranged into Beginner, Intermediate and Advanced tracks. In each tutorial, we worked through a topic and coded working examples along with the presenter – making it a really great way to learn. All the attendees stayed at or near the conference hotel so we generally stayed together over the 2 days. Lunch was provided on both days and the party held at the end of the first day (with food and drinks provided) was a fantastic way of getting to know everyone. Worth every penny.” –Adrian Strahan
CocoaLove is another unique conference. Rather than focusing on technical talks, CocoaLove focuses on the people behind the tech.
Specifically, this means a lot of inspiration-style non-technical talks, along with some unique activities designed to help conference attendees get to know each other. If you’re looking for something a little bit different, consider giving CocoaLove a shot!
“CocoaLove is held in Philadelphia, and I *love* attending because there’s plenty of time to meet the speakers and chat in-between sessions. Talks are high quality, and there’s extra-curricular activities involved – last time a fabulous brewery tour and night-ride on a double-decker bus. The year before it was cheese steaks, a carousel and mini-golf. You’ll meet some fab indie developers here.” –Gemma Barlow
“I have attended a lot of iOS conferences over the last three years. My absolute favorite conference was CocoaLove. The CocoaLove team went above and beyond to create a unique experience and showcase the venue in downtown Philadelphia. CocoaLove focuses on the iOS community as a whole more than the technology we use in our day to day lives. This created a wonderful atmosphere of friendship and acceptance where you could meet a lot of people you didn’t know initially and make friends. By focusing on the people CocoaLove created one of the most unique conference experiences I have ever had.” –Janie Clayton
“You can get the latest information about developing for iOS from books, online, and from WWDC Videos. CocoaLove is a completely different experience. The presenters are top developers but they’re talking about issues we all face without presenting any code walkthroughs. It’s a friendly, intimate conference in a great setting.” –Daniel Steinberg
The big selling point behind CocoaConf is they run multiple events each year, at cities all around the US – so chances are you can find one close to you. Be sure to check it out if one is nearby!
“I got my start in conference speaking at CocoaConf. The CocoaConf Tour has given me a great opportunity to travel around the US meeting a lot of awesome developers. I would not have a career if the Kleins were not doing this conference. They made it accessible for people living in places that weren’t the Bay Area to be able to afford to attend a conference. They bring top notch speakers to you so that you don’t have to travel all the way out to the West Coast to get a chance to meet awesome people like Daniel Steinberg and Jaimee Newberry. These conferences feel like family and it’s a great way to get some quality time with a prominent member of the programming community.” –Janie Clayton
“CocoaConf is a great affordable iOS and Mac circuit conference managed by Dave Klein and his family. This conference comes to at least eight locations throughout the year and is something every Mac and iOS developer should try to attend. Year to year some of the presenters haven’t changed but the content is fresh and the people are fun to be with.” –Aaron Douglas
“CocoaConf is a great traveling conference which allows you to learn a lot without having to travel too far from home. It’s a great way to meet other devs from your area and your region as well. I enjoyed attending for the first time in 2014 so much that I asked if I could put a talk together. Two years later, I’ve now spoken at five CocoaConfs and am about to speak at my sixth.” –Ellen Shapiro
iOSDevUK is a conference located in a university in Wales. It is extremely technical, and its remote location encourages attendees to bond and make good friendships. A raywenderlich.com team favorite that is definitely worth checking out! :]
“A quirky conference at the edge of the world – getting there is all part of the experience of iOSDevUK. The talks are all very technically focussed and well selected. It includes some peripheral workshops which are definitely worth attending. Highly recommended if you want to catch up with the latest developments in iOS and meet up with other devs from around the UK and the rest of the world.” –Sam Davies
“iOSDevUK is run on a university campus in the gorgeous, sunny town of Aberystwyth in Wales, a couple of weeks before the students return. The attendees stay together in the halls of residence, and eat together in the university’s canteen, which really promotes interaction and a sense of community. The sessions and hands-on workshops are always of the highest quality, and cover an eclectic mix of topics, so you’re guaranteed to learn something regardless of your experience. Chris and Neil do a sterling job organising things, always going above and beyond to make sure everything goes off without a hitch!” –Mic Pringle
“Held over 4 days at Aberystwyth University (which provided student accommodation to keep down the cost), this conference covered a wide range of topics and ideas from a varied group of presenters. Two tracks, roughly split into technical and more generic industry best-practice ensured that there was always something interesting to learn.” –Adrian Strahan
NSNorth is Canada’s premier iOS developer and designer conference, with a mix of both technical and non-technical talks. Attendance is usually kept small for an intimate feel.
“NSNorth is a great conference for the inhouse and indie developers. I’ve attended the conference a couple of as it has been just short hop up the road around Ottawa. This year it’s moving to Toronto and know there’s a ton of interest for devs around the GTA and NY state. There have been some great talks on promoting your apps, finding a niche, and finding your muse. It’s helped me shape my business and target my audience. I’m really looking forward to attending this year. It’s definitely on my top 3 iOS conference list. I wouldn’t miss it.” –Tim Mitra
“NSNorth is one of the best conferences I have ever been to. You are likely to meet every single attendee, including the presenters, when you’re there. I’ve personally made some great friends in our community every time I attend. If top-notch speakers weren’t enough, the organizers usually plan some wonderful social events like drinks in a dinosaur museum or ‘find-the-iBeacon’.” –Ryan Nystrom
NSSpain is a single-track conference in northern Spain with a strong focus on having fun and meeting fellow attendees.
“A thoroughly enjoyable conference with top-quality technical talks. The highlight is very much the region, food and opportunities for catching up with fellow iOS devs in a truly beautiful part of the world.” –Sam Davies
“NSSpain, #Pragma Conf and UIKonf represent a trend of european conferences that I pay extra special attention to. Aside from being really awesome locations, the organizers of these conferences are willing to try a lot of new things and still provide some solid technical meat. I’ve use these three conferences as places to meet other OSS contributors for whom it can be hard to find time for face to face chat otherwise. Being able to attend any of these is such a pleasure, and I aim to attend at least one each year for as long as they keep them running!” –Orta Therox
“NSSpain is easily the best event in Europe. A multi-day single-track conference in the amazing Spanish country side in the area of Rioja where the famous red wine comes from. The atmosphere is relaxed and the most famous tapas street in the world is at a convenient short walk from everywhere. A lot of extra curriculum activities and a lot of care guarantee everyone has good time. You will leave with more friends than you come with.” –Marin Todorov
#Pragma Conference is a conference in Italy that has both technical and design tracks, and covers both iOS and OS X development. Another one I’ve heard great things about!
“I really enjoyed Pragma Mark. I both gave a presentation and held a workshop, so I got to meet a tonne of people. Pragma Mark was a great relaxed environment. Being held in the heart of the beautiful Milan doesn’t hurt, either. It was like an Italian version of UIKonf.” –Ash Furrow
“Pragma Conference is one of the European events with the best content. Amazing workshops and stellar speakers attract audience from around the globe. The event is a fantastic opportunity to network, enjoy great food and wine, and visit Italy. The 2016 edition will take place in Verona where you can visit Julietta’s backyard where the famous scene on the balcony takes place.” –Marin Todorov
UIKonf is a single-track conference in Berlin with talks on iOS development, mobile design, and business.
This year, they have some free social events on the day before the conference, and they’re going to use them to raise donations for the refugees of the Syrian war.
“UIKonf is one of my favorite conferences, period. Drawing a crowd from all over Europe, you’ll hear talks from running an Indie business to Functional View Controllers to empathic Teaching and everything in between. You can really tell that the people behind the scenes put their heart and soul into it and it’s the best possible excuse to make a trip to Berlin.” –Robb Böhnke
“UIKonf is a hidden gem of iOS conferences: it’s the best conference to actually meet and talk to other developers from around the world, and the talks focus on quality and variety rather than the list of usual suspects when it comes to presenters.” –Daniel Eggert
“I spoke at UIKonf last year and I was thrilled because I knew it would be an extremely technical audience and I would be able to deliver a genuinely challenging talk about World Modeling without sparing the mathematics. If you’re one of the people constantly complaining that these other conferences are not technical enough, this is the one for you. Check out their talks on YouTube. They also have an amazing community-driven paper selection process everyone else should seek to emulate.” –Mike Lee
As I mentioned, it was very difficult (and subjective) to put together the top 10 list this year as there are so many great conferences. I felt it would be amiss if I didn’t also include a few honorable mentions.
If you’re unsure which to choose, here’s my advice – in a handy flowchart!
Again, note that this article is my personal opinion only and is based on what I’ve heard from team and community members who have attended these conferences.
Huge thank you to all who took the time to send me their thoughts and quotes about the conferences you attended for this post – I really appreciate it! :]
We’d love to hear your opinion too! Please let us know about your experiences at any iOS conferences you attended in 2016 – it will be really helpful for those trying to decide which to attend.
We hope to see you at some iOS conferences this year!
The post Top 10 iOS Conferences in 2016 appeared first on Ray Wenderlich.
View previous video: Photo Extensions: Shared Settings
The post Video Tutorial: iOS App Extensions Part 5: Today Extensions: Getting Started appeared first on Ray Wenderlich.
Alamofire is a Swift-based HTTP networking library for iOS and Mac OS X. It provides an elegant interface on top of Apple’s Foundation networking stack that simplifies a number of common networking tasks.
Alamofire provides chainable response/request methods, JSON parameter and response serialization, authentication, and many other features. In this Alamofire tutorial, you’ll use Alamofire to perform basic networking tasks like uploading files and requesting data from a third-party RESTful API.
Alamofire’s elegance comes from the fact it was written from the ground up in Swift and does not inherit anything from its Objective-C counterpart, AFNetworking.
You should have a conceptual understanding of HTTP networking and some exposure to Apple’s networking classes such as NSURLSession
and NSURLConnection
.
While Alamofire does obscure some implementation details, it’s good to have some background knowledge if you ever need to troubleshoot your network requests. You’ll also need CocoaPods installed to pull Alamofire into the tutorial project.
Download the starter project here.
The app for this Alamofire tutorial is named PhotoTagger; when completed, it will let you select an image from your library (or camera if you’re running on an actual device) and upload the image to a third-party service which will perform some image recognition tasks to come up with a list of tags and primary colors for the image:
Build and run the project in Xcode and you’ll see the following:
Click Select Photo and choose a photo. The background image will be replaced with the image you chose.
Open Main.storyboard and you’ll see the additional screens for displaying tags and colors have been added for you. All that remains is to upload the image and fetch the tags and colors.
Imagga is an image recognition Platform-as-a-Service that provides image tagging APIs for developers and businesses to build scalable, image-intensive cloud apps. You can play around with a demo of their auto-tagging service here.
You’ll need to create a free developer account with Imagga for this Alamofire tutorial. Imagga requires an authorization header in each HTTP request so only people with an account can use their services. Go to https://imagga.com/auth/signup/hacker and fill out the form. After you create your account, check out the dashboard:
Listed down in the Authorization section is a secret token that you’ll use later. This information needs to be included with every HTTP request as a header.
Note: Make sure you copy the whole secret token, be sure to scroll over to the right and verify you copied everything.
You’ll be using Imagga’s content endpoint to upload the photos, tagging endpoint for the image recognition and colors endpoint for color identification. You can read all about the Imagga API at http://docs.imagga.com.
Create a file named Podfile in the main directory of the project with the following contents:
platform :ios, '9.0' inhibit_all_warnings! use_frameworks! target 'PhotoTagger' do pod 'Alamofire', '~> 3.1.2' end |
Next, install the CocoaPods dependencies with pod install
. If you don’t have CocoaPods installed on your machine, check out the How to Use CocoaPods with Swift tutorial for more information.
Close the project and open the newly created PhotoTagger.xcworkspace. Build and run your project – you shouldn’t notice any visual changes in the running app. That’s good – your next task is to add some HTTP calls to a RESTful service to retrieve some JSON.
If you’re coming to this Alamofire tutorial with very little experience in using third-party services over the Internet, you might be wondering what all those acronyms mean! :]
HTTP is the application protocol, or set of rules, that web sites use to transfer data from the web server to your screen. You’ve seen HTTP (or HTTPS) listed in the front of every URL you type into a web browser. You might have heard of other application protocols, such as FTP, Telnet, and SSH. HTTP defines several request methods, or verbs, that the client (your web browser or app) use to indicate the desired action:
REST, or REpresentational State Transfer, is a set of rules for designing consistent, easy-to-use and maintainable web APIs. REST has several architecture rules that enforce things such as not persisting states across requests, making requests cacheable, and providing uniform interfaces. This makes it easy for app developers like you to integrate the API into your app — without needing to track the state of data across requests.
JSON stands for JavaScript Object Notation; it provides a straightforward, human-readable and portable mechanism for transporting data between two systems. JSON has a limited number of data types: string, boolean, array, object/dictionary, null and number; there’s no distinction between integers and decimals. Apple supplies the NSJSONSerialization
class to help convert your objects in memory to JSON and vice-versa.
The combination of HTTP, REST and JSON make up a good portion of the web services available to you as a developer. Trying to understand how every little piece works can be overwhelming. Libraries like Alamofire can help reduce the complexity of working with these services — and get you up and running faster than you could without its help.
Why do you need Alamofire at all? Apple already provides NSURLSession
and other classes for downloading content via HTTP, so why complicate things with another third party library?
The short answer is that Alamofire is based on NSURLSession
, but it frees you from writing boilerplate code which makes writing networking code much easier. You can access data on the Internet with very little effort, and your code will be much cleaner and easier to read.
There are several major functions available with Alamofire:
.upload
: Upload files with multipart, stream, file or data methods..download
: Download files or resume a download that was already in progress..request
: Every other HTTP request not associated with file transfers.These Alamofire functions are scoped to the module, not to a class or struct. There are underlying pieces to Alamofire that are classes and structs, like Manager
, Request
, and Response
; however, you don’t need to fully understand the entire structure of Alamofire to start using it.
Here’s an example of the same networking operation with both Apple’s NSURLSession
and Alamofire’s request
function:
// With NSURLSession public func fetchAllRooms(completion: ([RemoteRoom]?) -> Void) { let url = NSURL(string: "http://localhost:5984/rooms/_all_docs?include_docs=true")! let urlRequest = NSMutableURLRequest( URL: url, cachePolicy: .ReloadIgnoringLocalAndRemoteCacheData, timeoutInterval: 10.0 * 1000) urlRequest.HTTPMethod = "GET" urlRequest.addValue("application/json", forHTTPHeaderField: "Accept") let task = urlSession.dataTaskWithRequest(urlRequest) { (data, response, error) -> Void in guard error == nil else { print("Error while fetching remote rooms: \(error)") completion(nil) return } guard let json = try? NSJSONSerialization.JSONObjectWithData(data!, options: []) as? [String: AnyObject] else { print("Nil data received from fetchAllRooms service") completion(nil) return } guard let rows = json["rows"] as? [[String: AnyObject]] { print("Malformed data received from fetchAllRooms service") completion(nil) return } var rooms = [RemoteRoom]() for roomDict in rows { rooms.append(RemoteRoom(jsonData: roomDict)) } completion(rooms) } task.resume() } |
Versus:
// With Alamofire func fetchAllRooms(completion: ([RemoteRoom]?) -> Void) { Alamofire.request( .GET, "http://localhost:5984/rooms/_all_docs", parameters: ["include_docs": "true"], encoding: .URL) .validate() .responseJSON { (response) -> Void in guard response.result.isSuccess else { print("Error while fetching remote rooms: \(response.result.error)") completion(nil) return } guard let value = response.result.value as? [String: AnyObject], rows = value["rows"] as? [[String: AnyObject]] else { print("Malformed data received from fetchAllRooms service") completion(nil) return } var rooms = [RemoteRoom]() for roomDict in rows { rooms.append(RemoteRoom(jsonData: roomDict)) } completion(rooms) } } |
You can see that the required setup for Alamofire is shorter and is much more clear as to what the function does. You deserialize the response with responseJSON(options:completionHandler:)
and calling validate()
on the Response
object simplifies error condition handling.
Open ViewController.swift and add the following class extension to the end of the file:
// Networking calls extension ViewController { func uploadImage(image: UIImage, progress: (percent: Float) -> Void, completion: (tags: [String], colors: [PhotoColor]) -> Void) { guard let imageData = UIImageJPEGRepresentation(image, 0.5) else { print("Could not get JPEG representation of UIImage") return } } } |
The first step in uploading an image to Imagga is to get the correct format for the API. Above, the Image Picker API returns a UIImage
instance that you convert into a JPEG NSData
instance.
Next, go to imagePickerController(_:didFinishPickingMediaWithInfo:)
and add the following right after the point where you set the image on imageView
:
// 1 takePictureButton.hidden = true progressView.progress = 0.0 progressView.hidden = false activityIndicatorView.startAnimating() uploadImage( image, progress: { [unowned self] percent in // 2 self.progressView.setProgress(percent, animated: true) }, completion: { [unowned self] tags, colors in // 3 self.takePictureButton.hidden = false self.progressView.hidden = true self.activityIndicatorView.stopAnimating() self.tags = tags self.colors = colors // 4 self.performSegueWithIdentifier("ShowResults", sender: self) }) |
Everything with Alamofire is asynchronous, which means the you’ll update UI in an asynchronous manner:
Next add the following to the top of ViewController.swift:
import Alamofire |
This lets you use the functionality provided by the Alamofire module in your code.
Next, go back to uploadImage(_:progress:completion:)
and add the following after the point where you convert the UIImage
instance:
Alamofire.upload( .POST, "http://api.imagga.com/v1/content", headers: ["Authorization" : "Basic xxx"], multipartFormData: { multipartFormData in multipartFormData.appendBodyPart(data: imageData, name: "imagefile", fileName: "image.jpg", mimeType: "image/jpeg") }, encodingCompletion: { encodingResult in } ) |
Make sure to replace Basic xxx
with the actual authorization header taken from the Imagga dashboard. Here you convert the JPEG data blob (imageData
) into a MIME multipart request to the Imagga content endpoint.
Next, add the following to the encodingCompletion
closure:
switch encodingResult { case .Success(let upload, _, _): upload.progress { bytesWritten, totalBytesWritten, totalBytesExpectedToWrite in dispatch_async(dispatch_get_main_queue()) { let percent = (Float(totalBytesWritten) / Float(totalBytesExpectedToWrite)) progress(percent: percent) } } upload.validate() upload.responseJSON { response in } case .Failure(let encodingError): print(encodingError) } |
This chunk of code calls the Alamofire upload
function and passes in a small calculation to update the progress bar as the file uploads. Notice the progress callback is made in a dispatch to the main queue since you’re updating the UI.
Note: Alamofire is not guaranteed to call the progress callback on the main queue; therefore you must guard against updating the UI by dispatching to the main queue. Some of Alamofire’s callbacks, such as responseJSON, are called on the main queue by default. For example:
dispatch_async(queue ?? dispatch_get_main_queue()) { let response = ... completionHandler(response) } |
To change this default behavior you would provide a dispatch_queue_t
to Alamofire.
Next, add the following code to upload.responseJSON
:
// 1. guard response.result.isSuccess else { print("Error while uploading file: \(response.result.error)") completion(tags: [String](), colors: [PhotoColor]()) return } // 2. guard let responseJSON = response.result.value as? [String: AnyObject], uploadedFiles = responseJSON["uploaded"] as? [AnyObject], firstFile = uploadedFiles.first as? [String: AnyObject], firstFileID = firstFile["id"] as? String else { print("Invalid information received from service") completion(tags: [String](), colors: [PhotoColor]()) return } print("Content uploaded with ID: \(firstFileID)") // 3. completion(tags: [String](), colors: [PhotoColor]()) |
Here’s a step-by-step explanation of the above code:
firstFileID
from the response. If firstFileID
cannot be resolved, print out an error message and call the completion handler.Note: Every response has a Result
enum with a value and type. Using automatic validation, the result is considered a success when it returns a valid HTTP Code between 200 and 299 and the Content Type is of a valid type specified in the Accept HTTP header field.
You can perform manual validation by adding .validate
options as shown below:
Alamofire.request(.GET, "https://httpbin.org/get", parameters: ["foo": "bar"]) .validate(statusCode: 200..<300) .validate(contentType: ["application/json"]) .response { response in // response handling code } |
The UI won’t show an error if you hit an error during the upload; it merely returns no tags or colors to the user. This isn’t the best user experience, but it’s fine for this Alamofire tutorial.
Build and run your project; select an image and watch the progress bar change as the file uploads. You should see a note like the following in your console when the upload completes:
You’ve successfully uploaded a file over the Interwebs!
The next step after uploading the image to Imagga is to fetch the tags Imagga produces after it analyzes the photo.
Add the following method to the ViewController
extension below uploadImage(_:progress:completion:)
:
func downloadTags(contentID: String, completion: ([String]) -> Void) { Alamofire.request( .GET, "http://api.imagga.com/v1/tagging", parameters: ["content": contentID], headers: ["Authorization" : "Basic xxx"] ) .responseJSON { response in guard response.result.isSuccess else { print("Error while fetching tags: \(response.result.error)") completion([String]()) return } guard let responseJSON = response.result.value as? [String: AnyObject] else { print("Invalid tag information received from service") completion([String]()) return } print(responseJSON) completion([String]()) } } |
Again, be sure to replace Basic xxx
with your actual authorization header. Here you perform an HTTP GET request against the tagging endpoint, sending the URL parameter content
with the ID you received after the upload.
Next, go back to uploadImage(_:progress:completion:)
and replace the call to the completion handler in the success condition with the following:
self.downloadTags(firstFileID) { tags in completion(tags: tags, colors: [PhotoColor]()) } |
This simply sends along the tags to the completion handler.
Build and run your project; upload a file and you’ll see a dump of the data in the console:
You don’t care about the confidence score for this Alamofire tutorial, only the array of tag names.
Next, go back to downloadTags(_:completion:)
and replace the code inside .responseJSON
with the following:
// 1. guard response.result.isSuccess else { print("Error while fetching tags: \(response.result.error)") completion([String]()) return } // 2. guard let responseJSON = response.result.value as? [String: AnyObject], results = responseJSON["results"] as? [AnyObject], firstResult = results.first, tagsAndConfidences = firstResult["tags"] as? [[String: AnyObject]] else { print("Invalid tag information received from the service") completion([String]()) return } // 3. let tags = tagsAndConfidences.flatMap({ dict in return dict["tag"] as? String }) // 4. completion(tags) |
Here’s a step-by-step explanation of the above code:
tagsAndConfidences
information from the response. If tagsAndConfidences
cannot be resolved, print out an error message and call the completion handler.tagsAndConfidences
array, retrieving the value associated with the tag
key.tags
received from the service.Note: You’re using the Swift flatMap
method to iterate over each dictionary in the tagsAndConfidences
array; this method handles nil
values without crashing and will remove those values from the returned result. This lets you use conditional unwrapping (as?
) to verify whether the dictionary value can be converted to a String
.
Build and run your project again; select a photo and you should see the following appear:
Pretty slick! That Immaga is one smart API. :] Next, you’ll fetch the colors of the image.
Add the following method to the ViewController
extension below downloadTags(_:completion:)
:
func downloadColors(contentID: String, completion: ([PhotoColor]) -> Void) { Alamofire.request( .GET, "http://api.imagga.com/v1/colors", parameters: ["content": contentID, "extract_object_colors": NSNumber(int: 0)], // 1. headers: ["Authorization" : "Basic xxx"] ) .responseJSON { response in // 2. guard response.result.isSuccess else { print("Error while fetching colors: \(response.result.error)") completion([PhotoColor]()) return } // 3. guard let responseJSON = response.result.value as? [String: AnyObject], results = responseJSON["results"] as? [AnyObject], firstResult = results.first as? [String: AnyObject], info = firstResult["info"] as? [String: AnyObject], imageColors = info["image_colors"] as? [[String: AnyObject]] else { print("Invalid color information received from service") completion([PhotoColor]()) return } // 4. let photoColors = imageColors.flatMap({ (dict) -> PhotoColor? in guard let r = dict["r"] as? String, g = dict["g"] as? String, b = dict["b"] as? String, closestPaletteColor = dict["closest_palette_color"] as? String else { return nil } return PhotoColor(red: Int(r), green: Int(g), blue: Int(b), colorName: closestPaletteColor) }) // 5. completion(photoColors) } } |
Taking each numbered comment in turn:
Basic xxx
with your actual authorization header.imageColors
information from the response. If imageColors
cannot be resolved, print out an error message and call the completion handler.flatMap
again, you iterate over the returned imageColors
, transforming the data into PhotoColor
objects which pairs colors in the RGB format with the color name as a string. Note the provided closure allows returning nil values since flatMap
will simply ignore them.photoColors
from the service.Finally, go back to uploadImage(_:progress:completion:)
and replace the call to the completion handler in the success condition with the following:
self.downloadTags(firstFileID) { tags in self.downloadColors(firstFileID) { colors in completion(tags: tags, colors: colors) } } |
This nests the operations of uploading the image, downloading tags and downloading colors.
Build and run your project again; this time, you should see the returned color tags:
You use the RGB colors you mapped to PhotoColor
structs to change the background color of the view. You’ve now successfully uploaded an image to Imagga and fetched data from two different endpoints. You’ve come a long way — but there’s some room for improvement in how you’re using Alamofire in PhotoTagger.
You probably noticed some repeated code in PhotoTagger. If Imagga released v2 of their API and deprecated v1, PhotoTagger would no longer function and you’d have to update the URL in each of the three methods. Similarly, if your authorization token changed you’d be updating it all over the place.
Alamofire provides a simple method to eliminate this code duplication and provide centralized configuration. The technique involves creating a struct conforming to the URLRequestConvertible
protocol and updating your upload and request calls.
Create a new Swift file by clicking File\New\File… and selecting Swift file under iOS. Click Next, name the file ImaggaRouter.swift, select the Group PhotoTagger with the yellow folder icon and click Create.
Replace the contents of your new file with the following:
import Foundation import Alamofire public enum ImaggaRouter: URLRequestConvertible { static let baseURLPath = "http://api.imagga.com/v1" static let authenticationToken = "Basic xxx" case Content case Tags(String) case Colors(String) public var URLRequest: NSMutableURLRequest { let result: (path: String, method: Alamofire.Method, parameters: [String: AnyObject]) = { switch self { case .Content: return ("/content", .POST, [String: AnyObject]()) case .Tags(let contentID): let params = [ "content" : contentID ] return ("/tagging", .GET, params) case .Colors(let contentID): let params = [ "content" : contentID, "extract_object_colors" : NSNumber(int: 0) ] return ("/colors", .GET, params) } }() let URL = NSURL(string: ImaggaRouter.baseURLPath)! let URLRequest = NSMutableURLRequest(URL: URL.URLByAppendingPathComponent(result.path)) URLRequest.HTTPMethod = result.method.rawValue URLRequest.setValue(ImaggaRouter.authenticationToken, forHTTPHeaderField: "Authorization") URLRequest.timeoutInterval = NSTimeInterval(10 * 1000) let encoding = Alamofire.ParameterEncoding.URL return encoding.encode(URLRequest, parameters: result.parameters).0 } } |
Replace Basic xxx
with your actual authorization header. This router helps create instances of NSMutableURLRequest
by providing it one of the three cases: .Content
, .Tags(String)
, or .Colors(String)
. Now all of your boilerplate code is in single place, should you ever need to update it.
Go back to uploadImage(_:progress:completion:)
and replace the beginning of the call to Alamofire.upload
with the following:
Alamofire.upload( ImaggaRouter.Content, multipartFormData: { multipartFormData in multipartFormData.appendBodyPart(data: imageData, name: "imagefile", fileName: "image.jpg", mimeType: "image/jpeg") }, /// original code continues... |
Next replace the call for Alamofire.request
in downloadTags(_:completion:)
with:
Alamofire.request(ImaggaRouter.Tags(contentID)) |
Finally, update the call to Alamofire.request
in downloadColors(_:completion:)
with:
Alamofire.request(ImaggaRouter.Colors(contentID)) |
Build and run for the final time; everything should function just as before, which means you’ve refactored everything without breaking your app. Awesome job!
You can download the completed version of this Alamofire tutorial’s project here. Don’t forget to replace your authorization token as appropriate!
This Alamofire tutorial covered the very basics. You can take a deeper dive by looking at the documentation on the Alamofire site at https://github.com/Alamofire/Alamofire.
Also, you can take some time to learn more about Apple’s NSURLSession
which Alamofire uses under the hood:
Please share any comments or questions about this Alamofire tutorial in the forum discussion below!
The post Alamofire Tutorial: Getting Started appeared first on Ray Wenderlich.
Learn how to set up the interface for a Today Extension.
The post Video Tutorial: iOS App Extensions Part 6: Today Extensions: Layout appeared first on Ray Wenderlich.
Over the past few months, we’ve received some great applications from folks interested in joining the tutorial team, but unfortunately we’re currently at capacity.
However, there’s some good news – currently we have a few openings for OS X Tech Editors!
As an OS X tech editor, your role is incredibly important. You are the guardian that makes sure that each tutorial that comes out of our team is top-notch.
Not only does this help our readers get great tutorials, but it helps the rest of the team as well. As experienced developers and authors, you are helping our more junior team members improve through your improvements and comments on their work.
In our experience, our tech editors are often our very best writers and developers, and we treat them with the utmost regard. Tech editors are usually first in line for any book projects and special opportunities on our site.
Here are the top 5 reasons to apply as an OS X Tech Editor:
Here are the requirements:
To appply, send our OS X Team Lead Matthew Morey an email , with the following details:
For the applicants that look most promising, we will give you a tryout. If you pass the tryout, you’re in!
If you’ve ever been thinking about joining the Tutorial Team in the past, and have some OS X experience, this is a great time. We could really use the help, and we may have a secret project around the corner that you could get in on the ground floor :]
Thanks all – Matt, the OS X Team, and I look forward to creating some great tutorials with you! :]
The post Open Call For Applications: OS X Tech Editors appeared first on Ray Wenderlich.
Learn how to use a shared container to share data between the app and the extension via Core Data.
The post Video Tutorial: iOS App Extensions Part 7: Today Extensions: Core Data appeared first on Ray Wenderlich.
In the world of software development, it doesn’t take long before you need to persist app data. In many cases, this comes in the form of data structures. But how do you store it effectively — and efficiently?
Fortunately, some great minds have developed solutions for storing structured data in databases and writing language features to access that data. This SQLite tutorial shows you how to work with the popular database platform SQLite, from within Swift. SQLite is available by default on iOS; if you’re familiar with Core Data, you’ll know SQLite is the most common way to persist object graphs in Core Data.
Throughout this SQLite tutorial, you’ll learn how to perform the following database operations:
After learning how to perform these fundamental operations, you’ll see how to wrap them up in a more Swift-like manner. This will let you write abstraction APIs for your apps so that you can (mostly) avoid the pain of working with the SQLite C APIs! :]
Finally, I’ll briefly cover the popular open source Swift wrapper SQLite.swift to give you a basic understanding of how underlying frameworks work within a wrapper.
Download the starter project for this SQLite tutorial and open SQLiteTutorial.xcworkspace. From the Project Navigator open Tutorial.playground. You’ll immediately notice an error message on the line import SQLite
. You’ll need to build the project at least once by pressing Command + B; this is because the workspace is configured to create a module so that SQLite can be used in the playground.
import SQLite
line. SQLite is not a proper module, and you won’t be able to call import SQLite
like this in your projects; instead you’ll use a bridging header.
With the playground open, set it to run manually instead of automatically:
This will help ensure your SQL commands run when you intend them to.
You might also see a destroyPart1Database()
call at the top of the page; you can safely ignore this, since the database file is destroyed each time the playground runs. This ensures all statements execute successfully as you move through this SQLite tutorial.
Your playground will need somewhere to write SQLite database files on your file system. Run the following command in Terminal to create the data directory for your playground:
mkdir -p ~/Documents/Shared\ Playground\ Data/SQLiteTutorial |
True, SQLite isn’t the only way to persist data on iOS. Besides Core Data, there are lots of other alternatives for data persistence, including Realm, Couchbase Lite, Firebase, and NSCoding.
Each of these has their own pros and cons — including SQLite itself. There’s no silver bullet for data persistence, and as the developer, it’s up to you to determine which option outweighs the others based on your app’s requirements.
SQLite does have some advantages:
The cons of SQLite can be terribly subjective and opinionated, so we’ll leave the research on that up to you! :]
This part of the SQLite tutorial will walk you through the most common and basic SQLite APIs. You’ll soon realize that wrapping the C API in Swift methods would be ideal, but sit tight and work through the C code first; you’ll do some wrapping in the second part of this SQLite tutorial.
Before doing anything, you’ll first need to create a database connection.
Add the following method under the Getting Started section of the playground:
func openDatabase() -> COpaquePointer { var db: COpaquePointer = nil if sqlite3_open(part1DbPath, &db) == SQLITE_OK { print("Successfully opened connection to database at \(part1DbPath)") return db } else { print("Unable to open database. Verify that you created the directory described " + "in the Getting Started section.") XCPlaygroundPage.currentPage.finishExecution() } } |
The above method calls sqlite3_open()
, which opens or creates a new database file. If it’s successful, it returns a COpaquePointer
; this is a Swift type for C pointers that can’t be represented directly in Swift. When you call this method, you’ll have to capture the returned pointer in order to interact with the database.
Many of the SQLite functions return an Int32
result code. Most of these codes are defined as constants in the SQLite library. For example, SQLITE_OK
represents the result code 0
. A list of the different result codes can be found on the main SQLite site.
To open the database, add the following line to your playground:
let db = openDatabase() |
Press the Play button to run the playground and watch the console output. If the console isn’t open, press the button to the left of the play button:
If openDatabase()
succeeds, you’ll see some output like the following:
Successfully opened connection to database at /Users/username/Documents/Shared Playground Data/SQLiteTutorial/Part1.sqlite |
Where username
is your Home directory.
Now that you have a connection to a database file, you can create a table. You’ll work with a very simple table to store contacts.
The table will consist of two columns; Id
, which is an INT
and a PRIMARY KEY
; and Name
, which is a CHAR(255)
.
Add the following string, which contains the SQL statement necessary to create the table:
let createTableString = "CREATE TABLE Contact(" + "Id INT PRIMARY KEY NOT NULL," + "Name CHAR(255));" |
Next, add this method that executes the CREATE TABLE
SQL statement:
func createTable() { // 1 var createTableStatement: COpaquePointer = nil // 2 if sqlite3_prepare_v2(db, createTableString, -1, &createTableStatement, nil) == SQLITE_OK { // 3 if sqlite3_step(createTableStatement) == SQLITE_DONE { print("Contact table created.") } else { print("Contact table could not be created.") } } else { print("CREATE TABLE statement could not be prepared.") } // 4 sqlite3_finalize(createTableStatement) } |
Going over this step-by-step:
sqlite3_prepare_v2()
compiles the SQL statement into byte code and returns a status code — an important step before executing arbitrary statements against your database. If you’re interested, you can find out more here. You check the returned status code to ensure the statement compiled successfully. If so, the process moves to step 3; otherwise, you print a message noting the statement could not be compiled.
sqlite3_step()
runs the compiled statement. In this case, you only “step” once as this statement has a single result. Later in this SQLite tutorial you’ll see when it’s necessary to step multiple times for a single statement.
sqlite3_finalize()
on your compiled statement to delete it and avoid resource leaks. Once a statement has been finalized, you should never use it again.
Now, add the following method call to the playground:
createTable() |
Run your playground; you should see the following appear in your console output:
Contact table created. |
Now that you have a table, it’s time to add some data to it. You’re going to add a single row with an Id
of 1
and Name
of Ray
.
Add the following SQL statement to the bottom of your playground:
let insertStatementString = "INSERT INTO Contact (Id, Name) VALUES (?, ?);" |
This might look a little strange if you haven’t had much SQL experience. Why are the values represented by question marks?
Remember above when you used sqlite3_prepare_v2()
to compile your statement? The ?
syntax tells the compiler that you’ll provide real values when you actually execute the statement.
This has performance considerations, and lets you compile statements ahead of time, which can be a performance gain since compilation is a costly operation. The compiled statements can then be re-used over and over with different values.
Next, create the following method in your playground:
func insert() { var insertStatement: COpaquePointer = nil // 1 if sqlite3_prepare_v2(db, insertStatementString, -1, &insertStatement, nil) == SQLITE_OK { let id: Int32 = 1 let name: NSString = "Ray" // 2 sqlite3_bind_int(insertStatement, 1, id) // 3 sqlite3_bind_text(insertStatement, 2, name.UTF8String, -1, nil) // 4 if sqlite3_step(insertStatement) == SQLITE_DONE { print("Successfully inserted row.") } else { print("Could not insert row.") } } else { print("INSERT statement could not be prepared.") } // 5 sqlite3_finalize(insertStatement) } |
Here’s how the above method works:
?
placeholder. The function’s name — sqlite3_bind_int()
— implies you’re binding an Int
value to the statement. The first parameter of the function is the statement to bind to, while the second is a non-zero based index for the position of the ?
you’re binding to. The third and final parameter is the value itself. This binding call returns a status code, but for now you assume that it succeeds;
-1
and nil
for both. If you’d like, you can read more about binding parameters here;
sqlite3_step()
function to execute the statement and verify that it finished;
Next, call your new method by adding the following to the playground:
insert() |
Run your playground and verify that you see the following in your console output:
Successfully inserted row. |
Challenge time! Your task is to update insert()
to insert an array of contacts.
As a hint, you’ll need to reset your compiled statement back to its initial state by calling sqlite3_reset()
before you execute it again.
Solution Inside: Solution — Insert multiple rows | SelectShow> | |
---|---|---|
As you can see, the code is very similar to what you already had, but with these notable differences:
|
Now that you’ve inserted a row or two, it sure would be nice to verify that it’s really there! :]
Add the following to the playground:
let queryStatementString = "SELECT * FROM Contact;" |
This query simply retrieves all records FROM the contact table. Using a *
means all columns will be returned.
Add the following method to perform the query:
func query() { var queryStatement: COpaquePointer = nil // 1 if sqlite3_prepare_v2(db, queryStatementString, -1, &queryStatement, nil) == SQLITE_OK { // 2 if sqlite3_step(queryStatement) == SQLITE_ROW { // 3 let id = sqlite3_column_int(queryStatement, 0) // 4 let queryResultCol1 = sqlite3_column_text(queryStatement, 1) let name = String.fromCString(UnsafePointer<CChar>(queryResultCol1))! // 5 print("Query Result:") print("\(id) | \(name)") } else { print("Query returned no results") } } else { print("SELECT statement could not be prepared") } // 6 sqlite3_finalize(queryStatement) } |
Taking each numbered comment in turn:
SQLITE_ROW
, which means that you retrieved a row when you stepped through the result;
Int
, so you use sqlite3_column_int()
and pass in the statement and a zero-based column index. You assign the returned value to the locally-scoped id
constant;
Name
column. This is a bit messy due to the C API. First, you capture the value as queryResultCol1
so you can convert it to a proper Swift string on the next line;
Now, call your new method by adding the following to the bottom of the playground:
query() |
Run your playground; you’ll see the following output in your console:
Query Result: 1 | Ray |
W00t! It looks like your data made it into the database after all!
Your task is to update query()
to print out every contact in the table.
Solution Inside: Solution — Print all contacts | SelectShow> | |
---|---|---|
Note that instead of using a single step to retrieve the first row as you did before, this time you’re using a |
The next natural progression is to update an existing row. You should start to feel some patterns emerging.
First, create the UPDATE
statement:
let updateStatementString = "UPDATE Contact SET Name = 'Chris' WHERE Id = 1;" |
Here you’re using real values instead of ?
placeholders. Usually you’d use the placeholders and perform proper statement binding, but for brevity you can skip it here.
Next, add the following method to the playground:
func update() { var updateStatement: COpaquePointer = nil if sqlite3_prepare_v2(db, updateStatementString, -1, &updateStatement, nil) == SQLITE_OK { if sqlite3_step(updateStatement) == SQLITE_DONE { print("Successfully updated row.") } else { print("Could not update row.") } } else { print("UPDATE statement could not be prepared") } sqlite3_finalize(updateStatement) } |
This is a similar flow to what you’ve seen before: prepare, step, finalize! Now execute this method, followed by your query()
method from before, so you can see the results:
update() query() |
You should see the following output in your console:
Successfully updated row. Query Result: 1 | Chris |
Congratulations on updating your first row! How easy was that? :]
The final step on the path to becoming an SQLite ninja is to delete the row you created. Again, you’ll use the familiar pattern of prepare, step, and finalize.
Add the following to the playground:
let deleteStatementStirng = "DELETE FROM Contact WHERE Id = 1;" |
Now add the following method to execute the statement:
func delete() { var deleteStatement: COpaquePointer = nil if sqlite3_prepare_v2(db, deleteStatementStirng, -1, &deleteStatement, nil) == SQLITE_OK { if sqlite3_step(deleteStatement) == SQLITE_DONE { print("Successfully deleted row.") } else { print("Could not delete row.") } } else { print("DELETE statement could not be prepared") } sqlite3_finalize(deleteStatement) } |
Are you feeling it now? Prepare, step, and finalize! :]
Execute this new method, followed a call to query()
, like so:
delete() query() |
Now run your playground and you should see the following output in your console:
Successfully deleted row. Query returned no results |
Hopefully, you’ve managed to avoid SQLite errors up to this point. But the time will come when you make a call that doesn’t make sense, or simply cannot be compiled.
Handling the error message when these things happen can save you a lot of development time; it also gives you the opportunity to present meaningful error messages to your users.
Add the following statement – which is intentionally malformed – to your playground:
let malformedQueryString = "SELECT Stuff from Things WHERE Whatever;" |
Now add a method to execute this malformed statement:
func prepareMalformedQuery() { var malformedStatement: COpaquePointer = nil // 1 if sqlite3_prepare_v2(db, malformedQueryString, -1, &malformedStatement, nil) == SQLITE_OK { print("This should not have happened.") } else { // 2 let errorMessage = String.fromCString(sqlite3_errmsg(db))! print("Query could not be prepared! \(errorMessage)") } // 3 sqlite3_finalize(malformedStatement) } |
Here’s how you’re going to force an error:
SQLITE_OK
;
sqlite3_errmsg()
. This function returns a textual description of the most recent error. You then print the error to the console;
Call the method to see the error message:
prepareMalformedQuery() |
Run your playground; you should see the following output in your console:
Query could not be prepared! no such table: Things |
Well, that’s actually helpful — you obviously cannot run a SELECT
statement on a table that doesn’t exist!
When you’re done with a database connection, you’re responsible for closing it. But beware — there are a number of things you must have performed before closing your database will succeed, as described in the SQLite documentation.
Call the close function as shown below:
sqlite3_close(db) |
Run your playground; you should see a status code of 0
in the right side results view of the playground; this represents SQLITE_OK
, which means your close call succeeded.
You’ve successfully created a database, added a table, added rows to the table, queried and updated those rows, and even deleted a row — all using the SQLite C APIs from Swift. Great job!
In the next section, you’ll take what you’ve learned and see how to wrap some of these calls in Swift.
As a Swift developer, you’re probably feeling a little uneasy about what happened in the first part of this SQLite tutorial. That C API is a bit painful, but the good news is you can take the power of Swift and wrap those C routines to make things easier for yourself.
For this part of the SQLite tutorial, click the Making it Swift link at the bottom of the playground to open the playground for this section:
Getting at errors from the C API is a bit awkward as a Swift developer. Checking a result code and then calling another method just doesn’t make sense in this brave new world. It would make more sense if methods that can fail throw an error.
Add the following to your playground:
enum SQLiteError: ErrorType { case OpenDatabase(message: String) case Prepare(message: String) case Step(message: String) case Bind(message: String) } |
This is a custom ErrorType
enum that covers four of the main operations you are using that can fail. Note how each case has an associated value that will hold the error message.
Another not-so-Swifty aspect is the use of those blasted COpaquePointer
types.
Wrap up the database connection pointer in its own class, as shown below:
class SQLiteDatabase { private let dbPointer: COpaquePointer private init(dbPointer: COpaquePointer) { self.dbPointer = dbPointer } deinit { sqlite3_close(dbPointer) } } |
This looks much better. When you need a database connection, you can create a reference to a more meaningful type of SQLiteDatabase
rather than COpaquePointer
.
You’ll notice the initializer is private
; that’s because you don’t want your Swift developers passing in that COpaquePointer
. Instead, you let them instantiate this class with a path to the database file.
Add the following static method to SQLiteDatabase
as follows:
static func open(path: String) throws -> SQLiteDatabase { var db: COpaquePointer = nil // 1 if sqlite3_open(path, &db) == SQLITE_OK { // 2 return SQLiteDatabase(dbPointer: db) } else { // 3 defer { if db != nil { sqlite3_close(db) } } if let message = String.fromCString(sqlite3_errmsg(db)) { throw SQLiteError.OpenDatabase(message: message) } else { throw SQLiteError.OpenDatabase(message: "No error message provided from sqlite.") } } } |
Here’s what happening:
SQLiteDatabase
;
SQLITE_OK
and throw an error.
Now you can create and open a database connection using much cleaner syntax.
Add the following to your playground:
let db: SQLiteDatabase do { db = try SQLiteDatabase.open(part2DbPath) print("Successfully opened connection to database.") } catch SQLiteError.OpenDatabase(let message) { print("Unable to open database. Verify that you created the directory described in the Getting Started section.") XCPlaygroundPage.currentPage.finishExecution() } |
Ah, much more Swift like. Here, the attempt to open the database is wrapped in a do-try-catch
block, and the error message from SQLite is passed to the catch
block thanks to that custom enum you added earlier.
Run your playground and watch the console output; you’ll see something like the following:
Successfully opened connection to database. |
Now you can use and inspect the db
instance as a proper and meaningful type.
Before moving on to writing methods that execute statements, it would be nice if SQLiteDatabase
let you easily access SQLite error messages.
Add the following computed property to SQLiteDatabase
:
private var errorMessage: String { if let errorMessage = String.fromCString(sqlite3_errmsg(dbPointer)) { return errorMessage } else { return "No error message provided from sqlite." } } |
Here you’ve added a computed property that simply returns the most recent error SQLite knows about. If there is no error, it just returns a generic message stating as much.
Since you do this so often, it makes sense to wrap it like the other methods. As you move forward and add functionality to the SQLiteDatabase
class, you’ll make use class extensions.
Add the following extension, which will be used by your future methods, to invoke sqlite3_prepare_v2()
on SQL statements:
extension SQLiteDatabase { func prepareStatement(sql: String) throws -> COpaquePointer { var statement: COpaquePointer = nil guard sqlite3_prepare_v2(dbPointer, sql, -1, &statement, nil) == SQLITE_OK else { throw SQLiteError.Prepare(message: errorMessage) } return statement } } |
Here you declare that prepareStatement(_:)
can throw an error, and then use guard
to throw that error should sqlite3_prepare_v2()
fail. Just like before, you pass the error message from SQLite to the relevant case of your custom enum.
In these examples, you’ll use the same Contact
table as before, so it makes sense to define a proper struct
to represent a contact. I’ve already defined the following one for you in this Playground, so you don’t need to add it:
struct Contact { let id: Int32 let name: String } |
You’ll knock out the same database tasks as before, but this time you’ll use a “Swifter” approach.
To create a table, you need a CREATE TABLE
SQL statement. It makes sense for Contact
to define its own CREATE TABLE
statement.
Create the following protocol
for just that purpose:
protocol SQLTable { static var createStatement: String { get } } |
Now, extend Contact
to provide conformance to this new protocol:
extension Contact: SQLTable { static var createStatement: String { return "CREATE TABLE Contact(" + "Id INT PRIMARY KEY NOT NULL," + "Name CHAR(255)" + ");" } } |
Now you’re able to write the following method that accepts types that conform to SQLTable
to create a table:
extension SQLiteDatabase { func createTable(table: SQLTable.Type) throws { // 1 let createTableStatement = try prepareStatement(table.createStatement) // 2 defer { sqlite3_finalize(createTableStatement) } // 3 guard sqlite3_step(createTableStatement) == SQLITE_DONE else { throw SQLiteError.Step(message: errorMessage) } print("\(table) table created.") } } |
Here’s a breakdown of what’s happening:
prepareStatement()
throws, so you must use try
. You’re not doing this in a do-try-catch
block because this method itself throws, so any error from prepareStatement()
will simply be thrown to the caller of createTable()
;
defer
, you can ensure that your statements are always finalized, regardless of how this method exits its scope;
guard
lets you write a more expressive check for the SQLite status codes.
Give your new method a try, and add the following to your playground:
do { try db.createTable(Contact) } catch { print(db.errorMessage) } |
Here you simply attempt to create the Contact
, and catch the error if there is one.
Run your playground; you should see the following appear in your console:
Contact table created. |
Fantastic! Isn’t that a much cleaner API to work with?
Moving right along, it’s time to insert a row into the Contact
table. Add the following method:
extension SQLiteDatabase { func insertContact(contact: Contact) throws { let insertSql = "INSERT INTO Contact (Id, Name) VALUES (?, ?);" let insertStatement = try prepareStatement(insertSql) defer { sqlite3_finalize(insertStatement) } let name: NSString = contact.name guard sqlite3_bind_int(insertStatement, 1, contact.id) == SQLITE_OK && sqlite3_bind_text(insertStatement, 2, name.UTF8String, -1, nil) == SQLITE_OK else { throw SQLiteError.Bind(message: errorMessage) } guard sqlite3_step(insertStatement) == SQLITE_DONE else { throw SQLiteError.Step(message: errorMessage) } print("Successfully inserted row.") } } |
Now that you’ve got your SQLegs – see what I did there? :] – this code shouldn’t be too surprising. Given a Contact
instance, you prepare a statement, bind the values, execute and finalize. Again, using a potent mix of defer
, guard
and throw
allows you to take advantage of modern Swift language features.
Write the code to call this new method as shown below:
do { try db.insertContact(Contact(id: 1, name: "Ray")) } catch { print(db.errorMessage) } |
Run your playground; you should see the following in your console:
Successfully inserted row. |
Wrapping up (sorry, I couldn’t resist!) the section on creating the Swift wrapper is querying the database.
Add the following method to query the database for a contact:
extension SQLiteDatabase { func contact(id: Int32) -> Contact? { let querySql = "SELECT * FROM Contact WHERE Id = ?;" guard let queryStatement = try? prepareStatement(querySql) else { return nil } defer { sqlite3_finalize(queryStatement) } guard sqlite3_bind_int(queryStatement, 1, id) == SQLITE_OK else { return nil } guard sqlite3_step(queryStatement) == SQLITE_ROW else { return nil } let id = sqlite3_column_int(queryStatement, 0) let queryResultCol1 = sqlite3_column_text(queryStatement, 1) let name = String.fromCString(UnsafePointer<CChar>(queryResultCol1))! return Contact(id: id, name: name) } } |
This method simply takes the id of a contact and either returns that contact, or nil
if there isn’t a contact with that id. Again, these statements should feel somewhat familiar by now.
Write the code to query the first contact:
let first = db.contact(1) print("\(first)") |
Run your playground; you should see the following output in the console:
Optional(1 | Ray) |
By now, you’ve probably identified some calls you could create in a generic fashion and apply them to entirely different tables. The point of the above exercise is to show how you can use Swift to wrap low-level C APIs. This is no simple task for SQLite; there are a ton of intricacies to SQLite that were not covered here.
You might be thinking “Hasn’t someone already created a wrapper for this?” – let me answer that for you right now!
Stephen Celis has graciously written a fully-featured Swift wrapper for SQLite named SQLite.swift. I highly recommend that you check it out if you decide that SQLite fits the bill for data storage in your app.
SQLite.swift provides an expressive way to represent tables and lets you get started with SQLite — without worrying about many of the underlying details and idiosyncrasies of SQLite. You may even consider wrapping SQLite.swift itself to create a high-level API for your app’s domain model.
Check out the well-written README.md for SQLite.swift and decide for yourself if it has a place in your personal code toolbox.
What about those other common tasks that were skipped over in the Swift section? You can download the completed project for this SQLite tutorial to see how we implemented updates, deletes, and multiple row handling. There simply wasn’t enough space to outline them all here.
One thing I haven’t covered is debugging. In many cases, you’ll need some kind of database browser to see what’s going on under the hood. There are a number of different apps out there that range from free and open source, to paid closed source with commercial support. Here are a couple to take a look at, but a quick Google search will reveal many more:
You can also access your SQLite databases directly from your Terminal by typing sqlite3 file.db
. From there you can use the .help
command to see a list of commands, or you can simply start executing SQL statements directly at the prompt. More information on the command-line SQLite client can be found on the main SQLite site.
I hope you enjoyed this whirlwind introduction to working with SQLite from Swift! If you have any questions or comments, please join the discussion below!
The post SQLite Tutorial: Getting Started appeared first on Ray Wenderlich.
Learn how to keep the Today Extension up to date with NSURLSession.
The post Video Tutorial: iOS App Extensions Part 8: Today Extensions: Updating appeared first on Ray Wenderlich.
Learn how to use URL Schemes to communicate from the Extension to the app.
The post Video Tutorial: iOS App Extensions Part 9: Today Extensions: OpenURL appeared first on Ray Wenderlich.
The post Video Tutorial: Beginning watchOS: Series Introduction appeared first on Ray Wenderlich.
Learn the basics of layout on the watch in this first video.
The post Video Tutorial: Beginning watchOS Part 1: Layout I appeared first on Ray Wenderlich.
Learn more layout techniques in this video: insets, spacing, and adaptive layout.
The post Video Tutorial: Beginning watchOS Part 2: Layout II appeared first on Ray Wenderlich.
Back in the days when there was only Objective-C, encapsulation was limited to working with classes. However, in modern iOS and Mac programming there are three choices: enums, structs and classes in Swift.
Combined with protocols, these types make it possible to create amazing things. While they share many common abilities, these types also have important differences.
The objective of this tutorial is to:
In terms of prerequisites, this tutorial assumes that you have at least a little Swift and object-oriented programming experience.
Three big selling points of Swift are its safety, speed and simplicity.
Safety implies that it’s difficult to accidentally write code that runs amok, corrupting memory and producing hard-to-find bugs. Swift makes your work safer because it tries to make it obvious when you have a bug by showing you problems at compile time, rather than hanging you out to dry at runtime.
Moreover, because Swift lets you clearly express your intent, the optimizer can go to town to help your code run lightning fast.
The Swift language core is simple and highly-regularized, thanks to being built upon a surprisingly small number of concepts. Despite its relatively simple rules, you can do amazing things.
The key to making this happen is the Swift type system:
Swift types are powerful, despite there being only six of them. That’s right, unlike many other languages that have literally dozens of built-in types, Swift only has six.
These consist of four named types: protocol, enum, struct, class). There are two compound types as well: tuple, function.
There are those other things you might think of as basic types—like Bool
, Int
, UInt
, Float
, Double
, Character
, String
, Array
, Set
, Dictionary
, Optional
, etc. However, these are actually built up from the named types and delivered as part of the Swift Standard Library.
This tutorial focuses on the so-called named model types which consist of enum
, struct
and class
.
As a working example, you’ll build a safe, speedy and simple SVG shape (scalable vector graphics) rendering framework.
SVG is an XML-based vector image format for 2D graphics. This specification has been an open standard developed by the W3C since 1999. (https://developer.mozilla.org/en-US/docs/Web/SVG)
Create a new playground in Xcode to follow along by selecting File\New\Playground… and name it Shapes and set the platform as OS X. Click Next to choose a location to save it, then Create to save the file. Clear the file completely and then enter the following:
import Foundation |
Your goal will be able to render something like this:
<!DOCTYPE html><html><body><svg width='250' height='250'><rect x='110.0' y='10.0' width='100.0' height='130.0' stroke='Teal' fill='Aqua' stroke-width='5' /><circle cx='80.0' cy='160.0' r='60.0' stroke='Red' fill='Yellow' stroke-width='5' /></svg></body></html> |
It looks better when rendered by a browser or in a WebKit view, trust me. :]
You’ll need a representation for colors. SVG uses the CSS3 color type that can be specified as a name, RGB or HSL. The full specification is here: http://www.w3.org/TR/css3-color/.
To use a color in SVG, you specify it as an attribute of part of your drawing. For example, fill = 'Gray'
. An easy approach to represent this in Swift this would be just to use a String
as in, let fill = "Gray"
.
While using String
is easy and does the job, there are some major downsides.
Using a custom type solves these problems. If you’re coming from Cocoa Touch, you might think to implement an encapsulated class like UIColor
. While using a class design could work, Swift gives you more choices for how to define your model.
Without typing anything in just yet, let’s have a think about how you might implement the colors as an enum.
You might think to implement it like so:
enum ColorName { case Black case Silver case Gray case White case Maroon case Red // ... and so on ... } |
The above works very similarly to a set of C-style enums. However, unlike C-style enums, Swift gives you the option to specify a type to represent each case.
Enumerations that explicitly specify a backing store
type are referred to as RawRepresentable
, because they automatically adopt the RawRepresentable
protocol.
So, you can specify the type of ColorName
as String
, and assign a value to each case, like so:
enum ColorName : String { case Black = "Black" case Silver = "Silver" case Gray = "Gray" case White = "White" case Maroon = "Maroon" case Red = "Red" // ... and so on ... } |
However, Swift does something special for enums with a String
representation. If you don’t specify what the case is equal to, the compiler automatically makes the String the same as the name of the case. That means you only need to write the case name:
enum ColorName : String { case Black case Silver case Gray case White case Maroon case Red // ... and so on ... } |
You can further reduce your typing by separating the cases with commas using the keyword case
just once.
Add the following code to the end of your playground:
enum ColorName : String { case Black, Silver, Gray, White, Maroon, Red, Purple, Fuchsia, Green, Lime, Olive, Yellow, Navy, Blue, Teal, Aqua } |
Now you have a first class custom type and all the goodness that comes with that. For example,
let fill = ColorName.Grey // ERROR: Misspelled color names won't compile. Good! let fill = ColorName.Gray // Correct names autocomplete and compile. Yay! |
ColorName
is good for named colors, but you might recall that CSS colors have several representations: named, RGB, HSL and more. How do you model those?
Enums in Swift are great for modeling things that have one of a number of representations, such as CSS color, and each enum case can be paired with its own data. These data are called associated values.
Define the CSSColor
using an enum by adding this to your playground:
enum CSSColor { case Named(ColorName) case RGB(UInt8, UInt8, UInt8) } |
With this definition, you give the CSSColor
model one of two states.
Named
, in which case the associated data is a ColorName
valueRGB
, in which case the associated data is three UInt8
(0-255) numbers for red, green, and blue.Note that this example leaves out RGBA, HSL and HSLA cases for brevity.
You want to be able to print out multiple instances of CSSColor
.
In Swift, enums, just like the other named types, can adopt protocols. In particular, your type can magically work with the print statement by adopting the CustomStringConvertible
protocol.
The key to inter-operating with the Swift Standard Library is to adopt standard library protocols.
Add the following extension for CSSColor
to your playground:
extension CSSColor : CustomStringConvertible { var description: String { switch self { case .Named(let colorName): return colorName.rawValue case .RGB(let red, let green, let blue): return String(format: "#%02X%02X%02X", red,green,blue) } } } |
This makes CSSColor
conform to CustomStringConvertible
. This is what tells Swift that our type, CSSColor
, can be converted to a string. We tell it how by implementing the description
computed property.
In this implementation, self
is switched upon to determine if the underlying model is a named or RGB type. In each case you convert the color to the required string format for that case. The named case just returns the string name whereas the RGB case returns the red, green and blue values in the required format.
Add the following to your playground:
let color1 = CSSColor.Named(.Red) let color2 = CSSColor.RGB(0xAA, 0xAA, 0xAA) print("color1 = \(color1), color2 = \(color2)") // prints color1 = Red, color2 = #AAAAAA |
Everything is type checked and proven correct at compile time, unlike how it goes when you use only String
values to represent colors.
Note: While you could go back to your previous definition of CSSColor and modify it, you don’t have to. You’ve used an extension to re-open the color type and adopt a new protocol.
The extension style is nice because it makes what you define in order to conform to a given protocol fully explicit. In the case of CustomStringConvertible
, you’re required to implement a getter for a description
string property.
Just like classes and structs in Swift, you can also add custom initializers to enum. For example, you can make a custom initializer for grayscale values.
Add this extension to your playground:
extension CSSColor { init(gray: UInt8) { self = .RGB(gray, gray, gray) } } |
Add the following to your playground:
let color3 = CSSColor(gray: 0xaa) print(color3) // prints #AAAAAA |
You can now conveniently create grayscale colors!
Named types can act as a namespace to keep things organized and minimize complexity. You created ColorName
and CSSColor
, and yet ColorName
is only ever used in the context of a CSSColor
.
Wouldn’t it be nice if you could hide ColorName
within a CSSColor
model?
Well, you can! Remove ColorName
from your playground and replace it with the following code:
extension CSSColor { enum ColorName : String { case Black, Silver, Gray, White, Maroon, Red, Purple, Fuchsia, Green, Lime, Olive, Yellow, Navy, Blue, Teal, Aqua } } |
This moves ColorName
into an extension on CSSColor
. Now ColorName
is tucked away, and the inner type is defined on CSSColor
.
Note: One of the great features of Swift is that the order in which you declare things usually doesn’t matter. The compiler scans the file multiple times and figures it out without needing to forward declare things as it does when working with C/C++/Objective-C.
However, if you receive an error in your playground about ColorName
being an undeclared type, move the above extension to just below your enum definition of CSSColor
to clear the playground error.
Sometimes playgrounds are sensitive to the ordering of definitions, even when it doesn’t really matter. :]
Enums can be set up as pure namespaces that users can’t accidentally instantiate. For example, you’ll soon need the math constant pi
to perform some computations. While you could use Foundation’s M_PI macro, you’ll define your own to keep things as portable as possible. (Arduino micro-controller, here we come!)
Add the following code to the end of your playground:
enum Math { static let pi = 3.1415926535897932384626433832795028841971694 } |
Since the Math
enum contains no cases, and it’s illegal to add new cases in an extension, it can never be instantiated. You’ll never be able to accidentally misuse Math
as a variable or parameter.
By declaring pi
as a static constant, you don’t need to instantiate one. Whenever you need the value of pi, you can simply use Math.pi
rather than remembering all those digits!
Enums are much more powerful in Swift than they are in other languages, such as C or Objective-C. As you’ve seen, you can extend them, create custom initializer methods, provide namespaces and encapsulate related operations.
So far you’ve used enum
to model CSS colors. This works well because CSS colors are a well understood, fixed W3C specification.
Enumerations are great for picking items from a list of well-known things, such as days of the week, faces of a coin or states in a state machine. It’s no surprise that Swift optionals are implemented in terms of an enum with a state of .None
or .Some
with an associated value.
On the other hand, if you wanted CSSColor
to be user extensible to other color space models that are not defined in the W3C specification, an enumeration is not be the preferred abstraction.
Which, by the way, brings you to the next Swift named model type, Structures. :]
Because you want your users to be able to define their own custom shapes within the SVG, using an enum
is not a good choice for defining shape types.
New enum
cases cannot be added later in an extension. That leaves either a class
or a struct
.
The Swift Standard Library team suggests that when you create a new model, you should first design the interface using a protocol. You want your shapes to be drawable, so add this to your playground:
protocol Drawable { func draw(context: DrawingContext) } |
The protocol defines what it means to be Drawable
. It has a draw method that draws to something called a DrawingContext
.
Speaking of DrawingContext
, it’s just another protocol. Add it to your playground as follows:
protocol DrawingContext { func draw(circle: Circle) // more primitives will go here ... } |
A DrawingContext
knows how to draw pure geometric types: Circle, Rectangle and other primitives. Take note of something here: the actual drawing technology is not specified, but you could implement it in terms of anything — SVG, HTML5 Canvas, Core Graphics, OpenGL, Metal, etc.
You’re ready to define a circle that adopts the Drawable
protocol. Add this to your playground:
struct Circle : Drawable { var strokeWidth = 5 var strokeColor = CSSColor.Named(.Red) var fillColor = CSSColor.Named(.Yellow) var center = (x: 80.0, y: 160.0) var radius = 60.0 // Adopting the Drawable protocol. func draw(context: DrawingContext) { context.draw(self) } } |
In a struct
, you group together stored properties. Here you have implemented the following properties:
Structs work a lot like classes with a couple of key differences. Perhaps the biggest difference is that structs are value types and classes are reference types.
Value types work as separate and distinct entities.
The quintessential value type is an integer because it works that way in most programming languages. If you want to know how a value type acts, ask the question, “What would Int do?” For example:
For Int:
var a = 10 var b = a a = 30 // b still has the value of 10. a == b // false |
For Circle (defined using struct):
var a = Circle() a.radius = 60.0 var b = a a.radius = 1000.0 // b.radius still has the value 60.0 |
If you had made your circle from a class type, it would have been given reference semantics. That means that it references an underlying shared object.
For Circle (defined using class):
var a = Circle() // a class based circle a.radius = 60.0 var b = a a.radius = 1000.0 // b.radius also becomes 1000.0 |
When creating new objects using value types, copies are made; when using reference types, the new variable refers to the same object. This dissimiliarity in behavior is a critical difference between class
and struct
.
Add this to your playground to continue making your drawing library by creating a rectangle type:
struct Rectangle : Drawable { var strokeWidth = 5 var strokeColor = CSSColor.Named(.Teal) var fillColor = CSSColor.Named(.Aqua) var origin = (x: 110.0, y: 10.0) var size = (width: 100.0, height: 130.0) func draw(context: DrawingContext) { context.draw(self) } } |
You also need to update the DrawingContext
protocol so that it knows how to draw a rectangle. Update DrawingContext
in your playground so it reads:
protocol DrawingContext { func draw(circle: Circle) func draw(rectangle: Rectangle) // more primitives would go here ... } |
Circle
and Rectangle
adopt the drawable protocol. They defer the actual work to something that conforms to the DrawingContext
protocol.
Now it’s time to make a concrete model that does drawing SVG style. Add this to your playground:
final class SVGContext : DrawingContext { private var commands: [String] = [] var width = 250 var height = 250 // 1 func draw(circle: Circle) { commands.append("<circle cx='\(circle.center.x)' cy='\(circle.center.y)\' r='\(circle.radius)' stroke='\(circle.strokeColor)' fill='\(circle.fillColor)' stroke-width='\(circle.strokeWidth)' />") } // 2 func draw(rectangle: Rectangle) { commands.append("<rect x='\(rectangle.origin.x)' y='\(rectangle.origin.y)' width='\(rectangle.size.width)' height='\(rectangle.size.height)' stroke='\(rectangle.strokeColor)' fill='\(rectangle.fillColor)' stroke-width='\(rectangle.strokeWidth)' />") } var SVGString: String { var output = "<svg width='\(width)' height='\(height)'>" for command in commands { output += command } output += "</svg>" return output } var HTMLString: String { return "<!DOCTYPE html><html><body>" + SVGString + "</body></html>" } } |
SVGContext
is a class that wraps a private array of command strings. In sections 1 and 2, you conform to the DrawingContext
protocol, and the draw methods just append a string with the correct XML for rendering the shape.
Finally, you need a document type that can contain many Drawable
objects, so add this to your playground:
struct SVGDocument { var drawables: [Drawable] = [] var HTMLString: String { let context = SVGContext() for drawable in drawables { drawable.draw(context) } return context.HTMLString } mutating func append(drawable: Drawable) { drawables.append(drawable) } } |
Here, HTMLString
is a computed property on SVGDocument
that creates an SVGContext
and returns the HTMLString from the context.
How about we finally draw an SVG? Add this to your playground:
var document = SVGDocument() let rectangle = Rectangle() document.append(rectangle) let circle = Circle() document.append(circle) let HTMLString = document.HTMLString print(HTMLString) |
This creates a default circle and rectangle and puts them into a document, and then it prints the XML.
Let’s now visualize the SVG. Add the following to the end of the playground:
import WebKit import XCPlayground let view = WKWebView(frame: CGRect(x: 0, y: 0, width: 250, height: 250)) view.loadHTMLString(HTMLString, baseURL: nil) XCPlaygroundPage.currentPage.liveView = view |
This does some Playground trickery and sets up a web view to view the SVG. Press Command-Option-Return to show this web view in the assistant editor.
So far, you used a combination of structs (value types) and protocols to implement drawable models.
Now it’s time to play with classes too. They let you define base classes and derived classes. The more traditional object-oriented approach to the shapes problem is to make a Shape
base class with a draw()
method.
Even though you won’t use it now, it’s helpful to know how this approach would work. It would look something like this:
And in code, it would look like the following block — this is just for reference, so don’t add it to your playground:
class Shape { var strokeWidth = 1 var strokeColor = CSSColor.Named(.Black) var fillColor = CSSColor.Named(.Black) var origin = (x: 0.0, y: 0.0) func draw(context: DrawingContext) { fatalError("not implemented") } } class Circle : Shape { override init() { super.init() strokeWidth = 5 strokeColor = CSSColor.Named(.Red) fillColor = CSSColor.Named(.Yellow) origin = (x: 80.0, y: 80.0) } var radius = 60.0 override func draw(context: DrawingContext) { context.draw(self) } } class Rectangle : Shape { override init() { super.init() strokeWidth = 5 strokeColor = CSSColor.Named(.Teal) fillColor = CSSColor.Named(.Aqua) origin = (x: 110.0, y: 10.0) } var size = (width: 100.0, height: 130.0) override func draw(context: DrawingContext) { context.draw(self) } } |
In order to make object oriented programming safer, Swift introduced the override
keyword. It requires that you, the programmer, acknowledge when you’re overriding something.
It prevents accidentally shadowing existing methods or not properly overriding what you thought you were. It can be a lifesaver when consuming a new version of a library and understanding how things have changed.
Nevertheless, there are some drawbacks to this object oriented approach.
The first problem you’ll notice is in the base-class implementation of draw. Shape
wants to avoid being misused, so it calls fatalError()
to alert derived classes that they need to override this method.
Unfortunately, this check happens at runtime time and not compile time.
Secondly, the Circle
and Rectangle
classes have to deal with the initialization of the base class data. While this is a relatively easy scenario, class initialization can become a somewhat involved process in order to guarantee correctness.
Thirdly, it can be tricky to future proof a base-class.
For example, suppose you wanted to add a drawable Line
type. In order to work with your existing system, it would have to derive from Shape
, which is a little bit of a misnomer.
Moreover, your Line
class needs to initialize the base class’s fillColor
property, and that doesn’t really make sense for a line.
Based on this, you could refactor your hierarchy and it would work better. In practice, however, it might not be possible to modify your base class without breaking many existing clients, and it’s usually impossible to get it right the first time.
Finally, classes have the reference (shared) semantics that were discussed earlier. While Automatic Reference Counting (ARC) takes care of things most of the time, you need to be careful not to introduce reference cycles, or you’ll end up with memory leaks.
If you add the same shape to an array of shapes, it might be surprising when you modify the color of one shape to red and another one also seems to randomly change.
Given the above downsides, you might be wondering why you would ever want to use a class.
For starters, they allow you to adopt mature and battle-tested frameworks like Cocoa and Cocoa Touch.
Additionally, classes do have more important uses. For example, a large memory-hogging, expensive-to-copy object is a good candidate for wrapping in a class.
See where I’m going? They’re helpful anytime reference vs. value semantics come into play.
Check out this two-part tutorial on the subject: Reference vs. Value Types in Swift.
All named model types let you create custom setters and getters that don’t necessarily correspond to a stored property.
Suppose you want to add a diameter getter and setter to your Circle
model. It’s easy to implement it in terms of the existing radius
property.
Add the following code to the end of your playground:
extension Circle { var diameter: Double { get { return radius * 2 } set { radius = newValue / 2 } } } |
This implements a new computed property which is purely based on the radius. When you get the diameter, it returns the radius doubled. When you set the diameter, it sets the radius to the value divided by 2. Simple!
More often than not, you only want to implement a special getter. In this case, you don’t have to include the get {} keyword block and can just specify the body. Perimeter and area are good use cases for this.
Add the following to the circle extension you just added:
// Example of getter-only computed properties var area: Double { return radius * radius * Math.pi } var perimeter: Double { return 2 * radius * Math.pi } |
Unlike classes, struct methods are not allowed to modify — aka mutate — stored properties by default, but they can if you declare them as mutating.
For example, add the following to the Circle extension:
func shift(x: Double, y: Double) { center.x += x center.y += y } |
This tries to define a shift()
method on circle which moves the circle in space. i.e. it changes the center point.
But this throws the following error on the two lines which increment the center.x
and center.y
properties.
// ERROR: Left side of mutating operator has immutable type ‘Double' |
This can be fixed by adding the mutating keyword, like so:
mutating func shift(x: Double, y: Double) { center.x += x center.y += y } |
This tells Swift that it’s OK that your function mutates the struct.
One of the great features of Swift is retroactive modeling. It lets you extend behavior of a model type even if you don’t have the source code for it.
Here’s a use case: Suppose you’re a user of the SVG code and you want to add an area
and perimeter
property to Rectangle
just like Circle
.
To see what this all means, add this to your playground:
extension Rectangle { var area: Double { return size.width * size.height } var perimeter: Double { return 2 * (size.width + size.height) } } |
Previously, you used extension
to add methods to an existing model, and now, you’ll formalize these methods into a new protocol.
Add this to your playground:
protocol ClosedShapeType { var area: Double { get } var perimeter: Double { get } } |
That gives you an official protocol.
Next, you’ll tell the circle and rectangle to adopt this protocol retroactively by adding the following to your playground:
extension Circle: ClosedShapeType {} extension Rectangle: ClosedShapeType {} |
You can also define a function that, for example, computes the total perimeter of an array of models (any mix of structs, enums, classes) that adopt the ClosedShapeType
protocol.
Add the following to the end of the playground:
func totalPerimeter(shapes: [ClosedShapeType]) -> Double { return shapes.reduce(0) { $0 + $1.perimeter } } totalPerimeter([circle, rectangle]) |
This uses reduce
to calculate the sum of perimeters. You can learn more about how it works in An Introduction to Functional Programming.
The completed playground can be found here.
In this tutorial, you learned about enum
, struct
and class
— the named model types of Swift.
All three have key similarities: they provide encapsulation, can have initializer methods, can have computed properties, can adopt protocols, and they can be modeled retroactively.
However, they also have important differences.
Enums are value types that have a set of cases, where each case can have different associated values. Each value of an enum type represents a single case as defined by the enum. They can’t have any stored properties.
Structs, like enums, are value types but can also have stored properties.
Classes, like structs, can have stored properties, and they can be built into class hierarchies that override properties and methods. Because of this, explicit initialization of base classes is a requirement.
But unlike structs and enums, classes use reference, aka sharing, semantics.
For much more information on this, see the two-part series mentioned earlier, Reference vs. Value Types in Swift.
I hope you have enjoyed this whirlwind tour of the named model types in Swift. If you’re looking for a challenge, consider building a more complete version of the SVG rendering library. You’re off to a good start. :]
As always, if you have questions or insights you would like to share, please use the forums below!
The post Getting to Know Enums, Structs and Classes in Swift appeared first on Ray Wenderlich.
Learn the basics of tables in WatchKit.
The post Video Tutorial: Beginning watchOS Part 3: Tables appeared first on Ray Wenderlich.
Your challenge is to finish setting up the table rows so they display real data. See the Challenge PDF in the resources link below for full details.
View previous video: Tables
The post Video Tutorial: Beginning watchOS Part 4: Table Rows appeared first on Ray Wenderlich.
Join Mic, Jake, and Marin as they discuss Marin’s 2015 conference marathon, and his pick of the bunch with his three favorite iOS conferences. The three hosts then move on to talk about community facing content, the motivation behind getting involved, and how there are many rewards, not just financial.
[Subscribe in iTunes] [RSS Feed]
This episode was brought to you by the incredibly kind folks over at Hired and Couchbase.
Hired is the platform for the best iOS developer jobs.
Candidates registered with Hired receive an average of 5 offers on the platform, all from a single application. Companies looking to hire include Facebook, Uber and Stripe.
With Hired, you get job offers and salary and/or equity before you interview, so you don’t have to waste your time interviewing for jobs you might not end up wanting, and it’s totally free to use!
Plus you, our loyal audience, will receive a $2000 bonus from Hired if you find a job through the platform, just for signing up using the show’s exclusive link: hired.com/ray
Couchbase are running a hands-on lab at RWDevCon, designed to get you up and running with Couchbase Mobile, their NoSQL database for mobile applications.
If you’re attending RWDevCon, be sure to join Wayne Carter at 11:30am on Saturday 12th as he explains how to use Couchbase Mobile to create a consistent user experience, regardless of whether the user is on-, or offline. Wayne will touch on such topics as local persistence, sync, security, and taking your data cross-platform.
Make sure to get there early, as places are limited!
Interested in sponsoring a podcast episode? We sell ads via Syndicate Ads, check it out!
We hope you enjoyed this episode of our podcast. Be sure to subscribe in iTunes to get notified when the next episode comes out.
We’d love to hear what you think about the podcast, and any suggestions on what you’d like to hear in future episodes. Feel free to drop a comment here, or email us anytime at podcast@raywenderlich.com.
The post iOS Conferences, and Community Facing Content appeared first on Ray Wenderlich.