Keyboard extension no common words, no autocorrect - ios

I've been working on a little keyboard extension, and now I've reached the fase of implementing autocorrect, or something that will show what the user could be attempting to write.
The keyboard is just for myself, so just danish.
The documentation says that UILexicon contains some common words, which it doesn't. It contains my contacts, and not even that works. The only thing I get is like this: morten=morten, where it should be morten=Morten. They're all like this.
Then I tried the UITextChecker, and tried this in a playground (Swift, but the keyboard is in ObjectiveC:
import UIKit
var str:String = "h"
var textChecker:UITextChecker = UITextChecker();
var range:NSRange = NSMakeRange(0, 1)
var lan:NSArray = UITextChecker.availableLanguages()
var t:String = lan[1] as String
var ar:NSArray = textChecker.completionsForPartialWordRange(range, inString: str, language: t)!
This returns results in any language but da_DK, which is somewhat weird.
How should I do this? Should I download a danish dictionary and use it in the app using some kind of mysql database?
Is my code somehow wrong, or is UILexicon simply broken for the danish language?

Try taking a look at the UITextChecker class.
It's pretty strait forward to use:
NSString * test = #"stavefej";
NSArray * suggestions = [self.spellChecker guessesForWordRange:NSMakeRange(0, [test length]) inString:test language:#"da_DK"];
NSArray * autoCom = [self.spellChecker completionsForPartialWordRange:NSMakeRange(0, [test length]) inString:test language:#"da_DK"];

Related

Convert UITextView text into [String], separating paragraphs into the Strings [duplicate]

This question already has answers here:
Split a String into an array in Swift?
(40 answers)
Closed 6 years ago.
I have an iOS project using Xcode 7 and Swift3. I have a UITextView that enables the user to save that text via NSUserDefaults into a String variable. However, if the user has a lot of text that includes paragraphs, when they save the text, it compiles it together to be one big long string of text, not recognizing the original paragraphs.
How do I have the string of text in essence recognize there are paragraphs and then make them into separate strings that are part of a [String]? Is this possible?
Example of text:
This is a complex task for me.
I don't know how to do this.
After the user names this text, which is two separate small paragraphs, it would save to NSUserDefaults as:
This is a complex task for me. I don't know how to do this.
I know rather than save a String to NSUserDefaults I need to instead use a [String], but I still can't figure out how to take a UITextView and convert it's text into the string array, separating each paragraph into a separate String.
I looked at the reference in Apple's documents concerning paragraphs. I then took the advice and did the following:
// Assign the text to an NSString Variable
var newTextNS = instructions.text! as NSString
// Separate the text paragraphs into String Variables
var newArray = newTextNS.components(separatedBy: "\n")
I then had an array with all the different paragraphs as separate String variables.
Worked just as I needed it too.
Please check Apple's reference Words, Paragraphs, and Line Breaks
NSArray *arr = [myString componentsSeparatedByString:#"\n"];
or
NSString *string = /* assume this exists */;
NSRange range = NSMakeRange(0, string.length);
[string enumerateSubstringsInRange:range
options:NSStringEnumerationByParagraphs
usingBlock:^(NSString * _Nullable paragraph, NSRange paragraphRange, NSRange enclosingRange, BOOL * _Nonnull stop) { // ... }];
Hope it helps.

Swift 2 NSMutableArray() value don't shows in variable

Swift 2 NSMutableArray() value don't shows in variable
My codes here.
var detail_desc : NSMutableArray! = NSMutableArray()
when i added this code gives me
label.text = self.detail_desc.description
And my array output
{
{
"cat_img" = "http://asdasd.com/asd.jpg";
"cat_name" = Good;
id = 45;
"is_active" = 3;
"last_update" = "2015-11-21 15:32:42";
}
}
I want to use array item to variable ;
label.text = cat_img
label2.text = cat_name
any idea ?
There's a lot wrong with what you have, I'll try to tackle it one by one:
Underscore
Underscore naming isn't common in cocoa and reads very strange. Change detail_desc to detailDesc.
Note: There are situations in which we use the underscore, one of which is extensions on core classes. In these cases, it is prefix - underscore - name. ie: my_specialFunctionOrVariable
NSMutableArray
NSArray and NSDictionary classes shouldn't be used in Swift unless absolutely necessary. They behave slightly differently, and you lose all of the type safety that makes swift so great.
Naming
In general, naming schemes like label, label2, and detailDescription are very confusing. They don't tell me anything about what each represents. Try something like catImageUrlLabel, and catNameLabel to be more specific. Also, detailDescription could be catInformation
Single Object Encapsulated In Array
You really have a dictionary as the single object in an array, I think you should just have the dictionary reference. (unless there could be more in the future). I'll move forward assuming there won't be.
How it might look
Change your variable to something like this:
var detailDescription: [String : AnyObject] = ...
Then, in your UI setting code, do something like this:
label.text = detailDescription["cat_img"] as? String
label2.text = detailDescription["cat_name"] as? String
Modelling Objects
Json is dirty and we prefer not to deal with it as much as possible. Consider modeling your Cat json to a Cat object:
struct Cat {
let imageUrl: String
let name: String
let id: Int
// etc.
}
Then parse the json into this model when you receive it and pass that to your view controller.
Then our code might look like this:
nameLabel.text = cat.name
imageUrlLabel.text = cat.imageUrl
// etc.
This is very clear what everything represents, and will make for a more readable, and more maintainable code base.
Hope this gets you off on the right track.

Replace Characters in a Range in Swift

I have a chunk of code that looks like this:
HiClass *myNewClass;
Now, what I'm doing is writing a method to roll through and delete the Hi, as well as everything after it, including the *, so that only myNewClass; is left. Now, I take out the "Hi" like so:
textToConvert = inputField.stringValue.stringByReplacingOccurrencesOfString("Hi", withString: "",
options: NSStringCompareOptions.LiteralSearch, range: nil)
But within this same method, if possible, I would like to somehow delete everything leaving only myNewClass;, as stated above.
My first though on how to approach this is to use a range. Though still being new to Swift and trying to avoid Objective-C, I'm unsure of how to remove all of the characters between the Hi and the *, leaving onlymyClass.
you can use this method
var testStr = "HiClass *myNewClass"
let array = testStr.componentsSeparatedByString("*")
testStr = String(array.last)
You can use indexOf with Swift 2.0 to find the index, and just take a substring to the end of it like this:
var str = "HiClass *myNewClass"
if let idx = str.characters.indexOf("*") {
var s = str.substringFromIndex(advance(idx, 1))
}

How to use autocorrection and shortcut list in iOS8 custom keyboard?

I want to use the autocorrection and shortcut list like default English keyboard with my custom keyboard.
I check the in keyboard document but don't know how to use it.
In keyboard documentation.
Every custom keyboard (independent of the value of its RequestsOpenAccess key) has access to a basic autocorrection lexicon through the UILexicon class. Make use of this class, along with a lexicon of your own design, to provide suggestions and autocorrections as users are entering text. The UILexicon object contains words from various sources, including:
Unpaired first names and last names from the user’s Address Book database
Text shortcuts defined in the Settings > General > Keyboard > Shortcuts list
A common words dictionary
How to access shortcut list and input from our dictionary in Objective-C?
How to use UILexicon with requestSupplementaryLexiconWithCompletion?
Implementing the lexicon would look pretty much like this:
Use requestSupplementaryLexiconWithCompletion() to get the lexicon upon launch once.
Each type text is inputted add it to a NSString (tracking the current word)
When user presses space (end of curent word) check the string against the lexicon
If it's a match count the number of characters and delete that number of characters
Input the suggestion suggested by the lexicon
Clear the string and start again
Additionally you could also use UITextChecker to offer more advanced auto-correct features.
Code (in Objective-C, this may not be 100% accurate I wrote in SO while on the bus but it should do):
UILexicon *lexicon;
NSString *currentString;
-(void)viewDidLoad {
[self requestSupplementaryLexiconWithCompletion:^(UILexicon *receivedLexicon) {
self.lexicon = receivedLexicon;
}];
}
-(IBAction)myTypingAction:(UIButton *)sender {
[documentProxy insertText:sender.title];
[currentString stringByAppendingString:sender.title];
}
-(IBAction)space {
[documentProxy insertText:#" "];
for (UILexiconEntry *lexiconEntry in lexicon.entries) {
if (lexiconEntry.userInput isEqualToString:currentString) {
for (int i = 0; currentString.length >=i ; i++) {
[documentProxy deleteTextBackwards];
}
[documentProxy insertText:lexiconEntry.documentText];
currentString = #"";
}
}
}
Feel free to comment if you have any more questions.
Source: Personal experience with iOS 8 keyboards and UILexicon
With regards to auto-correction, I was able to add it using link. Here's the code snippet I used from the link:
UITextChecker *checker = [[UITextChecker alloc] init];
NSRange checkRange = NSMakeRange(0, self.txView.text.length);
NSRange misspelledRange = [checker rangeOfMisspelledWordInString:self.txView.text
range:checkRange
startingAt:checkRange.location
wrap:NO
language:#"en_US"];
NSArray *arrGuessed = [checker guessesForWordRange:misspelledRange inString:self.txView.text language:#"en_US"];
self.txView.text = [self.txView.text stringByReplacingCharactersInRange:misspelledRange
withString:[arrGuessed objectAtIndex:0]];
The full documentation from Apple can be found here.
Although I have not personally tried creating a custom keyboard, I am basing this answer on what I can see in the documentation.
In your keyboard, create a property called entries of type [AnyObject] (Array of AnyObjects).
In your init method, or wherever you create the keyboard, call this method:
requestSupplementaryLexiconWithCompletion(completionHandler: {
lexicon in
self.entries = lexicon.entries
})
I suspect that entries is actually an array of Strings or NSStrings, but it could be a dictionary or some other type. When testing this out, try figuring out what type is actually contained in entries before figuring out your logic.
I do not believe there is a way to get Apple's default autocorrect options currently. However, this WWDC talk gives insight about how they made autocorrect work in the original iPhone OS (around the 30 minute mark).
He mentions using a binary search of the array, which leads me to believe that this array is sorted. Of course, much could have changed since the first iPhone came out...
Good luck figuring out this new API!
This is the way you can actually access Lexicon words:
[self requestSupplementaryLexiconWithCompletion:^(UILexicon *receivedLexicon) {
self.lexicon = receivedLexicon;
for (UILexiconEntry *word in self.lexicon.entries) {
// Text to be inserted into a text input object by a custom keyboard, corresponding to the userInput value in the same lexicon entry.
NSLog(#"%#",word.documentText);
// Text to match, during user input, to provide appropriate output to a text document from the documentText value in the same lexicon entry.
NSLog(#"%#",word.userInput);
}
}];
Rachits answer above in swift 4. Works with iOS 12
I have this helper to check wether the current string to be tested by UITextChecker is not a space
func validate(string: String?) -> Bool {
guard let text = string,
!text.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines).isEmpty else {
return false
}
return true
}
The text checker is then in my "Spacebar", "Spacebar double tapped" & "return" methods. The example below is in my "Space" method
let textChecker = UITextChecker()
let currentString = self.textDocumentProxy.documentContextBeforeInput
if validate(string: currentString) {
let charSet = NSCharacterSet.whitespacesAndNewlines
let components = currentString?.components(separatedBy: charSet)
let lastWord = components?.last
let checkRange = NSMakeRange(0, lastWord?.count ?? 0)
let misspelledRange = textChecker.rangeOfMisspelledWord(in: lastWord!, range: checkRange, startingAt: checkRange.location, wrap: false, language: "en_US")
if misspelledRange.length != 0 {
let guessedWord: Array = textChecker.guesses(forWordRange: misspelledRange, in: lastWord!, language: "en_US")!
if guessedWord.count > 0 {
var i = 0
while (lastWord?.length)! > i {
textDocumentProxy.deleteBackward()
i += 1
}
self.textDocumentProxy.insertText(guessedWord[0])
}
}
}
self.textDocumentProxy.insertText(" ")
I had to make two changes to Rachits code. First to validate the currentString since it throws an exception if you press space bar twice. And second to check if the misspelled range is not 0 because that was also throwing an exception which I am yet to figure out why. But this works for me now as is.
Every custom keyboard (independent of the value of its RequestsOpenAccess key) has access to a basic autocorrection lexicon through the UILexicon class. Make use of this class, along with a lexicon of your own design, to provide suggestions and autocorrections as users are entering text. The UILexicon object contains words from various sources, including:
Unpaired first names and last names from the user’s Address Book database
Text shortcuts defined in the Settings > General > Keyboard > Shortcuts list
A common words dictionary that includes the names of Apple products
In case anyone is still looking into this, I found a really nice C++ predictive text library called Presage. It seems to do a good job based on the demo but I'm having a lot of trouble trying to integrate it as a library (see my question here).
Let me know if anyone has any ideas, very interested in getting this working!
Actually, UILexicon is just a way to get some user-specific words that your spellchecking system should't try to fix. Probably, the most common way to use it is to fill out UITextChecker's list of ignored words.
let lexicon: UILexicon = ...
let checker: UITextChecker = ...
for entry in lexicon.entries {
if entry.documentText == entry.userInput {
checker.ignoreWord(entry.documentText)
}
}
Additionally, UILexicon can be used as source of autoreplaced shortcuts like ("omw" = "On my way!"), but it is not autocorrection in terms of spelling.
You can use below logic for AutoCorrect & it will also work in iOS 10
-(void)didClickAtAlphaNumericKeyboardKey:(NSString *)value {
if ([value isEqualToString:#" "]) {
UITextChecker *checker = [[UITextChecker alloc] init];
currentString = self.textDocumentProxy.documentContextBeforeInput;
NSCharacterSet *charSet = [NSCharacterSet whitespaceAndNewlineCharacterSet];
NSArray *components = [currentString componentsSeparatedByCharactersInSet:charSet];
NSString *lastWord = components.lastObject;
NSRange checkRange = NSMakeRange(0, lastWord.length);
NSRange misspelledRange = [checker rangeOfMisspelledWordInString:lastWord
range:checkRange
startingAt:checkRange.location
wrap:NO
language:#"en_US"];
NSArray *guessedWord = [checker guessesForWordRange:misspelledRange inString:lastWord language:#"en_US"];
if (guessedWord && guessedWord.count > 0) {
for (int i = 0; lastWord.length >i ; i++) {
[self.textDocumentProxy deleteBackward];
}
[self.textDocumentProxy insertText:[guessedWord objectAtIndex:0]];
}
}
[self.textDocumentProxy insertText:value];
}

How to Get the Title of a HTML Page Displayed in UIWebView?

I need to extract the contents of the title tag from an HTML page displayed in a UIWebView. What is the most robust means of doing so?
I know I can do:
- (void)webViewDidFinishLoad:(UIWebView *)webView{
NSString *theTitle=[webView stringByEvaluatingJavaScriptFromString:#"document.title"];
}
However, that only works if javascript is enabled.
Alternatively, I could just scan the text of the HTML code for the title but that feels a bit cumbersome and might prove fragile if the page's authors got freaky with their code. If it comes to that, what's the best method to use for processing the html text within the iPhone API?
I feel that I've forgotten something obvious. Is there a better method than these two choices?
Update:
Following from the answer to this question: UIWebView: Can You Disable Javascript? there appears to be no way to turn off Javascript in UIWebView. Therefore the Javascript method above will always work.
For those who just scroll down to find the answer:
- (void)webViewDidFinishLoad:(UIWebView *)webView{
NSString *theTitle=[webView stringByEvaluatingJavaScriptFromString:#"document.title"];
}
This will always work as there is no way to turn off Javascript in UIWebView.
WKWebView has 'title' property, just do it like this,
func webView(_ wv: WKWebView, didFinish navigation: WKNavigation!) {
title = wv.title
}
I don't think UIWebView is suitable right now.
If Javascript Enabled Use this :-
NSString *theTitle=[webViewstringByEvaluatingJavaScriptFromString:#"document.title"];
If Javascript Disabled Use this :-
NSString * htmlCode = [NSString stringWithContentsOfURL:[NSURL URLWithString:#"http://www.appcoda.com"] encoding:NSASCIIStringEncoding error:nil];
NSString * start = #"<title>";
NSRange range1 = [htmlCode rangeOfString:start];
NSString * end = #"</title>";
NSRange range2 = [htmlCode rangeOfString:end];
NSString * subString = [htmlCode substringWithRange:NSMakeRange(range1.location + 7, range2.location - range1.location - 7)];
NSLog(#"substring is %#",subString);
I Used +7 and -7 in NSMakeRange to eliminate the length of <title> i.e 7
Edit: just saw you found out the answer... sheeeiiitttt
I literally just learned this! To do this, you don't even need to have it displayed in UIWebView. (But as you are using it, you can just get the URL of the current page)
Anyways, here's the code and some (feeble) explanation:
//create a URL which for the site you want to get the info from.. just replace google with whatever you want
NSURL *currentURL = [NSURL URLWithString:#"http://www.google.com"];
//for any exceptions/errors
NSError *error;
//converts the url html to a string
NSString *htmlCode = [NSString stringWithContentsOfURL:currentURL encoding:NSASCIIStringEncoding error:&error];
So we have the HTML code, now how do we get the title? Well, in every html-based doc the title is signaled by This Is the Title
So probably the easiest thing to do is to search that htmlCode string for , and for , and substring it so we get the stuff in between.
//so let's create two strings that are our starting and ending signs
NSString *startPoint = #"<title>";
NSString *endPoint = #"</title>";
//now in substringing in obj-c they're mostly based off of ranges, so we need to make some ranges
NSRange startRange = [htmlCode rangeOfString:startPoint];
NSRange endRange = [htmlCode rangeOfString:endPoint];
//so what this is doing is it is finding the location in the html code and turning it
//into two ints: the location and the length of the string
//once we have this, we can do the substringing!
//so just for easiness, let's make another string to have the title in
NSString *docTitle = [htmlString substringWithRange:NSMakeRange(startRange.location + startRange.length, endRange.location)];
NSLog(#"%#", docTitle);
//just to print it out and see it's right
And that's really it!
So basically to explain all the shenanigans going on in the docTitle, if we made a range just by saying NSMakeRange(startRange.location, endRange.location) we would get the title AND the text of startString (which is ) because the location is by the first character of the string.
So in order to offset that, we just added the length of the string
Now keep in mind this code is not tested.. if there are any problems it might be a spelling error, or that I didn't/did add a pointer when i wasn't supposed to.
If the title is a little weird and not completely right, try messing around with the NSMakeRange-- I mean like add/subtract different lengths/locations of the strings --- anything that seems logical.
If you have any questions or there are any problems, feel free to ask. This my first answer on this website so sorry if it's a little disorganized
Here is Swift 4 version, based on answer at here
func webViewDidFinishLoad(_ webView: UIWebView) {
let theTitle = webView.stringByEvaluatingJavaScript(from: "document.title")
}
I dońt have experience with webviews so far but, i believe it sets it´s title to the page title, so, a trick I suggest is to use a category on webview and overwrite the setter for self.title so you add a message to one of you object or modify some property to get the title.
Could you try and tell me if it works?
If you need it frequently in your code, i suggest you to add a func into "extension UIWebView" like this
extension UIWebView {
func title() -> String {
let title: String = self.stringByEvaluatingJavaScript(from: "document.title")!
return title
}
}
alternatively its better to use WKWebView.
Unfortunately, its not well supported in ARKit. I had to give up on WKWebView. I couldnt load the website into the webView. If someone has a solution to this issue here -> i have a simlar problem, it would help greatly.

Resources