I am getting json data and parsing it, but for some reason when I change the address to different json and recall the function, (Im changing one eliment in album search), I get the same data in the json. I am thinking it is cached.
Here is my Address:
var SEARCH_RESULTS_ADDRESS = "\(BASE_URL)\(choiceSearch!).search&\(choiceSearch!)=\(albumSearch)&api_key=\(API_KEY)&format=json"
Here is my AddressDataService function.
func getAlbumData(completion: #escaping (_ finished: Bool) -> ()) {
guard let url = URL(string: SEARCH_RESULTS_ADDRESS) else { return }
URLSession.shared.dataTask(with: url) { (data, response, err) in
guard let data = data else { return }
do {
let decoder = JSONDecoder()
let albumDataFull = try decoder.decode(Root.self, from: data)
albumInfo = []
for business in albumDataFull.results.albummatches.album {
let artist = business.artist!
let name = business.name!
let imgUrl = business.image
let albumInd = ["name":name, "artist":artist, "url":url, "imgUrl":imgUrl] as [String : Any]
albumInfo.append(albumInd)
print("Tony: \(albumInfo.count)")
}
completion(true)
}catch let jsonErr {
print("Error seroalizing json", jsonErr)
}
}.resume()
}
Here is my relead data and address change.
func getData(completion: #escaping (_ finished: Bool) -> ()) {
AlbumDataService().getAlbumData(completion: { (complete) in
completion(true)
})
}
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
albumSearch = searchBar.text
albumInfo = []
getData(completion: { (complete) in
DispatchQueue.main.async {
SEARCH_RESULTS_ADDRESS = "\(BASE_URL)\(choiceSearch!).search&\(choiceSearch!)=\(albumSearch!)&api_key=\(API_KEY)&format=json"
print(SEARCH_RESULTS_ADDRESS)
self.tableView.reloadData()
print("Tony Finishedediting \(albumSearch!)")
}
})
}
You need to set SEARCH_RESULTS_ADDRESS before it is used.
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
albumSearch = searchBar.text
albumInfo = []
//###
SEARCH_RESULTS_ADDRESS = "\(BASE_URL)\(choiceSearch!).search&\(choiceSearch!)=\(albumSearch!)&api_key=\(API_KEY)&format=json"
print(SEARCH_RESULTS_ADDRESS)
getData(completion: { (complete) in
DispatchQueue.main.async {
self.tableView.reloadData()
print("Tony Finishedediting \(albumSearch!)")
}
})
}
(Better make it a parameter than declaring a constant-like property.)
(ADDITION)
how I would make the address a param?
Assuming you can modify the methods of AddressDataService:
func getAlbumData(_ urlString: String, completion: #escaping (_ finished: Bool) -> ()) {
print(urlString)
guard let url = URL(string: urlString) else {
print("ursString is invalid")
return
}
//...
}
And you can use it as:
func getData(_ urlString: String, completion: #escaping (_ finished: Bool) -> ()) {
AlbumDataService().getAlbumData(urlString) { complete in
completion(true)
}
}
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
albumSearch = searchBar.text
albumInfo = []
let searchResultsAddress = "\(BASE_URL)\(choiceSearch!).search&\(choiceSearch!)=\(albumSearch!)&api_key=\(API_KEY)&format=json"
getData(searchResultsAddress) { complete in
DispatchQueue.main.async {
self.tableView.reloadData()
print("Tony Finishedediting \(albumSearch!)")
}
}
}
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 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 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 am using the google maps distance matrix api and using a compeletion handler to pass my async function calls like this:
func configureRoute(origin:String,destination:String, completionHandler: #escaping (_ duration:Int) -> ()){
let jsonURL = "https://maps.googleapis.com/maps/api/distancematrix/json?units=imperial&origins=place_id:\(origin)&destinations=place_id:\(destination)&key=MYAPI"
guard let url = URL(string: jsonURL ) else {return}
print(jsonURL)
URLSession.shared.dataTask(with: url) { (data, response, err) in
guard let data = data else {return}
do {
let route = try JSONDecoder().decode(Welcome.self, from: data)
// print(self.durations)
completionHandler(route.rows[0].elements[0].duration.value)
}
catch let jsonErr {
}
let dataAsString = String(data: data, encoding: .utf8)
}.resume()
}
and then I try and use that result like so, however my output results are still blank and I am unable to use any of the calls that I have received. I am not that good with compeltion handlers, so if anyone could let me know what I have done wrong?
func majorConfigure() {
permutations(placeID.count, &placeID, origin: startPlaceID, destination: endPlaceID)
for eachArray in finalRoutes {
for i in 0..<(eachArray.count-1) {
configureRoute(origin: eachArray[i], destination: eachArray[i+1]){
duration in
self.durations.append(duration)
}
}
groupedDurations.append(durations)
durations.removeAll()
}
print(groupedDurations)
}
After using Dispatch group this is my updated code:
func majorConfigure(){
permutations(placeID.count, &placeID, origin: startPlaceID, destination: endPlaceID)
let dispatchGroup = DispatchGroup()
for eachArray in finalRoutes{
for i in 0..<(eachArray.count-1){
dispatchGroup.enter()
configureRoute(origin: eachArray[i], destination: eachArray[i+1]){
duration in
self.durations.append(duration)
print(self.groupedDurations)
dispatchGroup.leave()
}
}
dispatchGroup.notify(queue: .main){
self.groupedDurations.append(self.durations)
self.durations.removeAll()
}
}
}
The result when I print groupedDurations is:
[]
[]
[]
[]
[]
[]
give this a try :
func majorConfigure() {
permutations(placeID.count, &placeID, origin: startPlaceID, destination: endPlaceID)
for eachArray in finalRoutes {
for i in 0..<(eachArray.count-1) {
configureRoute(origin: eachArray[i], destination: eachArray[i+1]){
duration in
self.durations.append(duration)
if i == eachArray.count - 1{
groupedDurations.append(durations)
}
if i == eachArray.count - 1 && eachArray == finalRoutes.last! {
print(groupedDurations)
durations.removeAll()
}
}
}
}
}
EDITED ANSWER
You can achieve this in this manner :
var indexFinalRoutes = 0
var indexEachArray = 0
func loopFinalRoutes(){
self.indexEachArray = 0
let eachArray = finalRoutes[indexFinalRoutes]
loopEachArray(eachArray: eachArray) { (success) in
self.indexFinalRoutes = self.indexFinalRoutes + 1
if self.indexFinalRoutes < self.finalRoutes.count - 1{
self.loopFinalRoutes()
}else{
self.indexFinalRoutes = 0
print(self.groupedDurations)
}
}
}
func loopEachArray(eachArray : [String] ,
completion: #escaping (_ success:Bool) -> Void){
if indexEachArray + 1 < eachArray.count - 1 {
configureRoute(origin: eachArray[indexEachArray], destination: eachArray[indexEachArray+1]){
duration in
self.durations.append(duration)
self.indexEachArray = self.indexEachArray + 1
let nextEachArray = self.finalRoutes[self.indexFinalRoutes]
self.loopEachArray(eachArray: nextEachArray, completion: completion)
}
}else{
groupedDurations.append(durations)
durations.removeAll()
completion(true)
}
}
call loopFinalRoutes functuin from your majorConfigure function and you will have the output.
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
}
})
}