Another alternative is to link libedit for proper line editing (arrow keys, etc.) and optional history support. I wanted this for a project I'm starting and put together a basic example for how I set it up.
Usage from swift
let prompt: Prompt = Prompt(argv0: C_ARGV[0])
while (true) {
if let line = prompt.gets() {
print("You typed \(line)")
}
}
ObjC wrapper to expose libedit
#import <histedit.h>
char* prompt(EditLine *e) {
return "> ";
}
@implementation Prompt
EditLine* _el;
History* _hist;
HistEvent _ev;
- (instancetype) initWithArgv0:(const char*)argv0 {
if (self = [super init]) {
// Setup the editor
_el = el_init(argv0, stdin, stdout, stderr);
el_set(_el, EL_PROMPT, &prompt);
el_set(_el, EL_EDITOR, "emacs");
// With support for history
_hist = history_init();
history(_hist, &_ev, H_SETSIZE, 800);
el_set(_el, EL_HIST, history, _hist);
}
return self;
}
- (void) dealloc {
if (_hist != NULL) {
history_end(_hist);
_hist = NULL;
}
if (_el != NULL) {
el_end(_el);
_el = NULL;
}
}
- (NSString*) gets {
// line includes the trailing newline
int count;
const char* line = el_gets(_el, &count);
if (count > 0) {
history(_hist, &_ev, H_ENTER, line);
return [NSString stringWithCString:line encoding:NSUTF8StringEncoding];
}
return nil;
}
@end
edit As of Swift 2.2 the standard library includes readLine. I'll also note Swift switched to markdown doc comments. Leaving my original answer for historical context.
Just for completeness, here is a Swift implementation of readln I've been using. It has an optional parameter to indicate the maximum number of bytes you want to read (which may or may not be the length of the String).
This also demonstrates the proper use of swiftdoc comments - Swift will generate a <project>.swiftdoc file and Xcode will use it.
///reads a line from standard input
///
///:param: max specifies the number of bytes to read
///
///:returns: the string, or nil if an error was encountered trying to read Stdin
public func readln(max:Int = 8192) -> String? {
assert(max > 0, "max must be between 1 and Int.max")
var buf:Array<CChar> = []
var c = getchar()
while c != EOF && c != 10 && buf.count < max {
buf.append(CChar(c))
c = getchar()
}
//always null terminate
buf.append(CChar(0))
return buf.withUnsafeBufferPointer { String.fromCString($0.baseAddress) }
}
I swear to God.. the solution to this utterly basic problem eluded me for YEARS. It's SO simple.. but there is so much vague / bad information out there; hopefully I can save someone from some of the bottomless rabbit holes that I ended up in...
So then, lets's get a "string" from "the user" via "the console", via stdin, shall we?
I just wanted to comment (I have not enough reps) on xenadu's implementation, because CChar in OS X is Int8, and Swift does not like at all when you add to the array when getchar() returns parts of UTF-8, or anything else above 7 bit.
I am using an array of UInt8 instead, and it works great and String.fromCString converts the UInt8 into UTF-8 just fine.
However this is how I done it
func readln() -> (str: String?, hadError: Bool) {
var cstr: [UInt8] = []
var c: Int32 = 0
while c != EOF {
c = getchar()
if (c == 10 || c == 13) || c > 255 { break }
cstr.append(UInt8(c))
}
cstr.append(0)
return String.fromCStringRepairingIllFormedUTF8(UnsafePointer<CChar>(cstr))
}
while true {
if let mystring = readln().str {
println(" > \(mystring)")
}
}
I have now been able to get Keyboard input in Swift by using the following:
In my main.swift file I declared a variable i and assigned to it the function GetInt() which I defined in Objective C. Through a so called Bridging Header where I declared the function prototype for GetInt I could link to main.swift. Here are the files:
main.swift:
var i: CInt = GetInt()
println("Your input is \(i) ");
Bridging Header:
#include "obj.m"
int GetInt();
obj.m:
#import <Foundation/Foundation.h>
#import <stdio.h>
#import <stdlib.h>
int GetInt()
{
int i;
scanf("%i", &i);
return i;
}
In obj.m it is possible to include the c standard output and input, stdio.h, as well as the c standard library stdlib.h which enables you to program in C in Objective-C, which means there is no need for including a real swift file like user.c or something like that.
Hope I could help,
Edit: It is not possible to get String input through C because here I am using the CInt -> the integer type of C and not of Swift. There is no equivalent Swift type for the C char*. Therefore String is not convertible to string. But there are fairly enough solutions around here to get String input.
Here is simple example of taking input from user on console based application:
You can use readLine(). Take input from console for first number then press enter. After that take input for second number as shown in the image below:
func solveMefirst(firstNo: Int , secondNo: Int) -> Int {
return firstNo + secondNo
}
let num1 = readLine()
let num2 = readLine()
var IntNum1 = Int(num1!)
var IntNum2 = Int(num2!)
let sum = solveMefirst(IntNum1!, secondNo: IntNum2!)
print(sum)
In general readLine() function is used for scanning input from console. But it will not work in normal iOS project until or unless you add "command-line tool".
The best way for testing, you can do :
1. Create an macOS file
2. Use the readLine() func to scan optional String from console
import Foundation
print("Please enter some input\n")
if let response = readLine() {
print("output :",response)
} else {
print("Nothing")
}
Output :
Please enter some input
Hello, World
output : Hello, World
Program ended with exit code: 0
The top ranked answer to this question suggests using the readLine() method to take in user input from the command line. However, I want to note that you need to use the ! operator when calling this method to return a string instead of an optional:
Lots of outdated answers to this question. As of Swift 2+ the Swift Standard Library contains the readline() function. It will return an Optional but it will only be nil if EOF has been reached, which will not happen when getting input from the keyboard so it can safely be unwrapped by force in those scenarios. If the user does not enter anything its (unwrapped) value will be an empty string. Here's a small utility function that uses recursion to prompt the user until at least one character has been entered:
Note that using optional binding (if let input = readLine()) to check if something was entered as proposed in other answers will not have the desired effect, as it will never be nil and at least "" when accepting keyboard input.
This will not work in a Playground or any other environment where you does not have access to the command prompt. It seems to have issues in the command-line REPL as well.
import Foundation
print("Enter a number")
let number : Int = Int(readLine(strippingNewline: true)!) ?? 0
if(number < 5)
{
print("Small")
}else{
print("Big")
}
for i in 0...number{
print(i)
}