Swift: static String variable in a struct becomes chinese characters - swift

The code in my Keychain class:
struct Keys {
static var token: String = "MyAppToken"
...
}
internal class func set(key: String, value: String) -> Bool {
if let data = value.dataUsingEncoding(NSUTF8StringEncoding) {
return set(key, value: data)
}
return false
}
internal class func get(key: String) -> NSString? {
if let data = getData(key) {
return NSString(data: data, encoding: NSUTF8StringEncoding)
}
return nil
}
...
In unit test doing something like this works without any issues:
func testKeychain() {
let setKey = Keychain.set(Keychain.Keys.token, value: "test")
let getKey = Keychain.get(Keychain.Keys.token)
XCTAssertTrue(setKey, "set keychain value with string")
XCTAssertNotNil(getKey, "retrieve keychain value with key: \(Keychain.Keys.token)")
XCTAssertTrue(getKey!.isEqualToString("test"), "retrieved keychain value matches raw string")
}
In the live code, when I save the value the same way as in the test, everything looks ok. But when trying to retrieve the token the parameter Keychain.Keys.token becomes Chinese characters (except for the last character!?! -> "慐杩湡潔敫n" instead of "MyAppToken"). This causes the get(Keychain.Keys.token) call to return nil. How can something like this happen?
Edit:
This only happens in the "release" build configuration. So, I guess it has something to do with the build settings.

Apparently this issue was caused by the Swift Compiler Optimization. Turning the Optimization Level to "none" has solved it.

Related

How to write Unit Test for UserDefaults

I have 2 functions where one basically retrieves string in User Defaults and another writes the string in User Defaults. I'm not able to understand that should I do the unit test or no? and I'm pretty new to the concept for Unit testing.
I scoured the internet for testing user defaults but I was advised to mock the test in the end.
My question is If there is a way to test User Defaults what is the best way to do it?
Constants and Structs
let defaults = UserDefaults.standard
let defaultinformation = "ABCDEFG"
struct Keys {
static let Information = "Information"
}
Function where saves Information
func SetDefaultInformation() {
defaults.set(defaultinformation, forKey: Keys.Information)
}
Function where retrieves Information
func checkForInformation() -> String {
let Information = defaults.value(forKey: Keys.Information) as? String ?? ""
return Information
}
Thanks in Advance
should I do the unit test or no
No. You know what UserDefaults does and you know how it works and that it works. It is ridiculous to test Apple's code. Testing defaults.set is just as silly; you know exactly what it does and you know that it will do it.
What you want to test is your code: not your retrieval from UserDefaults per se, but your response to that retrieval. Give yourself methods such that you can see what you do when information is a String and what you do when information is nil. As you've been told, a trivial mock can supply the back end, playing the part of UserDefaults and giving back different sorts of result. Just don't let your tests involve the real UserDefaults.
class ViewModel {
func saveUserName(name: String, userDefaults: UserDefaults = UserDefaults.standard) {
userDefaults.set(name, forKey: "username")
}
}
class MockUserDefault: UserDefaults {
var persistedUserName: String? = nil
var persistenceKey: String? = nil
override func set(_ value: Any?, forKey defaultName: String) {
persistedUserName = value as? String
persistenceKey = defaultName
}
}
func testSavingUserName() {
let viewModel = ViewModel()
let mockUserDefaults = MockUserDefault()
viewModel.saveUserName(name: "Oliver", userDefaults: mockUserDefaults)
XCTAssertEqual("Oliver", mockUserDefaults.persistedUserName)
XCTAssertEqual("username", mockUserDefaults.persistenceKey)
}

AutoreleasingUnsafeMutablePointer crashes the app

I receive some data from internet and need to guess the encoding if it's not provided, so I use this function stringEncoding(for:encodingOptions:convertedString:usedLossyConversion:), and it requires passing AutoreleasingUnsafeMutablePointer for receiving the converted string, I wrote code like this:
var str = "Hello, playground"
func decode(data: Data) -> String? {
var covertedString = NSString()
let stringPointer = AutoreleasingUnsafeMutablePointer<NSString?>(&covertedString)
guard NSString.stringEncoding(for: data, encodingOptions: nil, convertedString: stringPointer, usedLossyConversion: nil) != 0 else {
return nil
}
return covertedString as String
}
let data = str.data(using: .utf8)!
decode(data: data)
While the covertedString I got out of the function call is correct, the app always crashes. Any idea why AutoreleasingUnsafeMutablePointer is make it crashes? I tried to not passing convertedString, then it's not crashing any more, so looks like it's the root case. Any idea why it's crashing?
I am using Xcode Version 10.1 (10B61), with Swift 4
In your particular case the problem is that you have created an NSString, but then taken a pointer to a NSString?, which is a different thing.
But that doesn't really matter here. You don't create AutoreleasingUnsafeMutablePointer directly (or generally any kind of UnsafePointer). They're not promised to be valid by the time you use them. Instead, you create them implicitly using &.
func decode(data: Data) -> String? {
var convertedString: NSString? = "" // <- Make sure to make this optional
guard NSString.stringEncoding(for: data,
encodingOptions: nil,
convertedString: &convertedString, // <- Use &
usedLossyConversion: nil) != 0
else {
return nil
}
return convertedString as String?
}

Could not cast value of type Kanna.libxmlHTMLNode to 'NSString

I am trying to scrape some data from a website, and since there is no API, I am trying to use ALAMOFIRE + KANNA
I can print my results in the console, but as soon as I try to convert in String to use it in my app it says:
Could not cast value of type 'Kanna.libxmlHTMLNode' (0x10887d210) to 'NSString' (0x108efc0d0).
Why couldn't I cast the data in String using as! String
my code
var competitions:[String] = []
// Grabs the HTML
func scrapeData() -> Void {
Alamofire.request("MYWEBSITE.com").responseString { response in
print("\(response.result.isSuccess)")
if let html = response.result.value {
self.parseHTML(html: html)
}
}
}
func parseHTML(html: String) -> Void {
if let doc = try? Kanna.HTML(html: html, encoding: String.Encoding.utf8) {
do {
// Search for nodes by XPATH selector
for competition in doc.xpath("""
//*[#id="page_teams_1_block_teams_index_club_teams_2"]/ul
""") {
let competitionName = competition.at_xpath("li/div/a")
print(competitionName?.content ?? "N/A")
competitions.append(competition as! String)
competition is a libxmlHTMLNode, not a String. You can't simply force-cast one type of object to another, unrelated type.
Most likely you want to append competitionName, not competition to your string array. But you need to convert it to a String using its text property:
competitions.append(competitionName?.text ?? "N/A")

Swift as overload

I am creating simple Json Parser that works like that: I have JsonData class that contains Anyobject as data. When I use jsonData["key"] it returns JsonData to i can chain jsonData["key"]["key2"] etc.
My question is how can I implement that class so i could cast it to lets say String:
jsonData["key"] as String without using some workarouds like
jsonData["key"].data as String
Code:
class JsonData:CustomStringConvertible{
let data:AnyObject
var description: String{
get{
return "\(data)"
}
}
init(_ data: Data) {
self.data = try! JSONSerialization.jsonObject(with: data, options: []) as! [[String:AnyObject]]
}
init(_ data: AnyObject) {
self.data = data
}
subscript(key:String) -> JsonData{
let newData = data as! [String:AnyObject]
let test = newData[key]!
return JsonData(test)
}
subscript(index:Int) ->JsonData{
let newData = data[index]!
return JsonData(newData)
}
}
In order to do this, you'd add another overload, but it won't work like you're thinking.
subscript(key: String) -> String {
let newData = data as! [String:AnyObject]
return newData[key] as! String
}
So then jsonData["key"] as String works, but jsonData["key"]["key2"] is ambiguous and you'd have to write it (jsonData["key"] as JsonData)["key2"] which probably isn't what you want.
The short answer to this is don't do this. If you need this much access to JSON, you're probably storing your data incorrectly. Parse it to structs as quickly as you can, and then work with structs. Convert the structs back to JSON when you want that. Extensive work with AnyObject is going to break your brain and the compiler over and over again. AnyObject is a necessary evil, not an every day tool. Soon you will encounter that terrible day that you have an AnyObject? and the compiler just breaks down in tears. Well, at least it isn't Any.
Putting that aside, the better solution is to use labeled-subscripts.
subscript(string key: String) -> String {
let newData = data as! [String:AnyObject]
return newData[key] as! String
}
Now you can access that as json[string: "key"] rather than json["key"] as String.

Swift: Optional Text In Optional Value

How to remove Optional("") text on optional value when displaying without forcing to !.
Update
// I have somthing like this declared outside class
// I put question mark wrapper since I don't know when this session might have a value
var url = "\(self.session?.apiURL)/api/products.json"
// private session
private var _session:Session?
class MyClass
{
.
.
.
// the value of apiURL depends on session, session has optional value and declared as
// custom lazy loaded var session
var session:Session?
{
get
{
if _session == nil
{
_session = // fetch from coredata store if there is an active session. Might return nil
// if no active session
if _session == nil
{
// I just print "No active session"
}
}
// return _session may or may not contain any value
return _session
}
}
}
When the session has a value the url has a value:
Optional("my_api_url_here")/api/products.json
You can use this pod http://cocoapods.org/pods/NoOptionalInterpolation.
Alternatively, add this code to your project to remove the Optional(...) and nil text in string interpolation:
public protocol Unwrappable {
func unwrap() -> Any?
}
extension Optional: Unwrappable {
public func unwrap() -> Any? {
switch self {
case .None:
return nil
case .Some(let unwrappable as Unwrappable):
return unwrappable.unwrap()
case .Some (let some):
return some
}
}
}
public extension String {
init(stringInterpolationSegment expr: Unwrappable) {
self = String(expr.unwrap() ?? "")
}
}
Please note that simply overriding the description function of Optional won't work for string interpolation, although it works for print.
you can use ?? (null coalescing operator) to unwrap it and provide a default value if it is nil
let sessionApiURL = self.session?.apiURL ?? ""
var url = "\(sessionApiURL)/api/products.json"
If you want to without optional value you have to unwrap the Optional. You can use "optional binding" to unwrap an Optional:
if let url = self.session?.apiURL{
//you can use url without optional
print(url)
}
You can check my example in online swift playground for better understand.