Swift : Share text through Twitter - xcode

So basically I am making an event app. Everything has been going smoothly but there's just sharing the event to twitter.
I have searched the internet but all I am getting is using the native app of twitter which I don't want. I want to use the browser to tweet.
I have implemented this method for FB sharing.
Any idea would help me a lot.
let content = FBSDKShareLinkContent()
content.contentURL=NSURL(string: "http://facebook.com")
content.imageURL = NSURL(string: "http://facebook.com")
content.contentTitle = "Shou 3emlin test app "
content.contentDescription = "testing testing testing"
let shareDialog = FBSDKShareDialog()
shareDialog.fromViewController = self
shareDialog.mode=FBSDKShareDialogMode.Browser
shareDialog.shareContent = content
if !shareDialog.canShow() {
shareDialog.mode=FBSDKShareDialogMode.Native
shareDialog.shareContent = content
}
if shareDialog.canShow() {
shareDialog.show()
}

Put this in an action method of a button or in the method where you want to use the browser to tweet your text Swift 3.0:
let tweetText = "your text"
let tweetUrl = "http://stackoverflow.com/"
let shareString = "https://twitter.com/intent/tweet?text=\(tweetText)&url=\(tweetUrl)"
// encode a space to %20 for example
let escapedShareString = shareString.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryAllowed)!
// cast to an url
let url = URL(string: escapedShareString)
// open in safari
UIApplication.shared.openURL(url!)
Result:

Take a look at Fabric.io. This SDK allows you to compose tweets directly from your app.
let composer = TWTRComposer()
composer.setText("just setting up my Fabric")
composer.setImage(UIImage(named: "fabric"))
// Called from a UIViewController
composer.showFromViewController(self) { result in
if (result == TWTRComposerResult.Cancelled) {
print("Tweet composition cancelled")
}
else {
print("Sending tweet!")
}
}

let tweetText = "hy"
let tweetUrl = "http://rimmi/"
let shareString = "https://twitter.com/intent/tweet?text=\(tweetText)&url=\(tweetUrl)"
// encode a space to %20 for example
let escapedShareString = shareString.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryAllowed)!
// cast to an url
let url = URL(string: escapedShareString)
// open in safari
UIApplication.shared.openURL(url!)

#ronatory's solution worked like charm. It also opens a Twitter application if it's already installed on the user's device.
For swift 5+ use UIApplication.shared.open(url!) instead of UIApplication.shared.openURL(url!) as it's deprecated.

Related

In Swiftui, how to check the content from a URLsession?

I am using SwiftUI in Xcode 11, trying to check the content of a .txt file from the internet.
The problem is that the URLSession.shared.downloadTask takes time to finish. The code to check the content is always performed before the download is finished. Can anyone help me please? Thanks very much.
Sorry, forgot to add some codes.
let url = URL(string: "https://www.myweb.com/myfile.txt”)!
var myweb = “test”
URLSession.shared.downloadTask(with: url) { localURL, response, error in
if let localURL = localURL {
do { try myweb = String(contentsOf: localURL)}
catch { print (“test”) }
}
}.resume()
if myweb != “test” { Call some function here}
I assume that you need to create ViewModel with Published property and change it flag on true statement if downloadTask has finished. Use this property inside View

Making a GET request with swift 4 works in playgrounds but not in project

Using Swift 4 and Xcode 10
I am trying to make a GET request to an API and get the results in json, and my code works perfectly in my playground, but when I copy it over to my app, I get a "Program ended with exit code: 0" error.
I'd like to make this a function I can call and change up the headers, httpMethod, credentials, actionURL, etc.. so I can reuse it for different calls to this API.
This is my first shot at this, and have been searching all over.
1) This is where I borrowed most of the code for this part of the project. Error parsing JSON in swift and loop in array
2) I tried using the advice in this video to build a struct for the data. https://www.youtube.com/watch?v=WwT2EyAVLmI&t=105s
Not sure if it is something on the swift side, or the xcode side...
import Foundation
import Cocoa
// removed in project, works in playgrounds
//import PlaygroundSupport
func makeGetCall() {
// Set up the URL request
let baseURL: String = "https://ws.example.net/v1/action"
guard let url = URL(string: baseURL) else {
print("Error: cannot create URL")
return
}
// set up the session
let config = URLSessionConfiguration.default
let session = URLSession(configuration: config)
// set up auth
let token = "MYTOKEN"
let key = "MYKEY"
let loginString = String(format: "%#:%#", token, key)
let loginData = loginString.data(using: String.Encoding.utf8)?.base64EncodedString()
// make the request
var request = URLRequest(url: url)
request.httpMethod = "GET"
request.setValue("application/json", forHTTPHeaderField: "Accept")
request.addValue("Basic \(loginData!)", forHTTPHeaderField: "Authorization")
let task = session.dataTask(with: request) {
(data, response, error) in
// check for any errors
guard error == nil else {
print("error calling GET")
print(error!)
return
}
// make sure we got data
guard let responseData = data else {
print("Error: did not receive data")
return
}
// parse the result as JSON, since that's what the API provides
do {
guard let apiResponse = try JSONSerialization.jsonObject(with: responseData, options: [])
as? [String: Any] else {
print("error trying to convert data to JSON")
return
}
// let's just print it to prove we can access it
print(apiResponse)
// the apiResponse object is a dictionary
// so we just access the title using the "title" key
// so check for a title and print it if we have one
//guard let todoTitle = todo["title"] as? String else {
// print("Could not get todo title from JSON")
// return
//}
//print("The title is: " + todoTitle)
} catch {
print("error trying to convert data to JSON")
return
}
}
task.resume()
}
makeGetCall()
// removed in project, works in playgrounds
//PlaygroundPage.current.needsIndefiniteExecution = true
In playgrounds I receive the json response as expected, but when I copy the code over to my project I receive an error.
Expected output example:
["facet": <__NSArrayI 0x7fb4305d1b40>(
asnNumber,
assetPriority,
assetType,
etc...
"Program ended with exit code: 0" is not an error; it just means the program came to an end. In fact, it is the opposite of an error, since it means the program came to an end in good order.
So, what kind of project template did you use? I'm guessing you used a Cocoa command-line tool. In that case, the problem is that simply you have no runloop, so we reach the last line and come to an end before any asynchronous stuff (e.g. networking) can take place.
This is exactly parallel to what you had to do in your playground. You couldn't network asynchronously without needsIndefiniteExecution; the playground needs to keep running after it has reached the last line, to give the networking a chance to happen. In the same way, you need the runloop in the command-line tool to keep going after it has reached the last line, to give the networking a chance to happen.
For how to give your command-line tool a runloop, see for example Run NSRunLoop in a Cocoa command-line program
Alternatively, don't use a command-line tool. Make an actual app. Now the app persists (until you quit it), and asynchronous networking just works.

How to programmatically make phone call from apple watch app?

My Watch app has list of contact and I want open initiate call from watch. like we do in iPhone device.
I have tried this solution but not working for.
I have fixed it using following method
var phone = "123456"
if let telURL=NSURL(string:"tel:\(phone)") {
let wkExt=WKExtension.sharedExtension()
wkExtension.openSystemURL(telURL)
}
Swift 4 / WatchOS 5
var phone = "1234567890"
if let telURL = URL(string: "tel:\(phone)") {
let wkExt = WKExtension.shared()
wkExt.openSystemURL(telURL)
}

Security Scoped Bookmark in App Extension

I am creating a TodayWidget app extension which displays information about user selected folders outside the application directory.
In my main application I am able to use powerbox via NSOpenPanel to select the folder. I can then save a security scoped bookmark to the user defaults of the app group container accessible by my TodayWidget.
The TodayWidget can read in the bookmark data, but when it calls URLByResolvingBookmarkData, it errors out with:
The file couldn’t be opened because it isn’t in the correct format.
Both my main application and the TodayWidget have the below entitlements:
com.apple.security.files.bookmarks.app-scope
com.apple.security.files.user-selected.read-only
From Apple's documentation, only the application that created the security scoped bookmark can use it. I guess these means embedded applications aren't allowed?
I've looked in to using XPC, but that doesn't really help the problem, as XPC can't use security scoped bookmark either, only a normal bookmark. As soon as the computer is restarted, the XPC process will lose access to the directories.
Really all I need is a way for the XPC process to get read access to user specified directories. Is there a way without having to relaunch my main application every restart of the computer?
You have probably already solved this or moved on. But for all those that are attempting something similar I will leave this here for them. In order to access security scoped bookmarks in a different app they have to be transferred as NSData and re-resolved in the other application.
In my case I show an open dialog in the main application and then save the scoped bookmark into a shared NSUserDefaults suite. The other applications are also part of that suite and then access the container of NSData's and resolve them into usable NSURL's
Here are the relevant bits of code:
//Inside my main application's open function
... get url from NSOpenPanel
BookmarkUtils.saveURLForOtherApplications(openPanel.URL!)
//Inside BookmarkUtils.swift
static func saveURLForOtherApplications(url:NSURL)->Bool{
let defaults = NSUserDefaults(suiteName: <#Suite-Name#>)!
//I store them as a dictionary of path->encoded URL
let sandboxedBookmarks:NSMutableDictionary
if let prevBookmarks = defaults.objectForKey(kSandboxKey) as? NSDictionary{
sandboxedBookmarks = NSMutableDictionary(dictionary:prevBookmarks)
}
else{
sandboxedBookmarks = NSMutableDictionary()
}
if let shareData = BookmarkUtils.transportDataForSecureFileURL(url){
sandboxedBookmarks.setObject(shareData, forKey:url.path!)
defaults.setObject(sandboxedBookmarks, forKey:kSandboxKey)
defaults.synchronize()
return true
}
else{
println("Failed to save URL Data");
return false
}
}
static func transportDataForSecureFileURL(fileURL:NSURL)->NSData?{
// use normal bookmark for encoding security-scoped URLs for transport between applications
var error:NSError? = nil
if let data = fileURL.bookmarkDataWithOptions(NSURLBookmarkCreationOptions.allZeros, includingResourceValuesForKeys:nil, relativeToURL:nil, error:&error){
return data;
}
else{
println("Error creating transport data!\(error)")
return nil
}
}
So then in my extension (Today view in my case) I do something like this...
class TodayViewController: ...
...
override func viewDidLoad() {
super.viewDidLoad()
var status = [MyCoolObjects]()
for url in BookmarkUtils.sharedURLSFromApp(){
BookmarkUtils.startAccessingSecureFileURL(url)
status.append(statusOfURL(url))
BookmarkUtils.stopAccessingSecureFileURL(url)
}
self.listViewController.contents = status
}
And the relevant bookmark looks something like:
static func sharedURLSFromApp()->[NSURL]{
var urls = [NSURL]()
if let defaults = NSUserDefaults(suiteName: <#Suite-Name#>){
if let prevBookmarks = defaults.objectForKey(kSandboxKey) as? NSDictionary{
for key in prevBookmarks.allKeys{
if let transportData = prevBookmarks[key as! NSString] as? NSData{
if let url = secureFileURLFromTransportData(transportData){
urls.append(url)
}
}
}
}
}
return urls
}
static func secureFileURLFromTransportData(data:NSData)->NSURL?{
// use normal bookmark for decoding security-scoped URLs received from another application
var bookmarkIsStale:ObjCBool = false;
var error:NSError? = nil;
if let fileURL = NSURL(byResolvingBookmarkData: data, options: NSURLBookmarkResolutionOptions.WithoutUI, relativeToURL: nil, bookmarkDataIsStale: &bookmarkIsStale, error: &error){
return fileURL
}
else if(bookmarkIsStale){
println("Bookmark was stale....")
}
else if let resolveError = error{
println("Error resolving from transport data:\(resolveError)")
}
return nil
}
This solution works for me. Once you resolve the shared URL you can then create a bookmark for that application and save it for later if so desired.There may be better ways out there, hopefully Apple works on this as it is currently painful to share permissions with extensions.
Actually you don't need to startAccessingSecureFileURL and it will return fail to start. just transform bookmark data to url will gain access.

Detecting Gmail attachment downloads

Is there a way to detect if a particular file that is being downloaded is a Gmail attachment?
I am looking for a way to write a Greasemonkey script which would help me organize the downloads, based on their download sources, say Gmail email attachments would have a different behavior from other stuff.
So far, I've found out that attachments redirect to https://mail-attachment.googleusercontent.com/attachment/u/0/ , which I guess is not sufficient.
EDIT
Since an add-on would be more powerful than a userscript, I've decided to pursue the Add On idea. However, the problem of detection remains unsolved.
This is too complicated for just one question; it has at least these major parts:
Do you want to redirect downloads when the user clicks, or automatically download select files? Clarify the question.
Your GM script must identify the appropriate download links, and on which pages, and for which views? For gMail, this is not a trivial task, and the question needs to be clearer. It's worthy of a whole question just on this issue given the variety of views and AJAX involved.
Once identified, the script probably needs to intercept clicks on those links. (Depends on your goal (clarify!) and what the Firefox extension can do.)
Greasemonkey needs to interact with an extension that either intercepts the user-initiated download, or allows for an automatic download. I've detailed the auto-download approach, below.
Once your script has identified the appropriate file URLs and/or links (Open a new question for more help with that, and include pictures of the types of pages and links you want.), it can interface with a Firefox add-on, like the one below, to automatically save those files.
Automatically saving files from Greasemonkey with the help of an additional Add-on:
WARNING: The following is a working proof of concept for education only. It has no security features, and if you use it as-is, for actual surfing, some webpage or script writer or extension writer will use it to completely pwn your computer.
If you use the Add-on builder or SDK to install or "Test" the DANGER. DANGER. DANGER. File download utility,
Then you can use a Greasemonkey script, like this, to automatically save files:
// ==UserScript==
// #name _Call our File download add-on to trigger a file download.
// #include https://mail.google.com/mail/*
// #include https://stackoverflow.com/questions/14440362/*
// #require http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js
// #grant GM_addStyle
// ==/UserScript==
/*- The #grant directive is needed to work around a design change
introduced in GM 1.0. It restores the sandbox.
*/
var fileURL = "http://userscripts.org/scripts/source/29222.user.js";
var savePath = "D:\\temp\\";
var extensionLoaded = false;
window.addEventListener ("ImAlivefromExtension", function (zEvent) {
console.log ("The test extension appears to be loaded!", zEvent.detail);
extensionLoaded = true;
} );
window.addEventListener ("ReplyToDownloadRequest", function (zEvent) {
//var xxxx = JSON.parse (zEvent.detail);
console.log ("Extension replied: ", zEvent.detail);
} );
$("body").prepend ('<button id="gmFileDownloadBtn">Click to File download request.</button>');
$("#gmFileDownloadBtn").click ( function () {
if (extensionLoaded) {
detailVal = JSON.stringify (
{targFileURL: fileURL, targSavePath: savePath}
);
var zEvent = new CustomEvent (
"SuicidalDownloadRequestToAddOn",
{"detail": detailVal }
);
window.dispatchEvent (zEvent);
}
else {
alert ("The file download extension is not loaded!");
}
} );
You can test the script on this SO question page.
Note that any other extension, userscript, web page, or plugin can listen to or send spoof events, the only security, so far, is to limit which pages the extension runs on.
For reference, the extension source files are below. The rest is supplied by Firefox's Add-on SDK.
The content script:
var zEvent = new CustomEvent ("ImAlivefromExtension",
{"detail": "GM, DANGER, DANGER, DANGER, File download utility" }
);
window.dispatchEvent (zEvent)
window.addEventListener ("SuicidalDownloadRequestToAddOn", function (zEvent) {
console.log ("Extension received download request: ", zEvent.detail);
//-- Relay request to extension main.js
self.port.emit ("SuicidalDownloadRequestRelayed", zEvent.detail);
//-- Reply back to GM, or whoever is pretending to be GM.
var zEvent = new CustomEvent ("ReplyToDownloadRequest",
{"detail": "Your funeral!" }
);
window.dispatchEvent (zEvent)
} );
The background JS:
//--- For security, MAKE THESE AS RESTRICTIVE AS POSSIBLE!
const includePattern = [
'https://mail.google.com/mail/*',
'https://stackoverflow.com/questions/14440362/*'
];
let {Cc, Cu, Ci} = require ("chrome");
Cu.import ("resource://gre/modules/Services.jsm");
Cu.import ("resource://gre/modules/XPCOMUtils.jsm");
Cu.import ("resource://gre/modules/FileUtils.jsm");
let data = require ("sdk/self").data;
let pageMod = require ('sdk/page-mod');
let dlManageWindow = Cc['#mozilla.org/download-manager-ui;1'].getService (Ci.nsIDownloadManagerUI);
let fileURL = "";
let savePath = "";
let activeWindow = Services.wm.getMostRecentWindow ("navigator:browser");
let mod = pageMod.PageMod ( {
include: includePattern,
contentScriptWhen: 'end',
contentScriptFile: [ data.url ('ContentScript.js') ],
onAttach: function (worker) {
console.log ('DANGER download utility attached to: ' + worker.tab.url);
worker.port.on ('SuicidalDownloadRequestRelayed', function (message) {
var detailVal = JSON.parse (message);
fileURL = detailVal.targFileURL;
savePath = detailVal.targSavePath;
console.log ("Received request to \ndownload: ", fileURL, "\nto:", savePath);
downloadFile (fileURL, savePath);
} );
}
} );
function downloadFile (fileURL, savePath) {
dlManageWindow.show (activeWindow, 1);
try {
let newFile;
let fileURIToDownload = Services.io.newURI (fileURL, null, null);
let persistWin = Cc['#mozilla.org/embedding/browser/nsWebBrowserPersist;1']
.createInstance (Ci.nsIWebBrowserPersist);
let fileName = fileURIToDownload.path.slice (fileURIToDownload.path.lastIndexOf ('/') + 1);
let fileObj = new FileUtils.File (savePath);
fileObj.append (fileName);
if (fileObj.exists ()) {
console.error ('*** Error! File "' + fileName + '" already exists!');
}
else {
let newFile = Services.io.newFileURI (fileObj);
let newDownload = Services.downloads.addDownload (
0, fileURIToDownload, newFile, fileName, null, null, null, persistWin, false
);
persistWin.progressListener = newDownload;
persistWin.savePrivacyAwareURI (fileURIToDownload, null, null, null, "", newFile, false);
}
} catch (exception) {
console.error ("Error saving the file! ", exception);
dump (exception);
}
}
So far from what you are saying,the only thing you can do is making add-on(Firefox) and Extension(for chrome if you want).
If you have closer look at download of attachment,it happens when:
1) You click on icon of attachments
2) If you click download
For these two things you can find the click event of <a> tag containing download_url value.You can easily do that using js/jquery for creting extension.
So you can write the functionality when user tries to download attachment.
You could use Gmail contextual gadgets to modify the behavior on the Google side:
Gmail Contexual Gadgets
Contextual Gadgets don't have direct access to attachments but server side, you could use IMAP to access the attachment (based on the Gmail message ID identified by the gadget):
Gmail IMAP Extensions
Using gadgets and server-side IMAP has the advantage of being browser-agnostic.
It's not entirely clear what you want to do differently with the downloaded Gmail attachment as opposed to any given download (save it to a different location? Perform actions upon the attachment data?) But the contextual gadget and IMAP should give you some chance to modify the attachment data as needed before the browser download begins.

Resources