Extra readme, some formatting, and missing file in Helloworld

This commit is contained in:
QuentinArguillere 2021-09-15 10:54:15 +02:00
parent 98eb474c42
commit d24946be87
17 changed files with 597 additions and 526 deletions

1
.gitignore vendored
View File

@ -32,4 +32,5 @@ ios/swift/*/*.xcodeproj/project.xcworkspace/xcuserdata/
ios/swift/*/*.xcodeproj/xcuserdata/
ios/swift/*/*.xcworkspace/xcuserdata/
ios/swift/*/build
ios/swift/*/*.xcworkspace

12
ios/README.md Normal file
View File

@ -0,0 +1,12 @@
IOS tutorials
====================
Tutorials are written in swift, but the same features can be achieved in objective-C.
Tutorials are numbered 0 to 6, and we recommend you to read them in that order as features from previous tutorials are sometimes used in the next ones, such as account login.
Each tutorial is a full project, so you can generate your Xcode workspace with Cocoapods, build it and deploy it on a real device or an emulator.
Code is being kept as short and simple as possible, and comments explain how and why things are being done.
Full swift API is available [here](http://linphone.org/snapshots/docs/liblinphone/latest/swift).

View File

@ -13,7 +13,7 @@
663D8CE526E8E35500EE487F /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 663D8CE426E8E35500EE487F /* Assets.xcassets */; };
663D8CE826E8E35500EE487F /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 663D8CE726E8E35500EE487F /* Preview Assets.xcassets */; };
663D8CEB26E8E35500EE487F /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 663D8CE926E8E35500EE487F /* LaunchScreen.storyboard */; };
663D8CF326E8E73700EE487F /* HelloworldTutorial.swift in Sources */ = {isa = PBXBuildFile; fileRef = 663D8CF226E8E73700EE487F /* HelloworldTutorial.swift */; };
667C3B4826F1E7B2004D4F14 /* HelloworldTutorial.swift in Sources */ = {isa = PBXBuildFile; fileRef = 667C3B4726F1E7B2004D4F14 /* HelloworldTutorial.swift */; };
AE5DCE54FB07B00CDB39CF8C /* Pods_Helloworld.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 658B93FC56A6311767BE2F24 /* Pods_Helloworld.framework */; };
/* End PBXBuildFile section */
@ -28,7 +28,7 @@
663D8CE726E8E35500EE487F /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = "<group>"; };
663D8CEA26E8E35500EE487F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
663D8CEC26E8E35500EE487F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
663D8CF226E8E73700EE487F /* HelloworldTutorial.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = HelloworldTutorial.swift; path = ../../../../Downloads/HelloworldTutorial.swift; sourceTree = "<group>"; };
667C3B4726F1E7B2004D4F14 /* HelloworldTutorial.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HelloworldTutorial.swift; sourceTree = "<group>"; };
B14B29CDA5B52FB0218CB545 /* Pods-Helloworld.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Helloworld.release.xcconfig"; path = "Target Support Files/Pods-Helloworld/Pods-Helloworld.release.xcconfig"; sourceTree = "<group>"; };
/* End PBXFileReference section */
@ -71,8 +71,8 @@
663D8CE426E8E35500EE487F /* Assets.xcassets */,
663D8CE926E8E35500EE487F /* LaunchScreen.storyboard */,
663D8CEC26E8E35500EE487F /* Info.plist */,
667C3B4726F1E7B2004D4F14 /* HelloworldTutorial.swift */,
663D8CE626E8E35500EE487F /* Preview Content */,
663D8CF226E8E73700EE487F /* HelloworldTutorial.swift */,
);
path = Helloworld;
sourceTree = "<group>";
@ -219,7 +219,7 @@
663D8CDF26E8E35400EE487F /* AppDelegate.swift in Sources */,
663D8CE126E8E35400EE487F /* SceneDelegate.swift in Sources */,
663D8CE326E8E35400EE487F /* ContentView.swift in Sources */,
663D8CF326E8E73700EE487F /* HelloworldTutorial.swift in Sources */,
667C3B4826F1E7B2004D4F14 /* HelloworldTutorial.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};

View File

@ -0,0 +1,58 @@
//
// HellowolrdTutorial.swift
// HellowolrdTutorial
//
// Created by QuentinArguillere on 08/09/2021.
// Copyright © 2021 BelledonneCommunications. All rights reserved.
//
// Check the Podfile to see how to import the LibLinphone SDK !!!
import linphonesw
class HelloworldTutorialContext : ObservableObject
{
var mCore: Core!
@Published var coreVersion: String = Core.getVersion
/*------------ Login tutorial related variables -------*/
var mRegistrationDelegate : CoreDelegate!
@Published var username : String = "user"
@Published var passwd : String = "pwd"
@Published var domain : String = "sip.example.org"
@Published var loggedIn: Bool = false
@Published var transportType : String = "TLS"
init()
{
// Some configuration can be done before the Core is created, for example enable debug logs.
LoggingService.Instance.logLevel = LogLevel.Debug
// Core is the main object of the SDK. You can't do much without it.
// To create a Core, we need the instance of the Factory.
let factory = Factory.Instance
// Your Core can use up to 2 configuration files, but that isn't mandatory.
try! mCore = factory.createCore(configPath: "", factoryConfigPath: "", systemContext: nil)
try! mCore.start()
// Create a Core listener to listen for the callback we need
// In this case, we want to know about the account registration status
mRegistrationDelegate = CoreDelegateStub(onAccountRegistrationStateChanged: { (core: Core, account: Account, state: RegistrationState, message: String) in
// If account has been configured correctly, we will go through Progress and Ok states
// Otherwise, we will be Failed.
NSLog("New registration state is \(state) for user id \( String(describing: account.params?.identityAddress?.asString()))\n")
if (state == .Ok) {
self.loggedIn = true
} else if (state == .Cleared) {
self.loggedIn = false
}
})
mCore.addDelegate(delegate: mRegistrationDelegate)
coreVersion = Core.getVersion
// Now we can start using the Core object
}
}

View File

@ -9,27 +9,27 @@
import SwiftUI
struct ContentView: View {
@ObservedObject var tutorialContext : LoginTutorialContext
var body: some View {
VStack {
Group {
HStack {
Text("Username:")
.font(.title)
var body: some View {
VStack {
Group {
HStack {
Text("Username:")
.font(.title)
TextField("", text : $tutorialContext.username)
.textFieldStyle(RoundedBorderTextFieldStyle())
.textFieldStyle(RoundedBorderTextFieldStyle())
.disabled(tutorialContext.loggedIn)
}
HStack {
Text("Password:")
.font(.title)
TextField("", text : $tutorialContext.passwd)
.textFieldStyle(RoundedBorderTextFieldStyle())
}
HStack {
Text("Password:")
.font(.title)
TextField("", text : $tutorialContext.passwd)
.textFieldStyle(RoundedBorderTextFieldStyle())
.disabled(tutorialContext.loggedIn)
}
}
HStack {
Text("Domain:")
.font(.title)
@ -38,50 +38,50 @@ struct ContentView: View {
.disabled(tutorialContext.loggedIn)
}
Picker(selection: $tutorialContext.transportType, label: Text("Transport:")) {
Text("TLS").tag("TLS")
Text("TCP").tag("TCP")
Text("UDP").tag("UDP")
Text("TLS").tag("TLS")
Text("TCP").tag("TCP")
Text("UDP").tag("UDP")
}.pickerStyle(SegmentedPickerStyle()).padding()
VStack {
HStack {
Button(action: {
if (self.tutorialContext.loggedIn)
{
self.tutorialContext.unregister()
VStack {
HStack {
Button(action: {
if (self.tutorialContext.loggedIn)
{
self.tutorialContext.unregister()
self.tutorialContext.delete()
} else {
self.tutorialContext.login()
}
})
{
Text(tutorialContext.loggedIn ? "Log out & \ndelete account" : "Create & \nlog in account")
.font(.largeTitle)
.foregroundColor(Color.white)
} else {
self.tutorialContext.login()
}
})
{
Text(tutorialContext.loggedIn ? "Log out & \ndelete account" : "Create & \nlog in account")
.font(.largeTitle)
.foregroundColor(Color.white)
.frame(width: 220.0, height: 90)
.background(Color.gray)
}
}
HStack {
Text("Login State : ")
.font(.footnote)
Text(tutorialContext.loggedIn ? "Looged in" : "Unregistered")
.font(.footnote)
.foregroundColor(tutorialContext.loggedIn ? Color.green : Color.black)
}.padding(.top, 10.0)
}
}
Group {
Spacer()
Text("Core Version is \(tutorialContext.coreVersion)")
}
}
.padding()
}
.background(Color.gray)
}
}
HStack {
Text("Login State : ")
.font(.footnote)
Text(tutorialContext.loggedIn ? "Looged in" : "Unregistered")
.font(.footnote)
.foregroundColor(tutorialContext.loggedIn ? Color.green : Color.black)
}.padding(.top, 10.0)
}
}
Group {
Spacer()
Text("Core Version is \(tutorialContext.coreVersion)")
}
}
.padding()
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView(tutorialContext: LoginTutorialContext())
}
static var previews: some View {
ContentView(tutorialContext: LoginTutorialContext())
}
}

View File

@ -10,24 +10,24 @@ import linphonesw
class LoginTutorialContext : ObservableObject
{
var mCore: Core!
@Published var coreVersion: String = Core.getVersion
/*------------ Login tutorial related variables -------*/
var mCore: Core!
@Published var coreVersion: String = Core.getVersion
/*------------ Login tutorial related variables -------*/
var mRegistrationDelegate : CoreDelegate!
@Published var username : String = "user"
@Published var passwd : String = "pwd"
@Published var username : String = "user"
@Published var passwd : String = "pwd"
@Published var domain : String = "sip.example.org"
@Published var loggedIn: Bool = false
@Published var loggedIn: Bool = false
@Published var transportType : String = "TLS"
init()
{
init()
{
LoggingService.Instance.logLevel = LogLevel.Debug
try? mCore = Factory.Instance.createCore(configPath: "", factoryConfigPath: "", systemContext: nil)
try? mCore.start()
try? mCore = Factory.Instance.createCore(configPath: "", factoryConfigPath: "", systemContext: nil)
try? mCore.start()
// Create a Core listener to listen for the callback we need
// In this case, we want to know about the account registration status
@ -43,8 +43,8 @@ class LoginTutorialContext : ObservableObject
}
})
mCore.addDelegate(delegate: mRegistrationDelegate)
}
}
func login() {
do {
@ -64,39 +64,39 @@ class LoginTutorialContext : ObservableObject
// ha1 is set to null as we are using the clear text password. Upon first register, the hash will be computed automatically.
// The realm will be determined automatically from the first register, as well as the algorithm
let authInfo = try Factory.Instance.createAuthInfo(username: username, userid: "", passwd: passwd, ha1: "", realm: "", domain: domain)
// Account object replaces deprecated ProxyConfig object
// Account object is configured through an AccountParams object that we can obtain from the Core
let accountParams = try mCore.createAccountParams()
// A SIP account is identified by an identity address that we can construct from the username and domain
let identity = try Factory.Instance.createAddress(addr: String("sip:" + username + "@" + domain))
try! accountParams.setIdentityaddress(newValue: identity)
// We also need to configure where the proxy server is located
let address = try Factory.Instance.createAddress(addr: String("sip:" + domain))
// We use the Address object to easily set the transport protocol
try address.setTransport(newValue: transport)
try accountParams.setServeraddress(newValue: address)
// And we ensure the account will start the registration process
accountParams.registerEnabled = true
// Now that our AccountParams is configured, we can create the Account object
let account = try mCore.createAccount(params: accountParams)
// Now let's add our objects to the Core
mCore.addAuthInfo(info: authInfo)
try mCore.addAccount(account: account)
// Also set the newly added account as default
mCore.defaultAccount = account
} catch { NSLog(error.localizedDescription) }
}
func unregister()
{
func unregister()
{
// Here we will disable the registration of our Account
if let account = mCore.defaultAccount {
@ -110,19 +110,19 @@ class LoginTutorialContext : ObservableObject
// And apply them
account.params = clonedParams
}
}
}
func delete() {
// To completely remove an Account
if let account = mCore.defaultAccount {
mCore.removeAccount(account: account)
// To remove all accounts use
mCore.clearAccounts()
// Same for auth info
mCore.clearAllAuthInfo()
}
}
}

View File

@ -9,7 +9,7 @@
import SwiftUI
struct ContentView: View {
@ObservedObject var tutorialContext : IncomingCallTutorialContext
func callStateString() -> String {
@ -22,24 +22,24 @@ struct ContentView: View {
}
}
var body: some View {
VStack {
Group {
HStack {
Text("Username:")
.font(.title)
var body: some View {
VStack {
Group {
HStack {
Text("Username:")
.font(.title)
TextField("", text : $tutorialContext.username)
.textFieldStyle(RoundedBorderTextFieldStyle())
.textFieldStyle(RoundedBorderTextFieldStyle())
.disabled(tutorialContext.loggedIn)
}
HStack {
Text("Password:")
.font(.title)
TextField("", text : $tutorialContext.passwd)
.textFieldStyle(RoundedBorderTextFieldStyle())
}
HStack {
Text("Password:")
.font(.title)
TextField("", text : $tutorialContext.passwd)
.textFieldStyle(RoundedBorderTextFieldStyle())
.disabled(tutorialContext.loggedIn)
}
}
HStack {
Text("Domain:")
.font(.title)
@ -48,37 +48,37 @@ struct ContentView: View {
.disabled(tutorialContext.loggedIn)
}
Picker(selection: $tutorialContext.transportType, label: Text("Transport:")) {
Text("TLS").tag("TLS")
Text("TCP").tag("TCP")
Text("UDP").tag("UDP")
Text("TLS").tag("TLS")
Text("TCP").tag("TCP")
Text("UDP").tag("UDP")
}.pickerStyle(SegmentedPickerStyle()).padding()
VStack {
HStack {
Button(action: {
if (self.tutorialContext.loggedIn)
{
self.tutorialContext.unregister()
VStack {
HStack {
Button(action: {
if (self.tutorialContext.loggedIn)
{
self.tutorialContext.unregister()
self.tutorialContext.delete()
} else {
self.tutorialContext.login()
}
})
{
Text(tutorialContext.loggedIn ? "Log out & \ndelete account" : "Create & \nlog in account")
.font(.largeTitle)
.foregroundColor(Color.white)
} else {
self.tutorialContext.login()
}
})
{
Text(tutorialContext.loggedIn ? "Log out & \ndelete account" : "Create & \nlog in account")
.font(.largeTitle)
.foregroundColor(Color.white)
.frame(width: 220.0, height: 90)
.background(Color.gray)
}
}
HStack {
Text("Login State : ")
.font(.footnote)
Text(tutorialContext.loggedIn ? "Looged in" : "Unregistered")
.font(.footnote)
.foregroundColor(tutorialContext.loggedIn ? Color.green : Color.black)
}.padding(.top, 10.0)
}
.background(Color.gray)
}
}
HStack {
Text("Login State : ")
.font(.footnote)
Text(tutorialContext.loggedIn ? "Looged in" : "Unregistered")
.font(.footnote)
.foregroundColor(tutorialContext.loggedIn ? Color.green : Color.black)
}.padding(.top, 10.0)
}
VStack {
HStack {
Button(action: {
@ -90,10 +90,10 @@ struct ContentView: View {
})
{
Text( (tutorialContext.isCallRunning) ? "Terminate" : "Accept")
.font(.largeTitle)
.foregroundColor(Color.white)
.frame(width: 180.0, height: 42.0)
.background(Color.gray)
.font(.largeTitle)
.foregroundColor(Color.white)
.frame(width: 180.0, height: 42.0)
.background(Color.gray)
}
.disabled(!tutorialContext.isCallIncoming && !tutorialContext.isCallRunning)
HStack {
@ -132,18 +132,18 @@ struct ContentView: View {
.disabled(!tutorialContext.isCallRunning)
}.padding(.top, 10)
}.padding(.top, 30)
}
Group {
Spacer()
Text("Core Version is \(tutorialContext.coreVersion)")
}
}
.padding()
}
}
Group {
Spacer()
Text("Core Version is \(tutorialContext.coreVersion)")
}
}
.padding()
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView(tutorialContext: IncomingCallTutorialContext())
}
static var previews: some View {
ContentView(tutorialContext: IncomingCallTutorialContext())
}
}

View File

@ -10,15 +10,15 @@ import linphonesw
class IncomingCallTutorialContext : ObservableObject
{
var mCore: Core!
@Published var coreVersion: String = Core.getVersion
var mAccount: Account?
var mCore: Core!
@Published var coreVersion: String = Core.getVersion
var mAccount: Account?
var mCoreDelegate : CoreDelegate!
@Published var username : String = "user"
@Published var passwd : String = "pwd"
@Published var username : String = "user"
@Published var passwd : String = "pwd"
@Published var domain : String = "sip.example.org"
@Published var loggedIn: Bool = false
@Published var loggedIn: Bool = false
@Published var transportType : String = "TLS"
// Incoming call related variables
@ -29,12 +29,12 @@ class IncomingCallTutorialContext : ObservableObject
@Published var isSpeakerEnabled : Bool = false
@Published var isMicrophoneEnabled : Bool = false
init()
{
init()
{
LoggingService.Instance.logLevel = LogLevel.Debug
try? mCore = Factory.Instance.createCore(configPath: "", factoryConfigPath: "", systemContext: nil)
try? mCore.start()
try? mCore = Factory.Instance.createCore(configPath: "", factoryConfigPath: "", systemContext: nil)
try? mCore.start()
mCoreDelegate = CoreDelegateStub( onCallStateChanged: { (core: Core, call: Call, state: Call.State, message: String) in
self.callMsg = message
@ -64,8 +64,8 @@ class IncomingCallTutorialContext : ObservableObject
}
})
mCore.addDelegate(delegate: mCoreDelegate)
}
}
func login() {
do {
@ -86,19 +86,19 @@ class IncomingCallTutorialContext : ObservableObject
mCore.addAuthInfo(info: authInfo)
try mCore.addAccount(account: mAccount!)
mCore.defaultAccount = mAccount
} catch { NSLog(error.localizedDescription) }
}
func unregister()
{
func unregister()
{
if let account = mCore.defaultAccount {
let params = account.params
let clonedParams = params?.clone()
clonedParams?.registerEnabled = false
account.params = clonedParams
}
}
}
func delete() {
if let account = mCore.defaultAccount {
mCore.removeAccount(account: account)
@ -106,7 +106,7 @@ class IncomingCallTutorialContext : ObservableObject
mCore.clearAllAuthInfo()
}
}
func terminateCall() {
do {
// Terminates the call, whether it is ringing or running
@ -130,12 +130,12 @@ class IncomingCallTutorialContext : ObservableObject
mCore.micEnabled = !mCore.micEnabled
isMicrophoneEnabled = !isMicrophoneEnabled
}
func toggleSpeaker() {
// Get the currently used audio device
let currentAudioDevice = mCore.currentCall?.outputAudioDevice
let speakerEnabled = currentAudioDevice?.type == AudioDeviceType.Speaker
let test = currentAudioDevice?.deviceName
// We can get a list of all available audio devices using
// Note that on tablets for example, there may be no Earpiece device
@ -155,7 +155,7 @@ class IncomingCallTutorialContext : ObservableObject
}
/* If we wanted to route the audio to a bluetooth headset
else if (audioDevice.type == AudioDevice.Type.Bluetooth) {
core.currentCall?.outputAudioDevice = audioDevice
core.currentCall?.outputAudioDevice = audioDevice
}*/
}
}

View File

@ -10,7 +10,7 @@ import SwiftUI
import linphonesw
struct ContentView: View {
@ObservedObject var tutorialContext : OutgoingCallTutorialContext
func callStateString() -> String {
@ -21,24 +21,24 @@ struct ContentView: View {
}
}
var body: some View {
VStack {
Group {
HStack {
Text("Username:")
.font(.title)
var body: some View {
VStack {
Group {
HStack {
Text("Username:")
.font(.title)
TextField("", text : $tutorialContext.username)
.textFieldStyle(RoundedBorderTextFieldStyle())
.textFieldStyle(RoundedBorderTextFieldStyle())
.disabled(tutorialContext.loggedIn)
}
HStack {
Text("Password:")
.font(.title)
TextField("", text : $tutorialContext.passwd)
.textFieldStyle(RoundedBorderTextFieldStyle())
}
HStack {
Text("Password:")
.font(.title)
TextField("", text : $tutorialContext.passwd)
.textFieldStyle(RoundedBorderTextFieldStyle())
.disabled(tutorialContext.loggedIn)
}
}
HStack {
Text("Domain:")
.font(.title)
@ -47,37 +47,37 @@ struct ContentView: View {
.disabled(tutorialContext.loggedIn)
}
Picker(selection: $tutorialContext.transportType, label: Text("Transport:")) {
Text("TLS").tag("TLS")
Text("TCP").tag("TCP")
Text("UDP").tag("UDP")
Text("TLS").tag("TLS")
Text("TCP").tag("TCP")
Text("UDP").tag("UDP")
}.pickerStyle(SegmentedPickerStyle()).padding()
VStack {
HStack {
Button(action: {
if (self.tutorialContext.loggedIn)
{
self.tutorialContext.unregister()
VStack {
HStack {
Button(action: {
if (self.tutorialContext.loggedIn)
{
self.tutorialContext.unregister()
self.tutorialContext.delete()
} else {
self.tutorialContext.login()
}
})
{
Text(tutorialContext.loggedIn ? "Log out & \ndelete account" : "Create & \nlog in account")
.font(.largeTitle)
.foregroundColor(Color.white)
} else {
self.tutorialContext.login()
}
})
{
Text(tutorialContext.loggedIn ? "Log out & \ndelete account" : "Create & \nlog in account")
.font(.largeTitle)
.foregroundColor(Color.white)
.frame(width: 220.0, height: 90)
.background(Color.gray)
}
}
HStack {
Text("Login State : ")
.font(.footnote)
Text(tutorialContext.loggedIn ? "Looged in" : "Unregistered")
.font(.footnote)
.foregroundColor(tutorialContext.loggedIn ? Color.green : Color.black)
}.padding(.top, 10.0)
}
.background(Color.gray)
}
}
HStack {
Text("Login State : ")
.font(.footnote)
Text(tutorialContext.loggedIn ? "Looged in" : "Unregistered")
.font(.footnote)
.foregroundColor(tutorialContext.loggedIn ? Color.green : Color.black)
}.padding(.top, 10.0)
}
VStack {
HStack {
Text("Call dest:")
@ -96,10 +96,10 @@ struct ContentView: View {
})
{
Text( (tutorialContext.isCallRunning) ? "End" : "Call")
.font(.largeTitle)
.foregroundColor(Color.white)
.frame(width: 180.0, height: 42.0)
.background(Color.gray)
.font(.largeTitle)
.foregroundColor(Color.white)
.frame(width: 180.0, height: 42.0)
.background(Color.gray)
}
HStack {
Text(tutorialContext.isCallRunning ? "Running" : "")
@ -154,18 +154,18 @@ struct ContentView: View {
}
}
}.padding(.top, 30)
}
Group {
Spacer()
Text("Core Version is \(tutorialContext.coreVersion)")
}
}
.padding()
}
}
Group {
Spacer()
Text("Core Version is \(tutorialContext.coreVersion)")
}
}
.padding()
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView(tutorialContext: OutgoingCallTutorialContext())
}
static var previews: some View {
ContentView(tutorialContext: OutgoingCallTutorialContext())
}
}

View File

@ -10,15 +10,15 @@ import linphonesw
class OutgoingCallTutorialContext : ObservableObject
{
var mCore: Core!
@Published var coreVersion: String = Core.getVersion
var mAccount: Account?
var mCore: Core!
@Published var coreVersion: String = Core.getVersion
var mAccount: Account?
var mCoreDelegate : CoreDelegate!
@Published var username : String = "user"
@Published var passwd : String = "pwd"
@Published var username : String = "user"
@Published var passwd : String = "pwd"
@Published var domain : String = "sip.example.org"
@Published var loggedIn: Bool = false
@Published var loggedIn: Bool = false
@Published var transportType : String = "TLS"
// Outgoing call related variables
@ -28,11 +28,11 @@ class OutgoingCallTutorialContext : ObservableObject
@Published var canChangeCamera : Bool = false
@Published var remoteAddress : String = "sip:arguillq@sip.linphone.org"
init()
{
init()
{
LoggingService.Instance.logLevel = LogLevel.Debug
try? mCore = Factory.Instance.createCore(configPath: "", factoryConfigPath: "", systemContext: nil)
try? mCore = Factory.Instance.createCore(configPath: "", factoryConfigPath: "", systemContext: nil)
// Here we enable the video capture & display at Core level
// It doesn't mean calls will be made with video automatically,
// But it allows to use it later
@ -50,7 +50,7 @@ class OutgoingCallTutorialContext : ObservableObject
// If the following property is enabled, it will automatically configure created call params with video enabled
//core.videoActivationPolicy.automaticallyInitiate = true
try? mCore.start()
try? mCore.start()
mCoreDelegate = CoreDelegateStub( onCallStateChanged: { (core: Core, call: Call, state: Call.State, message: String) in
// This function will be called each time a call state changes,
@ -71,7 +71,7 @@ class OutgoingCallTutorialContext : ObservableObject
// or after the ICE negotiation completes
// Wait for the call to be connected before allowing a call update
self.isCallRunning = true
// Only enable toggle camera button if there is more than 1 camera
// We check if core.videoDevicesList.size > 2 because of the fake camera with static image created by our SDK (see below)
self.canChangeCamera = core.videoDevicesList.count > 2
@ -89,7 +89,7 @@ class OutgoingCallTutorialContext : ObservableObject
self.isCallRunning = false
self.canChangeCamera = false
} else if (state == .Error) {
}
}, onAccountRegistrationStateChanged: { (core: Core, account: Account, state: RegistrationState, message: String) in
NSLog("New registration state is \(state) for user id \( String(describing: account.params?.identityAddress?.asString()))\n")
@ -100,8 +100,8 @@ class OutgoingCallTutorialContext : ObservableObject
}
})
mCore.addDelegate(delegate: mCoreDelegate)
}
}
func login() {
do {
@ -122,19 +122,19 @@ class OutgoingCallTutorialContext : ObservableObject
mCore.addAuthInfo(info: authInfo)
try mCore.addAccount(account: mAccount!)
mCore.defaultAccount = mAccount
} catch { NSLog(error.localizedDescription) }
}
func unregister()
{
func unregister()
{
if let account = mCore.defaultAccount {
let params = account.params
let clonedParams = params?.clone()
clonedParams?.registerEnabled = false
account.params = clonedParams
}
}
}
func delete() {
if let account = mCore.defaultAccount {
mCore.removeAccount(account: account)
@ -142,17 +142,17 @@ class OutgoingCallTutorialContext : ObservableObject
mCore.clearAllAuthInfo()
}
}
func outgoingCall() {
do {
// As for everything we need to get the SIP URI of the remote and convert it to an Address
let remoteAddress = try Factory.Instance.createAddress(addr: remoteAddress)
// We also need a CallParams object
// Create call params expects a Call object for incoming calls, but for outgoing we must use null safely
let params = try mCore.createCallParams(call: nil)
// We can now configure it
// Here we ask for no encryption but we could ask for ZRTP/SRTP/DTLS
params.mediaEncryption = MediaEncryption.None
@ -169,10 +169,10 @@ class OutgoingCallTutorialContext : ObservableObject
func terminateCall() {
do {
if (mCore.callsNb == 0) { return }
// If the call state isn't paused, we can get it using core.currentCall
let coreCall = (mCore.currentCall != nil) ? mCore.currentCall : mCore.calls[0]
// Terminating a call is quite simple
if let call = coreCall {
try call.terminate()
@ -207,7 +207,7 @@ class OutgoingCallTutorialContext : ObservableObject
do {
// Currently used camera
let currentDevice = mCore.videoDevice
// Let's iterate over all camera available and choose another one
for camera in mCore.videoDevicesList {
// All devices will have a "Static picture" fake camera, and we don't want to use it
@ -223,7 +223,7 @@ class OutgoingCallTutorialContext : ObservableObject
do {
if (mCore.callsNb == 0) { return }
let coreCall = (mCore.currentCall != nil) ? mCore.currentCall : mCore.calls[0]
if let call = coreCall {
if (call.state != Call.State.Paused && call.state != Call.State.Pausing) {
// If our call isn't paused, let's pause it

View File

@ -14,53 +14,53 @@ import AVFoundation
class CallKitProviderDelegate : NSObject
{
private let provider: CXProvider
let mCallController = CXCallController()
var tutorialContext : CallKitExampleContext!
var incomingCallUUID : UUID!
init(context: CallKitExampleContext)
{
tutorialContext = context
let providerConfiguration = CXProviderConfiguration(localizedName: Bundle.main.infoDictionary!["CFBundleName"] as! String)
providerConfiguration.supportsVideo = true
providerConfiguration.supportedHandleTypes = [.generic]
providerConfiguration.maximumCallsPerCallGroup = 1
providerConfiguration.maximumCallGroups = 1
provider = CXProvider(configuration: providerConfiguration)
super.init()
provider.setDelegate(self, queue: nil) // The CXProvider delegate will trigger CallKit related callbacks
}
func incomingCall()
{
incomingCallUUID = UUID()
let update = CXCallUpdate()
update.remoteHandle = CXHandle(type:.generic, value: tutorialContext.incomingCallName)
provider.reportNewIncomingCall(with: incomingCallUUID, update: update, completion: { error in }) // Report to CallKit a call is incoming
}
func stopCall()
{
let endCallAction = CXEndCallAction(call: incomingCallUUID)
let transaction = CXTransaction(action: endCallAction)
mCallController.request(transaction, completion: { error in }) // Report to CallKit a call must end
}
private let provider: CXProvider
let mCallController = CXCallController()
var tutorialContext : CallKitExampleContext!
var incomingCallUUID : UUID!
init(context: CallKitExampleContext)
{
tutorialContext = context
let providerConfiguration = CXProviderConfiguration(localizedName: Bundle.main.infoDictionary!["CFBundleName"] as! String)
providerConfiguration.supportsVideo = true
providerConfiguration.supportedHandleTypes = [.generic]
providerConfiguration.maximumCallsPerCallGroup = 1
providerConfiguration.maximumCallGroups = 1
provider = CXProvider(configuration: providerConfiguration)
super.init()
provider.setDelegate(self, queue: nil) // The CXProvider delegate will trigger CallKit related callbacks
}
func incomingCall()
{
incomingCallUUID = UUID()
let update = CXCallUpdate()
update.remoteHandle = CXHandle(type:.generic, value: tutorialContext.incomingCallName)
provider.reportNewIncomingCall(with: incomingCallUUID, update: update, completion: { error in }) // Report to CallKit a call is incoming
}
func stopCall()
{
let endCallAction = CXEndCallAction(call: incomingCallUUID)
let transaction = CXTransaction(action: endCallAction)
mCallController.request(transaction, completion: { error in }) // Report to CallKit a call must end
}
}
// In this extension, we implement the action we want to be done when CallKit is notified of something.
// This can happen through the CallKit GUI in the app, or directly in the code (see, incomingCall(), stopCall() functions above)
extension CallKitProviderDelegate: CXProviderDelegate {
func provider(_ provider: CXProvider, perform action: CXEndCallAction) {
func provider(_ provider: CXProvider, perform action: CXEndCallAction) {
do {
if (tutorialContext.mCall?.state != .End && tutorialContext.mCall?.state != .Released) {
try tutorialContext.mCall?.terminate()
@ -69,31 +69,31 @@ extension CallKitProviderDelegate: CXProviderDelegate {
tutorialContext.isCallRunning = false
tutorialContext.isCallIncoming = false
action.fulfill()
}
func provider(_ provider: CXProvider, perform action: CXAnswerCallAction) {
do {
try tutorialContext.mCall?.accept()
tutorialContext.isCallRunning = true
} catch {
print(error)
}
action.fulfill()
}
func provider(_ provider: CXProvider, perform action: CXSetHeldCallAction) {}
func provider(_ provider: CXProvider, perform action: CXStartCallAction) {}
func provider(_ provider: CXProvider, perform action: CXSetMutedCallAction) {}
func provider(_ provider: CXProvider, perform action: CXPlayDTMFCallAction) {}
func provider(_ provider: CXProvider, timedOutPerforming action: CXAction) {}
func providerDidReset(_ provider: CXProvider) {}
func provider(_ provider: CXProvider, didActivate audioSession: AVAudioSession) {
tutorialContext.mCore.activateAudioSession(actived: true)
}
func provider(_ provider: CXProvider, didDeactivate audioSession: AVAudioSession) {
tutorialContext.mCore.activateAudioSession(actived: false)
}
action.fulfill()
}
func provider(_ provider: CXProvider, perform action: CXAnswerCallAction) {
do {
try tutorialContext.mCall?.accept()
tutorialContext.isCallRunning = true
} catch {
print(error)
}
action.fulfill()
}
func provider(_ provider: CXProvider, perform action: CXSetHeldCallAction) {}
func provider(_ provider: CXProvider, perform action: CXStartCallAction) {}
func provider(_ provider: CXProvider, perform action: CXSetMutedCallAction) {}
func provider(_ provider: CXProvider, perform action: CXPlayDTMFCallAction) {}
func provider(_ provider: CXProvider, timedOutPerforming action: CXAction) {}
func providerDidReset(_ provider: CXProvider) {}
func provider(_ provider: CXProvider, didActivate audioSession: AVAudioSession) {
tutorialContext.mCore.activateAudioSession(actived: true)
}
func provider(_ provider: CXProvider, didDeactivate audioSession: AVAudioSession) {
tutorialContext.mCore.activateAudioSession(actived: false)
}
}

View File

@ -11,14 +11,14 @@ import AVFoundation
class CallKitExampleContext : ObservableObject
{
var mCore: Core!
@Published var coreVersion: String = Core.getVersion
var mCore: Core!
@Published var coreVersion: String = Core.getVersion
var mAccount: Account?
var mCoreDelegate : CoreDelegate!
@Published var username : String = "quentindev"
@Published var passwd : String = "dev"
@Published var domain : String = "sip.linphone.org"
@Published var username : String = "user"
@Published var passwd : String = "pwd"
@Published var domain : String = "sip.example.org"
@Published var loggedIn: Bool = false
@Published var transportType : String = "TLS"
@ -35,8 +35,8 @@ class CallKitExampleContext : ObservableObject
var mProviderDelegate : CallKitProviderDelegate!
var mCallAlreadyStopped : Bool = false;
init()
{
init()
{
LoggingService.Instance.logLevel = LogLevel.Debug
let factory = Factory.Instance
@ -92,8 +92,8 @@ class CallKitExampleContext : ObservableObject
}
})
mCore.addDelegate(delegate: mCoreDelegate)
}
}
func login() {
do {
@ -118,7 +118,7 @@ class CallKitExampleContext : ObservableObject
mCore.addAuthInfo(info: authInfo)
try mCore.addAccount(account: mAccount!)
mCore.defaultAccount = mAccount
} catch { NSLog(error.localizedDescription) }
}

View File

@ -48,9 +48,9 @@ struct ContentView: View {
.disabled(tutorialContext.loggedIn)
}
Picker(selection: $tutorialContext.transportType, label: Text("Transport:")) {
Text("TLS").tag("TLS")
Text("TCP").tag("TCP")
Text("UDP").tag("UDP")
Text("TLS").tag("TLS")
Text("TCP").tag("TCP")
Text("UDP").tag("UDP")
}.pickerStyle(SegmentedPickerStyle()).padding()
VStack {
HStack {

View File

@ -12,14 +12,14 @@ import linphonesw
class BasicChatTutorialContext : ObservableObject
{
var mCore: Core!
@Published var coreVersion: String = Core.getVersion
var mCore: Core!
@Published var coreVersion: String = Core.getVersion
var mRegistrationDelegate : CoreDelegate!
@Published var username : String = "user"
@Published var passwd : String = "pwd"
@Published var username : String = "user"
@Published var passwd : String = "pwd"
@Published var domain : String = "sip.example.org"
@Published var loggedIn: Bool = false
@Published var loggedIn: Bool = false
@Published var transportType : String = "TLS"
/*------------ Basic chat tutorial related variables -------*/
@ -35,12 +35,12 @@ class BasicChatTutorialContext : ObservableObject
var fileFolderUrl : URL!
var fileUrl : URL!
init()
{
init()
{
LoggingService.Instance.logLevel = LogLevel.Debug
try? mCore = Factory.Instance.createCore(configPath: "", factoryConfigPath: "", systemContext: nil)
try? mCore.start()
try? mCore = Factory.Instance.createCore(configPath: "", factoryConfigPath: "", systemContext: nil)
try? mCore.start()
mRegistrationDelegate = CoreDelegateStub(onMessageReceived : { (core: Core, chatRoom: ChatRoom, message: ChatMessage) in
// We will be called in this when a message is received
@ -58,7 +58,7 @@ class BasicChatTutorialContext : ObservableObject
}
// We will notify the sender the message has been read by us
chatRoom.markAsRead()
for content in message.contents {
if (content.isFileTransfer) {
self.mLastFileMessageReceived = message
@ -98,8 +98,8 @@ class BasicChatTutorialContext : ObservableObject
}catch let error as NSError{
print("Unable to create d)irectory",error)
}
}
}
func login() {
do {
@ -117,24 +117,24 @@ class BasicChatTutorialContext : ObservableObject
try accountParams.setServeraddress(newValue: address)
accountParams.registerEnabled = true
let account = try mCore.createAccount(params: accountParams)
mCore.addAuthInfo(info: authInfo)
try mCore.addAccount(account: account)
mCore.defaultAccount = account
} catch { NSLog(error.localizedDescription) }
}
func unregister()
{
func unregister()
{
if let account = mCore.defaultAccount {
let params = account.params
let clonedParams = params?.clone()
clonedParams?.registerEnabled = false
account.params = clonedParams
}
}
}
func delete() {
if let account = mCore.defaultAccount {
mCore.removeAccount(account: account)
@ -153,7 +153,7 @@ class BasicChatTutorialContext : ObservableObject
params.backend = ChatRoomBackend.Basic
params.encryptionEnabled = false
params.groupEnabled = false
if (params.isValid) {
// We also need the SIP address of the person we will chat with
let remote = try Factory.Instance.createAddress(addr: remoteAddress)
@ -166,7 +166,7 @@ class BasicChatTutorialContext : ObservableObject
}
} catch { NSLog(error.localizedDescription) }
}
func sendMessage() {
do {
if (mChatroom == nil) {
@ -176,50 +176,50 @@ class BasicChatTutorialContext : ObservableObject
mChatMessage = nil
// We need to create a ChatMessage object using the ChatRoom
mChatMessage = try mChatroom!.createMessageFromUtf8(message: msgToSend)
// Then we can send it, progress will be notified using the onMsgStateChanged callback
mChatMessage!.addDelegate(delegate: mChatMessageDelegate)
// Send the message
mChatMessage!.send()
// Clear the message input field
msgToSend.removeAll()
} catch { NSLog(error.localizedDescription) }
}
func sendFile() {
do {
if (mChatroom == nil) {
// We need a ChatRoom object to send chat messages in it, so let's create it if it hasn't been done yet
createBasicChatRoom()
}
// We need to create a Content for our file transfer
let content = try Factory.Instance.createContent()
// Every content needs a content type & subtype
content.name = "file_to_transfer.txt"
content.type = "text"
content.subtype = "plain"
// The simplest way to upload a file is to provide it's path
content.filePath = fileUrl.path
// We need to create a ChatMessage object using the ChatRoom
let chatMessage = try mChatroom!.createFileTransferMessage(initialContent: content)
// Then we can send it, progress will be notified using the onMsgStateChanged callback
chatMessage.addDelegate(delegate: mChatMessageDelegate)
// Ensure a file sharing server URL is correctly set in the Core
mCore.fileTransferServer = "https://www.linphone.org:444/lft.php"
// Send the message
chatMessage.send()
} catch { NSLog(error.localizedDescription) }
}
func downloadLastFileMessage() {
if let message = mLastFileMessageReceived {

View File

@ -21,27 +21,27 @@ struct ActivityIndicator: UIViewRepresentable {
}
struct ContentView: View {
@ObservedObject var tutorialContext : BasicChatTutorialContext
var body: some View {
VStack {
Group {
HStack {
Text("Username:")
.font(.title)
var body: some View {
VStack {
Group {
HStack {
Text("Username:")
.font(.title)
TextField("", text : $tutorialContext.username)
.textFieldStyle(RoundedBorderTextFieldStyle())
.textFieldStyle(RoundedBorderTextFieldStyle())
.disabled(tutorialContext.loggedIn)
}
HStack {
Text("Password:")
.font(.title)
TextField("", text : $tutorialContext.passwd)
.textFieldStyle(RoundedBorderTextFieldStyle())
}
HStack {
Text("Password:")
.font(.title)
TextField("", text : $tutorialContext.passwd)
.textFieldStyle(RoundedBorderTextFieldStyle())
.disabled(tutorialContext.loggedIn)
}
}
HStack {
Text("Domain:")
.font(.title)
@ -50,37 +50,37 @@ struct ContentView: View {
.disabled(tutorialContext.loggedIn)
}
Picker(selection: $tutorialContext.transportType, label: Text("Transport:")) {
Text("TLS").tag("TLS")
Text("TCP").tag("TCP")
Text("UDP").tag("UDP")
Text("TLS").tag("TLS")
Text("TCP").tag("TCP")
Text("UDP").tag("UDP")
}.pickerStyle(SegmentedPickerStyle()).padding()
VStack {
HStack {
Button(action: {
if (self.tutorialContext.loggedIn)
{
self.tutorialContext.unregister()
VStack {
HStack {
Button(action: {
if (self.tutorialContext.loggedIn)
{
self.tutorialContext.unregister()
self.tutorialContext.delete()
} else {
self.tutorialContext.login()
}
})
{
Text(tutorialContext.loggedIn ? "Log out & \ndelete account" : "Create & \nlog in account")
.font(.largeTitle)
.foregroundColor(Color.white)
} else {
self.tutorialContext.login()
}
})
{
Text(tutorialContext.loggedIn ? "Log out & \ndelete account" : "Create & \nlog in account")
.font(.largeTitle)
.foregroundColor(Color.white)
.frame(width: 220.0, height: 90)
.background(Color.gray)
}
}
HStack {
Text("Login State : ")
.font(.footnote)
Text(tutorialContext.loggedIn ? "Looged in" : "Unregistered")
.font(.footnote)
.foregroundColor(tutorialContext.loggedIn ? Color.green : Color.black)
}.padding(.top, 10.0)
.background(Color.gray)
}
}
HStack {
Text("Login State : ")
.font(.footnote)
Text(tutorialContext.loggedIn ? "Looged in" : "Unregistered")
.font(.footnote)
.foregroundColor(tutorialContext.loggedIn ? Color.green : Color.black)
}.padding(.top, 10.0)
HStack {
Text("Chat with:")
TextField("", text : $tutorialContext.remoteAddress)
@ -109,36 +109,36 @@ struct ContentView: View {
Button(action: tutorialContext.sendFile)
{
Text("Send example \n file")
.foregroundColor(Color.white)
.multilineTextAlignment(.center)
.frame(width: 120.0, height: 50.0)
.background(Color.gray)
.foregroundColor(Color.white)
.multilineTextAlignment(.center)
.frame(width: 120.0, height: 50.0)
.background(Color.gray)
}
Button(action: tutorialContext.downloadLastFileMessage)
{
Text("Download last files \n received")
.foregroundColor(Color.white)
.multilineTextAlignment(.center)
.frame(width: 150.0, height: 50.0)
.background(Color.gray)
.foregroundColor(Color.white)
.multilineTextAlignment(.center)
.frame(width: 150.0, height: 50.0)
.background(Color.gray)
}.disabled(tutorialContext.mLastFileMessageReceived == nil)
if (tutorialContext.isDownloading) {
ActivityIndicator()
}
}
}
}
Group {
Spacer()
Text("Core Version is \(tutorialContext.coreVersion)")
}
}
.padding()
}
}
}
Group {
Spacer()
Text("Core Version is \(tutorialContext.coreVersion)")
}
}
.padding()
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView(tutorialContext: BasicChatTutorialContext())
}
static var previews: some View {
ContentView(tutorialContext: BasicChatTutorialContext())
}
}

View File

@ -12,14 +12,14 @@ import linphonesw
class AdvancedChatTutorialContext : ObservableObject
{
var mCore: Core!
@Published var coreVersion: String = Core.getVersion
var mCore: Core!
@Published var coreVersion: String = Core.getVersion
var mRegistrationDelegate : CoreDelegate!
@Published var username : String = "user"
@Published var passwd : String = "pwd"
@Published var username : String = "user"
@Published var passwd : String = "pwd"
@Published var domain : String = "sip.example.org"
@Published var loggedIn: Bool = false
@Published var loggedIn: Bool = false
@Published var transportType : String = "TLS"
/*------------ Advanced chat tutorial related variables -------*/
@ -36,12 +36,12 @@ class AdvancedChatTutorialContext : ObservableObject
var fileFolderUrl : URL!
var fileUrl : URL!
init()
{
init()
{
LoggingService.Instance.logLevel = LogLevel.Debug
try? mCore = Factory.Instance.createCore(configPath: "", factoryConfigPath: "", systemContext: nil)
try? mCore.start()
try? mCore = Factory.Instance.createCore(configPath: "", factoryConfigPath: "", systemContext: nil)
try? mCore.start()
mRegistrationDelegate = CoreDelegateStub(onMessageReceived : { (core: Core, chatRoom: ChatRoom, message: ChatMessage) in
if (self.mChatroom == nil) {
@ -60,7 +60,7 @@ class AdvancedChatTutorialContext : ObservableObject
}
chatRoom.markAsRead()
for content in message.contents {
if (content.isFileTransfer) {
self.mLastFileMessageReceived = message
@ -124,8 +124,8 @@ class AdvancedChatTutorialContext : ObservableObject
}catch let error as NSError{
print("Unable to create d)irectory",error)
}
}
}
func login() {
do {
@ -151,21 +151,21 @@ class AdvancedChatTutorialContext : ObservableObject
let account = try mCore.createAccount(params: accountParams)
try mCore.addAccount(account: account)
mCore.defaultAccount = account
// We also need a LIME X3DH server URL configured for end to end encryption
mCore.limeX3DhServerUrl = "https://lime.linphone.org/lime-server/lime-server.php"
} catch { NSLog(error.localizedDescription) }
}
func unregister()
{
func unregister()
{
if let account = mCore.defaultAccount {
let params = account.params
let clonedParams = params?.clone()
clonedParams?.registerEnabled = false
account.params = clonedParams
}
}
}
func delete() {
if let account = mCore.defaultAccount {
mCore.removeAccount(account: account)
@ -174,26 +174,26 @@ class AdvancedChatTutorialContext : ObservableObject
}
}
func createFlexisipChatRoom() {
do {
// In this tutorial we will create a Flexisip one-to-one chat room with end-to-end encryption
// For it to work, the proxy server we connect to must be an instance of Flexisip
// And we must have configured on the Account a conference-factory URI
let params = try mCore.createDefaultChatRoomParams()
// We won't create a group chat, only a 1-1 with advanced features such as end-to-end encryption
params.backend = ChatRoomBackend.FlexisipChat
params.groupEnabled = false
// We will rely on LIME encryption backend (we must have configured the core.limex3dhServerUrl first)
params.encryptionEnabled = true
params.encryptionBackend = ChatRoomEncryptionBackend.Lime
// A flexisip chat room must have a subject
// But as we are doing a 1-1 chat room here we won't display it, so we can set whatever we want
params.subject = "dummy subject"
if (params.isValid) {
// We also need the SIP address of the person we will chat with
let remote = try Factory.Instance.createAddress(addr: remoteAddress)
@ -204,7 +204,7 @@ class AdvancedChatTutorialContext : ObservableObject
// If chat room isn't created yet, wait for it to go in state Created
// as Flexisip chat room creation process is asynchronous
mChatroom!.addDelegate(delegate: mChatroomDelegate)
// Chat room may already be created (for example if you logged in with an account for which the chat room already exists)
if (mChatroom!.state == ChatRoom.State.Created) {
enableEphemeral()
@ -212,8 +212,8 @@ class AdvancedChatTutorialContext : ObservableObject
}
}
} catch { NSLog(error.localizedDescription) }
}
}
func sendMessage() {
do {
if (mChatroom == nil) {
@ -226,13 +226,13 @@ class AdvancedChatTutorialContext : ObservableObject
msgToSend.removeAll()
} catch { NSLog(error.localizedDescription) }
}
func sendFile() {
do {
if (mChatroom == nil) {
createFlexisipChatRoom()
}
let content = try Factory.Instance.createContent()
content.name = "file_to_transfer.txt"
content.type = "text"
@ -245,7 +245,7 @@ class AdvancedChatTutorialContext : ObservableObject
} catch { NSLog(error.localizedDescription) }
}
func downloadLastFileMessage() {
if let message = mLastFileMessageReceived {
for content in message.contents {

View File

@ -21,27 +21,27 @@ struct ActivityIndicator: UIViewRepresentable {
}
struct ContentView: View {
@ObservedObject var tutorialContext : AdvancedChatTutorialContext
var body: some View {
VStack {
Group {
HStack {
Text("Username:")
.font(.title)
var body: some View {
VStack {
Group {
HStack {
Text("Username:")
.font(.title)
TextField("", text : $tutorialContext.username)
.textFieldStyle(RoundedBorderTextFieldStyle())
.textFieldStyle(RoundedBorderTextFieldStyle())
.disabled(tutorialContext.loggedIn)
}
HStack {
Text("Password:")
.font(.title)
TextField("", text : $tutorialContext.passwd)
.textFieldStyle(RoundedBorderTextFieldStyle())
}
HStack {
Text("Password:")
.font(.title)
TextField("", text : $tutorialContext.passwd)
.textFieldStyle(RoundedBorderTextFieldStyle())
.disabled(tutorialContext.loggedIn)
}
}
HStack {
Text("Domain:")
.font(.title)
@ -50,37 +50,37 @@ struct ContentView: View {
.disabled(tutorialContext.loggedIn)
}
Picker(selection: $tutorialContext.transportType, label: Text("Transport:")) {
Text("TLS").tag("TLS")
Text("TCP").tag("TCP")
Text("UDP").tag("UDP")
Text("TLS").tag("TLS")
Text("TCP").tag("TCP")
Text("UDP").tag("UDP")
}.pickerStyle(SegmentedPickerStyle()).padding()
VStack {
HStack {
Button(action: {
if (self.tutorialContext.loggedIn)
{
self.tutorialContext.unregister()
VStack {
HStack {
Button(action: {
if (self.tutorialContext.loggedIn)
{
self.tutorialContext.unregister()
self.tutorialContext.delete()
} else {
self.tutorialContext.login()
}
})
{
Text(tutorialContext.loggedIn ? "Log out & \ndelete account" : "Create & \nlog in account")
.font(.largeTitle)
.foregroundColor(Color.white)
} else {
self.tutorialContext.login()
}
})
{
Text(tutorialContext.loggedIn ? "Log out & \ndelete account" : "Create & \nlog in account")
.font(.largeTitle)
.foregroundColor(Color.white)
.frame(width: 220.0, height: 90)
.background(Color.gray)
}
}
HStack {
Text("Login State : ")
.font(.footnote)
Text(tutorialContext.loggedIn ? "Looged in" : "Unregistered")
.font(.footnote)
.foregroundColor(tutorialContext.loggedIn ? Color.green : Color.black)
}.padding(.top, 10.0)
.background(Color.gray)
}
}
HStack {
Text("Login State : ")
.font(.footnote)
Text(tutorialContext.loggedIn ? "Looged in" : "Unregistered")
.font(.footnote)
.foregroundColor(tutorialContext.loggedIn ? Color.green : Color.black)
}.padding(.top, 10.0)
HStack {
Text("Chat with:")
TextField("", text : $tutorialContext.remoteAddress)
@ -109,36 +109,36 @@ struct ContentView: View {
Button(action: tutorialContext.sendFile)
{
Text("Send example \n file")
.foregroundColor(Color.white)
.multilineTextAlignment(.center)
.frame(width: 120.0, height: 50.0)
.background(Color.gray)
.foregroundColor(Color.white)
.multilineTextAlignment(.center)
.frame(width: 120.0, height: 50.0)
.background(Color.gray)
}
Button(action: tutorialContext.downloadLastFileMessage)
{
Text("Download last files \n received")
.foregroundColor(Color.white)
.multilineTextAlignment(.center)
.frame(width: 150.0, height: 50.0)
.background(Color.gray)
.foregroundColor(Color.white)
.multilineTextAlignment(.center)
.frame(width: 150.0, height: 50.0)
.background(Color.gray)
}.disabled(tutorialContext.mLastFileMessageReceived == nil)
if (tutorialContext.isDownloading) {
ActivityIndicator()
}
}
}
}
Group {
Spacer()
Text("Core Version is \(tutorialContext.coreVersion)")
}
}
.padding()
}
}
}
Group {
Spacer()
Text("Core Version is \(tutorialContext.coreVersion)")
}
}
.padding()
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView(tutorialContext: AdvancedChatTutorialContext())
}
static var previews: some View {
ContentView(tutorialContext: AdvancedChatTutorialContext())
}
}