Initialising model object without optionals - swift

The model object is populated using JSONDecoder method. Since it is needed to pass it on tableview i need to initialise a local variable of type KBArticle on UI side. Is there any possible method of initialising an object of the KBArticle without optionals or accessing the inner array directly ?
var kbArticle = KBArticle()
Missing argument for parameter 'from' in call. Insert 'from: <#Decoder#>'
/// UI Side
var kbArticle = KBArticle()
override func viewDidLoad() {
super.viewDidLoad()
/// Web services call method
SDKCore.getInstance.getKbService().fetchKbArticles(with: topicID) { (result) in
switch result {
case .success(let kbArticle):
self.kbArticle = kbArticle
DispatchQueue.main.async {
self.tableView.reloadData()
}
case .failed(let e):
print("Error=\(e)")
}
}
}
// Model Class in SDK
public struct KBArticle: Codable {
public let id: Int
public let topic: String
public let articleCount: Int
public let articles: [Article]
}
public struct Article: Codable {
public let id: Int
public let subject: String
}
/// Method in SDK
public func fetchKbArticles(with topicId: Int, completionHandler: #escaping (ResultModel<KBArticle, Error>) -> Void) {
let request = GetKBArticles(topicId: topicId)
Networking.shared.performRequest(request) { (response) in
switch response {
case .success(let response):
do {
let decoder = JSONDecoder()
let result = try decoder.decode(KBArticle.self, from: response.data!)
completionHandler(.success(result))
} catch let error {
completionHandler(.failed(error))
}
case .failed(let error):
completionHandler(.failed(error))
}
}
}
I have to use this articles inside kbArticle struct to provide my tableview datasource. Is is possible to initialise kbArticle object without a primary initialisation using nil values???

If you need new object without optional , you can set a default value for all params
ex :
public struct KBArticle: Codable {
public var id: Int = 0
public var topic: String = ""
public var articleCount: Int = 0
public var articles: [Article]?
}
public struct Article: Codable {
public var id: Int = 0
public var subject: String = ""
}
Use: let myObj = KBArticle()
Please check this Answer

Related

Return specific property when accessing a Struct without referencing its name

Is there a way in Swift that I can get a property from a struct without having to reference it?
For example
public struct ASValue<T: Hashable>: Hashable {
private let id = UUID()
public var item: T
public init(item: T) {
self.item = item
}
}
let val = ASValue(item: "Example")
print(val) // Prints: Example
It's possible by adopting CustomStringConvertible and constraining T also to CustomStringConvertible
public struct ASValue<T: Hashable>: Hashable, CustomStringConvertible where T: CustomStringConvertible {
private let id = UUID()
public var item: T
public var description : String {
return "\(item)"
}
}
let val = ASValue(item: "Example")
print(val) // Prints: Example
And this is a struct, the init method is for free.
What you are looking for is exactly provided with property wrappers in swift. You have to make your ASValue structproperty wrapper by applying #propertyWrapper to the declaration:
#propertyWrapper
public struct ASValue<T: Hashable>: Hashable {
private let id = UUID()
public var wrappedValue: T
public init(wrappedValue: T) {
self.wrappedValue = wrappedValue
}
}
Then you can use this wrapper to wrap any property in another type and access that property directly. You can even access the ASValue wrapper type by prefixing with _:
class SomeClass {
#ASValue var val = "Example"
func printVal() {
// print(_val) for actual value
print(val) // Prints: Example
}
}
You can also pass this property with its wrapper identity to other function:
func printVal<T: Hashable>(#ASValue val: T) {
// print(_val) for actual value
print(val) // Prints: Example
}
let obj = SomeClass()
printVal(val: obj.val)
If for some constraint you can't use the property wrapper approach you can try the #dynamicCallable attribute as a workaround. This allows values created from your types to be invoked as methods with specified arguments.
#dynamicCallable
public struct ASValue<T: Hashable>: Hashable {
private let id = UUID()
public var item: T
public init(item: T) {
self.item = item
}
// need to have withArguments with argument type confirming
// to ExpressibleByArrayLiteral
func dynamicallyCall(withArguments arg: [Int] = []) -> T {
return item
}
}
let val = ASValue(item: "Example")
print(val()) // Prints: Example

Published Object Not Working - Combine Swift

Anyone who understands please help me, I want to retrieve data from Firestore and it wants to be read in realtime whenever there is a change from the database, so I use addSnapshotListener to read it, it works every time there is a change from Firestore, but it's still in object form NewsResponse. Because the final result I want to change to a NewsDomainModel form, then I continue the results from NewsResponse to _mapper.transformerResponseToDomain to be converted into NewsDomainModel, but every latest data generated using addSnapshotListener is not updated to the _mapper.transformerResponseToDomain, _mapper only reads 1 time the data sent only.
GetNewsRepository
import SwiftUI
import Core
import Combine
public class GetNewsRepository<
NewsLocaleDataSource: LocaleDataSource,
RemoteDataSource: DataSource,
Transformer: Mapper>: ObservableObject, Repository
where
NewsLocaleDataSource.Request == String,
NewsLocaleDataSource.Response == NewsModuleEntity,
RemoteDataSource.Request == String,
RemoteDataSource.Response == [NewsResponse],
Transformer.Request == String,
Transformer.Response == [NewsResponse],
Transformer.Entity == [NewsModuleEntity],
Transformer.Domain == [NewsDomainModel] {
public typealias Request = String
#Published public var Response: [NewsDomainModel] = [NewsDomainModel]()
private let _localeDataSource: NewsLocaleDataSource
#Published public var _remoteDataSource: RemoteDataSource
#Published public var _mapper: Transformer
public init(
localeDataSource: NewsLocaleDataSource,
remoteDataSource: RemoteDataSource,
mapper: Transformer) {
_localeDataSource = localeDataSource
_remoteDataSource = remoteDataSource
_mapper = mapper
}
public func execute(request: String?) -> AnyPublisher<[NewsDomainModel], Error> {
return self._remoteDataSource.execute(request: request)
.map { self._mapper.transformerResponseToDomain(response: $0) }
.eraseToAnyPublisher()
}
}
GetNewsRemoteDataSource
import Core
import Combine
import FirebaseFirestore
import FirebaseFirestoreSwift
import Foundation
public class GetNewsRemoteDataSource: ObservableObject, DataSource {
public typealias Request = String
#Published public var Response: [NewsResponse] = [NewsResponse]()
private let _endPoint: String
public init(endPoint: String) {
_endPoint = endPoint
}
public func execute(request: String?) -> AnyPublisher<[NewsResponse], Error> {
return Future<[NewsResponse], Error> { completion in
let ref = Firestore.firestore()
ref.collection("news").addSnapshotListener { (querySnapshot, error) in
guard let documents = querySnapshot?.documents else {
print("Document not found")
return
}
let dataJson = documents.compactMap { queryDocumentSnapshot in
try? queryDocumentSnapshot.data(as: NewsResponse.self)
} // THIS DATA WILL BE AUTOMATIC UPDATE IF DATA FROM FIRESTORE UPDATED
completion(.success(dataJson))
}
}.eraseToAnyPublisher()
}
}
NewsTransformer
import Core
import Combine
public class NewsTransformer<NewsMapper: Mapper>: ObservableObject, Mapper
where
NewsMapper.Request == String,
NewsMapper.Response == NewsResponse,
NewsMapper.Entity == NewsModuleEntity,
NewsMapper.Domain == NewsDomainModel {
#Published public var Request = String()
#Published public var Response = [NewsResponse]()
#Published public var Entity = [NewsModuleEntity]()
#Published public var Domain = [NewsDomainModel]()
private let _newsMapper: NewsMapper
public init(newsMapper: NewsMapper) {
_newsMapper = newsMapper
}
public func transformerResponseToEntity(request: String?, response: [NewsResponse]) -> [NewsModuleEntity] {
return response.map { result in
_newsMapper.transformerResponseToEntity(request: request, response: result)
}
}
public func transformerResponseToDomain(response: [NewsResponse]) -> [NewsDomainModel] {
print("\(response) DATA RESPONSE") // NOT UPDATING
return response.map { results in
_newsMapper.transformerResponseToDomain(response: results)
}
}
public func transformerEntityToDomain(entity: [NewsModuleEntity]) -> [NewsDomainModel] {
return entity.map { result in
_newsMapper.transformerEntityToDomain(entity: result)
}
}
public func transformerDomainToEntities(domain: [NewsDomainModel]) -> [NewsModuleEntity] {
return domain.map { result in
_newsMapper.transformerDomainToEntities(domain: result)
}
}
}
The sequence of images above is GetNewsRepository (Repository), GetNewsRemoteDataSource (Get Data From Firebase), NewsTransformer (Transform from NewsResponse to NewsDomainModel)
Sorry if the title I ask is wrong.
Your Future in GetNewsRemoteDataSource completes. Once a publisher has completed it won't send any further values. Instead of using a Future, return a PassthroughSubject. And use the .send method to pass values through the publisher.
https://developer.apple.com/documentation/combine/passthroughsubject

swift5 - enum protocol - Self and self and a variable

I like to separate soms function and var from a enum and thought this was a way. (Just sample code)
This results in a compile error "Type 'Self' has no member 'allCases'"
public protocol EnumFunctions: Hashable {
static var numOfCases: Self { get }
}
public extension EnumFunctions {
static var numOfCases: Self {
return self.allCases.count
}
}
my Enum Cooking Timer
public struct Cook_Time {
// struct naming for the dump command like
// dump(Cook_Time(), name: Cook_Time().structname)
let structname : String = "Cook_Time"
let a = "a"
let allValues = PFTime.allValues
public enum PFTime : String , CaseIterable, EnumFunctions {
case t1m = "1mim"
case t3m = "3min"
case t5m = "5min"
case t10m = "10min"
....
static let allValues = PFTimer.allCases.map { $0.rawValue }
}
}
How can I fix this? what is my false mind setting about this? I do need Self instead of self, rigth?
Also if I make an extension for PFTime, in a separated file, why does I get the error "Cannot find type 'PFTime' in scope"?
In order to access allCases property of CaseIterable protocol, you need to change your EnumFunctions protocol to something like this:
public protocol EnumFunctions: Hashable, CaseIterable {
static var numOfCases: Int { get }
}
public extension EnumFunctions {
static var numOfCases: Int {
return allCases.count
}
}
Also you can create an extension to PFTime just by adding Cook_Time. as prefix because PFTime is placed inside Cook_Time struct:
extension Cook_Time.PFTime {
}

Getting Error:-Cannot convert value of type 'NotificationItem' to closure result type 'RTVNotification'

---------RTV Notification------------------
import Foundation
///Notification Info
public class RTVNotification: Codable {
public let id: String
public let title: String
public let comment: String
public let urlString: String
public let supportedDevice: String
public let publishStartDateString: String
required public init(from decoder: Decoder) throws {
id = try decoder.decode(CodingKeys.id)
title = try decoder.decode(CodingKeys.title)
comment = try decoder.decode(CodingKeys.comment)
urlString = try decoder.decode(CodingKeys.urlString)
supportedDevice = try decoder.decode(CodingKeys.supportedDevice)
publishStartDateString = try decoder.decode(CodingKeys.publishStartDateString)
}
public func encode(to encoder: Encoder) throws {}
}
// MARK: - CodingKeys
private extension RTVNotification {
enum CodingKeys: String, CodingKey {
case id = "id"
case title = "name"
case comment = "comment"
case urlString = "url"
case supportedDevice = "supported_device"
case publishStartDateString = "show_started_at"
}
}
---------- RTV InformationList-----------------------------
import Foundation
// MARK: InformationList
public class RTVInformationList: Codable {
public let offset: Int
public let count: Int
public let maxCount: Int
public let notifications: [RTVNotification]
required public init(from decoder: Decoder) throws {
offset = try decoder.decodeInt(CodingKeys.offset)
count = try decoder.decodeInt(CodingKeys.count)
maxCount = try decoder.decodeInt(CodingKeys.maxCount)
notifications = (try? decoder.decode(CodingKeys.notifications)) ?? []
}
public func encode(to encoder: Encoder) throws {}
}
// MARK: - CodingKeys
private extension RTVInformationList {
enum CodingKeys: String, CodingKey {
case offset = "offset"
case count = "count"
case maxCount = "max_count"
case notifications = "information_list"
}
}
--------------- NotificationItem----------------------
public class NotificationItem: Codable {
public let id: String
public let title: String
public let comment: String
public let urlString: String
public let supportedDevice: String
public var publishStartDateString: String
init(id: String,
title: String,
comment: String,
urlString: String,
supportedDevice: String,
publishStartDateString: String) {
self.id = id
self.title = title
self.comment = comment
self.urlString = urlString
self.supportedDevice = supportedDevice
self.publishStartDateString = publishStartDateString
}
}
extension NotificationItem {
static func instantiate(with notification: RTVNotification) -> NotificationItem {
NotificationItem(
id: notification.id,
title: notification.title,
comment: notification.comment,
urlString: notification.urlString,
supportedDevice: notification.supportedDevice,
publishStartDateString: notification.publishStartDateString)
}
}
---------------- Settings View Model--------------------
public class SettingsViewModel: ViewModel {
var item = [NotificationItem]()
public var fetchedNotifications: Driver<[RTVNotification]> = .empty()
public var apiErrorEvents: Driver<RTVAPIError> = .empty()
public var notificationCount: Driver<Int> = .empty()
public func bindNotificationEvents(with trigger: Driver<Void>) {
let webService: Driver<RTVInformationListWebService> = trigger
.map { RTVInformationListParameters() }
.webService()
let result = webService.request()
apiErrorEvents = Driver.merge(apiErrorEvents, result.error())
notificationCount = result.success().map {$0.informationList.maxCount }
fetchedNotifications = result.success()
.map {$0.informationList.notifications}
----> .map {$0.map {NotificationItem.instantiate(with: $0)}}
}
}
Getting Error :- Cannot convert value of type 'NotificationItem' to closure result type 'RTVNotification' Im not understanding how to convert the types.Im new to Programming
----> .map {$0.map {NotificationItem.instantiate(with: $0)}}
fetchedNotifications variable has already been declared as type: Driver<[RTVNotification]>. The $0.map {NotificationItem.instantiate(with: $0)} is now saying that it should be type Driver<[NotificationItem]>. This wont work.
If the result should be Driver<[NotificationItem]>, then create a new variable with the correct type.
Shorter example below:
let numbers: [Int] = [5,6,5]
var newNumbers: [Int] = []
/// This will provide you with an error of:
/// Cannot convert value of type 'String' to closure result type 'Int'
newNumbers = numbers.map {
return "\($0)"
}
// correctNewNumbers is now converted correctly to a [String]
let correctNewNumbers = numbers.map {
return "\($0)"
}

How to save a struct to realm in swift?

It is easy to use Realm with classes by inheriting from Object. But how would I save a struct containing several fields to realm in Swift? E.g.
struct DataModel {
var id = 0
var test = "test"
}
I know the documentation is clear about supported types. But maybe there is nice workaround or - even better - someone from realm could write about future plans about structs.
I' suggest you to use protocols, to achive what you want.
1) Create your Struct
struct Character {
public let identifier: Int
public let name: String
public let realName: String
}
2) Create your Realm Object
final class CharacterObject: Object {
dynamic var identifier = 0
dynamic var name = ""
dynamic var realName = ""
override static func primaryKey() -> String? {
return "identifier"
}
}
3) Use protocols to transform our struct to Realm Object
public protocol Persistable {
associatedtype ManagedObject: RealmSwift.Object
init(managedObject: ManagedObject)
func managedObject() -> ManagedObject
}
4) Make your struct persistable
extension Character: Persistable {
public init(managedObject: CharacterObject) {
identifier = managedObject.identifier
name = managedObject.name
realName = managedObject.realName
}
public func managedObject() -> CharacterObject {
let character = CharacterObject()
character.identifier = identifier
character.name = name
character.realName = realName
return character
}
}
With these tools in place, we are ready to implement the insertion methods of our persistence layer.
5) Exemple to write datas
public final class WriteTransaction {
private let realm: Realm
internal init(realm: Realm) {
self.realm = realm
}
public func add<T: Persistable>(_ value: T, update: Bool) {
realm.add(value.managedObject(), update: update)
}
}
// Implement the Container
public final class Container {
private let realm: Realm
public convenience init() throws {
try self.init(realm: Realm())
}
internal init(realm: Realm) {
self.realm = realm
}
public func write(_ block: (WriteTransaction) throws -> Void)
throws {
let transaction = WriteTransaction(realm: realm)
try realm.write {
try block(transaction)
}
}
}
5) Use the magic!
let character = Character(
identifier: 1000,
name: "Spiderman",
realName: "Peter Parker"
)
let container = try! Container()
try! container.write { transaction in
transaction.add(character)
}
Amazing source : Using Realm with Value Types & My Article
To save a struct in Realm, means copying the data into a Realm Object. The reason why Realm Objects are classes and not structs is because they are not inert values, but auto-updating objects that represent the persisted data in Realm. This has practical benefits, such as the fact that a Realm Object's data is lazy loaded.
You can take advantage of Realm's approach by responding to the change notifications from a Realm instance. For example if your UITableView data source is based off an array property on a Realm Object, as long as you have an instance of that object, you are guaranteed that after the notification it represents the correct values. Used properly this can simplify your code versus having multiple copies of values as structs.
Swift 4 shortest answer
Save structs as Data in Realm
struct MyStruct : Codable { // Variables here }
class MyRealObject : Object {
#objc private dynamic var structData:Data? = nil
var myStruct : MyStruct? {
get {
if let data = structData {
return try? JSONDecoder().decode(MyStruct.self, from: data)
}
return nil
}
set {
structData = try? JSONEncoder().encode(newValue)
}
}
}
Use the magic
let realm = try! Realm()
try! realm.write {
let myReal = MyRealObject()
myReal.myStruct = MyStruct(....)
realm.add(myReal)
}
You can do what suggests Ludovic, or you can automate that process and get rid of that boilerplate code for each of your structs by using Unrealm.