String.Index.encodedOffset has been deprecated - how do I use the replacement? - ios

I have this custom Label which can highlight certain words and detect when the user taps them in a NSAttributedString. I.E multiple different links in a label. When I register the UITapGesture, I can convert it to indexOfCharacter:Int using an NSLayoutManager. So I end up with an Int telling me the index of the character I touched.
The way I store the actual info for the Link-words is by storing their display-text, their directed URL, and the range:Range<String.Index> of the display-text as part of the overall NSAttributedString.
So to find out if the clicked character exists within any of the links, I simply loop through all links in the given string, and check if the clicked character-index is within the range of any link.
let indexOfCharacter = layoutManager.characterIndex(for: locationOfTouchInTextContainer, in: textContainer, fractionOfDistanceBetweenInsertionPoints: nil)
for link in links{
if indexOfCharacter >= link.range.lowerBound.encodedOffset && indexOfCharacter <= link.range.upperBound.encodedOffset{
callDelegateOrHandle(link: link)
break
}
}
This has been working as intended, but when converting to Swift 5, I get the warning 'encodedOffset' is deprecated: encodedOffset has been deprecated as most common usage is incorrect. Use utf16Offset(in:) to achieve the same behavior.
All this String.Index vs Range vs Int vs NSRange has given me headaches for four years, so I eventually stopped trying to figure it out. So I might be part of the common usage is incorrect-problem.
I feel like the problem here is that NSLayoutManager returns an Int when it should've returned a String.Index, alas.. NSLayoutManager would never..
So how can I fix this problem? I assume there are better ways to solve this. I tried switching from link.range.lowerBound.encodedOffset to link.range.lowerBound.utf16Offset(in: ), but not sure what to pass as parameter. I tried sending the overall NSAttributedString, but it doesn't conform to StringProtocol. I feel like it should be super easy to do this, but I don't get it.

Related

Getting Twitter characters count

I have a program, it is an editor for Twitter tweets, it's counting the text to make it less than 280 character as twitter restriction.
I use for that utf8 property like this:
var str = "℞"
let r = str.utf8.count
The result = 3
This symbol (℞) and more like it takes only 2 character in twitter counter but the result in this code gave me 3, so i can't give the user the exact character count!
How can I get the correct count: 2
Counting characters
Tweet length is measured by the number of codepoints in the NFC
normalized version of the text.
In Swift, you can get the NFC normalized form through precomposedStringWithCanonicalMapping, and the number of codepoints by unicodeScalars.count.
So, the right code in Swift should be like this:
var str = "℞"
let r = str.precomposedStringWithCanonicalMapping.unicodeScalars.count
print(r) //->1
The code above shows consistent result with some character counters on the web, I do not understand why you get 2 for ℞.
(Thanks to Rakesha Shastri.)
I believe the code above correctly implements the specification described in the documentation I linked above.
But it is reported that the actual Twitter does not work exactly as in the doc. (Sorry, I do not tweet myself.) We may need to guess or find another reliable source to make it fit for the actual Twitter.
I tried the official library text Tweet parsing library, but it shows the same result as my code.
let len = TwitterText.tweetLength(str)
print(len) //->1
(Though, the code of TwitterText.tweetLength(_:) is far more complex, as it handles t.co links. So, when some URLs are included in the text, it generates different output than my code.)
(UPDATE)
I'm not sure as the referred twitter apps are not open-source, but I guess they are showing the weighted length described in the text Tweet parsing library page linked above.
You may need to write something like this with importing the library using pod.
let config = TwitterTextConfiguration(fromJSONResource: kTwitterTextParserConfigurationV2)
let parser = TwitterTextParser(configuration: config)
let result = parser.parseTweet(str)
print(result.weightedLength) //->2

Remove region code from NSLocale.preferredLanguages array

After updating my Mac to OSX Sierra, I have noticed, that the NSLocale.preferredLanguages() array is now in different format (See quick explanation here)
Also, same thing happens for IOS9, I suppose.
So, in short, previously we had language format "en", "fr", "ru", e.t.c
Now, we have format "en-US", "en-RU", "fr-US" e.t.c.
In my application, some controls' coordinates were related to the locale. Now, because of the different format, they are all wrong. I was thinking about best way to fix this and to remain support for older versions.
So, my question is:
Is there any way of receiving the language code without region code only? The quick and dirty solution would be to read first two symbols from this string. Is there a more elegant one?
P.S. Can someone please explain Apple's logic to me. I mean, why they have decided, that best option would be to change the existing object format(breaking the backwards compatibility) instead of adding additional field to NSLocale?
You can use substringToIndex as follows:
let str = "en-US"
if let indexOfDash = str.characters.indexOf("-") {
let langCode = str.substringToIndex(indexOfDash)
}
This way should the language code be longer than 2 chars for whatever reason, you don't get caught out.
use
swift3
let splitArray = "en-US".components(separatedBy: "-") you get // ["en", "US"]
below swift3
let splitArray = "en-US".componentsSeparatedByString("-") // ["en", "US"]

How can I open a pdf link when click a button on Swift?

I am trying to open a pdf from my Swift application but I cannot make it to work.
I know that there are a lot of questions related with this and in most of them they use this code:
UIApplication.shared.openURL(URL(string: "http://www.url.com" + id)!)
But I am getting the following error:
fatal error: unexpectedly found nil while unwrapping an Optional value
so I thought that the error was because the id was nil so I made two prints to see what was retrieved: one for url and one for id. Both of them are correct and if I copy the url on the browser navigation bar it works perfectly (I cannot provide the real url because it is of a client).
I have this function wrapped inside an #IBAction to detect when a button is clicked so I looked if the connection had been broken. It is also correct.
So I cannot understand why this error is happening. I spent some hours but cannot figure out what is causing this error.
Am I missing something? Should I codify the url in some way?
P.S: I am using Xcode8 and Swift3.
UPDATE: If I set www.google.es it is working perfectly. Can it be a problem using variables inside of the url?
Thanks in advance!
Finally, after some hours searching about the problem and following the recommendation of #rmaddy (Thanks!) I have split my function in three parts and I could see that the URL was returning nil value.
It was strange because I could copy the string into my browser navigation bar and it worked well so I thought that it could be something about encoding. One time I have encoded it I noticed that the id of the pdf was retrieved with a \r at the final of the id. Like this:
id004.pdf\r
The solution that I have done is the following:
var string = "http://www.url.com" + id
var index1 = string.index(string.endIndex, offsetBy: -1)
var substring1 = string.substring(to: index1)
let encodedString = substring1.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)
UIApplication.shared.openURL(NSURL(string: encodedString!) as! URL)
Encoded string is necessary because if not the link no longer work.

How to read information about filtered data in datatable()?

I need to display Reset Filter only when the filter is in place.
Need to read the filtered message. If the message contains "filtered", I would call the showResetFilter method. Tried table.info() but I get an error message "Object doesn't support property or method 'info'". Is there any other method that I may be able to use to read the filter message ? Image is linked here TableImage.
var table = $('#myTable').DataTable();
var message = table.info();
var isFiltered = message.indexOf("filtered") > -1;
if (isFiltered) {
ShowResetFilter();
}
I have two distinct ways you could do this, neither of which is very nice looking.
Option 1
This is based on a previous version of DataTables, but it seems like it should still work. See this forum post on the DataTables website, where someone asked a similar question. The author of the plugin said that the following was the only way to do it, but admitted that it was a bit of a workaround
Edit: It's been brought to my attention that my previous synax is incorrect. I had to guess at the capitalization when I took it out of hungarian notation. See the quotation below for the original syntax of this call, which should work in 1.10, but will be deprecated soon so I'd recommend against it.
My code was based on this quote from the author:
Currently you need to do something like: fnSettings().oPreviousSearch.sSearch to get the current filter. I'll look at making that somewhat easier in 1.10.
But as far as I can tell, he didn't make it any easier in 1.10 :(
Option 2
This is my method, which is about as ugly but should also work. You can try calling page.info() (documentation here) to get the recordsTotal and recordsDisplay attributes. If they are equal, there is no filter, if they aren't, there must be a filter in place. That would look something like this:
var pageInfo = table.page.info();
if(pageInfo.recordsTotal != pageInfo.recordsDisplay){
//Handle it/do stuff
}
Note that both of these methods are kinda hack-y, so if you're looking for the optimal solution, I'd recommend you keep looking.
You could read it from the div where it is updated directly:
var message = $(".dataTables_info").text();
Or, to be more specific in case you have multiple tables,
var message = $("#myTable_info").text();
And then use your original logic. Though, if you end up changing how this information will be displayed in the future, this will fall through, so I do think it is more in your interests to refactor a bit and use Chris H.'s Option 2

How to add a custom matchOutcome in GKTurnBasedMatchOutcome?

I have a game in which the match ends in multiple ways, each one I have set using GKturnBasedMatchOutcome. Now I need to add one more to the present list. I have looked the custom range. But I couldn't understand how to do it. Please help. I found this answer, but that is giving a crash.
I ve also tried this one
typedef enum
{
GKTurnBasedMatchOutcomeCancel = 10 | GKTurnBasedMatchOutcomeCustomRange,
} GKTurnBasedMatchOutcome_Custom;

Resources