NSLog is unavailable - swift

I have following function:
func myNSLog(_ givenFormat: String, _ args: CVarArg..., _ function:String = #function) {
let format = "\(function): \(givenFormat)"
NSLog(format, args)
Which results in the following error:
'NSLog' has been explicitly marked unavailable here (Foundation.NSLog)
Within the documentation is it explicit listed as available. What do I miss?

Similar as in C, you cannot pass a variable argument list directly
to another function. You have to create a CVaListPointer (the Swift
equivalent of va_list) and pass that to the NSLogv variant:
func myNSLog(_ givenFormat: String, _ args: CVarArg..., _ function:String = #function) {
let format = "\(function): \(givenFormat)"
withVaList(args) { NSLogv(format, $0) }
}
(Swift 3 code.)

Related

Trouble creating a more complex closure

I am trying to get my head around creating closures. I get the simpler ones like:
let squaredNumber = { (num: Int) -> (Int) in
return num * num
}
print( squaredNumber(9) ) // 81
and I understand how the sorting one works:
let team = ["Bob", "Rick", "Peter"]
print( team.sorted() ) // ["Bob", "Peter", "Rick"]
print( team.sorted(by: { $0 > $1 }) ) //["Rick", "Peter", "Bob"]
So now I am trying to create my own version of sorting one, in terms of setup.
In the sorting one, there is a plain function sorted() and a sorted(by:...) option.
The code I am playing with so far is (commented code is just to play with):
struct Person {
let name_given = { (fn: String?, ln: String?) -> String in
// return a different value if a nil is supplied
guard let fn1 = fn else { return "No First Name" }
guard let ln1 = ln else { return "No Last Name" }
// Only returns value if not nil
// if let fn1 = fn { return fn1 }
// if let ln1 = ln { return ln1 }
// returns what has been decided above
return "\(fn!) \(ln!)"
}
let no_name = {() -> String in
return "Hello"
}
}
let dad = Person()
print( dad.name_given("Jeff", "Smith") )
print( dad.no_name() )
but I can only get this to work dad.something() but I would like the option of a parameter closure in a function that is I am guessing an optional.
Am I even on the right track in terms of thinking this through.
Basically I want to create a function that execute different code based on wether they have a parameter or not.
So something like dad() or dad(name: {"Jeff", "Smith"}) or dad(age: {35})
The closures would combine strings or do something with the code and then return the result or something.
There is no story to the code above, I just want to understand how to create it.
/////////////////
Edit for clarity:
So I realise my aim was explained with a lot of confusion, because I am trying to figure this out.
Here is hopefully a better attempt:
So this code still doesn't work, but bare with me.
Take for example this:
static func closure(both: (_ a: String, _ b: String) -> String { a, b in
return "\(a) \(b)"
})
and then this:
static func closure(single: (_ a: String) -> String { a in
return "\(a)"
})
So then I would effectively be able to do something like this:
Person.closure(both: {"First", "Last"}) -> This would output "First Last"
and
Person.closure(single: {"First"}) -> This would output "First"
My outcome would be that I could have a static class that has a bunch of closures, but that are grouped.
So if I want to a bunch of string type closures, it would be easy to find them because you could do something like:
StaticStruct.string(<thing1>: {<closure params>})
StaticStruct.string(<thing2>: {<closure params>})
StaticStruct.string(<thing3>: {<closure params>})
or if I want to do something with numbers, it would be:
StaticStruct.numbers(<thing1>: {<closure params>})
StaticStruct.numbers(<thing2>: {<closure params>})
StaticStruct.numbers(<thing3>: {<closure params>})
I hope this makes more sense.
I like the way it looks when you do an array sort, that is why I started thinking like this.
What you are asking is flawed for Swift:
If you don't provide anything at all, not even nil, how can Swift know what's missing?
Think about this example:
print( dad.name_given("Connor") )
How can Swift know if you provided only the first name, or only the last name? In your example, you will need to be specific on what is not provided:
print( dad.name_given(nil, "Connor") ) // Surely only last name is provided
A function would solve this problem by providing a default value to the parameters, so you don't have to pass nil, but then you would need to specifically tell what you're passing, or Swift will assume:
func person(fn: String? = "", ln: String? = "") -> String {...}
print( dad.person(ln: "Connor")) // Surely passing the second parameter
func person(_ fn: String? = "", _ ln: String? = "") -> String {...}
print( dad.person("Connor")) // It will assume that you are passing the first parameter
But with closures, you can't provide a default value to parameters:
let name_given = { (fn: String? = "", ln: String? = "") -> String in...}
// Error: Default arguments are not allowed in closures
It might not solve your problem, but you can create a function that provides default values to the parameters (no need to pass nil), and accepts a closure to treat those parameters.
func describe(fn: String = "", ln: String = "", _ action: #escaping (String?, String?)->String) -> String {
let first: String? = fn == "" ? nil : fn
let second: String? = ln == "" ? nil : ln
return action(first, second)
}
print( dad.describe(ln: "Connor", dad.name_given)) // No first Name

Using Foundation Decimal type in XCTAssertEqual

I am building some classes and code that store and perform arithmetic on currency values. I was originally using Doubles, but converted to Decimal due to arithmetic errors.
I am trying to find the best way to run unit tests against functions working with Decimal type.
Consider position.totalCost is a Decimal type.
XCTAssertEqual(position.totalCost, 3571.6, accuracy: 0.01)
This code does not compile because Decimal does not conform to FloatingPoint. XCTAssertEqual requires parameters to be Doubles or Floats.
I got around this by doing the following:
XCTAssertTrue(position.totalCost == 3571.6)
Which does work, but if an error arises during the unit test, I get a vague message:
XCTAssertTrue failed rather than the more useful XCTAssertEqual failed: ("2.0") is not equal to ("1.0")
So using XCTAssertEqual is ideal.
Potential Options (as a novice, no clue which is better or viable)
Code my Position class to store all properties as Decimal but use computed properties to get and set them as Doubles.
Write a custom assertion that accepts Decimals. This is probably the most 'proper' path because the only issue I've encountered so far with using Decimals is that XCT assertions cannot accept them.
Write a goofy Decimal extension that will return a Double value. For whatever reason, there is no property or function in the Decimal class that returns a Double or Floag.
Don't convert Decimal to a floating point if you don't have to since it will result in a loss of precision. If you want to compare two Decimal values with some accuracy you can use Decimal.distance(to:) function like so:
let other = Decimal(35716) / Decimal(10) // 3571.6
let absoluteDistance = abs(position.totalCost.distance(to: other))
let accuracy = Decimal(1) / Decimal(100) // 0.01
XCTAssertTrue(absoluteDistance < accuracy)
You can write an extension on Decimal:
extension Decimal {
func isEqual(to other: Decimal, accuracy: Decimal) -> Bool {
abs(distance(to: other)).isLess(than: accuracy)
}
}
And then use it in your tests:
XCTAssertTrue(position.totalCost.isEqual(to: 3571.6, accuracy: 0.01))
This is likely good enough. However, to get better error messages in the case of a failing test would require writing an overload for XCTAssertEqual, which is actually a bit tricky because elements of XCTest are not publicly available.
However, it is possible to approximate the behaviour:
Firstly, we need some plumbing to evaluate assertions, this can more or less be lifted straight from swift-corelibs-xctest.
import Foundation
import XCTest
internal enum __XCTAssertionResult {
case success
case expectedFailure(String?)
case unexpectedFailure(Swift.Error)
var isExpected: Bool {
switch self {
case .unexpectedFailure: return false
default: return true
}
}
func failureDescription() -> String {
let explanation: String
switch self {
case .success: explanation = "passed"
case .expectedFailure(let details?): explanation = "failed: \(details)"
case .expectedFailure: explanation = "failed"
case .unexpectedFailure(let error): explanation = "threw error \"\(error)\""
}
return explanation
}
}
internal func __XCTEvaluateAssertion(testCase: XCTestCase, _ message: #autoclosure () -> String = "", file: StaticString = #file, line: UInt = #line, expression: () throws -> __XCTAssertionResult) {
let result: __XCTAssertionResult
do {
result = try expression()
}
catch {
result = .unexpectedFailure(error)
}
switch result {
case .success: return
default:
let customMessage = message()
let description = customMessage.isEmpty ? result.failureDescription() : "\(result.failureDescription()) - \(customMessage)"
testCase.record(.init(
type: .assertionFailure,
compactDescription: description,
detailedDescription: nil,
sourceCodeContext: .init(
location: .init(filePath: String(describing: file), lineNumber: Int(line))
),
associatedError: nil,
attachments: [])
)
}
}
Now, for all of this to work, requires us to have access to the currently running XCTestCase, inside a global XCTAssert* function, which is not possible. Instead we can add our assert function in an extension.
extension XCTestCase {
func AssertEqual(
_ expression1: #autoclosure () throws -> Decimal,
_ expression2: #autoclosure () throws -> Decimal,
accuracy: #autoclosure () throws -> Decimal,
_ message: #autoclosure () -> String = "",
file: StaticString = #file,
line: UInt = #line
) {
__XCTEvaluateAssertion(testCase: self, message(), file: file, line: line) {
let lhs = try expression1()
let rhs = try expression2()
let acc = try accuracy()
guard lhs.isEqual(to: rhs, accuracy: acc) else {
return .expectedFailure("(\"\(lhs)\") is not equal to (\"\(rhs)\")")
}
return .success
}
}
}
All of which allows us to write our test cases as follows...
class MyTests: XCTestCase {
// etc
func test_decimal_equality() {
AssertEquals(position.totalCost, 3571.6, accuracy: 0.01)
}
}
And if the assertion fails, the test case will fail, with the message: ("3571.5") is not equal to ("3571.6") at the correct line.
We also cannot call our function XCTAssertEquals, as this will override all the global assert functions.
You milage may vary, but once you have the plumbing in place, this allows you to write bespoke custom assertions for your test suite.
Do you really need to specify the accuracy of 0.01?
Because if you omit this argument, it compiles just fine.
struct Position {
let totalCost: Decimal
}
let position = Position(totalCost: 3571.6)
//passes
XCTAssertEqual(position.totalCost, 3571.6)
// XCTAssertEqual failed: ("3571.6") is not equal to ("3571.61")
XCTAssertEqual(position.totalCost, 3571.61)

How to check if a string contains multiple characters in Swift 5

I have a failure initializer that takes a string, if this string contains incorrect characters (T, A, C, G) I want to return nil:
I tried something like this, unsuccessful:
init?(strand: String) {
let success = strand.contains(where: { !"TACG".contains($0) })
if !success {
return nil
}
self.strand = strand
}
I somehow got confused by the two contains calls, so I am not sure if my check is correct.
Any help is appreciated.
In this case I'd prefer the API rangeOfCharacter(from which checks the string against a character set
init?(strand: String) {
guard strand.rangeOfCharacter(from: CharacterSet(charactersIn: "TACG")) == nil else { return nil }
self.strand = strand
}
If you don't want to import Foundation you can also use Collection method allSatisfy
func allSatisfy(_ predicate: (Character) throws -> Bool) rethrows -> Bool
And make sure your string contains all characters
let allSatisfy = "CGAT".allSatisfy("TACG".contains)
print(allSatisfy) // true
Just move the ! placement, check out the code below .
init?(strand: String) {
let success = !strand.contains(where: { "TACG".contains($0) })
if !success {
return nil
}
self.strand = strand
}

Trouble converting a string to an Int

The following works in Playground:
func stringToInt(numberStr: String!) -> Int {
print(numberStr)
return Int(numberStr)!
}
let strNum1: String?
strNum1 = "1"
let result = stringToInt(numberStr: strNum1)
It returns 1 as expected.
In Xcode, a similar approach fails:
func stringToInt(numberStr: String!) -> Int {
print("\(numberStr!)")
let str = "\(numberStr!)"
print(Int(str))
return Int(str)!
}
The first print produces: Optional(1)
The second print produces: nil
The return statement fails because it is attempting to create an Int from a nil.
It must be something simple but I haven't been able to determine why it's not working. This is in Swift 3 and Xcode 8 BTW.
#Hamish:
In Xcode, I have a string with a numeric value. This:
print("number: (selectedAlertNumber) - unit: (selectedAlertUnit)")
...produces this:
number: Optional(1) - unit: Day
Then, I'm checking to see if either selectedAlertNumber of selecterAlertUnit != "-"
if selectedAlertNumber != "-" && selectedAlertUnit != "-" {
// set alert text
var unitStr = selectedAlertUnit
let alertNumber = stringToInt(numberStr: selectedAlertNumber)
if alertNumber > 1 {
unitStr.append("s")
}
let alertText = "...\(selectedAlertNumber) \(unitStr) before event."
alertTimeCell.setAlertText(alertText: alertText)
// set alert date/time
}
The let alertNumber = stringToInt... line is how I'm calling the function. I could just attempt the conversion there but I wanted to isolate the problem by wrapping the conversion in it's own function.
Using string interpolation to convert values to a String is usually not advised since the output may differ depending on optional status of the value. For example, consider these two functions:
func stringToInt(numberStr: String!) -> Int
{
print("\(numberStr!)")
let str = "\(numberStr!)"
return Int(str)!
}
func otherStringToInt(numberStr: String!) -> Int
{
print(numberStr)
let str = "\(numberStr)"
return Int(str)!
}
The only difference between these two is the ! in the second function when using string interpolation to get a String type value from numberStr. To be more specific, at the same line in function 1 compared to function 2, the string values are very different depending on whether or not the interpolated value is optional:
let str1: String = "1"
let str2: String! = "1"
let str3: String? = "1"
let otherStr1 = "\(str1)" // value: "1"
let otherStr2 = "\(str2)" // value: "Optional(1)"
let otherStr3 = "\(str2!)" // value: "1"
let otherStr4 = "\(str3)" // value: "Optional(1)"
let otherStr5 = "\(str3!)" // value: "1"
Passing otherStr2 or otherStr4 into the Int initializer will produce nil, since the string "Optional(1)" is not convertible to Int. Additionally, this will cause an error during the force unwrap. Instead of using string interpolation in your function, it would be better to just use the value directly since it's already a String.
func stringToInt(numberStr: String!) -> Int
{
return Int(numberStr)!
}
Let me know if this makes sense.
Also, my own personal feedback: watch out force unwrapping so frequently. In many cases, you're running the risk of getting an error while unwrapping a nil optional.

Syntax changes from Swift to Swift 2, cannot invoke enumerate with a string and extra NSErrorPointer argument

I am working on Swift 1.2 with Xcode 6 but now I've installed Xcode 7 with Swift 2.1. There are many errors in Swift 2.1 and many syntax changes, even though my code works well with Swift 1.2. The first problem is with this method:
func getSubstringUpToIndex(index: Int, fromString str: String) -> String
{
var substring = ""
for (i, letter) in enumerate(str) {
substring.append(letter)
if i == index - 1 {
break
}
}
return substring
}
Another problem occurs on this line, "extra argument 'error' in call":
let jsonResult: Dictionary = NSJSONSerialization.JSONObjectWithData(self.mutableData, options: NSJSONReadingOptions.MutableContainers, error: &error) as! Dictionary<String, AnyObject>
In both cases it's Swift 1.2 code, not 2.0.
In the first snippet of code, the enumerate method does not exist anymore, you can update it this way:
func getSubstringUpToIndex(index: Int,
fromString str: String) -> String
{
var substring = ""
for (i, letter) in str.characters.enumerate() {
substring.append(letter)
if i == index - 1 {
break
}
}
return substring
}
Or this way, using substringRithRange :
func getSubstringUpToIndex2(index: Int,
fromString str: String) -> String {
let range = str.startIndex.advancedBy(0)..<str.startIndex.advancedBy(index)
return str.substringWithRange(range)
}
Try them out on swiftstub.
The second line you pasted does not work anymore because swift 2 now handles errors with try/do/catch, the last parameter NSErrorPointer does not exist anymore, learn more about swift2 error handling here.