Data API using Swift (Alamofire or URLSession)

Hi every1!

Just stumbled across Joplin a couple of days ago. Awesome!! I started playing around with the Data API and Swift. Basically I'm trying out the possibilities to create a mobile companion app, which fills my local desktop Joplin (currently running desktop on Mac OS).
My first goal was easy to achieve, using some typical URLSession code to retrieve data. Notes, tags.. all easy.
But I'm struggling with uploading data, especially resources.

Anybody here had more luck with swift and Joplin's data API? I would be truly happy about a working snippet for uploading resources, e.g. images, pdfs, mov's....

Thanks to y'all!
Pete

There are some curl examples in the doc. If nobody knows here you could probably take these examples and ask on SO how to translate the curl commands to Swift.

Thanks Laurent!
I started with the curl examples and they worked like a charm. Just translating them into some usable Swift code is a challenge so far I‘m guessing that the headers are incomplete. Just getting „method not allowed“ errors back.

Any ideas on how to debug this, is highly appreciated :slight_smile:
The SO idea is great. I‘ll give that a try.

If somebody else in here - with some Swift knowledge- has an idea, I‘m grateful for all tips & tricks.

Thanks,
Pete

"method not allowed" is when, for example, you do a POST request when only PUT is supported.

Perhaps post your code here to see what might be the problem.

Thanks Laurent and sorry for the late reply.

It was indeed partly a problem with mixed up POST and PUT, alongside some other things, like "cannot create without file" or something. Took a bit, but finally I have some working code, that I'm happy to share with anyone wanting to play around with Joplin and Swift.

Use the below method in an Xcode playground for example.

func uploadResourceTest()
{
    //some example image in the playground's resources
    let path = Bundle.main.url(forResource:"image", withExtension: "jpg")
    guard let path = path else {
        print("File not found")
        return
    }
    //read file
    let fileName = path.lastPathComponent
    var fileData: Data?
    do {
        fileData = try Data(contentsOf: path)
    } catch _ {
        fileData = nil
        return
    }
    
    guard let fileData = fileData else {
        print("File not readable")
        return
    }

    //prep params
    let parameters = [
        [
          "key": "props",
          "value": "{\"title\":\"my resource test\"}",
          "type": "text"
        ],
      [
        "key": "data",
        "src": path.absoluteString,
        "type": "file"
      ]
      ] as [[String : Any]]

    //add your token here:
    let token = "PASTE_YOUR_TOKEN_HERE"

    //setup data request
    let boundary = "Boundary-\(UUID().uuidString)"

    var error: Error? = nil
    var data = Data()
    for param in parameters {
      if param["disabled"] == nil {
        let paramName = param["key"]!
        data.append("--\(boundary)\r\n".data(using: .utf8)!)
        data.append("Content-Disposition:form-data; name=\"\(paramName)\"".data(using: .utf8)!)
        if param["contentType"] != nil {
            data.append("\r\nContent-Type: \(param["contentType"] as! String)".data(using: .utf8)!)
        }
        let paramType = param["type"] as! String
        if paramType == "text" {
            let paramValue = param["value"] as! String
            data.append("\r\n\r\n\(paramValue)\r\n".data(using: .utf8)!)
        } else {
            data.append("; filename=\"\(path.lastPathComponent as! String)\"\r\n".data(using: .utf8)!)
            data.append("Content-Type: \"content-type header\"\r\n\r\n".data(using: .utf8)!)
            data.append(fileData)
            data.append("\r\n--\(boundary)--\r\n".data(using: .utf8)!)
        }
      }
    }
     
    //setup request
    var request = URLRequest(url: URL(string: "http://localhost:41184/resources?token=\(token)")!,timeoutInterval: Double.infinity)
    request.addValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")

    request.httpMethod = "POST"
    request.httpBody = data

    //setup task
    let task = URLSession.shared.dataTask(with: request) { data, response, error in
      guard let data = data else {
        print(String(describing: error))
        return
      }
      print(String(data: data, encoding: .utf8)!)
    }

    //start...
    task.resume()
}
1 Like