Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

swift 2.3 升级到 swift 3.0 #235

Open
wolfmanwoking opened this issue Jul 16, 2017 · 6 comments
Open

swift 2.3 升级到 swift 3.0 #235

wolfmanwoking opened this issue Jul 16, 2017 · 6 comments

Comments

@wolfmanwoking
Copy link

No description provided.

@wolfmanwoking wolfmanwoking changed the title swift swift 2.3 升级到 swift 3.0 Jul 16, 2017
@wolfmanwoking
Copy link
Author

我已经将 Config 、 PCObserver 、PhoneRTCPlugin、Session、SessionDescriptionDelegate、SJProgressHUD 等 swift 文件升级到 swift 3.0 了。请更新您的文件,以便项目在未来做好升级。

谢谢您的观看。

Config.swift

import Foundation

struct TurnConfig {
var host: String
var username: String
var password: String

// ivan - write - 7/03
init(){
    self.host = "";
    self.username = "";
    self.password = "";
}

// ivan - write - 7/03
init( host:String, username:String, password:String ){
    self.host = host;
    self.username = username;
    self.password = password;
}

}

struct StreamsConfig {
var audio: Bool
var video: Bool

// ivan - write - 7/03
init() {
    self.audio = true;
    self.video = true;
}

// ivan - write - 7/03
init( audio:Bool, video:Bool ){
    self.audio = audio;
    self.video = video;
}

}

class SessionConfig {
var isInitiator: Bool
var turn: TurnConfig
var streams: StreamsConfig

// ivan - write - 7/03
init(isInitiator:Bool, turn:TurnConfig, streams:StreamsConfig ){
    self.isInitiator = isInitiator;
    self.turn = TurnConfig( host: turn.host, username: turn.username, password: turn.password  );
    self.streams = StreamsConfig( audio: streams.audio, video: streams.video );
}

init( isInitiator:Bool, turn:[String: AnyObject], streams:[String: AnyObject] ){
    self.isInitiator = isInitiator;
    
    self.turn = TurnConfig( host: (turn["host"] as? String)!, username: (turn["username"] as? String)!, password: (turn["password"] as? String)!  );
    
    self.streams = StreamsConfig( audio: streams["audio"] as! Bool, video: streams["video"] as! Bool );
    
}

init(_ data: [String: AnyObject] ) {
    self.isInitiator = data["isInitiator"] as! Bool
    
    let turnObject: [String: AnyObject] = data["turn"] as! [String: AnyObject]
    self.turn = TurnConfig(
        host: turnObject["host"] as! String,
        username: turnObject["username"] as! String,
        password: turnObject["password"] as! String
    )

    let streamsObject: [String: AnyObject] = data["streams"] as! [String: AnyObject];
    self.streams = StreamsConfig(
        audio: streamsObject["audio"] as! Bool,
        video: streamsObject["video"] as! Bool
    )
}

}

class VideoConfig {
var container: VideoLayoutParams
var local: VideoLayoutParams?

// ivan - write - 7/03
init( container:VideoLayoutParams, local:VideoLayoutParams? ) {
    
    self.container = VideoLayoutParams( x:container.x, y:container.y, width: container.width, height: container.height );
    
    if local != nil {
        self.local = VideoLayoutParams( x:local!.x, y:local!.y, width: local!.width, height: local!.height );
    }
    
}

// ivan -- write - 7/7
init( data: [String : AnyObject]) {
    let containerParams: [String : AnyObject ] = data["containerParams"] as! [String : AnyObject ]
    let localParams: [String : AnyObject ]? = data["local"] as? [String : AnyObject ]
    
    self.container = VideoLayoutParams( containerParams )
    
    if localParams != nil {
        self.local = VideoLayoutParams( localParams! )
    }
}

// ivan -- write - 7/7
init(_ data: [String : AnyObject]) {
    let containerParams: [String : AnyObject ] = data["containerParams"] as! [String : AnyObject ]
    let localParams: [String : AnyObject ]? = data["local"] as? [String : AnyObject ]
    
    self.container = VideoLayoutParams( containerParams )
    
    if localParams != nil {
        self.local = VideoLayoutParams( localParams! )
    }
}

}

class VideoLayoutParams {
var x, y, width, height: Int

init(x: Int, y: Int, width: Int, height: Int) {
    self.x = x
    self.y = y
    self.width = width
    self.height = height
}

// ivan -- write - 7/7
init(_ data: [String : AnyObject ]) {
    let position: [Int] = data["position"] as! [Int]
    self.x = position[0]
    self.y = position[1]
    
    let size: [Int] = data["size"] as! [Int]
    self.width = size[0]
    self.height = size[1]
}

}


PCObserver.swift

import Foundation

class PCObserver : NSObject, RTCPeerConnectionDelegate {
var session: Session

init(session: Session) {
    self.session = session
}

func peerConnection(_ peerConnection: RTCPeerConnection!,
    addedStream stream: RTCMediaStream!) {
    print("PCO onAddStream.")
        
    DispatchQueue.main.async {
        if stream.videoTracks.count > 0 {
            self.session.addVideoTrack(stream.videoTracks[0] as! RTCVideoTrack)
        }
    }
    
    self.session.sendMessage(
        "{\"type\": \"__answered\"}".data(using: String.Encoding.utf8)!)
}

func peerConnection(_ peerConnection: RTCPeerConnection!,
    removedStream stream: RTCMediaStream!) {
    print("PCO onRemoveStream.")
    
}

func peerConnection(_ peerConnection: RTCPeerConnection!,
    iceGatheringChanged newState: RTCICEGatheringState) {
    print("PCO onIceGatheringChange. \(newState)")
    
}

func peerConnection(_ peerConnection: RTCPeerConnection!,
    iceConnectionChanged newState: RTCICEConnectionState)
{
    print("PCO onIceConnectionChange. \(newState)")
    
}

func peerConnection(_ peerConnection: RTCPeerConnection,
    gotICECandidate candidate: RTCICECandidate ) {
    let getSdpMid : String = candidate.sdpMid as String;
    let getSdpLineIndex : Int = candidate.sdpMLineIndex as Int;
    let getSdp : String = candidate.sdp as String;
    
    print("PCO -- onICECandidate --Mid: \(getSdpMid) \n Index: \(getSdpLineIndex)   \n Sdp:\(getSdp) ")

    let json: [String: AnyObject ] = [
        "type": "candidate" as AnyObject,
        "label": candidate.sdpMLineIndex as AnyObject,
        "id": candidate.sdpMid as AnyObject,
        "candidate": candidate.sdp as AnyObject
    ]
        
    let data: Data?
    do {
        data = try JSONSerialization.data(withJSONObject: json,
                    options: JSONSerialization.WritingOptions())
    } catch let error as NSError {
        print( "error: \(error)" );
        data = nil
    }
        
    self.session.sendMessage(data!)
}

func peerConnection(_ peerConnection: RTCPeerConnection!,
    signalingStateChanged stateChanged: RTCSignalingState) {
    print("PCO onSignalingStateChange: \(stateChanged)")
}

func peerConnection(_ peerConnection: RTCPeerConnection!,
    didOpen dataChannel: RTCDataChannel!) {
    print("PCO didOpenDataChannel.")
}

func peerConnectionOnError(_ peerConnection: RTCPeerConnection!) {
    print("PCO onError.")
}

func peerConnection(onRenegotiationNeeded peerConnection: RTCPeerConnection!) {
    print("PCO onRenegotiationNeeded.")
    // TODO: Handle this
}

}


PhoneRTCPlugin.swift

import Foundation
import AVFoundation
import AudioToolbox

@objc(PhoneRTCPlugin)
class PhoneRTCPlugin : CDVPlugin ,UIAlertViewDelegate{
var sessions: [String: Session]!
var peerConnectionFactory: RTCPeerConnectionFactory!

var videoConfig: VideoConfig?
var videoCapturer: RTCVideoCapturer?
var videoSource: RTCVideoSource?
var localVideoView: RTCEAGLVideoView?
var remoteVideoViews: [VideoTrackViewPair]!
var camera: String?
var scaleView:RTCEAGLVideoView?


var localVideoTrack: RTCVideoTrack?
var localAudioTrack: RTCAudioTrack?

func createSessionObject(_ command: CDVInvokedUrlCommand) {
    
    if let sessionKey = command.argument(at: 0) as? String {
        
        if let args: [String: AnyObject] = command.argument(at: 1) as! [String: AnyObject]? {
            
            let getIsInitiator: Bool = args["isInitiator"] as! Bool;
            let getTurn: [String:AnyObject] = args["turn"] as! [String:AnyObject];
            
            let getArgsStreams: [String:AnyObject] = args["streams"] as! [String:AnyObject];
            
            
            
            
            let config:SessionConfig = SessionConfig(isInitiator: getIsInitiator, turn: getTurn , streams:getArgsStreams );
                
            self.sessions[sessionKey] = Session(plugin: self,
                                                    peerConnectionFactory: peerConnectionFactory,
                                                    config: config,
                                                    callbackId: command.callbackId,
                                                    sessionKey: sessionKey )
            
            
            
            
        }
    }
}


func call(_ command: CDVInvokedUrlCommand) {
    let args: [String:AnyObject] = command.argument(at: 0 ) as! [String:AnyObject];
    if let sessionKey = args["sessionKey"] as? String {

        DispatchQueue.main.async() {
            if let session = self.sessions[sessionKey] {
                session.call()
            }
        }
    }
}

func receiveMessage(_ command: CDVInvokedUrlCommand) {
    let args: [String:AnyObject] = command.argument(at: 0 ) as! [String:AnyObject];
    
    if let sessionKey = args["sessionKey"] as? String {
        if let message = args["message"] as? String {
            if let session = self.sessions[sessionKey] {
                DispatchQueue.global().async() {
                    session.receiveMessage(message)
                }
            }
        }
    }
}

func renegotiate(_ command: CDVInvokedUrlCommand) {
    let args: [String:AnyObject] = command.argument(at: 0) as! [String:AnyObject];
    if let sessionKey = args["sessionKey"] as? String {
        if let config: SessionConfig = args["config"] as? SessionConfig {
            
            DispatchQueue.main.async() {
                if let session = self.sessions[sessionKey] {
                    session.config = config;
                    session.createOrUpdateStream()
                }
            }
        }
    }
}

func disconnect(_ command: CDVInvokedUrlCommand) {
    let args: [String: AnyObject] = command.argument(at: 0 ) as! [String: AnyObject];
    if let sessionKey = args["sessionKey"] as? String {
        
        DispatchQueue.global().async() {
            if (self.sessions[sessionKey] != nil) {
                self.sessions[sessionKey]!.disconnect(true)
            }
        }
    }
}


func sendMessage(_ callbackId: String, message: NSData) {
    let json = (try! JSONSerialization.jsonObject(with: message as Data,
        options: JSONSerialization.ReadingOptions.mutableLeaves)) as! NSDictionary
    let pluginResult = CDVPluginResult(status: CDVCommandStatus_OK, messageAs: json as [NSObject : AnyObject] )
    
    pluginResult?.setKeepCallbackAs(true);
    self.commandDelegate?.send(pluginResult, callbackId:callbackId)
    
}

func setVideoView(_ command: CDVInvokedUrlCommand) {
    let config: [String : AnyObject ] = command.argument(at: 0) as! [String : AnyObject ]
    
    DispatchQueue.main.async() {
        
        let videoConfig:VideoConfig = VideoConfig( data: config )
        if videoConfig.container.width == 0 || videoConfig.container.height == 0 {
            return
        }
        
        self.videoConfig = videoConfig
        self.camera = config["camera"] as? String
        
        if self.videoConfig!.local != nil {
            if self.localVideoTrack == nil {
                if(self.camera == "Front" || self.camera == "Back") {
                    self.initLocalVideoTrack(self.camera!)
                }else {
                    self.initLocalVideoTrack()
                }
            }
            
            if self.videoConfig!.local == nil {
                if self.localVideoView != nil {
                    self.localVideoView!.isHidden = true
                    self.localVideoView!.removeFromSuperview()
                    self.localVideoView = nil
                }
            } else {
                let params = self.videoConfig!.local!
                if self.localVideoView != nil {
                    self.localVideoView!.frame = CGRect( x:CGFloat(params.x + self.videoConfig!.container.x),
                                                         y:CGFloat(params.y + self.videoConfig!.container.y),
                                                         width:CGFloat(params.width),
                                                         height:CGFloat(params.height)
                                            )
                } else {
                    // otherwise, create the local video view
                    self.localVideoView = self.createVideoView(params)
                    self.localVideoTrack!.add(self.localVideoView!)
                }
            }
            
            self.refreshVideoContainer()
            
        }
        let gesture = UIPanGestureRecognizer(target:self, action:#selector(PhoneRTCPlugin.handlePanGesture(sender:)));
        self.localVideoView!.addGestureRecognizer(gesture)
    }
}

func handlePanGesture(sender: UIPanGestureRecognizer){
    let transX = sender.location(in: self.webView).x
    let transY = sender.location(in: self.webView).y
    print(transX,transY)
    sender.view?.center = CGPoint(x: transX, y:transY )
    
}

func hideVideoView(_ command: CDVInvokedUrlCommand) {
    DispatchQueue.main.async() {
        if (self.localVideoView != nil) {
            self.localVideoView!.isHidden = true;
        }
        
        for remoteVideoView in self.remoteVideoViews {
            remoteVideoView.videoView.isHidden = true;
        }
    }
}

func showVideoView(_ command: CDVInvokedUrlCommand) {
    DispatchQueue.main.async() {
        if (self.localVideoView != nil) {
            self.localVideoView!.isHidden = false;
        }
        
        for remoteVideoView in self.remoteVideoViews {
            remoteVideoView.videoView.isHidden = false;
        }
    }
}

func createVideoView(_ params: VideoLayoutParams? = nil) -> RTCEAGLVideoView {
    if params != nil {
        let frame = CGRect(x:
            CGFloat(params!.x + self.videoConfig!.container.x),
                           y:
            CGFloat(params!.y + self.videoConfig!.container.y),
                           width:
            CGFloat(params!.width),
                           height:
            CGFloat(params!.height)
        )
        
        scaleView = RTCEAGLVideoView(frame: frame)
    } else {
        scaleView = RTCEAGLVideoView()
        
        
        let buttonSpeaker = UIButton(frame: CGRect(x:
            CGFloat(self.videoConfig!.container.width-40),
                                                   y:
            CGFloat(self.videoConfig!.container.height-40),
                                                   width:
            CGFloat(30),
                                                   height:
            CGFloat(30)
            ))
        buttonSpeaker.addTarget(self, action: #selector(PhoneRTCPlugin.tapSpeakerBtn(sender:)), for: UIControlEvents.touchUpInside)

        
        DispatchQueue(label:"my_queue").async() {
            DispatchQueue.main.async() {
                try? AVAudioSession.sharedInstance().overrideOutputAudioPort(AVAudioSessionPortOverride.speaker);

            }
        }
    }

    self.webView!.addSubview(scaleView!)
    return scaleView!
}

func tapSpeakerBtn(sender:UIButton){
    if (sender.isSelected == true) {
        
        sender.isSelected=false
        SJProgressHUD.showInfo("扬声器已关闭", autoRemove:true)

        try? AVAudioSession.sharedInstance().overrideOutputAudioPort(.none)
        
    }else{
        
        sender.isSelected=true
        SJProgressHUD.showInfo("扬声器已打开", autoRemove:true)
        try? AVAudioSession.sharedInstance().overrideOutputAudioPort(.speaker)
        
    }
    
}



func initLocalAudioTrack() {
    
    localAudioTrack = peerConnectionFactory.audioTrack(withID: "ARDAMSa0")
    DispatchQueue(label:"my_queue").async() {
        DispatchQueue.main.async() {
            try? AVAudioSession.sharedInstance().overrideOutputAudioPort(AVAudioSessionPortOverride.speaker);
            
            
        }
    }
    
}

func initLocalVideoTrack() {
    var cameraID: String?
    for captureDevice in AVCaptureDevice.devices(withMediaType: AVMediaTypeVideo) {
        if (captureDevice as AnyObject).position == AVCaptureDevicePosition.front {
            cameraID = (captureDevice as AnyObject).localizedName
        }
    }
    
    self.videoCapturer = RTCVideoCapturer(deviceName: cameraID)
    self.videoSource = self.peerConnectionFactory.videoSource(
        with: self.videoCapturer,
        constraints: RTCMediaConstraints()
    )
    
    self.localVideoTrack = self.peerConnectionFactory
        .videoTrack(withID: "ARDAMSv0", source: self.videoSource)
    
}

func initLocalVideoTrack(_ camera: String) {
    NSLog("PhoneRTC: initLocalVideoTrack(camera: String) invoked")
    var cameraID: String?
    for captureDevice in AVCaptureDevice.devices(withMediaType: AVMediaTypeVideo) {
        if (captureDevice as AnyObject).position == AVCaptureDevicePosition.front {
            if camera == "Front"{
                cameraID = (captureDevice as AnyObject).localizedName
            }
        }
        if (captureDevice as AnyObject).position == AVCaptureDevicePosition.back {
            if camera == "Back"{
                cameraID = (captureDevice as AnyObject).localizedName
            }
        }
    }
    
    self.videoCapturer = RTCVideoCapturer(deviceName: cameraID)
    self.videoSource = self.peerConnectionFactory.videoSource(
        with: self.videoCapturer,
        constraints: RTCMediaConstraints()
    )
    
    self.localVideoTrack = self.peerConnectionFactory
        .videoTrack(withID: "ARDAMSv0", source: self.videoSource)
}

func addRemoteVideoTrack(_ videoTrack: RTCVideoTrack) {
    if self.videoConfig == nil {
        return
    }
    let videoView = createVideoView()
    
    videoTrack.add(videoView)
    self.remoteVideoViews.append(VideoTrackViewPair(videoView: videoView, videoTrack: videoTrack))
    
    refreshVideoContainer()
    
    if self.localVideoView != nil {
        self.webView!.addSubview(self.localVideoView!)
    }
    
}

func removeRemoteVideoTrack(_ videoTrack: RTCVideoTrack) {
    DispatchQueue.main.async() {
        
        if (self.localVideoView != nil) {
            self.localVideoView!.isHidden = true
            self.localVideoView!.removeFromSuperview()
            self.localVideoView = nil
        }
        for i in 0 ..< self.remoteVideoViews.count {
            let pair = self.remoteVideoViews[i]
            if pair.videoTrack == videoTrack {
                pair.videoView.isHidden = true
                pair.videoView.removeFromSuperview()
                
                self.remoteVideoViews.remove(at: i)
                self.refreshVideoContainer()
                return
            }
        }
    }
}

func refreshVideoContainer() {
    let n = self.remoteVideoViews.count
    
    if n == 0 {
        return
    }
    
    let rows = n < 9 ? 2 : 3
    let videosInRow = n == 2 ? 2 : Int(ceil(Float(n) / Float(rows)))
    
    let videoSize = Int(Float(self.videoConfig!.container.width) / Float(videosInRow))
    let actualRows = Int(ceil(Float(n) / Float(videosInRow)))
    
    var y = getCenter(actualRows, videoSize: videoSize, containerSize: self.videoConfig!.container.height)
        + self.videoConfig!.container.y
    
    var videoViewIndex = 0
    var row = 0
    
    while row < rows && videoViewIndex < n {
        
        var x = getCenter( row < row - 1 || n % rows == 0 ?
            videosInRow : n - (min(n, videoViewIndex + videosInRow) - 1),
                          videoSize: videoSize,
                          containerSize: self.videoConfig!.container.width)
            + self.videoConfig!.container.x
        
        var video = 0;
        while video < videosInRow && videoViewIndex < n {
            
            let pair = self.remoteVideoViews[videoViewIndex];
            videoViewIndex += 1;
            
            
            pair.videoView.frame = CGRect(x:
                CGFloat(x),
                                          y:
                CGFloat(y),
                                          width:
                CGFloat(videoSize),
                                          height:
                CGFloat(videoSize)
            )
            
            x += Int(videoSize)
            
            video += 1
        }
        
        y += Int(videoSize)

        
        row += 1
        
    }

    
}

func getCenter(_ videoCount: Int, videoSize: Int, containerSize: Int) -> Int {
    return lroundf(Float(containerSize - videoSize * videoCount) / 2.0)
}

func onSessionDisconnect(_ sessionKey: String) {
    self.sessions.removeValue(forKey: sessionKey)
    
    if self.sessions.count == 0 {
        
        DispatchQueue.main.sync() {
            if (self.localVideoView != nil) {
                self.localVideoView!.isHidden = true
                self.localVideoView!.removeFromSuperview()
                self.localVideoView = nil
            }
        }
        
        self.localVideoTrack = nil
        self.localAudioTrack = nil
        
        self.videoSource = nil
        self.videoCapturer = nil
        
    }
}

override func pluginInitialize() {
    self.sessions = [:];
    self.remoteVideoViews = [];
    
    peerConnectionFactory = RTCPeerConnectionFactory()
    RTCPeerConnectionFactory.initializeSSL()
    
}

}

struct VideoTrackViewPair {
var videoView: RTCEAGLVideoView
var videoTrack: RTCVideoTrack
}


Session.swift

import Foundation

class Session {
var plugin: PhoneRTCPlugin
var config: SessionConfig
var constraints: RTCMediaConstraints
var peerConnection: RTCPeerConnection!
var pcObserver: PCObserver!
var queuedRemoteCandidates: [RTCICECandidate]?
var peerConnectionFactory: RTCPeerConnectionFactory
var callbackId: String
var stream: RTCMediaStream?
var videoTrack: RTCVideoTrack?
var sessionKey: String

init(plugin: PhoneRTCPlugin, peerConnectionFactory: RTCPeerConnectionFactory, config: SessionConfig, callbackId: String, sessionKey: String ) {
    
    self.plugin = plugin
    self.queuedRemoteCandidates = []
    self.config = config
    self.peerConnectionFactory = peerConnectionFactory
    self.callbackId = callbackId
    self.sessionKey = sessionKey
        
    self.constraints = RTCMediaConstraints(
        mandatoryConstraints: [
            RTCPair(key: "OfferToReceiveAudio", value: "true"),
            RTCPair(key: "OfferToReceiveVideo", value:
                self.plugin.videoConfig == nil ? "false" : "true"),
        ],
        
        optionalConstraints: [
            RTCPair(key: "internalSctpDataChannels", value: "true"),
            RTCPair(key: "DtlsSrtpKeyAgreement", value: "true")
        ]
    )
}

func call() {
    var iceServers: [RTCICEServer] = []
    iceServers.append(RTCICEServer(
        uri: URL(string: "stun:stun.l.google.com:19302"),
        username: "",
        password: ""))
    
    iceServers.append(RTCICEServer(
        uri: URL(string: self.config.turn.host),
        username: self.config.turn.username,
        password: self.config.turn.password))
    
    self.pcObserver = PCObserver(session: self)
    self.peerConnection =
        peerConnectionFactory.peerConnection(withICEServers: iceServers,
            constraints: self.constraints,
            delegate: self.pcObserver)
    
    createOrUpdateStream()
    
    if self.config.isInitiator {
        self.peerConnection.createOffer(with: SessionDescriptionDelegate(session: self),
            constraints: constraints)
    }
}

func createOrUpdateStream() {
    if self.stream != nil {
        self.peerConnection.remove(self.stream)
        self.stream = nil
    }
    self.stream = peerConnectionFactory.mediaStream(withLabel: "ARDAMS")
    
    if self.config.streams.audio {
        // init local audio track if needed
        if self.plugin.localAudioTrack == nil {
            self.plugin.initLocalAudioTrack()
        }
        
        self.stream!.addAudioTrack(self.plugin.localAudioTrack!)
    }
    
    if self.config.streams.video {
        if self.plugin.localVideoTrack == nil {
            self.plugin.initLocalVideoTrack()
        }
        
        self.stream!.addVideoTrack(self.plugin.localVideoTrack!)
    }
    
    self.peerConnection.add( self.stream )
}

func receiveMessage(_ message: String) {
    var error : NSError?
    let data : [String:AnyObject]?
    do {
        data = try JSONSerialization.jsonObject( with: message.data(using: String.Encoding.utf8)!, options: JSONSerialization.ReadingOptions() ) as? [String:AnyObject];
        
    } catch let error1 as NSError {
        error = error1
        data = nil
    }
    
    print( "data: \(String(describing: data))" );
    
    if let object: [String:AnyObject] = data {
        if let type = object["type"] as? String {
            switch type {
            case "candidate":
                let mid: String = object["id"] as! String
                
                let sdpLineIndex: Int = (object["label"] as? Int)!

                
                let sdp: String = object["candidate"] as! String
                
                let candidate = RTCICECandidate(
                    mid: mid,
                    index: sdpLineIndex,
                    sdp: sdp
                )
                
                if self.queuedRemoteCandidates != nil {
                    self.queuedRemoteCandidates?.append(candidate!)
                } else {
                    self.peerConnection.add(candidate)
                }
                
                case "offer", "answer":
                    if let sdpString = object["sdp"] as? String {
                        let sdp = RTCSessionDescription(type: type, sdp: self.preferISAC(sdpString))
                        self.peerConnection.setRemoteDescriptionWith(SessionDescriptionDelegate(session: self),
                                                                             sessionDescription: sdp)
                    }
                case "bye":
                    self.disconnect(false)
                default:
                    print("Invalid message \(message)")
            }
        }
    } else {
        if let parseError = error {
            print("There was an error parsing the client message: \(parseError.localizedDescription)")
        }
        return
    }
}

func disconnect(_ sendByeMessage: Bool) {
    
    if self.videoTrack != nil {
        self.removeVideoTrack(self.videoTrack!)
    }
    
    if self.peerConnection != nil {
        if sendByeMessage {
            let json: [String : String] = [
                "type": "bye"
            ]
        
            let data = try? JSONSerialization.data(withJSONObject: json,
                options: JSONSerialization.WritingOptions())
        
            self.sendMessage(data!)
        }
    
        self.peerConnection.close()
        self.peerConnection = nil
        self.queuedRemoteCandidates = nil
    }
    
    let json: [String : String] = [
        "type": "__disconnected"
    ]
    
    let data = try? JSONSerialization.data(withJSONObject: json,
        options: JSONSerialization.WritingOptions())
    
    self.sendMessage(data!)
    
    self.plugin.onSessionDisconnect(self.sessionKey)
}

func addVideoTrack(_ videoTrack: RTCVideoTrack) {
    self.videoTrack = videoTrack
    self.plugin.addRemoteVideoTrack(videoTrack)
}

func removeVideoTrack(_ videoTrack: RTCVideoTrack) {
    self.plugin.removeRemoteVideoTrack(videoTrack)
}

func preferISAC(_ sdpDescription: String) -> String {
    
    var mLineIndex = -1
    var isac16kRtpMap: String?
    
    let origSDP = sdpDescription.replacingOccurrences(of: "\r\n", with: "\n")
    var lines = origSDP.components(separatedBy: "\n")
    let isac16kRegex = try? NSRegularExpression(
        pattern: "^a=rtpmap:(\\d+) ISAC/16000[\r]?$",
        options: NSRegularExpression.Options())
    
    var i = 0;
    while  (i < lines.count) && (mLineIndex == -1 || isac16kRtpMap == nil) {
        let line = lines[i]
        if line.hasPrefix("m=audio ") {
            mLineIndex = i
            i += 1
            continue
        }
        isac16kRtpMap = self.firstMatch(isac16kRegex!, string: line)
        i += 1
    }
    
    if mLineIndex == -1 {
        print("No m=audio line, so can't prefer iSAC")
        return origSDP
    }
    
    if isac16kRtpMap == nil {
        print("No ISAC/16000 line, so can't prefer iSAC")
        return origSDP
    }
    
    let origMLineParts = lines[mLineIndex].components(separatedBy: " ")

    var newMLine: [String] = []
    var origPartIndex = 0;
    
    newMLine.append(origMLineParts[origPartIndex])
    origPartIndex+=1;
    
    newMLine.append(origMLineParts[origPartIndex])
    origPartIndex+=1;
    
    newMLine.append(origMLineParts[origPartIndex])
    origPartIndex+=1;
    
    newMLine.append(isac16kRtpMap!)
    
    while origPartIndex < origMLineParts.count {
        if isac16kRtpMap != origMLineParts[origPartIndex] {
            newMLine.append(origMLineParts[origPartIndex])
        }
        origPartIndex += 1
    }
    
    lines[mLineIndex] = newMLine.joined(separator: " ")
    return lines.joined(separator: "\r\n")
}

func firstMatch(_ pattern: NSRegularExpression, string: String) -> String? {
    let nsString = string as NSString
    
    let result = pattern.firstMatch(in: string,
        options: NSRegularExpression.MatchingOptions(),
        range: NSMakeRange(0, nsString.length))
    
    if result == nil {
        return nil
    }
    
    return nsString.substring(with: result!.rangeAt(1))
}

func sendMessage(_ message: Data) {
    self.plugin.sendMessage( self.callbackId, message: message as NSData)
}

}


SessionDescriptionDelegate.swift

import Foundation

class SessionDescriptionDelegate : UIResponder, RTCSessionDescriptionDelegate {
var session: Session

init(session: Session) {
    self.session = session
}

func peerConnection(_ peerConnection: RTCPeerConnection!,
    didCreateSessionDescription originalSdp: RTCSessionDescription!, error: Error!) {
    if error != nil {
        print("SDP OnFailure: \(error)")
        return
    }
        
    let sdp = RTCSessionDescription(
        type: originalSdp.type,
        sdp: self.session.preferISAC(originalSdp.description)
    )
    
    self.session.peerConnection.setLocalDescriptionWith(self, sessionDescription: sdp)
        
    DispatchQueue.main.async {
        let json: [String: String ] = [
            "type": sdp!.type,
            "sdp": sdp!.description
        ]
        
        let data: Data?
        do {
            data = try JSONSerialization.data(withJSONObject: json,
                            options: JSONSerialization.WritingOptions())
        } catch let error as NSError {
            print( "error: \(error)" );
            
            data = nil
        } catch {
            
            fatalError()
        }
        
        self.session.sendMessage(data!)
    }
}

func peerConnection(_ peerConnection: RTCPeerConnection!,
    didSetSessionDescriptionWithError error: Error!) {
    if error != nil {
        print("SDP OnFailure: \(error)")
        return
    }
        
    DispatchQueue.main.async {
        if self.session.config.isInitiator {
            if self.session.peerConnection.remoteDescription != nil {
                print("SDP onSuccess - drain candidates")
                self.drainRemoteCandidates()
            }
        } else {
            if self.session.peerConnection.localDescription != nil {
                self.drainRemoteCandidates()
            } else {
                self.session.peerConnection.createAnswer(with: self,
                    constraints: self.session.constraints)
            }
        }
    }
}

func drainRemoteCandidates() {
    if self.session.queuedRemoteCandidates != nil {
        for candidate in self.session.queuedRemoteCandidates! {
            self.session.peerConnection.add(candidate)
        }
        self.session.queuedRemoteCandidates = nil
    }
}

}


SJProgressHUD.swift

//
// SJProgressHUD.swift
// SJProgressHUD
//
// Created by king on 16/4/10.
// Copyright © 2016年 king. All rights reserved.
//

import UIKit

enum ShowType {
case success
case error
case info
}

extension SJProgressHUD {

/**
 加载动画图片
 
 - parameter images:       图片数组
 - parameter timeInterval: 动画每次执行时间
 */
static func showWaitingWithImages(_ images : Array<UIImage>, timeInterval : TimeInterval = 0) {
    SJProgressHUD.showWaitWithImages(images, timeInterval: timeInterval)
}
/**
 显示菊花
 
 - parameter text:       需要显示的文字,如果不设置文字,则只显示菊花
 - parameter autoRemove: 是否自动移除,默认3秒后自动移除
 */
static func showWaiting(_ text: String = "", autoRemove: Bool = true) {
    SJProgressHUD.showWaitingWithText(text, autoRemove: autoRemove)
}
/**
 状态栏显示
 
 - parameter text:       需要显示的文字
 - parameter color:      背景颜色
 - parameter autoRemove: 是否自动移除,默认3秒后自动移除
 */
static func showStatusBarWithText(_ text: String = "OK", color: UIColor = UIColor(red: 131 / 255.0, green: 178 / 255.0, blue: 158 / 255.0, alpha: 1), autoRemove: Bool = true) {
    SJProgressHUD.showStatusBar(text, color: color, autoRemove: autoRemove)
}
/**
 只显示文字
 
 - parameter text:       需要显示的文字
 - parameter autoRemove: 是否自动移除,默认3秒后自动移除
 */
static func showOnlyText(_ text: String, autoRemove: Bool = true) {
    SJProgressHUD.onlyText(text, autoRemove: autoRemove)
}
/**
 Success样式
 
 - parameter successText: 需要显示的文字,默认为 Success!
 - parameter autoRemove:  是否自动移除,默认3秒后自动移除
 */
static func showSuccess(_ successText: String = "Success!", autoRemove: Bool = true) {
    SJProgressHUD.showText(.success, text: successText, autoRemove: autoRemove)
}
/**
 Error样式
 
 - parameter errorText:  需要显示的文字,默认为 Error!
 - parameter autoRemove: 是否自动移除,默认3秒后自动移除
 */
static func showError(_ errorText: String = "Error!", autoRemove: Bool = true) {
    SJProgressHUD.showText(.error, text: errorText, autoRemove: autoRemove)
}
/**
 Info样式
 
 - parameter infoText:   需要显示的文字,默认为 Info!
 - parameter autoRemove: 是否自动移除,默认3秒后自动移除
 */
static func showInfo(_ infoText: String = "info!", autoRemove: Bool = true) {
    SJProgressHUD.showText(.info, text: infoText, autoRemove: autoRemove)
}
/**
 移除HUD,会移除所有
 */
static func dismiss() {
    SJProgressHUD.clear()
}

}

private let circleSize = CGSize(width: 40, height: 40)
private let windowBgColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.002)
private func bgColor(_ alpha: CGFloat) -> UIColor {
return UIColor(red: 0, green: 0, blue: 0, alpha: alpha)
}

class SJProgressHUD : NSObject {

static var windows = Array<UIWindow!>()
static var angle: Double {
        return [0, 0, 180, 270, 90][UIApplication.shared.statusBarOrientation.hashValue] as Double
}
static fileprivate func showWaitWithImages(_ images : Array<UIImage>, timeInterval : TimeInterval) {
    
    let frame = CGRect(x: 0, y: 0, width: 100, height: 100)
    let imageView = UIImageView()
    imageView.frame = frame
    imageView.contentMode = .scaleAspectFit
    imageView.backgroundColor = bgColor(0.7)
    imageView.layer.cornerRadius = 10
    imageView.animationImages = images
    imageView.animationDuration = timeInterval == 0 ? TimeInterval(images.count) * 0.07 : timeInterval
    imageView.animationRepeatCount = 0
    imageView.startAnimating()
    
    _ = SJProgressHUD.createWindow(frame, view: imageView)

}
static fileprivate func showWaitingWithText(_ text: String, autoRemove: Bool) {
    
    let view = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
    view.backgroundColor = bgColor(0.7)
    view.layer.cornerRadius = 10
    
    let activity = UIActivityIndicatorView(activityIndicatorStyle: .white)
    if !text.isEmpty {
        activity.frame = CGRect(x: 35, y: 25, width: 30, height: 30)
        
        let lable = SJProgressHUD.createLable()
        lable.frame = CGRect(x: 0, y: 55, width: 100, height: 45)
        lable.text = text
        view.addSubview(lable)
    }
    activity.frame = CGRect(x: 30, y: 30, width: 40, height: 40)
    activity.startAnimating()
    view.addSubview(activity)
    
    
    let window = SJProgressHUD.createWindow(view.frame, view: view)

    
    if autoRemove {
        perform(#selector(SJProgressHUD.removeHUD(_:)), with: window, afterDelay: 3)
    }

}
static fileprivate func showStatusBar(_ text: String, color: UIColor, autoRemove: Bool) {
    
    let frame = UIApplication.shared.statusBarFrame
    
    let lable = SJProgressHUD.createLable()
    lable.text = text
    
    let window = SJProgressHUD.createWindow(frame, view: lable)
    window.backgroundColor = color
    window.windowLevel = UIWindowLevelStatusBar
    lable.frame = frame
    window.center = CGPoint(x: frame.width * 0.5, y: frame.height * 0.5)
    lable.center = window.center
    
    if autoRemove {
        perform(#selector(SJProgressHUD.removeHUD(_:)), with: window, afterDelay: 3)
    }
}
static fileprivate func onlyText(_ text: String, autoRemove: Bool) {
    
    let view = UIView()
    view.backgroundColor = bgColor(0.7)
    view.layer.cornerRadius = 10
    
    
    let label = SJProgressHUD.createLable()
    label.text = text
    label.font = UIFont.systemFont(ofSize: 12)
    label.frame = CGRect(x: 5, y: 0, width: 140, height: 60)
    view.addSubview(label)
    
    let frame = CGRect(x: 0, y: 0, width: 150 , height: 60)
    view.frame = frame
    label.center = view.center

    let window = SJProgressHUD.createWindow(frame, view: view)
    
    if autoRemove {
        perform(#selector(SJProgressHUD.removeHUD(_:)), with: window, afterDelay: 2)
    }
}
static fileprivate func showText(_ type: ShowType, text: String, autoRemove: Bool) {
    let frame = CGRect(x: 0, y: 0, width: 100, height: 100)
   
    let view = UIView(frame: frame)
    view.layer.cornerRadius = 10
    view.backgroundColor = bgColor(0.7)
    
    var image = UIImage()
    switch type {
    case .success:
        image = drawImage.imageOfSuccess
    case .error:
        image = drawImage.imageOfError
    case .info:
        image = drawImage.imageOfInfo
    }
    
    let imageView = UIImageView(image: image)
    imageView.frame = CGRect(origin: CGPoint(x: 30, y: 25), size: circleSize)
    imageView.contentMode = .scaleAspectFit
    view.addSubview(imageView)
    
    let lable = SJProgressHUD.createLable()
    lable.frame = CGRect(x: 0, y: 70, width: 100, height: 30)
    lable.text = text
    view.addSubview(lable)
    
    let window = SJProgressHUD.createWindow(frame, view: view)
    
    if autoRemove {
        perform(#selector(SJProgressHUD.removeHUD(_:)), with: window, afterDelay: 3)
    }
    
}

static fileprivate func createLable() -> UILabel {
    let lable = UILabel()
    lable.font = UIFont.systemFont(ofSize: 10)
    lable.backgroundColor = UIColor.clear
    lable.textColor = UIColor.white
    lable.numberOfLines = 0
    lable.textAlignment = .center
    return lable
}
static fileprivate func createWindow(_ frame: CGRect, view: UIView) -> UIWindow {
    
    let window = UIWindow(frame: frame)
    window.backgroundColor = windowBgColor
    window.windowLevel = UIWindowLevelAlert

// window.transform = CGAffineTransform(rotationAngle: CGFloat(angle * M_PI / 180))
window.transform = CGAffineTransform(rotationAngle: CGFloat(angle * .pi / 180))
window.isHidden = false
window.center = getCenter()
window.addSubview(view)
windows.append(window)
return window
}
@objc static fileprivate func removeHUD(_ object: AnyObject) {
if let window = object as? UIWindow {
if let index = windows.index(where: { (item) -> Bool in
return item == window
}) {
windows.remove(at: index)
}
}
}
static fileprivate func clear() {
if windows.isEmpty { return }
self.cancelPreviousPerformRequests(withTarget: self)
windows.removeAll(keepingCapacity: false)
}
static func getCenter() -> CGPoint {
let view = UIApplication.shared.keyWindow?.subviews.first as UIView!
if UIApplication.shared.statusBarOrientation.hashValue >= 3 {
return CGPoint(x: view!.center.y, y: view!.center.x)
} else {
return view!.center
}
}
}

class drawImage {

struct imageCache {
    static var imageOfSuccess: UIImage?
    static var imageOfError: UIImage?
    static var imageOfInfo: UIImage?
}

class func draw(_ type : ShowType) {
    
    let path = UIBezierPath()
    
    path.move(to: CGPoint(x: 40, y: 20))

// path.addArc(withCenter: CGPoint(x: 20, y: 20), radius: 19, startAngle: 0, endAngle: CGFloat(M_PI*2), clockwise: true)
path.addArc(withCenter: CGPoint(x: 20, y: 20), radius: 19, startAngle: 0, endAngle: CGFloat( Double.pi * 2 ), clockwise: true)

    path.close()
    
    switch type {
    case .success:
        path.move(to: CGPoint(x: 15, y: 20))
        path.addLine(to: CGPoint(x: 20, y: 25))
        path.addLine(to: CGPoint(x: 30, y: 15))
        path.move(to: CGPoint(x: 15, y: 20))
        path.close()
    case .error:
        path.move(to: CGPoint(x: 10, y: 10))
        path.addLine(to: CGPoint(x: 30, y: 30))
        path.move(to: CGPoint(x: 10, y: 30))
        path.addLine(to: CGPoint(x: 30, y: 10))
        path.move(to: CGPoint(x: 10, y: 10))
        path.close()
    case .info:
        path.move(to: CGPoint(x: 20, y: 8))
        path.addLine(to: CGPoint(x: 20, y: 28))
        path.move(to: CGPoint(x: 20, y: 8))
        path.close()
        
        let tmpPath = UIBezierPath()
        tmpPath.move(to: CGPoint(x: 20, y: 30))

// tmpPath.addArc(withCenter: CGPoint(x: 20, y: 30), radius: 1, startAngle: 0, endAngle: CGFloat(M_PI2), clockwise: true)
tmpPath.addArc(withCenter: CGPoint(x: 20, y: 30), radius: 1, startAngle: 0, endAngle: CGFloat( Double.pi
2), clockwise: true)

        tmpPath.close()
        UIColor.white.setFill()
        tmpPath.fill()
    }
    UIColor.white.setStroke()
    path.stroke()
}

class var imageOfSuccess: UIImage {
    if imageCache.imageOfSuccess != nil {
        return imageCache.imageOfSuccess!
    }
    UIGraphicsBeginImageContextWithOptions(circleSize, false, 0)
    drawImage.draw(.success)
    imageCache.imageOfSuccess = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()
    return imageCache.imageOfSuccess!
}
class var imageOfError: UIImage {
    if imageCache.imageOfError != nil {
        return imageCache.imageOfError!
    }
    UIGraphicsBeginImageContextWithOptions(circleSize, false, 0)
    drawImage.draw(.error)
    imageCache.imageOfError = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()
    return imageCache.imageOfError!
}
class var imageOfInfo: UIImage {
    if imageCache.imageOfInfo != nil {
        return imageCache.imageOfInfo!
    }
    UIGraphicsBeginImageContextWithOptions(circleSize, false, 0)
    drawImage.draw(.info)
    imageCache.imageOfInfo = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()
    return imageCache.imageOfInfo!
}

}

@ferassaqqa
Copy link

Just update the files with the new Code ? or there other things we have to do ?

@De-Lac
Copy link
Contributor

De-Lac commented Aug 30, 2017

I copied modification of @wolfmanwoking , plus I've added the package.json to be Cordova 7 compliant (to fix the plugin add error) in my fork:
https://github.com/De-Lac/phonertc

It's not perfect, but I think I'm on the right way. The project doesn't compile with ionic cordova build ios, but it successfully compiles in Xcode.

I am currently using it with success.

$ ionic info

cli packages: (/usr/local/lib/node_modules)

    @ionic/cli-utils  : 1.9.2
    ionic (Ionic CLI) : 3.9.2

global packages:

    Cordova CLI : 7.0.1 
    Gulp CLI    : CLI version 3.9.1 Local version 3.9.1

local packages:

    Cordova Platforms : browser 4.1.0 ios 4.4.0
    Ionic Framework   : ionic1 1.3.3

System:

    ios-deploy : 1.9.1 
    ios-sim    : 5.0.8 
    Node       : v8.1.3
    npm        : 5.3.0 
    OS         : macOS Sierra
    Xcode      : Xcode 8.3.3 Build version 8E3004b 

@MrShakes
Copy link

@De-Lac @wolfmanwoking Getting Use of unresolved identifier 'SJProgressHUD' in the PhoneRTCPlugin.swift file and yes SJProgressHUD.swift is in the src/ios folder of the plugin file.

Have at least 5 errors when running ionic cordova build ios:

[ERROR] An error occurred while running cordova build ios (exit code 1):

        
        (truncated) ...  SecureChat -emit-module-path 
        /Users/shakes/Library/Developer/Xcode/DerivedData/SecureChat-awsdbeqrzpuuapgkwxmvennmqvlo/Build/Intermediates/SecureChat.build/Debug-iphonesimulator/SecureChat.build/Objects-normal/x86_64/SessionDescriptionDelegate~partial.swiftmodule 
        -emit-dependencies-path 
        /Users/shakes/Library/Developer/Xcode/DerivedData/SecureChat-awsdbeqrzpuuapgkwxmvennmqvlo/Build/Intermediates/SecureChat.build/Debug-iphonesimulator/SecureChat.build/Objects-normal/x86_64/SessionDescriptionDelegate.d 
        -emit-reference-dependencies-path 
        /Users/shakes/Library/Developer/Xcode/DerivedData/SecureChat-awsdbeqrzpuuapgkwxmvennmqvlo/Build/Intermediates/SecureChat.build/Debug-iphonesimulator/SecureChat.build/Objects-normal/x86_64/SessionDescriptionDelegate.swiftdeps 
        -o 
        /Users/shakes/Library/Developer/Xcode/DerivedData/SecureChat-awsdbeqrzpuuapgkwxmvennmqvlo/Build/Intermediates/SecureChat.build/Debug-iphonesimulator/SecureChat.build/Objects-normal/x86_64/SessionDescriptionDelegate.o
        /Users/shakes/Desktop/Awork/s-c/securechat/platforms/ios/SecureChat/Plugins/com.dooble.phonertc/SessionDescriptionDelegate.swift:3:49: 
        error: use of undeclared type 'RTCSessionDescriptionDelegate'
        class SessionDescriptionDelegate : UIResponder, 
        RTCSessionDescriptionDelegate {
                                                         
        ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        /Users/shakes/Desktop/Awork/s-c/securechat/platforms/ios/SecureChat/Plugins/com.dooble.phonertc/SessionDescriptionDelegate.swift:10:39: 
        error: use of undeclared type 'RTCPeerConnection'
        func peerConnection(_ peerConnection: RTCPeerConnection!,
                                               ^~~~~~~~~~~~~~~~~
        /Users/shakes/Desktop/Awork/s-c/securechat/platforms/ios/SecureChat/Plugins/com.dooble.phonertc/SessionDescriptionDelegate.swift:11:46: 
        error: use of undeclared type 'RTCSessionDescription'
             didCreateSessionDescription originalSdp: RTCSessionDescription!, 
        error: Error!) {
                                                      ^~~~~~~~~~~~~~~~~~~~~
        /Users/shakes/Desktop/Awork/s-c/securechat/platforms/ios/SecureChat/Plugins/com.dooble.phonertc/SessionDescriptionDelegate.swift:47:39: 
        error: use of undeclared type 'RTCPeerConnection'
        func peerConnection(_ peerConnection: RTCPeerConnection!,
                                               ^~~~~~~~~~~~~~~~~
        /Users/shakes/Desktop/Awork/s-c/securechat/platforms/ios/SecureChat/Plugins/com.dooble.phonertc/PCObserver.swift:3:30: 
        error: use of undeclared type 'RTCPeerConnectionDelegate'
        class PCObserver : NSObject, RTCPeerConnectionDelegate {
                                      ^~~~~~~~~~~~~~~~~~~~~~~~~
        /Users/shakes/Desktop/Awork/s-c/securechat/platforms/ios/SecureChat/Plugins/com.dooble.phonertc/SessionDescriptionDelegate.swift:17:15: 
        error: use of unresolved identifier 'RTCSessionDescription'
             let sdp = RTCSessionDescription(
                       ^~~~~~~~~~~~~~~~~~~~~
        /Users/shakes/Desktop/Awork/s-c/securechat/platforms/ios/SecureChat/Plugins/com.dooble.phonertc/Session.swift:7:21: 
        error: use of undeclared type 'RTCPeerConnection'
        var peerConnection: RTCPeerConnection!
                             ^~~~~~~~~~~~~~~~~
        /Users/shakes/Desktop/Awork/s-c/securechat/platforms/ios/SecureChat/Plugins/com.dooble.phonertc/Session.swift:6:18: 
        error: use of undeclared type 'RTCMediaConstraints'
        var constraints: RTCMediaConstraints
                          ^~~~~~~~~~~~~~~~~~~
        /Users/shakes/Desktop/Awork/s-c/securechat/platforms/ios/SecureChat/Plugins/com.dooble.phonertc/Session.swift:9:30: 
        error: use of undeclared type 'RTCICECandidate'
        var queuedRemoteCandidates: [RTCICECandidate]?
                                      ^~~~~~~~~~~~~~~
        
        ** BUILD FAILED **
        
        
        The following build commands failed:
        	CompileSwift normal x86_64 
        /Users/shakes/Desktop/Awork/s-c/securechat/platforms/ios/SecureChat/Plugins/com.dooble.phonertc/PhoneRTCPlugin.swift
        	CompileSwift normal x86_64 
        /Users/shakes/Desktop/Awork/s-c/securechat/platforms/ios/SecureChat/Plugins/com.dooble.phonertc/PCObserver.swift
        	CompileSwift normal x86_64 
        /Users/shakes/Desktop/Awork/s-c/securechat/platforms/ios/SecureChat/Plugins/com.dooble.phonertc/Session.swift
        	CompileSwift normal x86_64 
        /Users/shakes/Desktop/Awork/s-c/securechat/platforms/ios/SecureChat/Plugins/com.dooble.phonertc/SessionDescriptionDelegate.swift
        	CompileSwiftSources normal x86_64 com.apple.xcode.tools.swift.compiler
        (5 failures)
        Error: Error code 65 for command: xcodebuild with args: 
        -xcconfig,/Users/shakes/Desktop/Awork/s-c/securechat/platforms/ios/cordova/build-debug.xcconfig,-workspace,SecureChat.xcworkspace,-scheme,SecureChat,-configuration,Debug,-sdk,iphonesimulator,-destination,platform=iOS 
        Simulator,name=iPhone 
        SE,build,CONFIGURATION_BUILD_DIR=/Users/shakes/Desktop/Awork/s-c/securechat/platforms/ios/build/emulator,SHARED_PRECOMPS_DIR=/Users/shakes/Desktop/Awork/s-c/securechat/platforms/ios/build/sharedpch

System info:

cli packages: (/usr/local/lib/node_modules)

    @ionic/cli-utils  : 1.9.2
    ionic (Ionic CLI) : 3.9.2

global packages:

    Cordova CLI : 7.0.1 

local packages:

    Cordova Platforms : android 6.2.3 ios 4.4.0
    Ionic Framework   : ionic1 1.3.2

System:

    ios-deploy : 1.9.1 
    ios-sim    : 5.0.8 
    Node       : v6.10.2
    npm        : 3.10.10 
    OS         : macOS Sierra
    Xcode      : Xcode 8.3.3 Build version 8E3004b

@De-Lac
Copy link
Contributor

De-Lac commented Sep 13, 2017

add that file manually in the Xcode project.
Open Xcode, open the project, find where are the other files .swift, right-click, add-file and manually paste the code. I don't know why it's missing during the installation

@MrShakes
Copy link

And a few more errors:
screen shot 2017-09-13 at 10 31 31

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants