extreme swift
TRANSCRIPT
var dollarSigns = ""
for _ in 0..<3 { dollarSigns += "$" }
the normal wayRepeat a String a Specified Number of Times
var dollarSigns = "".stringByPaddingToLength(3, withString: "$", startingAtIndex: 0)
or
Repeat a String a Specified Number of Times
let dollarSigns = "$" * 3
func * (left:String, right:Int) -> String { return right == 0 ? "" : "".stringByPaddingToLength(right, withString: left, startingAtIndex: 0) }
Function must be given global scope e.g. defined outside of a class.
Operator Overloading
the extreme way
Repeat a String a Specified Number of Timesthe extreme way with Emoji
let 🍊🍊🍊🍊🍊 = 🍊 * 5
let 🍊 = "🍎 "
print(🍊🍊🍊🍊🍊 )
?
normalGet a Substring of a String
var message = "[Extreme] Swift (What was Apple thinking?)”
message = message.substring(0, length: 15)
// [Extreme Swift]
Get a Substring of a String
var message = "[Extreme] Swift (What was Apple thinking?)”
message >= 15
// [Extreme Swift]
extreme
Get a Substring of a Stringextreme
infix operator <= { associativity left }
func <= (inout left:String, right:Int) { left = left.substring(0, length: right) }
var message = "[Extreme] Swift (What was Apple thinking?)”
message <= 15
// [Extreme Swift]
Pop Quiz!
var places = [Place]() var place = Place() place.address = "718 7th St. NW, Washington DC" place.name = "WeWork" place.website = "https://www.wework.com/locations/washington-d-c/chinatown" places.append(place)
place = Place() place.address = "21165 Whitfield Pl. Ste#206 Sterling, VA" place.name = "Movel" place.website = "http://www.movel.co" places.append(place)
place = Place() place.address = "One Microsoft Way Redmond, WA" place.name = "Microsoft" place.website = "http://www.microsoft.com" places.append(place)
place = Place() place.address = "4938 Hampden Lane PO Box 264, Bethesda MD" place.name = "Cam-Built" place.website = "http://www.movel.co" places.append(place)
Let’s say you have this…
class Place { var name:String? var address:String? var website:String? }
Pop QuizYou got it!
func -= (inout left:[Place], right:String) { for index in 0..<left.count { if left[index].name == right { left.removeAtIndex(index) break } } }
Note that no infix operator needs to be defined since -= already exists.
Include this protocol in your custom class when you want to compare instances using ==.
Equatable Protocol
Your class must implement the == operator function.
func == (left:SearchResult, right:SearchResult) -> Bool { return left.name == right.name && left.address == right.address && left.city == right.city }
class SearchResult: NSManagedObject, Equatable
You can implement more than one == operator function.
Equatable Protocol
func == (left:[String:AnyObject?], right:SearchResult) -> Bool { if let leftName = left["name"] as! String?, rightName = right.name as String? { var leftNameClean = SearchResult.cleanCompareString(leftName) var rightNameClean = SearchResult.cleanCompareString(rightName) if leftNameClean.hasPrefix(rightNameClean) ||
rightNameClean.hasPrefix(leftNameClean) { return true } else { if let leftPhone = left["phone"] as? String { if leftPhone == right.phone { return true } } } } return false }
Foursquare search results are stored in a dictionary (left) and compared with results from Yelp and TripAdvisor. If there is a match on name after cleaning punctuation and white space, Foursquare data is merged.
Pop Quiz!Which class is better?
class MeetupMembers { var members:[String]? }
class MeetupMembership { private var members = [String:String]() func addMember(name:String) { members[name] = name } func memberForKey(name:String) -> String? { return members[name] }
func memberForIndex(index:Int) -> String? { return members.keys.array[index] } }
or…
Pop QuizThe answer is the one that is most like a black box.
class MeetupMembership { private var members = [String:String]() func addMember(name:String) { members[name] = name } func memberForKey(name:String) -> String? { return members[name] }
func memberForIndex(index:Int) -> String? { return members.keys.array[index] } }
Anyone that uses MeetupMembership doesn’t have to know if “members” is an array, dictionary or any other kind of collection.
Thus, the Law of Demeter!
Demeter is the Greek goddess of agriculture. Like plants, the Law of Demeter adheres to a bottom-up philosophy.
The ground does not offer its water to the leaves of a plant. The ground only passes the water parameter to the roots.
If you have a dog and it’s time to go walkies, you do not expect a dog’s legs to have a “walk” method. You call the dog’s “walk” method.
However, if you do not obey to the Law of Demeter, you will have less code and probably finish your projects sooner.
Swift encourages you to follow the Law of Demeter, and not just for objects but for stand-alone functions and value types.
The Swift Perspective
No Class Required// // NoClass.swift // Camtinerary // // Created by Cameron Conway on 4/26/15. // Copyright (c) 2015 cambuilt. All rights reserved. //
func helloWorld() { println("hello world") }
internal func showSecretMessage(code:String) { if code == "Abc123(*)" { println(secretMessage()) } }
private func secretMessage() -> String { return "Swift knows about AOP." }
Swift is both OO and AO (Aspect Oriented). Code files in Swift do not require a class definition, but follow the same rules of access control.
Swift Access Control
private makes a function or variable invisible to code outside of the file containing it. The benefits are that the project global namespace is kept clean and the compiler can do a better job optimizing.
internal is the default access level that makes access available across your project, but not to outside consumers of your framework or to any Objective C code.
public should be rarely used and makes access available to everybody.
Who am I?
● Learned COBOL programming on punch cards in 1985.
● dBASE III and Clipper from 1987 to 1989.
● Visual Basic from 1990 to 2000.
● C#, SQL Server, JavaScript from 2001 to present.
● Objective C from 2009 to present.
● Swift from 2014 to present.
● BASIC, Fortran and SPSS in college (American University) 1977 to 1981
“Design is not just what it looks like and feels like. Design is how it works.”
Steve Jobs, October 27 2003 as quoted in Newsweek.
Search result contains content from multiple well-known sources. Pins are small and tagged with the place name to provide useful information.
Camtinerary API Code Samplesfunc searchYelp() { let client = YelpClient(consumerKey: yelpKey, consumerSecret: yelpSecret, accessToken: yelpToken, accessSecret: yelpTokenSecret) client.searchWithCategory("hotels", ll: "\(lat),\(lng)", radius:rad, offset:offset, success: { (operation: AFHTTPRequestOperation!, response: AnyObject!) -> Void in
for searchResult in jsonResult["businesses"] as! Array<NSDictionary> { yelpSearchResults.append(searchResult) ctr++ }
}) { (operation: AFHTTPRequestOperation!, error: NSError!) -> Void in println(error) } }
Using a Yelp client from GitHub and adding results to an array.
Camtinerary API Code Sampleslet url = NSURL(string: "https://api.locu.com/v2/venue/search") var request = NSMutableURLRequest(URL: url!) request.HTTPMethod = “POST"
var params = ["api_key":LOCU_API_KEY,"fields":["name", "menu_url"], “venue_queries":[["name":name,"location":["locality":city]]]]
as [String:AnyObject!] var err: NSError?
request.HTTPBody = NSJSONSerialization.dataWithJSONObject(params, options: nil, error: &err) request.addValue("application/json", forHTTPHeaderField: "Content-Type") request.addValue("application/json", forHTTPHeaderField: "Accept")
NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue.mainQueue(), completionHandler: { (response:NSURLResponse!, data:NSData!, error:NSError!) -> Void in
if error == nil { var err:NSError? let httpResponse = response as! NSHTTPURLResponse! if httpResponse.statusCode == 200 { if var json = NSJSONSerialization.JSONObjectWithData(data, options:
NSJSONReadingOptions.MutableContainers, error:&err) as? NSDictionary { if let venues = json["venues"] as? Array<NSDictionary> { for venue in venues { println(venue["name"]) println(venue["menu_url"]) } } } } } }) Using standard Cocoa routines for Locu data.
Driving and walking time from place to place is determined by getting directions in code using MapKit.
Camtinerary API Code Samples
let request = MKDirectionsRequest() request.setSource(MKMapItem(placemark: startMark)); request.setDestination(MKMapItem(placemark: endMark)) request.transportType = travelType; request.requestsAlternateRoutes = true let directions = MKDirections(request: request) directions.calculateDirectionsWithCompletionHandler( {(response: MKDirectionsResponse!, error: NSError!) -> () in if error == nil { if response.routes.count > 0 { let route = response.routes[0] as! MKRoute
switch travelType { case .Walking: destination.walkingTime = route.expectedTravelTime case.Automobile: destination.drivingTime = route.expectedTravelTime default: break
} Util.i.save() } } else { if !self.travelTimeError && self.refreshButtonTapped { self.travelTimeError = true self.refreshButtonTapped = false let alert = UIAlertView(title: "Travel time", message: "Travel time unavailable.",
delegate: self, cancelButtonTitle: "OK") alert.show() } } })
MapKit code for getting directions and travel times.
Camtinerary API Code Samples
CLGeocoder().geocodeAddressString("502 Park Ave New York NY", completionHandler: {(placemarks: [AnyObject]!, error:NSError!) -> Void in
if error == nil { for placemark in placemarks as [CLPlacemark] { println(placemark.location.coordinate.longitude) println(placemark.location.coordinate.latitude) } } })
CLGeocoder for looking up coordinates with a given address.
Camtinerary API Code Sampleslet queryString = "502 Park Ave New York NY" let urlString = "https://api.datamarket.azure.com/Data.ashx/Bing/Search/Image?$format=json&Query=\(queryString)" let request = NSMutableURLRequest(URL: NSURL(string: urlString)!) let keyString = BING_SEARCH_API_KEY + ":" + BING_SEARCH_API_KEY let plainTextData = keyString.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false) as NSData! let base64String = plainTextData.base64EncodedStringWithOptions(.EncodingEndLineWithLineFeed) as String! request.setValue("Basic " + base64String!, forHTTPHeaderField: "Authorization")
NSURLConnection.sendAsynchronousRequest(request, queue: .mainQueue(), completionHandler: { (response:NSURLResponse!, data:NSData!, error:NSError!) -> Void in if error == nil { var err:NSError? if var json = NSJSONSerialization.JSONObjectWithData(data, options: .MutableContainers, error:&err) as? NSDictionary { for result in (json["d"] as! NSDictionary)["results"] as! Array<NSDictionary> { if webURLs[result["MediaUrl"] as! String] == nil { result.setValue(false, forKey: "Selected") self.webImageURLs.append(result) } } } } }) Bing Image Search API.
Camtinerary API Code Samplesvar address = "502 Park Ave New York NY" var urlString = "https://maps.googleapis.com/maps/api/geocode/json?address=\(address)&key=\(GOOGLE_API_KEY)" let url:NSURL! = NSURL(string: urlString)
if url != nil { let request = NSURLRequest(URL: url) var err:NSError? NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue.mainQueue(), completionHandler: { (response:NSURLResponse!, data:NSData!, error:NSError!) -> Void in if error == nil {
if var json = NSJSONSerialization.JSONObjectWithData(data, options: .MutableContainers, error:&err) as? NSDictionary {
var resDict = (json["results"] as! NSArray)[0] as! NSDictionary var geoDict = resDict["geometry"] as! NSDictionary var locDict = geoDict["location"] as! NSDictionary var lat = locDict["lat"] as! Double var lng = locDict["lng"] as! Double for index in 0..<4 { let url = "http://maps.googleapis.com/maps/api/streetview?location=\(lat),\(lng)&" + "size=150x150&heading=\(index * 90)" let request = NSURLRequest(URL: NSURL(string: url)!) var response:NSURLResponse?; var error:NSError? let data = NSURLConnection.sendSynchronousRequest(request, returningResponse: &response, error: &error) self.streetViewImages.append(UIImage(data: data!)!) } self.photoCollectionView.reloadData() self.closeLoadingView() } } }) } Google API to retrieve StreetView photos.
Sharing from Camtinerary uses UIActivityController with custom UIActivity objects to email or text the entire list of search results or itinerary.
Tweet from Camtinerary and the name of selected place is included along with a Bitly version of the website URL.
“Long Touch” on a list item displays a menu to read reviews on multiple sites, restaurant menus, websites, etc.
Options allow change in sort order, map style and price filter setting. Photos can be excluded to save cell data.