sharing a url with whatsapp/Facebook - swift3

I'm trying to share a message string from my app in Whatsapp. my code for this is below. my messageString is my message. If messageString does not include a website link this works with no issue. However I now need to include a link in what I'm sharing. Now when I share I just get a blank message in Whatsapp. I have other share functions in the app such as email/sms which display messageString with a url but my Whatsapp one no longer does. How can I fix this?
let urlStringEncoded = messageString.addingPercentEncoding(withAllowedCharacters: .urlUserAllowed)
let url = URL(string: "whatsapp://send?text=\(urlStringEncoded!)")
if UIApplication.shared.canOpenURL(url!) {
if #available(iOS 10.0, *) {
UIApplication.shared.open(url!, options: [:], completionHandler: nil)
} else {
UIApplication.shared.openURL(url!)
}
}

To answer my own question. The problem turned out to be the encoding of the URL with included an Equals Sign. I was unable to send a string containing = with or with out a url.
the problem was fixed by by changing .withAllowedCharacters to
let urlStringEncoded = messageString.addingPercentEncoding(withAllowedCharacters: .alphanumerics)

Related

Loading a file into a C++ object from an iOS App / issues with <iostream> on iOS

I'm working on integrating a C++ library (the GRT, a machine learning toolkit, to be specific) inside of an iOS app.
I've built the GRT as a framework, including using some Objective-C++ wrapper functions to call between my app and the framework.
At the moment, I'm trying to troubleshoot something involving file loading. Specifically, I'm trying to load a file from my app bundle into a GRT module.
Here's where I get the file I want access to, and initialize the GRT wrapper:
func loadTrainingData(){
let documentsUrl = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
let fileUrl = documentsUrl.appendingPathComponent("train.grt")
let pipeline = GestureRecognitionPipeline()
let test:Bool = pipeline.load(fileUrl.path)
print(test)
}
And here's the Obj-C++ wrapper code that's called when pipeline.load is called:
- (BOOL)load:(NSString *) path
{
BOOL result = self.instance->load(std::string(path.UTF8String));
if (result) {
std::cout << "GRT config";
std::cout << self.instance->getModelAsString();
std::cout << "GRT info: " << self.instance->getInfo();
}
return result;
}
Finally, here's the actual C++ code that's part of the GRT library, where file loading is handled:
bool GestureRecognitionPipeline::load(const std::string &filename){
std::fstream file;
//Clear any previous setup
clear();
file.open(filename.c_str(), std::iostream::in );
if( !file.is_open() ){
errorLog << __GRT_LOG__ << " Failed to open file with filename: " << filename << std::endl;
return false;
}
...
}
Currently, I'm always failing to have my pipeline object successfully import a file. I don't think it's necessarily something to do with the way I'm accessing the file on the iOS side (though, I could be wrong). Does anyone have any insight? Any appreciated - thanks!
EDIT: I was able to verify that I am loading my file is being loaded properly by this check:
let path = Bundle.main.path(forResource: "acc-orientation", ofType: "grt")
print(path as Any!)
But, I'm still getting the same issues as before.
EDIT 2 I verified that the path is being loaded correctly in the the Obj-C++ wrapper too; which leads me to think it may be something related to the way that is handled in iOS....totally lost here now...
EDIT 3 As suggested by a colleague, I tried using the absoluteString of the file url to pass to my wrapper and the underlying C++ code, since the C++ doesn't have access to the sandboxed environment of iOS. Still the same result:
let documentsUrl = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
let fileUrl = documentsUrl.appendingPathComponent("acc-orientation.grt")
let pipeline = GestureRecognitionPipeline()
let test:Bool = pipeline.load(fileUrl.absoluteString)
EDIT 4 As suggested in the comments, I tried using fileSystemRepresentation, but this also didn't bring success.
- (BOOL)load:(NSURL *) url {
BOOL result = self.instance->load(std::string([url fileSystemRepresentation]));
...
}
EDIT 5: I made a simple test project that attempts to only access the file and load it using Swift->Obj-C++->C++ (no framework files, in other words). Here's a link where it can be downloaded. Any help appreciated!
Well, you are almost there. I have downloaded your sample project and got it working. Your problem has to do with the actual location of the file you want to open. At the moment you are trying to open the file from the Documents folder but you never actually copy the file from the App Bundle to the Documents folder. So there are two solutions:
Solution 1: App Bundle
Alter the loadTrainingData method in ViewController.swift to access the file from the App Bundle:
func loadTrainingData(){
let path = Bundle.main.url(forResource: "acc-orientation", withExtension: "grt")
let wrapper = Wrapper()
let test:Bool = wrapper.load(path)
print(test)
}
Solution 2: Documents folder
Copy the file from your App Bundle to your Documents folder right after the first launch. Therefore, copy the following code snippet to your AppDelegate.swift:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
do {
let url = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!.appendingPathComponent("acc-orientation.grt")
let bundleURL = Bundle.main.url(forResource: "acc-orientation", withExtension: "grt")
try FileManager.default.copyItem(at: bundleURL!, to: url)
} catch {
print("File already exists")
}
return true
}
With either of these solutions your FileLoader::load method will return true.
Hope that helps.

FMDB & Swift, " Optional("no such table: Student info") " in real device but it can be done in simulator

I'm newbie, plz help me to solve this out, I still have lots of other things to work on, really thank you thank you very much!
This is a further question after How to use FMDB on the generic iOS device instead of simulator?
When I execute the app on my device and the error threw out: "no such table: Student info", I've print all the path and they all pointed to the same file so I assumed the database has already copied? Console shows like this:
file:///var/mobile/Containers/Data/Application/B5E42F3C-524E-4BBF-8667-1EED0C963A77/Documents/
file:///var/mobile/Containers/Data/Application/B5E42F3C-524E-4BBF-8667-1EED0C963A77/Documents/Data.db
/var/mobile/Containers/Data/Application/B5E42F3C-524E-4BBF-8667-1EED0C963A77/Documents/Data.db
file:///var/mobile/Containers/Data/Application/B5E42F3C-524E-4BBF-8667-1EED0C963A77/Documents/
file:///var/mobile/Containers/Data/Application/B5E42F3C-524E-4BBF-8667-1EED0C963A77/Documents/Data.db
/var/mobile/Containers/Data/Application/B5E42F3C-524E-4BBF-8667-1EED0C963A77/Documents/Data.db
/var/mobile/Containers/Data/Application/B5E42F3C-524E-4BBF-8667-1EED0C963A77/Documents/Data.db
<NSFileManager: 0x17401c1b0>
2017-03-13 16:43:25.446039 Test1.3[16360:5045427] [MC] System group container for systemgroup.com.apple.configurationprofiles path is /private/var/containers/Shared/SystemGroup/systemgroup.com.apple.configurationprofiles
2017-03-13 16:43:25.457278 Test1.3[16360:5045427] [MC] Reading from public effective user settings.
Insert failed:
Optional("no such table: Student info")
The Data.db is in my bundle resources in target; and the contents in my device is a blank Data.db;
The 2nd question: If you look at the Utility.Swift in the previous question, although the app works good on simulator but after it was loaded, there should be an alertView said "Your database copy successfully", but it didn't. Following is that part of the code:
class func copyFile(_ fileName: NSString){
let dbPath: String = getPath(fileName as String)
let fileManager = FileManager.default
print(dbPath)
print(fileManager)
if !fileManager.fileExists(atPath: dbPath) {
let documentsURL = Bundle.main.resourceURL
let fromPath = documentsURL!.appendingPathComponent(fileName as String)
var error : NSError?
do {
try fileManager.copyItem(atPath: fromPath.path, toPath: dbPath)
}
catch let error1 as NSError {
error = error1
}
if(error != nil){
self.invokeAlertMethod("Error Occured", strBody: "\(error?.localizedDescription)" as NSString, delegate: nil)
}
else{
self.invokeAlertMethod("Successed", strBody: "Your database copy successfully", delegate: nil)
}
}
}
Okay for answering this question I went through your demo.
Where I found couple of mistakes. Let me go through one by one.
1) Your class Utility have a getPath method. What it does it will
keep copying db every time although db is already present in documents
directory and your documents directory db will be replaced with the sample structure. You should always check that if db is already present in documents directory or not.
2) Your db was getting copied into documents directory but structure
wasn't. There was no Student info table in db of documents directory.
3) Please avoid using space or any special characters in table names.
So what I did just corrected your method getPath in utility class.
Please replace your method with this one
class func getPath(_ fileName: String) -> String {
let bundlePath = Bundle.main.path(forResource: "Data", ofType: ".db")
let destPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]
let fileManager = FileManager.default
let fullDestPath = URL(fileURLWithPath: destPath).appendingPathComponent("Data.db")
if fileManager.fileExists(atPath: fullDestPath.path){
print("Database file is exist")
print(fileManager.fileExists(atPath: bundlePath!))
}else{
do{
try fileManager.copyItem(atPath: bundlePath!, toPath: fullDestPath.path)
}catch{
print("\n",error)
}
}
print(fullDestPath.path)
return fullDestPath.path
}
After changing this piece of code I tried to run in my device and inserted couple of records.
Let me know if you have any more questions.
If you find this answer helpful just accept it.
First trying delete your app and then reinstall it.
OR
I have created a project over FMDB in Swift which you can use to solve your issue. FMDB Wrapper class you can use in Objective C project as well.
https://github.com/LalitKY/Swift-FMDB
Hope this helps.

IBM Watson Alchemy news iOS SDK Swift

The IBM Watson iOS SDK using the Alchemy News service on Bluemix returns a string result which requires parsing to pull out the fields like url and cleaned title. ref: https://github.com/watson-developer-cloud/swift-sdk
I pull the string into an array and parse it in swift3 using some string methods but this is pretty ordinary and can produce unpredictable results
Is there a more elegant approach where I can access specific fields, like the url and cleaned title which I am passing to a UITableViewCell to select and segue to the url link.
sample code:
let alchemyDataNews = AlchemyDataNews(apiKey: apiKey)
let failure = { (error: Error) in print(error) }
let start = "now-14d" // 7 day ago
let end = "now" // today
let query = ["count": "15",
"dedup": "true",
"q.enriched.url.title": "[IBM]",
"return": "enriched.url.url,enriched.url.title" "enriched.url.title,enriched.url.entities.entity.text,enriched.url.entities.entity.type"]
Also I have noticed the search string [IBM] has a prefix of 0, i.e. 0[IBM] and have also seen an "A". What do these prefixes mean and where are they documented
Here is one way you can access the fields from a returned payload.
alchemyDataNews.getNews(from: "now-4d", to: "now", query: queryDict, failure: failWithError) { news in
for doc in (news.result?.docs)! {
var cleanedTitle = doc.source?.enriched?.url?.cleanedTitle
var author = doc.source?.enriched?.url?.author
var title = doc.source?.enriched?.url?.title
}}
Also, here is a nice API reference link for alchemy data which contains all of the request parameters and filters.
https://www.ibm.com/watson/developercloud/alchemydata-news/api/v1/

Web scraping in apple swift 3

I'm a Swift beginner and I'm trying to figure out how to retrieve text from a web article, create a new text file and save the text data into it (Using Swift Playgrounds). Is this possible?
The only thing I could find online regarding the subject was this, and I don't think it is even written for Swift 3:
P.S. If my question needs more details, please let me know instead of putting it on hold. Thanks!
import Cocoa
var url = NSURL(string: "http://finance.yahoo.com/news/tv-news-ces-2017-120931816.html")
if url != nil {
let task = NSURLSession.sharedSession().dataTaskWithURL(url!, completionHandler: { (data, response, error) -> Void in
print(data)
if error == nil {
var urlContent = NSString(data: data, encoding: NSUTF8StringEncoding) as NSString!
print(urlContent)
That's Swift 2.3. In Swift 3 use URL instead of NSURL and use URLSession rather than NSURLSession, etc. You'd also use String rather than NSString. E.g.
let url = URL(string: "http://finance.yahoo.com/news/tv-news-ces-2017-120931816.html")!
let task = URLSession.shared.dataTask(with: url) { data, response, error in
guard let data = data, error == nil else {
print("\(error)")
return
}
let string = String(data: data, encoding: .utf8)
print("\(string)")
}
task.resume()
If you're going to do this in a playground, remember that this runs asynchronously, so you'll need to set needsIndefiniteExecution.
import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true
To actually parse the HTML, you should consider using a HTML parser like TFHpple (written in Objective-C, but still works great from Swift) or NDHpple (a Swift version, in which I don't have as much confidence as TFHpple, but probably would work fine).
You might want to see How to Parse HTML on iOS. It's dated, but walks you through the concepts (making sure you're not violating ToS of the web site, how to use the parsers, etc.).
If you want to save this to a file, you can do something like:
let fileURL = try! FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
.appendingPathComponent("test.dat")
try! data.write(to: fileURL)
You can use whatever file extension you want.

facebookclient.post() or .posttaskasync() with .net sdk returns invalid parameter when trying to post a link

I've had this working in a previous version of my application and I tried using the old code, but I think the new sdk has something different going on. I'm simply trying to post a link (that includes an image) to my wall and receiving an "Invalid Parameter" response.
Here is the relevant code (I've also tried PostTaskAsync()...same result):
var client = new FacebookClient(accessToken);
var postParams = new
{
name = "the name",
caption = "the caption",
description = "the description",
link = "http://www.example.com/",
picture = "http://www.example.com/uploadedimages/myimage.jpg"
};
client.Post("me/feed", postParams);
I've tried substituting the object with a Dictionary with the same result. I've tried substituting object with dynamic parameters = new ExpandoObject(); with the same result.
If I post the object with just { message = "this is a test message" } it posts fine so I know that I have permissions to post on my wall. Something just isn't jiving when I try to post the link with the image. I also tried urlencoding the link and the image url and received a different error indicating that the "link/picture URL is not properly formatted".
I stripped out all of the parameters thinking one of them was no longer supported, but still no dice.
Here is the exact exception being thrown:
Facebook.FacebookApiException: (FacebookApiException - #100) Invalid
parameter at Facebook.FacebookClient.ProcessResponse(HttpHelper
httpHelper, String responseString, Type resultType, Boolean
containsEtag, IList`1 batchEtags) at
Facebook.FacebookClient.Api(HttpMethod httpMethod, String path, Object
parameters, Type resultType) at Facebook.FacebookClient.Post(String
path, Object parameters)
I got this sorted out. It turns out the link and the image url have to be in the same domain as the app you're using to post.
EDIT: just to clarify. The domain has to be included in your app's config section (on Facebook) in the "App domains" section at the top.