how to make realm CRUD operation in separate swift file? - ios

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

Related

Network Extension didn't called

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)
}
})

'Data' is ambiguous for type lookup in this context

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
}
}

How to save normal array into realm in background thread swift

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)
}

Realm is not updating existent objects

I'm using the Realm to save some objects and the user has the ability to reload them through a network function.
class Task: Object {
dynamic var id: String = ""
dynamic var price: Double = 0
override class func primaryKey() -> String {
return "id"
}
func fillNew(json: JSON) {
self.id = json["id"].stringValue
self.name = json["prive"].doubleValue
}
}
The network function get an array of json to build these objects. Once these new objects are created, I pass them through an add or update but when I fetch them again the fields have not changed.
func getPendingTasks(completion: (JSON?, NSError?) -> ()) {
let urlString = ".."
Alamofire.request(.GET, urlString).responseJSON { response in
let json = response.result.value != nil ? JSON(response.result.value!) : nil
dispatch_async(dispatch_get_main_queue()) {
completion(json, response.result.error)
}
}
}
func getTasks() {
self.getPendingTasks() { json, error in
if let error = error {
return
}
let jsonTasks = json!["tasks"].arrayValue
for jsonTask in jsonTasks {
let task = Task()
task.fillNew(jsonTask)
self.tasks.append(task);
}
self.tasks = Task.sortGenericTasks(self.tasks)
dispatch_async(dispatch_get_main_queue()) {
let realm = try! Realm()
try! realm.write {
realm.add(self.tasks, update: true)
}
}
}
}
I'm currently using the latest version of realm, but can not pinpoint exactly what is happening or if I'm doing something wrong.

recall func after auth in uiwebview

In my current project i have CAS auth. I made it in modal uiwebview. My func look like:
func models(callback: ([ModelType])->()) {
UIApplication.sharedApplication().networkActivityIndicatorVisible = true
MLMPProvider.request(MLMP.Models, completion: {
(data, status, response, error) -> () in
func checkResponse(response:NSURLResponse){
if toString(response.URL!).rangeOfString("login") != nil{
NSNotificationCenter.defaultCenter().postNotificationName("LOGIN", object: response.URL!)
}
}
checkResponse(response!)
var result: [ModelType] = []
if let data = data, let models = JSON(data: data, options: NSJSONReadingOptions.allZeros, error: nil).array {
for model in models {
if let modelId = model["uuid"].string {
result += [.Custom(modelId)]
}
}
}
callback(result)
UIApplication.sharedApplication().networkActivityIndicatorVisible = false
})
}
My question is: What is the best way to recall models with callback after auth? Maybe i can do some magic with selectors?
I solve my problem
i add vars to my network layer
var closure:Any!
var methodName:String!
and add notification callback when i get token from server
#objc private func authTokenGetted(notification: NSNotification){
recallMethodWith(methodName, andcallback: closure)
}
func recallMethodWith(name:String, andcallback: Any){
switch name{
case MethodsName.models:
models(andcallback as! ([ModelType])->())
case MethodsName.model:
println("modelcall")
default:
println(name)
}
}
init(){
NSNotificationCenter.defaultCenter().addObserver(self, selector: "authTokenGetted:", name: "AuthDone", object: nil)
}
and my models func looks like:
func checkResponse(response:NSURLResponse) -> Bool{
if toString(response.URL!).rangeOfString("login") != nil{
NSNotificationCenter.defaultCenter().postNotificationName("LOGIN", object: response.URL!)
return false
}
return true
}
func models(callback: ([ModelType])->()) {
UIApplication.sharedApplication().networkActivityIndicatorVisible = true
self.methodName = __FUNCTION__
self.closure = callback
MLMPProvider.request(MLMP.Models, completion: {
(data, status, response, error) -> () in
if self.checkResponse(response!){
var result: [ModelType] = []
if let data = data, let models = JSON(data: data, options: NSJSONReadingOptions.allZeros, error: nil).array {
for model in models {
if let modelId = model["uuid"].string {
result += [.Custom(modelId)]
}
}
}
callback(result)
UIApplication.sharedApplication().networkActivityIndicatorVisible = false
}
})
}

Resources