Send Core Data object to post api Alamofire multipartFormData - swift

I have a screen on my app that I get some fields and save on my object Order. This object is my Core Data Object. After saving it, I need to send it to my backend through Alamofire POST multipartFormData.
The problem is that this is a Core Data Object (not Codable) and I need to send Data type on multipartFormData. How can I convert my object to Data? Is there another way of doing it?
What I've done:
let order = Order(context: DatabaseController.getContext())
order.orderItem = orderItem
order.product = product
order.value = value
order.date = date
Alamofire part:
Alamofire.upload (
multipartFormData: { multipartFormData in
multipartFormData.append(order, withName: "order")
},
to: url,
headers: headers,
encodingCompletion: { encodingResult in
The problem is how to put my object Order inside multipartFormData?
Could anyone help me please?
Updated:
Ok, sending the whole object didn't work, my api didn't accept, so I made a specific json only with the fields needed and turned it a Data type:
(PS: files are Data type from images user choose, even from camera or gallery)
var files = Dictionary<Data, String>()
var jsonFiles = [[String: String]]()
var jsonOrder = [String: Any]()
for file in files {
let dict : [String: String] = [ "orderImageIdLocal": uuidOrderImageIdLocal,
"orderItemAnalysisIdLocal": uuidAnalysisIdLocal,
"urlImageLocal": "\(imageId).jpg"]
jsonFiles.append(dict)
}
jsonOrder = [ "reason": "\(textViewReason)",
"orderImagess": jsonFiles,
"orderAnalysisId": "",
"orderIdLocal": "\(uuidAnaliseIdLocal)",
"orderId": "\(orderId ?? "")",
"typeSolicitation": "\(typeSolicitation)"]
Then I convert it to Data type like you said and send to Alamofire like above:
let orderData = try? JSONSerialization.data(withJSONObject: jsonOrder, options: .prettyPrinted) {
My problem now is that my api expect a zip file with those images user took from camera or gallery. So I am trying to use ZIPFoundation. I still don't know how to zip it and send. Should I zip each picture as Data type? Then transform zip file into Data type so I can send through multipartFormData.append?
I have tried: here and here

Here the code as an extension of NSManagedObject which creates dictionary from the attributes name.
extension NSManagedObject {
func toData() -> Data? {
let keys = Array(self.entity.attributesByName.keys)
let dict = self.dictionaryWithValues(forKeys: keys)
do {
let jsonData = try JSONSerialization.data(withJSONObject: dict, options: .prettyPrinted)
return jsonData
}
catch{}
return nil
}
}
Usage:
let jsonData = order.toData()
multipartFormData.append(jsonData, withName: "order")

Related

How to handle dynamic keys with single element as value in Swift

I'm having a problem figuring out how to handle dynamic keys with a single value one level deep. I've seen a few examples but they appear to handle other cases.
Here's an example.
[
{ "asdofjiodi": "asdofidj.com" },
{ "sadjlkj": "iejjol.com" },
{ "ijijwjljlijl": "adsijf.com" },
{ "jgncmkz": "mlkjaoijf.com" }
]
Any ideas on how I accomplish this with Codable or CodingKey? This is what I'd like for the end result.
["asdofidj.com", "iejjol.com", "adsijf.com", "mlkjaoijf.com"]
let url = Bundle.main.url(forResource: "file", withExtension: "json")!
let data = try! Data(contentsOf: url)
let decoder = JSONDecoder()
// NOTE: The below line doesn't work because I'm not sure how to do the encoding/decoding
try? decoder.decode([[String: String]].self, from: data)
First of all, is not a valid JSON. A "Valid JSON" can be a JSON Array (multiple json objects) or a single JSON object (starts and ends with { and })
After cleaning this...
You are trying to make a dictionary (json object) from a data. You don't need JSONDecoder to accomplish this. Try using JSONSerialization with jsonObject static function...
let data = Data("{\"asdofjiodi\": \"asdofidj.com\",\"sadjlkj\": \"iejjol.com\",\"ijijwjljlijl\": \"adsijf.com\",\"jgncmkz\": \"mlkjaoijf.com\"}".utf8)
let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any]
let values = json?.map({ $1 })
print(values)
I Hope I helped!
JSONSerialization Apple documentation
Here is a minimal working example, given the JSON in your question:
let json = """
[
{ "asdofjiodi": "asdofidj.com" },
{ "sadjlkj": "iejjol.com" },
{ "ijijwjljlijl": "adsijf.com" },
{ "jgncmkz": "mlkjaoijf.com" }
]
"""
let data = Data(json.utf8)
let decoder = JSONDecoder()
do {
let decodedJson = try decoder.decode([[String: String]].self, from: data)
let values = decodedJson.compactMap(\.values.first)
print(values)
} catch {
print("Error: \(error.localizedDescription)")
}
If this doesn't appear to work for you, it may be related to how you load in the JSON.

How to read variables from txt file?

For example i've got a build archive and i want it to read some variables from txt file. So the question is there any way to upload a txt file to iPhone device and how to read that variables?
In my project i've got this constants:
baseURL: String = "someUrl"
client_id: String
client_secret: String
authorize_uri: String
token_uri: String
scope: String
redirect_uris: [String]
secret_in_body: Bool
and for example i created a txt file with this strings and somehow uploaded it to my device
i want the builded project to read this file
You could create a new plist file in your project, then parse it into a dictionary at startup. Once there you can just refer to each value by its key
If you create an extension to Bundle()
extension Bundle {
func parsePlist(ofName name: String) -> [String: String]? {
// check if plist data available
guard let plistURL = Bundle.main.url(forResource: name, withExtension: "plist"),
let data = try? Data(contentsOf: plistURL)
else {
return nil
}
// parse plist into [String: Anyobject]
guard let plistDictionary = try? PropertyListSerialization.propertyList(from: data, options: [], format: nil) as? [String: String] else {
return nil
}
return plistDictionary
}
}
then you can load it into a dictionary like this
var dict = Bundle().parsePlist(ofName: "plistName")!
Xcode does a nice job of allowing you to edit the plist, and it'll be automatically included in your build

Pass an uploaded image directly from json file

I am trying to upload an image from my app to my heroku backend and then pass that to Stripe for verification. Here is my swift code to upload and pass the image:
#IBAction func registerAccountButtonWasPressed(sender: UIButton) {
let dob = self.dobTextField.text!.components(separatedBy: "/")
let URL = "https://splitterstripeservertest.herokuapp.com/account/create"
var imageData = UIImageJPEGRepresentation(photoID,1)
let params = ["year": UInt(dob[2])! as UInt] as [String : Any]
let manager = AFHTTPSessionManager()
let operation = manager.post(URL, parameters: params, constructingBodyWith: { (formData: AFMultipartFormData!) -> Void in
formData.appendPart(withFileData: imageData!, name: "file", fileName: "photoID.jpg", mimeType: "image/jpeg")
}, success: { (operation, responseObject) -> Void in
print(responseObject!)
}) { (operation, error) -> Void in
self.handleError(error as NSError)
}
}
I've deleted the list of params above out and left one for readability.
Is there a way to then receive this file and upload it to stripe without having to save it by passing the file parameter? like so:
Stripe::FileUpload.create(
{
:purpose => 'identity_document',
:file => params[file]
},
{:stripe_account => params[stripe_account]}
)
Also in the stripe docs it says to upload the file to 'https://uploads.stripe.com/v1/files' but then shows code to put in your backend, does Stripe::FileUpload.create do the uploading to stripe for me?
Any insight on either would be great thanks.
You need to first upload the file to Stripe's API using the "create file upload" call. You can then use the file upload's ID (file_...) to update the account's legal_entity.verification.document attribute.
(This process is explained here:
https://stripe.com/docs/connect/identity-verification#uploading-a-file
https://stripe.com/docs/connect/identity-verification#attaching-the-file)
Since the file is provided by the user, you have two choices for creating the file upload:
have your app upload the file to your backend server, then on your backend, use the file to create the file upload
create the file upload directly from the app (using your publishable API key), and send the resulting file_upload's ID (file_...) to your backend
Here's an example for creating file uploads client-side, using jQuery: https://jsfiddle.net/captainapollo/d8yd3761/.
You could do the same thing from your iOS app's code. Basically all you need to do is send a POST request to https://uploads.stripe.com/v1/files with an Authorization header with value Bearer pk_... (where pk_... is your publishable API key) and type multipart/form-data, with the file's contents as the request's body. This blog post should be helpful for sending multipart/form-data requests using Swift: http://www.kaleidosblog.com/how-to-upload-images-using-swift-2-send-multipart-post-request
Thanks to #Ywain I was able to come up with this solution for IOS Swift 4. I created the file upload directly from the app and retrieved the file_upload's ID to send to my backend to attach to the Connect Account. I did this by importing Alamofire and SwiftyJSON.
let heads = ["Authorization": "Bearer \(stripePublishableKey)"]
let imageData = image!.jpegData(compressionQuality: 1.0)
let fileName = "\(NSUUID().uuidString).jpeg"
Alamofire.upload(multipartFormData: { multipart in
multipart.append(imageData!, withName: "file", fileName: fileName, mimeType: "image/jpeg")
multipart.append(("identity_document").data(using: .utf8)!, withName :"purpose")
}, to: fileUrl!, method: .post, headers: heads) { encodingResult in
switch encodingResult {
case .success(let upload, _, _):
upload.responseJSON { answer in
let value = JSON(answer.value!)
let FileID = value["id"].stringValue
// Use fileID and Connect Account ID to create params to send to backend.
let params: [String: Any] = [
"acct_id": ConnectID!,
"fileID": FileID,
]
print("statusCode: \(String(describing: answer.response?.statusCode))")
}
case .failure(let encodingError):
print("multipart upload encodingError: \(encodingError)")
}
}

How do I convert a file into a JSON Object in SwiftyJson

I am trying to import Json data from a user uploaded txt file into a standard object that I can use via SwiftyJson framework
Here is the contents of the text fie:
{
"String": "answer",
"String2": "answer2"
}
I have successfully read it and turned it into a String file using:
let openPanel = NSOpenPanel()
let arrayOfExtensions = ["txt"]
openPanel.allowedFileTypes = arrayOfExtensions
let result = openPanel.runModal()
if result == NSFileHandlingPanelCancelButton {
return
}
let fileUrl = openPanel.URL
do {
let stringResult = try String(contentsOfURL: fileUrl!, encoding: NSUTF8StringEncoding)
print (stringResult)
completionhandler(retrievedData: stringResult, error: nil)
I am trying to convert this into a JSON object using:
let jsonFile = JSON(contentsOfFile)
The problem is that the resulting JSON object created appears to be blank for all the fields except rawvalue.
Here is the screenshot from the debug console.
How to I sucessfully read in the string from the file and then make it populate via SwiftJson correctly?
The problem above was that I was using the wrong method to parse it into JSON.
SwiftyJSON seems to be badly documented hence others had a similar problem.
The correct way to do this is to use :
let jsonFile = JSON.parse(string:contentsOfFile)

Content Blocker extension with a String instead of a file

I'm using the function NSItemProvider(contentsOfURL: NSBundle.mainBundle().URLForResource("blockerList", withExtension: "json") in a content blocker extension.
The thing is that all my rules are stored in a few dictionaries and, when I'm using this function, it's always because the rules have changed. I'm currently creating a String from these dictionaries that looks like "[{\"trigger\": {\"url-filter\": \"webiste.com\"},\"action\": {"\type\": \"css-display-none\",\"selector\":\".testContentBlocker\"}}]"and I have to transform it in a JSON file to finally be able to use it in the function written previously described.
Instead of having to put the String in a JSON file to be able to use it, could I do something simpler to use NSItemProvider()?
By loading the extension up in the debugger (and by using Hopper), you can see that NSItemProvider(contentsOfURL:) is simply registering to provide data from the file's contents with type public.json.
(lldb) po attachment
<NSItemProvider: 0x7fd4c250f2a0> {types = (
"public.file-url",
"public.json"
)}
It's roughly equivalent to this:
// possible implementation of NSItemProvider.init(contentsOfURL:)
convenience init?(contentsOfURL fileURL: NSURL!)
{
self.init(item: fileURL, typeIdentifier: (fileURL.fileURL ? kUTTypeFileURL : kUTTypeURL) as String)
let type = UTTypeCreatePreferredIdentifierForTag(
kUTTagClassFilenameExtension, fileURL.pathExtension!, nil)?.takeRetainedValue() as! String
registerItemForTypeIdentifier(type) { completionHandler, expectedValueClass, options in
let data = try! NSData(contentsOfURL: fileURL, options: .DataReadingMappedAlways)
completionHandler(data, nil)
}
}
So you can do this yourself, in memory:
// get the data
let data = NSData(contentsOfURL: NSBundle.mainBundle().URLForResource("blockerList", withExtension: "json")!)
// put the data in an item provider
let attachment = NSItemProvider(item: data, typeIdentifier: kUTTypeJSON as String)
// send the item to Safari
let item = NSExtensionItem()
item.attachments = [attachment]
context.completeRequestReturningItems([item], completionHandler: nil);
If you want to provide content dynamically, you can use NSJSONSerialization to transform a dictionary into NSData at runtime.