UIActivityViewController for a kml and text files - file

I using following code to save couple of files on an iPhone app. One is a straight text file with a list of longitude and latitudes. The second file is same with KML code to display the Placemarks with the same coordinates. The code works to generate the two files as expected with .txt extensions. What I would like to do is to generate two different extensions, .txt for the straight text file and .kml for the second one. The reason for .kml extension is to be able to display those coordinates in Google Earth app on iPhone. Any help would be appreciated...
let activityViewController = UIActivityViewController(activityItems: [kml, locations], applicationActivities: nil)
present(activityViewController, animated: true, completion: nil)``
The two arrays activityViewController contain the kml and text code respectively.

This is the way I have solved it... I am sure it is not the most efficient way but it works :-). Still looking for a more efficient way as this method generates a temporary file adding to app overhead... Also, the user has to save the file twice, once for the kml and once again for the text file. So anyway, hope it helps others.
let kmlFile = "soofie_loc.kml"
let tmpDir = FileManager.default.temporaryDirectory
var fileURL : URL?
fileURL = tmpDir.appendingPathComponent(kmlFile)
let data = Data(kml.utf8)
do {
try data.write(to: fileURL! , options: .atomic)
} catch {
print(error)
}
//let activityViewController = UIActivityViewController(activityItems: [kml, locations], applicationActivities: nil)
if ( !kmlOption ) {
let activityViewController = UIActivityViewController(activityItems: [locations], applicationActivities: nil)
present(activityViewController, animated: true, completion: nil)
}
else {
let activityViewController = UIActivityViewController(activityItems: [fileURL!], applicationActivities: nil)
present(activityViewController, animated: true, completion: nil)
}

Related

Share Array of UIImages With Text via UIActivityViewController in Swift

I am using UIActivityViewController to share text and images both in Swift using the following code:
#IBAction func shareTapped(_ sender: Any) {
var text = "I am enjoying swift"
var image = UIImage(systemName: "person")
let itemsToShare = [text, image!]
let activityViewController = UIActivityViewController(activityItems: itemsToShare, applicationActivities: nil)
activityViewController.popoverPresentationController?.sourceView = self.view
self.present(activityViewController, animated: true, completion: nil)
}
This works fine when I share it with Skype but when I try to share it with WhatsApp, it only shares text.
There are two questions now:
Can I share both image and text on WhatsApp or iOS does not support this?
Can I share the array of images along with text? If yes, how can I achieve this?
Sorry, currently there is no way to share both image and text together on WhatsApp.
However, you can post one at a time. It is a limitation of WhatsApp under iOS, it is nothing related to UIActivityViewController.
For more read from here

How to segue a JSON file in swift?

I am building an app where people need to fill out a form and then it creates an HTTP post where the API will return a Json file to the app with data I need. Everything is working fine with accessing the API however I want to parse the data in another view controller. How can I access the JSON file from another view controller?
let task = session.dataTask(with: request as URLRequest, completionHandler: { data, response, error in
guard error == nil else {
return
}
guard let data = data else {
return
}
do {
//create json object from data
if let json = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as? [String: Any] {
print(json)
^^^^^^^
How do I take this JSON file to the next view controller so I dont have to do the parsing below?
let jsonData = json
let ratesJson = jsonData["rates"]!
let rates = ratesJson as! NSArray
print("Rates: \(rates)")
print("*************")
print(rates.count)
print("*************")
for item in 0..<rates.count {
let specificRate = rates[item]
let price = (specificRate as AnyObject)["amount_local"]!
let provider = (specificRate as AnyObject)["provider"]!
print("--\(item)--")
print("Price: \(price!)")
print("Provider: \(provider!)")
}
}
} catch let error {
print(error.localizedDescription)
}
})
I assume by your comment, your intention is to actually pass a JSON object, not a JSON file to the next view controller.
Therefore, you just need to pass the JSON object to your segue and assign it as a property to the next view controller.
Since the question was very open ended, here is one possible solution and interpretation.
Example:
//Sample snippet from code from question
if let json = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as? [String: Any] {
performSegue(withIdentifier: "MySegue", sender: json);
}
override func prepare(for segue: UIStoryboardSegue!, sender: Any?) {
if (segue.identifier == "MySegue") {
let vc = segue.destinationViewController as! RandomViewController
vc.json = sender as! [String:Any];
}
}
One of the things that usually cause bugs in programs is state. What you are asking here in my opinion is state.
What I would do is i would write a generic function ( Swift encourages functional programming in a lot of cases, have a look at: swift map, filter etc. that deals with the HTTP request and returning the data in a closure.
This closure will then live within the view controller you want your json object in, thus solving your problem of accessing said data.
Now I don't believe that is the right approach either. I would create an object for your specific data returned, like say a "struct Person" or whatever your needs are.
This has 2 advantages:
In swift 3.1 you can have a custom init() method that parses the JSON safely ( remember, optionals! ) and populates the structs data accordingly
In swift 4.0 and Xcode9.0 You will be able to morph the struct you just created to conform to the Codable and Decodable protocols with wich json parsing will be greatly simplified, as explained here: ultimate swift json guide
Hope this was of any help.

Firebase Storage Async Image Download not in the right order

I am met with a problem when I tried to download a batch of images off firebase storage. Basically, because the file sizes differ, the images are not appended to the image array properly causing the images to be in the wrong order that I wanted. Below is the code
import Foundation
import FirebaseStorage
class GalleryCellDetailedData {
var selectedGallery:String?
var count:Int
init(selectedGallery:String?,count:Int){
self.selectedGallery = selectedGallery
self.count = count
}
func addImages(completion:(data:[NSData])->()){
var datas = [NSData]()
let myGroup = dispatch_group_create()
for i in 0..<count {
dispatch_group_enter(myGroup)
getImage(i, completion: { (image:NSData) in
datas.append(image)
print("Finish Request \(i)")
dispatch_group_leave(myGroup)
})
}
dispatch_group_notify(myGroup, dispatch_get_main_queue(), {
completion(data: datas)
})
}
private func getImage(number:Int, completion:(image:NSData)->()){
let storage = FIRStorage.storage()
//Reference to Firebase Profile Picture Storage
let storageRef = storage.referenceForURL("gs://mannacatering-addcb.appspot.com")
print("Initiating Image Download")
let galleryPicRef = storageRef.child("Gallery/\(selectedGallery!)/g\(String(number)).jpg")
//Download Image
galleryPicRef.dataWithMaxSize(1 * 1024 * 1024) { (data, error) -> Void in
if (error != nil) {
print("fail to download image")
}
dispatch_async(dispatch_get_main_queue(), {
print("Dispatching image")
completion(image:data!)
})
}
}
}
I split them up into 2 separate functions because I tried to manage them in a single function and the order is in a mess as well and I thought this might work but apparently not.
Instead of storing your data in an array, store it in a dictionary. The key can be the number i or however you want to refer to an image when you use it.
I recommend taking an approach similar to Storing multiple images into firebase and getting urls, where you store the URLs in the Realtime Database, and use that as your source of truth for ordering, display, etc. It's far easier and makes for better apps :)

How to save image from swift to SQL Server database using webservice?

I converted ipad signature to png image successfully using UIImagePNGRrepresentation(Image). Now I want to store this Image from swift to a SQL Server database using a web service. I have not any idea about how do this?
This is my swift code
UIGraphicsBeginImageContextWithOptions(self.signatureMainImageview.bounds.size, false, 0.0)
self.signatureMainImageview.image?.drawInRect(CGRectMake(0, 0, self.signatureMainImageview.frame.size.width, self.signatureMainImageview.frame.size.height))
let SaveImage: UIImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
let image = UIImagePNGRepresentation(SaveImage)
var CardDataObj = structCardData()
CardDataObj.CustomerSignature = image!
let requestCardData = NSMutableURLRequest(URL: NSURL(string: "http://URL")!)
requestCardData.HTTPMethod = "POST"
let postString = CardDataObj.jsonRepresentation
requestCardData.HTTPBody = postString.dataUsingEncoding(NSUTF8StringEncoding)
let task = NSURLSession.sharedSession().dataTaskWithRequest(requestCardData) {
data, response, error in
if error != nil {
print("error=\(error)")
return
}
print("response = \(response)")
let responseString = NSString(data: data!, encoding: NSUTF8StringEncoding)
print("responseString = \(responseString)")
}
Now I want to know how to get this image in webservice? which datatype use in webservice for image? which datatype use in sql for image? How to send this image to sql?
Rather than a data task you need an upload task. Either uploadTaskWithRequest:fromData:completionHandler or its file or stream variants
In order to begin the task you need to call task.resume()
It also helps to retrieve the response if you cast to HTTPURLResponse like so:
if let response = response as? NSHTTPURLResponse {
response.statusCode
response.allHeaderFields
}
I wrote a blogpost on uploading using a stream, which might be of some use. Here's also a more general post about NSURLSession.
The first blogpost linked to will give you some server-side code in PHP to receive a stream, but if you are uncertain about what to do on the SQL I'd recommended breaking this question into two and asking that question separately.

Upload file in GAE Go

I am trying to upload a file in my GAE app. How do I the upload of a file in Google App Engine using Go and using the r.FormValue()?
You have to go through the Blobstore Go API Overview to get an idea and there is a full example on how could you store & serve user data on Google App Engine using Go.
I would suggest you to do that example in a completely separate application, so you'll be able to experiment with it for a while before trying to integrate it to your already existing one.
I managed to solve my problem by using the middle return param, "other". These code below are inside the upload handler
blobs, other, err := blobstore.ParseUpload(r)
Then assign corresponding formkey
file := blobs["file"]
**name := other["name"]** //name is a form field
**description := other["description"]** //descriptionis a form field
And use it like this in my struct value assignment
newData := data{
Name: **string(name[0])**,
Description: **string(description[0])**,
Image: string(file[0].BlobKey),
}
datastore.Put(c, datastore.NewIncompleteKey(c, "data", nil), &newData )
Not 100% sure this is the right thing but this solves my problem and it is now uploading the image to blobstore and saving other data and blobkey to datastore.
Hope this could help others too.
I have tried the full example from here https://developers.google.com/appengine/docs/go/blobstore/overview, and it worked fine doing the upload in blobstore and serving it.
But inserting extra post values to be saved somewhere in the datastore erases the values of "r.FormValue() "? Please refer to the code below
func handleUpload(w http.ResponseWriter, r *http.Request) {
c := appengine.NewContext(r)
//tried to put the saving in the datastore here, it saves as expected with correct values but would raised a server error.
blobs, _, err := blobstore.ParseUpload(r)
if err != nil {
serveError(c, w, err)
return
}
file := blobs["file"]
if len(file) == 0 {
c.Errorf("no file uploaded")
http.Redirect(w, r, "/", http.StatusFound)
return
}
// a new row is inserted but no values in column name and description
newData:= data{
Name: r.FormValue("name"), //this is always blank
Description: r.FormValue("description"), //this is always blank
}
datastore.Put(c, datastore.NewIncompleteKey(c, "Data", nil), &newData)
//the image is displayed as expected
http.Redirect(w, r, "/serve/?blobKey="+string(file[0].BlobKey), http.StatusFound)
}
Is it not possible to combine the upload with regular data? How come the values of r.FormValue() seems to disappear except for the file (input file type)? Even if I would have to force upload first before associating the blobkey, as the result of the upload, to other data, it would not be possible since I could not pass any r.FormValue() to the upload handler(which like I said becomes empty, or would raised an error when accessed prior the blobs, _, err := blobstore.ParseUpload(r) statement). I hope someone could help me solve this problem. Thank you!
In addition to using the Blobstore API, you can just use the Request.FormFile() method to get the file upload content. Use net\http package documentation for additional help.
Using the Request directly allows you to skip setting up an blobstore.UploadUrl() before handling the upload POST message.
A simple example would be:
func uploadHandler(w http.ResponseWriter, r *http.Request) {
// Create an App Engine context.
c := appengine.NewContext(r)
// use FormFile()
f, _, err := r.FormFile("file")
if err != nil {
c.Errorf("FormFile error: %v", err)
return
}
defer f.Close()
// do something with the file here
c.Infof("Hey!!! got a file: %v", f)
}

Resources