• Sending HTTP POST Requests to ORDS from Swift

    In this example I am referring to a RESTful web services framework created by Oracle however, you could substitute your own web server / web application framework / RESTful service as well.

    Oracle REST Data Services (ORDS) sits on top of the Oracle database and allows resources in the database to be REST enabled. Once you have ORDS setup you can send HTTP requests to ORDS and define application logic using PL/SQL blocks inside of resource handlers. Resource handlers are grouped within resource templates. Templates define the base URI (i.e.: /login). Resource templates are grouped within RESTful Service Modules. Modules allow you to group your URIs using a specified "base URI" called the "URI Prefix".

    Resource Hanlder

    Suppose we built a custom login resource handler inside our HR module in ORDS to accept HTTP POST requests by reading an email address and password from the HTTP header data. The goal is to allow users to login to a native iOS application written in Swift. This would be similar to traditional login requests sent to web applications.

    The process should look something like this:

    1. An iOS application (app) gets the ID (email address in this case) and password from the user
    2. The app creates an HTTP post request and sends this data to ORDS
    3. ORDS performs some application logic in PL/SQL and verifies if the login credentials are valid
    4. ORDS optionally populates OUT bind variables for HTTP header data or the response body with some session data (or error messages)

    In my proof of concept I decided to use OUT bind variables to return the login status / results as HTTP header data.

    ORDS Variables

    Here is a minimalistic example of the Swift code that sends an HTTP POST request to an ORDS resource handler called hr/login/. NB: you will not be able to run this example code by copying and pasting it as-is. You will first need to change the URL of the web application to something that is valid or by replacing the URI [workspace]/hr/login/ with the name of your own workspace and resource handler.

    let login = ["user":"ords_user@gmail.com", "pass":"ords_password"]
    
    let url = NSURL(string: "https://apex.oracle.com/pls/apex/[workspace]/hr/login/")!
    
    let session = NSURLSession.sharedSession()
    
    let request = NSMutableURLRequest(URL: url)
    
    do {
        // JSON all the things
        let auth = try NSJSONSerialization.dataWithJSONObject(login, options: .PrettyPrinted)
    
        // Set the request content type to JSON
        request.setValue("application/json", forHTTPHeaderField: "Content-Type")
    
        // The magic...set the HTTP request method to POST
        request.HTTPMethod = "POST"
    
        // Add the JSON serialized login data to the body
        request.HTTPBody = auth
    
        // Create the task that will send our login request (asynchronously)
        let task = session.dataTaskWithRequest(request, completionHandler: { (data, response, error) -> Void in
            // Do something with the HTTP response
            print("Got response \(response) with error \(error)")
            print("Done.")
        })
    
        // Start the task on a background thread
        task.resume()
    
    } catch {
        // Handle your errors folks...
        print("Error")
    }
    

    Note: My ORDS proof of concept for the Swift HTTP POST example code was built using a demonstration Oracle Application Express (APEX) workspace hosted at apex.oracle.com. If you don't have ORDS up and running anywhere else you can test this out by getting your own APEX workspace setup and playing with the RESTful Services module within your own workspace.

    You can run this sample code in a Playground in Xcode but as of Xcode 7.3.1 and Swift 2 you need to add the following lines of code to the top of your playground.

    import XCPlayground
    let page = XCPlaygroundPage.currentPage
    page.needsIndefiniteExecution = true
    

    This enables background threaded operations to be executed and results returned. Otherwise you will not see any output from the HTTP request task. In my example Swift code above the login ID and password are hardcoded as an associative array (hash -or- array of key-value pairs) but in a real application you would likely have some kind of login screen with UITextField objects for email and password input.

    After Swift sends the HTTP POST request, here is an example of what my ORDS resource handler sends back to iOS as an HTTP response.

    Got response Optional( { URL: https://apex.oracle.com/pls/apex/*****/hr/login/ } { status code: 200, headers {
        Connection = "Keep-Alive";
        "Content-Language" = en;
        "Content-Length" = 0;
        "Content-Type" = "text/plain";
        Date = "Mon, 30 May 2016 21:26:41 GMT";
        "Keep-Alive" = "timeout=5, max=100";
        Server = "Oracle-Application-Server-11g";
        "Set-Cookie" = "BIGipServer~Public~adc-apex_oracle_com_http=rd2o00000000000000000000ffff8d927229o7801; path=/";
        "X-Powered-By" = "Servlet/2.5 JSP/2.1";
        "api_session_id" = UF9RO4BXM9NS7W1UUOVW9YMOM4IKZKZN5HVHBOOOYSC5AW7YMJSDJ3CH15177202;
        "api_status" = OK;
    } }) with error nil
    Done.
    

    The two HTTP header variables returned from my ORDS proof of concept are api_session_id and api_status which I named arbitrarily to represent some hypothetical API and session state... assuming I was using ORDS as a backend for an iOS application.

    Based on this example you should be able to create Swift code that will send data using the HTTP POST method to a web application server. Whether you are using ORDS or any other application server is up to you and the requirements for your application.

    For bad passwords or invalid email addresses, here is an example of what my ORDS resource handler returns in cases where the user fails to login to the application.

    Got response Optional( { URL: https://apex.oracle.com/pls/apex/*****/hr/login/ } { status code: 401, headers {
        ...
        "api_status" = "Invalid email or password.";
    } }) with error nil
    Done.
    

    Notice how the HTTP status code is changed to 401 (Unauthorized) and my api_status is now a generic error message instead of "OK".

    I hope you found this to be useful and that your Swift apps are a huge success.

    Cheers!

Justin Biard

Working with computers / technology for 20+ years and just scratching the surface... (Opinions are my own)

Copyright Justin Biard © 2017