Share Array of UIImages With Text via UIActivityViewController in Swift - arrays

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

Related

UIActivityViewController for a kml and text files

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)
}

CKModifyBadgeOperation is deprecated in iOS 11. Anyone know an alternative approach?

I have searched, and I cannot find an example. I have also tried adapting this code (recommended elsewhere (CloudKit won't reset my badge count to 0):
func resetBadgeCounter() {
let badgeResetOperation = CKModifyBadgeOperation(badgeValue: 0)
badgeResetOperation.modifyBadgeCompletionBlock = { (error) -> Void in
if error != nil {
print("Error resetting badge: \(String(describing: error))")
}
else {
UIApplication.shared.applicationIconBadgeNumber = 0
}
}
CKContainer.default().add(badgeResetOperation)
}
This works for now, but is no longer supported, and may go away soon.
I thought perhaps I should use a CKModfyRecordsOperation or some other CKDatabaseOperation, but I can't even guess how.
I hope this helps someone as there doesn't seem to be another solution/workaround to this.
Create a Notification Service Extension (UNNotificationServiceExtension) for your app. It is fairly straightforward and is described in detail in Modifying Content in Newly Delivered Notifications. You might already have one, if you do any kind of processing on incoming notifications.
Within the extension, you typically do all the processing of the notification in override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: #escaping (UNNotificationContent) -> Void). The request comes with the content of the notification, which includes the badge number. You can then modify that and pass on to the contentHandler to be displayed. (If you are unsure how to do this, check the link above; it has detailed instructions and code.)
To keep track and reset the badge number when needed, you need to take advantage of an app group (e.g. group.com.tzatziki). This way you can share data between the app and the extension. Creating an app group in Xcode can be done by adding the relevant capability and is explained in App Extension Programming Guide: Handling Common Scenarios.
Use a storage mechanism (within the app group) to keep track of the badge count, e.g. UserDefaults, and use it in the extension to update the badge count. In the aforementioned override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: #escaping (UNNotificationContent) -> Void), you could write something like this:
let badgeNumber = UserDefaults(suiteName: "group.com.tzatziki")?.value(forKey: "badgeNumber") as? NSNumber ?? NSNumber(integerLiteral: 0)
let badgeNumberInt = badgeNumber.intValue + 1
notificationContent.badge = NSNumber(value: badgeNumberInt)
UserDefaults(suiteName: "group.io.prata")?.setValue(notificationContent.badge, forKey: "badgeNumber")
where notificationContent is an instance of UNMutableNotificationContent, originating from request.content.mutableCopy() as? UNMutableNotificationContent
Now, the only thing left to do is to reset the badge count in your app, in the shared UserDefaults and in UIApplication.shared.applicationIconBadgeNumber.
Cheers!
It was reported here: https://levelup.gitconnected.com/how-to-reset-badge-value-of-cloudkit-remote-notification-on-ios-ipados-15-46726a435599 that Apple is aware that they need to maintain CKModifyBadgeOperation until they come up with a replacement API. There is no other solution to this problem because CloudKit resets the badge number whenever it pushes a new notification.
It's probably best to just track the items you are counting and set the app badge count yourself. I reference a local database that has my items in it, and I return the total and set my app badge accordingly.

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 :)

windows phone 8 official supported file icons

Does anyone know how to display the supported file type icons in WP8 ?
I have links on a xaml page which by clicking opens pdf, excel, word, powerpoints etc and these work nicely.
I would like to get the "official" icon which I can see for example in the office app in WP8 next to the link to indicate what kind of a file is in question.
I know from the file mimeType (for example application/pdf) what file is in question
Does anyone know how to accomplish this ?
Thanks,
Jani
Firstly, you need to get app product id, then use below code to get stream for official icon.
But you couldn't get icon of apps which are not scanned by InstallationManager.FindPackages().
IEnumerable<Windows.ApplicationModel.Package> apps = Windows.Phone.Management.Deployment.InstallationManager.FindPackages();
foreach(var app in apps)
{
if(app.Id.ProductId.Equals("APP_ID", StringComparison.InvariantCultureIgnoreCase)
{
var token = p.GetThumbnailToken();
var name = app.Id.Name + SharedStorageAccessManager.GetSharedFileName(token);
await SharedStorageAccessManager.CopySharedFileAsync(ApplicationData.Current.LocalFolder, name, NameCollisionOption.ReplaceExisting, token);
var file = await ApplicationData.Current.LocalFolder.GetFileAsync(name);
var randomStream = await file.OpenReadAsync();
Stream stream = randomStream.AsStream();
}
}

Resources