I need to use struct in my custom framework, how can the following be done?
Here my framework:
MyFramework.framework
public class SomeClass : NSObject {
public struct myDetails {
public var firstName: String?
public var lastName: String?
public init(fName: String? LastName lName: String?) {
self.firstName = fName
self.lastName = lName
}//init end
}// struct end
public func fillStruct(inout myStruct: myDetails) {
myStruct.firstName = "John"
myStruct.lastName = "Devid"
}
}// class end
Here's my Application:
// ViewController.swift
import MyFramework
var refObj: SomeClass?
override func ViewDidLoad() {
refObj = SomeClass()
let tempStruct = refObj.myDetails(fName: nil LastName lName: nil) // I CANT ABLE ACCESS LIKE THIS.
refObj.fillStruct(&tempStruct)
}
What I want, is my application to send the data to the struct and my framework to fill the struct and finish the function.
How can this be achieved?
Related
I have this core data issue:
*** -[NSKeyedUnarchiver decodeObjectForKey:]: value for key (id) is not an object. This will become an error in the future.
First I would like to describe what I am trying to do:
I perform a GET request to my API, get an array of elements and add the data I got to Core Data.
Here is my CoreData Entity:
extension PetEntity {
#nonobjc public class func fetchRequest() -> NSFetchRequest<PetEntity> {
return NSFetchRequest<PetEntity>(entityName: "PetEntity")
}
#NSManaged public var category: CategoryDTO?
#NSManaged public var id: Int64
#NSManaged public var name: String?
#NSManaged public var photoUrls: [String]?
#NSManaged public var status: String?
#NSManaged public var tags: [TagDTO]?
}
extension PetEntity : Identifiable {
}
My DTO I use to get Json data in and then map it to my model:
struct PetDTO: Codable {
var id: Int
var category: CategoryDTO?
var name: String?
var photoUrls: [String]?
var tags: [TagDTO]?
var status: StatusDTO
}
public class CategoryDTO: NSObject, Codable {
var id: Int
var name: String?
public enum CodingKeys: String, CodingKey {
case id
case name
}
public required init?(coder: NSCoder) {
id = coder.decodeObject(forKey: CodingKeys.id.rawValue) as? Int ?? 0 // error here
name = coder.decodeObject(forKey: CodingKeys.name.rawValue) as? String
}
}
extension CategoryDTO: NSSecureCoding{
public static var supportsSecureCoding: Bool{
return true
}
public func encode(with coder: NSCoder) {
coder.encode(id, forKey: CodingKeys.id.rawValue)
coder.encode(name, forKey: CodingKeys.name.rawValue)
}
}
#objc(CategoryDtoTransformer)
public final class CategoryDtoTransformer: NSSecureUnarchiveFromDataTransformer {
public static let name = NSValueTransformerName(rawValue: String(describing: CategoryDtoTransformer.self))
public override static var allowedTopLevelClasses: [AnyClass] {
return [CategoryDTO.self, NSString.self, NSArray.self]
}
#objc dynamic
public static func register() {
let transformer = CategoryDtoTransformer()
ValueTransformer.setValueTransformer(transformer, forName: name)
}
}
public class TagDTO: NSObject, Codable {
var id: Int
var name: String?
public enum CodingKeys: String, CodingKey {
case id
case name
}
public required init?(coder: NSCoder) {
id = coder.decodeObject(forKey: CodingKeys.id.rawValue) as? Int ?? 0 // Error here
name = coder.decodeObject(forKey: CodingKeys.name.rawValue) as? String
}
}
extension TagDTO: NSSecureCoding{
public static var supportsSecureCoding: Bool{
return true
}
public func encode(with coder: NSCoder) {
coder.encode(id, forKey: CodingKeys.id.rawValue)
coder.encode(name, forKey: CodingKeys.name.rawValue)
}
}
#objc(TagDtoTransformer)
public final class TagDtoTransformer: NSSecureUnarchiveFromDataTransformer {
public static let name = NSValueTransformerName(rawValue: String(describing: TagDtoTransformer.self))
public override static var allowedTopLevelClasses: [AnyClass] {
return [TagDTO.self, NSString.self, NSArray.self]
}
#objc dynamic
public static func register() {
let transformer = TagDtoTransformer()
ValueTransformer.setValueTransformer(transformer, forName: name)
}
}
enum StatusDTO: String, Codable {
case available
case sold
case pending
}
And here is my CRUD code for adding to database:
class CoreDataPetPersistance: PetPerstitenceProtocol {
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
func add(pet: PetDTO) {
let newPet = PetEntity(context: self.context)
newPet.id = Int64(pet.id)
newPet.name = pet.name
newPet.category = pet.category
newPet.photoUrls = pet.photoUrls
newPet.tags = pet.tags
newPet.status = pet.status.rawValue
do {
try self.context.save()
} catch {
print(error)
}
}
}
Also I have added constraints to my ID to make each pet I get from API unique and registered CategoryDTO and TagDTO in app delegate. It should be fine.
Full error I get:
CoreData: error: -executeRequest: encountered exception = Error Domain=NSCocoaErrorDomain Code=133000 "Attempt to access an object not found in store." UserInfo={objectID=0xa57351074a65fcb3 <x-coredata://FEB02FEE-9B8B-4785-82A1-263F82D4CDBC/PetEntity/p1095>} with userInfo = {
NSCocoaErrorDomain = 133000;
NSUnderlyingError = "Error Domain=NSCocoaErrorDomain Code=133000 \"Attempt to access an object not found in store.\" UserInfo={objectID=0xa57351074a65fcb3 <x-coredata://FEB02FEE-9B8B-4785-82A1-263F82D4CDBC/PetEntity/p1095>}";
}
Please help me fix this and explain why this error happens
Thanks
There are dedicated APIs for scalar types
id = coder.decodeInt64(forKey: CodingKeys.id.rawValue)
Have a look for all available APIs
To match the types declare id as Int64 or convert the type.
I have a phone number model which looks like this:
import UIKit
import Foundation
struct PhoneValidation : OptionSet {
let rawValue: Int
static let phoneInValid = PhoneValidation(rawValue: 1 << 0)
static let phoneValid = PhoneValidation(rawValue: 1 << 1)
static let smsValidationAttempted = PhoneValidation(rawValue: 1 << 2)
static let smsValidationFailed = PhoneValidation(rawValue: 1 << 3)
static let smsValidationSuccessful = PhoneValidation(rawValue: 1 << 4) // OTP is successfully validated in backend. The field should be non-editable in this duration
static let smsValidationOTPTriggered = PhoneValidation(rawValue: 1 << 5) // OTP validation triggered. The field should be non-editable in this duration
}
class PhonesViewModel: NSCopying {
public var phoneType: PhoneNumberType = PhoneNumberType.mobile
public var phone: String?
public var code: String?
public var countryCode: String?
public var isValid : PhoneValidation?
func copy(with zone: NSZone? = nil) -> Any {
let copy = PhonesViewModel()
copy.phoneType = phoneType
copy.phone = phone
copy.code = code
copy.countryCode = countryCode
copy.isValid = isValid
return copy
}
}
As you can see above the phone model can transition between different states. The SMS validation is available for few countries and for few it is not applicable. So, I plan on setting smsValidationOTPTriggered state when SMS validation is applicable for a country and while the validation is in progress.
What I need here is, while the states smsValidationOTPTriggered or smsValidationSuccessful are set I would not want any module of the application to modify the values(phoneType, phone, code, countryCode) of the model. In other words, I would like the model to switch to a read-only mode while these 2 states are set in model and would like the module to be informed with an error or exception when a modification is attempted.
Is there a best practice already available for what I am trying to achieve here? I have searched before raising this question but did not find any. How can I achieve this?
Thanks,
Raj Pawan Gumdal
How about something like this, I think its better to use property wrappers for your case! The below is not an exact solution but can modify/change to accommodate your need
import UIKit
enum PhoneNumberType {
case mobile
}
enum PhoneValidation {
case phoneInValid
case phoneValid
case smsValidationAttempted
case smsValidationFailed
case smsValidationSuccessful
case smsValidationOTPTriggered
}
struct PhonesViewModel {
public var phoneType: PhoneNumberType = PhoneNumberType.mobile
public var phone: String?
public var code: String?
public var countryCode: String?
public var phoneValidation : PhoneValidation?
func validate(value: [PhoneValidation]) -> Bool {
//add proper check here
return false
}
}
#propertyWrapper
struct Wrapper {
private(set) var value: PhonesViewModel? = nil
var validators: [PhoneValidation] = []
var wrappedValue: PhonesViewModel? {
get { value }
set {
if let model = newValue, model.validate(value: validators) {
value = newValue
print("Value assigned")
} else {
print("Value not assigned")
}
}
}
}
struct SomeOtherClass {
#Wrapper(validators: [PhoneValidation.phoneInValid])
var model: PhonesViewModel?
}
var a = SomeOtherClass()
a.model = PhonesViewModel()
a.model = PhonesViewModel()
You can use a technique with the name "popsicle immutability". An object is initially mutable, but can be "frozen". Modifications for frozen objects are forbidden. In your case PhonesViewModel become frozen when isValid property have value smsValidationOTPTriggered or smsValidationSuccessful.
Let's add Freezable protocol for requirements to objects that can become immutable and conforming for PhonesViewModel:
protocol Freezable: class {
var isFrozen: Bool { get }
}
extension PhonesViewModel: Freezable {
var isFrozen: Bool {
isValid == .smsValidationOTPTriggered || isValid == .smsValidationSuccessful
}
}
Now we must add validation for isFrozen value when a property is assigned. It can be added in property observers like:
...
public var phone: String? {
didSet {
validate()
}
}
...
private func validate() {
assert(!isFrozen)
}
Or using property wrapper:
#propertyWrapper
struct Guarded<Value> {
private var value: Value
init(wrappedValue: Value) {
value = wrappedValue
}
#available(*, unavailable)
var wrappedValue: Value {
get { fatalError("only works on instance properties of classes that conforms to Freezable protocol") }
set { fatalError("only works on instance properties of classes that conforms to Freezable protocol") }
}
static subscript<EnclosingSelf: Freezable>(
_enclosingInstance object: EnclosingSelf,
wrapped wrappedKeyPath: ReferenceWritableKeyPath<EnclosingSelf, Value>,
storage storageKeyPath: ReferenceWritableKeyPath<EnclosingSelf, Self>
) -> Value {
get {
object[keyPath: storageKeyPath].value
}
set {
precondition(!object.isFrozen, "Object \(object) is frozen! Modifications are forbidden")
object[keyPath: storageKeyPath].value = newValue
}
}
}
So your class will look like:
class PhonesViewModel: NSCopying {
#Guarded
public var phoneType: PhoneNumberType = PhoneNumberType.mobile
#Guarded
public var phone: String?
#Guarded
public var code: String?
#Guarded
public var countryCode: String?
#Guarded
public var isValid : PhoneValidation?
func copy(with zone: NSZone? = nil) -> Any {
let copy = PhonesViewModel()
copy.phoneType = phoneType
copy.phone = phone
copy.code = code
copy.countryCode = countryCode
copy.isValid = isValid
return copy
}
}
I need to use two classes with the same name in swift 5. For this, I have created those two classes in two different modules, but I am confused on how to use both the classes in an UIViewController
one of my class is Person which is in models > student module and another class is Person with is in the models module
I have tried importing class like
import class models.student.Person
class BookViewController: UIViewController {
var students:[Person] = [] //it should call models.student.Person
var people: [Person] = [] //it should call models.Person
...
but above Person class is pointing to models.Person only, It is not pointing to models.student.Person
Person class in models > Person.swift is
import Foundation
// MARK: - Person
public struct Person: Codable {
public let firstName: String?
public let lastName: String?
public let address: String?
public let phone: String?
enum CodingKeys: String, CodingKey {
case firstName = "first_name"
case lastName = "last_name"
case address = "address"
case phone = "phone"
}
public init(firstName: String?, lastName: String?, address: String?, phone: String?) {
self.firstName = firstName
self.lastName = lastName
self.address = address
self.phone = phone
}
}
and the models.student.Person.swift is
import Foundation
// MARK: - Person
public struct Person: Codable {
public let fullName: String?
public let educationalQualification: String?
public let college: String?
enum CodingKeys: String, CodingKey {
case fullName = "full_name"
case educationalQualification = "educational_qualification"
case college = "college"
}
public init(fullName: String?, educationalQualification: String?, college: String?) {
self.fullName = fullName
self.educationalQualification = educationalQualification
self.college = college
}
}
I need both the class in my BookViewController
I can't change the name of the class to a different one, I should use the same class name.
You could try typealias:
typealias StudentPerson = yourStudentFramework.Person
and then use it like:
import yourStudentFramework
class BookViewController: UIViewController {
var students:[StudentPerson] = [] //it should call yourStudentFramework.Person
var people: [Person] = [] //it should call models.Person
...
I have a query in Swift 3.0. I am calling a web service and getting an array of contacts from the server. I am creating a class Contacts:
Class Contacts: NSObject {
var address : String?
var email : String?
var gender : String?
var name : String?
var id : String?
var phone : String?
//var array to store class object
var arrayOfContacts = [Contacts]()
override init() {
super.init()
}
}
I need to access all the objects store in arrayOfContacts from somewhere in my code.
How should I create such a functionality or I am on the wrong way?
Two ways
way 1:
Declare your arrayOfContacts as static
class Contacts: NSObject {
var address : String?
var email : String?
var gender : String?
var name : String?
var id : String?
var phone : String?
//var array to store class object
static var arrayOfContacts = [Contacts]()
override init() {
super.init()
}
}
And access it as
print(Contacts.arrayOfContacts)
Way 2:
If you want your contacts to be singleton use
class Contacts: NSObject {
var address : String?
var email : String?
var gender : String?
var name : String?
var id : String?
var phone : String?
var arrayOfContacts = [Contacts]()
static var sharedInstance = Contacts()
private override init() {
super.init()
}
}
Then access it as
Contacts.sharedInstance.arrayOfContacts
Hope it helps
actually you can create an array of objects in same class but you must not it in same class, so I provide code how to do it
Class Contacts: NSObject {
var address : String?
var email : String?
var gender : String?
var name : String?
var id : String?
var phone : String?
override init() {
super.init()
}
static func arrayOfObject(_ array: NSArray)-> [Contacts] {
var contactsArr: [Contacts] = []
for data in array {
let cont = Contacts(...)//init all properties
contactsArr.append(cont)
}
return contactsArr
}
}
and call this method where you get your data
Contacts.arrayOfObject(<array of data>)
You can use Singleton pattern for this.
Play with it in playground.
import Foundation
class Contacts: NSObject {
var address : String?
var email : String?
var gender : String?
var name : String?
var id : String?
var phone : String?
override init() {
super.init()
ContactSingleton.shared.arrayOfContacts.append(self)
}
}
final class ContactSingleton {
// MARK: Properties
internal static let shared = ContactSingleton()
internal var arrayOfContacts: [Contacts] = []
// MARK: - Initializer
private init() { }
}
let contact1 = Contacts()
print(ContactSingleton.shared.arrayOfContacts.count) // 1
let contact2 = Contacts()
let contact3 = Contacts()
print(ContactSingleton.shared.arrayOfContacts.count) // 3
I have a viewcontroller in Swift that works well, and I would like to use it from an Objective-C class. My problem is that I don't know how to use it.
I try to add the delegate in the header of my main Objective-C class.
#interface MainViewController : UIViewController <PlanningSearchDelegate>
But I think as it is a swift class, it doesn't work. The error is:
Cannot find protocol declaration for PlanningSearchDelegate
Then, I tried to add the #objc word in the swift class.
import UIKit
#objc protocol PlanningSearchDelegate
{
func planningSearchDidSelectResult(_ result:DALSearchResult)
}
class PlanningSearchViewController: UIViewController
{
}
extension PlanningSearchViewController: UITableViewDelegate
{
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
{
let result = _results[indexPath.row]
delegate?.planningSearchDidSelectResult(result)
}
}
But I have an error message:
Method cannot be a member of an #objc protocol because the type of the parameter cannot be represented in Objective-C.
I put the delegate = self in the prepareForSegue of the main Objective-C class without success :
else if ([segue.identifier isEqualToString:#"showSearchViewSegue"])
{
PlanningSearchViewController *searchViewController = segue.destinationViewController;
searchViewController.delegate = self;
}
The error message is:
Property 'delegate' not found on object of type "PlanningSearchViewController *'
Here is the DALSearchResult Class :
import Foundation
class DALSearchResult
{
var contactId: Int
var firstname: String
var lastname: String
var email: String
var phone: String
var city: String
var registrationId: Int?
var slotId: Int?
var vehicleId: Int?
var startDate: Date?
var vehicleName: String?
init(contactId:Int, firstname:String, lastname:String, email:String, phone:String, city:String)
{
self.contactId = contactId
self.firstname = firstname
self.lastname = lastname
self.email = email
self.phone = phone
self.city = city
}
convenience init(registrationId:Int, slotId:Int, vehicleId:Int, contactId:Int, firstname:String, lastname:String, email:String, phone:String, city:String, startDate:Date, vehicleName: String)
{
self.init(contactId: contactId, firstname: firstname, lastname: lastname, email: email, phone: phone, city: city)
self.registrationId = registrationId
self.vehicleId = vehicleId
self.slotId = slotId
self.startDate = startDate
self.vehicleName = vehicleName
}
}
Anyone has an idea ?
Thanks in advance.
Make DALSearchResult class Objective-C compatible. If your DALSearchResult does not extend from NSObject, you can extend it form NSObject.
Can you share how the declaration of DALSearchResult looks like? Maybe it's an enum which cannot be translated to Obj-C. In this case you might need to pass the result in a different way.
To access swift from objective-C you have to import the swift.h always in your class that you want to use it like so
#import "Yourprojectname-Swift.h"