I am trying swift on IBM's new Swift online Sandbox.
The following script is not running on that sandbox: http://swiftlang.ng.bluemix.net/
import Foundation
func palindromTest(s: String) -> Bool{
let lower = s.lowercaseString
let letters = NSCharacterSet.letterCharacterSet()
let onlyLetters = lower.characters.filter({String($0).rangeOfCharacterFromSet(letters) != nil})
let reverseLetters = Array(onlyLetters).reverse()
return String(onlyLetters) == String(reverseLetters)
}
palindromTest("abc")
The sandbox prints the following error message:
/swift-execution/code-tmp.swift:7:48: error: value of type 'String' has no member 'rangeOfCharacterFromSet'
let onlyLetters = lower.characters.filter({String($0).rangeOfCharacterFromSet(letters) != nil})
Did I forget to import something? Hope you can help me.
Thanks.
rangeOfCharacterFromSet is one of the many String methods
which are actually handled by NSString.
From this commit
to NSString.swift, it seems that support for rangeOfCharacterFromSet
was added only recently to the (non-Apple) Foundation library,
so you cannot use it until the IBM Swift Sandbox is updated to use
a new Swift version.
FWIW, this works now in the Sandbox swiftlang.ng.bluemix.net/#/repl/aae5d1caf4e0a6232ff428c3a0160e6e98cba6ed913ce9176f2baee46d30cb1c
Lots of things have changed since these answers were given. I've updated Pat's Sandbox answer for Swift 3.1.1. Below is the code and a summary of the API changes.
Several methods in the standard library and Foundation have since been renamed:
Array: .reverse() → .reversed()
String: .rangeOfCharacterFromSet() → .rangeOfCharacter(from:)
(NS)CharacterSet: .lettersCharacterSet() → .letters
I also made a slight change, perhaps just from personal preference. Rather than use String.rangeOfCharacter(from:) I chose to use CharacterSet.contains(), which required I use String.unicodeScalars instead of .characters. Not sure about the performance tradeoffs in general, but I liked how cleaned things up.
import Foundation
func palindromTest(_ word: String) -> Bool{
let letters = CharacterSet.letters
let onlyLetters = word.lowercased().unicodeScalars.filter {
letters.contains($0)
}.map { Character($0) }
let reverseLetters = onlyLetters.reversed()
print("\(String(onlyLetters)) == \(String(reverseLetters))")
return String(onlyLetters) == String(reverseLetters)
}
print(palindromTest("abc"))
// false
print(palindromTest("dud"))
// true
Related
I am very green with Xcode (apologize in advance). Trying to bring some old code to life. Getting the following with trying to move to Swift 5.
withUnsafeMutableBytes' is deprecated: use withUnsafeMutableBytes<R>(_: (UnsafeMutableRawBufferPointer) throws -> R) rethrows -> R instead
Goal: All I need to do is modify the code appropriately and be done.
I have looked at other Stack Overflow messages, searched various articles, experimented with different things, but can't quickly determine what needs to change. I am sure the solution is super simple for someone that knows more.
var responseData = Data(count: Int(responseDataLength))
_ = responseData.withUnsafeMutableBytes
{
mfError = MFMediaIDResponse_GetAsString(mfMediaIdResponsePtr.pointee, MFString($0), responseDataLength)
}
Here is my example, when refreshing old code of withUnsafeMutableBytes
hope it helps
The old one:
_ = data.withUnsafeMutableBytes { (bytes: UnsafeMutablePointer<UInt8>) in
memcpy((ioData.pointee.mBuffers.mData?.assumingMemoryBound(to: UInt8.self))!, bytes, dataCount)
}
The new one:
_ = data.withUnsafeMutableBytes { (rawMutableBufferPointer) in
let bufferPointer = rawMutableBufferPointer.bindMemory(to: UInt8.self)
if let address = bufferPointer.baseAddress{
memcpy((ioData.pointee.mBuffers.mData?.assumingMemoryBound(to: UInt8.self))!, address, dataCount)
}
}
Explains:
use UnsafeMutablePointer<ContentType>, you get an unsafeMutablePointer in its closure.
To access to its memory, so need to typed it with bindMemory,
more details on Apple Pointer Doc
I recently found that the code below ends up with hash collision.
FYI, I'm using XCode 9.4.1 (9F2000), which uses Swift 4.1.2
import Foundation
let lhs = "あいうえおあいう21あいうえ"
let rhs = "あいうえおあいう22あいうえ"
let percentEncodedLhs = lhs.addingPercentEncoding(withAllowedCharacters: .urlPathAllowed)!
let percentEncodedRhs = rhs.addingPercentEncoding(withAllowedCharacters: .urlPathAllowed)!
let lhsHashValue = lhs.hashValue
let rhsHashValue = rhs.hashValue
let lhsPercentHashValue = percentEncodedLhs.hashValue
let rhsPercentHashValue = percentEncodedRhs.hashValue
print(lhsHashValue == rhsHashValue)
print(lhsPercentHashValue == rhsPercentHashValue)
/*
Output:
false
true
*/
I know that hash collision can happen in some circumstance, but I couldn't find how Swift calculates hashValue for String.
For example, Java calculates hashCode of String like:
https://docs.oracle.com/javase/6/docs/api/java/lang/String.html#hashCode()
Is there any official explanation or maybe some assumptions?
but I couldn't find how Swift calculates hashValue for String
Why couldn’t you? Swift is open source. If you are interested, read the source.
https://github.com/apple/swift/blob/111499d2bfc58dc12fcb9cd1ce1dda7978c995b7/stdlib/public/core/StringHashable.swift
I had this all working in Swift 3 and earlier but with Swift 4 no matter what variation I use this code will instead output text as a URL. If I put in "This is my sample text" the output after pasting the clipboard will be "This%20is%20my%20sample%20text". I have tried KuTTypeFileURL but that doesn't appear to make any difference either. What am I missing here? I have seen posts and discussions about how Apple is changing Pboards and other issues with sandboxing but I can't seem to figure this out at all.
original code what was working in swift 3 and earlier
private func copyToClipBoard(textToCopy: String) {
let pasteBoard = NSPasteboard.general()
pasteBoard.clearContents()
pasteBoard.setString(textToCopy, forType: NSStringPboardType)
}
This gives an error of
'NSStringPboardType' is unavailable in Swift: use 'PasteboardType.string'
After searching online I came across these posts that describe the same issue and the workaround was to use the kuTTypeUrl as String
Found here stackoverflow.com/questions/44537356/… and here forums.developer.apple.com/thread/79144
When I try it this way it simply outputs as a URL when I just need a String.
#IBOutlet weak var nameTextField: NSTextField!
#IBAction func nameCopy(_ sender: Any) {
copyToClipBoard(textToCopy: nameTextField.stringValue)
}
let NSStringPboardType = NSPasteboard.PasteboardType(kUTTypeURL as String)
private func copyToClipBoard(textToCopy: String) {
let pasteBoard = NSPasteboard.general
pasteBoard.clearContents()
pasteBoard.setString(textToCopy, forType: NSStringPboardType)
}
You are pasting an URL because you created a PasteboardType kUTTypeURL.
The solution is much simpler, there is a predefined string type
private func copyToClipBoard(textToCopy: String) {
let pasteBoard = NSPasteboard.general
pasteBoard.clearContents()
pasteBoard.setString(textToCopy, forType: .string)
}
The note in the documentation
Apps that adopt App Sandbox cannot access files identified using the string pasteboard type. Instead, use an NSURL object, a bookmark, or a filename pasteboard type.
is related to files (aka string paths), not to regular strings
I just ran into a similar issue. My code looked like this:
NSPasteboard.general.setString("Hello World", forType: .string)
Unfortunately, this didn't work. But I figured there is a bug that if you don't store the NSPasteboard.general into a variable, the object created as part of the general computed property gets deinitialized before the setString change is propagated to the system.
So if you tried doing this in one line like me, just split it up to two instead, which worked for me:
let pasteboard = NSPasteboard.general
pasteboard.setString("Hello World", forType: .string)
I reported this bug via Feedback Assistant to Apple (FB9988062).
UPDATE:
Apple answered my bug report, stating that you need to call declareTypes before setting a value, like so:
NSPasteboard.general.declareTypes([.string], owner: nil)
If I have a string variable with JXA source code, is there a way to run that from swift? It seems NSAppleScript only works with AppleScript source.
Here is an example showing how to use OSAKit from Swift to run a JavaScript for Automation script stored in a string:
import OSAKit
let scriptString = "Math.PI";
let script = OSAScript.init(source: scriptString, language: OSALanguage.init(forName: "JavaScript"));
var compileError : NSDictionary?
script.compileAndReturnError(&compileError)
if let compileError = compileError {
print(compileError);
return;
}
var scriptError : NSDictionary?
let result = script.executeAndReturnError(&scriptError)
if let scriptError = scriptError {
print(scriptError);
}
else if let result = result?.stringValue {
print(result)
}
This Swift code was adapted from the Hammerspoon source code (Objective-C).
Why? JXA is a dog in every respect. If you just want to run JS code, the JavaScriptCore Obj-C API is far cleaner and easier. If you want to control "AppleScriptable" applications then use AppleScript—it's the only officially supported† option that works right.
(† There is SwiftAutomation, but Apple haven't bitten and I'm not inclined to support it myself given Apple's chronic mismanagement of Mac Automation. We'll see what happens with WWDC17.)
Trying to follow along and code the Smashtag project while watching the Lecture 10 iTunes video.
When I add the dowloaded Twitter package to my Smashtag project, XCode couldn't find the Tweet class when I made reference to it in the TweetTableViewController.
Because of the problem described above, I added the four classes belonging to the Twitter package individually to the project. XCode found the four classes but adding them in this manner generated 11 compile errors.
I'm using XCode Version 6.3 (6D570) which is subsequent to the iOS 8.3 release.
Has anyone else encountered this issue?
Thank you for reading my question.
~ Lee
Possibly not the most-correct (read: best practice) way to do this, but I'm going to chalk it up to doing what it takes to finish the course.
I just went through the list of compile errors and changed the relevant properties to var instead of let. Constants can't be changed and in the new version of Swift they can only be instantiated once. So for the sake of not rewriting too much code, I chose to make certain properties vars instead of lets.
Other bugs I found following the iTunes U course:
The named ‘handler:’ argument needs the name explicitly in a few places.
The simulator will show "TwitterRequest: Couldn\'t discover Twitter account type.” until you go to Settings (inside the simulator) and set the Twitter account. At this point I had to reboot the device, as the call is made in the ViewDidLoad, and thus is only called the first time the view loads. (Alternatively, you could close out the app from the app switcher in the simulator and relaunch that way.)
Here is a gist with corrected code that you can use as a Twitter package that will work with the course and has fixes for the aforementioned bugs, minus the Twitter account setting:
https://gist.github.com/mattpetters/ccf87678ccce0c354398
As Christian R. Jimenez said, "I went to Settings in the Simulated iphone and add my Twitter Account. And everything works perfect." in http://cs193p.m2m.at/cs193p-lecture-10-table-view-winter-2015/. I just added my Twitter Account and tested it, it works!
I had similar problems with the Twitter packages using Swift 2.0 and Xcode 7.2
I'm very new to Swift, so there is a good chance the changes I made are not best practices, but the updated files do work: https://gist.github.com/awaxman11/9c48c0b4c622bffb879f.
For the most part I used Xcode's suggested changes. The two larger changes I made were:
In Tweet.swift I updated the the IndexedKeyword struct's init method to use advanceBy() instead of advance()
In TwitterRequest.swift I updated the signature of NSJSONSerialization to conform to the new error handling system
I've just had a big session fixing the Twitter package files for this same version of Xcode.
It seems that what has broken is that in this version of Swift, constants ('let x...') may only be initialized once, so if a constant is given a value in the declaration ('let x = false'), it may not be changed in the init() function. The Twitter package gives some constants initial values, but then changes the values in the init() function.
My solution to this was to follow the styles suggested in the current version of the Apple Swift language book: declare (many of) the constants as implicitly unwrapped optionals, unconditionally assign a value to them in the init() function (which value may be nil), then test whether any of them are nil, and, if so, return nil from init().
See https://developer.apple.com/library/mac/documentation/Swift/Conceptual/Swift_Programming_Language/Initialization.html, click "On This Page" and choose "Failable Initializers"
Also, in TwitterRequest.swift, I needed to add the parameter name 'handler:' in a couple of calls to performTwitterRequest(request, handler: handler).
As an example of constant initialization, in MediaItem.swift:
<< Original Code >>
...
public let aspectRatio: Double = 0
...
init?(data: NSDictionary?) {
var valid = false
if let urlString = data?.valueForKeyPath(TwitterKey.MediaURL) as? NSString {
if let url = NSURL(string: urlString) {
self.url = url
let h = data?.valueForKeyPath(TwitterKey.Height) as? NSNumber
let w = data?.valueForKeyPath(TwitterKey.Width) as? NSNumber
if h != nil && w != nil && h?.doubleValue != 0 {
aspectRatio = w!.doubleValue / h!.doubleValue
valid = true
}
}
}
if !valid {
return nil
}
}
...
<< Updated code >>
...
public let aspectRatio: Double
...
init?(data: NSDictionary?) {
if let urlString = data?.valueForKeyPath(TwitterKey.MediaURL) as? NSString {
if let url = NSURL(string: urlString as String) {
self.url = url
let h = data?.valueForKeyPath(TwitterKey.Height) as? NSNumber
let w = data?.valueForKeyPath(TwitterKey.Width) as? NSNumber
if h != nil && w != nil && h?.doubleValue != 0 {
aspectRatio = w!.doubleValue / h!.doubleValue
return
}
}
}
return nil
}
...