I have a RealmManager class as bellow:
class RealmManager {
static func save<T: Object>(obj: [T]) {
let personRef = ThreadSafeReference(to: obj)
DispatchQueue.global(qos: .background).async {
autoreleasepool {
let realm = try! Realm()
try! realm.write({
realm.add(obj, update: .modified)
})
}
}
}
}
I'm trying to save a obj array into Realm database but my code do not compile getting this error:
Referencing initializer 'init(to:)' on 'ThreadSafeReference' requires
that '[_]' conform to 'ThreadConfined'
Any help?
Code update:
class RealmManager {
static func save<T: Object>(obj: [T]) {
let realm = try! Realm()
realm.asyncSaveArray(obj: obj)
}
static func get<T: Object>(type: T.Type) -> [T]? {
let realm = try! Realm()
return realm.objects(T.self).toArray(type: T.self)
}
}
extension Results {
func toArray<T>(type: T.Type) -> [T] {
return compactMap { $0 as? T }
}
}
extension Realm {
func asyncWrite<T : ThreadConfined>(obj: T, errorHandler: #escaping ((_ error : Swift.Error) -> Void) = { _ in return }, block: #escaping ((Realm, T?) -> Void)) {
let wrappedObj = ThreadSafeReference(to: obj)
let config = self.configuration
DispatchQueue.global(qos: .background).async {
autoreleasepool {
do {
let realm = try Realm(configuration: config)
let obj = realm.resolve(wrappedObj)
try realm.write {
block(realm, obj)
}
}
catch {
errorHandler(error)
}
}
}
}
func asyncSaveArray<T: Object>(obj: [T]) {
for item in obj {
self.asyncWrite(obj: item) { (realm, itemToSave) in
guard let itemToSave = itemToSave else { return }
realm.add(itemToSave, update: .modified)
}
}
}
}
To pass in an array of objects, you might use a Realm extension:
extension Realm {
func asyncWrite<T : ThreadConfined>(obj: T, errorHandler: #escaping ((_ error : Swift.Error) -> Void) = { _ in return }, block: #escaping ((Realm, T?) -> Void)) {
let wrappedObj = ThreadSafeReference(to: obj)
let config = self.configuration
DispatchQueue.global(qos: .background).async {
autoreleasepool {
do {
let realm = try Realm(configuration: config)
let obj = realm.resolve(wrappedObj)
try realm.write {
block(realm, obj)
}
}
catch {
errorHandler(error)
}
}
}
}
func asyncSaveArray<T: Object>(obj: [T]) {
for item in obj {
self.asyncWrite(obj: item) { (realm, itemToSave) in
guard itemToSave != nil else { return }
realm.add(itemToSave!, update: .modified)
}
}
}
}
There might be two possible use cases.
One is to actually pass an array of Objects:
realm.asyncSaveArray(obj: yourObjectsArray)
The other one is instead of passing in the array of objects, you can use an instance of Results<Object> received somewhere in code previously:
var yourObjects = realm.objects(YourType.self)
self.asyncWrite(obj: yourObjects) { (realm, itemToSave) in
guard itemToSave != nil else { return }
realm.add(itemToSave!, update: .modified)
}
Related
I am trying to connect VPN using OpenVPNAdapter but the PacketTunnelProvider isn't called from the controller. What am i missing here?
Controller.swift
import NetworkExtension
var providerManager: NETunnelProviderManager!
var provider = PacketTunnelProvider()
override func viewDidLoad() {
super.viewDidLoad()
self.loadProviderManager {
self.configureVPN(response: self.arrResponse[0], serverAddress: self.arrResponse[0].iP, username: "vpn", password: "vpn")
}
}
func loadProviderManager(completion:#escaping () -> Void) {
NETunnelProviderManager.loadAllFromPreferences { (managers, error) in
if error == nil {
self.providerManager = managers?.first ?? NETunnelProviderManager()
completion()
}
}
}
func configureVPN(response:Response,serverAddress: String, username: String, password: String) {
let data = Data(base64Encoded:response.openVPNConfigDataBase64, options: .ignoreUnknownCharacters)
print(data!)
let decodedString = String(data: data!, encoding: .utf8)!
print(decodedString)
self.providerManager?.loadFromPreferences { error in
if error == nil {
let tunnelProtocol = NETunnelProviderProtocol()
tunnelProtocol.username = username
tunnelProtocol.serverAddress = serverAddress
tunnelProtocol.providerBundleIdentifier = "***.*****.********.***********.********"
tunnelProtocol.providerConfiguration = ["ovpn": data!, "username": username, "password": password]
tunnelProtocol.disconnectOnSleep = false
self.providerManager.protocolConfiguration = tunnelProtocol
self.providerManager.localizedDescription = "SMVPN"
self.providerManager.isEnabled = true
self.providerManager.saveToPreferences(completionHandler: { (error) in
if error == nil {
self.providerManager.loadFromPreferences(completionHandler: { (error) in
if error == nil {
self.provider.startTunnel(options: nil) { error in //this called here not in network extension
if error != nil {
print(error!)
}else {
}
}
}else {
print(error!.localizedDescription)
}
})
}
})
}
}
}
Project Entitlement
PacketTunnelProvider.swift
import NetworkExtension
import OpenVPNAdapter
class PacketTunnelProvider: NEPacketTunnelProvider {
var startHandler: ((Error?) -> Void)?
var stopHandler: (() -> Void)?
var vpnReachability = OpenVPNReachability()
var configuration: OpenVPNConfiguration!
var properties: OpenVPNConfigurationEvaluation!
var UDPSession: NWUDPSession!
var TCPConnection: NWTCPConnection!
lazy var vpnAdapter: OpenVPNAdapter = {
let adapter = OpenVPNAdapter()
adapter.delegate = self
return adapter
}()
override func startTunnel(options: [String : NSObject]?, completionHandler: #escaping (Error?) -> Void) {
// Add code here to start the process of connecting the tunnel.
guard
let protocolConfiguration = protocolConfiguration as? NETunnelProviderProtocol,
let providerConfiguration = protocolConfiguration.providerConfiguration
else {
fatalError()
}
guard let ovpnFileContent: Data = providerConfiguration["ovpn"] as? Data else { return }
let configuration = OpenVPNConfiguration()
configuration.fileContent = ovpnFileContent
do {
properties = try vpnAdapter.apply(configuration: configuration)
} catch {
completionHandler(error)
return
}
configuration.tunPersist = true
if !properties.autologin {
if let username: String = providerConfiguration["username"] as? String, let password: String = providerConfiguration["password"] as? String {
let credentials = OpenVPNCredentials()
credentials.username = username
credentials.password = password
do {
try vpnAdapter.provide(credentials: credentials)
} catch {
completionHandler(error)
return
}
}
}
vpnReachability.startTracking { [weak self] status in
guard status != .notReachable else { return }
self?.vpnAdapter.reconnect(afterTimeInterval: 5)
}
startHandler = completionHandler
vpnAdapter.connect(using: self)
}
override func stopTunnel(with reason: NEProviderStopReason, completionHandler: #escaping () -> Void) {
// Add code here to start the process of stopping the tunnel.
stopHandler = completionHandler
if vpnReachability.isTracking {
vpnReachability.stopTracking()
}
vpnAdapter.disconnect()
completionHandler()
}
override func handleAppMessage(_ messageData: Data, completionHandler: ((Data?) -> Void)?) {
// Add code here to handle the message.
if let handler = completionHandler {
handler(messageData)
}
}
override func sleep(completionHandler: #escaping () -> Void) {
// Add code here to get ready to sleep.
completionHandler()
}
override func wake() {
// Add code here to wake up.
}
}
extension PacketTunnelProvider: OpenVPNAdapterDelegate {
func openVPNAdapter(_ openVPNAdapter: OpenVPNAdapter, configureTunnelWithNetworkSettings networkSettings: NEPacketTunnelNetworkSettings?, completionHandler: #escaping (Error?) -> Void) {
setTunnelNetworkSettings(networkSettings) { (error) in
completionHandler(error == nil ? self.packetFlow as? Error : nil)
}
}
func openVPNAdapter(_ openVPNAdapter: OpenVPNAdapter, configureTunnelWithNetworkSettings networkSettings: NEPacketTunnelNetworkSettings?, completionHandler: #escaping (OpenVPNAdapterPacketFlow?) -> Void) {
setTunnelNetworkSettings(networkSettings) { (error) in
completionHandler(error == nil ? self.packetFlow : nil)
}
}
func openVPNAdapter(_ openVPNAdapter: OpenVPNAdapter, handleEvent event: OpenVPNAdapterEvent, message: String?) {
switch event {
case .connected:
if reasserting {
reasserting = false
}
guard let startHandler = startHandler else { return }
startHandler(nil)
self.startHandler = nil
case .disconnected:
guard let stopHandler = stopHandler else { return }
if vpnReachability.isTracking {
vpnReachability.stopTracking()
}
stopHandler()
self.stopHandler = nil
case .reconnecting:
reasserting = true
default:
break
}
}
func openVPNAdapter(_ openVPNAdapter: OpenVPNAdapter, handleError error: Error) {
guard let fatal = (error as NSError).userInfo[OpenVPNAdapterErrorFatalKey] as? Bool, fatal == true else {
return
}
NSLog("Error: \(error.localizedDescription)")
NSLog("Connection Info: \(vpnAdapter.connectionInformation.debugDescription)")
if vpnReachability.isTracking {
vpnReachability.stopTracking()
}
if let startHandler = startHandler {
startHandler(error)
self.startHandler = nil
} else {
cancelTunnelWithError(error)
}
}
func openVPNAdapter(_ openVPNAdapter: OpenVPNAdapter, handleLogMessage logMessage: String) {
NSLog("Log: \(logMessage)")
}
}
extension PacketTunnelProvider: OpenVPNAdapterPacketFlow {
func readPackets(completionHandler: #escaping ([Data], [NSNumber]) -> Void) {
packetFlow.readPackets(completionHandler: completionHandler)
}
func writePackets(_ packets: [Data], withProtocols protocols: [NSNumber]) -> Bool {
return packetFlow.writePackets(packets, withProtocols: protocols)
}
}
extension NEPacketTunnelFlow: OpenVPNAdapterPacketFlow {}
Extension Entitlement
self.provider.startTunnel(options: nil) { error in // This is called here, not in network extension
This method is called from controller but didn't get called in network extension.
I posted all my code, so if I missed something, then please let me know. Any help is appreciated.
I found this question but I haven't figured it out yet.
PacketTunnelProvider network extension not called Swift 3
You haven't mentioned anything about how you are testing network extension. You should attach a Network extension process before running your project for debugging. Then only network extension methods will trigger for debug.
Finally i found my mistake it's i tried to called network extension but it will never called. so i called NETunnelProviderManager directly and it will work.
self.providerManager.loadFromPreferences(completionHandler: { (error) in
if error == nil {
do {
try self.providerManager.connection.startVPNTunnel()
} catch let error {
print(error.localizedDescription)
}
}else {
print(error!.localizedDescription)
}
})
I am trying to test Realm Database Service with RealmSwift but I get this error in the mock class in the functions addData and fetchData:
'Data' is ambiguous for type lookup in this context
In the original class, this error does not appear.
MockPersistence:
#testable import AirWatcher
import RealmSwift
final class MockPersistenceService: PersistenceServiceProtocol {
func addStations(stations: [Station]) throws {
<#code#>
}
func fetchStations() throws -> Results<Station> {
<#code#>
}
func addSensors(sensors: [Sensor]) throws {
<#code#>
}
func fetchSensors() throws -> Results<Sensor> {
<#code#>
}
func addAddItem(addItem: AddItem) throws {
<#code#>
}
func addAddItems(addItems: [AddItem]) throws {
<#code#>
}
func deleteAddItem(addItem: AddItem) throws {
<#code#>
}
func fetchAddItems() throws -> Results<AddItem> {
<#code#>
}
func addData(data: [Data]) throws { //the problem appears here
}
func fetchData() throws -> Results<Data> { //the problem appears here
}
func addAirQualityIndex(air: [AirQualityIndex]) throws {
<#code#>
}
func fetchAirQualityIndex() throws -> Results<AirQualityIndex> {
<#code#>
}
}
PersistenceService:
import RealmSwift
enum RuntimeError: Error {
case NoRealmSet
}
protocol PersistenceServiceProtocol {
func addStations(stations: [Station]) throws
func fetchStations() throws -> Results<Station>
func addSensors(sensors: [Sensor]) throws
func fetchSensors() throws -> Results<Sensor>
func addAddItem(addItem: AddItem) throws
func addAddItems(addItems: [AddItem]) throws
func deleteAddItem(addItem: AddItem) throws
func fetchAddItems() throws -> Results<AddItem>
func addData(data: [Data]) throws
func fetchData() throws -> Results<Data>
func addAirQualityIndex(air: [AirQualityIndex]) throws
func fetchAirQualityIndex() throws -> Results<AirQualityIndex>
}
final class PersistenceService: PersistenceServiceProtocol {
var realm: Realm?
func addStations(stations: [Station]) throws {
let objects = stations.map { Station(value: $0)}
autoreleasepool {
do {
realm = try Realm()
try realm!.write {
realm!.add(objects, update: Realm.UpdatePolicy.modified)
}
}
catch RuntimeError.NoRealmSet
{
print("PERSISTENCE SERVICE: No Realm Database addStations")
}
catch let error as NSError
{
print(error.localizedDescription)
}
}
}
func addSensors(sensors: [Sensor]) throws {
let objects = sensors.map { Sensor(value: $0)}
autoreleasepool {
do {
realm = try Realm()
try realm!.write {
realm!.add(objects, update: Realm.UpdatePolicy.modified)
}
}
catch RuntimeError.NoRealmSet
{
print("PERSISTENCE SERVICE: No Realm Database addSensors")
}
catch let error as NSError
{
print(error.localizedDescription)
}
}
}
func addData(data: [Data]) throws {
let objects = data.map { Data(value: $0)}
autoreleasepool {
do {
self.realm = try Realm()
try self.realm!.write {
self.realm!.add(objects, update: Realm.UpdatePolicy.modified)
}
}
catch RuntimeError.NoRealmSet
{
print("PERSISTENCE SERVICE: No Realm Database addData")
}
catch let error as NSError
{
print(error.localizedDescription)
}
}
}
func addAirQualityIndex(air: [AirQualityIndex]) throws {
let objects = air.map { AirQualityIndex(value: $0)}
autoreleasepool {
do {
self.realm = try Realm()
try self.realm!.write {
self.realm!.add(objects, update: Realm.UpdatePolicy.modified)
}
}
catch RuntimeError.NoRealmSet
{
print("PERSISTENCE SERVICE: No Realm Database addAirQualityIndex")
}
catch let error as NSError
{
print(error.localizedDescription)
}
}
}
func addAddItem(addItem: AddItem) throws {
autoreleasepool {
do {
realm = try Realm()
try realm!.write {
realm!.add(addItem, update: Realm.UpdatePolicy.modified)
}
}
catch RuntimeError.NoRealmSet
{
print("PERSISTENCE SERVICE: No Realm Database addAddItem")
}
catch let error as NSError
{
print(error.localizedDescription)
}
}
}
func addAddItems(addItems: [AddItem]) throws {
let objects = addItems.map { AddItem(value: $0)}
autoreleasepool {
do {
self.realm = try Realm()
try self.realm!.write {
self.realm!.add(objects, update: Realm.UpdatePolicy.modified)
}
}
catch RuntimeError.NoRealmSet
{
print("PERSISTENCE SERVICE: No Realm Database addAddItems")
}
catch let error as NSError
{
print(error.localizedDescription)
}
}
}
func deleteAddItem(addItem: AddItem){
let realm = try! Realm()
try! realm.write {
if let itemToDelete = realm.object(ofType: AddItem.self, forPrimaryKey: addItem.id) {
realm.delete(itemToDelete)
}
}
}
func fetchAddItems() throws -> Results<AddItem> {
do {
let realm = try Realm()
return realm.objects(AddItem.self)
}
catch {
throw RuntimeError.NoRealmSet
}
}
func fetchStations() throws -> Results<Station> {
do {
realm = try Realm()
return realm!.objects(Station.self)
}
catch {
throw RuntimeError.NoRealmSet
}
}
func fetchSensors() throws -> Results<Sensor> {
do {
realm = try Realm()
return realm!.objects(Sensor.self)
}
catch {
throw RuntimeError.NoRealmSet
}
}
func fetchData() throws -> Results<Data> {
do {
realm = try Realm()
return realm!.objects(Data.self)
}
catch {
throw RuntimeError.NoRealmSet
}
}
func fetchAirQualityIndex() throws -> Results<AirQualityIndex> {
do {
let realm = try Realm()
return realm.objects(AirQualityIndex.self)
}
catch {
throw RuntimeError.NoRealmSet
}
}
}
Data:
import Foundation.NSURL
import RealmSwift
class Data: Object, Decodable, Identifiable {
#objc dynamic var id: Int = 0
#objc dynamic var stationId: Int = 0
#objc dynamic var key: String = ""
var values = List<Value>()
convenience init(id: Int, stationId: Int, key: String, values: [Value]) {
self.init()
self.id = id
self.stationId = stationId
self.key = key
self.values.append(objectsIn: values)
}
private enum CodingKeys: String, CodingKey {
case key = "key"
case values = "values"
}
override class func primaryKey() -> String? {
return "id"
}
}
class Value: Object, Decodable {
#objc dynamic var date: String? = ""
var value = RealmOptional<Double>()
convenience init(date: String, value: RealmOptional<Double>) {
self.init()
self.date = date
self.value = value
}
}
I am working on a personal project where I will be connecting the user to a VPN. I have followed two blog posts on doing this which are https://medium.com/better-programming/how-to-build-an-openvpn-client-on-ios-c8f927c11e80 & https://kean.blog/post/vpn-configuration-manager. I was able to configure the VPN settings but still, it does not connect. Here are the images for my project Signing & Capabilities one for the App and the other is for the network extension.
my ViewController looks like this. There is a folder in the user app document directory that is named user which gets downloaded from the internet and these are the files that are in the folder. I am not sure if I am supposed to use it other than the user.ovpn file
import UIKit
import Zip
import Alamofire
import NetworkExtension
class ViewController: UIViewController {
var selectedServer: Server?
var providerManager: NETunnelProviderManager!
override func viewDidLoad() {
super.viewDidLoad()
self.loadProviderManager {
self.configureVPN(serverAddress: "openvpn://\(self.selectedServer!.server.address):\(self.selectedServer!.server.port)", username: "\(self.username)", password: "\(self.password)")
}
}
func loadProviderManager(completion:#escaping () -> Void) {
NETunnelProviderManager.loadAllFromPreferences { (managers, error) in
if error == nil {
self.providerManager = managers?.first ?? NETunnelProviderManager()
completion()
}
}
}
func configureVPN(serverAddress: String, username: String, password: String) {
guard let configData = self.readFile(path: "user/user.ovpn") else { return }
self.providerManager?.loadFromPreferences { error in
if error == nil {
let tunnelProtocol = NETunnelProviderProtocol()
tunnelProtocol.username = username
tunnelProtocol.serverAddress = serverAddress
tunnelProtocol.providerBundleIdentifier = "com.example.Networking.tunnel"
tunnelProtocol.providerConfiguration = ["ovpn": configData, "username": username, "password": password]
tunnelProtocol.disconnectOnSleep = false
self.providerManager.protocolConfiguration = tunnelProtocol
self.providerManager.localizedDescription = "OpenVPN"
self.providerManager.isEnabled = true
self.providerManager.saveToPreferences(completionHandler: { (error) in
if error == nil {
self.providerManager.loadFromPreferences(completionHandler: { (error) in
do {
try self.providerManager.connection.startVPNTunnel()
} catch let error {
print(error.localizedDescription)
}
})
}
})
}
}
}
func readFile(path: String) -> Data? {
let fileManager = FileManager.default
do {
let documentDirectory = try fileManager.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
let fileURL = documentDirectory.appendingPathComponent(path)
return try Data(contentsOf: fileURL, options: .uncached)
}
catch let error {
print(error.localizedDescription)
}
return nil
}
}
And my PacketTunnelProvider looks like this
import NetworkExtension
import OpenVPNAdapter
class PacketTunnelProvider: NEPacketTunnelProvider {
var startHandler: ((Error?) -> Void)?
var stopHandler: (() -> Void)?
var vpnReachability = OpenVPNReachability()
var configuration: OpenVPNConfiguration!
var properties: OpenVPNProperties!
var UDPSession: NWUDPSession!
var TCPConnection: NWTCPConnection!
lazy var vpnAdapter: OpenVPNAdapter = {
let adapter = OpenVPNAdapter()
adapter.delegate = self
return adapter
}()
override func startTunnel(options: [String : NSObject]?, completionHandler: #escaping (Error?) -> Void) {
guard
let protocolConfiguration = protocolConfiguration as? NETunnelProviderProtocol,
let providerConfiguration = protocolConfiguration.providerConfiguration
else {
fatalError()
}
guard let ovpnFileContent: Data = providerConfiguration["ovpn"] as? Data else { return }
let configuration = OpenVPNConfiguration()
configuration.fileContent = ovpnFileContent
do {
properties = try vpnAdapter.apply(configuration: configuration)
} catch {
completionHandler(error)
return
}
configuration.tunPersist = true
if !properties.autologin {
if let username: String = providerConfiguration["username"] as? String, let password: String = providerConfiguration["password"] as? String {
let credentials = OpenVPNCredentials()
credentials.username = username
credentials.password = password
do {
try vpnAdapter.provide(credentials: credentials)
} catch {
completionHandler(error)
return
}
}
}
vpnReachability.startTracking { [weak self] status in
guard status != .notReachable else { return }
self?.vpnAdapter.reconnect(afterTimeInterval: 5)
}
startHandler = completionHandler
vpnAdapter.connect()
}
override func stopTunnel(with reason: NEProviderStopReason, completionHandler: #escaping () -> Void) {
stopHandler = completionHandler
if vpnReachability.isTracking {
vpnReachability.stopTracking()
}
vpnAdapter.disconnect()
}
override func handleAppMessage(_ messageData: Data, completionHandler: ((Data?) -> Void)?) {
if let handler = completionHandler {
handler(messageData)
}
}
override func sleep(completionHandler: #escaping () -> Void) {
completionHandler()
}
override func wake() {
}
}
extension PacketTunnelProvider: OpenVPNAdapterDelegate {
func openVPNAdapter(_ openVPNAdapter: OpenVPNAdapter, configureTunnelWithNetworkSettings networkSettings: NEPacketTunnelNetworkSettings?, completionHandler: #escaping (OpenVPNAdapterPacketFlow?) -> Void) {
setTunnelNetworkSettings(networkSettings) { (error) in
completionHandler(error == nil ? self.packetFlow : nil)
}
}
func openVPNAdapter(_ openVPNAdapter: OpenVPNAdapter, handleEvent event: OpenVPNAdapterEvent, message: String?) {
switch event {
case .connected:
if reasserting {
reasserting = false
}
guard let startHandler = startHandler else { return }
startHandler(nil)
self.startHandler = nil
case .disconnected:
guard let stopHandler = stopHandler else { return }
if vpnReachability.isTracking {
vpnReachability.stopTracking()
}
stopHandler()
self.stopHandler = nil
case .reconnecting:
reasserting = true
default:
break
}
}
func openVPNAdapter(_ openVPNAdapter: OpenVPNAdapter, handleError error: Error) {
guard let fatal = (error as NSError).userInfo[OpenVPNAdapterErrorFatalKey] as? Bool, fatal == true else {
return
}
NSLog("Error: \(error.localizedDescription)")
NSLog("Connection Info: \(vpnAdapter.connectionInformation.debugDescription)")
if vpnReachability.isTracking {
vpnReachability.stopTracking()
}
if let startHandler = startHandler {
startHandler(error)
self.startHandler = nil
} else {
cancelTunnelWithError(error)
}
}
func openVPNAdapter(_ openVPNAdapter: OpenVPNAdapter, handleLogMessage logMessage: String) {
NSLog("Log: \(logMessage)")
}
}
extension PacketTunnelProvider: OpenVPNAdapterPacketFlow {
func readPackets(completionHandler: #escaping ([Data], [NSNumber]) -> Void) {
packetFlow.readPackets(completionHandler: completionHandler)
}
func writePackets(_ packets: [Data], withProtocols protocols: [NSNumber]) -> Bool {
return packetFlow.writePackets(packets, withProtocols: protocols)
}
}
extension NEPacketTunnelFlow: OpenVPNAdapterPacketFlow {}
I have update DataBase in background. My data can contain ~2000 items and it take time to update.
func updateData(items: [JSON], _ complete:#escaping() -> Void) {
DispatchQueue.global(qos: .userInitiated).async {
let currentModels = EgrnModel.getAllModels()
var newModels: [EgrnModel] = []
var toDelete: [EgrnModel] = []
for model in currentModels {
let contain = items.contains(where: {$0["id"].intValue == model.id})
if !contain {
toDelete.append(model)
}
}
let realm = try! Realm()
try! realm.write {
for item in items {
if let model = currentModels.first(where: {$0.id == item["id"].intValue}) {
model.update(item)
}
else {
newModels.append(EgrnModel(item))
}
}
realm.delete(toDelete)
realm.add(newModels)
}
DispatchQueue.main.async {
complete()
}
}
}
and I have a function in which I need update data momentarily. When I tap checkmark I have a freeze. (I think it because at this time other data is updating in background)
func checkMark(index: Int) {
let model = models[index]
let realm = try! Realm()
try! realm.write {
model.needToUpdateOnServer = true
model.lastEditUpdate = Date()
model.read = true
}
}
I try next code to fix a freeze. But in this code I have a crash Terminating app due to uncaught exception 'RLMException', reason: 'Realm accessed from incorrect thread.
func checkMark(index: Int) {
let model = models[index]
DispatchQueue.global(qos: .userInitiated).async {
let realm = try! Realm()
try! realm.write {
model.needToUpdateOnServer = true
model.lastEditUpdate = Date()
model.read = true
}
}
}
What you need to is "move" realm objects from one thread to another because realm objects are not thread-safe but Thread Confined. To achieve this you have to use ThreadSafeReference API.
To solve this problem do the following:
Create an extension on the realm class
extension Realm {
func writeAsync<T : ThreadConfined>(obj: T, errorHandler: #escaping ((_ error : Swift.Error) -> Void) = { _ in return }, block: #escaping ((Realm, T?) -> Void)) {
let wrappedObj = ThreadSafeReference(to: obj)
let config = self.configuration
DispatchQueue(label: "background").async {
autoreleasepool {
do {
let realm = try Realm(configuration: config)
let obj = realm.resolve(wrappedObj)
try realm.write {
block(realm, obj)
}
}
catch {
errorHandler(error)
}
}
}
}
}
Use it in your code this way
func checkMark(index: Int) {
let model = models[index]
let realm = try! Realm()
realm.asyncWrite(model) { realm, model in
model.needToUpdateOnServer = true
model.lastEditUpdate = Date()
model.read = true
}
}
HAPPY SWIFTING!
I am a beginner in iOS development and in programming, I am trying to make a swift file that contain realm CRUD operation called RealmService , so I don't have to write do catch block and try! realm all over the place, like my code below
import Foundation
import RealmSwift
class RealmService {
var realm = try! Realm()
func save(_ object: Object) {
do {
try realm.write {
realm.add(object)
}
} catch {
print("Failed to save \(object) in realm: \(error.localizedDescription)")
}
}
func load(_ object: Object) -> Results<Object>? {
return realm.objects(object.self)
}
func update(_ object: Object, with dictionary: [String: Any?]) {
do {
try realm.write {
for (key, value) in dictionary {
object.setValue(value, forKey: key)
}
}
} catch {
print("Failed to update \(object) in realm: \(error.localizedDescription)")
}
}
func delete(_ object: Object) {
do {
try realm.write {
realm.delete(object)
}
} catch {
print("Failed to delete \(object) in realm: \(error.localizedDescription)")
}
}
}
but in the load method, I get an error in the line realm.objects(object.self) , it is said
Cannot convert value of type 'Object' to expected argument type
'Object.Type'
I think I make mistake when insert object.self in the realm.objects. it should be element.type, but I dont have a clue what is this element type like this picture
how to load data from realm? sadly i just know a little bit from youtube tutorial by using realm.objects(object.self)
You are doing the work of select through an object and this is a mistake because it is required of the type of object Element.Type and not the object itself
try to use this
func load<T: Object>(ofType: T.Type) -> Results<T> {
return realm.objects(T.self)
}
call this func
class MyObject: Object {
...
#objc dynamic var name = ""
...
}
.....
ream.objects(MyObject.self)
I use ReamManager with CRUD like this:
import UIKit
import RealmSwift
class RealmManager {
private let NotificationError = Notification.Name.Realm.realmErrorNotification
var realm = try! Realm()
func create<T: Object>(_ object: T) {
do {
try realm.write {
realm.add(object, update: true)
}
} catch {
post(error)
}
}
func create<T: Object>(_ objects: [T]) {
do {
try realm.write {
realm.add(objects, update: true)
}
} catch {
post(error)
}
}
func read<T: Object>(_ object: T.Type) -> Results<T> {
let result = realm.objects(object.self)
return result
}
func update<T: Object>(_ object: T, with dictionary: [String: Any]) {
do {
try realm.write {
for (key, value) in dictionary {
object.setValue(value, forKey: key)
}
}
} catch {
post(error)
}
}
func delete<T: Object>(_ object: T) {
do {
try realm.write {
realm.delete(object)
}
} catch {
post(error)
}
}
func post(_ error: Error) {
NotificationCenter.default.post(name: NotificationError, object: error)
}
func observeRealErrors(in viewController: UIViewController, complation: #escaping (Error?) -> Void) {
NotificationCenter.default.addObserver(forName: NotificationError, object: viewController, queue: nil) { (notification) in
complation(notification.object as? Error)
}
}
func stopObserveRealErrors(in viewController: UIViewController) {
NotificationCenter.default.removeObserver(viewController, name: NotificationError, object: nil)
}
}
maybe that's exactly what you wanted