I have a swift struct named Product which takes a Dictionary in its init method. I want to compute a price value in a local Price struct within my Product. I want this value to be a let constant since it won't ever change, but swift won't allow me to do that without using var, saying that the let constant is not properly initialized.
How can I make my value property inside my Price struct a let constant in this case?
struct Product {
let price: Price
init(dictionary: Dictionary<String, AnyObject>) {
if let tmp = dictionary["price"] as? Dictionary<String, AnyObject> { price = Price(dictionary: tmp) } else { price = Price() }
}
struct Price {
var value = ""
init() {
}
init(dictionary: Dictionary<String, AnyObject>) {
if let xForY = dictionary["xForY"] as? Array<Int> {
if xForY.count == 2 {
value = "\(xForY[0]) for \(xForY[1])"
}
}
if let xForPrice = dictionary["xForPrice"] as? Array<Int> {
if value == "" && xForPrice.count == 2 {
value = "\(xForPrice[0]) / \(xForPrice[1]):-"
}
}
if let reduced = dictionary["reduced"] as? String {
if value == "" {
value = "\(reduced):-"
}
}
}
}
}
You have to rewrite the code so that the compiler gets what you intend to do actually. It is not clever enough to deduce it from the way you coded it.
I would also suggest that you make the initializer for the Price struct failable instead of using an empty string for the value property. As a consequence of that change the price property of the Product struct becomes an optional.
struct Product {
let price: Price?
init(dictionary: Dictionary<String, AnyObject>) {
if let tmp = dictionary["price"] as? Dictionary<String, AnyObject> {
price = Price(dictionary: tmp)
}
else {
price = nil
}
}
struct Price {
let value : String
init?(dictionary: Dictionary<String, AnyObject>) {
if let xForY = dictionary["xForY"] as? Array<Int> where xForY.count == 2 {
value = "\(xForY[0]) for \(xForY[1])"
}
else if let xForPrice = dictionary["xForPrice"] as? Array<Int> where xForPrice.count == 2 {
value = "\(xForPrice[0]) / \(xForPrice[1]):-"
}
else if let reduced = dictionary["reduced"] as? String {
value = "\(reduced):-"
}
else {
return nil
}
}
}
}
The trouble is that (1) you're assigning to value in the declaration, (2) you're not assigning a value in init(), and (3) you're referencing and reassigning value in init([String: AnyObject]). You can only assign a value to a constant once and can only reference its value after its been assigned to.
To fix the issue, you can either make your property publicly readonly:
private(set) var value: String = ""
Or you can use a second variable inside your init:
struct Price {
let value: String
init() {
self.value = ""
}
init(dictionary: Dictionary<String, AnyObject>) {
var v: String = ""
if let xForY = dictionary["xForY"] as? Array<Int> {
if xForY.count == 2 {
v = "\(xForY[0]) for \(xForY[1])"
}
}
if let xForPrice = dictionary["xForPrice"] as? Array<Int> {
if v == "" && xForPrice.count == 2 {
v = "\(xForPrice[0]) / \(xForPrice[1]):-"
}
}
if let reduced = dictionary["reduced"] as? String {
if v == "" {
v = "\(reduced):-"
}
}
self.value = v
}
}
Related
I am getting data from different sources, the variable could be a number or a string of number. How do I make sure that "(number as? NSString)" or "(number as? NSNumber)" always success? Something similar to Java optInt, which will never fail even if the number is a String. See example below:
func testNumber()
{
var number = 123
guard let a = (number as? NSNumber)?.intValue else { print("1");return; }
}
func testNumberString()
{
var number = "123"
guard let a = (number as? NSNumber)?.intValue else { print("2");return; } // this failed.
}
func testNumberToString()
{
var number = 123
guard let a = (number as? NSString)?.intValue else { print("2");return; } // this sometimes failed too depend on datasource.
}
As I understand from your question, you want an integer value at the end, no matter if the input type is string or integer.
You can achieve this by using ExpressibleByStringLiteral.
Here is the demo
extension Int: ExpressibleByStringLiteral {
public typealias StringLiteralType = String
public init(stringLiteral value: StringLiteralType) {
self = Int(value) ?? 0
}
}
This Int extension allows you to accept string value as Int and return int value. If it did not convert it will give you 0 by default.
Example
func testInt() {
let numberOne: Int = "5656"
let numberTwo: Int = 1234
print(numberOne)
print(numberTwo)
}
Or another way is to create your own ExpressibleByStringLiteral, which helps you to give default value as you want.
struct StringInt: ExpressibleByStringLiteral {
var value: Int?
init(stringLiteral value: String) {
self.value = Int("\(value)")
}
func wrapped(with defaultValue: Int) -> Int {
return self.value ?? defaultValue
}
}
Example
func testInt() {
var numberThree: StringInt = "5656"
print(numberThree.value as Any) // with nil or optional value
numberThree = "asf"
print(numberThree.wrapped(with: 15)) // with default value
/**
Output
Optional(5656)
15
*/
}
I'm using swift in my project. I have 2 object from same class(for example object A and object B from Class MyClass):
class MyClass: NSObject {
var someString: String = ""
var someInt: Int = 0
}
...
let A = MyClass()
A.someString = "A object"
A.someInt = 1
let B = MyClass()
B.someString = "B object"
B.someInt = 2
how I can check if same properties have same value, and if not return value and key of the property?
I think we can do this with using Mirror with 2 for loop inside each other, am I write?
I think you're looking for something like this pretty much:
import Foundation
class MyClass {
var someString: String = ""
var someInt: Int = 0
}
let a = MyClass()
a.someString = "A object"
a.someInt = 1
let b = MyClass()
b.someString = "B object"
b.someInt = 2
func compare<T: MyClass>(_ instance: T, with instance2: T) -> [String: AnyHashable] {
let sourceMirror = Mirror(reflecting: instance)
let targetMirror = Mirror(reflecting: instance2)
var output = [String: AnyHashable]()
for sourceChild in sourceMirror.children {
guard let label = sourceChild.label else { continue }
guard let targetChild = (targetMirror.children.first { $0.label! == label }) else {
fatalError("Failed to find target child, since types are same this fatal error should not be fired")
}
guard
let firstValue = sourceChild.value as? AnyHashable,
let secondValue = targetChild.value as? AnyHashable
else {
continue
}
guard firstValue != secondValue else { continue }
output[label] = secondValue
}
return output
}
for result in compare(a, with: b) {
print("label: \(result.key), value: \(result.value)")
}
The downside of this method is all of your fields must be conforming to Hashable protocol if you want to see the difference between these.
The output is:
label: someInt, value: 2
label: someString, value: B object
After downcasting an array of structs, my Variables View window shows that all of the values in my struct have shifted "down" (will explain in a second). But when I print(structName), the values are fine. However, when I run an equality check on the struct, it once again behaves as though my values have shifted.
For example, I am trying to downcast Model A to ModelProtocol. var m = Model A and has the values {id: "1234", name: "Cal"}. When I downcast, m now has the values { id:"\0\0", name:"1234" }.
Actual Example Below:
Models that I want to downcast:
struct PrivateSchoolModel: Decodable, SchoolProtocol {
var id: String
var name: String
var city: String
var state: String
}
struct PublicSchoolModel: Decodable, SchoolProtocol {
var id: String
var name: String
var city: String
var state: String
var latitude: String
var longitude: String
}
Protocol I want to downcast to:
protocol SchoolProtocol {
var id: String { get set }
var name: String { get set }
var city: String { get set }
var state: String { get set }
var longitude: Float { get set }
var latitude: Float { get set }
}
extension SchoolProtocol {
var longitude: Float {
get { return -1.0 }
set {}
}
var latitude: Float {
get { return -1.0 }
set {}
}
}
Downcasting:
guard let downcastedArr = privateSchoolArray as? [SchoolProtocol] else { return [] }
Result (item at index 0) or originalArr:
id = "1234"
name = "Leo High School"
city = "Bellview"
state = "WA"
Result (item at index 0) of downcastedArr:
id = "\0\0"
name = "1234"
city = "Leo High School"
state = "Bellview"
But if I print(downcastArr[0]), it will show:
id = "1234"
name = "Leo High School"
city = "Bellview"
state = "WA"
But if I try originalArray[0].id == downcastArr[0].id, it returns false
My Code with the problem:
class SchoolJSONHandler {
private enum JSONFile: String {
case publicSchool = "publicSchool"
case privateSchool = "privateSchool"
}
private lazy var privateSchoolArray = getPrivateSchools()
private lazy var publicSchoolArray = getPublicSchools()
func getSchoolArray(sorted: Bool, filtered: Bool, by stateAbbreviation: String?) -> [SchoolProtocol] {
var schools = combineArrays()
if sorted {
schools.sort(by: { $0.name < $1.name })
}
if filtered {
guard let abbr = stateAbbreviation else { return [] }
schools = schools.filter {
return $0.state == abbr
}
}
return schools
}
private func combineArrays() -> [SchoolProtocol] {
// EVERYTHING IS FINE IN NEXT LINE
let a = privateSchoolArray
// PROBLEM OCCURS IN NEXT 2 LINES WHEN DOWNCASTING
let b = privateSchoolArray as [SchoolProtocol]
let c = publicSchoolArray as [SchoolProtocol]
return b + c
}
private func getPublicSchools() -> [PublicSchoolModel] {
guard let jsonData = getJSONData(from: .publicSchool) else { return [] }
guard let schools = decode(jsonData: jsonData, using: [PublicSchoolModel].self) else { return [] }
return schools
}
private func getPrivateSchools() -> [PrivateSchoolModel] {
guard let jsonData = getJSONData(from: .privateSchool) else { return [] }
guard let schools = decode(jsonData: jsonData, using: [PrivateSchoolModel].self) else { return [] }
return schools
}
private func getJSONData(from resource: JSONFile) -> Data? {
let url = Bundle.main.url(forResource: resource.rawValue, withExtension: "json")!
do {
let jsonData = try Data(contentsOf: url)
return jsonData
}
catch {
print(error)
}
return nil
}
private func decode<M: Decodable>(jsonData: Data, using modelType: M.Type) -> M? {
do {
//here dataResponse received from a network request
let decoder = JSONDecoder()
let model = try decoder.decode(modelType, from:
jsonData) //Decode JSON Response Data
return model
} catch let parsingError {
print("Error", parsingError)
}
return nil
}
}
And then it is just called in another class by schoolJSONHandler.getSchoolArray(sorted: true, filtered: true, by: "WA")
It's my class initializer:
typealias JSONDictionaty = [String: AnyObject]
private var _conditions: [[String: String]]?
init(data: JSONDictionaty) {
if let conditions = data["conditions"] as? [JSONDictionaty] {
if conditions.count > 0 {
self._conditions = [[:]] // init _condition
}
// adding all conditions to _condition variable
for condition in conditions {
if let subCond = condition as? [String: String] ,
type = subCond["type"],
expression = subCond["expression"] {
self._conditions?.append([type: expression])
}
}
}
/// deinit _condition variable
if self._conditions?.count == 0 {
self._conditions = nil
}
}
}
I have leaks on this lines:
if let type = subCond["type"], expression = subCond["expression"] {
self._conditions?.append([type: expression])
}
but if I change above lines to this line, memory leaks will be gone:
self._conditions?.append(subCond)
I am new to Swift. I tried with this Swift link Detect a Null value in NSDictionaryNSDictionary, but I failed to do so.
Data:
"end_time" = "<null>"
Here is my code:
if endTime["end_time"] is NSNull {
print("your session still available ")
}
else{
print("your session end \(endTime["end_time"])")
}
Every time it is going to else statement. May be I need to convert string to null or alternative solution. Could you help me please?
Thank you.
Here's how you check null in swift:
let time = endTime["end_time"]
if time != "<null>" {
print("time is not <null>")
}
else
{
print("time is <null>")
}
You can create a NilCheck controller to check nil or null for various datatypes. For example i have created a function to remove null [if any] from the dictionary and store the array of dictionary in Userdefaults. Please be free to ask your queries :)
func removeNilAndSaveToLocalStore(array : [[String:Any]]) {
var arrayToSave = [[String:Any]]()
for place in array {
var dict = [String:Any]()
dict["AreaId"] = NilCheck.sharedInstance.checkIntForNil(nbr: place["AreaId"]! as? Int)
dict["AreaNameAr"] = NilCheck.sharedInstance.checkStringForNil(str: place["AreaNameAr"]! as? String)
dict["AreaName"] = NilCheck.sharedInstance.checkStringForNil(str: place["AreaName"]! as? String)
dict["GovernorateId"] = NilCheck.sharedInstance.checkIntForNil(nbr: place["GovernorateId"]! as? Int)
arrayToSave.append(dict)
}
LocalStore.setAreaList(token: arrayToSave)
}
class NilCheck {
static let sharedInstance : NilCheck = {
let instance = NilCheck()
return instance
}()
func checkStringForNil(str : String?) -> String {
guard let str = str else {
return String() // return default string
}
return str
}
func checkIntForNil(nbr : Int?) -> Int {
guard let num = nbr else {
return 0 // return default Int
}
return num
} }