Swift: Optional Text In Optional Value - swift

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.

Related

Set value of array via KeyPath

I am trying to update the value stored in an array property of a class via the use of KeyPaths. Here is my code:
func listenAndUpdateDocuments<T: JSONDecodable>(
_ property: ReferenceWritableKeyPath<MyModel, [T]?>,
from model: MyModel) {
guard let reference = reference else {
return
}
guard listener == nil else {
return
}
listener = backendClient.listenToDocuments(reference) { [weak model] (result: Result<[T], RequestError>) in
switch result {
case .success(let value):
model?[keyPath: property] = value
case .failure:
model?[keyPath: property] = []
}
}
}
The problem is when I call this function like this:
myListener.listenAndUpdateDocuments(\.viewers, from: self)
where viewers is of type [ViewersModel], it always comes back with the following error:
Type of expression is ambiguous without more context
How do I solve this? I have a similar version of the code but where the property parameter isn't an array, and that works.
I struggled with something similar:
_ = Token.query(on: req).filter(\.expiry < Date()).delete()
The solution I found was to use a more up-to-date api to handle my request parameters.
_ = Token.query(on: req).filter(\.expiry, .lessThan, Date()).delete()
It had less to do with the keypath itself than I thought!

how to unwrap optionals, how can i write this code to not have any ? or ! in it, or is it possible?

I am having a hard time to rewrite this code so that there are no optionals ? in it or force unwrapping ! So far I was able to get it to work but with the optional in the output. I would like the output to not have the optional in it.
class CeaserCipher {
var secret: Int? = 0
func setSecret(_ maybeString: String?) {
guard let stringSecret = maybeString else {
return
}
self.secret = Int(stringSecret)
}
}
let cipher = CeaserCipher()
cipher.setSecret(nil)
print(cipher.secret)
cipher.setSecret("ten")
print(cipher.secret)
cipher.setSecret("125")
print(cipher.secret)
So, you have a cat, there are many ways you might skin it.
You "could" make the cipher immutable by providing a failable constructor, for example...
struct CeaserCipher {
let secret: Int
init?(string: String) {
guard let value = Int(string) else { return nil }
secret = value
}
}
This doesn't stop you needing to deal with optionals, but it means that an instance of CeaserCipher will be valid.
The struct is focrcing at least one requirement, that you have a non-optional String, so you will need to valid that first
So if you did something like...
let cipher = CeaserCipher(string: "Bad")
cipher would be nil and you'd need to deal with it, but if you did something like...
let cipher = CeaserCipher(string: "123456789")
cipher would be a valid instance and you could work with it.
Using guard and if let are important here, as they will allow you to avoid crashing the code, which you would use would depend on your needs.
guard let cipher = CeaserCipher(string: "123456789") else {
// Cipher is invalid, deal with it...
return
}
// Valid cipher, continue to work with it
or
if let cipher = CeaserCipher(string: "123456789") {
// Valid cipher, continue to work with it
} else {
// Cipher is invalid, deal with it...or not
}
The point of the example is, you will either get a valid instance of CeaserCipher or a nil, which is "generally" safer then having an instance which is in a invalid state and generally easier to deal with

Can I call a swift function that throws in an if statement?

Say I have a function called:
func myFunction() throws -> Bool {
// ...
return true
}
Is there a syntax I am unaware of to use it like so:
if aFlag && try myFunction() {
// do this
}
When I compile this, it fails of course because it says a "try cannot appear to the right of a non-assignment operation". Is there a way to make this work or will I have to go with my current solution of:
if aFlag {
let result = try myFunction()
if result {
// do this
}
}
You use try? to handle an error by converting it to an optional value, and combine with the nil coalescing operator to supply e.g. false as concrete value in case the call throws (-> treated as nil)
func myFunction() throws -> Bool {
return true
}
let aFlag = true
if aFlag, (try? myFunction()) ?? false {
print("Function call succeeded and returned 'true'!")
}
/* or ...
if aFlag && (try? myFunction()) ?? false { ... } */
/* or, optionally bind the return value and use as predicate if non-nil ...
if aFlag, let anotherFlag = (try? myFunction()), anotherFlag { ... } */
This will, however, supress the error thrown, which makes it kind of questionable for myFunction() to be a throwing one in the first place. Perhaps you rather want a function that simply returns nil in the cases you treat as erroneous in your throwing function?
In Swift 3, you can use , rather than &&, e.g.
if aFlag, try myFunction() {
print("OK")
}

Swift: static String variable in a struct becomes chinese characters

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.

AlamoFire GET api request not working as expected

I am trying to get learn how to use AlamoFire and I am having trouble.
My method so far is as follows:
func siteInfo()->String?{
var info:NSDictionary!
var str:String!
Alamofire.request(.GET, MY_API_END_POINT).responseJSON {(request, response, JSON, error) in
info = JSON as NSDictionary
str = info["access_key"] as String
//return str
}
return str
}
This returns nil which is a problem. From what I have read here, this is because the request can take a while so the closure doesn't execute till after the return. The suggested solution of moving the return into the closure does not work for me and the compiler just yells (adding ->String after (request,response,JSON,error) which gives "'String' is not a subtype of void"). Same goes for the other solution provided.
Any ideas? Even some source code that is not related to this problem, that uses AlamoFire, would be helpful.
Thanks!
One way to handle this is to pass a closure (I usually call it a completionHandler) to your siteInfo function and call that inside Alamofire.request's closure:
func siteInfo(completionHandler: (String?, NSError?) -> ()) -> () {
Alamofire.request(.GET, MY_API_END_POINT).responseJSON {
(request, response, JSON, error) in
let info = JSON as? NSDictionary // info will be nil if it's not an NSDictionary
let str = info?["access_key"] as? String // str will be nil if info is nil or the value for "access_key" is not a String
completionHandler(str, error)
}
}
Then call it like this (don't forget error handling):
siteInfo { (str, error) in
if str != nil {
// Use str value
} else {
// Handle error / nil value
}
}
In the comments you asked:
So how would you save the info you collect from the get request if you
can only do stuff inside the closure and not effect objects outside of
the closure? Also, how to keep track to know when the request has
finished?
You can save the result of the get request to an instance variable in your class from inside the closure; there's nothing about the closure stopping you from doing that. What you do from there really depends on, well, what you want to do with that data.
How about an example?
Since it looks like you're getting an access key form that get request, maybe you need that for future requests made in other functions.
In that case, you can do something like this:
Note: Asynchronous programming is a huge topic; way too much to cover here. This is just one example of how you might handle the data you get back from your asynchronous request.
public class Site {
private var _accessKey: String?
private func getAccessKey(completionHandler: (String?, NSError?) -> ()) -> () {
// If we already have an access key, call the completion handler with it immediately
if let accessKey = self._accessKey {
completionHandler(accessKey, nil)
} else { // Otherwise request one
Alamofire.request(.GET, MY_API_END_POINT).responseJSON {
(request, response, JSON, error) in
let info = JSON as? NSDictionary // info will be nil if it's not an NSDictionary
let accessKey = info?["access_key"] as? String // accessKey will be nil if info is nil or the value for "access_key" is not a String
self._accessKey = accessKey
completionHandler(accessKey, error)
}
}
}
public func somethingNeedingAccessKey() {
getAccessKey { (accessKey, error) in
if accessKey != nil {
// Use accessKey however you'd like here
println(accessKey)
} else {
// Handle error / nil accessKey here
}
}
}
}
With that setup, calling somethingNeedingAccessKey() the first time will trigger a request to get the access key. Any calls to somethingNeedingAccessKey() after that will use the value already stored in self._accessKey. If you do the rest of somethingNeedingAccessKey's work inside the closure being passed to getAccessKey, you can be sure that your accessKey will always be valid. If you need another function that needs accessKey, just write it the same way somethingNeedingAccessKey is written.
public func somethingElse() {
getAccessKey { (accessKey, error) in
if accessKey != nil {
// Do something else with accessKey
} else {
// Handle nil accessKey / error here
}
}
}