The answer in
How to strip special characters out of string?
is not working.
Here is what I got and it gives me an error
func removeSpecialCharsFromString(str: String) -> String {
let chars: Set<String> = Set(arrayLiteral: "abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLKMNOPQRSTUVWXYZ1234567890+-*=(),.:!_")
return String(str.characters.filter { chars.contains($0) }) //error here at $0
}
The error at $0 says
_Element (aka Character) cannot be converted to expected argument type 'String'.
Like this:
func removeSpecialCharsFromString(text: String) -> String {
let okayChars : Set<Character> =
Set("abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLKMNOPQRSTUVWXYZ1234567890+-*=(),.:!_".characters)
return String(text.characters.filter {okayChars.contains($0) })
}
And here's how to test:
let s = removeSpecialCharsFromString("père") // "pre"
SWIFT 4:
func removeSpecialCharsFromString(text: String) -> String {
let okayChars = Set("abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLKMNOPQRSTUVWXYZ1234567890+-=().!_")
return text.filter {okayChars.contains($0) }
}
More cleaner way:
extension String {
var stripped: String {
let okayChars = Set("abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLKMNOPQRSTUVWXYZ1234567890+-=().!_")
return self.filter {okayChars.contains($0) }
}
}
Use this extension like:
let myCleanString = "some.Text##$".stripped
Output: "some.Text"
I think that a cleaner solution could be this approach:
extension String {
var alphanumeric: String {
return self.components(separatedBy: CharacterSet.alphanumerics.inverted).joined().lowercased()
}
}
Try this:
someString.removeAll(where: {$0.isPunctuation})
In Swift 1.2,
let chars = Set("abcde...")
created a set containing all characters from the given string.
In Swift 2.0 this has to be done as
let chars = Set("abcde...".characters)
The reason is that a string itself does no longer conform to
SequenceType, you have to use the characters view explicitly.
With that change, your method compiles and works as expected:
func removeSpecialCharsFromString(str: String) -> String {
let chars = Set("abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLKMNOPQRSTUVWXYZ1234567890+-*=(),.:!_".characters)
return String(str.characters.filter { chars.contains($0) })
}
let cleaned = removeSpecialCharsFromString("ab€xy")
print(cleaned) // abxy
Remark: #Kametrixom suggested to create the set only once. So if there is
performance issue with the above method you can either move the
declaration of the set outside of the function, or make it a
local static:
func removeSpecialCharsFromString(str: String) -> String {
struct Constants {
static let validChars = Set("abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLKMNOPQRSTUVWXYZ1234567890+-*=(),.:!_".characters)
}
return String(str.characters.filter { Constants.validChars.contains($0) })
}
without removing spaces between words
extension String {
var removeSpecialCharacters: String {
return self.components(separatedBy: CharacterSet.alphanumerics.inverted).filter({ !$0.isEmpty }).joined(separator: " ")
}
}
Related
I have a method that loads an array of dictionaries from a propertylist. Then I change those arrays of dictionaries to array of a defined custom type;
I want to write that method in generic form so I call that method with the type I expect, then the method loads it and returns an array of my custom type rather than dictionaries
func loadPropertyList(fileName: String) -> [[String:AnyObject]]?
{
if let path = NSBundle.mainBundle().pathForResource(fileName, ofType: "plist")
{
if let plistXML = NSFileManager.defaultManager().contentsAtPath(path)
{
do {
if let temp = try NSPropertyListSerialization.propertyListWithData(plistXML, options: .Immutable, format: nil) as? [[String:AnyObject]]
{
return temp
}
}catch{}
}
}
return nil
}
//
func loadList<T>(fileName: String) -> [T]?{//**Here the answer I am expecting**}
I am assuming your function to read from a Plist works and that you don't want to subclass NSObject.
Since Swift reflecting does not support setting values this is not possible without some implementation for each Type you want this to work for.
It can however be done in a pretty elegant way.
struct PlistUtils { // encapsulate everything
static func loadPropertyList(fileName: String) -> [[String:AnyObject]]? {
if let path = NSBundle.mainBundle().pathForResource(fileName, ofType: "plist") {
if let plistXML = NSFileManager.defaultManager().contentsAtPath(path) {
do {
if let temp = try NSPropertyListSerialization.propertyListWithData(plistXML, options: .Immutable, format: nil) as? [[String:AnyObject]] {
return temp
}
} catch {
return nil
}
}
}
return nil
}
}
This protocol will be used in a generic fashion to get the Type name and read the corresponding Plist.
protocol PListConstructible {
static func read() -> [Self]
}
This protocol will be used to implement Key Value setters.
protocol KeyValueSettable {
static func set(fromKeyValueStore values:[String:AnyObject]) -> Self
}
This is the combination of both to generate an array of objects. This does require that the Plist is named after the Type.
extension PListConstructible where Self : KeyValueSettable {
static func read() -> [Self] {
let name = String(reflecting: self)
var instances : [Self] = []
if let data = PlistUtils.loadPropertyList(name) {
for entry in data {
instances.append(Self.set(fromKeyValueStore: entry))
}
}
return instances
}
}
This is some Type.
struct Some : PListConstructible {
var alpha : Int = 0
var beta : String = ""
}
All you have to do is implement the Key Value setter and it will now be able to be read from a Plist.
extension Some : KeyValueSettable {
static func set(fromKeyValueStore values: [String : AnyObject]) -> Some {
var some = Some()
some.alpha = (values["alpha"] as? Int) ?? some.alpha
some.beta = (values["beta"] as? String) ?? some.beta
return some
}
}
This is how you use it.
Some.read()
so i've created function and it does something, but i want it to do something else if the parameter type is different, for example:
func (parameter: unknownType){
if(typeof parameter == Int){
//do this
}else if(typeof parameter == String){
//do that
}
}
i've done this in javascript or other programming languages, but i don't know how to do this in swift
i've created function which takes 1 argument UITextField and centers it using constraints
now i want to center my button, but since button is not UITextField type it does not work, so is there a way i can tell function to do the same on UIButton??
Use Overload:
class Example
{
func method(a : String) -> NSString {
return a;
}
func method(a : UInt) -> NSString {
return "{\(a)}"
}
}
Example().method("Foo") // "Foo"
Example().method(123) // "{123}"
The equivalent of the Javascript code would be:
func doSomething(parameter: Any?) {
if let intValue = parameter as? Int {
// do something with the int
} else if let stringValue = parameter as? String {
// do something with the string
}
}
But be warned, this approach makes you loose the type safety which is one of most useful feature of Swift.
A better approach would be to declare a protocol that is implemented by all types that you want to allow to be passed to doSomething:
protocol MyProtocol {
func doSomething()
}
extension Int: MyProtocol {
func doSomething() {
print("I am an int")
}
}
extension String: MyProtocol {
func doSomething() {
print("I am a string")
}
}
func doSomething(parameter: MyProtocol) {
parameter.doSomething()
}
doSomething(1) // will print "I am an int"
doSomething("a") // will print "I am a string"
doSomething(14.0) // compiler error as Double does not conform to MyProtocol
It can
Sample code:
func temFunc(obj:AnyObject){
if let intValue = obj as? Int{
print(intValue)
}else if let str = obj as? String{
print(str)
}
}
You can make use of Any and downcasting:
func foo(bar: Any){
switch(bar) {
case let a as String:
/* do something with String instance 'a' */
print("The bar is a String, bar = " + a)
case let a as Int:
/* do something with Int instance 'a' */
print("The bar is an Int, bar = \(a)")
case _ : print("The bar is neither an Int nor a String, bar = \(bar)")
}
}
/* Example */
var myString = "Hello"
var myInt = 1
var myDouble = 1.5
foo(myString) // The bar is a String, bar = Hello
foo(myInt) // The bar is an Int, bar = 1
foo(myDouble) // The bar is neither an Int nor a String, bar = 1.5
Check this solution:
https://stackoverflow.com/a/25528882/256738
You can pass an object AnyObject and check the class in order to know what kind of object it is.
UPDATE
Good point #Vojtech Vrbka
Here an example:
let x : AnyObject = "abc"
switch x {
case is String: println("I'm a string")
case is Array: println("I'm an Array")
// Other cases
default: println("Unknown")
}
I have been trying to fix all my code since swift 2.0 update. I have a problem that seems to be the way tuples work now:
public func generate() -> AnyGenerator <(String, JSON)> {
switch self.type {
case .Array:
let array_ = object as! [AnyObject]
var generate_ = array_.generate()
var index_: Int = 0
return anyGenerator{
if let element_: AnyObject = generate_.next() {
return ("\(index_++)", JSON(element_))
} else {
return nil
}
}
case .Dictionary:
let dictionary_ = object as! [String : AnyObject]
var generate_ = dictionary_.generate()
return anyGenerator{
if let (key_: String, value_: AnyObject) = generate_.next() {
return (key_, JSON(value_))
} else {
return nil
}
}
default:
return anyGenerator{
return nil
}
}
}
Specifically the line:
if let (key_: String, value_: AnyObject) = generate_.next()
Is throwing the error: Tuple pattern element label 'key' must be '_'
I tried to make that change already, but I didnt work...
Any ideas?
The problem is: We cannot use type annotation inside of tuple patterns anymore.
In the release notes:
Type annotations are no longer allowed in patterns and are considered part of the outlying declaration. This means that code previously written as:
var (a : Int, b : Float) = foo()
needs to be written as:
var (a,b) : (Int, Float) = foo()
if an explicit type annotation is needed. The former syntax was ambiguous with tuple element labels. (20167393)
So, you can:
if let (key_, value_): (String, AnyObject) = generate_.next() {
But in this case, you could omit : (String, AnyObject):
if let (key_, value_) = generate_.next() {
Given the name of a file in the bundle, I want load the file into my Swift app. So I need to use this method:
let soundURL = NSBundle.mainBundle().URLForResource(fname, withExtension: ext)
For whatever reason, the method needs the filename separated from the file extension. Fine, it's easy enough to separate the two in most languages. But so far I'm not finding it to be so in Swift.
So here is what I have:
var rt: String.Index = fileName.rangeOfString(".", options:NSStringCompareOptions.BackwardsSearch)
var fname: String = fileName .substringToIndex(rt)
var ext = fileName.substringFromIndex(rt)
If I don't include the typing on the first line, I get errors on the two subsequent lines. With it, I'm getting an error on the first line:
Cannot convert the expression's type '(UnicodeScalarLiteralConvertible, options: NSStringCompareOptions)' to type 'UnicodeScalarLiteralConvertible'
How can I split the filename from the extension? Is there some elegant way to do this?
I was all excited about Swift because it seemed like a much more elegant language than Objective C. But now I'm finding that it has its own cumbersomeness.
Second attempt: I decided to make my own string-search method:
func rfind(haystack: String, needle: Character) -> Int {
var a = Array(haystack)
for var i = a.count - 1; i >= 0; i-- {
println(a[i])
if a[i] == needle {
println(i)
return i;
}
}
return -1
}
But now I get an error on the line var rt: String.Index = rfind(fileName, needle: "."):
'Int' is not convertible to 'String.Index'
Without the cast, I get an error on the two subsequent lines.
Can anyone help me to split this filename and extension?
Swift 5.0 update:
As pointed out in the comment, you can use this.
let filename: NSString = "bottom_bar.png"
let pathExtention = filename.pathExtension
let pathPrefix = filename.deletingPathExtension
This is with Swift 2, Xcode 7: If you have the filename with the extension already on it, then you can pass the full filename in as the first parameter and a blank string as the second parameter:
let soundURL = NSBundle.mainBundle()
.URLForResource("soundfile.ext", withExtension: "")
Alternatively nil as the extension parameter also works.
If you have a URL, and you want to get the name of the file itself for some reason, then you can do this:
soundURL.URLByDeletingPathExtension?.lastPathComponent
Swift 4
let soundURL = NSBundle.mainBundle().URLForResource("soundfile.ext", withExtension: "")
soundURL.deletingPathExtension().lastPathComponent
Works in Swift 5. Adding these behaviors to String class:
extension String {
func fileName() -> String {
return URL(fileURLWithPath: self).deletingPathExtension().lastPathComponent
}
func fileExtension() -> String {
return URL(fileURLWithPath: self).pathExtension
}
}
Example:
let file = "image.png"
let fileNameWithoutExtension = file.fileName()
let fileExtension = file.fileExtension()
Solution Swift 4
This solution will work for all instances and does not depend on manually parsing the string.
let path = "/Some/Random/Path/To/This.Strange.File.txt"
let fileName = URL(fileURLWithPath: path).deletingPathExtension().lastPathComponent
Swift.print(fileName)
The resulting output will be
This.Strange.File
In Swift 2.1 String.pathExtension is not available anymore. Instead you need to determine it through NSURL conversion:
NSURL(fileURLWithPath: filePath).pathExtension
In Swift you can change to NSString to get extension faster:
extension String {
func getPathExtension() -> String {
return (self as NSString).pathExtension
}
}
Latest Swift 4.2 works like this:
extension String {
func fileName() -> String {
return URL(fileURLWithPath: self).deletingPathExtension().lastPathComponent
}
func fileExtension() -> String {
return URL(fileURLWithPath: self).pathExtension
}
}
In Swift 2.1, it seems that the current way to do this is:
let filename = fileURL.URLByDeletingPathExtension?.lastPathComponent
let extension = fileURL.pathExtension
Swift 5 with code sugar
extension String {
var fileName: String {
URL(fileURLWithPath: self).deletingPathExtension().lastPathComponent
}
var fileExtension: String{
URL(fileURLWithPath: self).pathExtension
}
}
SWIFT 3.x Shortest Native Solution
let fileName:NSString = "the_file_name.mp3"
let onlyName = fileName.deletingPathExtension
let onlyExt = fileName.pathExtension
No extension or any extra stuff
(I've tested. based on #gabbler solution for Swift 2)
Swift 5
URL.deletingPathExtension().lastPathComponent
Strings in Swift can definitely by tricky. If you want a pure Swift method, here's how I would do it:
Use find to find the last occurrence of a "." in the reverse of the string
Use advance to get the correct index of the "." in the original string
Use String's subscript function that takes an IntervalType to get the strings
Package this all up in a function that returns an optional tuple of the name and extension
Something like this:
func splitFilename(str: String) -> (name: String, ext: String)? {
if let rDotIdx = find(reverse(str), ".") {
let dotIdx = advance(str.endIndex, -rDotIdx)
let fname = str[str.startIndex..<advance(dotIdx, -1)]
let ext = str[dotIdx..<str.endIndex]
return (fname, ext)
}
return nil
}
Which would be used like:
let str = "/Users/me/Documents/Something.something/text.txt"
if let split = splitFilename(str) {
println(split.name)
println(split.ext)
}
Which outputs:
/Users/me/Documents/Something.something/text
txt
Or, just use the already available NSString methods like pathExtension and stringByDeletingPathExtension.
Swift 5
URL(string: filePath)?.pathExtension
Try this for a simple Swift 4 solution
extension String {
func stripExtension(_ extensionSeperator: Character = ".") -> String {
let selfReversed = self.reversed()
guard let extensionPosition = selfReversed.index(of: extensionSeperator) else { return self }
return String(self[..<self.index(before: (extensionPosition.base.samePosition(in: self)!))])
}
}
print("hello.there.world".stripExtension())
// prints "hello.there"
Swift 3.0
let sourcePath = NSURL(string: fnName)?.pathExtension
let pathPrefix = fnName.replacingOccurrences(of: "." + sourcePath!, with: "")
Swift 3.x extended solution:
extension String {
func lastPathComponent(withExtension: Bool = true) -> String {
let lpc = self.nsString.lastPathComponent
return withExtension ? lpc : lpc.nsString.deletingPathExtension
}
var nsString: NSString {
return NSString(string: self)
}
}
let path = "/very/long/path/to/filename_v123.456.plist"
let filename = path.lastPathComponent(withExtension: false)
filename constant now contains "filename_v123.456"
A better way (or at least an alternative in Swift 2.0) is to use the String pathComponents property. This splits the pathname into an array of strings. e.g
if let pathComponents = filePath.pathComponents {
if let last = pathComponents.last {
print(" The last component is \(last)") // This would be the extension
// Getting the last but one component is a bit harder
// Note the edge case of a string with no delimiters!
}
}
// Otherwise you're out of luck, this wasn't a path name!
They got rid of pathExtension for whatever reason.
let str = "Hello/this/is/a/filepath/file.ext"
let l = str.componentsSeparatedByString("/")
let file = l.last?.componentsSeparatedByString(".")[0]
let ext = l.last?.componentsSeparatedByString(".")[1]
A cleaned up answer for Swift 4 with an extension off of PHAsset:
import Photos
extension PHAsset {
var originalFilename: String? {
if #available(iOS 9.0, *),
let resource = PHAssetResource.assetResources(for: self).first {
return resource.originalFilename
}
return value(forKey: "filename") as? String
}
}
As noted in XCode, the originalFilename is the name of the asset at the time it was created or imported.
Maybe I'm getting too late for this but a solution that worked for me and consider quite simple is using the #file compiler directive. Here is an example where I have a class FixtureManager, defined in FixtureManager.swift inside the /Tests/MyProjectTests/Fixturesdirectory. This works both in Xcode and withswift test`
import Foundation
final class FixtureManager {
static let fixturesDirectory = URL(fileURLWithPath: #file).deletingLastPathComponent()
func loadFixture(in fixturePath: String) throws -> Data {
return try Data(contentsOf: fixtureUrl(for: fixturePath))
}
func fixtureUrl(for fixturePath: String) -> URL {
return FixtureManager.fixturesDirectory.appendingPathComponent(fixturePath)
}
func save<T: Encodable>(object: T, in fixturePath: String) throws {
let data = try JSONEncoder().encode(object)
try data.write(to: fixtureUrl(for: fixturePath))
}
func loadFixture<T: Decodable>(in fixturePath: String, as decodableType: T.Type) throws -> T {
let data = try loadFixture(in: fixturePath)
return try JSONDecoder().decode(decodableType, from: data)
}
}
Creates unique "file name" form url including two previous folders
func createFileNameFromURL (colorUrl: URL) -> String {
var arrayFolders = colorUrl.pathComponents
// -3 because last element from url is "file name" and 2 previous are folders on server
let indx = arrayFolders.count - 3
var fileName = ""
switch indx{
case 0...:
fileName = arrayFolders[indx] + arrayFolders[indx+1] + arrayFolders[indx+2]
case -1:
fileName = arrayFolders[indx+1] + arrayFolders[indx+2]
case -2:
fileName = arrayFolders[indx+2]
default:
break
}
return fileName
}
I am trying to rewrite my logging class and I would like to know how to substitute PRETTY_FUNCTION or NSStringFromSelector(_cmd) in a swift file in order to track the method calls?
Special literals in swift are as follows (from [the swift guide]
#file String The name of the file in which it appears.
#line Int The line number on which it appears.
#column Int The column number in which it begins.
#function String The name of the declaration in which it appears.
Prior to Swift 2.2b4, these were
(https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Expressions.html)):
__FILE__ String The name of the file in which it appears.
__LINE__ Int The line number on which it appears.
__COLUMN__ Int The column number in which it begins.
__FUNCTION__ String The name of the declaration in which it appears.
You can use these in logging statements like so:
println("error occurred on line \(__LINE__) in function \(__FUNCTION__)")
Check out a new library I've just published: https://github.com/DaveWoodCom/XCGLogger
It's a debug logging library for Swift.
The key to being able to use the #function macros, is to set them as default values to your logging function. The compiler will then fill them in using the expected values.
func log(logMessage: String, functionName: String = #function) {
print("\(functionName): \(logMessage)")
}
Then just call:
log("my message")
And it works as expected giving you something like:
whateverFunction(): my message
More info on how this works: https://www.cerebralgardens.com/blog/entry/2014/06/09/the-first-essential-swift-3rd-party-library-to-include-in-your-project
I'd use something like this:
func Log(message: String = "", _ path: String = __FILE__, _ function: String = __FUNCTION__) {
let file = path.componentsSeparatedByString("/").last!.componentsSeparatedByString(".").first! // Sorry
NSLog("\(file).\(function): \(message)")
}
Improvements compared to previous answers:
Uses NSLog, not print/println
Does not use lastPathComponent which is not available on Strings anymore
The log message is optional
Try this:
class Log {
class func msg(message: String,
functionName: String = __FUNCTION__, fileNameWithPath: String = __FILE__, lineNumber: Int = __LINE__ ) {
// In the default arguments to this function:
// 1) If I use a String type, the macros (e.g., __LINE__) don't expand at run time.
// "\(__FUNCTION__)\(__FILE__)\(__LINE__)"
// 2) A tuple type, like,
// typealias SMLogFuncDetails = (String, String, Int)
// SMLogFuncDetails = (__FUNCTION__, __FILE__, __LINE__)
// doesn't work either.
// 3) This String = __FUNCTION__ + __FILE__
// also doesn't work.
var fileNameWithoutPath = fileNameWithPath.lastPathComponent
#if DEBUG
let output = "\(NSDate()): \(message) [\(functionName) in \(fileNameWithoutPath), line \(lineNumber)]"
println(output)
#endif
}
}
Log using:
let x = 100
Log.msg("My output message \(x)")
Here is what I used in: https://github.com/goktugyil/QorumLogs
Its like XCGLogger but better.
func myLog<T>(object: T, _ file: String = __FILE__, _ function: String = __FUNCTION__, _ line: Int = __LINE__) {
let info = "\(file).\(function)[\(line)]:\(object)"
print(info)
}
This will print only in debug mode:
func debugLog(text: String, fileName: String = __FILE__, function: String = __FUNCTION__, line: Int = __LINE__) {
debugPrint("[\((fileName as NSString).lastPathComponent), in \(function)() at line: \(line)]: \(text)")
}
Result:
"[Book.swift, in addPage() at line: 33]: Page added with success"
For Swift 3 and above:
print("\(#function)")
Swift 4, based on all these awesome answers. ❤️
/*
That's how I protect my virginity.
*/
import Foundation
/// Based on [this SO question](https://stackoverflow.com/questions/24048430/logging-method-signature-using-swift).
class Logger {
// MARK: - Lifecycle
private init() {} // Disallows direct instantiation e.g.: "Logger()"
// MARK: - Logging
class func log(_ message: Any = "",
withEmoji: Bool = true,
filename: String = #file,
function: String = #function,
line: Int = #line) {
if withEmoji {
let body = emojiBody(filename: filename, function: function, line: line)
emojiLog(messageHeader: emojiHeader(), messageBody: body)
} else {
let body = regularBody(filename: filename, function: function, line: line)
regularLog(messageHeader: regularHeader(), messageBody: body)
}
let messageString = String(describing: message)
guard !messageString.isEmpty else { return }
print(" └ 📣 \(messageString)\n")
}
}
// MARK: - Private
// MARK: Emoji
private extension Logger {
class func emojiHeader() -> String {
return "⏱ \(formattedDate())"
}
class func emojiBody(filename: String, function: String, line: Int) -> String {
return "🗂 \(filenameWithoutPath(filename: filename)), in 🔠 \(function) at #️⃣ \(line)"
}
class func emojiLog(messageHeader: String, messageBody: String) {
print("\(messageHeader) │ \(messageBody)")
}
}
// MARK: Regular
private extension Logger {
class func regularHeader() -> String {
return " \(formattedDate()) "
}
class func regularBody(filename: String, function: String, line: Int) -> String {
return " \(filenameWithoutPath(filename: filename)), in \(function) at \(line) "
}
class func regularLog(messageHeader: String, messageBody: String) {
let headerHorizontalLine = horizontalLine(for: messageHeader)
let bodyHorizontalLine = horizontalLine(for: messageBody)
print("┌\(headerHorizontalLine)┬\(bodyHorizontalLine)┐")
print("│\(messageHeader)│\(messageBody)│")
print("└\(headerHorizontalLine)┴\(bodyHorizontalLine)┘")
}
/// Returns a `String` composed by horizontal box-drawing characters (─) based on the given message length.
///
/// For example:
///
/// " ViewController.swift, in viewDidLoad() at 26 " // Message
/// "──────────────────────────────────────────────" // Returned String
///
/// Reference: [U+250x Unicode](https://en.wikipedia.org/wiki/Box-drawing_character)
class func horizontalLine(for message: String) -> String {
return Array(repeating: "─", count: message.count).joined()
}
}
// MARK: Util
private extension Logger {
/// "/Users/blablabla/Class.swift" becomes "Class.swift"
class func filenameWithoutPath(filename: String) -> String {
return URL(fileURLWithPath: filename).lastPathComponent
}
/// E.g. `15:25:04.749`
class func formattedDate() -> String {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "HH:mm:ss.SSS"
return "\(dateFormatter.string(from: Date()))"
}
}
Calling with Logger.log() -- (emoji is on by default):
Calling with Logger.log(withEmoji: false):
More usage examples:
Logger.log()
Logger.log(withEmoji: false)
Logger.log("I'm a virgin.")
Logger.log("I'm a virgin.", withEmoji: false)
Logger.log(NSScreen.min.frame.maxX) // Can handle "Any" (not only String).
As of Swift 2.2, you can specify it using Literal Expressions, as described at the Swift Programming Language guide.
So if you had a Logger struct that had a function that logged where the error happened, then you would call it like this:
Logger().log(message, fileName: #file, functionName: #function, atLine: #line)
Here's my take on it.
func Log<T>(_ object: Shit, _ file: String = #file, _ function: String = #function, _ line: Int = #line) {
var filename = (file as NSString).lastPathComponent
filename = filename.components(separatedBy: ".")[0]
let currentDate = Date()
let df = DateFormatter()
df.dateFormat = "HH:mm:ss.SSS"
print("┌──────────────┬───────────────────────────────────────────────────────────────")
print("│ \(df.string(from: currentDate)) │ \(filename).\(function) (\(line))")
print("└──────────────┴───────────────────────────────────────────────────────────────")
print(" \(object)\n")}
Hope you enjoy.
This will get you the class and the function name in one go:
var name = NSStringFromClass(self.classForCoder) + "." + __FUNCTION__
this seems to work fine in swift 3.1
print("File: \((#file as NSString).lastPathComponent) Func: \(#function) Line: \(#line)")
Swift 3 support debugLog object with date, function name, file name, line number:
public func debugLog(object: Any, functionName: String = #function, fileName: String = #file, lineNumber: Int = #line) {
let className = (fileName as NSString).lastPathComponent
print("\(NSDate()): <\(className)> \(functionName) [#\(lineNumber)]| \(object)\n")
}
****** Possibly outdated. *******
As pranav mentioned in the comments please use Logger for iOS 14+
There's a new library I have published: Printer.
It has many functions to let you log in different ways.
To log a success message:
Printer.log.success(details: "This is a Success message.")
Output:
Printer ➞ [✅ Success] [⌚04-27-2017 10:53:28] ➞ ✹✹This is a Success message.✹✹
[Trace] ➞ ViewController.swift ➞ viewDidLoad() #58
Disclaimer: This library has been created by me.
func Log<T>(_ object: T, fileName: String = #file, function: String = #function, line: Int = #line) {
NSLog("\((fileName as NSString).lastPathComponent), in \(function) at line: \(line): \(object)")
}
An alternative version, using os_log, could be:
func Log(_ msg: String = "", _ file: NSString = #file, _ function: String = #function) {
let baseName = file.lastPathComponent.replacingOccurrences(of: ".swift", with: "")
os_log("%{public}#:%{public}#: %#", type: .default, baseName, function, msg)
}
Still heavy on string processing, if you can't afford that, just use os_log directly.