Logtrace equivalent in Swift Language - iPhone - ios

Is there any logtrace equivalent in Swift language?
I do not wish to use the bridge-header concept.
The objective I'm trying to achieve is that I want println statements to write to console during dev/staging phase of the app and with the flip of a switch, console printing should be stopped just before pushing to app store.
On another thought-line, does println automatically stop printing on the console during distribution? (may be a bonus of migrating to Swift)

You could take a similar approach as the built-in assert() function, which is
explained in the Swift blog:
Building assert() in Swift, Part 1: Lazy Evaluation
Building assert() in Swift, Part 2: FILE and LINE
The logTrace function takes an "auto-closure" as the first argument:
func logTrace(message: #autoclosure () -> String, file: StaticString = __FILE__, line: UWord = __LINE__) {
#if DEBUG
let msg = message()
println("\(file):\(line): \(msg)")
#endif
}
Example usage:
let height = 13
logTrace ( "height = \(height)" )
// Output: /Users/.../main.swift:14: height = 13
To make this work you have to add "-DDEBUG" to the "Other Swift Flags" for the
debug configuration, compare
In absence of preprocessor macros, is there a way to define practical scheme specific flags at project level in Xcode project.
The advantage of this method is that (as with assert()) the block is not
evaluated at all in the Release configuration, where "DEBUG" is not defined, e.g. in
logTrace ( someFunctionReturningAString() )
the function would not be called in the Release configuration, so any side-effects
or performance overhead is avoided.
Update for Swift 2:
func logTrace(#autoclosure message: () -> String, file: String = __FILE__, line: Int = __LINE__) {
#if DEBUG
let msg = message()
print("\(file):\(line): \(msg)")
#endif
}
Update for Swift 3:
func logTrace(_ message: #autoclosure () -> String, file: String = #file, line: Int = #line) {
#if DEBUG
let msg = message()
print("\(file):\(line): \(msg)")
#endif
}

Related

Integrating LogglyLogger-CocoaLumberjack in swift project

I am trying to use LogglyLogger-CocoaLumberjack in my swift project.
I am getting this error in xCode.
Enum case 'verbose' has no associated values
I am unable to resolve this.
https://prnt.sc/uznr01
I am actually trying to translate the Objective-C code in swift 5. Here is my swift function
in appDelegate.swift class
func initLoggly(){
// static const DDLogLevel ddLogLevel = DDLogLevelVerbose;
let ddLogLevel:DDLogLevel = .verbose
// LogglyLogger *logglyLogger = [[LogglyLogger alloc] init];
let logglyLogger = LogglyLogger()
// [logglyLogger setLogFormatter:[[LogglyFormatter alloc] init]];
logglyLogger.logFormatter = LogglyFormatter()
// logglyLogger.logglyKey = #"your-loggly-api-key";
logglyLogger.logglyKey = "XXXXXXXXXXXX-XXXXXX"
//
// // Set posting interval every 15 seconds, just for testing this out, but the default value of 600 seconds is better in apps
// // that normally don't access the network very often. When the user suspends the app, the logs will always be posted.
// logglyLogger.saveInterval = 15;
logglyLogger.saveInterval = 15
// [DDLog addLogger:logglyLogger];
DDLog.add(logglyLogger)
// // Do some logging
// DDLogVerbose(#"{\"myJsonKey\":\"some verbose json value\"}");
// ddLogLevel.verbose("{\"initloggly\":\"some verbose json value\"}") // also tried this, error ==> Enum case 'verbose' cannot be used as an instance member
DDLogLevel.verbose("{\"initloggly\":\"some verbose json value\"}") // Here is the error on this line
}
Please point out what I am doing wrong!
This library is heavly based on C preprocessor macros which aren't accessible from Swift.
You will probably need to write a small set of wrapper functions in Objective-C that use these macros and are in turn callable from Swift.
Here's an example of how this could look like:
LogglyWrapper.h:
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
#interface LogglyWrapper : NSObject
+(void) logVerbose:(NSString*) msg;
#end
NS_ASSUME_NONNULL_END
and LogglyWrapper.m:
#import "LogglyWrapper.h"
#import <LogglyLogger.h>
#implementation LogglyWrapper
static const DDLogLevel ddLogLevel = DDLogLevelVerbose;
+(void) logVerbose:(NSString*) msg {
DDLogVerbose(#"%#", msg);
}
#end
usage from Swift:
LogglyWrapper.logVerbose("foo")

Enum initializer as const

Are vala enums not integer based? This example generates a "c" compile error. Not a big deal, but would like to understand why.
const int INT_UNINITIALIZED = 999;
public enum ScopeTypes {
/*OSS:Fix:GLib requires a default value, set GLOBALS = 0
(VSCodeDbgSvr.exe:31979): GLib-GObject-CRITICAL **: g_param_spec_enum: assertion 'g_enum_get_value (enum_class, default_value) != NULL' failed*/
NONE = INT_UNINITIALIZED,
GLOBALS = 0,
ARGUMENTS,
LOCALS,
EXCEPTIONS,
TOT_SCOPE_TYPES;
//Vala enums may have methods:
public bool is_global() {
return (this == GLOBALS || this == EXCEPTIONS);
}
public bool is_function() {
return (this == ARGUMENTS || this == LOCALS);
}
public bool is_valid() {
return (this != NONE);
}
}
The compile output:
> Executing task: /opt/vala/bin/valac helloworld.vala class1.vala --pkg libvala-0.40 -X -I/opt/vala/include/vala-0.40 -X -O0 --vapidir=/opt/vala/share/vala/vapi --debug --save-temps -o helloworld.exe <
/media/george/SharedData/Projects/Vala/Examples/playground-2/helloworld.c:82:21: error: ‘INT_UNINITIALIZED’ undeclared here (not in a function)
SCOPE_TYPES_NONE = INT_UNINITIALIZED,
^~~~~~~~~~~~~~~~~
error: cc exited with status 256
Compilation failed: 1 error(s), 1 warning(s)
The terminal process terminated with exit code: 1
The relevant part of the error message is:
error: ‘INT_UNINITIALIZED’ undeclared here (not in a function)
The C compiler is complaining that it can not find the declaration of your constant. So it is not a type problem at all.
It is a scope / ordering problem.
If you compile the code with valac -C you get a .c file that looks something like this:
typedef enum {
SCOPE_TYPES_NONE = INT_UNINITIALIZED,
SCOPE_TYPES_GLOBALS = 0,
SCOPE_TYPES_ARGUMENTS,
SCOPE_TYPES_LOCALS,
SCOPE_TYPES_EXCEPTIONS,
SCOPE_TYPES_TOT_SCOPE_TYPES
} ScopeTypes;
#define INT_UNINITIALIZED 999
Note how the Vala compiler has reordered the code to declare the enum first and the constant later.
Since in C the order of declarations in a file is important this can not compile.
I would consider this to be a compiler bug and you may want to report this to the GNOME bugtracker (product Vala).

Swift 3 CVaListPointer Type Conflict

I am integrating with a C library - liblinphone. It has the following typedef and function I need to call from my Swift 3 iOS app.
typedef void (*OrtpLogFunc)(const char *domain,
int lev,
const char *fmt,
va_list args);
void linphone_core_set_log_handler(OrtpLogFunc logfunc);
It appears that Swift is interpreting va_list differently when compiling for the simulator than when compiling for a device.
Here is the Swift code that uses the C function and
This compiles only when the target is the Device:
class MyClass {
func setupLogging() {
linphone_core_set_log_handler(my_callback)
}
}
func my_callback(_ domain: Optional<UnsafePointer<Int8>>,
level: OrtpLogLevel,
format: Optional<UnsafePointer<Int8>>,
args: CVaListPointer?) { // NOTE: Optional CVAListPointer
// do some logging
}
This compiles only when the target is the Simulator:
class MyClass {
func setupLogging() {
linphone_core_set_log_handler(my_callback)
}
}
func my_callback(_ domain: Optional<UnsafePointer<Int8>>,
level: OrtpLogLevel,
format: Optional<UnsafePointer<Int8>>,
args: CVaListPointer) { // NOTE: CVAListPointer is NOT optional
// do some logging
}
When I run on the device, logging works, so it appears that using the optional CVaListPoint? is the safest, so how do I get this to compile for the simulator.
The first version only compiles and runs on a device but issues this compiler error when targeting the simulator:
Swift Compiler Error
C function pointer signature
'(Optional<UnsafePointer<Int8>>, OrtpLogLevel,
Optional<UnsafePointer<Int8>>, CVaListPointer?) -> ()'
is not compatible with expected type 'OrtpLogFunc' (aka
'#convention(c)
(Optional<UnsafePointer<Int8>>, OrtpLogLevel,
Optional<UnsafePointer<Int8>>, CVaListPointer) -> ()')
The second version only compiles when targeting the simulator, but when targeting a device, it issues this error:
Swift Compiler Error
Cannot convert value of type
'(Optional<UnsafePointer<Int8>>, OrtpLogLevel,
Optional<UnsafePointer<Int8>>, CVaListPointer) -> ()'
to expected argument type 'OrtpLogFunc!'
Is there some way I can force the simulator to accept this function without changing the C headers?
Or, is there something I can do in Swift to make this work?
You'd better send a bug report to Apple or swift.org as soon as possible.
And, until this issue will be fixed, this sort of coding would be a workaround:
let my_callback: OrtpLogFunc = {domain, level, format, _args in
let args: CVaListPointer? = _args
// do some logging
}

How can I write automated tests for custom XCTest assertions?

I am developing a testing framework for iOS development. I'd also like for this testing framework to be well-tested. The problem is, I can't figure out how to write a test for my test target that asserts my framework is correctly causing failed tests. If I create a failed test, I have in turn, caused the test to fail (I know, it's confusing).
Consider an example. Part of my framework includes function to verify that a particular code snippet does not have any breaking constraints.
MTKAssertNoBrokenConstraints {
// UI code that might break some constraints
}
I have tested this by hand to verify that when there are no broken constraints, the assertion passes, but when there are broken constraints, it correctly marks the test as failing.
But I need a way to verify that MTKAssertNoBrokenConstraints would mark a test as failing without actually marking the test for this itself as failing.
I have looked into creating a custom object that conforms to XCTestObservation, but so far I've only ended up with infinite recursion. I'm not sure whether this is the right path, or whether resolving the infinite recursion will actually get me where I need to be.
The following test intercepts the failure of XCTFail("FOO") and then performs some checks against the failure.
class TestTheTests: XCTestCase {
var interceptFailure = false
var failureCount = 0
var failureDescription = ""
var failureFilePath = ""
var failureLineNumber: UInt = 0
var failureExpected = false
override func recordFailureWithDescription(description: String, inFile filePath: String, atLine lineNumber: UInt, expected: Bool) {
if !interceptFailure {
super.recordFailureWithDescription(description, inFile: filePath, atLine: lineNumber, expected: expected)
} else {
failureCount += 1
failureDescription = description
failureFilePath = filePath
failureLineNumber = lineNumber
failureExpected = expected
}
}
func testExample() {
interceptFailure = true
XCTFail("FOO")
interceptFailure = false
XCTAssertEqual(failureCount, 1)
XCTAssertTrue(failureDescription.hasSuffix("FOO"), "Was \"\(failureDescription)\"")
}
}

Printing using fmt::Display

I am trying to print an enum (or structure) using fmt::Display. Though the code compiles and gets to the display method, it doesn't print the value.
pub enum TestEnum<'a> {
Foo(&'a str),
Bar(f32)
}
impl<'b> fmt::Display for TestEnum <'b> {
fn fmt(&self, f : &mut fmt::Formatter) -> fmt::Result {
println!("Got this far");
match self{
&TestEnum::Foo(x) => write!(f,"{}",x),
&TestEnum::Bar(x) => write!(f,"{}",x),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_print() {
let cell = TestEnum::Str("foo");
println!("Printing");
println!("{}",cell); // No output here
}
}
I tried using {:?} and {} but to no avail.
This happens because Rust test program hides stdout of successful tests. You can disable this behavior passing --nocapture option to test binary or to cargo test command this way:
cargo test -- --nocapture
PS: your code is broken/incomplete
The test runner seems to divert the standard output; you should consider using assert!, assert_eq! or other panicky ways to test your assertions rather than printing in tests.
Besides, your code fails to compile due to mismatching names. I got it working as expected from main:
use std::fmt;
pub enum TestEnum<'a> {
Foo(&'a str),
Bar(f32)
}
impl<'b> fmt::Display for TestEnum <'b> {
fn fmt(&self, f : &mut fmt::Formatter) -> fmt::Result {
match self {
&TestEnum::Foo(x) => write!(f, "{}", x),
&TestEnum::Bar(x) => write!(f, "{}", x),
}
}
}
fn main() {
let cell = TestEnum::Foo("foo");
println!("Printing");
println!("{}", cell);
}
Test output is redirected to a buffer when the test succeeds as to not mangle up with the test "FAILED" or "ok" messages.
If you just want to test something while developing your test, you can always add a panic!() at the end of your test to make sure it keeps failing and outputting all logging. Or as #AndreaP notes in his answer, you can use cargo test -- --nocapture to display the standard output of all tests.
Usually a test should not write to stdout, but instead write to a buffer and check whether that buffer contains what it should:
let cell = TestEnum::Foo("foo");
let mut buf = Vec::new();
let _ = write!(buf, "{}\n", cell);
assert_eq!(&buf, b"foo\n");
If you truly want to output something, you need to write directly to stdout.
let _ = write!(io::stdout(), "{}\n", cell);
but this will mix with the test's output:
test tests::blub ... foo
ok
PlayPen

Resources