I have a string with interpolation like this
let userID = 123
let userProfileUrl = "website.com/user/\(userID)"
I would like make website.com/user/\(userID) a constant but still remain its interpolation, so that I can interpolate it with an userID.
I wonder if anyone knows a good way to do that
You can make userProfileUrl a lazy var. In this case you would need to specify the type of the userProfileUrl (i.e. String) and would need to use self.userID instead of userID
let userID = 123
lazy var userProfileUrl: String = "website.com/user/\(self.userID)"
Or if both properties are constants, and don't depend on an instance of the class you can place them outside of the class definition and it should work:
let userID = 123
let userProfileUrl = "website.com/user/\(userID)"
class MyClass {
}
You can also make userProfileUrl a computed property
let userID = 123
var userProfileUrl: String {
return "website.com/user/\(userID)"
}
If you don't like the extra lines that the computed property adds you could format it so that it's all on one line
let userID = 123
var userProfileUrl: String { return "website.com/user/\(userID)" }
var userProfileURL: (String) -> String = {
return "website.com/user/\($0)"
}
userProfileURL(userID)
This works but I would consider using an enum. You can now create a new case per endpoint.
enum Website {
case UserProfile(Int)
var base: String { return "http://website.com" }
var path: String {
switch self {
case let .UserProfile(userID):
return "user/\(userID)"
}
}
var url: URL { return URL(string: "\(base)/\(path)")! }
}
let userProfileUrl = Website.UserProfile(123).url
This might be a place where you want to use NSString's bridging to String and it's -initWithFormat:
let userProfileUrl = String(format: "website.com/user/%d", userId)
Related
So im using CloudKit and fetching all the data into an array as [StartDay], my StartDay class looks like this:
import UIKit
import CloudKit
class StartDay {
var recordID: CKRecord.ID!
var wakeUp: String!
var sleptWell: String!
var dNN: String!
var created: String! {
get {
return created
}
}
}`
My function loads get an arraylist, which contains information received from the database. In my database it stands like this: "22.01.09:
func checkIfButtonShouldBeEnabled(startDayList: [StartDay]){
let startDayDates = startDayList.map{$0.created}
for i in 0..<startDayDates.count {
print(startDayDates)
}
}`
OUTPUT:
Optional("22.01.2019")
Optional("22.01.2019")
I want to remove "Optional()", so it only says "22.01.2019", how can I do so?
UPDATE: FETCH FUNC
func loadStartDay() -> [StartDay]{
let predicate = NSPredicate(value: true)
let query = CKQuery(recordType: "StartDay", predicate: predicate)
let operation = CKQueryOperation(query: query)
var startDays: [StartDay] = []
operation.desiredKeys = ["wakeUp", "wellSlept", "dNN", "recordID", "createdDato"]
operation.recordFetchedBlock = { (record:CKRecord) in
let newStartDay = StartDay()
newStartDay.wakeUp = record.object(forKey: "wakeUP") as? String
newStartDay.sleptWell = record.object(forKey: "sleptWell") as? String
newStartDay.dNN = record.object(forKey: "dNN") as? String
newStartDay.recordID = record.object(forKey: "recordID") as? CKRecord.ID
newStartDay.created = record.object(forKey: "createdDato") as? String
print(newStartDay.created)
startDays.append(newStartDay)
}
You can use print(startDayDates!) or print(startDayDates ?? "default value").
But I recommend usage of startDayList.compactMap() instead of startDayList.map()to ensure your array doesn't contain nil values.
You can also do like this:
startDayList
.compactMap { $0.created }
.forEach { print($0) }
As you designed the database model you exactly know which record attributes always exist. Declaring class properties as implicit unwrapped optional as an alibi not to write an initializer is very bad practice.
Assuming every attribute in a record does have a value declare the properties as non-optional and write an initializer.
At least created and recordID are supposed to have always a value!
import UIKit
import CloudKit
class StartDay {
var recordID: CKRecord.ID
var wakeUp: String
var sleptWell: String
var dNN: String
var created: String
init(record : CKRecord) {
// recordID can be retrieved directly
self.recordID = record.recordID
self.wakeUp = record.object(forKey: "wakeUP") as! String
self.sleptWell = record.object(forKey: "sleptWell") as! String
self.dNN = record.object(forKey: "dNN") as! String
self.created = record.object(forKey: "createdDato") as! String
}
}
and create instances with
operation.recordFetchedBlock = { record in
startDays.append(StartDay(record: record))
}
Now the Optional has gone.
print(startDayList.map{ $0.created })
Is it possible to do a getter and setter for an attribute that has a type of 'Any'
Here is my thought:
private var _valueObject: Any?
public var valueObject: Any? {
set {
if newValue is String {
self._valueObject = newValue as? String
} else if newValue is BFSignature {
self._valueObject = newValue as? BFSignature
}
}
get {
if self._valueObject is String {
return self._valueObject as? String
} else if self._valueObject is BFSignature {
return self._valueObject as? BFSignature
} else {
return self._valueObject
}
}
}
When I try to use it through out my code though I get errors stating:
Cannot compare String to type Any
Is there a way to use something like this without casting the 'valueObject' to a string whenever I need it. A way to use it and it already knows its a 'String' or 'BFSignature' instead of 'Any'.
Here is an example of the error:
I would rather it just know that cellValue is a 'String.' Instead of casting it each time I use it.
You shouldn't use Any
In my opinion, you should make a common representation of the API call result instead of using Any. You know exactly what the API is going to return, don't you? It's either a String or something that you turn into your custom object BFSignature.
Therefore, you can make an enum to represent your API call result:
enum APIResult {
case signature(BFASignature)
case justString(String)
}
and use it like
private var _valueObject: APIResult?
if let stringValue = newValue as? String {
self._valueObject = .justString(stringValue)
}
if let signatureValue = newValue as? BFSignature {
self._valueObject = .signature(signatureValue)
}
If there are a fixed number of types that you need to use here, you can use an enum:
struct BFSignature {
var a: Int
}
enum Either {
case bfSig(BFSignature)
case string(String)
}
var a: Either
var b: Either
a = .bfSig(BFSignature(a: 7))
b = .string("Stack Overflow")
a = b
Usage:
switch (b) {
case Either.bfSig(let signature):
print(signature.a) // Output integeral value
case Either.string(let str):
print(str) //Output string value
}
I have a class User:
import UIKit
import ObjectMapper
class User: NSObject, CustomStringConvertible, Mappable {
var FirstName: NSString! ;
var LastName: NSString! ;
required init?(_ map: Map){
}
func mapping(map: Map) {
FirstName <- map["FirstName"]
LastName <- map["LastName"]
}
override var description:String {
var s:String=""
//USE REFLECTION TO GET NAME AND VALUE OF DATA MEMBERS
for var index=1; index<reflect(self).count; ++index {
s += (reflect(self)[index].0 + ": "+reflect(self)[index].1.summary+"\t")
}
return s
}
}
In swift 1.2, I was using reflect() method to get array of all the data members with their names and values.
Now, after I have updated to swift 2, I am getting the following error:
'reflect' is unavailable: call the 'Mirror(reflecting:)' initializer
With some trials, I was able to get the count of data members by this: Int(Mirror(reflecting: self).children.count), but still, I am unable to get the member name and its value.
I have looked into the following resources:
https://netguru.co/blog/reflection-swift
http://nshipster.com/mirrortype/
UPDATE
I have found the an answer here: https://stackoverflow.com/a/32846514/4959077. But this doesn't tell how to find out the type of reflected value. If the value is int and we parse it into String then it gives error.
You may access the reflected attribute "label" name, value and type as follows:
let mirror = Mirror(reflecting: SomeObject)
var dictionary = [String: Any]()
for child in mirror.children {
guard let key = child.label else { continue }
let value: Any = child.value
dictionary[key] = value
switch value {
case is Int: print("integer = \(anyValue)")
case is String: print("string = \(anyValue)")
default: print("other type = \(anyValue)")
}
switch value {
case let i as Int: print("• integer = \(i)")
case let s as String: print("• string = \(s)")
default: print("• other type = \(anyValue)")
}
if let i = value as? Int {
print("•• integer = \(i)")
}
}
Note: per the question followup, three approaches to determine the type of the reflected value are shown.
I have a solution that finds the name and type of a property given any class that inherits from NSObject.
I wrote a lengthy explanation on StackOverflow here, and my project is available here on Github,
In short you can do something like this (but really check out the code Github):
public class func getTypesOfProperties(inClass clazz: NSObject.Type) -> Dictionary<String, Any>? {
var count = UInt32()
guard let properties = class_copyPropertyList(clazz, &count) else { return nil }
var types: Dictionary<String, Any> = [:]
for i in 0..<Int(count) {
guard let property: objc_property_t = properties[i], let name = getNameOf(property: property) else { continue }
let type = getTypeOf(property: property)
types[name] = type
}
free(properties)
return types
}
When I try to just set a constant based on the settings like below, it results in Optional("value").
let accesstoken = NSUserDefaults.standardUserDefaults().stringForKey("accessToken")
let userId = NSUserDefaults.standardUserDefaults().stringForKey("userId")
If I do it like the below, I get an error saying variable used within its own initial value. I can't seem to win here. What am I doing wrong?
var accesstoken = String()
var userId = Int()
if let atString = NSUserDefaults.standardUserDefaults().stringForKey("accessToken") {
accesstoken = atString
}
if let userIdString = NSUserDefaults.standardUserDefaults().stringForKey("userId") {
userId = userIdString
}
You can achieve what you want with a read only computed property combined with the nil coalescing operator "??". Try like this:
var accessToken: String {
return NSUserDefaults().stringForKey("accessToken") ?? ""
}
var userId: String {
return NSUserDefaults().stringForKey("userId") ?? ""
}
or if you need an Int for your userID
var userId: Int {
return NSUserDefaults().integerForKey("userId")
}
This won't compile:
I've tried a couple different things; different ways of declaring the Dictionary, changing its type to match the nested-ness of the data. I also tried explicitly saying my Any was a collection so it could be subscripted. No dice.
import UIKit
import Foundation
class CurrencyManager {
var response = Dictionary<String,Any>()
var symbols = []
struct Static {
static var token : dispatch_once_t = 0
static var instance : CurrencyManager?
}
class var shared: CurrencyManager {
dispatch_once(&Static.token) { Static.instance = CurrencyManager() }
return Static.instance!
}
init(){
assert(Static.instance == nil, "Singleton already initialized!")
getRates()
}
func defaultCurrency() -> String {
let countryCode = NSLocale.currentLocale().objectForKey(NSLocaleCountryCode) as String
let codesToCountries :Dictionary = [ "US":"USD" ]
if let localCurrency = codesToCountries[countryCode]{
return localCurrency
}
return "USD"
}
func updateBadgeCurrency() {
let chanCurr = defaultCurrency()
var currVal :Float = valueForCurrency(chanCurr, exchange: "Coinbase")!
UIApplication.sharedApplication().applicationIconBadgeNumber = Int(currVal)
}
func getRates() {
//Network code here
valueForCurrency("", exchange: "")
}
func valueForCurrency(currency :String, exchange :String) -> Float? {
return response["current_rates"][exchange][currency] as Float
}
}
Let's take a look at
response["current_rates"][exchange][currency]
response is declared as Dictionary<String,Any>(), so after the first subscript you try to call another two subscripts on an object of type Any.
Solution 1. Change the type of response to be a nested dictionary. Note that I added the question marks because anytime you access a dictionary item you get back an optional.
var response = Dictionary<String,Dictionary<String,Dictionary<String, Float>>>()
func valueForCurrency(currency :String, exchange :String) -> Float? {
return response["current_rates"]?[exchange]?[currency]
}
Solution 2. Cast each level to a Dictionary as you parse. Make sure to still check if optional values exist.
var response = Dictionary<String,Any>()
func valueForCurrency(currency :String, exchange :String) -> Float? {
let exchanges = response["current_rates"] as? Dictionary<String,Any>
let currencies = exchanges?[exchange] as? Dictionary<String,Any>
return currencies?[currency] as? Float
}
You can get nested dictionary data by following these steps:
let imageData: NSDictionary = userInfo["picture"]?["data"]? as NSDictionary
let profilePic = imageData["url"] as? String
func valueForCurrency(currency :String, exchange :String) -> Float? {
if let exchanges = response["current_rates"] as? Dictionary<String,Any> {
if let currencies = exchanges[exchange] as? Dictionary<String,Any> {
return currencies[currency] as? Float
}
}
return nil
}
response is declared as such:
var response = Dictionary<String,Any>()
So the compiler thinks response["current_rates"] will return an Any. Which may or may not be something that is subscript indexable.
You should be able to define you type with nested Dictionaries, 3 levels and eventually you get to a float. You also need to drill in with optional chaining since the dictionary may or may not have a value for that key, so it's subscript accessor returns an optional.
var response = Dictionary<String,Dictionary<String,Dictionary<String,Float>>>()
// ... populate dictionaries
println(response["current_rates"]?["a"]?["b"]) // The float