As an exercise I am rewriting one of my apps using Swift.
In Objective-C I had the following line of code:
NSRange extendedRange = NSUnionRange(range, [[self.backingStore string]lineRangeForRange:NSMakeRange(NSMaxRange(range), 0)]);
In swift it looks like this:
let str = backingStore.string
let extendedRange: NSRange = NSUnionRange(range, str.lineRangeForRange(NSMakeRange(NSMaxRange(range), 0)))
However, for some reason I am getting 'Int' is not identical to 'String.Index' and I can't figure out why. Any help / insight would be appreciated.
EDIT:
If I bridge my string, it works:
let str = backingStore.string
let extendedRange: NSRange = NSUnionRange(range, str.bridgeToObjectiveC().lineRangeForRange(NSMakeRange(NSMaxRange(range), 0)))
Swift strings are not the same as Foundation strings: they can be bridged, and they may have method names in common, but that does not mean they should be treated as being compatible. Here are two views of the type definition of Swift.String.lineRangeForRange.
Swift.String.lineRangeForRange (Swift.String)(Swift.Range<Swift.String.Index>) -> Swift.Range<Swift.String.Index>
String -> (Range<String.Index>) -> Range<String.Index>
Note also that Swift's Range<T> is start/end while Foundation's NSRange is location/length. There are ways to convert between NSRange and Range<Int>, but a Range<String.Index> is a different story: String.Index is more like an "iterator" than an "index".
What I'd say (and I'm highly pragmatic about this sort of thing) is that if you have a block of code that is based on the semantics of Objective-C strings it might be good to keep using them for a while: get your code working, and then evaluate changing to Swift's String later.
Curious myself, I Cmd+Clicked on String.Index and discovered it is actually defined as a struct in an extension type:
extension String : Collection {
struct Index : BidirectionalIndex {
func succ() -> String.Index
func pred() -> String.Index
}
BidirectionalIndex is a protocol that inherits from another protocol, ForwardIndex.
I assume the succ() and pred() methods stand for 'successor' and 'predecessor' respectively.
Related
I have a method that was originally defined to take an NSString parameter but now needs to be able to take an NSString parameter or an NSAttributedString parameter. Unfortunately these do not share an inheritance hierarchy but each inherit from NSObject. For this reason, I modified the method to take an NSObject and then check the class of the parameter within the method body.
This is working as I would like. However, now I am getting a compiler error from some swift code that calls the method:
Cannot convert value of String 'String' to expected argument type 'NSObject!'
I'm only beginning to work with Swift, and this particular code was written by someone else. I tried to surround the string in the method call with NSObject(...) but that didn't work. What would be the best way to accommodate this situation?
Cast it with as:
Either str as NSObject or str as NSString will work.
Example:
func test(_ s: NSObject) {
if s is NSString {
print("it is an NSString")
} else if s is NSAttributedString {
print("it is an NSAttributedString")
}
}
let str = "hello"
test(str as NSObject)
Output:
it is an NSString
Using the as operator should solve your immediate problem.
func someFunction(withAnObjectParameter: NSObject!) {
//...
}
let myString: String = "aString"
let myAttributedString: NSAttributedString = NSAttributedString()
someFunction(withAnObjectParameter: myString as NSObject)
someFunction(withAnObjectParameter: myAttributedString as NSObject)
Here are some links describing its usage in other scenarios:
swift blog
developer documentation
In Swift 3, I have written a custom operator prefix operator § which I use in a method taking a String as value returning a LocalizedString struct (holding key and value).
public prefix func §(key: String) -> LocalizedString {
return LocalizedString(key: key)
}
public struct LocalizedString {
public var key: String
public var value: String
public init(key: String) {
let translated = translate(using: key) // assume we have this
self.key = key
self.value = translated ?? "!!\(key)!!"
}
}
(Yes I know about the awesome L10n enum in SwiftGen, but we are downloading our strings from our backend, and this question is more about how to work with custom operators)
But what if we wanna get the translated value from the result of the § operator (i.e. the property value from the resulting LocalizedString)
let translation = §"MyKey".value // Compile error "Value of type 'String' has no member 'value'"
We can of course easily fix this compile error by wraping it in parenthesis (§"MyKey").value. But if do not want to do that. Is it possible to set precedence for custom operators in relationship to the 'dot' literal?
Yes I know that only infix operators may declare precedence, but it would make sense to somehow work with precedence in order to achieve what I want:
precedencegroup Localization { higherThan: DotPrecedence } // There is no such group as "Dot"
prefix operator §: Localization
To mark that the Swift compiler first should evaluate §"MyKey" and understand that is not a string, but in fact an LocalizedString (struct).
Feels unlikely that this would be impossible? What am I missing?
The . is not an operator like all the other ones defined in the standard library, it is provided by the compiler instead. The grammar for it are Explicit Member Expressions.
Having a higher precedence than the . is nothing the compiler should enable you to do, as it's such a fundamental use case. Imagine what you could do if the compiler enabled such a thing:
-"Test".characters.count
If you could have a higher precedence than ., the compiler has to check all possibilities:
(-"Test").characters.count // func -(s: String) -> String
(-("Test".characters)).count // func -(s: String.CharacterView) -> String.CharacterView
-("Test".characters.count) // func -(s: Int) -> Int
Which would
Potentially increase the compile time a lot
Be ambiguous
Possibly change behaviour of existing code upon adding overloads
What I suggest you to do is abandon the idea with a new operator, it's only going to be adding more cognitive load by squashing some specific behaviour into a single obscure character. This is how I'd do it:
extension String {
var translatedString : String {
return translate(using: self)
}
}
"MyKey".localizedString
Or if you want to use your LocalizedString:
extension String {
var localized : LocalizedString {
return LocalizedString(key: self)
}
}
"MyKey".localized.value
These versions are much more comprehensive.
I have a receipt validation class that is deprecated since Swift 3 has released. I fixed some issues, but I still have many ...
Here is the GitHub source code I used : https://gist.github.com/baileysh9/4386ea92b047d97c7285#file-parsing_productids-swift and https://gist.github.com/baileysh9/eddcba49d544635b3cf5
First Error :
var p = UnsafePointer<UInt8>(data.bytes)
Compiler throws : Cannot invoke initializer for type UnsafePointer(UInt8) with an argument list of type UnsafeRawPointer
Second error
while (ptr < end)
Binary operators < cannot be applied to two UnsafePointer(UInt8) operands
Thank you very much in advance :)
EDIT
Thanks to LinShiwei answer I found a solution to UnsafePointer declaration. It compiles but not tested yet (because other errors avoid me to test) :
func getProductIdFromReceipt(_ data:Data) -> String?
{
let tempData: NSMutableData = NSMutableData(length: 26)!
data.withUnsafeBytes {
tempData.replaceBytes(in: NSMakeRange(0, data.count), withBytes: $0)
}
var p: UnsafePointer? = tempData.bytes.assumingMemoryBound(to: UInt8.self)
In Swift 3, you cannot init an UnsafePointer using an UnsafeRawPointer.
You can use assumingMemoryBound(to:) to convert an UnsafeRawPointer into an UnsafePointer<T>. Like this:
var ptr = data.bytes.assumingMemoryBound(to: UInt8.self)
Use debugDescription or distance(to:) to compare two pointer.
while(ptr.debugDescription < endPtr.debugDescription)
or
while(ptr.distance(to:endPtr) > 0)
It is possible to put a regular pointer sign i C & in front of a Int8 array or Uint8 array to make a pointer to supply a C-function input. Like the &aBuffer array below (but the data array must copied locally first, to keep control of the storage of the data storage until finished with the operation, you get an error else). Here in a routine handling dropInteraction data (delivering a byte array):
func interpretInstanceData(filename: String, Buffer: [UInt8]) -> String {
var aBuffer = Buffer
let sInstanceData = String(cString: Ios_C_InterpretInstanceData(filename, &aBuffer, Int32(aBuffer.count)))
The question is slightly old. But googling for a solution on the topic of converting a swift byte array to a C-pointer (what else is UnsafePointer< UInt8 >). This question made a hit. But I think this answer is helpful for later editions of Swift (that I use). Would have worked even then. Worked in any kind of use needing a pointer from swift (just make the array of right type first).
May have recently changed to just this, without the ".bytes." part:
var p: UnsafePointer = data.assumingMemoryBound(to: UInt8.self)
from the original:
var p = UnsafePointer<UInt8>(data.bytes)
Just downloaded Xcode 7 Beta, and this error appeared on enumerate keyword.
for (index, string) in enumerate(mySwiftStringArray)
{
}
Can anyone help me overcome this ?
Also, seems like count() is no longer working for counting length of String.
let stringLength = count(myString)
On above line, compiler says :
'count' is unavailable: access the 'count' property on the collection.
Has Apple has released any programming guide for Swift 2.0 ?
Many global functions have been replaced by protocol extension methods,
a new feature of Swift 2, so enumerate() is now an extension method
for SequenceType:
extension SequenceType {
func enumerate() -> EnumerateSequence<Self>
}
and used as
let mySwiftStringArray = [ "foo", "bar" ]
for (index, string) in mySwiftStringArray.enumerate() {
print(string)
}
And String does no longer conform to SequenceType, you have to
use the characters property to get the collection of Unicode
characters. Also, count() is a protocol extension method of
CollectionType instead of a global function:
let myString = "foo"
let stringLength = myString.characters.count
print(stringLength)
Update for Swift 3: enumerate() has been renamed to enumerated():
let mySwiftStringArray = [ "foo", "bar" ]
for (index, string) in mySwiftStringArray.enumerated() {
print(string)
}
There was an update for Swift 2 on using enumerate().
Instead of enumerate(...), people should use
... .enumerate()
The reason is that many global functions have been replaced by protocol extension methods and they will get an enumerate error.
Hope this helps.
All the best.
n
I know this is a old thread but I've just been messing around with Swift 2.0 and Playgrounds and I came across the same problem I thought I'd share a solution which uses the enumerate() method for a String
// This line works in Swift 1.2
// for (idx, character) in enumerate("A random string, it has a comma.")
// Swift 2.x
let count = inputString.characters
for (idx, character) in count.enumerate() where character == "," {
// Do something with idx
}
Hope this helps
Thanks
Kai
I am calling a Objective method from Swift by using a Bridging header.
-(NSString *) PatternSetCreator: (char)Signature detection_time_in_sec:(int)detection_time_in_sec patternLength:(int)patternLength maxPatternSetSize:(int)maxPatternSetSize
There are two issuse:
a) I am not able to pass a single character as parameter while calling this method from Swift
b) I am not exactly sure how to get the return type NSString and assign it to a String variable
A single C char in Swift is represented as CChar, a typealias for Int8.
(similarly, C int in Swift is CInt, a typealias for Int32)
If you want a specific character and are using Swift 1.2, there’s an initializer for UInt8 that takes a UnicodeScalar. Annoyingly, though, you have to then convert it to a Int8 to make it compatible with the C method:
let ch = CChar(UInt8(ascii: "x"))
let i = CInt(100)
let s = obj.PatternSetCreator(ch,
detection_time_in_sec: i,
patternLength: i,
maxPatternSetSize: i)
You should not need to do anything special to turn the returned NSString to a String. The bridging will do that automatically.
(or rather, it’ll return a String! – but if the objective c code is guaranteed to return a valid string every time and never a null pointer, the definition can be changed to -(nonnull NSString *) PatternSetCreator: etc… which means it will return a String instead)