tagged: ios

Swift 3 and iOS: Form data and multipart uploads with URLRequest

If you want to upload an image to a server, an image from the photo gallery or camera for example, you often use a multipart request.
First create a POST Request as normal, with with a boundary string created via a UUID request and the content type as multipart/form-data with the boundary string.

var r  = URLRequest(url: URL(string: "https://prospero.uatproxy.cdlis.co.uk/prospero/DocumentUpload.ajax")!)
r.httpMethod = "POST"
let boundary = "Boundary-\(UUID().uuidString)"
r.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")

We must now create the http body. We use the below function, passing in parameters as a dictionary with strings, the boundary string we created, data from the UIImage, a mime-type and the filename for the image.

r.httpBody = createBody(parameters: params,
                        boundary: boundary,
                        data: UIImageJPEGRepresentation(chosenImage, 0.7)!,
                        mimeType: "image/jpg",
                        filename: "hello.jpg")

The createBody method first loops over the parameters dictionary, adding them to the body as a Content-Disposition with the boundary.
Finally, it adds the image as data, with the filename, the mime-type and with the boundary as before.

func createBody(parameters: [String: String],
                boundary: String,
                data: Data,
                mimeType: String,
                filename: String) -> Data {
    let body = NSMutableData()
    let boundaryPrefix = "--\(boundary)\r\n"
    for (key, value) in parameters {
        body.appendString("Content-Disposition: form-data; name=\"\(key)\"\r\n\r\n")
    body.appendString("Content-Disposition: form-data; name=\"file\"; filename=\"\(filename)\"\r\n")
    body.appendString("Content-Type: \(mimeType)\r\n\r\n")
    return body as Data

The appendString doesn't exist on a NSMutableData. It's a helper extension as defined below:

extension NSMutableData {
    func appendString(_ string: String) {
        let data = string.data(using: String.Encoding.utf8, allowLossyConversion: false)

Now we have the URLRequest we can send it off as usual.

swift ios ios-urlrequest

Swift 3 and iOS: Choose an image with UIImagePickerController

If you want to use an image from your iOS device, you'll want to use this. First put Privacy - Photo Library Usage Description with a string description into your Info.plist file.
Next define let picker = UIImagePickerController() and in your viewDidLoad() set its delegate as picker.delegate = self.
You can start it using:

  picker.allowsEditing = false
  picker.sourceType = .photoLibrary
  self.present(picker, animated: true, completion: nil)

Since we've made our class the delegate, you need to make the class use these protocols: UIImagePickerControllerDelegate, UINavigationControllerDelegate.
And we define two methods, one that dismisses the popup and another that grabs our image:

func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
    let chosenImage = info[UIImagePickerControllerOriginalImage] as! UIImage
    // use the image
    dismiss(animated: true, completion: nil)

func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
    dismiss(animated: true, completion: nil)

If you want to use the camera instead of the photo album, change your initialisation code to:

picker.allowsEditing = false
picker.sourceType = .camera
picker.cameraCaptureMode = .photo

swift ios

Swift 3 and iOS: Save a file

Let's say you already have a Data object filled with PDF data, or whatever.
We first get the url for our documents directory (in our user's home domain). Then we append our filename to that.
Finally we write our data above to this new url atomically, marking it with try since it may throw an exception.

var docURL = (FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)).last
docURL = docURL?.appendingPathComponent("sample1.pdf")
try OURDATA.write(to: docURL!, options: .atomicWrite)

swift ios

Swift 3 and iOS: Read a value from a plist

Create a Something.plist file in your main project. Add a String value with the key SomethingYeah in it.
Then, from your main bunle, get a path for the resource Something with the type plist.
Then get a dictionary from that path. And then use that to get the value we inserted above.

if let path = Bundle.main.path(forResource: "Something", ofType: "plist") {
    let dictRoot = NSDictionary(contentsOfFile: path)
    if let dict = dictRoot {
        debugPrint(dict["SomethingYeah"] as! String)

swift ios

Swift 3 and iOS: Presenting a UIPopoverPresentationController popup

First create a normal view controller in your storyboard. Then in a button click method, or whatever, initialise it:

let storyboard : UIStoryboard = UIStoryboard(name: "THE_NAME_OF_YOUR_STORYBOARD", bundle: nil)
let vc = storyboard.instantiateViewController(withIdentifier: "THE_IDENTIFIER_OF_YOUR_VIEWCONTROLLER")

Next set the modalPresentationStyle of it to popup, get a reference to the popup presentation controller, make this class its delegate and say it may show only an up arrow.

vc.modalPresentationStyle = .popover
let popover = vc.popoverPresentationController!
popover.delegate = self
popover.permittedArrowDirections = .up

Next set the source view and rect to whatever initialise the popup. In my case it's a sender object in a touch listener.

popover.sourceView = sender as? UIView
popover.sourceRect = sender.bounds

Finallly present it with present(vc, animated: true, completion:nil). It will take up the size of a new screen, unless we're on an iPad, regardless of our source rect and view.
To ensure we only take up the space under the aforementioned button, add this delegate method:

func adaptivePresentationStyle(for controller: UIPresentationController, traitCollection: UITraitCollection) -> UIModalPresentationStyle {
    return .none

It may not take up all the space of a new screen, but it will still take up everything below the button.
You can size the view controller with use preferred explicit size checked or do something like vc.preferredContentSize = CGSize(width: 200, height: 100), but I'd like a better method...

swift ios

Page 1 of 6