Add swift tutorials for basic call, voip push and chat features with SDK 5.0

This commit is contained in:
QuentinArguillere 2020-07-24 15:55:28 +02:00 committed by QuentinArguillere
parent 22df57e83d
commit 4a5d86e9aa
107 changed files with 6976 additions and 170 deletions

13
.gitignore vendored
View File

@ -7,7 +7,7 @@ java/.idea/modules.xml
java/.idea/workspace.xml
java/.idea/navEditor.xml
java/.idea/assetWizardSettings.xml
java/.DS_Store
.DS_Store
java/build
java/captures
java/.externalNativeBuild
@ -26,9 +26,10 @@ AppPackages
[Oo]bj/
[Bb]in/
swift/HelloLinphone/Podfile.lock
swift/HelloLinphone/Pods/
swift/HelloLinphone/HelloLinphone.xcodeproj/project.xcworkspace/xcuserdata/
swift/HelloLinphone/HelloLinphone.xcodeproj/xcuserdata/
swift/HelloLinphone/HelloLinphone.xcworkspace/xcuserdata/
ios/swift/*/Podfile.lock
ios/swift/*/Pods/
ios/swift/*/*.xcodeproj/project.xcworkspace/xcuserdata/
ios/swift/*/*.xcodeproj/xcuserdata/
ios/swift/*/*.xcworkspace/xcuserdata/
ios/swift/*/build

View File

@ -0,0 +1,424 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 51;
objects = {
/* Begin PBXBuildFile section */
663D8CDF26E8E35400EE487F /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 663D8CDE26E8E35400EE487F /* AppDelegate.swift */; };
663D8CE126E8E35400EE487F /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 663D8CE026E8E35400EE487F /* SceneDelegate.swift */; };
663D8CE326E8E35400EE487F /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 663D8CE226E8E35400EE487F /* ContentView.swift */; };
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 */; };
AE5DCE54FB07B00CDB39CF8C /* Pods_Helloworld.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 658B93FC56A6311767BE2F24 /* Pods_Helloworld.framework */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
1D8A919C73E1D36DB079968D /* Pods-Helloworld.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Helloworld.debug.xcconfig"; path = "Target Support Files/Pods-Helloworld/Pods-Helloworld.debug.xcconfig"; sourceTree = "<group>"; };
658B93FC56A6311767BE2F24 /* Pods_Helloworld.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Helloworld.framework; sourceTree = BUILT_PRODUCTS_DIR; };
663D8CDB26E8E35400EE487F /* Helloworld.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Helloworld.app; sourceTree = BUILT_PRODUCTS_DIR; };
663D8CDE26E8E35400EE487F /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
663D8CE026E8E35400EE487F /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = "<group>"; };
663D8CE226E8E35400EE487F /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
663D8CE426E8E35500EE487F /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
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>"; };
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 */
/* Begin PBXFrameworksBuildPhase section */
663D8CD826E8E35400EE487F /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
AE5DCE54FB07B00CDB39CF8C /* Pods_Helloworld.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
663D8CD226E8E35400EE487F = {
isa = PBXGroup;
children = (
663D8CDD26E8E35400EE487F /* Helloworld */,
663D8CDC26E8E35400EE487F /* Products */,
FBA8E9BB676F4A0224467983 /* Pods */,
930DDB069926306D8F86A1DF /* Frameworks */,
);
sourceTree = "<group>";
};
663D8CDC26E8E35400EE487F /* Products */ = {
isa = PBXGroup;
children = (
663D8CDB26E8E35400EE487F /* Helloworld.app */,
);
name = Products;
sourceTree = "<group>";
};
663D8CDD26E8E35400EE487F /* Helloworld */ = {
isa = PBXGroup;
children = (
663D8CDE26E8E35400EE487F /* AppDelegate.swift */,
663D8CE026E8E35400EE487F /* SceneDelegate.swift */,
663D8CE226E8E35400EE487F /* ContentView.swift */,
663D8CE426E8E35500EE487F /* Assets.xcassets */,
663D8CE926E8E35500EE487F /* LaunchScreen.storyboard */,
663D8CEC26E8E35500EE487F /* Info.plist */,
663D8CE626E8E35500EE487F /* Preview Content */,
663D8CF226E8E73700EE487F /* HelloworldTutorial.swift */,
);
path = Helloworld;
sourceTree = "<group>";
};
663D8CE626E8E35500EE487F /* Preview Content */ = {
isa = PBXGroup;
children = (
663D8CE726E8E35500EE487F /* Preview Assets.xcassets */,
);
path = "Preview Content";
sourceTree = "<group>";
};
930DDB069926306D8F86A1DF /* Frameworks */ = {
isa = PBXGroup;
children = (
658B93FC56A6311767BE2F24 /* Pods_Helloworld.framework */,
);
name = Frameworks;
sourceTree = "<group>";
};
FBA8E9BB676F4A0224467983 /* Pods */ = {
isa = PBXGroup;
children = (
1D8A919C73E1D36DB079968D /* Pods-Helloworld.debug.xcconfig */,
B14B29CDA5B52FB0218CB545 /* Pods-Helloworld.release.xcconfig */,
);
path = Pods;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
663D8CDA26E8E35400EE487F /* Helloworld */ = {
isa = PBXNativeTarget;
buildConfigurationList = 663D8CEF26E8E35500EE487F /* Build configuration list for PBXNativeTarget "Helloworld" */;
buildPhases = (
65BF27D12FE9B5322F627C5F /* [CP] Check Pods Manifest.lock */,
663D8CD726E8E35400EE487F /* Sources */,
663D8CD826E8E35400EE487F /* Frameworks */,
663D8CD926E8E35400EE487F /* Resources */,
6D3F26BE93D3EC1C45D2BDAE /* [CP] Embed Pods Frameworks */,
);
buildRules = (
);
dependencies = (
);
name = Helloworld;
productName = Helloworld;
productReference = 663D8CDB26E8E35400EE487F /* Helloworld.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
663D8CD326E8E35400EE487F /* Project object */ = {
isa = PBXProject;
attributes = {
LastSwiftUpdateCheck = 1250;
LastUpgradeCheck = 1250;
TargetAttributes = {
663D8CDA26E8E35400EE487F = {
CreatedOnToolsVersion = 12.5.1;
};
};
};
buildConfigurationList = 663D8CD626E8E35400EE487F /* Build configuration list for PBXProject "Helloworld" */;
compatibilityVersion = "Xcode 9.3";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = 663D8CD226E8E35400EE487F;
productRefGroup = 663D8CDC26E8E35400EE487F /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
663D8CDA26E8E35400EE487F /* Helloworld */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
663D8CD926E8E35400EE487F /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
663D8CEB26E8E35500EE487F /* LaunchScreen.storyboard in Resources */,
663D8CE826E8E35500EE487F /* Preview Assets.xcassets in Resources */,
663D8CE526E8E35500EE487F /* Assets.xcassets in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
65BF27D12FE9B5322F627C5F /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputFileListPaths = (
);
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-Helloworld-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
6D3F26BE93D3EC1C45D2BDAE /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Helloworld/Pods-Helloworld-frameworks-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Embed Pods Frameworks";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Helloworld/Pods-Helloworld-frameworks-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Helloworld/Pods-Helloworld-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
663D8CD726E8E35400EE487F /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
663D8CDF26E8E35400EE487F /* AppDelegate.swift in Sources */,
663D8CE126E8E35400EE487F /* SceneDelegate.swift in Sources */,
663D8CE326E8E35400EE487F /* ContentView.swift in Sources */,
663D8CF326E8E73700EE487F /* HelloworldTutorial.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXVariantGroup section */
663D8CE926E8E35500EE487F /* LaunchScreen.storyboard */ = {
isa = PBXVariantGroup;
children = (
663D8CEA26E8E35500EE487F /* Base */,
);
name = LaunchScreen.storyboard;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
663D8CED26E8E35500EE487F /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 14.5;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
};
name = Debug;
};
663D8CEE26E8E35500EE487F /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 14.5;
MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES;
SDKROOT = iphoneos;
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_OPTIMIZATION_LEVEL = "-O";
VALIDATE_PRODUCT = YES;
};
name = Release;
};
663D8CF026E8E35500EE487F /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 1D8A919C73E1D36DB079968D /* Pods-Helloworld.debug.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_ASSET_PATHS = "\"Helloworld/Preview Content\"";
DEVELOPMENT_TEAM = Z2V957B3D6;
ENABLE_PREVIEWS = YES;
INFOPLIST_FILE = Helloworld/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = BC.Helloworld;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
663D8CF126E8E35500EE487F /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = B14B29CDA5B52FB0218CB545 /* Pods-Helloworld.release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_ASSET_PATHS = "\"Helloworld/Preview Content\"";
DEVELOPMENT_TEAM = Z2V957B3D6;
ENABLE_PREVIEWS = YES;
INFOPLIST_FILE = Helloworld/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = BC.Helloworld;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
663D8CD626E8E35400EE487F /* Build configuration list for PBXProject "Helloworld" */ = {
isa = XCConfigurationList;
buildConfigurations = (
663D8CED26E8E35500EE487F /* Debug */,
663D8CEE26E8E35500EE487F /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
663D8CEF26E8E35500EE487F /* Build configuration list for PBXNativeTarget "Helloworld" */ = {
isa = XCConfigurationList;
buildConfigurations = (
663D8CF026E8E35500EE487F /* Debug */,
663D8CF126E8E35500EE487F /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 663D8CD326E8E35400EE487F /* Project object */;
}

View File

@ -2,6 +2,6 @@
<Workspace
version = "1.0">
<FileRef
location = "self:HelloLinphone.xcodeproj">
location = "self:">
</FileRef>
</Workspace>

View File

@ -1,17 +1,17 @@
//
// AppDelegate.swift
// HelloLinphone
// Helloworld
//
// Created by Danmei Chen on 23/06/2020.
// Copyright © 2020 belledonne. All rights reserved.
// Created by QuentinArguillere on 08/09/2021.
//
import UIKit
import SwiftUI
@UIApplicationMain
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
@ObservedObject var tutorialContext = HelloworldTutorialContext()
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.

View File

@ -0,0 +1,11 @@
{
"colors" : [
{
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,32 @@
//
// ContentView.swift
// HelloworldTutorial
//
// Created by QuentinArguillere on 08/09/2021.
// Copyright © 2021 BelledonneCommunications. All rights reserved.
//
import SwiftUI
struct ContentView: View {
@ObservedObject var tutorialContext : HelloworldTutorialContext
var body: some View {
VStack {
Group {
Spacer()
Text("Hello World ! \nCore Version is \(tutorialContext.coreVersion)")
Spacer()
}
}
.padding()
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView(tutorialContext: HelloworldTutorialContext())
}
}

View File

@ -0,0 +1,62 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UIApplicationSceneManifest</key>
<dict>
<key>UIApplicationSupportsMultipleScenes</key>
<false/>
<key>UISceneConfigurations</key>
<dict>
<key>UIWindowSceneSessionRoleApplication</key>
<array>
<dict>
<key>UISceneConfigurationName</key>
<string>Default Configuration</string>
<key>UISceneDelegateClassName</key>
<string>$(PRODUCT_MODULE_NAME).SceneDelegate</string>
</dict>
</array>
</dict>
</dict>
<key>UIApplicationSupportsIndirectInputEvents</key>
<true/>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
</dict>
</plist>

View File

@ -1,19 +1,16 @@
//
// SceneDelegate.swift
// HelloLinphone
// Helloworld
//
// Created by Danmei Chen on 23/06/2020.
// Copyright © 2020 belledonne. All rights reserved.
// Created by QuentinArguillere on 08/09/2021.
//
import UIKit
import SwiftUI
import linphonesw
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
var mCore: Core!
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
@ -21,23 +18,17 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
// If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
// This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
// 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.
// On ios the Core doesn't need to have the application context to work.
try? mCore = factory.createCore(configPath: "", factoryConfigPath: "", systemContext: nil)
let delegate = UIApplication.shared.delegate as! AppDelegate
// Create the SwiftUI view that provides the window contents.
let contentView = ContentView(coreVersion: Core.getVersion)
let contentView = ContentView(tutorialContext: delegate.tutorialContext)
// Use a UIHostingController as window root view controller.
if let windowScene = scene as? UIWindowScene {
let window = UIWindow(windowScene: windowScene)
window.rootViewController = UIHostingController(rootView: contentView)
self.window = window
window.makeKeyAndVisible()
let window = UIWindow(windowScene: windowScene)
window.rootViewController = UIHostingController(rootView: contentView)
self.window = window
window.makeKeyAndVisible()
}
}
@ -45,7 +36,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
// Called as the scene is being released by the system.
// This occurs shortly after the scene enters the background, or when its session is discarded.
// Release any resources associated with this scene that can be re-created the next time the scene connects.
// The scene may re-connect later, as its session was not neccessarily discarded (see `application:didDiscardSceneSessions` instead).
// The scene may re-connect later, as its session was not necessarily discarded (see `application:didDiscardSceneSessions` instead).
}
func sceneDidBecomeActive(_ scene: UIScene) {

View File

@ -0,0 +1,24 @@
# Uncomment the next line to define a global platform for your project
platform :ios, '11.0'
source "https://gitlab.linphone.org/BC/public/podspec.git"
source "https://github.com/CocoaPods/Specs.git"
def basic_pods
if ENV['PODFILE_PATH'].nil?
pod 'linphone-sdk', '~> 5.0.0'
else
pod 'linphone-sdk', :path => ENV['PODFILE_PATH'] # local sdk
end
end
target 'Helloworld' do
# Comment the next line if you don't want to use dynamic frameworks
use_frameworks!
# Pods for Helloworld
basic_pods
end

View File

@ -0,0 +1,8 @@
Hello World tutorial
====================
The purpose of this tutorial is to explain how to add our SDK as a dependency of an Swift project and how to create the `Core` object that all our APIs depends on.
Start by taking a look at the `Podfile` file, and use the "pod install" command to create Helloworld.xcworkspace and build the app
The user interface will only display the `Core`'s version, but in the next tutorial you will learn how to use it to login your SIP account.

View File

@ -3,141 +3,143 @@
archiveVersion = 1;
classes = {
};
objectVersion = 50;
objectVersion = 51;
objects = {
/* Begin PBXBuildFile section */
6109A53324A1FB600080B339 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6109A53224A1FB600080B339 /* AppDelegate.swift */; };
6109A53524A1FB600080B339 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6109A53424A1FB600080B339 /* SceneDelegate.swift */; };
6109A53724A1FB600080B339 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6109A53624A1FB600080B339 /* ContentView.swift */; };
6109A53924A1FB610080B339 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 6109A53824A1FB610080B339 /* Assets.xcassets */; };
6109A53C24A1FB610080B339 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 6109A53B24A1FB610080B339 /* Preview Assets.xcassets */; };
6109A53F24A1FB610080B339 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 6109A53D24A1FB610080B339 /* LaunchScreen.storyboard */; };
77A49EE3C36F606277338500 /* Pods_HelloLinphone.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 77C78D4B854746B756F138EC /* Pods_HelloLinphone.framework */; };
43EDF804909F295D8FD16E72 /* Pods_LoginTutorial.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 52A14DA7DC9DFF94D4A572CE /* Pods_LoginTutorial.framework */; };
6604167924D4606A0064FC6C /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6604167824D4606A0064FC6C /* AppDelegate.swift */; };
6604167B24D4606A0064FC6C /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6604167A24D4606A0064FC6C /* SceneDelegate.swift */; };
6604167D24D4606A0064FC6C /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6604167C24D4606A0064FC6C /* ContentView.swift */; };
6604167F24D4606B0064FC6C /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 6604167E24D4606B0064FC6C /* Assets.xcassets */; };
6604168224D4606B0064FC6C /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 6604168124D4606B0064FC6C /* Preview Assets.xcassets */; };
6604168524D4606B0064FC6C /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 6604168324D4606B0064FC6C /* LaunchScreen.storyboard */; };
6604168D24D4607A0064FC6C /* LoginTutorial.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6604168C24D4607A0064FC6C /* LoginTutorial.swift */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
58D449155D749B90DECEF33F /* Pods-HelloLinphone.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-HelloLinphone.debug.xcconfig"; path = "Target Support Files/Pods-HelloLinphone/Pods-HelloLinphone.debug.xcconfig"; sourceTree = "<group>"; };
6109A52F24A1FB600080B339 /* HelloLinphone.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = HelloLinphone.app; sourceTree = BUILT_PRODUCTS_DIR; };
6109A53224A1FB600080B339 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
6109A53424A1FB600080B339 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = "<group>"; };
6109A53624A1FB600080B339 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
6109A53824A1FB610080B339 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
6109A53B24A1FB610080B339 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = "<group>"; };
6109A53E24A1FB610080B339 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
6109A54024A1FB610080B339 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
77C78D4B854746B756F138EC /* Pods_HelloLinphone.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_HelloLinphone.framework; sourceTree = BUILT_PRODUCTS_DIR; };
CCABD171CB8B5F0B32B7C819 /* Pods-HelloLinphone.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-HelloLinphone.release.xcconfig"; path = "Target Support Files/Pods-HelloLinphone/Pods-HelloLinphone.release.xcconfig"; sourceTree = "<group>"; };
52A14DA7DC9DFF94D4A572CE /* Pods_LoginTutorial.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_LoginTutorial.framework; sourceTree = BUILT_PRODUCTS_DIR; };
6604167524D4606A0064FC6C /* LoginTutorial.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = LoginTutorial.app; sourceTree = BUILT_PRODUCTS_DIR; };
6604167824D4606A0064FC6C /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
6604167A24D4606A0064FC6C /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = "<group>"; };
6604167C24D4606A0064FC6C /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
6604167E24D4606B0064FC6C /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
6604168124D4606B0064FC6C /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = "<group>"; };
6604168424D4606B0064FC6C /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
6604168624D4606B0064FC6C /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
6604168C24D4607A0064FC6C /* LoginTutorial.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginTutorial.swift; sourceTree = "<group>"; };
86EBE739D84760246FCA17F1 /* Pods-LoginTutorial.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-LoginTutorial.debug.xcconfig"; path = "Target Support Files/Pods-LoginTutorial/Pods-LoginTutorial.debug.xcconfig"; sourceTree = "<group>"; };
A3C6682B4C89F9C91835CCC0 /* Pods-LoginTutorial.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-LoginTutorial.release.xcconfig"; path = "Target Support Files/Pods-LoginTutorial/Pods-LoginTutorial.release.xcconfig"; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
6109A52C24A1FB600080B339 /* Frameworks */ = {
6604167224D4606A0064FC6C /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
77A49EE3C36F606277338500 /* Pods_HelloLinphone.framework in Frameworks */,
43EDF804909F295D8FD16E72 /* Pods_LoginTutorial.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
12F6903ABA1B6D98B920C503 /* Frameworks */ = {
0A22ADFA8AED8923D454874F /* Pods */ = {
isa = PBXGroup;
children = (
77C78D4B854746B756F138EC /* Pods_HelloLinphone.framework */,
86EBE739D84760246FCA17F1 /* Pods-LoginTutorial.debug.xcconfig */,
A3C6682B4C89F9C91835CCC0 /* Pods-LoginTutorial.release.xcconfig */,
);
path = Pods;
sourceTree = "<group>";
};
3200FE58EDFCC48A3C66678D /* Frameworks */ = {
isa = PBXGroup;
children = (
52A14DA7DC9DFF94D4A572CE /* Pods_LoginTutorial.framework */,
);
name = Frameworks;
sourceTree = "<group>";
};
6109A52624A1FB600080B339 = {
6604166C24D4606A0064FC6C = {
isa = PBXGroup;
children = (
6109A53124A1FB600080B339 /* HelloLinphone */,
6109A53024A1FB600080B339 /* Products */,
7DEB177E8F89B4C059EAD432 /* Pods */,
12F6903ABA1B6D98B920C503 /* Frameworks */,
6604167724D4606A0064FC6C /* LoginTutorial */,
6604167624D4606A0064FC6C /* Products */,
0A22ADFA8AED8923D454874F /* Pods */,
3200FE58EDFCC48A3C66678D /* Frameworks */,
);
sourceTree = "<group>";
};
6109A53024A1FB600080B339 /* Products */ = {
6604167624D4606A0064FC6C /* Products */ = {
isa = PBXGroup;
children = (
6109A52F24A1FB600080B339 /* HelloLinphone.app */,
6604167524D4606A0064FC6C /* LoginTutorial.app */,
);
name = Products;
sourceTree = "<group>";
};
6109A53124A1FB600080B339 /* HelloLinphone */ = {
6604167724D4606A0064FC6C /* LoginTutorial */ = {
isa = PBXGroup;
children = (
6109A53224A1FB600080B339 /* AppDelegate.swift */,
6109A53424A1FB600080B339 /* SceneDelegate.swift */,
6109A53624A1FB600080B339 /* ContentView.swift */,
6109A53824A1FB610080B339 /* Assets.xcassets */,
6109A53D24A1FB610080B339 /* LaunchScreen.storyboard */,
6109A54024A1FB610080B339 /* Info.plist */,
6109A53A24A1FB610080B339 /* Preview Content */,
6604167824D4606A0064FC6C /* AppDelegate.swift */,
6604167A24D4606A0064FC6C /* SceneDelegate.swift */,
6604168C24D4607A0064FC6C /* LoginTutorial.swift */,
6604167C24D4606A0064FC6C /* ContentView.swift */,
6604167E24D4606B0064FC6C /* Assets.xcassets */,
6604168324D4606B0064FC6C /* LaunchScreen.storyboard */,
6604168624D4606B0064FC6C /* Info.plist */,
6604168024D4606B0064FC6C /* Preview Content */,
);
path = HelloLinphone;
path = LoginTutorial;
sourceTree = "<group>";
};
6109A53A24A1FB610080B339 /* Preview Content */ = {
6604168024D4606B0064FC6C /* Preview Content */ = {
isa = PBXGroup;
children = (
6109A53B24A1FB610080B339 /* Preview Assets.xcassets */,
6604168124D4606B0064FC6C /* Preview Assets.xcassets */,
);
path = "Preview Content";
sourceTree = "<group>";
};
7DEB177E8F89B4C059EAD432 /* Pods */ = {
isa = PBXGroup;
children = (
58D449155D749B90DECEF33F /* Pods-HelloLinphone.debug.xcconfig */,
CCABD171CB8B5F0B32B7C819 /* Pods-HelloLinphone.release.xcconfig */,
);
name = Pods;
path = Pods;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
6109A52E24A1FB600080B339 /* HelloLinphone */ = {
6604167424D4606A0064FC6C /* LoginTutorial */ = {
isa = PBXNativeTarget;
buildConfigurationList = 6109A54324A1FB610080B339 /* Build configuration list for PBXNativeTarget "HelloLinphone" */;
buildConfigurationList = 6604168924D4606B0064FC6C /* Build configuration list for PBXNativeTarget "LoginTutorial" */;
buildPhases = (
F68B6F6ACD48C202C8DC4322 /* [CP] Check Pods Manifest.lock */,
6109A52B24A1FB600080B339 /* Sources */,
6109A52C24A1FB600080B339 /* Frameworks */,
6109A52D24A1FB600080B339 /* Resources */,
724EF46BD0CBE3A1971D77D0 /* [CP] Embed Pods Frameworks */,
4EF45A68E9E85663FD1A4EC6 /* [CP] Check Pods Manifest.lock */,
6604167124D4606A0064FC6C /* Sources */,
6604167224D4606A0064FC6C /* Frameworks */,
6604167324D4606A0064FC6C /* Resources */,
0131188DB5E696F536B36946 /* [CP] Embed Pods Frameworks */,
);
buildRules = (
);
dependencies = (
);
name = HelloLinphone;
productName = HelloLinphone;
productReference = 6109A52F24A1FB600080B339 /* HelloLinphone.app */;
name = LoginTutorial;
productName = LoginTutorial;
productReference = 6604167524D4606A0064FC6C /* LoginTutorial.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
6109A52724A1FB600080B339 /* Project object */ = {
6604166D24D4606A0064FC6C /* Project object */ = {
isa = PBXProject;
attributes = {
LastSwiftUpdateCheck = 1150;
LastUpgradeCheck = 1150;
ORGANIZATIONNAME = belledonne;
ORGANIZATIONNAME = BelledonneCommunications;
TargetAttributes = {
6109A52E24A1FB600080B339 = {
6604167424D4606A0064FC6C = {
CreatedOnToolsVersion = 11.5;
};
};
};
buildConfigurationList = 6109A52A24A1FB600080B339 /* Build configuration list for PBXProject "HelloLinphone" */;
buildConfigurationList = 6604167024D4606A0064FC6C /* Build configuration list for PBXProject "LoginTutorial" */;
compatibilityVersion = "Xcode 9.3";
developmentRegion = en;
hasScannedForEncodings = 0;
@ -145,48 +147,48 @@
en,
Base,
);
mainGroup = 6109A52624A1FB600080B339;
productRefGroup = 6109A53024A1FB600080B339 /* Products */;
mainGroup = 6604166C24D4606A0064FC6C;
productRefGroup = 6604167624D4606A0064FC6C /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
6109A52E24A1FB600080B339 /* HelloLinphone */,
6604167424D4606A0064FC6C /* LoginTutorial */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
6109A52D24A1FB600080B339 /* Resources */ = {
6604167324D4606A0064FC6C /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
6109A53F24A1FB610080B339 /* LaunchScreen.storyboard in Resources */,
6109A53C24A1FB610080B339 /* Preview Assets.xcassets in Resources */,
6109A53924A1FB610080B339 /* Assets.xcassets in Resources */,
6604168524D4606B0064FC6C /* LaunchScreen.storyboard in Resources */,
6604168224D4606B0064FC6C /* Preview Assets.xcassets in Resources */,
6604167F24D4606B0064FC6C /* Assets.xcassets in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
724EF46BD0CBE3A1971D77D0 /* [CP] Embed Pods Frameworks */ = {
0131188DB5E696F536B36946 /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-HelloLinphone/Pods-HelloLinphone-frameworks-${CONFIGURATION}-input-files.xcfilelist",
"${PODS_ROOT}/Target Support Files/Pods-LoginTutorial/Pods-LoginTutorial-frameworks-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Embed Pods Frameworks";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-HelloLinphone/Pods-HelloLinphone-frameworks-${CONFIGURATION}-output-files.xcfilelist",
"${PODS_ROOT}/Target Support Files/Pods-LoginTutorial/Pods-LoginTutorial-frameworks-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-HelloLinphone/Pods-HelloLinphone-frameworks.sh\"\n";
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-LoginTutorial/Pods-LoginTutorial-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
F68B6F6ACD48C202C8DC4322 /* [CP] Check Pods Manifest.lock */ = {
4EF45A68E9E85663FD1A4EC6 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
@ -201,7 +203,7 @@
outputFileListPaths = (
);
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-HelloLinphone-checkManifestLockResult.txt",
"$(DERIVED_FILE_DIR)/Pods-LoginTutorial-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
@ -211,23 +213,24 @@
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
6109A52B24A1FB600080B339 /* Sources */ = {
6604167124D4606A0064FC6C /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
6109A53324A1FB600080B339 /* AppDelegate.swift in Sources */,
6109A53524A1FB600080B339 /* SceneDelegate.swift in Sources */,
6109A53724A1FB600080B339 /* ContentView.swift in Sources */,
6604167924D4606A0064FC6C /* AppDelegate.swift in Sources */,
6604167B24D4606A0064FC6C /* SceneDelegate.swift in Sources */,
6604168D24D4607A0064FC6C /* LoginTutorial.swift in Sources */,
6604167D24D4606A0064FC6C /* ContentView.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXVariantGroup section */
6109A53D24A1FB610080B339 /* LaunchScreen.storyboard */ = {
6604168324D4606B0064FC6C /* LaunchScreen.storyboard */ = {
isa = PBXVariantGroup;
children = (
6109A53E24A1FB610080B339 /* Base */,
6604168424D4606B0064FC6C /* Base */,
);
name = LaunchScreen.storyboard;
sourceTree = "<group>";
@ -235,7 +238,7 @@
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
6109A54124A1FB610080B339 /* Debug */ = {
6604168724D4606B0064FC6C /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
@ -295,7 +298,7 @@
};
name = Debug;
};
6109A54224A1FB610080B339 /* Release */ = {
6604168824D4606B0064FC6C /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
@ -349,42 +352,42 @@
};
name = Release;
};
6109A54424A1FB610080B339 /* Debug */ = {
6604168A24D4606B0064FC6C /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 58D449155D749B90DECEF33F /* Pods-HelloLinphone.debug.xcconfig */;
baseConfigurationReference = 86EBE739D84760246FCA17F1 /* Pods-LoginTutorial.debug.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_ASSET_PATHS = "\"HelloLinphone/Preview Content\"";
DEVELOPMENT_ASSET_PATHS = "\"LoginTutorial/Preview Content\"";
DEVELOPMENT_TEAM = Z2V957B3D6;
ENABLE_PREVIEWS = YES;
INFOPLIST_FILE = HelloLinphone/Info.plist;
INFOPLIST_FILE = LoginTutorial/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = belledonne.HelloLinphone;
PRODUCT_BUNDLE_IDENTIFIER = BC.LoginTutorial;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
6109A54524A1FB610080B339 /* Release */ = {
6604168B24D4606B0064FC6C /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = CCABD171CB8B5F0B32B7C819 /* Pods-HelloLinphone.release.xcconfig */;
baseConfigurationReference = A3C6682B4C89F9C91835CCC0 /* Pods-LoginTutorial.release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_ASSET_PATHS = "\"HelloLinphone/Preview Content\"";
DEVELOPMENT_ASSET_PATHS = "\"LoginTutorial/Preview Content\"";
DEVELOPMENT_TEAM = Z2V957B3D6;
ENABLE_PREVIEWS = YES;
INFOPLIST_FILE = HelloLinphone/Info.plist;
INFOPLIST_FILE = LoginTutorial/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = belledonne.HelloLinphone;
PRODUCT_BUNDLE_IDENTIFIER = BC.LoginTutorial;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
@ -394,25 +397,25 @@
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
6109A52A24A1FB600080B339 /* Build configuration list for PBXProject "HelloLinphone" */ = {
6604167024D4606A0064FC6C /* Build configuration list for PBXProject "LoginTutorial" */ = {
isa = XCConfigurationList;
buildConfigurations = (
6109A54124A1FB610080B339 /* Debug */,
6109A54224A1FB610080B339 /* Release */,
6604168724D4606B0064FC6C /* Debug */,
6604168824D4606B0064FC6C /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
6109A54324A1FB610080B339 /* Build configuration list for PBXNativeTarget "HelloLinphone" */ = {
6604168924D4606B0064FC6C /* Build configuration list for PBXNativeTarget "LoginTutorial" */ = {
isa = XCConfigurationList;
buildConfigurations = (
6109A54424A1FB610080B339 /* Debug */,
6109A54524A1FB610080B339 /* Release */,
6604168A24D4606B0064FC6C /* Debug */,
6604168B24D4606B0064FC6C /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 6109A52724A1FB600080B339 /* Project object */;
rootObject = 6604166D24D4606A0064FC6C /* Project object */;
}

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:LoginTutorial.xcodeproj">
</FileRef>
</Workspace>

View File

@ -0,0 +1,38 @@
//
// AppDelegate.swift
// LoginTutorial
//
// Created by QuentinArguillere on 31/07/2020.
// Copyright © 2020 BelledonneCommunications. All rights reserved.
//
import UIKit
import SwiftUI
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
@ObservedObject var tutorialContext = LoginTutorialContext()
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
return true
}
// MARK: UISceneSession Lifecycle
func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
// Called when a new scene session is being created.
// Use this method to select a configuration to create the new scene with.
return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
}
func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {
// Called when the user discards a scene session.
// If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
// Use this method to release any resources that were specific to the discarded scenes, as they will not return.
}
}

View File

@ -0,0 +1,98 @@
{
"images" : [
{
"idiom" : "iphone",
"scale" : "2x",
"size" : "20x20"
},
{
"idiom" : "iphone",
"scale" : "3x",
"size" : "20x20"
},
{
"idiom" : "iphone",
"scale" : "2x",
"size" : "29x29"
},
{
"idiom" : "iphone",
"scale" : "3x",
"size" : "29x29"
},
{
"idiom" : "iphone",
"scale" : "2x",
"size" : "40x40"
},
{
"idiom" : "iphone",
"scale" : "3x",
"size" : "40x40"
},
{
"idiom" : "iphone",
"scale" : "2x",
"size" : "60x60"
},
{
"idiom" : "iphone",
"scale" : "3x",
"size" : "60x60"
},
{
"idiom" : "ipad",
"scale" : "1x",
"size" : "20x20"
},
{
"idiom" : "ipad",
"scale" : "2x",
"size" : "20x20"
},
{
"idiom" : "ipad",
"scale" : "1x",
"size" : "29x29"
},
{
"idiom" : "ipad",
"scale" : "2x",
"size" : "29x29"
},
{
"idiom" : "ipad",
"scale" : "1x",
"size" : "40x40"
},
{
"idiom" : "ipad",
"scale" : "2x",
"size" : "40x40"
},
{
"idiom" : "ipad",
"scale" : "1x",
"size" : "76x76"
},
{
"idiom" : "ipad",
"scale" : "2x",
"size" : "76x76"
},
{
"idiom" : "ipad",
"scale" : "2x",
"size" : "83.5x83.5"
},
{
"idiom" : "ios-marketing",
"scale" : "1x",
"size" : "1024x1024"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13122.16" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13104.12"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="EHf-IW-A2E">
<objects>
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" xcode11CocoaTouchSystemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="53" y="375"/>
</scene>
</scenes>
</document>

View File

@ -0,0 +1,87 @@
//
// ContentView.swift
// LoginTutorial
//
// Created by QuentinArguillere on 31/07/2020.
// Copyright © 2020 BelledonneCommunications. All rights reserved.
//
import SwiftUI
struct ContentView: View {
@ObservedObject var tutorialContext : LoginTutorialContext
var body: some View {
VStack {
Group {
HStack {
Text("Username:")
.font(.title)
TextField("", text : $tutorialContext.username)
.textFieldStyle(RoundedBorderTextFieldStyle())
.disabled(tutorialContext.loggedIn)
}
HStack {
Text("Password:")
.font(.title)
TextField("", text : $tutorialContext.passwd)
.textFieldStyle(RoundedBorderTextFieldStyle())
.disabled(tutorialContext.loggedIn)
}
HStack {
Text("Domain:")
.font(.title)
TextField("", text : $tutorialContext.domain)
.textFieldStyle(RoundedBorderTextFieldStyle())
.disabled(tutorialContext.loggedIn)
}
Picker(selection: $tutorialContext.transportType, label: Text("Transport:")) {
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()
self.tutorialContext.delete()
} 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()
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView(tutorialContext: LoginTutorialContext())
}
}

View File

@ -0,0 +1,128 @@
//
// LoginExample.swift
// LoginTutorial
//
// Created by QuentinArguillere on 08/09/2021.
// Copyright © 2021 BelledonneCommunications. All rights reserved.
//
import linphonesw
class LoginTutorialContext : 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()
{
LoggingService.Instance.logLevel = LogLevel.Debug
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
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)
}
func login() {
do {
// Get the transport protocol to use.
// TLS is strongly recommended
// Only use UDP if you don't have the choice
var transport : TransportType
if (transportType == "TLS") { transport = TransportType.Tls }
else if (transportType == "TCP") { transport = TransportType.Tcp }
else { transport = TransportType.Udp }
// To configure a SIP account, we need an Account object and an AuthInfo object
// The first one is how to connect to the proxy server, the second one stores the credentials
// The auth info can be created from the Factory as it's only a data class
// userID is set to null as it's the same as the username in our case
// 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()
{
// Here we will disable the registration of our Account
if let account = mCore.defaultAccount {
let params = account.params
// Returned params object is const, so to make changes we first need to clone it
let clonedParams = params?.clone()
// Now let's make our changes
clonedParams?.registerEnabled = false
// 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

@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,65 @@
//
// SceneDelegate.swift
// LoginTutorial
//
// Created by QuentinArguillere on 31/07/2020.
// Copyright © 2020 BelledonneCommunications. All rights reserved.
//
import UIKit
import SwiftUI
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
// Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
// If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
// This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
let delegate = UIApplication.shared.delegate as! AppDelegate
// Create the SwiftUI view that provides the window contents.
let contentView = ContentView(tutorialContext: delegate.tutorialContext)
// Use a UIHostingController as window root view controller.
if let windowScene = scene as? UIWindowScene {
let window = UIWindow(windowScene: windowScene)
window.rootViewController = UIHostingController(rootView: contentView)
self.window = window
window.makeKeyAndVisible()
}
}
func sceneDidDisconnect(_ scene: UIScene) {
// Called as the scene is being released by the system.
// This occurs shortly after the scene enters the background, or when its session is discarded.
// Release any resources associated with this scene that can be re-created the next time the scene connects.
// The scene may re-connect later, as its session was not neccessarily discarded (see `application:didDiscardSceneSessions` instead).
}
func sceneDidBecomeActive(_ scene: UIScene) {
// Called when the scene has moved from an inactive state to an active state.
// Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive.
}
func sceneWillResignActive(_ scene: UIScene) {
// Called when the scene will move from an active state to an inactive state.
// This may occur due to temporary interruptions (ex. an incoming phone call).
}
func sceneWillEnterForeground(_ scene: UIScene) {
// Called as the scene transitions from the background to the foreground.
// Use this method to undo the changes made on entering the background.
}
func sceneDidEnterBackground(_ scene: UIScene) {
// Called as the scene transitions from the foreground to the background.
// Use this method to save data, release shared resources, and store enough scene-specific state information
// to restore the scene back to its current state.
}
}

View File

@ -0,0 +1,24 @@
# Uncomment the next line to define a global platform for your project
platform :ios, '11.0'
source "https://gitlab.linphone.org/BC/public/podspec.git"
source "https://github.com/CocoaPods/Specs.git"
def basic_pods
if ENV['PODFILE_PATH'].nil?
pod 'linphone-sdk', '~> 5.0.0'
else
pod 'linphone-sdk', :path => ENV['PODFILE_PATH'] # local sdk
end
end
target 'LoginTutorial' do
# Comment the next line if you don't want to use dynamic frameworks
use_frameworks!
# Pods for LoginTutorial
basic_pods
end

View File

@ -0,0 +1,10 @@
Account login tutorial
====================
Now that you have set up Linphone-SDK in a Swift project, let's start using it.
We will see how to login on a SIP server using the `Core` object instanciated in the previous tutorial. As always, you will have to start with a "Pod install" to generate the xcworkspace for this app.
If you don't have a SIP server yet, you can create an account for free using our [free SIP service](https://subscribe.linphone.org/).
Once you'll be logged-in, you'll be able to continue to the next tutorials to make calls and send messages.

View File

@ -0,0 +1,424 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 51;
objects = {
/* Begin PBXBuildFile section */
203CAD3D220FB2AEDD1C2463 /* Pods_IncomingCall.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1F200A240C9F5BF773AF9024 /* Pods_IncomingCall.framework */; };
66BF265826E9FCC90048D176 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66BF265726E9FCC90048D176 /* AppDelegate.swift */; };
66BF265A26E9FCC90048D176 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66BF265926E9FCC90048D176 /* SceneDelegate.swift */; };
66BF265C26E9FCC90048D176 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66BF265B26E9FCC90048D176 /* ContentView.swift */; };
66BF265E26E9FCD00048D176 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 66BF265D26E9FCD00048D176 /* Assets.xcassets */; };
66BF266126E9FCD00048D176 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 66BF266026E9FCD00048D176 /* Preview Assets.xcassets */; };
66BF266426E9FCD00048D176 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 66BF266226E9FCD00048D176 /* LaunchScreen.storyboard */; };
66BF266C26EA003C0048D176 /* IncomingCall.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66BF266B26EA003C0048D176 /* IncomingCall.swift */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
1F200A240C9F5BF773AF9024 /* Pods_IncomingCall.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_IncomingCall.framework; sourceTree = BUILT_PRODUCTS_DIR; };
4B0753133024A1C67E84CE1C /* Pods-IncomingCall.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-IncomingCall.debug.xcconfig"; path = "Target Support Files/Pods-IncomingCall/Pods-IncomingCall.debug.xcconfig"; sourceTree = "<group>"; };
4B835D0562384FC441266A58 /* Pods-IncomingCall.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-IncomingCall.release.xcconfig"; path = "Target Support Files/Pods-IncomingCall/Pods-IncomingCall.release.xcconfig"; sourceTree = "<group>"; };
66BF265426E9FCC90048D176 /* IncomingCall.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = IncomingCall.app; sourceTree = BUILT_PRODUCTS_DIR; };
66BF265726E9FCC90048D176 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
66BF265926E9FCC90048D176 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = "<group>"; };
66BF265B26E9FCC90048D176 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
66BF265D26E9FCD00048D176 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
66BF266026E9FCD00048D176 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = "<group>"; };
66BF266326E9FCD00048D176 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
66BF266526E9FCD00048D176 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
66BF266B26EA003C0048D176 /* IncomingCall.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IncomingCall.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
66BF265126E9FCC90048D176 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
203CAD3D220FB2AEDD1C2463 /* Pods_IncomingCall.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
521BEF3D6EE06929A69119A3 /* Frameworks */ = {
isa = PBXGroup;
children = (
1F200A240C9F5BF773AF9024 /* Pods_IncomingCall.framework */,
);
name = Frameworks;
sourceTree = "<group>";
};
66BF264B26E9FCC90048D176 = {
isa = PBXGroup;
children = (
66BF265626E9FCC90048D176 /* IncomingCall */,
66BF265526E9FCC90048D176 /* Products */,
8ED5175BFA4F710BD04F20C6 /* Pods */,
521BEF3D6EE06929A69119A3 /* Frameworks */,
);
sourceTree = "<group>";
};
66BF265526E9FCC90048D176 /* Products */ = {
isa = PBXGroup;
children = (
66BF265426E9FCC90048D176 /* IncomingCall.app */,
);
name = Products;
sourceTree = "<group>";
};
66BF265626E9FCC90048D176 /* IncomingCall */ = {
isa = PBXGroup;
children = (
66BF265726E9FCC90048D176 /* AppDelegate.swift */,
66BF265926E9FCC90048D176 /* SceneDelegate.swift */,
66BF265B26E9FCC90048D176 /* ContentView.swift */,
66BF265D26E9FCD00048D176 /* Assets.xcassets */,
66BF266226E9FCD00048D176 /* LaunchScreen.storyboard */,
66BF266526E9FCD00048D176 /* Info.plist */,
66BF266B26EA003C0048D176 /* IncomingCall.swift */,
66BF265F26E9FCD00048D176 /* Preview Content */,
);
path = IncomingCall;
sourceTree = "<group>";
};
66BF265F26E9FCD00048D176 /* Preview Content */ = {
isa = PBXGroup;
children = (
66BF266026E9FCD00048D176 /* Preview Assets.xcassets */,
);
path = "Preview Content";
sourceTree = "<group>";
};
8ED5175BFA4F710BD04F20C6 /* Pods */ = {
isa = PBXGroup;
children = (
4B0753133024A1C67E84CE1C /* Pods-IncomingCall.debug.xcconfig */,
4B835D0562384FC441266A58 /* Pods-IncomingCall.release.xcconfig */,
);
path = Pods;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
66BF265326E9FCC90048D176 /* IncomingCall */ = {
isa = PBXNativeTarget;
buildConfigurationList = 66BF266826E9FCD00048D176 /* Build configuration list for PBXNativeTarget "IncomingCall" */;
buildPhases = (
1A16B6EAABD03BDFB6DDFABD /* [CP] Check Pods Manifest.lock */,
66BF265026E9FCC90048D176 /* Sources */,
66BF265126E9FCC90048D176 /* Frameworks */,
66BF265226E9FCC90048D176 /* Resources */,
37BA1CF9C1962947CFE3234B /* [CP] Embed Pods Frameworks */,
);
buildRules = (
);
dependencies = (
);
name = IncomingCall;
productName = IncomingCall;
productReference = 66BF265426E9FCC90048D176 /* IncomingCall.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
66BF264C26E9FCC90048D176 /* Project object */ = {
isa = PBXProject;
attributes = {
LastSwiftUpdateCheck = 1250;
LastUpgradeCheck = 1250;
TargetAttributes = {
66BF265326E9FCC90048D176 = {
CreatedOnToolsVersion = 12.5.1;
};
};
};
buildConfigurationList = 66BF264F26E9FCC90048D176 /* Build configuration list for PBXProject "IncomingCall" */;
compatibilityVersion = "Xcode 9.3";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = 66BF264B26E9FCC90048D176;
productRefGroup = 66BF265526E9FCC90048D176 /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
66BF265326E9FCC90048D176 /* IncomingCall */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
66BF265226E9FCC90048D176 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
66BF266426E9FCD00048D176 /* LaunchScreen.storyboard in Resources */,
66BF266126E9FCD00048D176 /* Preview Assets.xcassets in Resources */,
66BF265E26E9FCD00048D176 /* Assets.xcassets in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
1A16B6EAABD03BDFB6DDFABD /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputFileListPaths = (
);
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-IncomingCall-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
37BA1CF9C1962947CFE3234B /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-IncomingCall/Pods-IncomingCall-frameworks-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Embed Pods Frameworks";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-IncomingCall/Pods-IncomingCall-frameworks-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-IncomingCall/Pods-IncomingCall-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
66BF265026E9FCC90048D176 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
66BF265826E9FCC90048D176 /* AppDelegate.swift in Sources */,
66BF266C26EA003C0048D176 /* IncomingCall.swift in Sources */,
66BF265A26E9FCC90048D176 /* SceneDelegate.swift in Sources */,
66BF265C26E9FCC90048D176 /* ContentView.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXVariantGroup section */
66BF266226E9FCD00048D176 /* LaunchScreen.storyboard */ = {
isa = PBXVariantGroup;
children = (
66BF266326E9FCD00048D176 /* Base */,
);
name = LaunchScreen.storyboard;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
66BF266626E9FCD00048D176 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 14.5;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
};
name = Debug;
};
66BF266726E9FCD00048D176 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 14.5;
MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES;
SDKROOT = iphoneos;
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_OPTIMIZATION_LEVEL = "-O";
VALIDATE_PRODUCT = YES;
};
name = Release;
};
66BF266926E9FCD00048D176 /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 4B0753133024A1C67E84CE1C /* Pods-IncomingCall.debug.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_ASSET_PATHS = "\"IncomingCall/Preview Content\"";
DEVELOPMENT_TEAM = Z2V957B3D6;
ENABLE_PREVIEWS = YES;
INFOPLIST_FILE = IncomingCall/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = BC.IncomingCall;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
66BF266A26E9FCD00048D176 /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 4B835D0562384FC441266A58 /* Pods-IncomingCall.release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_ASSET_PATHS = "\"IncomingCall/Preview Content\"";
DEVELOPMENT_TEAM = Z2V957B3D6;
ENABLE_PREVIEWS = YES;
INFOPLIST_FILE = IncomingCall/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = BC.IncomingCall;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
66BF264F26E9FCC90048D176 /* Build configuration list for PBXProject "IncomingCall" */ = {
isa = XCConfigurationList;
buildConfigurations = (
66BF266626E9FCD00048D176 /* Debug */,
66BF266726E9FCD00048D176 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
66BF266826E9FCD00048D176 /* Build configuration list for PBXNativeTarget "IncomingCall" */ = {
isa = XCConfigurationList;
buildConfigurations = (
66BF266926E9FCD00048D176 /* Debug */,
66BF266A26E9FCD00048D176 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 66BF264C26E9FCC90048D176 /* Project object */;
}

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:">
</FileRef>
</Workspace>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>

View File

@ -0,0 +1,38 @@
//
// AppDelegate.swift
// LoginTutorial
//
// Created by QuentinArguillere on 31/07/2020.
// Copyright © 2020 BelledonneCommunications. All rights reserved.
//
import UIKit
import SwiftUI
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
@ObservedObject var tutorialContext = IncomingCallTutorialContext()
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
return true
}
// MARK: UISceneSession Lifecycle
func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
// Called when a new scene session is being created.
// Use this method to select a configuration to create the new scene with.
return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
}
func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {
// Called when the user discards a scene session.
// If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
// Use this method to release any resources that were specific to the discarded scenes, as they will not return.
}
}

View File

@ -0,0 +1,11 @@
{
"colors" : [
{
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,98 @@
{
"images" : [
{
"idiom" : "iphone",
"scale" : "2x",
"size" : "20x20"
},
{
"idiom" : "iphone",
"scale" : "3x",
"size" : "20x20"
},
{
"idiom" : "iphone",
"scale" : "2x",
"size" : "29x29"
},
{
"idiom" : "iphone",
"scale" : "3x",
"size" : "29x29"
},
{
"idiom" : "iphone",
"scale" : "2x",
"size" : "40x40"
},
{
"idiom" : "iphone",
"scale" : "3x",
"size" : "40x40"
},
{
"idiom" : "iphone",
"scale" : "2x",
"size" : "60x60"
},
{
"idiom" : "iphone",
"scale" : "3x",
"size" : "60x60"
},
{
"idiom" : "ipad",
"scale" : "1x",
"size" : "20x20"
},
{
"idiom" : "ipad",
"scale" : "2x",
"size" : "20x20"
},
{
"idiom" : "ipad",
"scale" : "1x",
"size" : "29x29"
},
{
"idiom" : "ipad",
"scale" : "2x",
"size" : "29x29"
},
{
"idiom" : "ipad",
"scale" : "1x",
"size" : "40x40"
},
{
"idiom" : "ipad",
"scale" : "2x",
"size" : "40x40"
},
{
"idiom" : "ipad",
"scale" : "1x",
"size" : "76x76"
},
{
"idiom" : "ipad",
"scale" : "2x",
"size" : "76x76"
},
{
"idiom" : "ipad",
"scale" : "2x",
"size" : "83.5x83.5"
},
{
"idiom" : "ios-marketing",
"scale" : "1x",
"size" : "1024x1024"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13122.16" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13104.12"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="EHf-IW-A2E">
<objects>
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" xcode11CocoaTouchSystemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="53" y="375"/>
</scene>
</scenes>
</document>

View File

@ -0,0 +1,149 @@
//
// ContentView.swift
// IncomingCall tutorial
//
// Created by QuentinArguillere on 09/09/2021.
// Copyright © 2021 BelledonneCommunications. All rights reserved.
//
import SwiftUI
struct ContentView: View {
@ObservedObject var tutorialContext : IncomingCallTutorialContext
func callStateString() -> String {
if (tutorialContext.isCallRunning) {
return "Call running"
} else if (tutorialContext.isCallIncoming) {
return "Incoming call"
} else {
return "No Call"
}
}
var body: some View {
VStack {
Group {
HStack {
Text("Username:")
.font(.title)
TextField("", text : $tutorialContext.username)
.textFieldStyle(RoundedBorderTextFieldStyle())
.disabled(tutorialContext.loggedIn)
}
HStack {
Text("Password:")
.font(.title)
TextField("", text : $tutorialContext.passwd)
.textFieldStyle(RoundedBorderTextFieldStyle())
.disabled(tutorialContext.loggedIn)
}
HStack {
Text("Domain:")
.font(.title)
TextField("", text : $tutorialContext.domain)
.textFieldStyle(RoundedBorderTextFieldStyle())
.disabled(tutorialContext.loggedIn)
}
Picker(selection: $tutorialContext.transportType, label: Text("Transport:")) {
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()
self.tutorialContext.delete()
} 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)
}
VStack {
HStack {
Button(action: {
if (self.tutorialContext.isCallIncoming) {
self.tutorialContext.acceptCall()
} else if (self.tutorialContext.isCallRunning){
self.tutorialContext.terminateCall()
}
})
{
Text( (tutorialContext.isCallRunning) ? "Terminate" : "Accept")
.font(.largeTitle)
.foregroundColor(Color.white)
.frame(width: 180.0, height: 42.0)
.background(Color.gray)
}
.disabled(!tutorialContext.isCallIncoming && !tutorialContext.isCallRunning)
HStack {
Text(callStateString()).italic()
Spacer()
}
}
HStack {
Text("Caller:").font(.title).underline()
Text(tutorialContext.remoteAddress)
Spacer()
}.padding(.top, 5)
HStack {
Text("Call msg:").font(.title3).underline()
Text(tutorialContext.callMsg)
Spacer()
}.padding(.top, 5)
HStack {
Button(action: tutorialContext.toggleSpeaker)
{
Text((tutorialContext.isSpeakerEnabled) ? "Speaker OFF" : "Speaker ON")
.font(.title3)
.foregroundColor(Color.white)
.frame(width: 140.0, height: 42.0)
.background(Color.gray)
}
.disabled(!tutorialContext.isCallRunning)
Button(action: tutorialContext.muteMicrophone)
{
Text((tutorialContext.isMicrophoneEnabled) ? "Microphone OFF" : "Microphone ON")
.font(.title3)
.foregroundColor(Color.white)
.frame(width: 160.0, height: 42.0)
.background(Color.gray)
}
.disabled(!tutorialContext.isCallRunning)
}.padding(.top, 10)
}.padding(.top, 30)
}
Group {
Spacer()
Text("Core Version is \(tutorialContext.coreVersion)")
}
}
.padding()
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView(tutorialContext: IncomingCallTutorialContext())
}
}

View File

@ -0,0 +1,162 @@
//
// IncomingCall.swift
// IncomingCall tutorial
//
// Created by QuentinArguillere on 08/09/2021.
// Copyright © 2021 BelledonneCommunications. All rights reserved.
//
import linphonesw
class IncomingCallTutorialContext : ObservableObject
{
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 domain : String = "sip.example.org"
@Published var loggedIn: Bool = false
@Published var transportType : String = "TLS"
// Incoming call related variables
@Published var callMsg : String = ""
@Published var isCallIncoming : Bool = false
@Published var isCallRunning : Bool = false
@Published var remoteAddress : String = "Nobody yet"
@Published var isSpeakerEnabled : Bool = false
@Published var isMicrophoneEnabled : Bool = false
init()
{
LoggingService.Instance.logLevel = LogLevel.Debug
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
if (state == .IncomingReceived) { // When a call is received
self.isCallIncoming = true
self.isCallRunning = false
self.remoteAddress = call.remoteAddress!.asStringUriOnly()
} else if (state == .Connected) { // When a call is over
self.isCallIncoming = false
self.isCallRunning = true
} else if (state == .Released) { // When a call is over
self.isCallIncoming = false
self.isCallRunning = false
self.remoteAddress = "Nobody yet"
}
}, onAudioDeviceChanged: { (core: Core, device: AudioDevice) in
// This callback will be triggered when a successful audio device has been changed
}, onAudioDevicesListUpdated: { (core: Core) in
// This callback will be triggered when the available devices list has changed,
// for example after a bluetooth headset has been connected/disconnected.
}, 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")
if (state == .Ok) {
self.loggedIn = true
} else if (state == .Cleared) {
self.loggedIn = false
}
})
mCore.addDelegate(delegate: mCoreDelegate)
}
func login() {
do {
var transport : TransportType
if (transportType == "TLS") { transport = TransportType.Tls }
else if (transportType == "TCP") { transport = TransportType.Tcp }
else { transport = TransportType.Udp }
let authInfo = try Factory.Instance.createAuthInfo(username: username, userid: "", passwd: passwd, ha1: "", realm: "", domain: domain)
let accountParams = try mCore.createAccountParams()
let identity = try Factory.Instance.createAddress(addr: String("sip:" + username + "@" + domain))
try! accountParams.setIdentityaddress(newValue: identity)
let address = try Factory.Instance.createAddress(addr: String("sip:" + domain))
try address.setTransport(newValue: transport)
try accountParams.setServeraddress(newValue: address)
accountParams.registerEnabled = true
mAccount = try mCore.createAccount(params: accountParams)
mCore.addAuthInfo(info: authInfo)
try mCore.addAccount(account: mAccount!)
mCore.defaultAccount = mAccount
} catch { NSLog(error.localizedDescription) }
}
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)
mCore.clearAccounts()
mCore.clearAllAuthInfo()
}
}
func terminateCall() {
do {
// Terminates the call, whether it is ringing or running
try mCore.currentCall?.terminate()
} catch { NSLog(error.localizedDescription) }
}
func acceptCall() {
// IMPORTANT : Make sure you allowed the use of the microphone (see key "Privacy - Microphone usage description" in Info.plist) !
do {
// if we wanted, we could create a CallParams object
// and answer using this object to make changes to the call configuration
// (see OutgoingCall tutorial)
try mCore.currentCall?.accept()
} catch { NSLog(error.localizedDescription) }
}
func muteMicrophone() {
// The following toggles the microphone, disabling completely / enabling the sound capture
// from the device microphone
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
for audioDevice in mCore.audioDevices {
// For IOS, the Speaker is an exception, Linphone cannot differentiate Input and Output.
// This means that the default output device, the earpiece, is paired with the default phone microphone.
// Setting the output audio device to the microphone will redirect the sound to the earpiece.
if (speakerEnabled && audioDevice.type == AudioDeviceType.Microphone) {
mCore.currentCall?.outputAudioDevice = audioDevice
isSpeakerEnabled = false
return
} else if (!speakerEnabled && audioDevice.type == AudioDeviceType.Speaker) {
mCore.currentCall?.outputAudioDevice = audioDevice
isSpeakerEnabled = true
return
}
/* If we wanted to route the audio to a bluetooth headset
else if (audioDevice.type == AudioDevice.Type.Bluetooth) {
core.currentCall?.outputAudioDevice = audioDevice
}*/
}
}
}

View File

@ -0,0 +1,64 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>NSMicrophoneUsageDescription</key>
<string>Microphone access</string>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UIApplicationSceneManifest</key>
<dict>
<key>UIApplicationSupportsMultipleScenes</key>
<false/>
<key>UISceneConfigurations</key>
<dict>
<key>UIWindowSceneSessionRoleApplication</key>
<array>
<dict>
<key>UISceneConfigurationName</key>
<string>Default Configuration</string>
<key>UISceneDelegateClassName</key>
<string>$(PRODUCT_MODULE_NAME).SceneDelegate</string>
</dict>
</array>
</dict>
</dict>
<key>UIApplicationSupportsIndirectInputEvents</key>
<true/>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
</dict>
</plist>

View File

@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,65 @@
//
// SceneDelegate.swift
// LoginTutorial
//
// Created by QuentinArguillere on 31/07/2020.
// Copyright © 2020 BelledonneCommunications. All rights reserved.
//
import UIKit
import SwiftUI
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
// Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
// If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
// This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
let delegate = UIApplication.shared.delegate as! AppDelegate
// Create the SwiftUI view that provides the window contents.
let contentView = ContentView(tutorialContext: delegate.tutorialContext)
// Use a UIHostingController as window root view controller.
if let windowScene = scene as? UIWindowScene {
let window = UIWindow(windowScene: windowScene)
window.rootViewController = UIHostingController(rootView: contentView)
self.window = window
window.makeKeyAndVisible()
}
}
func sceneDidDisconnect(_ scene: UIScene) {
// Called as the scene is being released by the system.
// This occurs shortly after the scene enters the background, or when its session is discarded.
// Release any resources associated with this scene that can be re-created the next time the scene connects.
// The scene may re-connect later, as its session was not neccessarily discarded (see `application:didDiscardSceneSessions` instead).
}
func sceneDidBecomeActive(_ scene: UIScene) {
// Called when the scene has moved from an inactive state to an active state.
// Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive.
}
func sceneWillResignActive(_ scene: UIScene) {
// Called when the scene will move from an active state to an inactive state.
// This may occur due to temporary interruptions (ex. an incoming phone call).
}
func sceneWillEnterForeground(_ scene: UIScene) {
// Called as the scene transitions from the background to the foreground.
// Use this method to undo the changes made on entering the background.
}
func sceneDidEnterBackground(_ scene: UIScene) {
// Called as the scene transitions from the foreground to the background.
// Use this method to save data, release shared resources, and store enough scene-specific state information
// to restore the scene back to its current state.
}
}

View File

@ -0,0 +1,24 @@
# Uncomment the next line to define a global platform for your project
platform :ios, '11.0'
source "https://gitlab.linphone.org/BC/public/podspec.git"
source "https://github.com/CocoaPods/Specs.git"
def basic_pods
if ENV['PODFILE_PATH'].nil?
pod 'linphone-sdk', '~> 5.0.0'
else
pod 'linphone-sdk', :path => ENV['PODFILE_PATH'] # local sdk
end
end
target 'IncomingCall' do
# Comment the next line if you don't want to use dynamic frameworks
use_frameworks!
# Pods for IncomingCall
basic_pods
end

View File

@ -0,0 +1,10 @@
Incoming call tutorial
====================
This tutorial will focus on how the app will be notified when a call is being received and how to either accept it or terminate it.
We'll also cover how to toggle the microphone and the speakerphone during an active call.
If you want to test it on either a device or an emulator, you'll need another SIP client to make the call. If you don't, you can use the [outgoing call tutorial](https://gitlab.linphone.org/BC/public/tutorials/-/tree/master/ios/swift/3-OutgoingCall) to do it.
Note that changes to the "info.plist" file were made to enable the iphone microphone

View File

@ -0,0 +1,424 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 51;
objects = {
/* Begin PBXBuildFile section */
664BC0C126EA4E6D007EE298 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 664BC0C026EA4E6D007EE298 /* AppDelegate.swift */; };
664BC0C326EA4E6D007EE298 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 664BC0C226EA4E6D007EE298 /* SceneDelegate.swift */; };
664BC0C526EA4E6D007EE298 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 664BC0C426EA4E6D007EE298 /* ContentView.swift */; };
664BC0C726EA4E6E007EE298 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 664BC0C626EA4E6E007EE298 /* Assets.xcassets */; };
664BC0CA26EA4E6E007EE298 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 664BC0C926EA4E6E007EE298 /* Preview Assets.xcassets */; };
664BC0CD26EA4E6E007EE298 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 664BC0CB26EA4E6E007EE298 /* LaunchScreen.storyboard */; };
664BC0D526EA5B2E007EE298 /* OutgoingCall.swift in Sources */ = {isa = PBXBuildFile; fileRef = 664BC0D426EA5B2E007EE298 /* OutgoingCall.swift */; };
CCE49B664A0761B2077AA237 /* Pods_OutgoingCall.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0E3E7DFC01F4CC7F8D82420D /* Pods_OutgoingCall.framework */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
0E3E7DFC01F4CC7F8D82420D /* Pods_OutgoingCall.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_OutgoingCall.framework; sourceTree = BUILT_PRODUCTS_DIR; };
4F69E3A4EB1062C38B03A5BE /* Pods-OutgoingCall.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-OutgoingCall.debug.xcconfig"; path = "Target Support Files/Pods-OutgoingCall/Pods-OutgoingCall.debug.xcconfig"; sourceTree = "<group>"; };
664BC0BD26EA4E6D007EE298 /* OutgoingCall.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = OutgoingCall.app; sourceTree = BUILT_PRODUCTS_DIR; };
664BC0C026EA4E6D007EE298 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
664BC0C226EA4E6D007EE298 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = "<group>"; };
664BC0C426EA4E6D007EE298 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
664BC0C626EA4E6E007EE298 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
664BC0C926EA4E6E007EE298 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = "<group>"; };
664BC0CC26EA4E6E007EE298 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
664BC0CE26EA4E6E007EE298 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
664BC0D426EA5B2E007EE298 /* OutgoingCall.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OutgoingCall.swift; sourceTree = "<group>"; };
673A75DA326F185877FA9965 /* Pods-OutgoingCall.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-OutgoingCall.release.xcconfig"; path = "Target Support Files/Pods-OutgoingCall/Pods-OutgoingCall.release.xcconfig"; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
664BC0BA26EA4E6D007EE298 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
CCE49B664A0761B2077AA237 /* Pods_OutgoingCall.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
011FA7818C38DEF3B6BB2C74 /* Frameworks */ = {
isa = PBXGroup;
children = (
0E3E7DFC01F4CC7F8D82420D /* Pods_OutgoingCall.framework */,
);
name = Frameworks;
sourceTree = "<group>";
};
664BC0B426EA4E6D007EE298 = {
isa = PBXGroup;
children = (
664BC0BF26EA4E6D007EE298 /* OutgoingCall */,
664BC0BE26EA4E6D007EE298 /* Products */,
740448E44E890F2FA8682B0B /* Pods */,
011FA7818C38DEF3B6BB2C74 /* Frameworks */,
);
sourceTree = "<group>";
};
664BC0BE26EA4E6D007EE298 /* Products */ = {
isa = PBXGroup;
children = (
664BC0BD26EA4E6D007EE298 /* OutgoingCall.app */,
);
name = Products;
sourceTree = "<group>";
};
664BC0BF26EA4E6D007EE298 /* OutgoingCall */ = {
isa = PBXGroup;
children = (
664BC0C026EA4E6D007EE298 /* AppDelegate.swift */,
664BC0C226EA4E6D007EE298 /* SceneDelegate.swift */,
664BC0C426EA4E6D007EE298 /* ContentView.swift */,
664BC0C626EA4E6E007EE298 /* Assets.xcassets */,
664BC0CB26EA4E6E007EE298 /* LaunchScreen.storyboard */,
664BC0CE26EA4E6E007EE298 /* Info.plist */,
664BC0D426EA5B2E007EE298 /* OutgoingCall.swift */,
664BC0C826EA4E6E007EE298 /* Preview Content */,
);
path = OutgoingCall;
sourceTree = "<group>";
};
664BC0C826EA4E6E007EE298 /* Preview Content */ = {
isa = PBXGroup;
children = (
664BC0C926EA4E6E007EE298 /* Preview Assets.xcassets */,
);
path = "Preview Content";
sourceTree = "<group>";
};
740448E44E890F2FA8682B0B /* Pods */ = {
isa = PBXGroup;
children = (
4F69E3A4EB1062C38B03A5BE /* Pods-OutgoingCall.debug.xcconfig */,
673A75DA326F185877FA9965 /* Pods-OutgoingCall.release.xcconfig */,
);
path = Pods;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
664BC0BC26EA4E6D007EE298 /* OutgoingCall */ = {
isa = PBXNativeTarget;
buildConfigurationList = 664BC0D126EA4E6E007EE298 /* Build configuration list for PBXNativeTarget "OutgoingCall" */;
buildPhases = (
A67E5FDCF316693C4106D5EC /* [CP] Check Pods Manifest.lock */,
664BC0B926EA4E6D007EE298 /* Sources */,
664BC0BA26EA4E6D007EE298 /* Frameworks */,
664BC0BB26EA4E6D007EE298 /* Resources */,
71824FDC693517F29C1C5E13 /* [CP] Embed Pods Frameworks */,
);
buildRules = (
);
dependencies = (
);
name = OutgoingCall;
productName = OutgoingCall;
productReference = 664BC0BD26EA4E6D007EE298 /* OutgoingCall.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
664BC0B526EA4E6D007EE298 /* Project object */ = {
isa = PBXProject;
attributes = {
LastSwiftUpdateCheck = 1250;
LastUpgradeCheck = 1250;
TargetAttributes = {
664BC0BC26EA4E6D007EE298 = {
CreatedOnToolsVersion = 12.5.1;
};
};
};
buildConfigurationList = 664BC0B826EA4E6D007EE298 /* Build configuration list for PBXProject "OutgoingCall" */;
compatibilityVersion = "Xcode 9.3";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = 664BC0B426EA4E6D007EE298;
productRefGroup = 664BC0BE26EA4E6D007EE298 /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
664BC0BC26EA4E6D007EE298 /* OutgoingCall */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
664BC0BB26EA4E6D007EE298 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
664BC0CD26EA4E6E007EE298 /* LaunchScreen.storyboard in Resources */,
664BC0CA26EA4E6E007EE298 /* Preview Assets.xcassets in Resources */,
664BC0C726EA4E6E007EE298 /* Assets.xcassets in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
71824FDC693517F29C1C5E13 /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-OutgoingCall/Pods-OutgoingCall-frameworks-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Embed Pods Frameworks";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-OutgoingCall/Pods-OutgoingCall-frameworks-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-OutgoingCall/Pods-OutgoingCall-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
A67E5FDCF316693C4106D5EC /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputFileListPaths = (
);
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-OutgoingCall-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
664BC0B926EA4E6D007EE298 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
664BC0C126EA4E6D007EE298 /* AppDelegate.swift in Sources */,
664BC0D526EA5B2E007EE298 /* OutgoingCall.swift in Sources */,
664BC0C326EA4E6D007EE298 /* SceneDelegate.swift in Sources */,
664BC0C526EA4E6D007EE298 /* ContentView.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXVariantGroup section */
664BC0CB26EA4E6E007EE298 /* LaunchScreen.storyboard */ = {
isa = PBXVariantGroup;
children = (
664BC0CC26EA4E6E007EE298 /* Base */,
);
name = LaunchScreen.storyboard;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
664BC0CF26EA4E6E007EE298 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 14.5;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
};
name = Debug;
};
664BC0D026EA4E6E007EE298 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 14.5;
MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES;
SDKROOT = iphoneos;
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_OPTIMIZATION_LEVEL = "-O";
VALIDATE_PRODUCT = YES;
};
name = Release;
};
664BC0D226EA4E6E007EE298 /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 4F69E3A4EB1062C38B03A5BE /* Pods-OutgoingCall.debug.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_ASSET_PATHS = "\"OutgoingCall/Preview Content\"";
DEVELOPMENT_TEAM = Z2V957B3D6;
ENABLE_PREVIEWS = YES;
INFOPLIST_FILE = OutgoingCall/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = BC.OutgoingCall;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
664BC0D326EA4E6E007EE298 /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 673A75DA326F185877FA9965 /* Pods-OutgoingCall.release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_ASSET_PATHS = "\"OutgoingCall/Preview Content\"";
DEVELOPMENT_TEAM = Z2V957B3D6;
ENABLE_PREVIEWS = YES;
INFOPLIST_FILE = OutgoingCall/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = BC.OutgoingCall;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
664BC0B826EA4E6D007EE298 /* Build configuration list for PBXProject "OutgoingCall" */ = {
isa = XCConfigurationList;
buildConfigurations = (
664BC0CF26EA4E6E007EE298 /* Debug */,
664BC0D026EA4E6E007EE298 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
664BC0D126EA4E6E007EE298 /* Build configuration list for PBXNativeTarget "OutgoingCall" */ = {
isa = XCConfigurationList;
buildConfigurations = (
664BC0D226EA4E6E007EE298 /* Debug */,
664BC0D326EA4E6E007EE298 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 664BC0B526EA4E6D007EE298 /* Project object */;
}

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:">
</FileRef>
</Workspace>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>

View File

@ -0,0 +1,38 @@
//
// AppDelegate.swift
// LoginTutorial
//
// Created by QuentinArguillere on 31/07/2020.
// Copyright © 2020 BelledonneCommunications. All rights reserved.
//
import UIKit
import SwiftUI
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
@ObservedObject var tutorialContext = OutgoingCallTutorialContext()
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
return true
}
// MARK: UISceneSession Lifecycle
func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
// Called when a new scene session is being created.
// Use this method to select a configuration to create the new scene with.
return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
}
func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {
// Called when the user discards a scene session.
// If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
// Use this method to release any resources that were specific to the discarded scenes, as they will not return.
}
}

View File

@ -0,0 +1,11 @@
{
"colors" : [
{
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,98 @@
{
"images" : [
{
"idiom" : "iphone",
"scale" : "2x",
"size" : "20x20"
},
{
"idiom" : "iphone",
"scale" : "3x",
"size" : "20x20"
},
{
"idiom" : "iphone",
"scale" : "2x",
"size" : "29x29"
},
{
"idiom" : "iphone",
"scale" : "3x",
"size" : "29x29"
},
{
"idiom" : "iphone",
"scale" : "2x",
"size" : "40x40"
},
{
"idiom" : "iphone",
"scale" : "3x",
"size" : "40x40"
},
{
"idiom" : "iphone",
"scale" : "2x",
"size" : "60x60"
},
{
"idiom" : "iphone",
"scale" : "3x",
"size" : "60x60"
},
{
"idiom" : "ipad",
"scale" : "1x",
"size" : "20x20"
},
{
"idiom" : "ipad",
"scale" : "2x",
"size" : "20x20"
},
{
"idiom" : "ipad",
"scale" : "1x",
"size" : "29x29"
},
{
"idiom" : "ipad",
"scale" : "2x",
"size" : "29x29"
},
{
"idiom" : "ipad",
"scale" : "1x",
"size" : "40x40"
},
{
"idiom" : "ipad",
"scale" : "2x",
"size" : "40x40"
},
{
"idiom" : "ipad",
"scale" : "1x",
"size" : "76x76"
},
{
"idiom" : "ipad",
"scale" : "2x",
"size" : "76x76"
},
{
"idiom" : "ipad",
"scale" : "2x",
"size" : "83.5x83.5"
},
{
"idiom" : "ios-marketing",
"scale" : "1x",
"size" : "1024x1024"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13122.16" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13104.12"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="EHf-IW-A2E">
<objects>
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" xcode11CocoaTouchSystemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="53" y="375"/>
</scene>
</scenes>
</document>

View File

@ -0,0 +1,171 @@
//
// ContentView.swift
// IncomingCall tutorial
//
// Created by QuentinArguillere on 09/09/2021.
// Copyright © 2021 BelledonneCommunications. All rights reserved.
//
import SwiftUI
import linphonesw
struct ContentView: View {
@ObservedObject var tutorialContext : OutgoingCallTutorialContext
func callStateString() -> String {
if (tutorialContext.isCallRunning) {
return "Call running"
} else {
return "No Call"
}
}
var body: some View {
VStack {
Group {
HStack {
Text("Username:")
.font(.title)
TextField("", text : $tutorialContext.username)
.textFieldStyle(RoundedBorderTextFieldStyle())
.disabled(tutorialContext.loggedIn)
}
HStack {
Text("Password:")
.font(.title)
TextField("", text : $tutorialContext.passwd)
.textFieldStyle(RoundedBorderTextFieldStyle())
.disabled(tutorialContext.loggedIn)
}
HStack {
Text("Domain:")
.font(.title)
TextField("", text : $tutorialContext.domain)
.textFieldStyle(RoundedBorderTextFieldStyle())
.disabled(tutorialContext.loggedIn)
}
Picker(selection: $tutorialContext.transportType, label: Text("Transport:")) {
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()
self.tutorialContext.delete()
} 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)
}
VStack {
HStack {
Text("Call dest:")
.font(.title)
TextField("", text : $tutorialContext.remoteAddress)
.textFieldStyle(RoundedBorderTextFieldStyle())
.disabled(!tutorialContext.loggedIn)
}
HStack {
Button(action: {
if (self.tutorialContext.isCallRunning) {
self.tutorialContext.terminateCall()
} else {
self.tutorialContext.outgoingCall()
}
})
{
Text( (tutorialContext.isCallRunning) ? "End" : "Call")
.font(.largeTitle)
.foregroundColor(Color.white)
.frame(width: 180.0, height: 42.0)
.background(Color.gray)
}
HStack {
Text(tutorialContext.isCallRunning ? "Running" : "")
.italic().foregroundColor(.green)
Spacer()
}
}
HStack {
Text("Call msg:").font(.title3).underline()
Text(tutorialContext.callMsg)
Spacer()
}.padding(.top, 5)
HStack {
Button(action: tutorialContext.toggleVideo)
{
Text((tutorialContext.isVideoEnabled) ? "Video OFF" : "Video ON")
.font(.title3)
.foregroundColor(Color.white)
.frame(width: 130.0, height: 42.0)
.background(Color.gray)
}
.disabled(!tutorialContext.isCallRunning)
Button(action: tutorialContext.toggleCamera)
{
Text("Change camera")
.font(.title3)
.foregroundColor(Color.white)
.frame(width: 160.0, height: 42.0)
.background(Color.gray)
}
.disabled(!tutorialContext.canChangeCamera || !tutorialContext.isVideoEnabled)
}.padding(.top, 5)
HStack {
VStack {
LinphoneVideoViewHolder() { view in
self.tutorialContext.mCore.nativeVideoWindow = view
}
.frame(width: 120, height: 160.0)
.border(Color.gray)
.padding(.leading)
Text("What I receive")
}
Spacer()
VStack {
LinphoneVideoViewHolder() { view in
self.tutorialContext.mCore.nativePreviewWindow = view
}
.frame(width: 120, height: 160.0)
.border(Color.gray)
.padding(.leading)
Text("What I send")
}
}
}.padding(.top, 30)
}
Group {
Spacer()
Text("Core Version is \(tutorialContext.coreVersion)")
}
}
.padding()
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView(tutorialContext: OutgoingCallTutorialContext())
}
}

View File

@ -0,0 +1,66 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>NSCameraUsageDescription</key>
<string>Cameras access</string>
<key>NSMicrophoneUsageDescription</key>
<string>Microphone access</string>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UIApplicationSceneManifest</key>
<dict>
<key>UIApplicationSupportsMultipleScenes</key>
<false/>
<key>UISceneConfigurations</key>
<dict>
<key>UIWindowSceneSessionRoleApplication</key>
<array>
<dict>
<key>UISceneConfigurationName</key>
<string>Default Configuration</string>
<key>UISceneDelegateClassName</key>
<string>$(PRODUCT_MODULE_NAME).SceneDelegate</string>
</dict>
</array>
</dict>
</dict>
<key>UIApplicationSupportsIndirectInputEvents</key>
<true/>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
</dict>
</plist>

View File

@ -0,0 +1,238 @@
//
// OutgoingCall.swift
// OutgoingCall tutorial
//
// Created by QuentinArguillere on 08/09/2021.
// Copyright © 2021 BelledonneCommunications. All rights reserved.
//
import linphonesw
class OutgoingCallTutorialContext : ObservableObject
{
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 domain : String = "sip.example.org"
@Published var loggedIn: Bool = false
@Published var transportType : String = "TLS"
// Outgoing call related variables
@Published var callMsg : String = ""
@Published var isCallRunning : Bool = false
@Published var isVideoEnabled : Bool = false
@Published var canChangeCamera : Bool = false
@Published var remoteAddress : String = "sip:arguillq@sip.linphone.org"
init()
{
LoggingService.Instance.logLevel = LogLevel.Debug
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
mCore.videoCaptureEnabled = true
mCore.videoDisplayEnabled = true
// When enabling the video, the remote will either automatically answer the update request
// or it will ask it's user depending on it's policy.
// Here we have configured the policy to always automatically accept video requests
mCore.videoActivationPolicy!.automaticallyAccept = true
// If you don't want to automatically accept,
// you'll have to use a code similar to the one in toggleVideo to answer a received request
// If the following property is enabled, it will automatically configure created call params with video enabled
//core.videoActivationPolicy.automaticallyInitiate = true
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,
// which includes new incoming/outgoing calls
self.callMsg = message
if (state == .OutgoingInit) {
// First state an outgoing call will go through
} else if (state == .OutgoingProgress) {
// Right after outgoing init
} else if (state == .OutgoingRinging) {
// This state will be reached upon reception of the 180 RINGING
} else if (state == .Connected) {
// When the 200 OK has been received
} else if (state == .StreamsRunning) {
// This state indicates the call is active.
// You may reach this state multiple times, for example after a pause/resume
// 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
} else if (state == .Paused) {
// When you put a call in pause, it will became Paused
self.canChangeCamera = false
} else if (state == .PausedByRemote) {
// When the remote end of the call pauses it, it will be PausedByRemote
} else if (state == .Updating) {
// When we request a call update, for example when toggling video
} else if (state == .UpdatedByRemote) {
// When the remote requests a call update
} else if (state == .Released) {
// Call state will be released shortly after the End state
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")
if (state == .Ok) {
self.loggedIn = true
} else if (state == .Cleared) {
self.loggedIn = false
}
})
mCore.addDelegate(delegate: mCoreDelegate)
}
func login() {
do {
var transport : TransportType
if (transportType == "TLS") { transport = TransportType.Tls }
else if (transportType == "TCP") { transport = TransportType.Tcp }
else { transport = TransportType.Udp }
let authInfo = try Factory.Instance.createAuthInfo(username: username, userid: "", passwd: passwd, ha1: "", realm: "", domain: domain)
let accountParams = try mCore.createAccountParams()
let identity = try Factory.Instance.createAddress(addr: String("sip:" + username + "@" + domain))
try! accountParams.setIdentityaddress(newValue: identity)
let address = try Factory.Instance.createAddress(addr: String("sip:" + domain))
try address.setTransport(newValue: transport)
try accountParams.setServeraddress(newValue: address)
accountParams.registerEnabled = true
mAccount = try mCore.createAccount(params: accountParams)
mCore.addAuthInfo(info: authInfo)
try mCore.addAccount(account: mAccount!)
mCore.defaultAccount = mAccount
} catch { NSLog(error.localizedDescription) }
}
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)
mCore.clearAccounts()
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
// If we wanted to start the call with video directly
//params.videoEnabled = true
// Finally we start the call
let _ = mCore.inviteAddressWithParams(addr: remoteAddress, params: params)
// Call process can be followed in onCallStateChanged callback from core listener
} catch { NSLog(error.localizedDescription) }
}
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()
}
} catch { NSLog(error.localizedDescription) }
}
func toggleVideo() {
do {
if (mCore.callsNb == 0) { return }
let coreCall = (mCore.currentCall != nil) ? mCore.currentCall : mCore.calls[0]
// We will need the CAMERA permission for video call
if let call = coreCall {
// To update the call, we need to create a new call params, from the call object this time
let params = try mCore.createCallParams(call: call)
// Here we toggle the video state (disable it if enabled, enable it if disabled)
// Note that we are using currentParams and not params or remoteParams
// params is the object you configured when the call was started
// remote params is the same but for the remote
// current params is the real params of the call, resulting of the mix of local & remote params
params.videoEnabled = !(call.currentParams!.videoEnabled)
isVideoEnabled = params.videoEnabled
// Finally we request the call update
try call.update(params: params)
// Note that when toggling off the video, TextureViews will keep showing the latest frame displayed
}
} catch { NSLog(error.localizedDescription) }
}
func toggleCamera() {
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
if (camera != currentDevice && camera != "StaticImage: Static picture") {
try mCore.setVideodevice(newValue: camera)
break
}
}
} catch { NSLog(error.localizedDescription) }
}
func pauseOrResume() {
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
try call.pause()
} else if (call.state != Call.State.Resuming) {
// Otherwise let's resume it
try call.resume()
}
}
} catch { NSLog(error.localizedDescription) }
}
}

View File

@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,65 @@
//
// SceneDelegate.swift
// LoginTutorial
//
// Created by QuentinArguillere on 31/07/2020.
// Copyright © 2020 BelledonneCommunications. All rights reserved.
//
import UIKit
import SwiftUI
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
// Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
// If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
// This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
let delegate = UIApplication.shared.delegate as! AppDelegate
// Create the SwiftUI view that provides the window contents.
let contentView = ContentView(tutorialContext: delegate.tutorialContext)
// Use a UIHostingController as window root view controller.
if let windowScene = scene as? UIWindowScene {
let window = UIWindow(windowScene: windowScene)
window.rootViewController = UIHostingController(rootView: contentView)
self.window = window
window.makeKeyAndVisible()
}
}
func sceneDidDisconnect(_ scene: UIScene) {
// Called as the scene is being released by the system.
// This occurs shortly after the scene enters the background, or when its session is discarded.
// Release any resources associated with this scene that can be re-created the next time the scene connects.
// The scene may re-connect later, as its session was not neccessarily discarded (see `application:didDiscardSceneSessions` instead).
}
func sceneDidBecomeActive(_ scene: UIScene) {
// Called when the scene has moved from an inactive state to an active state.
// Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive.
}
func sceneWillResignActive(_ scene: UIScene) {
// Called when the scene will move from an active state to an inactive state.
// This may occur due to temporary interruptions (ex. an incoming phone call).
}
func sceneWillEnterForeground(_ scene: UIScene) {
// Called as the scene transitions from the background to the foreground.
// Use this method to undo the changes made on entering the background.
}
func sceneDidEnterBackground(_ scene: UIScene) {
// Called as the scene transitions from the foreground to the background.
// Use this method to save data, release shared resources, and store enough scene-specific state information
// to restore the scene back to its current state.
}
}

View File

@ -0,0 +1,24 @@
# Uncomment the next line to define a global platform for your project
platform :ios, '11.0'
source "https://gitlab.linphone.org/BC/public/podspec.git"
source "https://github.com/CocoaPods/Specs.git"
def basic_pods
if ENV['PODFILE_PATH'].nil?
pod 'linphone-sdk', '~> 5.0.0'
else
pod 'linphone-sdk', :path => ENV['PODFILE_PATH'] # local sdk
end
end
target 'OutgoingCall' do
# Comment the next line if you don't want to use dynamic frameworks
use_frameworks!
# Pods for OutgoingCall
basic_pods
end

View File

@ -0,0 +1,8 @@
Outgoing call tutorial
====================
In the previous tutorial we saw how to handle an incoming call, now let's start one.
We'll also see how to enable video during a call, switch between the front and back cameras if available and display our own preview.
Note that changes to the "info.plist" file were made to enable iphone cameras.

View File

@ -0,0 +1,431 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 51;
objects = {
/* Begin PBXBuildFile section */
577005D03F6821591475FBF9 /* Pods_CallKitTutorial.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 454830E9C41DBBF20AF2BD05 /* Pods_CallKitTutorial.framework */; };
6608A96624E197D5006E6C68 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6608A96524E197D5006E6C68 /* AppDelegate.swift */; };
6608A96824E197D5006E6C68 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6608A96724E197D5006E6C68 /* SceneDelegate.swift */; };
6608A96A24E197D5006E6C68 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6608A96924E197D5006E6C68 /* ContentView.swift */; };
6608A96C24E197D5006E6C68 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 6608A96B24E197D5006E6C68 /* Assets.xcassets */; };
6608A96F24E197D5006E6C68 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 6608A96E24E197D5006E6C68 /* Preview Assets.xcassets */; };
6608A97224E197D5006E6C68 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 6608A97024E197D5006E6C68 /* LaunchScreen.storyboard */; };
6608A97A24E19817006E6C68 /* CallKitTutorial.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6608A97924E19817006E6C68 /* CallKitTutorial.swift */; };
6608A97C24E1981E006E6C68 /* CallKitProviderDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6608A97B24E1981E006E6C68 /* CallKitProviderDelegate.swift */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
018936DA8FE1500B9E181610 /* Pods-CallKitTutorial.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-CallKitTutorial.debug.xcconfig"; path = "Target Support Files/Pods-CallKitTutorial/Pods-CallKitTutorial.debug.xcconfig"; sourceTree = "<group>"; };
454830E9C41DBBF20AF2BD05 /* Pods_CallKitTutorial.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_CallKitTutorial.framework; sourceTree = BUILT_PRODUCTS_DIR; };
6608A96224E197D5006E6C68 /* CallKitTutorial.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = CallKitTutorial.app; sourceTree = BUILT_PRODUCTS_DIR; };
6608A96524E197D5006E6C68 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
6608A96724E197D5006E6C68 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = "<group>"; };
6608A96924E197D5006E6C68 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
6608A96B24E197D5006E6C68 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
6608A96E24E197D5006E6C68 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = "<group>"; };
6608A97124E197D5006E6C68 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
6608A97324E197D5006E6C68 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
6608A97924E19817006E6C68 /* CallKitTutorial.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CallKitTutorial.swift; sourceTree = "<group>"; };
6608A97B24E1981E006E6C68 /* CallKitProviderDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CallKitProviderDelegate.swift; sourceTree = "<group>"; };
6608A97D24E19852006E6C68 /* CallKitTutorial.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = CallKitTutorial.entitlements; sourceTree = "<group>"; };
C87B170DFD4D3072825B25EF /* Pods-CallKitTutorial.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-CallKitTutorial.release.xcconfig"; path = "Target Support Files/Pods-CallKitTutorial/Pods-CallKitTutorial.release.xcconfig"; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
6608A95F24E197D5006E6C68 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
577005D03F6821591475FBF9 /* Pods_CallKitTutorial.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
319770E3ECD19EBD7557F82B /* Frameworks */ = {
isa = PBXGroup;
children = (
454830E9C41DBBF20AF2BD05 /* Pods_CallKitTutorial.framework */,
);
name = Frameworks;
sourceTree = "<group>";
};
38284A71627D413A7ACC2F07 /* Pods */ = {
isa = PBXGroup;
children = (
018936DA8FE1500B9E181610 /* Pods-CallKitTutorial.debug.xcconfig */,
C87B170DFD4D3072825B25EF /* Pods-CallKitTutorial.release.xcconfig */,
);
path = Pods;
sourceTree = "<group>";
};
6608A95924E197D5006E6C68 = {
isa = PBXGroup;
children = (
6608A96424E197D5006E6C68 /* CallKitTutorial */,
6608A96324E197D5006E6C68 /* Products */,
38284A71627D413A7ACC2F07 /* Pods */,
319770E3ECD19EBD7557F82B /* Frameworks */,
);
sourceTree = "<group>";
};
6608A96324E197D5006E6C68 /* Products */ = {
isa = PBXGroup;
children = (
6608A96224E197D5006E6C68 /* CallKitTutorial.app */,
);
name = Products;
sourceTree = "<group>";
};
6608A96424E197D5006E6C68 /* CallKitTutorial */ = {
isa = PBXGroup;
children = (
6608A97D24E19852006E6C68 /* CallKitTutorial.entitlements */,
6608A96524E197D5006E6C68 /* AppDelegate.swift */,
6608A96724E197D5006E6C68 /* SceneDelegate.swift */,
6608A97B24E1981E006E6C68 /* CallKitProviderDelegate.swift */,
6608A97924E19817006E6C68 /* CallKitTutorial.swift */,
6608A96924E197D5006E6C68 /* ContentView.swift */,
6608A96B24E197D5006E6C68 /* Assets.xcassets */,
6608A97024E197D5006E6C68 /* LaunchScreen.storyboard */,
6608A97324E197D5006E6C68 /* Info.plist */,
6608A96D24E197D5006E6C68 /* Preview Content */,
);
path = CallKitTutorial;
sourceTree = "<group>";
};
6608A96D24E197D5006E6C68 /* Preview Content */ = {
isa = PBXGroup;
children = (
6608A96E24E197D5006E6C68 /* Preview Assets.xcassets */,
);
path = "Preview Content";
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
6608A96124E197D5006E6C68 /* CallKitTutorial */ = {
isa = PBXNativeTarget;
buildConfigurationList = 6608A97624E197D5006E6C68 /* Build configuration list for PBXNativeTarget "CallKitTutorial" */;
buildPhases = (
D642DC4C3C74BB17FF5A3E9F /* [CP] Check Pods Manifest.lock */,
6608A95E24E197D5006E6C68 /* Sources */,
6608A95F24E197D5006E6C68 /* Frameworks */,
6608A96024E197D5006E6C68 /* Resources */,
640A5744B5ABBA6A3EDBFDB1 /* [CP] Embed Pods Frameworks */,
);
buildRules = (
);
dependencies = (
);
name = CallKitTutorial;
productName = CallKitTutorial;
productReference = 6608A96224E197D5006E6C68 /* CallKitTutorial.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
6608A95A24E197D5006E6C68 /* Project object */ = {
isa = PBXProject;
attributes = {
LastSwiftUpdateCheck = 1160;
LastUpgradeCheck = 1160;
ORGANIZATIONNAME = BelledonneCommunications;
TargetAttributes = {
6608A96124E197D5006E6C68 = {
CreatedOnToolsVersion = 11.6;
};
};
};
buildConfigurationList = 6608A95D24E197D5006E6C68 /* Build configuration list for PBXProject "CallKitTutorial" */;
compatibilityVersion = "Xcode 9.3";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = 6608A95924E197D5006E6C68;
productRefGroup = 6608A96324E197D5006E6C68 /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
6608A96124E197D5006E6C68 /* CallKitTutorial */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
6608A96024E197D5006E6C68 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
6608A97224E197D5006E6C68 /* LaunchScreen.storyboard in Resources */,
6608A96F24E197D5006E6C68 /* Preview Assets.xcassets in Resources */,
6608A96C24E197D5006E6C68 /* Assets.xcassets in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
640A5744B5ABBA6A3EDBFDB1 /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-CallKitTutorial/Pods-CallKitTutorial-frameworks-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Embed Pods Frameworks";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-CallKitTutorial/Pods-CallKitTutorial-frameworks-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-CallKitTutorial/Pods-CallKitTutorial-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
D642DC4C3C74BB17FF5A3E9F /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputFileListPaths = (
);
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-CallKitTutorial-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
6608A95E24E197D5006E6C68 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
6608A97A24E19817006E6C68 /* CallKitTutorial.swift in Sources */,
6608A96624E197D5006E6C68 /* AppDelegate.swift in Sources */,
6608A96824E197D5006E6C68 /* SceneDelegate.swift in Sources */,
6608A96A24E197D5006E6C68 /* ContentView.swift in Sources */,
6608A97C24E1981E006E6C68 /* CallKitProviderDelegate.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXVariantGroup section */
6608A97024E197D5006E6C68 /* LaunchScreen.storyboard */ = {
isa = PBXVariantGroup;
children = (
6608A97124E197D5006E6C68 /* Base */,
);
name = LaunchScreen.storyboard;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
6608A97424E197D5006E6C68 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 13.6;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
};
name = Debug;
};
6608A97524E197D5006E6C68 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 13.6;
MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES;
SDKROOT = iphoneos;
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_OPTIMIZATION_LEVEL = "-O";
VALIDATE_PRODUCT = YES;
};
name = Release;
};
6608A97724E197D5006E6C68 /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 018936DA8FE1500B9E181610 /* Pods-CallKitTutorial.debug.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_ENTITLEMENTS = CallKitTutorial/CallKitTutorial.entitlements;
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_ASSET_PATHS = "\"CallKitTutorial/Preview Content\"";
DEVELOPMENT_TEAM = Z2V957B3D6;
ENABLE_PREVIEWS = YES;
INFOPLIST_FILE = CallKitTutorial/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 14.5;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = org.linphone.tutorials.callkit;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
6608A97824E197D5006E6C68 /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = C87B170DFD4D3072825B25EF /* Pods-CallKitTutorial.release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_ENTITLEMENTS = CallKitTutorial/CallKitTutorial.entitlements;
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_ASSET_PATHS = "\"CallKitTutorial/Preview Content\"";
DEVELOPMENT_TEAM = Z2V957B3D6;
ENABLE_PREVIEWS = YES;
INFOPLIST_FILE = CallKitTutorial/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 14.5;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = org.linphone.tutorials.callkit;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
6608A95D24E197D5006E6C68 /* Build configuration list for PBXProject "CallKitTutorial" */ = {
isa = XCConfigurationList;
buildConfigurations = (
6608A97424E197D5006E6C68 /* Debug */,
6608A97524E197D5006E6C68 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
6608A97624E197D5006E6C68 /* Build configuration list for PBXNativeTarget "CallKitTutorial" */ = {
isa = XCConfigurationList;
buildConfigurations = (
6608A97724E197D5006E6C68 /* Debug */,
6608A97824E197D5006E6C68 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 6608A95A24E197D5006E6C68 /* Project object */;
}

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:CallKitTutorial.xcodeproj">
</FileRef>
</Workspace>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>

View File

@ -0,0 +1,38 @@
//
// AppDelegate.swift
// CallKitTutorial
//
// Created by QuentinArguillere on 10/08/2020.
// Copyright © 2020 BelledonneCommunications. All rights reserved.
//
import UIKit
import SwiftUI
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
@ObservedObject var tutorialContext = CallKitExampleContext()
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
return true
}
// MARK: UISceneSession Lifecycle
func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
// Called when a new scene session is being created.
// Use this method to select a configuration to create the new scene with.
return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
}
func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {
// Called when the user discards a scene session.
// If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
// Use this method to release any resources that were specific to the discarded scenes, as they will not return.
}
}

View File

@ -0,0 +1,98 @@
{
"images" : [
{
"idiom" : "iphone",
"scale" : "2x",
"size" : "20x20"
},
{
"idiom" : "iphone",
"scale" : "3x",
"size" : "20x20"
},
{
"idiom" : "iphone",
"scale" : "2x",
"size" : "29x29"
},
{
"idiom" : "iphone",
"scale" : "3x",
"size" : "29x29"
},
{
"idiom" : "iphone",
"scale" : "2x",
"size" : "40x40"
},
{
"idiom" : "iphone",
"scale" : "3x",
"size" : "40x40"
},
{
"idiom" : "iphone",
"scale" : "2x",
"size" : "60x60"
},
{
"idiom" : "iphone",
"scale" : "3x",
"size" : "60x60"
},
{
"idiom" : "ipad",
"scale" : "1x",
"size" : "20x20"
},
{
"idiom" : "ipad",
"scale" : "2x",
"size" : "20x20"
},
{
"idiom" : "ipad",
"scale" : "1x",
"size" : "29x29"
},
{
"idiom" : "ipad",
"scale" : "2x",
"size" : "29x29"
},
{
"idiom" : "ipad",
"scale" : "1x",
"size" : "40x40"
},
{
"idiom" : "ipad",
"scale" : "2x",
"size" : "40x40"
},
{
"idiom" : "ipad",
"scale" : "1x",
"size" : "76x76"
},
{
"idiom" : "ipad",
"scale" : "2x",
"size" : "76x76"
},
{
"idiom" : "ipad",
"scale" : "2x",
"size" : "83.5x83.5"
},
{
"idiom" : "ios-marketing",
"scale" : "1x",
"size" : "1024x1024"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13122.16" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13104.12"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="EHf-IW-A2E">
<objects>
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" xcode11CocoaTouchSystemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="53" y="375"/>
</scene>
</scenes>
</document>

View File

@ -0,0 +1,99 @@
//
// ProviderDelegate.swift
// CallTutorial
//
// Created by QuentinArguillere on 05/08/2020.
// Copyright © 2020 BelledonneCommunications. All rights reserved.
//
import Foundation
import CallKit
import linphonesw
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
}
}
// 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) {
do {
if (tutorialContext.mCall?.state != .End && tutorialContext.mCall?.state != .Released) {
try tutorialContext.mCall?.terminate()
}
} catch { NSLog(error.localizedDescription) }
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)
}
}

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>aps-environment</key>
<string>development</string>
</dict>
</plist>

View File

@ -0,0 +1,141 @@
//
// CallExample.swift
// CallTutorial
//
// Created by QuentinArguillere on 31/07/2020.
// Copyright © 2020 BelledonneCommunications. All rights reserved.
//
import linphonesw
import AVFoundation
class CallKitExampleContext : ObservableObject
{
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 loggedIn: Bool = false
@Published var transportType : String = "TLS"
@Published var callMsg : String = ""
@Published var isCallIncoming : Bool = false
@Published var isCallRunning : Bool = false
@Published var remoteAddress : String = "Nobody yet"
@Published var isSpeakerEnabled : Bool = false
@Published var isMicrophoneEnabled : Bool = false
/*------------ Callkit tutorial related variables ---------------*/
let incomingCallName = "Incoming call example"
var mCall : Call?
var mProviderDelegate : CallKitProviderDelegate!
var mCallAlreadyStopped : Bool = false;
init()
{
LoggingService.Instance.logLevel = LogLevel.Debug
let factory = Factory.Instance
// IMPORTANT : In this tutorial, we require the use of a core configuration file.
// This way, once the registration is done, and until it is cleared, it will return to the LoggedIn state on launch.
// This allows us to have a functional call when the app was closed and is started by a VOIP push notification (incoming call
// We also need to enable "Push Notitifications" and "Background Mode - Voice Over IP"
let configDir = factory.getConfigDir(context: nil)
try? mCore = factory.createCore(configPath: "\(configDir)/MyConfig", factoryConfigPath: "", systemContext: nil)
mProviderDelegate = CallKitProviderDelegate(context: self)
// enabling push notifications management in the core
mCore.callkitEnabled = true
mCore.pushNotificationEnabled = true
try? mCore.start()
mCoreDelegate = CoreDelegateStub( onCallStateChanged: { (core: Core, call: Call, state: Call.State, message: String) in
self.callMsg = message
if (state == .PushIncomingReceived){
// We're being called by someone (and app is in background)
self.mCall = call
self.isCallIncoming = true
self.mProviderDelegate.incomingCall()
} else if (state == .IncomingReceived) {
// If app is in foreground, it's likely that we will receive the SIP invite before the Push notification
if (!self.isCallIncoming) {
self.mCall = call
self.isCallIncoming = true
self.mProviderDelegate.incomingCall()
}
self.remoteAddress = call.remoteAddress!.asStringUriOnly()
} else if (state == .Connected) {
self.isCallIncoming = false
self.isCallRunning = true
} else if (state == .Released || state == .End || state == .Error) {
// Call has been terminated by any side
// Report to CallKit that the call is over, if the terminate action was initiated by other end of the call
if (self.isCallRunning) {
self.mProviderDelegate.stopCall()
}
self.remoteAddress = "Nobody yet"
}
}, 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")
if (state == .Ok) {
self.loggedIn = true
// Since core has "Push Enabled", the reception and setting of the push notification token is done automatically
// It should have been set and used when we log in, you can check here or in the liblinphone logs
NSLog("Account registered Push voip token: \(account.params?.pushNotificationConfig?.voipToken)")
} else if (state == .Cleared) {
self.loggedIn = false
}
})
mCore.addDelegate(delegate: mCoreDelegate)
}
func login() {
do {
var transport : TransportType
if (transportType == "TLS") { transport = TransportType.Tls }
else if (transportType == "TCP") { transport = TransportType.Tcp }
else { transport = TransportType.Udp }
let authInfo = try Factory.Instance.createAuthInfo(username: username, userid: "", passwd: passwd, ha1: "", realm: "", domain: domain)
let accountParams = try mCore.createAccountParams()
let identity = try Factory.Instance.createAddress(addr: String("sip:" + username + "@" + domain))
try! accountParams.setIdentityaddress(newValue: identity)
let address = try Factory.Instance.createAddress(addr: String("sip:" + domain))
try address.setTransport(newValue: transport)
try accountParams.setServeraddress(newValue: address)
accountParams.registerEnabled = true
// Enable push notifications on this account
accountParams.pushNotificationAllowed = true
// We're in a sandbox application, so we must set the provider to "apns.dev" since it will be "apns" by default, which is used only for production apps
accountParams.pushNotificationConfig?.provider = "apns.dev"
mAccount = try mCore.createAccount(params: accountParams)
mCore.addAuthInfo(info: authInfo)
try mCore.addAccount(account: mAccount!)
mCore.defaultAccount = mAccount
} catch { NSLog(error.localizedDescription) }
}
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)
mCore.clearAccounts()
mCore.clearAllAuthInfo()
}
}
}

View File

@ -0,0 +1,116 @@
//
// ContentView.swift
// CallKit tutorial
//
// Created by QuentinArguillere on 09/09/2021.
// Copyright © 2021 BelledonneCommunications. All rights reserved.
//
import SwiftUI
struct ContentView: View {
@ObservedObject var tutorialContext : CallKitExampleContext
func callStateString() -> String {
if (tutorialContext.isCallRunning) {
return "Call running"
} else if (tutorialContext.isCallIncoming) {
return "Incoming call"
} else {
return "No Call"
}
}
var body: some View {
VStack {
Group {
HStack {
Text("Username:")
.font(.title)
TextField("", text : $tutorialContext.username)
.textFieldStyle(RoundedBorderTextFieldStyle())
.disabled(tutorialContext.loggedIn)
}
HStack {
Text("Password:")
.font(.title)
TextField("", text : $tutorialContext.passwd)
.textFieldStyle(RoundedBorderTextFieldStyle())
.disabled(tutorialContext.loggedIn)
}
HStack {
Text("Domain:")
.font(.title)
TextField("", text : $tutorialContext.domain)
.textFieldStyle(RoundedBorderTextFieldStyle())
.disabled(tutorialContext.loggedIn)
}
Picker(selection: $tutorialContext.transportType, label: Text("Transport:")) {
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()
self.tutorialContext.delete()
} 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)
}
VStack {
HStack {
Text("Caller:").font(.title).underline()
Text(tutorialContext.remoteAddress)
Spacer()
}.padding(.top, 5)
HStack {
Text("Call msg:").font(.title3).underline()
Text(tutorialContext.callMsg)
Spacer()
}.padding(.top, 5)
}.padding(.top, 30)
Button(action: tutorialContext.mProviderDelegate.stopCall)
{
Text("End call").font(.largeTitle)
.foregroundColor(Color.white)
.frame(width: 120.0, height: 42.0)
.background(Color.gray)
}.disabled(!tutorialContext.isCallRunning)
.padding(.top, 10)
}
Group {
Spacer()
Text("Core Version is \(tutorialContext.coreVersion)")
}
}
.padding()
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView(tutorialContext: CallKitExampleContext())
}
}

View File

@ -0,0 +1,70 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>LSApplicationCategoryType</key>
<string></string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>NSCameraUsageDescription</key>
<string>Camera access</string>
<key>NSMicrophoneUsageDescription</key>
<string>Microphone access</string>
<key>UIApplicationSceneManifest</key>
<dict>
<key>UIApplicationSupportsMultipleScenes</key>
<false/>
<key>UISceneConfigurations</key>
<dict>
<key>UIWindowSceneSessionRoleApplication</key>
<array>
<dict>
<key>UISceneConfigurationName</key>
<string>Default Configuration</string>
<key>UISceneDelegateClassName</key>
<string>$(PRODUCT_MODULE_NAME).SceneDelegate</string>
</dict>
</array>
</dict>
</dict>
<key>UIBackgroundModes</key>
<array>
<string>voip</string>
</array>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
</dict>
</plist>

View File

@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,66 @@
//
// SceneDelegate.swift
// CallKitTutorial
//
// Created by QuentinArguillere on 10/08/2020.
// Copyright © 2020 BelledonneCommunications. All rights reserved.
//
import UIKit
import SwiftUI
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
// Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
// If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
// This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
// Create the SwiftUI view that provides the window contents.
let delegate = UIApplication.shared.delegate as! AppDelegate
let contentView = ContentView(tutorialContext: delegate.tutorialContext)
// Use a UIHostingController as window root view controller.
if let windowScene = scene as? UIWindowScene {
let window = UIWindow(windowScene: windowScene)
window.rootViewController = UIHostingController(rootView: contentView)
self.window = window
window.makeKeyAndVisible()
}
}
func sceneDidDisconnect(_ scene: UIScene) {
// Called as the scene is being released by the system.
// This occurs shortly after the scene enters the background, or when its session is discarded.
// Release any resources associated with this scene that can be re-created the next time the scene connects.
// The scene may re-connect later, as its session was not neccessarily discarded (see `application:didDiscardSceneSessions` instead).
}
func sceneDidBecomeActive(_ scene: UIScene) {
// Called when the scene has moved from an inactive state to an active state.
// Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive.
}
func sceneWillResignActive(_ scene: UIScene) {
// Called when the scene will move from an active state to an inactive state.
// This may occur due to temporary interruptions (ex. an incoming phone call).
}
func sceneWillEnterForeground(_ scene: UIScene) {
// Called as the scene transitions from the background to the foreground.
// Use this method to undo the changes made on entering the background.
}
func sceneDidEnterBackground(_ scene: UIScene) {
// Called as the scene transitions from the foreground to the background.
// Use this method to save data, release shared resources, and store enough scene-specific state information
// to restore the scene back to its current state.
}
}

View File

@ -1,11 +1,11 @@
# Uncomment the next line to define a global platform for your project
platform :ios, '11.0'
source "https://gitlab.linphone.org/BC/public/podspec.git"
#source "https://github.com/CocoaPods/Specs.git"
source "https://github.com/CocoaPods/Specs.git"
def basic_pods
if ENV['PODFILE_PATH'].nil?
pod 'linphone-sdk', '~> 4.4.0'
pod 'linphone-sdk', '~> 5.0.0'
else
pod 'linphone-sdk', :path => ENV['PODFILE_PATH'] # local sdk
end
@ -14,11 +14,11 @@ end
target 'HelloLinphone' do
target 'CallKitTutorial' do
# Comment the next line if you don't want to use dynamic frameworks
use_frameworks!
# Pods for HelloLinphone
# Pods for CallKitTutorial
basic_pods
end

View File

@ -0,0 +1,10 @@
Push notifications tutorial
====================
On mobile devices (Android & iOS), you probably want your app to be reachable even if it's not in the foreground.
To do that you need it to be able to receive push notifications from your SIP proxy, and in this tutorial, using [Apple Push Notification Service](https://developer.apple.com/library/archive/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/APNSOverview.html), you'll learn how to simply send the device push information to your server.
Apple will also require you to use their CallKit API when you receive a push notification, you must always notify CallKit when you receive a VOIP Push or your app will be terminated. If you use the "Core.pushEnabled=true" settings, as is done in this app, most of this work is done in the sdk and you only require to notify an incoming call when your Core Delegate notifies you of a new call.
Compared to the previous tutorials, some changes have been required in `CallKitTutorial.xcodeproj` in order to enable `Push Notifications` and `BackGround Modes (Voice Over IP)` in the capabilities of your project.

View File

@ -0,0 +1,424 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 51;
objects = {
/* Begin PBXBuildFile section */
34397F2DCE0A26771A291122 /* Pods_BasicChat.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 17BDC97FF3DCFB7B82244F46 /* Pods_BasicChat.framework */; };
664BC0E326EF459B007EE298 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 664BC0E226EF459B007EE298 /* AppDelegate.swift */; };
664BC0E526EF459B007EE298 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 664BC0E426EF459B007EE298 /* SceneDelegate.swift */; };
664BC0E726EF459B007EE298 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 664BC0E626EF459B007EE298 /* ContentView.swift */; };
664BC0E926EF459D007EE298 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 664BC0E826EF459D007EE298 /* Assets.xcassets */; };
664BC0EC26EF459D007EE298 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 664BC0EB26EF459D007EE298 /* Preview Assets.xcassets */; };
664BC0EF26EF459D007EE298 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 664BC0ED26EF459D007EE298 /* LaunchScreen.storyboard */; };
664BC0F726EF481A007EE298 /* BasicChat.swift in Sources */ = {isa = PBXBuildFile; fileRef = 664BC0F626EF481A007EE298 /* BasicChat.swift */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
17BDC97FF3DCFB7B82244F46 /* Pods_BasicChat.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_BasicChat.framework; sourceTree = BUILT_PRODUCTS_DIR; };
664BC0DF26EF459B007EE298 /* BasicChat.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = BasicChat.app; sourceTree = BUILT_PRODUCTS_DIR; };
664BC0E226EF459B007EE298 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
664BC0E426EF459B007EE298 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = "<group>"; };
664BC0E626EF459B007EE298 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
664BC0E826EF459D007EE298 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
664BC0EB26EF459D007EE298 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = "<group>"; };
664BC0EE26EF459D007EE298 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
664BC0F026EF459D007EE298 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
664BC0F626EF481A007EE298 /* BasicChat.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BasicChat.swift; sourceTree = "<group>"; };
C9B5BB74C5F76BED02B82031 /* Pods-BasicChat.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-BasicChat.release.xcconfig"; path = "Target Support Files/Pods-BasicChat/Pods-BasicChat.release.xcconfig"; sourceTree = "<group>"; };
F46732417EF2391C68D697D6 /* Pods-BasicChat.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-BasicChat.debug.xcconfig"; path = "Target Support Files/Pods-BasicChat/Pods-BasicChat.debug.xcconfig"; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
664BC0DC26EF459B007EE298 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
34397F2DCE0A26771A291122 /* Pods_BasicChat.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
05AF3043CF55B85A1A53217A /* Frameworks */ = {
isa = PBXGroup;
children = (
17BDC97FF3DCFB7B82244F46 /* Pods_BasicChat.framework */,
);
name = Frameworks;
sourceTree = "<group>";
};
664BC0D626EF459B007EE298 = {
isa = PBXGroup;
children = (
664BC0E126EF459B007EE298 /* BasicChat */,
664BC0E026EF459B007EE298 /* Products */,
9DDE73864BE97E2EE2764318 /* Pods */,
05AF3043CF55B85A1A53217A /* Frameworks */,
);
sourceTree = "<group>";
};
664BC0E026EF459B007EE298 /* Products */ = {
isa = PBXGroup;
children = (
664BC0DF26EF459B007EE298 /* BasicChat.app */,
);
name = Products;
sourceTree = "<group>";
};
664BC0E126EF459B007EE298 /* BasicChat */ = {
isa = PBXGroup;
children = (
664BC0E226EF459B007EE298 /* AppDelegate.swift */,
664BC0E426EF459B007EE298 /* SceneDelegate.swift */,
664BC0E626EF459B007EE298 /* ContentView.swift */,
664BC0E826EF459D007EE298 /* Assets.xcassets */,
664BC0ED26EF459D007EE298 /* LaunchScreen.storyboard */,
664BC0F026EF459D007EE298 /* Info.plist */,
664BC0F626EF481A007EE298 /* BasicChat.swift */,
664BC0EA26EF459D007EE298 /* Preview Content */,
);
path = BasicChat;
sourceTree = "<group>";
};
664BC0EA26EF459D007EE298 /* Preview Content */ = {
isa = PBXGroup;
children = (
664BC0EB26EF459D007EE298 /* Preview Assets.xcassets */,
);
path = "Preview Content";
sourceTree = "<group>";
};
9DDE73864BE97E2EE2764318 /* Pods */ = {
isa = PBXGroup;
children = (
F46732417EF2391C68D697D6 /* Pods-BasicChat.debug.xcconfig */,
C9B5BB74C5F76BED02B82031 /* Pods-BasicChat.release.xcconfig */,
);
path = Pods;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
664BC0DE26EF459B007EE298 /* BasicChat */ = {
isa = PBXNativeTarget;
buildConfigurationList = 664BC0F326EF459D007EE298 /* Build configuration list for PBXNativeTarget "BasicChat" */;
buildPhases = (
24E9CA4BD04B47A0932B80AF /* [CP] Check Pods Manifest.lock */,
664BC0DB26EF459B007EE298 /* Sources */,
664BC0DC26EF459B007EE298 /* Frameworks */,
664BC0DD26EF459B007EE298 /* Resources */,
3288DCA5B7DA5A1E061B4A8E /* [CP] Embed Pods Frameworks */,
);
buildRules = (
);
dependencies = (
);
name = BasicChat;
productName = BasicChat;
productReference = 664BC0DF26EF459B007EE298 /* BasicChat.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
664BC0D726EF459B007EE298 /* Project object */ = {
isa = PBXProject;
attributes = {
LastSwiftUpdateCheck = 1250;
LastUpgradeCheck = 1250;
TargetAttributes = {
664BC0DE26EF459B007EE298 = {
CreatedOnToolsVersion = 12.5.1;
};
};
};
buildConfigurationList = 664BC0DA26EF459B007EE298 /* Build configuration list for PBXProject "BasicChat" */;
compatibilityVersion = "Xcode 9.3";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = 664BC0D626EF459B007EE298;
productRefGroup = 664BC0E026EF459B007EE298 /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
664BC0DE26EF459B007EE298 /* BasicChat */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
664BC0DD26EF459B007EE298 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
664BC0EF26EF459D007EE298 /* LaunchScreen.storyboard in Resources */,
664BC0EC26EF459D007EE298 /* Preview Assets.xcassets in Resources */,
664BC0E926EF459D007EE298 /* Assets.xcassets in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
24E9CA4BD04B47A0932B80AF /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputFileListPaths = (
);
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-BasicChat-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
3288DCA5B7DA5A1E061B4A8E /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-BasicChat/Pods-BasicChat-frameworks-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Embed Pods Frameworks";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-BasicChat/Pods-BasicChat-frameworks-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-BasicChat/Pods-BasicChat-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
664BC0DB26EF459B007EE298 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
664BC0F726EF481A007EE298 /* BasicChat.swift in Sources */,
664BC0E326EF459B007EE298 /* AppDelegate.swift in Sources */,
664BC0E526EF459B007EE298 /* SceneDelegate.swift in Sources */,
664BC0E726EF459B007EE298 /* ContentView.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXVariantGroup section */
664BC0ED26EF459D007EE298 /* LaunchScreen.storyboard */ = {
isa = PBXVariantGroup;
children = (
664BC0EE26EF459D007EE298 /* Base */,
);
name = LaunchScreen.storyboard;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
664BC0F126EF459D007EE298 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 14.5;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
};
name = Debug;
};
664BC0F226EF459D007EE298 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 14.5;
MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES;
SDKROOT = iphoneos;
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_OPTIMIZATION_LEVEL = "-O";
VALIDATE_PRODUCT = YES;
};
name = Release;
};
664BC0F426EF459D007EE298 /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = F46732417EF2391C68D697D6 /* Pods-BasicChat.debug.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_ASSET_PATHS = "\"BasicChat/Preview Content\"";
DEVELOPMENT_TEAM = Z2V957B3D6;
ENABLE_PREVIEWS = YES;
INFOPLIST_FILE = BasicChat/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = BC.BasicChat;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
664BC0F526EF459D007EE298 /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = C9B5BB74C5F76BED02B82031 /* Pods-BasicChat.release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_ASSET_PATHS = "\"BasicChat/Preview Content\"";
DEVELOPMENT_TEAM = Z2V957B3D6;
ENABLE_PREVIEWS = YES;
INFOPLIST_FILE = BasicChat/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = BC.BasicChat;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
664BC0DA26EF459B007EE298 /* Build configuration list for PBXProject "BasicChat" */ = {
isa = XCConfigurationList;
buildConfigurations = (
664BC0F126EF459D007EE298 /* Debug */,
664BC0F226EF459D007EE298 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
664BC0F326EF459D007EE298 /* Build configuration list for PBXNativeTarget "BasicChat" */ = {
isa = XCConfigurationList;
buildConfigurations = (
664BC0F426EF459D007EE298 /* Debug */,
664BC0F526EF459D007EE298 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 664BC0D726EF459B007EE298 /* Project object */;
}

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:">
</FileRef>
</Workspace>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>

View File

@ -0,0 +1,38 @@
//
// AppDelegate.swift
// BasicChat
//
// Created by QuentinArguillere on 31/07/2020.
// Copyright © 2020 BelledonneCommunications. All rights reserved.
//
import UIKit
import SwiftUI
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
@ObservedObject var tutorialContext = BasicChatTutorialContext()
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
return true
}
// MARK: UISceneSession Lifecycle
func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
// Called when a new scene session is being created.
// Use this method to select a configuration to create the new scene with.
return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
}
func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {
// Called when the user discards a scene session.
// If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
// Use this method to release any resources that were specific to the discarded scenes, as they will not return.
}
}

View File

@ -0,0 +1,11 @@
{
"colors" : [
{
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,98 @@
{
"images" : [
{
"idiom" : "iphone",
"scale" : "2x",
"size" : "20x20"
},
{
"idiom" : "iphone",
"scale" : "3x",
"size" : "20x20"
},
{
"idiom" : "iphone",
"scale" : "2x",
"size" : "29x29"
},
{
"idiom" : "iphone",
"scale" : "3x",
"size" : "29x29"
},
{
"idiom" : "iphone",
"scale" : "2x",
"size" : "40x40"
},
{
"idiom" : "iphone",
"scale" : "3x",
"size" : "40x40"
},
{
"idiom" : "iphone",
"scale" : "2x",
"size" : "60x60"
},
{
"idiom" : "iphone",
"scale" : "3x",
"size" : "60x60"
},
{
"idiom" : "ipad",
"scale" : "1x",
"size" : "20x20"
},
{
"idiom" : "ipad",
"scale" : "2x",
"size" : "20x20"
},
{
"idiom" : "ipad",
"scale" : "1x",
"size" : "29x29"
},
{
"idiom" : "ipad",
"scale" : "2x",
"size" : "29x29"
},
{
"idiom" : "ipad",
"scale" : "1x",
"size" : "40x40"
},
{
"idiom" : "ipad",
"scale" : "2x",
"size" : "40x40"
},
{
"idiom" : "ipad",
"scale" : "1x",
"size" : "76x76"
},
{
"idiom" : "ipad",
"scale" : "2x",
"size" : "76x76"
},
{
"idiom" : "ipad",
"scale" : "2x",
"size" : "83.5x83.5"
},
{
"idiom" : "ios-marketing",
"scale" : "1x",
"size" : "1024x1024"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13122.16" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13104.12"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="EHf-IW-A2E">
<objects>
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" xcode11CocoaTouchSystemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="53" y="375"/>
</scene>
</scenes>
</document>

View File

@ -0,0 +1,241 @@
//
// BasicChat.swift
// BasicChat
//
// Created by QuentinArguillere on 08/09/2021.
// Copyright © 2021 BelledonneCommunications. All rights reserved.
//
import linphonesw
class BasicChatTutorialContext : ObservableObject
{
var mCore: Core!
@Published var coreVersion: String = Core.getVersion
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"
/*------------ Basic chat tutorial related variables -------*/
var mChatroom : ChatRoom?
var mChatMessageDelegate : ChatMessageDelegate!
var mChatMessage : ChatMessage?
var mLastFileMessageReceived : ChatMessage?
@Published var msgToSend : String = "msg"
@Published var remoteAddress : String = "sip:remote@sip.example.org"
@Published var canEditAddress : Bool = true
@Published var isDownloading : Bool = false
@Published var messagesReceived : String = ""
var fileFolderUrl : URL!
var fileUrl : URL!
init()
{
LoggingService.Instance.logLevel = LogLevel.Debug
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
// If the chat room wasn't existing, it is automatically created by the library
// If we already sent a chat message, the chatRoom variable will be the same as the one we already have
if (self.mChatroom == nil) {
if (chatRoom.hasCapability(mask: ChatRoomCapabilities.Basic.rawValue)) {
// Keep the chatRoom object to use it to send messages if it hasn't been created yet
self.mChatroom = chatRoom
if let remoteAddress = chatRoom.peerAddress?.asStringUriOnly() {
self.remoteAddress = remoteAddress
}
self.canEditAddress = false
}
}
// 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
self.messagesReceived += "\n--File available for download--"
} else if (content.isText) {
self.messagesReceived += "\nThem: \(message.utf8Text)"
}
}
}, 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")
if (state == .Ok) {
self.loggedIn = true
} else if (state == .Cleared) {
self.loggedIn = false
}
})
mCore.addDelegate(delegate: mRegistrationDelegate)
// This delegate has to be attached to each specific chat message we want to monitor, before sending it
mChatMessageDelegate = ChatMessageDelegateStub(onMsgStateChanged : { (message: ChatMessage, state: ChatMessage.State) in
print("MessageTrace - msg state changed: \(state)\n")
if (state == ChatMessage.State.FileTransferDone && self.isDownloading == true) {
self.isDownloading = false
} else if (state == .Delivered) {
self.messagesReceived += "\nMe: \(message.utf8Text)"
}
})
// example file to send
let documentsPath = NSURL(fileURLWithPath: NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0])
fileFolderUrl = documentsPath.appendingPathComponent("TutorialFiles")
fileUrl = fileFolderUrl?.appendingPathComponent("file_to_transfer.txt")
do{
try FileManager.default.createDirectory(atPath: fileFolderUrl!.path, withIntermediateDirectories: true, attributes: nil)
try String("My file content").write(to: fileUrl!, atomically: false, encoding: .utf8)
}catch let error as NSError{
print("Unable to create d)irectory",error)
}
}
func login() {
do {
var transport : TransportType
if (transportType == "TLS") { transport = TransportType.Tls }
else if (transportType == "TCP") { transport = TransportType.Tcp }
else { transport = TransportType.Udp }
let authInfo = try Factory.Instance.createAuthInfo(username: username, userid: "", passwd: passwd, ha1: "", realm: "", domain: domain)
let accountParams = try mCore.createAccountParams()
let identity = try Factory.Instance.createAddress(addr: String("sip:" + username + "@" + domain))
try! accountParams.setIdentityaddress(newValue: identity)
let address = try Factory.Instance.createAddress(addr: String("sip:" + domain))
try address.setTransport(newValue: transport)
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()
{
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)
mCore.clearAccounts()
mCore.clearAllAuthInfo()
}
}
func createBasicChatRoom() {
do {
// In this tutorial we will create a Basic chat room
// It doesn't include advanced features such as end-to-end encryption or groups
// But it is interoperable with any SIP service as it's relying on SIP SIMPLE messages
// If you try to enable a feature not supported by the basic backend, isValid() will return false
let params = try mCore.createDefaultChatRoomParams()
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)
// And finally we will need our local SIP address
let localAddress = mCore.defaultAccount?.params?.identityAddress
mChatroom = try mCore.createChatRoom(params: params, localAddr: localAddress, participants: [remote])
if (mChatroom != nil) {
canEditAddress = false
}
}
} catch { NSLog(error.localizedDescription) }
}
func sendMessage() {
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()
}
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 {
for content in message.contents {
if (content.isFileTransfer && content.filePath.isEmpty) {
let contentName = content.name
if (!contentName.isEmpty) {
content.filePath = fileFolderUrl!.appendingPathComponent(contentName).path
print("Start downloading \(content.name) into \(content.filePath)")
isDownloading = true
if (!message.downloadContent(content: content)) {
print ("Download of \(contentName) failed")
}
}
}
}
}
}
}

View File

@ -0,0 +1,144 @@
//
// ContentView.swift
// BasicChat
//
// Created by QuentinArguillere on 31/07/2020.
// Copyright © 2020 BelledonneCommunications. All rights reserved.
//
import SwiftUI
struct ActivityIndicator: UIViewRepresentable {
func makeUIView(context: UIViewRepresentableContext<ActivityIndicator>) -> UIActivityIndicatorView
{
return UIActivityIndicatorView(style: .medium)
}
func updateUIView(_ uiView: UIActivityIndicatorView, context: UIViewRepresentableContext<ActivityIndicator>)
{
uiView.startAnimating()
}
}
struct ContentView: View {
@ObservedObject var tutorialContext : BasicChatTutorialContext
var body: some View {
VStack {
Group {
HStack {
Text("Username:")
.font(.title)
TextField("", text : $tutorialContext.username)
.textFieldStyle(RoundedBorderTextFieldStyle())
.disabled(tutorialContext.loggedIn)
}
HStack {
Text("Password:")
.font(.title)
TextField("", text : $tutorialContext.passwd)
.textFieldStyle(RoundedBorderTextFieldStyle())
.disabled(tutorialContext.loggedIn)
}
HStack {
Text("Domain:")
.font(.title)
TextField("", text : $tutorialContext.domain)
.textFieldStyle(RoundedBorderTextFieldStyle())
.disabled(tutorialContext.loggedIn)
}
Picker(selection: $tutorialContext.transportType, label: Text("Transport:")) {
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()
self.tutorialContext.delete()
} 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)
HStack {
Text("Chat with:")
TextField("", text : $tutorialContext.remoteAddress)
.textFieldStyle(RoundedBorderTextFieldStyle())
.disabled(!tutorialContext.canEditAddress)
}
Text("Chat received").bold()
ScrollView {
Text(tutorialContext.messagesReceived)
.font(.footnote)
.frame(width: 330, height: 400)
}.border(Color.gray)
HStack {
TextField("Sent text", text : $tutorialContext.msgToSend)
.textFieldStyle(RoundedBorderTextFieldStyle())
Button(action: tutorialContext.sendMessage)
{
Text("Send")
.font(.callout)
.foregroundColor(Color.white)
.frame(width: 50.0, height: 30.0)
.background(Color.gray)
}
}
HStack {
Button(action: tutorialContext.sendFile)
{
Text("Send example \n file")
.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)
}.disabled(tutorialContext.mLastFileMessageReceived == nil)
if (tutorialContext.isDownloading) {
ActivityIndicator()
}
}
}
}
Group {
Spacer()
Text("Core Version is \(tutorialContext.coreVersion)")
}
}
.padding()
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView(tutorialContext: BasicChatTutorialContext())
}
}

View File

@ -0,0 +1,62 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UIApplicationSceneManifest</key>
<dict>
<key>UIApplicationSupportsMultipleScenes</key>
<false/>
<key>UISceneConfigurations</key>
<dict>
<key>UIWindowSceneSessionRoleApplication</key>
<array>
<dict>
<key>UISceneConfigurationName</key>
<string>Default Configuration</string>
<key>UISceneDelegateClassName</key>
<string>$(PRODUCT_MODULE_NAME).SceneDelegate</string>
</dict>
</array>
</dict>
</dict>
<key>UIApplicationSupportsIndirectInputEvents</key>
<true/>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
</dict>
</plist>

View File

@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,65 @@
//
// SceneDelegate.swift
// BasicChat
//
// Created by QuentinArguillere on 31/07/2020.
// Copyright © 2020 BelledonneCommunications. All rights reserved.
//
import UIKit
import SwiftUI
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
// Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
// If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
// This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
let delegate = UIApplication.shared.delegate as! AppDelegate
// Create the SwiftUI view that provides the window contents.
let contentView = ContentView(tutorialContext: delegate.tutorialContext)
// Use a UIHostingController as window root view controller.
if let windowScene = scene as? UIWindowScene {
let window = UIWindow(windowScene: windowScene)
window.rootViewController = UIHostingController(rootView: contentView)
self.window = window
window.makeKeyAndVisible()
}
}
func sceneDidDisconnect(_ scene: UIScene) {
// Called as the scene is being released by the system.
// This occurs shortly after the scene enters the background, or when its session is discarded.
// Release any resources associated with this scene that can be re-created the next time the scene connects.
// The scene may re-connect later, as its session was not neccessarily discarded (see `application:didDiscardSceneSessions` instead).
}
func sceneDidBecomeActive(_ scene: UIScene) {
// Called when the scene has moved from an inactive state to an active state.
// Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive.
}
func sceneWillResignActive(_ scene: UIScene) {
// Called when the scene will move from an active state to an inactive state.
// This may occur due to temporary interruptions (ex. an incoming phone call).
}
func sceneWillEnterForeground(_ scene: UIScene) {
// Called as the scene transitions from the background to the foreground.
// Use this method to undo the changes made on entering the background.
}
func sceneDidEnterBackground(_ scene: UIScene) {
// Called as the scene transitions from the foreground to the background.
// Use this method to save data, release shared resources, and store enough scene-specific state information
// to restore the scene back to its current state.
}
}

View File

@ -0,0 +1,24 @@
# Uncomment the next line to define a global platform for your project
platform :ios, '11.0'
source "https://gitlab.linphone.org/BC/public/podspec.git"
source "https://github.com/CocoaPods/Specs.git"
def basic_pods
if ENV['PODFILE_PATH'].nil?
pod 'linphone-sdk', '~> 5.0.0'
else
pod 'linphone-sdk', :path => ENV['PODFILE_PATH'] # local sdk
end
end
target 'BasicChat' do
# Comment the next line if you don't want to use dynamic frameworks
use_frameworks!
# Pods for BasicChat
basic_pods
end

View File

@ -0,0 +1,8 @@
Basic chat tutorial
====================
This tutorial will demonstrate how to send and display a simple SIP message containing either text or an image (but it works the same for any kind of file).
Note that for file transfer, a file transfer server is required. In this tutorial we'll use the one at `https://www.linphone.org:444/lft.php` that we use in our own linphone-android and linphone-iphone apps, but you can get its [source code](https://gitlab.linphone.org/BC/public/flexisip-http-file-transfer-server) and deploy your own.
Messages sent in this tutorial are standard SIP messages, so no matter the SIP proxy server you are using it should work, unlike the next [advanced chat tutorial](https://gitlab.linphone.org/BC/public/tutorials/-/tree/master/ios/swift/6-AdvancedChat).

View File

@ -0,0 +1,424 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 51;
objects = {
/* Begin PBXBuildFile section */
5F916A81CD75D10483A4DAC8 /* Pods_AdvancedChat.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E72FBDC953F141F267B03540 /* Pods_AdvancedChat.framework */; };
664BC12726F0CA1F007EE298 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 664BC12626F0CA1F007EE298 /* AppDelegate.swift */; };
664BC12926F0CA1F007EE298 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 664BC12826F0CA1F007EE298 /* SceneDelegate.swift */; };
664BC12B26F0CA1F007EE298 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 664BC12A26F0CA1F007EE298 /* ContentView.swift */; };
664BC12D26F0CA20007EE298 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 664BC12C26F0CA20007EE298 /* Assets.xcassets */; };
664BC13026F0CA20007EE298 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 664BC12F26F0CA20007EE298 /* Preview Assets.xcassets */; };
664BC13326F0CA20007EE298 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 664BC13126F0CA20007EE298 /* LaunchScreen.storyboard */; };
664BC13B26F0CA94007EE298 /* AdvancedChat.swift in Sources */ = {isa = PBXBuildFile; fileRef = 664BC13A26F0CA94007EE298 /* AdvancedChat.swift */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
664BC12326F0CA1F007EE298 /* AdvancedChat.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = AdvancedChat.app; sourceTree = BUILT_PRODUCTS_DIR; };
664BC12626F0CA1F007EE298 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
664BC12826F0CA1F007EE298 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = "<group>"; };
664BC12A26F0CA1F007EE298 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
664BC12C26F0CA20007EE298 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
664BC12F26F0CA20007EE298 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = "<group>"; };
664BC13226F0CA20007EE298 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
664BC13426F0CA20007EE298 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
664BC13A26F0CA94007EE298 /* AdvancedChat.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AdvancedChat.swift; sourceTree = "<group>"; };
8FCAA86A89E245EB7D78FAED /* Pods-AdvancedChat.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AdvancedChat.debug.xcconfig"; path = "Target Support Files/Pods-AdvancedChat/Pods-AdvancedChat.debug.xcconfig"; sourceTree = "<group>"; };
9B4A3E0699D8A5E8F930E207 /* Pods-AdvancedChat.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AdvancedChat.release.xcconfig"; path = "Target Support Files/Pods-AdvancedChat/Pods-AdvancedChat.release.xcconfig"; sourceTree = "<group>"; };
E72FBDC953F141F267B03540 /* Pods_AdvancedChat.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_AdvancedChat.framework; sourceTree = BUILT_PRODUCTS_DIR; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
664BC12026F0CA1F007EE298 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
5F916A81CD75D10483A4DAC8 /* Pods_AdvancedChat.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
47B1E7CE04184A06D7C8479E /* Frameworks */ = {
isa = PBXGroup;
children = (
E72FBDC953F141F267B03540 /* Pods_AdvancedChat.framework */,
);
name = Frameworks;
sourceTree = "<group>";
};
664BC11A26F0CA1F007EE298 = {
isa = PBXGroup;
children = (
664BC12526F0CA1F007EE298 /* AdvancedChat */,
664BC12426F0CA1F007EE298 /* Products */,
D66D20255E37FA73AC508469 /* Pods */,
47B1E7CE04184A06D7C8479E /* Frameworks */,
);
sourceTree = "<group>";
};
664BC12426F0CA1F007EE298 /* Products */ = {
isa = PBXGroup;
children = (
664BC12326F0CA1F007EE298 /* AdvancedChat.app */,
);
name = Products;
sourceTree = "<group>";
};
664BC12526F0CA1F007EE298 /* AdvancedChat */ = {
isa = PBXGroup;
children = (
664BC12626F0CA1F007EE298 /* AppDelegate.swift */,
664BC12826F0CA1F007EE298 /* SceneDelegate.swift */,
664BC12A26F0CA1F007EE298 /* ContentView.swift */,
664BC12C26F0CA20007EE298 /* Assets.xcassets */,
664BC13126F0CA20007EE298 /* LaunchScreen.storyboard */,
664BC13A26F0CA94007EE298 /* AdvancedChat.swift */,
664BC13426F0CA20007EE298 /* Info.plist */,
664BC12E26F0CA20007EE298 /* Preview Content */,
);
path = AdvancedChat;
sourceTree = "<group>";
};
664BC12E26F0CA20007EE298 /* Preview Content */ = {
isa = PBXGroup;
children = (
664BC12F26F0CA20007EE298 /* Preview Assets.xcassets */,
);
path = "Preview Content";
sourceTree = "<group>";
};
D66D20255E37FA73AC508469 /* Pods */ = {
isa = PBXGroup;
children = (
8FCAA86A89E245EB7D78FAED /* Pods-AdvancedChat.debug.xcconfig */,
9B4A3E0699D8A5E8F930E207 /* Pods-AdvancedChat.release.xcconfig */,
);
path = Pods;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
664BC12226F0CA1F007EE298 /* AdvancedChat */ = {
isa = PBXNativeTarget;
buildConfigurationList = 664BC13726F0CA20007EE298 /* Build configuration list for PBXNativeTarget "AdvancedChat" */;
buildPhases = (
70209B2F1633EA190EA8E09D /* [CP] Check Pods Manifest.lock */,
664BC11F26F0CA1F007EE298 /* Sources */,
664BC12026F0CA1F007EE298 /* Frameworks */,
664BC12126F0CA1F007EE298 /* Resources */,
16DC7246B49FC8754CAB12B5 /* [CP] Embed Pods Frameworks */,
);
buildRules = (
);
dependencies = (
);
name = AdvancedChat;
productName = AdvancedChat;
productReference = 664BC12326F0CA1F007EE298 /* AdvancedChat.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
664BC11B26F0CA1F007EE298 /* Project object */ = {
isa = PBXProject;
attributes = {
LastSwiftUpdateCheck = 1250;
LastUpgradeCheck = 1250;
TargetAttributes = {
664BC12226F0CA1F007EE298 = {
CreatedOnToolsVersion = 12.5.1;
};
};
};
buildConfigurationList = 664BC11E26F0CA1F007EE298 /* Build configuration list for PBXProject "AdvancedChat" */;
compatibilityVersion = "Xcode 9.3";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = 664BC11A26F0CA1F007EE298;
productRefGroup = 664BC12426F0CA1F007EE298 /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
664BC12226F0CA1F007EE298 /* AdvancedChat */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
664BC12126F0CA1F007EE298 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
664BC13326F0CA20007EE298 /* LaunchScreen.storyboard in Resources */,
664BC13026F0CA20007EE298 /* Preview Assets.xcassets in Resources */,
664BC12D26F0CA20007EE298 /* Assets.xcassets in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
16DC7246B49FC8754CAB12B5 /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-AdvancedChat/Pods-AdvancedChat-frameworks-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Embed Pods Frameworks";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-AdvancedChat/Pods-AdvancedChat-frameworks-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-AdvancedChat/Pods-AdvancedChat-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
70209B2F1633EA190EA8E09D /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputFileListPaths = (
);
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-AdvancedChat-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
664BC11F26F0CA1F007EE298 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
664BC12726F0CA1F007EE298 /* AppDelegate.swift in Sources */,
664BC12926F0CA1F007EE298 /* SceneDelegate.swift in Sources */,
664BC12B26F0CA1F007EE298 /* ContentView.swift in Sources */,
664BC13B26F0CA94007EE298 /* AdvancedChat.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXVariantGroup section */
664BC13126F0CA20007EE298 /* LaunchScreen.storyboard */ = {
isa = PBXVariantGroup;
children = (
664BC13226F0CA20007EE298 /* Base */,
);
name = LaunchScreen.storyboard;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
664BC13526F0CA20007EE298 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 14.5;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
};
name = Debug;
};
664BC13626F0CA20007EE298 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 14.5;
MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES;
SDKROOT = iphoneos;
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_OPTIMIZATION_LEVEL = "-O";
VALIDATE_PRODUCT = YES;
};
name = Release;
};
664BC13826F0CA20007EE298 /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 8FCAA86A89E245EB7D78FAED /* Pods-AdvancedChat.debug.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_ASSET_PATHS = "\"AdvancedChat/Preview Content\"";
DEVELOPMENT_TEAM = Z2V957B3D6;
ENABLE_PREVIEWS = YES;
INFOPLIST_FILE = AdvancedChat/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = BC.AdvancedChat;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
664BC13926F0CA20007EE298 /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 9B4A3E0699D8A5E8F930E207 /* Pods-AdvancedChat.release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_ASSET_PATHS = "\"AdvancedChat/Preview Content\"";
DEVELOPMENT_TEAM = Z2V957B3D6;
ENABLE_PREVIEWS = YES;
INFOPLIST_FILE = AdvancedChat/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = BC.AdvancedChat;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
664BC11E26F0CA1F007EE298 /* Build configuration list for PBXProject "AdvancedChat" */ = {
isa = XCConfigurationList;
buildConfigurations = (
664BC13526F0CA20007EE298 /* Debug */,
664BC13626F0CA20007EE298 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
664BC13726F0CA20007EE298 /* Build configuration list for PBXNativeTarget "AdvancedChat" */ = {
isa = XCConfigurationList;
buildConfigurations = (
664BC13826F0CA20007EE298 /* Debug */,
664BC13926F0CA20007EE298 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 664BC11B26F0CA1F007EE298 /* Project object */;
}

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:">
</FileRef>
</Workspace>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>

View File

@ -0,0 +1,276 @@
//
// GroupChat.swift
// GroupChat
//
// Created by QuentinArguillere on 08/09/2021.
// Copyright © 2021 BelledonneCommunications. All rights reserved.
//
import linphonesw
class AdvancedChatTutorialContext : ObservableObject
{
var mCore: Core!
@Published var coreVersion: String = Core.getVersion
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"
/*------------ Advanced chat tutorial related variables -------*/
var mChatroom : ChatRoom?
var mChatroomDelegate : ChatRoomDelegate!
var mChatMessageDelegate : ChatMessageDelegate!
var mChatMessage : ChatMessage?
var mLastFileMessageReceived : ChatMessage?
@Published var msgToSend : String = "msg"
@Published var remoteAddress : String = "sip:remote@sip.example.org"
@Published var canEditAddress : Bool = true
@Published var isDownloading : Bool = false
@Published var messagesReceived : String = ""
var fileFolderUrl : URL!
var fileUrl : URL!
init()
{
LoggingService.Instance.logLevel = LogLevel.Debug
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) {
// Check it is an one-to-one encrypted chat room
if (chatRoom.hasCapability(mask: ChatRoomCapabilities.OneToOne.rawValue) &&
chatRoom.hasCapability(mask: ChatRoomCapabilities.Encrypted.rawValue)) {
// Keep the chatRoom object to use it to send messages if it hasn't been created yet
self.mChatroom = chatRoom
self.mChatroom!.addDelegate(delegate: self.mChatroomDelegate)
self.enableEphemeral()
if let remoteAddress = chatRoom.peerAddress?.asStringUriOnly() {
self.remoteAddress = remoteAddress
}
self.canEditAddress = false
}
}
chatRoom.markAsRead()
for content in message.contents {
if (content.isFileTransfer) {
self.mLastFileMessageReceived = message
self.messagesReceived += "\n--File available for download--"
} else if (content.isText) {
self.messagesReceived += "\nThem: \(message.utf8Text)"
}
}
}, 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")
if (state == .Ok) {
self.loggedIn = true
} else if (state == .Cleared) {
self.loggedIn = false
}
})
mCore.addDelegate(delegate: mRegistrationDelegate)
// This delegate has to be attached to each specific chat message we want to monitor, before sending it
mChatMessageDelegate = ChatMessageDelegateStub(onMsgStateChanged : { (message: ChatMessage, state: ChatMessage.State) in
print("MessageTrace - msg state changed: \(state)\n")
if (state == .InProgress) {
} else if (state == .Delivered) {
// The proxy server has acknowledged the message with a 200 OK
self.messagesReceived += "\nMe: \(message.utf8Text)"
} else if (state == .DeliveredToUser) {
// User has received it
} else if (state == .Displayed) {
// User has read it (client called chatRoom.markAsRead()
} else if (state == .NotDelivered) {
// User might be invalid or not registered
} else if (state == .FileTransferDone && self.isDownloading == true) {
// We finished uploading/downloading the file
self.isDownloading = false
}
})
mChatroomDelegate = ChatRoomDelegateStub ( onStateChanged: { (chatRoom: ChatRoom, newState: ChatRoom.State?) in
if (newState == ChatRoom.State.Created) {
self.enableEphemeral()
}
}, onEphemeralEvent: { (chatRoom: ChatRoom, eventLog: EventLog) in
// This event is generated when the chat room ephemeral settings are being changed
}, onEphemeralMessageTimerStarted: { (chatRoom: ChatRoom, eventLog: EventLog) in
// This is called when a message has been read by all recipient, so the timer has started
}, onEphemeralMessageDeleted: { (chatRoom: ChatRoom, eventLog: EventLog) in
// This is called when a message has expired and we should remove it from the view
})
// example file to send
let documentsPath = NSURL(fileURLWithPath: NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0])
fileFolderUrl = documentsPath.appendingPathComponent("TutorialFiles")
fileUrl = fileFolderUrl?.appendingPathComponent("file_to_transfer.txt")
do{
try FileManager.default.createDirectory(atPath: fileFolderUrl!.path, withIntermediateDirectories: true, attributes: nil)
try String("My file content").write(to: fileUrl!, atomically: false, encoding: .utf8)
}catch let error as NSError{
print("Unable to create d)irectory",error)
}
}
func login() {
do {
var transport : TransportType
if (transportType == "TLS") { transport = TransportType.Tls }
else if (transportType == "TCP") { transport = TransportType.Tcp }
else { transport = TransportType.Udp }
let authInfo = try Factory.Instance.createAuthInfo(username: username, userid: "", passwd: passwd, ha1: "", realm: "", domain: domain)
let accountParams = try mCore.createAccountParams()
let identity = try Factory.Instance.createAddress(addr: String("sip:" + username + "@" + domain))
try! accountParams.setIdentityaddress(newValue: identity)
let address = try Factory.Instance.createAddress(addr: String("sip:" + domain))
try address.setTransport(newValue: transport)
try accountParams.setServeraddress(newValue: address)
accountParams.registerEnabled = true
// We need a conference factory URI set on the Account to be able to create chat rooms with flexisip backend
accountParams.conferenceFactoryUri = "sip:conference-factory@sip.linphone.org"
mCore.addAuthInfo(info: authInfo)
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()
{
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)
mCore.clearAccounts()
mCore.clearAllAuthInfo()
}
}
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)
// And finally we will need our local SIP address
let localAddress = mCore.defaultAccount?.params?.identityAddress
mChatroom = try mCore.createChatRoom(params: params, localAddr: localAddress, participants: [remote])
// 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()
canEditAddress = false
}
}
} catch { NSLog(error.localizedDescription) }
}
func sendMessage() {
do {
if (mChatroom == nil) {
createFlexisipChatRoom()
}
mChatMessage = nil
mChatMessage = try mChatroom!.createMessageFromUtf8(message: msgToSend)
mChatMessage!.addDelegate(delegate: mChatMessageDelegate)
mChatMessage!.send()
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"
content.subtype = "plain"
content.filePath = fileUrl.path
let chatMessage = try mChatroom!.createFileTransferMessage(initialContent: content)
chatMessage.addDelegate(delegate: mChatMessageDelegate)
mCore.fileTransferServer = "https://www.linphone.org:444/lft.php"
chatMessage.send()
} catch { NSLog(error.localizedDescription) }
}
func downloadLastFileMessage() {
if let message = mLastFileMessageReceived {
for content in message.contents {
if (content.isFileTransfer && content.filePath.isEmpty) {
let contentName = content.name
if (!contentName.isEmpty) {
content.filePath = fileFolderUrl!.appendingPathComponent(contentName).path
print("Start downloading \(content.name) into \(content.filePath)")
isDownloading = true
if (!message.downloadContent(content: content)) {
print ("Download of \(contentName) failed")
}
}
}
}
}
}
func enableEphemeral() {
// Once chat room has been created, we can enable ephemeral feature
// We enable ephemeral messages at the chat room level
// Please note this only affects messages we send, not the ones we receive
mChatroom?.ephemeralEnabled = true
// Here we ask for a lifetime of 60 seconds, starting the moment the message has been read
mChatroom?.ephemeralLifetime = 60
}
}

View File

@ -0,0 +1,38 @@
//
// AppDelegate.swift
// BasicChat
//
// Created by QuentinArguillere on 31/07/2020.
// Copyright © 2020 BelledonneCommunications. All rights reserved.
//
import UIKit
import SwiftUI
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
@ObservedObject var tutorialContext = AdvancedChatTutorialContext()
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
return true
}
// MARK: UISceneSession Lifecycle
func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
// Called when a new scene session is being created.
// Use this method to select a configuration to create the new scene with.
return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
}
func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {
// Called when the user discards a scene session.
// If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
// Use this method to release any resources that were specific to the discarded scenes, as they will not return.
}
}

View File

@ -0,0 +1,11 @@
{
"colors" : [
{
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,98 @@
{
"images" : [
{
"idiom" : "iphone",
"scale" : "2x",
"size" : "20x20"
},
{
"idiom" : "iphone",
"scale" : "3x",
"size" : "20x20"
},
{
"idiom" : "iphone",
"scale" : "2x",
"size" : "29x29"
},
{
"idiom" : "iphone",
"scale" : "3x",
"size" : "29x29"
},
{
"idiom" : "iphone",
"scale" : "2x",
"size" : "40x40"
},
{
"idiom" : "iphone",
"scale" : "3x",
"size" : "40x40"
},
{
"idiom" : "iphone",
"scale" : "2x",
"size" : "60x60"
},
{
"idiom" : "iphone",
"scale" : "3x",
"size" : "60x60"
},
{
"idiom" : "ipad",
"scale" : "1x",
"size" : "20x20"
},
{
"idiom" : "ipad",
"scale" : "2x",
"size" : "20x20"
},
{
"idiom" : "ipad",
"scale" : "1x",
"size" : "29x29"
},
{
"idiom" : "ipad",
"scale" : "2x",
"size" : "29x29"
},
{
"idiom" : "ipad",
"scale" : "1x",
"size" : "40x40"
},
{
"idiom" : "ipad",
"scale" : "2x",
"size" : "40x40"
},
{
"idiom" : "ipad",
"scale" : "1x",
"size" : "76x76"
},
{
"idiom" : "ipad",
"scale" : "2x",
"size" : "76x76"
},
{
"idiom" : "ipad",
"scale" : "2x",
"size" : "83.5x83.5"
},
{
"idiom" : "ios-marketing",
"scale" : "1x",
"size" : "1024x1024"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13122.16" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13104.12"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="EHf-IW-A2E">
<objects>
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" xcode11CocoaTouchSystemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="53" y="375"/>
</scene>
</scenes>
</document>

View File

@ -0,0 +1,144 @@
//
// ContentView.swift
// BasicChat
//
// Created by QuentinArguillere on 31/07/2020.
// Copyright © 2020 BelledonneCommunications. All rights reserved.
//
import SwiftUI
struct ActivityIndicator: UIViewRepresentable {
func makeUIView(context: UIViewRepresentableContext<ActivityIndicator>) -> UIActivityIndicatorView
{
return UIActivityIndicatorView(style: .medium)
}
func updateUIView(_ uiView: UIActivityIndicatorView, context: UIViewRepresentableContext<ActivityIndicator>)
{
uiView.startAnimating()
}
}
struct ContentView: View {
@ObservedObject var tutorialContext : AdvancedChatTutorialContext
var body: some View {
VStack {
Group {
HStack {
Text("Username:")
.font(.title)
TextField("", text : $tutorialContext.username)
.textFieldStyle(RoundedBorderTextFieldStyle())
.disabled(tutorialContext.loggedIn)
}
HStack {
Text("Password:")
.font(.title)
TextField("", text : $tutorialContext.passwd)
.textFieldStyle(RoundedBorderTextFieldStyle())
.disabled(tutorialContext.loggedIn)
}
HStack {
Text("Domain:")
.font(.title)
TextField("", text : $tutorialContext.domain)
.textFieldStyle(RoundedBorderTextFieldStyle())
.disabled(tutorialContext.loggedIn)
}
Picker(selection: $tutorialContext.transportType, label: Text("Transport:")) {
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()
self.tutorialContext.delete()
} 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)
HStack {
Text("Chat with:")
TextField("", text : $tutorialContext.remoteAddress)
.textFieldStyle(RoundedBorderTextFieldStyle())
.disabled(!tutorialContext.canEditAddress)
}
Text("Chat received").bold()
ScrollView {
Text(tutorialContext.messagesReceived)
.font(.footnote)
.frame(width: 330, height: 400)
}.border(Color.gray)
HStack {
TextField("Sent text", text : $tutorialContext.msgToSend)
.textFieldStyle(RoundedBorderTextFieldStyle())
Button(action: tutorialContext.sendMessage)
{
Text("Send")
.font(.callout)
.foregroundColor(Color.white)
.frame(width: 50.0, height: 30.0)
.background(Color.gray)
}
}
HStack {
Button(action: tutorialContext.sendFile)
{
Text("Send example \n file")
.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)
}.disabled(tutorialContext.mLastFileMessageReceived == nil)
if (tutorialContext.isDownloading) {
ActivityIndicator()
}
}
}
}
Group {
Spacer()
Text("Core Version is \(tutorialContext.coreVersion)")
}
}
.padding()
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView(tutorialContext: AdvancedChatTutorialContext())
}
}

Some files were not shown because too many files have changed in this diff Show More