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