Tag: iOS应用开发

使用Vapor Server Side Swift框架存根XCUITests

最初在XCBlog上发布在这里 毫无疑问,Swift是用于为iOS,macOS,watchOS和tvOS等Apple平台开发本机应用程序的出色语言。 但是,作为一种新语言,Swift缺少许多测试功能,就像我们在其他编程语言(如Ruby)中看到的那样。 在Swift中使用协议模拟类非常困难,并且存根网络请求的选项非常有限。 模拟是脆弱而艰苦的,到Swift时模拟变得更加困难。 Swift中没有可用的成熟模拟库来生成类似Java,Ruby,Python或其他语言的模拟。 开发人员必须手动编写所有模拟,并将测试代码与生产代码紧密耦合。 这里有一篇很棒的关于iOS网络测试的文章,以了解有关如何使用协议进行模拟的更多信息。 存根是测试代码的另一种方式,而不必依赖后端或网络。 使用存根,我们仍然可以实现网络测试的目标,并且不必将测试代码与生产代码紧密耦合。 我们将看到可用于存根的库的详细信息。 一些最受欢迎的库是用于XCTest(unit)测试的OHHTTPStubs,Mockingjay,Hippolyte。 用户界面测试贯穿网络层,涵盖了网络测试的所有方面。 苹果有XCUITest框架来涵盖Xcode UI测试。 我们可以使用一些库来为UITest存根网络。 一些流行的库包括Swifter,SBTUITestTunel和XCUITest(UITest)的Embassy。 但是,市场上出现了新的服务器端Swift框架,例如Vapor,Kitura,Perfect,它们也可以用于暂存网络请求。 在这篇文章中,我们将看到如何使用Vapor服务器端swift框架对网络请求进行存根。 Apple在WWDC 2015上宣布了Xcode的UI测试支持。UI测试被设计为完全黑盒,无需访问主应用程序代码和数据。 XCUITest使用两个独立的进程Test Runner和Target应用程序,测试运行器启动了目标应用程序,并使用UIKit内置的可访问性功能与在单独进程中运行的应用程序进行交互。 在嘲笑或测试UITest时,我们几乎没有选择 模拟和通过发射参数/环境 这意味着您不能直接模拟API。 与代理应用程序通信的唯一方法是传递启动参数或启动环境。 我们可以传递模拟API并创建启动环境变量,然后将其传递给XCUITests,但这需要大量的工作。 代码用if-else语句括起来,以确定听起来不太好的构建配置。 Web服务器存根后端并返回响应 剩下的其他选择是对网络呼叫进行存根并从服务器获取所需的数据。 为此,我们需要模拟后端并指向我们的应用程序以使用这些端点。 问题是我们是否仍可以使用上面提到的存根服务进行单元测试。 我们仍然可以使用这些库来处理网络请求,但是有时它需要更改主应用程序的逻辑,并且需要重构某些东西以增加额外的工作。 我们需要在生产代码中添加很多测试代码。 在我先前关于XCUITest的网络存根选项的文章中,我们介绍了所有选项,例如Swifter,SBTUITestTunel和Embassy for XCUITest。 现在,让我们详细介绍一下Vapor。 服务器端Swift框架尚未投入生产,但我们可以将其用于存根XCUITest的目的。 我还没有在互联网上读过任何人做的书,但是它工作得很好。 市场上几乎没有可用的Server Side Swift框架,例如Perfect,Kitura,Zewo和Vapor,但在此演示中,我们将使用Vapor。 选择蒸气的原因是它很容易学习,并且蒸气周围的社区正在迅速发展。 它完全由Swift编写,并带有易于上手的Vapor工具箱。 为了演示此功能,我创建了一个演示应用程序,当我们在文本字段中输入Github用户名时,它将显示Github用户信息。 您可以从Github Vapor-XCTest克隆该应用程序,该应用程序具有带有和不带有存根的XCUITests。 您需要安装Xcode 9+和OpenSSL,然后我们才能签出演示应用程序。 $ git clone https://github.com/Shashikant86/Vapor-XCTest […]

可编码协议

从Objective-C到Swift的过渡当然也取得了成果-尽管有时陡峭的学习曲线和不断发展的语言性质有时可能会带来挑战。 (当然也有挫折 。多年来,iOS处理设备轮换的多种方式越少说越好!) 对我来说,在不遥远的历史中最能引起欢乐的变化之一就是添加了Codable协议。 Swift 5 刚刚发布(并获得了广泛好评),但是-在Swift 4中增加了Codable-对我而言,这是一个更加令人兴奋的发行版。 可编码协议 Codable是一个非常漂亮的Swift协议。 在Swift中(与Objective-C相反),相比于继承,它更加强调组合,使用协议在不使用子类的情况下向类,结构或枚举实例添加功能。 实现协议的类,结构或枚举被称为与该协议兼容 ,从而在该过程中获得了该协议的功能。 可编码的独特之处在于,它实际上只是可Encodable和可Decodable协议的结合的Encodable Decodable 。 一个实例可以独立地符合任何一个,但是如果它同时符合两个,那么它也是可编码的。 这些协议给我们带来了什么? 让我们来看看! 编码利益 许多应用程序的一项常见操作是从API提取内容。 例如,我们的KPCC API客户端下载文章,剧集和节目时间表数据,并将其转换为适用于应用程序的模型实例。 这是解码的示例(也称为反序列化)。 我们使用数据的序列化表示形式(在本例中为JSON),并将其转换为可以使用的形式。 我们还可以反向执行此操作,将数据编码(序列化)为一种格式,该格式可以传递回服务器或以本地缓存的形式保存到用户的设备中。 总而言之,我们得到了一个用于编码/解码的标准化系统,并且有机会删除许多样板代码。 我们的KPCC API客户端项目在很大程度上依赖于使用Codable来获取API响应,并使用它们返回本地模型实例。 基本编码和解码 符合Codable很简单。 假设您有一个结构,只需将Codable协议添加到您的结构定义中,如下所示: struct Cat:可编码{ var age:Int 变量名称:字符串 } 而已! 现在,如果您希望对该Cat结构进行编码并将其保存到用户的设备中,然后将其读回,则可以这样进行: let apollo = Cat(年龄:10,名字:“ Apollo”) 让apolloData =尝试吗? JSONEncoder()。encode(apollo) 让documentsURL = FileManager.default.urls(用于:.documentDirectory,在:.userDomainMask中)。 让fileURL = documentsURL?.appendingPathComponent(“ Cat.json”) […]

AVAudioSession简介

这周,我将谈论一个小得多的话题,但这个话题与我的内心深处息息相关。 我从音频界开始从事iOS开发,我总是喜欢谈论Swift中的音频。 今天,我在写有关AVAudioSession的文章。 花一点时间考虑一下我们如何在应用程序中使用音频。 有时,我们会出现一些小错误和bloop来提醒用户某些事情。 其他时候,我们会提供游戏的背景音乐或视频中的声音。 有时,可以通过手机侧面的静音开关关闭声音。 一切都很好,但是如果我们有一个时钟应用程序,并且需要警报来覆盖该开关怎么办? 如果我们希望我们的应用在手机屏幕锁定时继续播放音乐怎么办? 当我们的应用程序播放声音而另一个应用程序播放音乐时会发生什么? 应用程序通过使用音频会话来处理此问题。 关于iOS应用中的音频的第一件事是,您的应用不会直接接触设备的任何音频硬件。 该应用程序通过中介与操作系统进行通信。 该中介是音频会话,特别是AVAudioSession。 看! Apple文档中的方便图形! 启动应用程序时,在幕后将为它提供AVAudioSession的单例实例。 我们的应用程序可以使用AVAudioSession的共享实例来配置应用程序中音频的行为(首先必须导入AVFoundation)。 要调整AVAudioSession的总体行为,我们可以将其设置为以下几类之一。 AVAudioSession具有一个简单的方法: setCategory(_:mode:options 🙂 。 请注意,您可以设置的类别由全局字符串常量表示,例如“ AVAudioSessionCategoryAmbient” 那么,这些类别中的每一个都有什么作用? 好吧,我不会遍历其中的每一个,因此,这里有一个方便的图表以获取更多详细信息。 AVAudioSessionCategoryAmbient是默认类别。 设置为此时,来自应用程序的音频将通过静音开关关闭,不会中断来自其他应用程序的音频,并且不会接受任何音频输入。 如果我们将类别设置为AVAudioSessionCategoryPlayAndRecord,则每个类别的情况都将相反。 重要说明:如果您打算在应用程序处于后台运行时播放音频,则必须更改info.plist文件中的某些设置。 幸运的是,Xcode有一个简单的方法可以做到这一点。 只需进入项目功能,打开背景模式,然后选择“音频,Airplay和画中画”即可。 在每个AVAudioSession类别中,可以设置许多模式,这将为您的会话添加更多功能。 同样,这些模式中的每一个都是由常量表示的字符串。 我想知道这些值是否是从较旧的Objective-C代码中遗留下来的,因为通常在Swift中,这看起来像枚举通常表示的那种。 无论如何, 这是每个列表 。 这些模式是针对特定用例进行优化的较小的性能调整。 例如,在具有多个麦克风的设备上,AVAudioSessionModeVideoRecording将打开最靠近该设备摄像机的麦克风。 仅当您设置了较大的类别AVAudioSessionCategoryRecord和AVAudioSessionCategoryPlayAndRecord时,它才起作用。 要考虑的另一重要事项是AVAudioSession的激活和停用,以及在激活或停用失败的情况下的错误处理。 首先,这是来自Apple文档的绝对荒谬的图像。 我发誓我没有化妆: 这是他们的…有关AVAudioSession激活工作原理的解释。 在适当的时间激活和停用音频会话非常重要。 如果您在激活音频会话后将其激活,则您的应用将崩溃。 这是直接从Apple文档中获得的所有内容 : let session = AVAudioSession.sharedInstance() 做{ […]

Swift中的原型设计模式

通过复制现有对象的所有属性并创建独立的克隆,原型模式用于实例化新对象。 当新对象的构造效率低下时,此做法特别有用。 想象一下,下面有一个SmartPhone类: 现在我们需要创建此类的5个实例,如下所示: 我们可以很容易地意识到许多实例具有相同的属性,因此我们经常复制代码行,然后以不同的方式编辑任何属性。 没关系,但不是经过优化且容易出错。 让我们尝试将Prototype模式应用到下面的SmartPhone类中: 我们声明一个带有默认可选参数的克隆 func与属性相同。 该函数通过复制现有实例的所有属性返回一个新实例,然后根据需要修改某些属性。 现在创建5个实例作为波纹管: 我们不再需要通过调用设计的初始化程序来初始化新实例,只需“克隆”当前实例然后对其进行修改! 就这样! 原型模式非常易于理解,也易于通过其他编程语言实现。 当新对象的构造效率低下时,此功能特别有用。 如果您有任何疑问,请留下您的赞赏,感谢您的阅读!

iPad App开发:为什么印度是Wagon Master?

通过Daffodil Software 一项研究表明,到2018年,美国的iPad用户数量预计将达到809亿 。 如此高的数字生动地证明了最近对iPad应用程序开发的呼声越来越高。 苹果应用商店拥有超过一百万个用于平板设备的本地应用。 这肯定是由于企业通过移动设备(以及智能手机和可穿戴设备)接触潜在客户和用户的事实不遗余力。 这些细节使我们很接近iPad应用程序开发外包为何成为当前趋势主题的原因。 在这种情况下,越来越受到关注的是印度成为开发iPad应用程序的首选。 在后面的部分中,我们将找出在苹果iPad应用程序开发方面使企业对印度保持兴趣的理由。 1.印度是技术熟练的海洋 “ 如果我们停止从印度雇用工程师,那么印度将再有一个微软 。”微软联合创始人比尔·盖茨认为。 作为美国科技偶像,IBM雇用的印第安人多于美国人 。 一项研究表明, 美国宇航局的科学家中有 3 6%是印第安人 。 这些事实支持印度人在IT和工程领域的信誉。 凭借绝对的编程技能和逻辑思维,将iPad应用程序开发外包到印度绝对是任何企业的最佳选择。 2.印度的开发者社区庞大 仅次于美国,印度拥有最大的开发商社区。 据信,到2018年,这一数字将达到400万,这将使印度超过美国。 考虑到这一点,苹果公司已经分享了在印度班加罗尔开设新的iOS应用程序设计和开发加速器的计划。 目的是为本地iOS开发人员提供专业支持。 该开发人员社区包括公司雇用的人员和自由职业者。 因此,在印度寻找iPad应用开发公司不再是一项艰巨的任务。 3.印度比美国便宜10倍 这是印度成为外包IT服务的首选的最明显原因之一。 平均而言,如果一个应用需要花7到8周的时间进行开发,则其成本大约为48,000美元,比印度的价格高10倍。 定价模式的巨大差异驱使世界各地的企业和个人雇用印度开发商。 就iPad应用程序的开发成本而言,印度提供了最有效的开发模型,因此,印度被认为是雇用相同程序员的最主要选择。 4.印度的政府政策是灵活的 外包一直是印度政府的主要收入来源,因此它允许100%的外国直接投资(FDI)。 根据2000年IT法案,印度IT入伍,这是该国最大的投资领域。 考虑到这一点,印度可以算是外包IT服务(包括iPad应用程序开发)的最佳场所。 5.改变时区是一个优势 印度时区与其他国家的时区差异很大。 因此,就有机会在预期时间或之前完成并交付项目。 例如:在白天,在美国一天结束前发送给印度的项目需求已完成。 因此,这可以提高生产率,定期报告项目状态,并有时间专注于更相关的任务。 另请阅读: 外包IT服务:一家公司与Freelancer 由于可用选项太多,因此很难在印度列出iPad应用程序开发合作伙伴的难度。 您可以选择知名的应用程序开发公司(例如Daffodil),也可以访问汇总网站(例如Clutch,Good Firms等)做出明智的决定。 最初发布在 appdevelopment.daffodilsw.com上 。

挥舞着2019年:值得关注的iOS应用开发趋势!

iOS永远领先一切。 推出来年的功能或发布一些新闻。 当然,与技术相关的人员和公司绝不应落后于趋势。 来到2019年的iOS趋势中,它有很多事情要做。 问候现代iOS开发人员和软件工程师之后,苹果公司已经在iPhone和iPad中带来了市场上的最新趋势。 虽然软件开发公司已经在尽最大努力从其应用程序开发过程中获益,但我们还是需要一些服务。 如果您是有才华的iOS开发人员之一,并且希望跟上即将到来的移动应用程序趋势,请在此处注意以下几点: 智能家居,智能世界 我们都听说过物联网和机器学习方面的知识,我们非常喜欢将这些应用程序引入标签和手机中。 但是,如果iOS今年本身就将事情摆在桌面上呢? 印度著名的iOS应用开发人员已开始设计用于多用途家庭用途的特定iOS应用。 此外,苹果用户对家用套件非常熟悉,他们不会阻止他们充分利用它。 Siri的语音命令-有关AI的全部内容 苹果在人工智能和机器学习领域几乎占据了主导地位。 出于同样的考虑,Siri已经开始以出色的抓地力和集成的惊人Siri快捷方式来处理不同的语音命令。 这也引起了iOT设备的关注。 苹果已经为iOS开发人员推出了Sirikit,它可以与人声命令无缝协作。 因此,现在Apple用户可以自由使用他们的手机,而无需解锁手机。 借助启用了Sirikit的人工智能的这些巨大好处,Apple用户都很好地欢迎2019年iOS趋势。 此外,快速编程语言的使用增加了 多笔交易 Apple知道随时随地进行快速交易的必要性。 在技​​术时代,世界太小了,而且相互联系,在这种时代中,更好地考虑轻松交易是可以接受的方法。 随着我们越来越接近电子钱包将成为主流的世界,越来越多的企业正在寻找最好的应用程序来协助 仅苹果一家公司就见证了上一年的10亿笔交易,苹果公司的薪酬已经证明了其重要性。 在用户可行性方面,Apple一直处在前一个阶段,而在2019年,Apple具有更高的水平。 ML2工具包 机器学习和人工智能是这个时代蓬勃发展的技术术语。 ML 2工具箱在开发移动应用程序中起着至关重要的作用。 2019年将见证Core ML 2的超强发布,并将增强顶级移动应用程序开发公司的实力。 更快,更安全,更强大。 全新的核心ML 2是通过一流的改进来美化iOS功能及其完整性的工具。 增强安全性 对于iOS应用程序开发,苹果已将应用程序传输安全性强制性化。 Apple的应用程序传输安全性定义了iOS移动应用程序中新的安全级别。 作为iOS应用程序的传统,兼容性和安全性每年都得到前所未有的更新和扩展。

iOS Huddle#4

2016年12月29日星期四 iOS Huddle是我们每月在Black Pixel iOS团队的Slack频道中共享的最佳链接的摘要。 反应式编程 如今,反应性编程似乎风行一时。 如果您对反应式编程感到好奇,我们认为Cocoa with Love的这篇文章很好地介绍了其背后的一些概念。 Matt Gallagher通过以下原则定义了反应式编程: 可变状态的任何“获取器”都会引起问题。 代替使用getter,任何计算,生成,加载或接收的状态值都应立即发送到通道中,并且想要访问这些值的程序的任何部分都必须订阅该通道。 本文就一些简单用例的KVO有多高提出了一些很好的观点,并概述了我们在使用它时遇到的一些典型问题。 与往常一样,反应式编程可能不是万灵药,但它提供了一种不同的方式来了解如何设计应用程序。 的GitHub GitHub现在允许您直接从PR中解决合并冲突。 对于最简单的问题,这是一个实时保护程序。 对于更复杂的问题,万花筒仍然是我们的最爱。 元编程 DRY是一个好概念,通常不那么容易实现。 有些人认为过早地进行DRYing类似于过早的优化。 其他人发誓。 无论您对DRY感觉如何,我们可能都同意的一件事是,样板代码的实现可能是乏味,重复且容易出错的。 我在看您,JSON序列化和NSCoding! 因此,为什么不使用一些不错的元编程和自动代码生成来替换其中的部分或全部。 这就是Sourcery可以为您做的。 您可以查看该崩溃报告吗? 我们中有多少人被问到这个问题并交出了未标记的堆栈跟踪信息? 没什么好玩的。 幸运的是,Apple写了一篇非常不错的技术说明,标题为“了解和分析应用程序崩溃报告”。 快速游乐场 Swift Playgrounds确实可以帮助您快速制作原型并测试想法。 操场也可以用来隔离一些特定的问题。 PlayAlways可让您直接从菜单栏中快速创建Xcode游乐场。 幕后花絮 当Instagram展示其如何重新设计其Feed时,深入介绍实现细节时,您知道您可能会学到一些东西。 Instagram软件工程师Ryan Nystrom的这次演讲并不令人失望。 对于任何好奇或渴望完善自己的工艺的人来说,这都是必看的。 尝试? 与As? 这是Erica Sadun撰写的简短文章,概述了如何混合尝试? 与作为? 和条件绑定可能会带来意想不到的后果。 我的记忆在哪里? 如果像我们一样,您是Mike Ash深入技术博客的狂热读者,您将对他的GOTO 2016会议演讲感到高兴:对Swift内存布局的探索。 请放大此文本 iOS 7为iOS应用程序引入了全新的视觉设计范例。 […]

为iPhone X调整Apps UI时如何解决最常见的界面问题

在不到1个月的时间内,iPhone X就会投放市场。 全球的苹果迷迫不及待地想要购买这款外观精美的智能手机。 作为开发人员,调整您的应用程序以支持iPhone X边缘到边缘屏幕是一项不容忽视的任务。 在本文中,我将向您展示在iPhone X上运行现有应用程序时可能会遇到的一些常见UI问题,以及如何仅使用界面生成器进行一些简单的调整即可解决该问题。 请注意,我们将要讨论的问题通常在尝试在Xcode 9上运行Xcode 8或更低版本的项目时发生。但是,在使用Xcode 9创建新应用程序时,每种解决方案背后的概念仍然非常有用。让我们开始吧! 上图显示了当我们尝试在新的iPhone X上运行我们的应用程序时一个非常常见的UI问题。现在,让我们关注Label-A 。 问题的原因是因为我们假设状态栏的高度并在Label-A.Top和它的Superview.Top之间设置了垂直空间约束。 由于iPhone X的状态栏高度与所有以前的iPhone不同,因此这会导致Label-A错位到状态栏下方。 要解决此问题,我们将需要使用Xcode 9界面构建器中引入的“ 安全区域布局指南 ”。 ( 您可以 参考 Apple的 本 指南 以了解有关安全区域的更多信息 )。 首先,启用“安全区域布局指南”。 接下来,我们需要通过在Label-A.Top和Safe Area.Top之间建立关系来更新Label-A的top约束。 而已! 现在,当您尝试在iPhone X模拟器上运行该应用程序时,您将能够看到Label-A神奇地将其自身定位在正确的位置。 您基本上可以应用相同的概念来固定Label-B的位置。 只需通过在Label-B.Bottom和Safe Area.Bottom之间建立关系来更新Label-B的底部约束。 这是更改后的最终产品,这要归功于“安全区域布局指南”,您可以在iPhone 8和iPhone X屏幕尺寸上正确看到Label-A和Label-B布局。 💪 通过遵循Apple提供的指南,背景图像和UITableView(或UIScrollView)都应始终延伸到边缘并填满整个iPhone X屏幕。 上面的UI问题的原因与我们在“ 问题1 ”中讨论的原因完全相反。 以背景图片为例,屏幕顶部的间隙是由我们在ImageView.Top和Top Layout Guide.Bottom之间设置的约束导致的。 为了使图像视图占据整个屏幕,我们需要在图像视图与其超级视图之间具有顶部和底部约束。 因此,我们需要做的是将Top Layout Guide.Bottom更改为Superview.Top并将Bottom Layout […]

Swift:forIn和forEach循环!

Swift提供了各种类型的循环进行迭代。 即使对于正在学习编程语言的新开发人员,也很容易理解和使用。 在这里,我想描述两种类型的for循环 ,乍一看似乎很令人困惑。 它们的工作方式相似,但是两者之间存在一些基本差异。 我在WordCount对象的函数中都尝试过。 下面是操场的快照。 如果您在操场上方看到直升机视图,则表明这两个for循环之间完全没有区别,以访问或循环访问数组的元素。 这是使用forIn和forEach的两个区别! 1)您不能使用break或Continue语句退出当前的闭包调用或跳过forEach循环中的后续调用,但可以在forIn循环中执行此操作。 2)在ForEach循环中使用return语句仅针对闭包中的当前调用退出,不会跳过后续调用,但ForIn循环也退出所有后续后续调用。 和上面的操场一样, forEach跳过了单词“ two”的打印语句的执行,这意味着它不会超出printByForEach函数。 而forIn跳过了所有随后的print语句的执行,这意味着它不在printByForIn函数中。 快乐编码,继续!!!

iOS应用开发服务

计划新的移动应用程序需要大量的时间和金钱投资。 每个企业都梦想着创建一个可以被成千上万的用户下载的应用程序,并在最短的时间内开始实现业务目标。 设计并维护应用程序并不是开玩笑,并不是每个人都可以胜任。 因此,选择正确的iOS应用程序开发服务确实可以给您带来很大的不同。 是的,我们知道以下事实:拥有经过认证的专家级iOS应用开发 公司可以为您提供不仅用户友好的服务,而且还为您提供一种体验。 当然也可以满足您的需求,因为这始终是当务之急。 选择一家设计公司并为您提供对短期和长期都有好处的应用程序开发服务的公司至关重要。 您需要确保的事实是,公司应该履行其职责,从设计应用程序到分发应用程序,并为其客户提供端到端服务。 您是否觉得有必要提高社交媒体的覆盖率? 您是否想在客户中树立品牌知名度? 或者,您只想获取有关对您的收入产生重大影响的主要客户的更多信息。 这就是iOS应用程序开发服务平台的所在。它们为您提供专业的员工和专家,他们会严格工作以设计具有卓越功能的高级应用程序,从而使您能够毫无困难地满足您的需求。 他们确保高质量的应用程序能够带来“爆炸式”的结果,并为企业带来高于平均水平的回报以及更好的ROI。 那些不熟悉的人代表投资回报。 众所周知,i-phone位于技术中心的中心。 我们见过的最创新的技术之一,以其卓越的应用程序而闻名,这一卓越的应用程序使我们所有人都对这一惊人的发明的运作方式着迷。 因此,iOS应用程序开发服务平台为企业提供了无与伦比的功能。 为了使任何应用程序开发服务成功,他们需要完全了解系统。 他们配备了完全了解i-phone技术的人员。 这使他们能够提供强大,可扩展和定制的应用程序。 此外,它们为广泛的企业和企业提供应用程序。 它们为寻求发展和扩大客户群的企业提供了推动力。 IOS应用程序开发服务平台为他们的客户提供可靠,专用的服务。 一些iOS应用程序开发服务还为企业提供了有价值的咨询服务。 您可能会想这样的服务有什么用? 好! 通过此咨询服务,您可以与许多专家交谈,讨论您的业务需求以及适合您需求的应用程序。 最初发布于:www.origamistudios.us