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)
}
})
Related
Purpose: To log in to Web3.0 using metamask in iOS, and to save login sessions to maintain repeated logins.
Problem: The current login is successful, but I don't know where to handle the session.
Source:
https://github.com/maurovz/Glaip <- I use this API
Among those who have implemented WalletConnect in the Swift environment, if anyone knows how to get a session, I'm desperately asking for your help.
// This is Button to login Metamask. action call a function 'glaip.loginUser'
WalletButtonView(
title: "MetaMask",
action: {
glaip.loginUser(type: .MetaMask) { result in
switch result {
case .success(let user):
print("walletAddress:\(user.wallet.address)")
`walletInfo`.walletAddress = user.wallet.address
`walletInfo`.setNFTInfo(user.wallet.address)
case .failure(let error):
print(error)
}
}
},
iconImage: Image("MetaMaskIcon")
)
// This is loginUser function.
public func loginUser(type: WalletType, completion: #escaping (Result<User, Error>) -> Void) {
walletLogin(wallet: type, completion: { [weak self] result in
switch result {
case let .success(user):
DispatchQueue.main.async {
self?.userState = .loggedIn(user)
}
self?.currentWallet = type
completion(.success(user))
case let .failure(error):
completion(.failure(error))
}
})
}
public protocol WalletService {
func connect(wallet: WalletType, completion: #escaping (Result<WalletDetails, Error>) -> Void)
func sign(wallet: WalletType, message: String, completion: #escaping (Result<String, Error>) -> Void)
}
public struct WalletDetails {
public let address: String
public let chainId: Int
public init(address: String, chainId: Int) {
self.address = address
self.chainId = chainId
}
}
public final class WalletLinkService: WalletService {
private let title: String
private let description: String
private var walletConnect: WalletConnect!
private var onDidConnect: ((WalletDetails) -> Void)?
public init(title: String, description: String) {
self.title = title
self.description = description
setWalletConnect()
}
public func connect(wallet: WalletType, completion: #escaping (Result<WalletDetails, Error>) -> Void) {
openAppToConnect(wallet: wallet, getDeepLink(wallet: wallet), delay: 1)
// Temp fix to avoid threading issue with async await
let lock = NSLock()
onDidConnect = { walletInfo in
lock.lock()
defer { lock.unlock() }
completion(.success(walletInfo))
}
}
public func disconnect() {
do {
guard let session = walletConnect.session else { return }
try walletConnect.client.disconnect(from: session)
} catch {
print("error disconnecting")
}
}
public func sign(wallet: WalletType, message: String, completion: #escaping (Result<String, Error>) -> Void) {
openAppToConnect(wallet: wallet, getDeepLink(wallet: .MetaMask), delay: 3)
walletConnect.sign(message: message, completion: completion)
}
private func setWalletConnect() {
walletConnect = WalletConnect(delegate: self)
walletConnect.reconnectIfNeeded()
}
private func openAppToConnect(wallet: WalletType, _ url: String, delay: CGFloat = 0) {
DispatchQueue.main.asyncAfter(deadline: .now() + delay) {
if let url = URL(string: self.getDeepLink(wallet: wallet)), UIApplication.shared.canOpenURL(url) {
UIApplication.shared.open(url, options: [:], completionHandler: nil)
}
}
}
private func getDeepLink(wallet: WalletType) -> String {
let connectionUrl = walletConnect.connect(title: title, description: description)
let encodeURL = connectionUrl.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed) ?? ""
let end = encodeURL.replacingOccurrences(of: "=", with: "%3D").replacingOccurrences(of: "&", with: "%26")
return "\(wallet.rawValue)\(end)"
}
}
// MARK: - WalletConnectDelegate
extension WalletLinkService: WalletConnectDelegate {
func didUpdate() {
}
func failedToConnect() {
}
func didConnect() {
guard
let session = walletConnect.session,
let walletInfo = session.walletInfo,
let walletAddress = walletInfo.accounts.first
else { return }
onDidConnect?(WalletDetails(address: walletAddress, chainId: walletInfo.chainId))
}
func didDisconnect() {
}
}
I've been trying multiple ways to run a function in a for in loop and when all have returned to run another function but for some reason it appears the final function is running before one or more of the others have returned a result.
This is my latest attempt: (both functions work it is just the order which is the issue)
var counter: Int = 0
for owner in arrOwnerList {
self.removeDevice(device: self.device, account: owner as! String) { (isSuccess) -> Void in
print(isSuccess)
if isSuccess {
}
}
}
if self.arrOwnerList.count == self.counter {
self.removeDeviceFromServer(device: self.device)
self.sendEmail(to:"gordon#myemail.co.uk", subject:self.device+" has been removed", text:self.device+" has been removed from the server, please check the sim for bar and termination")
}
}
func removeDevice(device: String, account: String, completion: #escaping (Bool) -> Void) {
let dictHeader : [String:String] = ["username":username,"password":password]
let dictArray = [device]
self.counter += 1
WebHelper.requestPUTAPIRemoveDevice(baseURL+"rootaccount/removedevices/"+account+"?server=MUIR", header: dictHeader, dictArray: dictArray, controllerView: self, success: { (response) in
print(response)
if response.count == 0 {
self.Label1.alpha = 1
print("response count == 0")
DispatchQueue.main.async {
GlobalConstant.showAlertMessage(withOkButtonAndTitle: GlobalConstant.AppName, andMessage: Messages.ServerError, on: self)
}
}
else {
}
}) { (error) in
DispatchQueue.main.async {
GlobalConstant.showAlertMessage(withOkButtonAndTitle: GlobalConstant.AppName, andMessage: error?.localizedDescription ?? Messages.ServerError, on: self)
}
}
let isSuccess = true
self.Label1.alpha = 1
completion(isSuccess)
}
func removeDeviceFromServer(device: String) {
let dictHeader : [String:String] = ["username":username,"password":password]
WebHelper.requestDELETEAPI(baseURL+"defaultdevice/"+device+"?server=MUIR", header: dictHeader, controllerView: self, success: { (response) in
if response.count == 0 {
DispatchQueue.main.async {
GlobalConstant.showAlertMessage(withOkButtonAndTitle: GlobalConstant.AppName, andMessage: Messages.ServerError, on: self)
}
}
else {
if response.count != 0 {
self.Label2.alpha = 1
DispatchQueue.main.async {
self.Label2.alpha = 1
}
}
else{
DispatchQueue.main.async {
GlobalConstant.showAlertMessage(withOkButtonAndTitle: GlobalConstant.AppName, andMessage: Messages.NoDataFound, on: self)
}
}
}
}) { (error) in
DispatchQueue.main.async {
GlobalConstant.showAlertMessage(withOkButtonAndTitle: GlobalConstant.AppName, andMessage: error?.localizedDescription ?? Messages.ServerError, on: self)
}
}
}
DispatchGroup will solve the problems. (https://developer.apple.com/documentation/dispatch/dispatchgroup)
let dispatchGroup = DispatchGroup()
for owner in arrOwnerList {
dispatchGroup.enter()
self.removeDevice(device: self.device, account: owner as! String) { (isSuccess) -> Void in
defer {
dispatchGroup.leave()
}
print(isSuccess)
if isSuccess {
}
}
}
// This block execute when the loop is completed.
dispatchGroup.notify(queue: .main) { [weak self] in
guard let self = self else { return }
self.removeDeviceFromServer(device: self.device)
self.sendEmail(to:"gordon#myemail.co.uk", subject:self.device+" has been removed", text:self.device+" has been removed from the server, please check the sim for bar and termination")
}
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 {}
in watchOS, i am just trying to get HR value in real-time while a workoutSession is running.
func startHeartRateQuery(updateHandler: #escaping ([HKQuantitySample]?) -> Void) {
guard let quantityType = HKObjectType.quantityType(forIdentifier: .heartRate) else {
return
}
let heartRateQuery = HKAnchoredObjectQuery(type: quantityType, predicate: nil, anchor: anchor, limit: Int(HKObjectQueryNoLimit)) { (query, sampleObjects, deletedObjects, newAnchor, error) -> Void in
guard let newAnchor = newAnchor else {return}
self.anchor = newAnchor
updateHandler(sampleObjects as? [HKQuantitySample])
}
heartRateQuery.updateHandler = {(query, samples, deleteObjects, newAnchor, error) -> Void in
self.anchor = newAnchor!
updateHandler(samples as? [HKQuantitySample])
}
healthStore.execute(heartRateQuery)
activeDataQueries.append(heartRateQuery)
}
And This is how i do start workoutSession.
func session(_ session: WCSession, didReceiveUserInfo userInfo: [String : Any] = [:]) {
guard let d = delegate else {
return
}
if let u = userInfo["status"] as? String {
d.receivedWorkoutRunningStatus(u)
}
if let c = userInfo["clock"] as? String {
d.receiveWorkoutRestPeriodClock(c)
}
}
You can find receivedWorkoutRunningStatus function on InterfaceController.
This is InterfaceController.swift which is first screen on Watch App.
class InterfaceController: WKInterfaceController, HKWorkoutSessionDelegate {
#IBOutlet var lblHeartRate: WKInterfaceLabel!
#IBOutlet var lblSplitIntervalNumber: WKInterfaceLabel!
#IBOutlet var lblRestPeriodClock: WKInterfaceLabel!
private let healthStoreManager = WatchHealthKitManager()
private let parentConnector = ParentConnector()
private var workoutSession: HKWorkoutSession!
override func awake(withContext context: Any?) {
super.awake(withContext: context)
// Create a workout session with the workout configuration
do {
let workoutConfiguration = HKWorkoutConfiguration()
workoutConfiguration.locationType = .indoor
workoutConfiguration.activityType = .rowing
workoutSession = try HKWorkoutSession(configuration: workoutConfiguration)
} catch {
fatalError(error.localizedDescription)
}
//// initial setup
parentConnector.activate()
parentConnector.delegate = self
workoutSession.delegate = self
}
// MARK: - Data Accumulation
private func startAccumulatingData() {
healthStoreManager.startHeartRateQuery() { quantitySamples in
DispatchQueue.main.async {
guard !self.isPaused() else {
return
}
guard let heartRateSamples = quantitySamples else {
return
}
let hrUnit = HKUnit(from: "count/min")
guard let sample = heartRateSamples.first else {
return
}
let value = sample.quantity.doubleValue(for: hrUnit)
self.updateHeartRate(value: value)
self.parentConnector.transfer(value: value)
}
}
}
func workoutSession(_ workoutSession: HKWorkoutSession,
didChangeTo toState: HKWorkoutSessionState,
from fromState: HKWorkoutSessionState,
date: Date) {
switch (toState) {
case .running:
startAccumulatingData()
case .ended:
stopAccumulatingData()
default:
print("Error")
}
}
func receivedWorkoutRunningStatus(_ status: String) {
if (status == "Start") {
healthStoreManager.start(workoutSession)
} else if (status == "Finish") {
healthStoreManager.end(workoutSession)
lblHeartRate.setText("No Active Workout")
}
DispatchQueue.main.async {
self.lblSplitIntervalNumber.setText(status)
}
}
On the iPhone App, I send a "Start" string using transferUserInfo function to trigger the workoutSession beginning. This is not working properly, it only sometimes works, it is very inconsistent.
I would appreciate if you have any advice or alternative approaches.
Thank you in advance.
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
}
})
}