Core Data App Shuts down at launch "Exited Voluntarily" - swift

I started a new project in SwiftUI and CoreData. As soon as I added Transformable to on of the child record in my parent /child data model, the App launches and immediately shuts down. When I try to use Previews, I now get Error Domain=FBProcessExit Code=1 "The process exited voluntarily." UserInfo={BSErrorCodeDescription=voluntary, NSLocalizedFailureReason=The process exited voluntarily.} on all views in the app. Can anyone help me figure out how to debug this?
I am including the model code and the transformer in case this helps:
import Foundation
import CoreData
import UIKit
extension Event {
#nonobjc public class func fetchRequest() -> NSFetchRequest<Event> {
return NSFetchRequest<Event>(entityName: "Event")
}
#NSManaged public var cardFrontImage: UIImage?
#NSManaged public var event: String?
#NSManaged public var recipient: Recipient?
}
extension Event : Identifiable {
}
Tranformer code:
import Foundation
import UIKit
class ImageTransformer: ValueTransformer {
override class func transformedValueClass() -> AnyClass {
return NSData.self
}
override class func allowsReverseTransformation() -> Bool {
return true
}
override func reverseTransformedValue(_ value: Any?) -> Any? {
autoreleasepool{
guard let data = value as? Data else {
return nil
}
return UIImage(data: data)
}
}
override func transformedValue(_ value: Any?) -> Any? {
autoreleasepool{
guard let image = value as? UIImage else {
return nil
}
return image.jpegData(compressionQuality: 1.0)
}
}
}
And Where I set the Transformer in the #main
#main
struct HolidayCardApp: App {
#Environment(\.scenePhase) private var scenePhase
let context = PersistentCloudKitContainer.persistentContainer.viewContext
static let doInitialize: Void = {
ValueTransformer.setValueTransformer(ImageTransformer(), forName: NSValueTransformerName("ImageTransformer"))
// register transformers here
}()
static func main() {
HolidayCardApp.doInitialize
}
var body: some Scene {
// CODE
}
}

Solved the issue, by moving the Initialization of the ValueTransformer into my PersistentCloudKit Setup.

Related

Swift - How can cardParams be obtained from STPPaymentOptionsViewController

I want to complete the addition of new payment method that I implemented in this SO question. This save and reuse stripe tutorial shows that cardParams and billingDetails need to be passed to STPPaymentMethodParams. How to I get the cardParams and billingDetails from STPPaymentOptionsViewController?
This is the code that produce the STPPaymentOptionsViewController and then display the STPAddCardViewController when add card button is pressed.
import Foundation
import SwiftUI
import Stripe
struct PaymentOptionsView: UIViewControllerRepresentable {
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
class Coordinator: NSObject, STPPaymentOptionsViewControllerDelegate {
var control: PaymentOptionsView
init(_ control: PaymentOptionsView) {
self.control = control
}
// Implement required delegate methods here:
func paymentOptionsViewControllerDidCancel(_ paymentOptionsViewController: STPPaymentOptionsViewController) {
}
func paymentOptionsViewControllerDidFinish(_ paymentOptionsViewController: STPPaymentOptionsViewController) {
}
func paymentOptionsViewController(_ paymentOptionsViewController: STPPaymentOptionsViewController, didFailToLoadWithError error: Error) {
}
}
func makeUIViewController(context: UIViewControllerRepresentableContext<PaymentOptionsView>) -> STPPaymentOptionsViewController {
let config = STPPaymentConfiguration()
// config.requiredBillingAddressFields = .none
config.appleMerchantIdentifier = "dummy-merchant-id"
return STPPaymentOptionsViewController(configuration: config, theme: STPTheme(), apiAdapter: STPCustomerContext(keyProvider: MyAPIClient()), delegate: context.coordinator)
}
func updateUIViewController(_ uiViewController: STPPaymentOptionsViewController, context: UIViewControllerRepresentableContext<PaymentOptionsView>) { }
}
STPPaymentOptionsViewController by design only returns you a PaymentMethod and not the raw card details.
Since the STPPaymentOptionsViewControllerDelegate returns you a STPPaymentOption, you can cast it to a STPPaymentMethod and then get the billingDetails for that PaymentMethod.
In your didSelect() delegate method, you can cast it like:
if let paymentMethod = paymentOption as? STPPaymentMethod {
print(paymentMethod.stripeId)
}
https://stripe.dev/stripe-ios/docs/Classes/STPPaymentMethod.html#/c:#M#Stripe#objc(cs)STPPaymentMethod(py)billingDetails

Passing Data from Delegate Swift Class to EnvironmentObject in SwiftUI Struct

How do I pass incoming data from a method triggered by a delegate in a Swift class to an EnvironmentObject?
I am aware that for this to work my Swift class needs to be called/initialized from the SwiftUI struct (be a child of the parent SwiftUI struct). However, I initialise my Swift class in the ExtensionDelegate of the Apple Watch app. I would like to see the UI Text element change when the name is updated.
The following code runs on the Apple Watch:
class User: ObservableObject {
#Published var id: UUID?
#Published var name: String?
}
//SwiftUI struct
struct UI: View {
#EnvironmentObject var userEnv: User
var body: some View {
Text(userEnv.name)
}
}
// Swift class
class WatchConnectivityProvider: NSObject, WCSessionDelegate {
static let shared = WatchConnectivityProvider()
private let session: WCSession
init(session: WCSession = .default) {
self.session = session
super.init()
}
func activateSession() {
if WCSession.isSupported() {
session.delegate = self
session.activate()
}
}
//This func gets triggered when data is sent from the iPhone
func session(_ session: WCSession, didReceiveMessage message: [String : Any], replyHandler: #escaping ([String : Any]) -> Void) {
let list = message["list"]
let jsonDecoder = JSONDecoder()
if let data = try? jsonDecoder.decode(User.self, from: list as! Data) {
// !!! This is where I would like to update the EnvironmentObject userEnv !!!
// What is the best way to do this? Remember, this class has already been initialised within the ExtensionDelegate.
}
}
}
//ExtensionDelegate of Apple Watch app, initialising the WatchConnectivityProvider
class ExtensionDelegate: NSObject, WKExtensionDelegate {
func applicationDidFinishLaunching() {
// Perform any final initialization of your application.
WatchConnectivityProvider.shared.activateSession()
}
}
Dependency Injection
One of the solutions could be to store the reference to your #EnvironmentObject globally, eg. in some dependency container.
enum Dependencies {
struct Name: Equatable {
let rawValue: String
static let `default` = Name(rawValue: "__default__")
static func == (lhs: Name, rhs: Name) -> Bool { lhs.rawValue == rhs.rawValue }
}
final class Container {
private var dependencies: [(key: Dependencies.Name, value: Any)] = []
static let `default` = Container()
func register(_ dependency: Any, for key: Dependencies.Name = .default) {
dependencies.append((key: key, value: dependency))
}
func resolve<T>(_ key: Dependencies.Name = .default) -> T {
return (dependencies
.filter { (dependencyTuple) -> Bool in
return dependencyTuple.key == key
&& dependencyTuple.value is T
}
.first)?.value as! T
}
}
}
Then you create your object like this:
Dependencies.Container.default.register(User())
And you can access it from anywhere in your code:
let user: User = Dependencies.Container.default.resolve()
user.modify()
A more detailed explanation of Dependency Injection for Swift can be found here.
Singleton
Alternatively you can use standard Singleton pattern to make your User data available globally. A more detailed explanation can be found here.
Final thoughts
Clean Architecture for SwiftUI is a good example (in my opinion at least) of how to write an iOS app in the clean way. It's a little bit complicated, but you can pick up some parts.
Here bad Jumbo code:
class ExtensionDelegate: ObservableObject, NSObject, WCSessionDelegate, WKExtensionDelegate {
var session: WCSession?
#Published var model = Model
func session(_ session: WCSession, didReceiveMessage message: [String : Any], replyHandler: #escaping ([String : Any]) -> Void) {
print(#function)
var replyValues = Dictionary<String, Any>()
replyValues["status"] = "failed"
// 2442 Bytes
if let data = message["data"] as? Data {
// push that work back to the main thread
DispatchQueue.main.async {
self.model = try? JSONDecoder().decode(Model.self, from: data)
}
if let vm = vm {
replyValues["status"] = "ok"
replyValues["id"] = vm.id.uuidString
}
}
replyHandler(replyValues)
}
...

No storage has been registered for component type

I made a custom component and made sure to call registerComponent() before instantiating an Entity that uses this component.
Even after registering my component before use I am still getting an EXC_BAD_ACCESS and the old
"No storage has been registered for component type 3445452219525568944(ARSDKPrototypePlatform.ManagedARObjectComponent) " error
I have a component and protocol
struct ManagedARObjectComponent: Component, Codable, Equatable {
var isDeployed = false
var id: Int
public static func == (lhs: ManagedARObjectComponent, rhs: ManagedARObjectComponent) -> Bool {
return lhs.id == rhs.id
}
}
protocol HasManagement {
var managedObject: ManagedARObjectComponent { get set }
}
and when I instantiate the ARView I register this component
func makeUIView(context: Context) -> ARView {
let arView = sessionDelegate.arView
// Configure the AR session for horizontal plane tracking.
let arConfiguration = ARWorldTrackingConfiguration()
arConfiguration.planeDetection = .horizontal
arView.session.run(arConfiguration)
ManagedARObjectComponent.registerComponent()
return arView
}
I have a class that conforms to the HasManagement protocol
class AREntity: Entity, HasModel, HasCollision, HasAnchoring, HasManagement {
var managedObject: ManagedARObjectComponent {
get { components[ManagedARObjectComponent] ?? ManagedARObjectComponent(id: -1) }
set { components[ManagedARObjectComponent] = newValue }
}
init(id: Int) {
super.init()
self.managedObject = ManagedARObjectComponent(id: id)
// ... more initialization code
}
}
I tried registering it in the function where I first instantiate the AREntity
#discardableResult
func generateBox() -> AREntity {
ManagedARObjectComponent.self.registerComponent()
let entity = AREntity(id: UUID().hashValue)
arView.scene.addAnchor(entity)
return entity
}
I still get the error I still do not see this component when I po the components on the object itself.
With such scant documentation, I can't seem to find what I am doing wrong. Any help would be appreciated.

RxSwift Mapping issue

I am using RxSwift and Moya. I am getting
fatal error: unexpectedly found nil while unwrapping an Optional value
error at line authors.map {... in tableViewController. When I print authors I found out it is nil. And I couldn't find where the problem is. I am using Moya-ObjectMapper/RxSwift library. In the latest release Observable+Objectmapper.swift file is missing so I found it on github and replaced it.
//import libraries
class AuthorsTableViewController: UITableViewController {
var viewModel: AuthorsViewModelType!
var authors: Driver<RequestResult<[Author]>>!
private let disposeBag = DisposeBag()
override func viewDidLoad() {
super.viewDidLoad()
setUpBindings()
}
func setUpBindings() {
tableView.dataSource = nil
tableView.delegate = nil
authors.map { result -> [Author] in ---> Here
...
}.disposed(by: disposeBag)
}
}
My ViewModel:
//import libraries
public protocol AuthorsViewModelType {
func getAuthors(destination: YazarAPI) -> Driver<RequestResult<[Author]>>
}
public class AuthorsViewModel: AuthorsViewModelType {
fileprivate var provider = YazarAPI.sharedProviderInstance
public func getAuthors(destination: YazarAPI) -> Driver<RequestResult<[Author]>> {
return provider.request(destination)
.observeOn(MainScheduler.instance)
.filterSuccessfulStatusCodes()
.mapAuthors(destination: destination)
.map { .Success($0) }
.asDriver(onErrorRecover: { error in
return .just(.Error(ReqestError(description: error.localizedDescription, code: .requestError)))
})
}
}
private extension Observable where E == Response {
func mapAuthors(destination: YazarAPI) -> Observable<[Author]> {
let authors: Observable<[Author]>
switch destination {
case .authors:
authors = self.mapObject(Authors.self).map { $0.authors ?? [] }
default:
fatalError("Unexpected request type \"\(destination)\"")
}
return authors
}
}
You should assign values to viewModel and authors properties, or if you're assigning them somewhere, move setupBindings() there too.
P.S.: It is not RxSwift question

MVVM with realm: Passing Realm-results across threads?

Using Xcode-8.2.1, Swift-3.0.2, RealmSwift-2.2.0, iOS-Simulator-10:
I try applying the MVVM pattern (explained by Steve Scott here) using Realm.
Everything works until the moment (inside the VIEW-part - see below) where I try to access a viewmodel-property. It says: Realm accessed from incorrect thread
How could I still make the MVVM-pattern do its job of separating model, view-model and view but, on the same time, get thread-safety with realm ?
Is there a way to make Realm-results (i.e. Results<BalancesDataEntry>) being passed across threads ??
Here is my code:
(the issue happens at the very bottom, inside the View-part)
// REALM-OBJECT:
import Foundation
import RealmSwift
class BalancesDataEntry: Object {
dynamic var category: String = ""
dynamic var index: Int = 0
}
MODEL:
import Foundation
import RealmSwift
class MVVMCBalancesModel: BalancesModel
{
fileprivate var entries = [BalancesDataEntry]()
let realm = try! Realm()
init() {
self.createDataEntries()
}
fileprivate func createDataEntries() {
let myBalance = BalancesDataEntry()
myBalance.index = 0
myBalance.category = "Love"
try! self.realm.write {
self.realm.deleteAll()
self.realm.add(myBalance)
}
}
func getEntries(_ completionHandler: #escaping (_ entries: [BalancesDataEntry]) -> Void)
{
// Simulate Aysnchronous data access
DispatchQueue.global().async {
let realmThread = try! Realm()
let returnArray: [BalancesDataEntry] = Array(realmThread.objects(BalancesDataEntry.self))
completionHandler(returnArray)
}
}
}
VIEW-MODEL:
import Foundation
import RealmSwift
class MVVMCBalancesViewModel: BalancesViewModel
{
weak var viewDelegate: BalancesViewModelViewDelegate?
weak var coordinatorDelegate: BalancesViewModelCoordinatorDelegate?
fileprivate var entries: [BalancesDataEntry]? {
didSet {
viewDelegate?.entriesDidChange(viewModel: self)
}
}
var model: BalancesModel? {
didSet {
entries = nil;
model?.getEntries({ (myEntries) in
self.entries = myEntries
})
}
}
var title: String {
return "My Balances"
}
var numberOfEntries: Int {
if let entries = entries {
return entries.count
}
return 0
}
func entryAtIndex(_ index: Int) -> BalancesDataEntry?
{
if let entries = entries , entries.count > index {
return entries[index]
}
return nil
}
func useEntryAtIndex(_ index: Int)
{
if let entries = entries, let coordinatorDelegate = coordinatorDelegate , index < entries.count {
coordinatorDelegate.balancesViewModelDidSelectData(self, data: entries[index])
}
}
}
VIEW:
import UIKit
class MVVMCBalancesViewController: UIViewController {
#IBOutlet weak var label1Outlet: UILabel!
#IBOutlet weak var label2Outlet: UILabel!
var viewModel: BalancesViewModel? {
willSet {
viewModel?.viewDelegate = nil
}
didSet {
viewModel?.viewDelegate = self
refreshDisplay()
}
}
var isLoaded: Bool = false
func refreshDisplay()
{
if let viewModel = viewModel , isLoaded {
// !!!!!!! HERE IS THE ISSUE: Realm accessed from incorrect thread !!!!
self.label1Outlet.text = viewModel.entryAtIndex(0)?.category
self.label2Outlet.text = viewModel.entryAtIndex(1)?.category
} else {
}
}
override func viewDidLoad()
{
super.viewDidLoad()
isLoaded = true
refreshDisplay();
}
}
extension MVVMCBalancesViewController: BalancesViewModelViewDelegate
{
func entriesDidChange(viewModel: BalancesViewModel)
{
}
}
You can use ThreadSafeReference to pass Realm's thread-confined types (Object, Results, List, LinkingObjects) to a different thread. The documentation's section on Passing Instances Across Threads contains this example of passing a single instance of an Object subclass across threads:
let person = Person(name: "Jane")
try! realm.write {
realm.add(person)
}
let personRef = ThreadSafeReference(to: person)
DispatchQueue(label: "background").async {
let realm = try! Realm()
guard let person = realm.resolve(personRef) else {
return // person was deleted
}
try! realm.write {
person.name = "Jane Doe"
}
}
It can be used similarly for Results.
I have found a workaround (see below): Maybe you have better solutions - please let me know!
Here is my github-code realm_mvvm_c on github
After introducing a new protocol and making (pretty much everything) conform to it, things worked out.
Here is the protocol called DataEntry:
import Foundation
protocol DataEntry: class {
var idx: Int { get set }
var category: String { get set }
}
Now, make everything conform to it, such as
--> the realm object (i.e. class BalancesDataEntry: Object, DataEntry {...)
--> the getEntries return value (i.e. func getEntries(_ completionHandler: #escaping (_ entries: [DataEntry]) -> Void))
--> the View-Model's entries (i.e. fileprivate var entries: [DataEntry]? {..)
--> all the corresponding Model- and View-Model protocols also need the DataEntry datatype (see git-repo for complete picture)
After that, it was enough to change the completion-handler return-array of the MODEL's method getEntries(..) to a newly created object-instance (ie. DataEntryDub) that is keept conform to the DataEntry protocol:
func getEntries(_ completionHandler: #escaping (_ entries: [DataEntry]) -> Void)
{
// Simulate Aysnchronous data access
DispatchQueue.global().async {
let realmThread = try! Realm()
class DataEntryDub: DataEntry {
var idx: Int
var category: String
init(idx: Int, category: String) {
self.idx = idx
self.category = category
}
}
var returnArray = [DataEntry]()
for entry in realmThread.objects(BalancesDataEntry.self) {
returnArray.append(DataEntryDub(idx: entry.idx, category: entry.category))
}
completionHandler(returnArray)
}
}
Here is my github-code realm_mvvm_c on github