I am converting my project Objective-C code to Swift. The thing fine but while having its for in loop i am facing error something like its type conversion issue. Thanks In Advance.
class GymUserSession: NSObject {
var passes = [AnyHashable]()
func getPassList() -> [AnyHashable]? {
var list = [AnyHashable]()
for pass: GymPass? in passes {
if pass?.isGift == nil || pass?.activated != nil {
if let aPass = pass {
list.append(aPass)
}
}
}
return list
}
}
GymPass is another NSObject Class
class GymPass: NSObject {
var gymID : String
var passID : String
var isGift : Bool
var activated : Bool
var dateCreated : Date?
var dateActivated : Date?
}
The short answer is that Swift can't implicitly downcast an AnyHashable to a GymPass, which is what you have asked it to do.
You could fix the error by explicitly downcasting, but really that is just addressing one small issue that would let the code compile
In Swift you should always use the most explicit type you can, when it is known. Types such as Any, AnyObject and AnyHashable should only be used when you don't know the type or there may be multiple types. For example a dictionary obtained decoding JSON could be [String:Any] since you know it will have String keys, but the value types will be varied.
In this case, presumably, you know that passes will contain GymPass instances. A more "Swifty" version of your code could look something like this (but it is hard to be specific as I don't have enough detail on where the data is coming from and what you are trying to achieve, exactly):
struct GymPass {
var gymID: String
var passID: String
var isGift: Bool
var dateCreated: Date
var dateActivated: Date?
var isActivated: Bool {
get {
return self.dateActivated != nil
}
}
}
class GymUserSession: NSObject {
var passes = [GymPass]()
func getPassList() -> [GymPass] {
return passes.filter ( { $0.isGift || $0.isActivated } )
}
}
Related
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 2 years ago.
Improve this question
I have a computed property of type Parameters in my APIRouter
// MARK: - Parameters
private var parameters: Parameters? {
switch self {
case .searchForDoctors(let doctorsFilter):
var params: Parameters = ["main_category_id": doctorsFilter.0, "page": doctorsFilter.1, "specialty_id": doctorsFilter.2, "city_id": doctorsFilter.3, "region_id": doctorsFilter.4, "name": doctorsFilter.5, "company_id": doctorsFilter.6, "order_by": doctorsFilter.7]
return params
default:
return nil
}
}
some values in the Typealias called doctorsFilter are optional.
currently I have a warning asking me to provide default value for the optional values, and I don't want to provide default values , I want to check if the value exist to add it, otherwise i will not add the key and the value
how can I safely unwrap the optional values and add it to the parameters dictionary with out saying if let for all optional values?
example:
if let specialtyID = doctorsFilter.2 {
params["specialty_id"] = specialtyID
}
I don't want to unwrap it this way as I will check for all optional values and it will take more lines of code
EDIT:-
the DoctorsFilter type is documented, when I initialize an instance of type DoctorsFilter the autocomplete tells me which of them is what, I I've thought about making the DoctorsFilter class before but I'm looking for another way if any, maybe a built in reserved word can handle the whole situation! , I want to make it simple as much as possible.
making a function that handles the dictionary and returns it in DoctorsFilter class is an option. I'm thinking of adding this function to the APIRouter, is it fine to add it there? is it the rule of the APIRouter to handle the parameters ? or the APIRouter just interested in taking the parameters and will not handle it ?
There is no "one line" solution, but you can use KeyPaths to reduce the series of if let ... statements down to a loop.
Start by creating a struct for your filter rather than using a tuple.
To facilitate this, we define a protocol for Parameterable - This protocol requires a dictionary that maps parameter names (String) to the property (KeyPath) that holds that parameter name as well as a function to return the parameters dictionary.
protocol Parameterable {
var paramNames: [String:KeyPath<Self,String?>] {get}
func parameters() -> [String:Any]
}
Use an extension to create a default implementation of the parameters() function, as the code will be the same for all Parameterables. It iterates over the dictionary entries and uses the associated KeyPath to access the relevant property and put it in the output dictionary. If a given property is nil then it simply isn't added to the output dictionary, because that is how dictionaries work. No need to explicitly check.
(If you import Alamofire then you can use the typedef Parameters where I have used [String:Any])
extension Parameterable {
func parameters() -> [String:Any] {
var parameters = [String:Any]()
for (paramName,keypath) in self.paramNames {
parameters[paramName]=self[keyPath:keypath]
}
return parameters
}
}
Use this protocol to create a DoctorsFilter implementation:
struct DoctorsFilter: Parameterable {
var mainCategoryId: String?
var page: String?
var specialtyId: String?
var cityID: String?
var regionId: String?
var name: String?
var companyId: String?
var orderBy: String?
let paramNames:[String:KeyPath<Self,String?>] = [
"main_category_id":\.mainCategoryId,
"page":\.page,
"specialty_id":\.specialtyId,
"city_id":\.cityID,
"region_id":\.regionId,
"name":\.name,
"company_id":\.companyId,
"order_by":\.orderBy]
}
private var parameters: Parameters? {
switch self {
case .searchForDoctors(let doctorsFilter):
return doctorsFilter.parameters()
case .someOtherThing(let someOtherThing):
return someOtherThing.parameters()
default:
return nil
}
}
}
The other approach is to simply split your creation of the parameters dictionary into multiple lines; If you assign nil against a dictionary key then there is no key/value pair stored in the dictionary for that key. In this case I have left your tuple approach in place, but you could use the struct (and I strongly suggest you do so)
private var parameters: Parameters? {
switch self {
case .searchForDoctors(let doctorsFilter):
var params: Parameters()
params["main_category_id"] = doctorsFilter.0
params["page"] = doctorsFilter.1
params["specialty_id"] = doctorsFilter.2
params["city_id"] = doctorsFilter.3
params["region_id"] = doctorsFilter.4
params["name"] = doctorsFilter.5
params["company_id"] = doctorsFilter.6
params["order_by"] = doctorsFilter.7
return params
default:
return nil
}
}
If we want to handle mixed properties, rather than just optional strings, we need to modify the code slightly. We need to use PartialKeyPath. This makes the code a little more complex since the subscript operator for a PartialKeyPath returns a double optional. This needs to be handled.
protocol Parameterable {
var paramNames: [String:PartialKeyPath<Self>] {get}
func parameters() -> [String:Any]
}
extension Parameterable {
func parameters() -> [String:Any] {
var parameters = [String:Any]()
for (paramName,keypath) in self.paramNames {
let value = self[keyPath:keypath] as? Any?
if let value = value {
parameters[paramName] = value
}
}
return parameters
}
}
struct DoctorsFilter:Parameterable {
var mainCategoryId: String?
var page: String?
var specialtyId: String?
var cityID: Int
var regionId: String?
var name: String?
var companyId: String?
var orderBy: String?
let paramNames:[String:PartialKeyPath<Self>] =
["main_category_id":\Self.mainCategoryId,
"page":\Self.page,
"specialty_id":\Self.specialtyId,
"city_id":\Self.cityID,
"region_id":\Self.regionId,
"name":\Self.name,
"company_id":\Self.companyId,
"order_by":\Self.orderBy]
}
There are three primary ways to safely unwrap an optional. You can also provide default values if you wish to unwrap an optional.
Guard Statement Unwrapping
var firstString: String?
// In some cases you might performa a break, continue or a return.
guard let someString = firstString else { return }
print(someString)
If Let Unwrapping
var secondString: String?
var thirdString: String?
thirdString = "Hello, World!"
// Notice that we are able to use the same "IF LET" to unwrap
// multiple values. However, if one fails, they all fail.
// You can do the same thing with "Guard" statements.
if let someString = secondString,
let someOtherString = thirdString {
print(someString)
print(someOtherString)
} else {
// With this code snippet, we will ALWAYS hit this block.
// because secondString has no value.
print("We weren't able to unwrap.")
}
Default Value Unwrapping
var fourthString: String?
// The ?? is telling the compiler that if it cannot be unwrapped,
// use this value instead.
print(fourthString ?? "Hello, World")
In Swift it is recommended that anytime you see a ! that you use some form of unwrapping. Swift is very "Type Safe".
Here's a resource you can use for Optionals.
https://docs.swift.org/swift-book/LanguageGuide/TheBasics.html
Your Solution
Your solution might look something like this.
private var parameters: Parameters? {
switch self {
case .searchForDoctors(let doctorsFilter):
if let mainCatID = doctorsFilter.0,
let page = doctorsFilter.1,
let specialtyID = doctorsFilter.2,
let cityID = doctorsFilter.3,
let regionID = doctorsFilter.4,
let name = doctorsFilter.5,
let companyID = doctorsFilter.6,
let orderBy = doctorsFilter.7 {
params: Parameters = ["main_category_id": mainCatID,
"page": page,
"specialty_id": specialtyID,
"city_id": cityID,
"region_id": regionID,
"name": name,
"company_id": companyID,
"order_by": orderBy]
return params
} else {
//Unable to safely unwrap, return nothing.
return nil
}
default:
return nil
}
}
Goal of the code:
To assign a struct dictionary with Strings as Keys and String Arrays as values to a variable and then pull one (can be at random) specific String key value in the String Array and return that one String element in the underlying String Array so that it can be used elsewhere (potentially assigned to a label.text)
Essentially (please reference code below), I want to access one value at random in myDictionary using a specific key ("keyOne"), and pull, let's say, "Value2" then return only the string "Value2" from the underlying String Array associated with "keyOne" using indexing.
Errors are in the code below.
The issue I'm thinking is that I haven't figured out how to turn my final var Testing = dict["keyOne"] into an Int compatible index... if it was an index, the code would pull an Int value and the corresponding String from the three Strings in the underlying value array (due to the three String values associated with "keyOne").
Also, variableView() just inherits the datasource from several other containers, but the var dataSource : Structure? is the main reference, so that is what I included.
Code so far:
let myDictionary = [Structure(name: "keyOne", text: ["Value1", "Value2", "Value3"]), Structure(name: "keyTwo", text: ["Value4", "Value5", "Value6"])]
lazy var dict = Dictionary(uniqueKeysWithValues: myDictionary.lazy.map { ($0.name, $0.text) })
struct Structure: Hashable {
var name: String
var text: [String]
init(name: String, text: [String]){
self.name = name
self.text = text
}
}
func variable(at index: Int) -> variableView {
let variable = variableView()
var Testing = dict["keyOne"]
variable.dataSource = Testing![index] <- Cannot assign value of type 'String' to type 'structure'
return variable
var dataSource : Structure? {
didSet {
label.text = "This is a test"
} else {
// n/a
}
}
Please note that the error message is above in the code for variable.dataSource = Testing![index].
I am also suspecting that my issue lies in the "looping" logic of how I am assigning a variable with a struct, to a datasource which references that same struct.
Any help is appreciated as I have been stuck on this for legitimately a week (I truly have exhausted every single StackOverflow answer/question pair I could find).
THANK YOU!
EDIT:
I found this documentation to assist me greatly with this, and I recommend anyone with a similar question as mine to reference this: https://swift.org/blog/dictionary-and-set-improvements/
Given the question and the discussion in the comments I would add a mutating func to the struct that removes and returns a random string
mutating func pullText() -> String? {
guard let index = text.indices.randomElement() else {
return nil
}
return text.remove(at: index)
}
Example
if let index = myDictionary.firstIndex(where: { $0.name == "keyOne" }),
let text = myDictionary[index].pullText() {
someLabel.text = text
}
Here is another example based on the code in the question
Assuming VariableView looks something like this
struct VariableView: View {
var dataSource : Structure?
var word: String?
var body: some View {
Text(word ?? "")
}
}
Then the func variable can be changed to
func variable() -> VariableView {
var variable = VariableView()
if let index = dict.firstIndex(where: { $0.name == "keyOne" }) {
variable.dataSource = dict[index]
variable.word = dict[index].pullText()
}
return variable
}
I have 2 functions that have a lot in common, and I want to re-factor my code to remove the repeated logic, however the things that are different are types, specifically a protocol, and and a struct type. The way I can think about it now is that to re-factor this I'd have 1 common method that would take one protocol type as an argument, and one struct type with the restriction that the struct type must implement the protocol 'DataDictionaryStore'. And would return an array of the protocol type passed in as argument 1
I've tried to implement this with generics, but from how I understand it, you still pass an instance as an argument when using generics, not the actual type itself.
The methods I'd like to re-factor in the code below are 'articles()', and 'authors()'
Here's the code (which can be copied to a playground Xcode 7+):
import Foundation
protocol Article {
var headline: NSString? { get }
}
protocol Author {
var firstName: NSString? { get }
}
protocol DataDictionaryStore {
init(dataDictionary: NSDictionary)
}
struct CollectionStruct {
let arrayOfModels: [NSDictionary]
//This function is identical to authors() except for return type [Article], and 'ArticleStruct'
func articles() -> [Article] {
var articlesArray = [Article]()
for articleDict in arrayOfModels {
let articleStruct = ArticleStruct(dataDictionary: articleDict)
articlesArray.append(articleStruct)
}
return articlesArray
}
func authors() -> [Author] {
var authorsArray = [Author]()
for authorDict in arrayOfModels {
let authorStruct = AuthorStruct(dataDictionary: authorDict)
authorsArray.append(authorStruct)
}
return authorsArray
}
}
struct ArticleStruct : Article, DataDictionaryStore {
var internalDataDictionary: NSDictionary
init(dataDictionary: NSDictionary) {
internalDataDictionary = dataDictionary
}
var headline: NSString? { return (internalDataDictionary["headline"] as? NSString) }
}
struct AuthorStruct : Author, DataDictionaryStore {
var internalDataDictionary: NSDictionary
init(dataDictionary: NSDictionary) {
internalDataDictionary = dataDictionary
}
var firstName: NSString? { return (internalDataDictionary["firstName"] as? NSString) }
}
var collStruct = CollectionStruct(arrayOfModels: [NSDictionary(objects: ["object1", "object2"], forKeys: ["key1", "headline"])])
print(collStruct)
var articles = collStruct.articles()
print(articles)
for article in articles {
print(article.headline)
}
If there is another way to re-factor this to remove the repeated logic, all suggestions welcome.
It's not exactly an answer to your question, but this might simplify it enough for you to be happy:
func articles() -> [Article] {
return arrayOfModels.map(ArticleStruct.init)
}
func authors() -> [Author] {
return arrayOfModels.map(AuthorStruct.init)
}
Based on PEEJWEEJ's answer, this refactor is also worth a shot. Instead of returning a single array, you can return a tuple of authors and articles. If you aren't going to be processing both the authors and articles arrays at once, this method is more expensive. But the syntax is much nicer than the previous solution using generics below.
func allObjects() -> (authors: [AuthorStruct], articles: [ArticleStruct]) {
let authors = arrayOfModels.map(AuthorStruct.init)
let articles = arrayOfModels.map(ArticleStruct.init)
return(authors, articles)
}
You would then call the method like this:
let objects = collection.allObjects()
let authors = objects.authors
let articles = objects.articles
I'm not a huge fan of the clarity here but maybe you can refactor it a bit. It seems to work at least.
func allObjectsOfType<T>(type: T.Type) -> [T] {
var objectArray = [T]()
for objectDict in arrayOfModels {
var objectStruct: T?
if type == Author.self {
objectStruct = AuthorStruct(dataDictionary: objectDict) as? T
} else if type == Article.self {
objectStruct = ArticleStruct(dataDictionary: objectDict) as? T
}
guard objectStruct != nil else {
continue
}
objectArray.append(objectStruct!)
}
return objectArray
}
You can then call it like this...
collection.allObjectsOfType(Author)
collection.allObjectsOfType(Article)
I have a class I made that I fill with some JSON data. A couple properties in the class MIGHT be null in some cases. I thought I prepared for this but when I try to access the class and one of the properties are null I get an error unexpectedly found nil while unwrapping an Optional value. This error only pops if the value of one of the properties is null. Here's my class:
class Inventory {
private var _id, _quantityOnHand: Int!;
private var _item, _description: String!;
private var _supplierId: Int?;
private var _supplierName: String?;
var id: Int {
get {
return _id;
}
}
var item: String {
get {
return _item;
}
}
// ... removed for brevity
var supplierId: Int {
get {
return _supplierId!;
}
}
var supplierName: String {
get {
return _supplierName!; //this is where error is when value is null
}
}
init(id: Int, item: String, description: String, quantityOnHand: Int, supplierId: Int?, supplierName: String?) {
_id = id;
_item = item;
_description = description;
_quantityOnHand = quantityOnHand;
_supplierId = supplierId;
_supplierName = supplierName;
}
}
The fields supplierId and supplierName MIGHT possibly be null (not always though. When I run my application and it tries to get the value from one of the null properties I get the error.
I tried removing the ! forced unwrapped from my getter but it won't compile and complains about it.
I have a UITableView with a segue the sends the information to the next view controller. In it I'm putting all the values of the tapped row of the UITableView into variables. Here's the segue:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "segueToItemDetail" {
if let destination = segue.destinationViewController as? InventoryItemViewController {
if let itemIndex = inventoryListTableView.indexPathForSelectedRow?.row {
destination.idInt = warehouseItems[itemIndex].id;
destination.itemString = warehouseItems[itemIndex].item;
destination.descriptionString
= warehouseItems[itemIndex].description;
destination.supplierNameString = warehouseItems[itemIndex].supplierName ?? "";
destination.quantityOnHandInt = warehouseItems[itemIndex].quantityOnHand;
}
}
}
I tried using a Nil Coalescing Operator on the properties that might be null but it still throws the error when trying to pass any values that are null.
Why can't I satiate these optionals?
If your design can live with empty string for "no String value" and 0 for "no Int value" this is a much easier syntax (simplified with 3 properties). Due to the declaration as constant all properties are treated as read-only without a private backing variable.
class Inventory {
let id, supplierId: Int
let supplierName: String
init(id: Int, supplierId: Int?, supplierName: String?) {
self.id = id
self.supplierId = supplierId ?? 0
self.supplierName = supplierName ?? ""
}
}
Rather than if supplierName == nil you can check if supplierName.isEmpty
Try this
var supplierId: Int? {
return _supplierId;
}
var supplierName: String? {
return _supplierName; //return optional value
}
Don't forcefully unwrap your iVars while getting its value, rather check it when using it's value.
For your information :
when you have only get block directly return value instead of writing get block.
https://github.com/github/swift-style-guide
I'm banging my head against the wall with the following code in Swift. I've defined a simple protocol:
protocol Nameable {
var name : String { get set }
}
and implemented that with:
class NameableImpl : Nameable {
var name : String = ""
}
and then I have the following method in another file (don't ask me why):
func nameNameable( nameable: Nameable, name: String ) {
nameable.name = name
}
The problem is that the compiler gives the following error for the property assignment in this method:
cannot assign to 'name' in 'nameable'
I can't see what I'm doing wrong... The following code compiles fine:
var nameable : Nameable = NameableImpl()
nameable.name = "John"
I'm sure it's something simple I've overlooked - what am I doing wrong?
#matt's anwer is correct.
Another solution is to declare Nameable as a class only protocol.
protocol Nameable: class {
// ^^^^^^^
var name : String { get set }
}
I think, this solution is more suitable for this case. Because nameNameable is useless unless nameable is a instance of class.
It's because, Nameable being a protocol, Swift doesn't know what kind (flavor) of object your function's incoming Nameable is. It might be a class instance, sure - but it might be a struct instance. And you can't assign to a property of a constant struct, as the following example demonstrates:
struct NameableStruct : Nameable {
var name : String = ""
}
let ns = NameableStruct(name:"one")
ns.name = "two" // can't assign
Well, by default, an incoming function parameter is a constant - it is exactly as if you had said let in your function declaration before you said nameable.
The solution is to make this parameter not be a constant:
func nameNameable(var nameable: Nameable, name: String ) {
^^^
NOTE Later versions of Swift have abolished the var function parameter notation, so you'd accomplish the same thing by assigning the constant to a variable:
protocol Nameable {
var name : String { get set }
}
func nameNameable(nameable: Nameable, name: String) {
var nameable = nameable // can't compile without this line
nameable.name = name
}
Here, i written some code, that might give some idea on Associated generic type Usage:
protocol NumaricType
{
typealias elementType
func plus(lhs : elementType, _ rhs : elementType) -> elementType
func minus(lhs : elementType, _ rhs : elementType) -> elementType
}
struct Arthamatic :NumaricType {
func addMethod(element1 :Int, element2 :Int) -> Int {
return plus(element1, element2)
}
func minusMethod(ele1 :Int, ele2 :Int) -> Int {
return minus(ele1, ele2)
}
typealias elementType = Int
func plus(lhs: elementType, _ rhs: elementType) -> elementType {
return lhs + rhs
}
func minus(lhs: elementType, _ rhs: elementType) -> elementType {
return lhs - rhs
}
}
**Output:**
let obj = Arthamatic().addMethod(34, element2: 45) // 79