Monthly Archives: July 2014

Swift – all that glitters is not gold (casting to CFString)

Yes, it was love at first sight, but recently Swift annoys me more and more with every day that passes. Today I spent a few hours in an attempt to run a method which converts the string to the machine url (percent instead of space etc). Knowing that String requires double casting to CFString I finished with something like this:

let baseUrl = "http://example.com"
let nsBaseUrl:NSString = baseUrl as NSString
let cfBaseUrl:CFString = nsBaseUrl as CFString

let chars = "!*'();:@&=+$,/?%#[]"
let nsChars:NSString = chars as NSString
let cfChars:CFString = nsChars as CFString

var encodedCfStringRef = CFURLCreateStringByAddingPercentEscapes(
  kCFAllocatorDefault,
  cfBaseUrl,
  nil,
  cfChars,
  CFStringEncoding(UTF8)
)

Unfortunately, the stubborn Xcode constantly tells me that the strings I provide are NSStrings, not CFStrings.

swift-nsstring-to-cfstring

I am not the only one who struggles with same problem:

Unfortunately, Swift still needs a little polishing. I will keep you posted on the progress of my fight with this problem.

Google Maps SDK with Swift – tutorial [ENG]

WARNING:

This tutorial was created with Xcode beta and early version of Swift. Use it at your own risk.

Swift, why are you so awesome? For all those who want to start with Google Maps SDK using Swift I have created this short tutorial. Of course it is too early for Google developers to provide full support for Swift but there is nothing to fear. As Swift is compatibile with Objective-C, you can use existing Google Maps SDK framework.

1. Create new Swift project. My choise was “Tabbed application”.

2. Obtain new api key or add your bundle id to existing one (more info on Google Maps SDK website)

3. Follow instructions from official tutorial (same link as above):

  1. Drag the GoogleMaps.framework bundle to the Frameworks group of your project. When prompted, select Copy items into destination group’s folder.
  2. Right-click GoogleMaps.framework in your project, and select Show In Finder.
  3. Drag the GoogleMaps.bundle from the Resources folder to your project. We suggest putting it in the Frameworks group. When prompted, ensure Copy items into destination group’s folder is not selected.
  4. Select your project from the Project Navigator, and choose your application’s target.
  5. Open the Build Phases tab, and within Link Binary with Libraries, add the following frameworks:
    • AVFoundation.framework
    • CoreData.framework
    • CoreLocation.framework
    • CoreText.framework
    • GLKit.framework
    • ImageIO.framework
    • libc++.dylib
    • libicucore.dylib
    • libz.dylib
    • OpenGLES.framework
    • QuartzCore.framework
    • SystemConfiguration.framework
  6. Choose your project, rather than a specific target, and open the Build Settings tab.
    • In the Other Linker Flags section, add -ObjC. If these settings are not visible, change the filter in the Build Settings bar fromBasic to All.

4. Create new Objective-C file (*.m) anywhere in the project (File -> New -> File -> iOS -> source -> Objective-C class). Xcode will ask you if you want to create bridge file which allows to import Objective-C headers to Swift project. Accept it.

5. Open the bridging file ([projectname]-Bridging-Header.h) and paste following line:

#import <GoogleMaps/GoogleMaps.h>

6. Remove Objective-C (*.m) file if you want – it won’t be useful.

7. Open AppDelegate.swift and initialize Google Maps with your api key in didFinishLaunchingWithOptions method:

func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: NSDictionary?) -> Bool {
    GMSServices.provideAPIKey("MYAp1KEy-348234239423949294239");
    return true
}

8. It should be it. You can go to your ViewController and start playing with Google Maps. My ViewController looks like this:

#1
class FirstViewController: UIViewController, GMSMapViewDelegate {

    //#2
    var gmaps: GMSMapView?

    init(coder aDecoder: NSCoder!) {
        super.init(coder: aDecoder)
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        var target: CLLocationCoordinate2D = CLLocationCoordinate2D(latitude: 51.6, longitude: 17.2)
        var camera: GMSCameraPosition = GMSCameraPosition(target: target, zoom: 6, bearing: 0, viewingAngle: 0)

        gmaps = GMSMapView(frame: CGRectMake(0, 0, self.view.bounds.width, self.view.bounds.height - super.tabBarController.tabBar.bounds.height))
        if let map = gmaps? {
            map.myLocationEnabled = true
            map.camera = camera
            map.delegate = self

            self.view.addSubview(gmaps)
        }
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

}

#1 – remember to make your controller a GMSMapViewDelegate

#2 – my property storing map object is optional, because I don’t want it to be initialised before “didFinishLaunching” method (where I keep my API Key). Is it the best way? I don’t know yet. Feel free to comment!

9. Run the application!