如今,每个人都重视隐私和安全性。 这就是为什么最近我们需要在我们的Distillery项目之一上使用加密库的原因。 对于相关项目,已决定使用libsignal 。 libsignal最初是为Signal Private Messenger开发的,在安全专家中享有很高的声誉。 使用libsignal 在2018年7月末,在Pure C , Java和JavaScript中实现了libsignal。 在Objective-C中也有一个称为SignalProtocolKit的实现 ,但那时它已被弃用。 我们的目标是在以Swift编写的iOS应用程序中使用该库。 为了使所有内容保持最新,我们必须使用C语言编写的代码,源代码存储在此处。 幸运的是, Swift可以非常顺畅地与C代码交互,至少因为苹果的一些底层库是用C编写的。 棘手的部分是将库正确添加到项目中。 创建一个module.map 经过一些研究,我发现我可以简单地将库的源代码添加到项目中,并创建一个module.map文件来指定要公开的库头(有关更多信息,请查看此博客)。 libsignal包含许多头文件,但是如果我想在常规C项目中使用它,则只需包含一个称为signal_protocol.h的文件。 由于signal_protocol.h包括它依赖的其他头文件,就像其他每个头文件一样,我们可以确定所有文件都包括在内。 因此,我制作了一个具有以下内容的module.map文件: module SignalProtocol [system] { header “src/signal_protocol.h” export * } 然后,将其放入带有库源代码的文件夹中,将其添加到项目中,然后开始进行测试。 Xcode允许导入此模块并使用某些库函数,但是缺少某些库组件。 例如,我无法使用signal_protocol_internal.h中定义的signal_context数据类型。 在那一刻,很明显Xcode没有正确包含嵌套的头文件。 因此,我决定尝试将来自libsignal的所有标头包含在我的module.map文件中。 (将更新后的module.map放在这里是多余的,因为它只包含与上面的文件类似的每个* .h文件。) 完成此操作并尝试构建以构建项目后,我从libsignal中获得了许多与某些头文件有关的错误。 当我查看它们时,我意识到有些头文件不是实际的头文件。 相反,它们存储一些常数值,并按如下方式使用: static const fe sqrtm1 = { #include “sqrtm1.h” } ; 截至2018年7月底,已使用以下文件: sqrtm1.h […]
面向协议的编程是什么意思,为什么它比OOP更好? “像我五岁一样解释”-洗衣服务示例 首先,提供的示例非常容易理解。 我强烈建议您跳过阅读评论者写的内容。 主题是“洗衣服务”。 假设有一个Laundry对象,其中封装了某些与洗衣相关的功能……“请给我洗衣服”-“好,这是您的衣服!”。 您作为客户只需说“请洗钱!”即可与Laundry互动。 Laundry对象开始工作并执行其工作,其范围可能从简单到令人难以置信的复杂–美丽之处在于,作为客户,就像需要洗衣服的人一样,您不在乎。 只要洗完衣服就可以退回衣服,生活就很棒! 面向对象的“问题” 在构建软件时,我们是在使用其他人构建的代码,还是创建供他人使用的代码,不是吗? 我们正在使用的代码“挂钩”到其他开发人员设计并提供给我们的东西,或者我们正在创建其他代码将“挂钩”并与之交互的代码,即使“其他代码”是由我们在自己的应用程序中。 在成为代码的客户和创建者时,我们会同时戴上两顶帽子。 但是,如果我们将Laundry对象作为开发人员/创建者 ,而不是作为客户端 (即需要洗衣服的人)来工作,该怎么办? 如果我们作为开发人员将一个Laundry对象交给了一个库,并且想要自定义它的行为,那么……可能会提高launderClothes()的性能,或者重写它的实现以使用一些令人惊奇的新洗衣服务。 我们这样做的方法是创建一个子类 。 评论者说,这是具有对象定向的牛肉: [Object Orientation]通过继承来公开状态和内部,从而鼓励“封装复杂性”。 解说:软件具有天生的复杂性。 对象是封装这种复杂性的“事物”。 但是,他们以某种方式做到这一点:他们公开了某些状态和功能。 这些关于复杂性的抽象通过系统传播和定制的方式就是通过称为继承的机制。 但是评论者称这种方法是“麻烦的”。 为什么? 好… “我可能不想知道我的干洗是如何完成的,但是如果我想设计一条从脏衣服到干净衣服的更好方法,我就必须知道所执行步骤的每一个最后细节,这样我才能尝试在我的子类中完善它们。” 因此,评论者从开发人员/创建者的角度出发。 需要指出的是,要真正能够改善子类中的性能或优化算法,我们必须要知道在超类中执行的步骤的每个细节。 我们并非总是可以发现超类实现来改进它的情况。 转向协议方向 那么…协议方向? 如果更好,那会更好吗? 我喜欢评论者以洗衣服务的面向对象示例为例,并从协议而非对象的角度出发细化了细微差别。 真。 看一看。 我们看到的主要区别是,与其拥有一个Laundry对象而不是一个以自己的特定方式进行Laundry对象,而是发生了转变:我们开始着手描述完成洗衣的方式 。 如果我想由一个特定的人以一种特定的方式来完成洗衣, 那么我会去面向对象的,而不再关心事情如何完成。 但是,如果我想概括一下洗衣服的方法 ,则需要以协议为导向,并不再关心其他所有事情。 在协议定向中,唯一重要的是接口 ……客户端将与之交互的事物。 尽最大可能通过一个协议来描述它,然后让其他事情出现并担心该如何做 。 最后,需要洗衣服的人唯一需要知道的是他们可以使用哪些界面来完成他们需要完成的洗涤。 能够坐下来思考一下,如果您从图片中删除了状态和继承,并且只考虑了与该特定任务的最低要求接口,将会看起来如何,这是一种更强大,更简单的编程方法,使重用比OO曾经做到过。 外卖 对我来说,这个关于Protocol Orientation的要点是:Protocols是泛化的 。 […]
有时,您可能希望在野外测试时可以调整应用程序的某些值(颜色,字体大小,文本)。 或者,您想启用一项功能来对其进行测试,但还不希望向Beta测试人员展示该功能。 使用我的DDHTweaks框架,您可以做到这一点。 您只需要添加一些代码即可在应用运行时更改某些值。 这是一个简单的例子。 假设您有一个登录屏幕,并且想在显示该屏幕时为文本字段和按钮的外观设置动画。 如果您以前做过,您可能还记得为动画找到正确的值会花费一些时间,因为您必须在每次更改后进行构建和运行。 有些人甚至开始在Playgrounds中调整动画,因为它在模拟器上的测试速度更快。 使用DDHTweaks,您可以更快地找到正确的值。 注意:在此示例中,我将使用情节提要,因为这是大多数人构建其应用程序的方式。 通常我不使用情节提要。 😉 我们要设置动画的屏幕如下所示: 元素的布局是使用“自动布局”完成的。 因此,为了使外观具有动画效果,我们需要引用所有布局约束: @IBOutlet弱var labelTopConstraint:NSLayoutConstraint! @IBOutlet弱var labelUsernameConstraint:NSLayoutConstraint! @IBOutlet弱var usernamePasswordConstraint:NSLayoutConstraint! @IBOutlet弱var passwordButtonConstraint:NSLayoutConstraint! 这些约束中的每一个都定义了到布局中下一个元素的垂直距离。 为这些元素的外观设置动画的一种方法如下所示: override func viewWillAppear (_ animated: Bool ) { super.viewWillAppear (animated) labelTopConstraint . constant = – 40 labelUsernameConstraint . constant = – 40 usernamePasswordConstraint . constant = – 40 passwordButtonConstraint . […]
复杂转换 正如我们前面提到的,Swiftify执行的转换不是对Objective-C语法的愚蠢搜索和替换操作,而是该工具更像是一个编译器,该编译器具有: 一次扫描一个字符。 通过标记扫描的字符,对代码执行词法分析 。 然后执行语法分析以确保从词法分析中产生的标记顺序正确。 可以产生期望结果的一组关键字的正确顺序称为语法。 然后, 语义分析会生成一种中间语言或结构,以便可以将其转换为等效的Swift。 这个复杂的过程为Swiftify提供了令人印象深刻的优势,即数据类型感知能力。 例如: 转换为: 如果Swiftify不知道i’的数据类型,并且将加法转换为仅var g = i + f ,它将产生一个编译器错误,表明Binary operator ‘+’ cannot be applied to operands of type ‘Int’ and ‘Float’ ; 您可以在此处探索免费的在线代码转换器。 仅此一项就可以向您展示该工具的真正智能程度。 再以下面的代码段为例,该代码段将持久性存储添加到NSPersistentStoreCoordinator中: 转换为: 在这里,您可以看到Swiftify能够try包装该调用,因为此函数throws ,并且,此外,它还可以推断persistentStoreCoordinator是一个可选属性,并为该属性添加了可选的展开功能 ! 您还可以在这里找到许多其他这样的示例。 包起来 我希望这可以帮助您了解为什么我将Swiftify用于所有转换。 从Xcode扩展到离线转换器,Swiftify可以帮助您! 使用Swiftify,您还可以: 生成Swift文档。 增加您的图书馆的用户群。 迁移您的旧代码。 如有任何疑问,请随时在下面的评论部分中提问。
最后要看的重要功能是数据工厂,稍后我将对其进行解释,然后我将介绍一些我尚未介绍的其他功能(因为我只是不够擅长于其他功能)将它们放入前面的示例中)。 数据工厂:这是什么?为什么要使事情变得更复杂? 当我开始这个项目时,这是我没有想到的功能,但是在Scribd中用LiveCollections制作食物后,这项功能成为必需的功能。 必需的是, CollectionData中的计算基于采用UniquelyIdentifiable通用类型,因此,使用默认的对象相等性函数定义相等性比较。 这是有问题的,因为我们必须对每种数据类型的均等性进行单一定义。 尽管这对于数据管理是正常的,但对于该数据的视图表示而言却不是理想的选择。 平等本质上是定义视图中行/项目的重载 。 这种不平等告诉我们发生了变化。 问题是我们可能不想在数据的两个不同表示形式中使用相同的相等概念 。 具体来说,我们可能需要考虑其他信息,这些信息会修改我们的数据,但它们是扩展的元数据,而不是根属性。 有两种方法可以解决此问题: 只需创建一个包装类/结构来定义数据类型和其他元数据的元组。 创建相同的包装类/结构,但使用采用UniquelyIdentifiableDataFactory协议的对象来支持它,并将为您构建包装器。 解决方案1在有限的情况下完全适用,而解决方案2隐藏了映射逻辑以转换数据,并允许您公开标准化的更新方法(您可能需要这种方法来实现自定义协议一致性,注入和可测试性)。 如果包装类型是标准类型Movie MovieWrapper ,则这是方案1的更新函数: func update(_数据:[ MovieWrapper ],完成:(()-> Void)?) 对于解决方案2: func update(_数据:[ Movie ],完成:(()->无效)?) 用法和抽象方面的微小但重要的变化,因为它与到目前为止我们在所有示例中一直使用的相同API相匹配。 好的,我们如何建立数据工厂? 我们将在最终方案中对此进行回答… 方案9:使用数据工厂 让我们从一个简短而简单的情况开始。 数据对象将是Movie ,而我们要添加的元数据是boolean isInTheaters: Bool 。 本质上,即使电影结构上的所有数据保持完全相同,但是电影结束了其戏剧性的放映,我们仍要在视图中重新加载该单元格(大概是删除横幅或徽章)。 为此,我们需要一个将在CollectionData使用的新结构。 我们可以将其称为DistributedMove以表示已拾取要在剧院上映的电影。 看起来像这样: struct DistributedMovie:哈希{ 让电影:电影 let isInTheaters:布尔 } 扩展名DistributedMovie:UniquelyIdentifiable { var rawData:电影{返回电影} var […]
上周,我谈到了如何选择Perfect作为Canopy服务器的基础。 本周,我将讨论云提供商,Linux上的Swift,开发过程和部署。 什么是天篷? 现在输入swift run将运行您的服务器。 我建议您使用Xcode,但是要执行这种类型的swift package generate-xcodeproj ,您将获得一个Xcode项目,该项目将在您R时运行您的服务器。 在开发过程中,您需要在Mac上运行服务器应用程序的本地实例,并使您的应用程序与此相对应。 这大大减少了调试周期。 就像我在本系列的第1部分中所说的那样,如果您有一个用于客户端和服务器的项目,则可以共享代码并降低Rev-lock的严重性。 但是,您不可避免地要部署这些开发版本,并且使用rsync很容易: 该脚本将源与服务器同步,构建应用程序,然后要求systemd重新启动它。 在脚本中,我使用了foo ,在其中您可以将foo更改为.ssh/config服务器的名称。 下周我将讨论systemd和其他生产问题。 值得注意的是,使用最便宜的实例编译代码将花费很多时间。 也许有足够的理由升级您的实例,或者直接进入本地交叉编译,接下来我将讨论这些选项。 在某个时候,您将决定现在的时间:上线的时间! 生产似乎很可怕,但实际上您只需要做好准备。 此时,您需要进行大量练习,并且您会非常了解自己的应用及其代码,因此对自己有信心。 首先为您的用户配置密码,以便sudo要求输入密码(默认情况下, sudo AWS实例不提供密码) 您应该在本地或Docker Ubuntu实例上设置交叉编译,以便可以在本地构建应用程序,然后将其scp到您的服务器。 现在,您可以删除服务器上的Swift,并将远程软件包安装减少到最低限度。 Swift二进制文件需要一些内容,尤其是libicu和libcurl才能保留。 我发现由于SwiftPM团队做出了许多奇怪的决定,导致交叉编译不起作用:特别是#if os(Linux)在Package清单中不起作用,事实证明我的依赖关系图充满了此类声明。 我理解为什么有这些陈述: 这是一种语言功能 。 我不明白为什么苹果公司决定在Package.swift文件中不支持此语言功能 。 相反,他们打算强迫用户指定平台作为清单声明的一部分。 海事组织,这可能会使每个地方平台规范(即DSL每个子部分的每个子部分)肿,这将需要数年(基于当前进展),这意味着该功能将无法使用(因此强制#if os因为还没有其他选择)。 毕竟,#if os仍然更加灵活。 SwiftPM团队为什么要与这种语言作斗争 ? 你告诉我。 总而言之,我别无选择,只能使用Docker实例来构建我的Linux二进制文件,因为SwiftPM无法交叉编译我的依赖图。 幸运的是,Docker可以正常工作。 您可以使用简单的脚本在本地进行构建,使用scp二进制文件并调用systemctl restart foo来重新启动服务器应用程序。 请注意,指定–static-swift-stdlib进行swift build否则生成的二进制文件在未安装Swift的情况下将无法工作(严格来说,如果没有LD_PATH中的libswift.so和libFoundation.so ,它将无法工作)。 另外,不要忘记使用发行版优化( -c release […]
苹果宣布了一系列新功能以帮助应用程序分发。 显着的改进是分阶段发行,该版本将使开发人员几乎可以在Google Play分阶段推出时逐步部署其应用程序的更新。 iOS 11将以32位应用程序结尾。 从2018年1月开始,所有将提交到iOS应用商店的新应用都将需要64位,并且在2018年6月还将包括更新。 由于iOS macOS将类似地淘汰32位应用程序,因此High Sierra是最后一个完全支持32位应用程序的版本。 斯威夫特游乐场 如今,迅速的游乐场已被超过一百万的用户使用。 在WWDC发布1.5版本的应用程序前一周,您可以与鹦鹉,Sphero甚至LEGOMINDSTORMS®等品牌的无人机和机器人进行互动。 您可以找到一个新的附件选项卡,可以在其中找到准备与此机器人一起使用的示例。 在将随iOS 11一起发布的2.0版本中,将添加8个新的本地化版本,开发人员,教师和组织可以创建操场上供稿,以帮助与Swift Playgrounds应用程序用户轻松共享内容。 Xcode 9 更好的源代码编辑器 当他们谈论Xcode的新功能时,它始终是我最好的部分,而今年我们有很多新东西。 首先,源代码编辑器完全是用Swift重写的,现在它包含一个markdown编辑器,突出显示问题的布局正在重新考虑,以使其更适合代码,并且最终可以一次解决所有问题。 现在,如果您在按CMD的同时单击某个地方,Xcode 9现在也会提供更好的代码重构,它将显示一个菜单,其中包含可能的重构和与所选代码相关的操作: 另一件事是像现在Xcode高亮逻辑组只是按CMD并传递了func或class关键字: 向类添加协议已经存在一个问题,该问题将实现非可选方法: 同样,CMD + / CMD —现在将增加和减小字体大小。 对于仍在使用Objective-C的人们,他们迅速使用了@availabe关键字。 现在,您的项目也有了组组织,它将与您的文件夹匹配。 Github也直接集成在Xcode中。 苹果团队将使转换引擎成为开放源代码,任何开发人员都可以轻松地添加新的重构功能。 更好的迅捷 他们删除了所需的characters关键字来访问字符串的字符,而不是字符串是可范围替换的双向Collections。 在整个iOS上拖放 您可以在应用程序之间以及同一应用程序内进行拖放。 它会自动用于UITextfield和UIWebviews。 在其他情况下,仅使用两个UICollection和UITableview委托方法即可轻松实现,添加它可能是向列表添加重新排序支持的一种好方法。 拖放所涉及的数据无法由其他应用访问,因为期望该应用将删除该内容。 拖放的所有部分都是可定制的,动画,数据模型… 都是关于图片的 iOS摄像头现在可以检测QR码,并带有通用链接的通知。 苹果公司向我们展示了一种新的压缩格式: HEVC ,视频和照片的压缩率是原来的2 倍 。 结合起来,有一个名为HEIF的新图像容器,可以由不同的资产类型组成。 HEIF是ISO标准。 深度 :应用程序现在可以访问HEIF图像的深度图 视觉 :您可以直接从相机进行面部,界标,矩形,文本,条形码的检测,还可以进行一些对象跟踪,并且使用核心ML,您可以使用机器学习模型来帮助进行对象识别。 […]
i)针对您的iOS应用使用Google-API SDK。 首先在Appdelegate中进行设置: GMSServices.provideAPIKey(AppConstants.googleMapsApiKey) GMSPlacesClient.provideAPIKey(AppConstants.googleMapsApiKey) *不要忘记在Appdelegate import中导入GoogleMaps和GooglePlaces ii)转到ViewController并添加View。 将名称更改为GMSMapView 。 在地图中心创建GMSMapView的出口和附加的Image,并在顶部创建搜索栏以显示位置名称。 在Viewcontroller中检查位置授权 iii)在iPhone上拖动地图,这将触发GMSMapViewDelegate的 2个委托方法。 func mapView(_ mapView:GMSMapView,didChange位置:GMSCameraPosition)—将触发 在地图上的任何动画或手势期间反复进行。 * func mapView(_ mapView:GMSMapView,idleAt position:GMSCameraPosition)—在地图变为空闲时调用 iv)因此最终的归还是要从上述代表处返回Posion: Github演示
关于如何精简视图控制器,进行依赖注入以及从不使用情节提要的文章很多。 我想写一点服务定位器模式如何提供一种轻量级的替代方案,例如使用Coordinators将事物连接在一起。 您仍然可以在情节提要中直观地查看应用程序流程,同时将配置与使用分开。 情节提要的一种未充分利用的技术是通过对象占位符将对象添加到视图控制器: 您可以像连接任何视图插座一样连接对象: 让我们继续定义一个简单的服务定位器: 类PostServiceLocator:NSObject { 懒惰的var dataSource:PostDataSource = { 返回PostDataSource() }() } 您可以为所有依赖项使用单个服务定位器,也可以为更复杂的应用程序使用,根据功能组创建单独的实例。 关于从情节提要中初始化对象的一件事要记住的是,它不会调用任何init()方法,而是通过awakeFromNib()方法调用。 我们的PostDataSource代码涉及更多。 首先,让我们创建一个协议: 协议TableViewDataSource { 关联类型ItemType var个项目:[ItemType] {get} func load(completion:(error:NSError?)-> Void) func tableView(tableView:UITableView,numberOfRowsInSection部分:Int)-> Int func tableView(tableView:UITableView,cellForRowAtIndexPath indexPath:NSIndexPath)-> UITableViewCell } 我们定义一个associatedType,以便我们可以添加协议扩展,该扩展知道如何从数据源中选择单个项目: 扩展TableViewDataSource { func itemInRow(row:Int)-> ItemType { 返回项目[行] } } 例如,这将允许从数据源中选择一个项目并将其传递给下一个视图控制器。 可以在github上找到完成实际工作的PostDataSource的代码,本文涉及太多。 那么我们如何在视图控制器中使用服务定位器呢? 我们将其连接如下: @IBOutlet私人var serviceLocator:PostServiceLocator! @IBOutlet私有var tableView:UITableView! 覆盖func viewDidLoad(){ […]
这是我的第一个关于信用卡折扣的应用程序 该项目被使用 Facebook SDK。 GoogleMapSDK。 GooglePlaces API。 FirebaseCloudDatabase 1.此视图可以选择多个折扣 3.显示信息 5.管理您的卡 7.地图 8.可以推送通知 最初于 2018 年1月14日 发布在 medium.com 上。