Tag: iOS应用开发

Swift中的内存泄漏

在本文中,我们将讨论内存泄漏,并将学习如何使用单元测试来检测它们。 偷看一下: 这是用SpecLeaks编写的测试。 重要:我将解释什么是内存泄漏,讨论保留周期以及您可能已经知道的其他事情。 如果您只想阅读有关单元测试泄漏的信息,请跳至最后一部分。 内存泄漏 确实,这是我们作为开发人员面临的最常见的问题之一。 我们会逐个功能地编写代码,并且随着应用的增长,我们会引入漏洞。 内存泄漏是内存中永远被占用且永远不会再使用的一部分。 这是占用空间并导致问题的垃圾。 在某个时刻已分配但从未释放过并且不再被您的应用程序引用的内存。 由于没有对其的引用,因此现在无法释放它,并且该内存无法再次使用。 苹果文件 从初级到高级开发人员,我们都会在某个时候造成漏洞。 我们有多经验都没关系。 拥有一个干净,无崩溃的应用程序,消除它们是至关重要的。 为什么? 因为它们很危险。 泄漏很危险 它们不仅增加了应用程序的内存占用 ,而且还引入了有害的副作用和崩溃。 为什么内存占用量会增加? 这是对象未释放的直接结果。 这些对象实际上是垃圾。 随着创建这些对象的动作的重复,占用的内存将增加。 垃圾太多了! 这可能会导致出现内存警告情况,最终,该应用程序将崩溃。 解释不必要的副作用需要更多细节。 想象一下在init内创建通知时开始监听的对象。 它对此做出反应,将内容保存到数据库,播放视频或将事件发布到分析引擎。 由于需要平衡对象,因此我们在释放对象时在deinit内停止监听通知。 如果此类物体泄漏,会发生什么? 它永远不会死,也永远不会停止收听通知。 每次发布通知时,对象都会对此做出反应。 如果用户重复创建有问题的对象的操作,则将存在多个实例。 所有这些实例都响应该通知并相互介入。 在这种情况下, 崩溃可能是最好的事情。 多个泄漏的对象对应用程序通知做出反应,更改了数据库,UI,破坏了应用程序的整个状态。 您可以在The Pragmatic Programmer中的“死程序不说谎”中了解有关此类问题的重要性。 泄漏无疑会导致不良的用户体验和不良的App Store评分。 泄漏来自哪里? 例如,泄漏可能来自第三方SDK或框架。 甚至来自Apple创建的类,例如CALayer或UILabel 。 在这些情况下,除了等待更新或放弃SDK之外,我们无能为力。 但是我们很有可能在 我们的代码。 泄漏的 首要 原因是 保持周期 […]

应用程序扩展-探索小部件

大家好,来到这里真是太好了! 好吧,如果您想知道我现在在哪里,请点击以下链接。 https://itechroof.wordpress.com/ 让我们开始吧。 iOS现在具有版本11.3(在撰写本文时),但我们将跳回到本文的版本8,并探讨引入的功能。 好吧,我们不要再拖下去了。 它称为应用程序扩展。 我们不会涵盖所有应用程序扩展,因为每种类型本身都可以是博客文章。 因此,我们将介绍“今日小工具”或最常见的“小工具”。 在这篇文章中,我们将介绍 什么是应用程序扩展程序? 它的类型 生命周期 受限制的API 今天扩展 创建Today扩展的步骤 应用程式扩充功能: 应用程序扩展提供了访问自定义功能或应用程序内容到其他应用程序(包括系统应用程序)的桥梁。 为了更好地理解,我们要说的是执行特定的任务,例如共享选项,以将内容/媒体从一个应用程序发布到另一个应用程序,或者将自定义键盘扩展名提供给其他应用程序/系统本身使用。 类型: 苹果提供各种应用程序扩展。 要浏览所有内容,可以导航至文件->新建->目标->应用程序扩展 。 生命周期: App Extension具有自己的生命周期和环境。 它与普通应用程序不同。 但是您需要了解有关“包含应用程序和主机应用程序”的知识。 包含应用程序是拥有扩展名的应用程序。 在我们的方案中,该应用将具有一个Today小部件。 因此我们的应用程序称为“包含应用程序”。 主机应用是访问应用扩展的应用。 例如,假设(包含)应用程序中有一个应用程序扩展。 现在,苹果照片具有共享功能,单击该功能会显示可以共享照片的应用程序列表。 在这种情况下,如果调用我们的应用程序扩展将照片与我们(包含的)应用程序共享,则“照片”应用程序称为“主机”应用程序。 在显示的应用程序列表中,通过单击我们的应用程序,请求将被发送到应用程序扩展。 应用扩展程序将处理请求,并在完成任务后终止。 对于上图,一旦用户按下共享按钮,主机应用就会调用应用扩展。 该扩展程序提供了将照片发布到我们的应用程序的功能。 在此过程中,应用扩展程序将在主机应用程序(在我们的情况下为照片应用程序)的上下文中具有自己的视图。 根据用户输入/请求,扩展程序会将结果返回到主机应用程序。 如果需要,扩展程序还可以执行后台处理。 请求完成后,系统将终止扩展。 由于扩展程序可以与主机应用程序通信,因此主机应用程序将向扩展程序发出请求,并且扩展程序将向主机应用程序发送响应。 应用扩展程序和包含应用程序的应用程序不直接通信。 应用程序扩展程序及其包含的应用程序通过私有定义的共享容器进行通信。 通常,当包含的扩展程序正在处理来自主机应用程序的请求时,包含的应用程序甚至无法运行。 受限制的API: 应用扩展程序无法执行长时间运行的后台任务。 限制可能因平台而异。 它可以通过AirDrop发送数据,但无法接收数据。 它无法访问sharedApplication对象。 应用扩展程序不能使用在NS_EXTENSION_UNAVAILABLE宏或类似的不可用性宏下标记的任何API。 它不能在不可用的框架中使用任何API。 它无法访问iOS设备上的相机或麦克风(iMessage扩展名除外)。 […]

在Swift 5中使用UIPageViewController:第1部分

•19/5/30:已为Swift 5更新了此内容,更正了一个错误。 这比其他任何东西都更适合我自己,因为UIPageViewControllers具有一些我经常要提醒自己的怪癖。 如果有人遇到这个问题,希望对您有所帮助。 首先,本教程将不使用情节提要。 这仍然适用于情节提要,但是如果您正在寻找基于情节提要的解决方案,则应牢记这一点。 因此,创建一个名为ViewController的新ViewController并添加一个名为UIPageViewController类型的名为pageController的可选变量。 私人var pageController:UIPageViewController? 接下来,我们需要viewcontroller扩展UIPageViewControllerDataSource和UIPageViewControllerDelegate 。 使用extension执行此操作,并添加所需的协议方法UIViewController()现在返回UIViewController() 。 扩展ViewController:UIPageViewControllerDataSource,UIPageViewControllerDelegate { func pageViewController(_ pageViewController:UIPageViewController,viewControllerBefore viewController:UIViewController)-> UIViewController吗? { 返回UIViewController() } func pageViewController(_ pageViewController:UIPageViewController,viewControllerAfter viewController:UIViewController)-> UIViewController吗? { 返回UIViewController() } 现在,在您的viewDidLoad或其他方法中,设置您的pageController 。 私人功能setupPageController(){ self.pageController = UIPageViewController(transitionStyle:.scroll,navigationOrientation:.horizo​​ntal,选项:nil) self.pageController?.dataSource =自我 self.pageController?.delegate =自我 self.pageController?.view.backgroundColor = .clear self.pageController?.view.frame = CGRect(x:0,y:0,width:self.view.frame.width,height:self.view.frame.height) self.addChild(self.pageController!) self.view.addSubview(self.pageController!.view) self.pageController?.didMove(toParent:self) } 这就是一大堆代码,它是做什么的? 首先,我们以滚动样式创建pageController ,并使其侧向滑动。 然后,将viewController设置为数据源,以便它知道从哪里获取尚未添加的所有viewControllers […]

Apple Search Ads Platform更新4.5.17

看来,较早的服务中断是在较小的平台更新之前进行的,Apple现在允许Apple Search Ads用户复制实体。 可通过蓝色操作下拉菜单访问重复的实体。 此更新似乎仅限于复制广告组, 关键字和否定关键字的功能 。 仍然明显缺少删除,更改匹配类型或进行批量出价更改的功能(复制广告系列的功能)。 如果您尝试将广告组复制到同一广告系列中,则它们将在广告组名称的末尾附加“ -Duplicate”。 如果您尝试将关键字复制到同一广告组中,则会遇到恰当命名的错误消息“ CN.ERROR.ADGROUP_SELECTOR.INVALID_SELECTION”。 关键字重复 关键字复制成功 重复的广告组 选择广告组复制目标 广告组复制成功 最重要的是: 虽然此更新是次要的更新,但它为将来进行更大的更新提供了希望,并证明了Apple正在致力于改进其Search Ads平台。 伙计们,到此为止! 请确保将我们的博客添加为书签,注册到我们的电子邮件新闻通讯以获取新的帖子更新,如果您有兴趣与我们合作,请与我们联系。 Incipia是一家移动应用程序开发和营销机构,专门为公司构建和营销应用程序,并擅长于高质量,稳定的应用程序开发以及基于关键字的营销策略,例如App Store Optimization和Apple Search Ads。 对于帖子主题,反馈或业务咨询,请 与我们联系 ,或将查询发送至 hello@incipia.co 。

使用iOS 10预提取API增强平滑滚动

在上一篇文章中,我们探讨了一些常见的策略来实现iOS移动应用程序中的平滑滚动。 应用这些策略的主要目的是避免混乱的滚动 ,这是对用户体验产生负面影响的常见问题。 为了帮助开发人员完成这项任务,Apple对iOS10中的UICollectionView进行了一些非常有用的更改。 但是,在审查此新引入的功能之前,让我们先检查促使他们需要这些功能的原因。 是什么导致滚动不稳定? 您是否曾经与某个偶尔出现断断续续的滚动的应用程序进行过交互或工作过? 如果答案是“是”,那么当您尝试快速滚动并且应用程序内容似乎断断续续时,您就会知道它多么令人失望。 您可能已经问过自己,是什么触发了这种不稳定的滚动行为以及随之而来的不良用户体验。 简短的答案是:该应用丢帧了 。 但这到底是什么意思? 为了确保一致的平滑滚动,应用程序需要能够稳定显示60 FPS(每秒帧数)。 或者,换句话说,应用程序需要每秒刷新其内容60次。 这意味着每个帧大约要渲染16ms(1000ms / 60帧〜16ms /帧)。 在不幸的情况下,显示一帧所花费的时间比分配的时间长,下一帧将不会显示任何数据,并且据说该应用程序“ 丢弃了一帧” 。 下图说明了这种不幸的情况。 蓝色标记表示绘图操作,其厚度表示完成渲染所需的时间。 如我们所见,在第二帧中,我们发生了一些渲染事件,这些事件花费的时间超过了分配的时间(〜16ms),因此,第三帧已被删除。 我们可以从刷新操作所花费的CPU时间的角度来可视化同一场景。 在下图中,尖峰对应于当应用花费超过预期的〜16ms来刷新当前内容时发生的丢帧现象。 为了获得良好的用户体验,刷新时间必须始终低于允许的最大值〜16ms。 理想情况下,由于我们希望创造出色的用户体验(而不仅仅是一个好的体验),因此每次刷新时间应为: 始终低于允许的最大时间(〜16ms)。 尽可能地低,以节省可用于其他任务的CPU时间。 丢失帧的最常见来源是从主线程为单元加载昂贵的数据模型。 这种情况的典型示例是: 从URL加载图像。 从数据库或CoreData访问项目。 在iOS10中,Apple对单元格的加载和显示方式进行了一些优化。 让我们看一下iOS10可用的改进,以及它们如何使开发人员更轻松地创建流畅的滚动用户体验。 iOS9中的单元生命周期 UICollectionViewCell的生命周期可以如下所示: 集合视图及其单元之间的主要交互是: 收集视图正在请求要显示的单元格的内容,该单元格将要输入可见字段: collectionView(_:cellForItemAt:) 。 集合视图要求显示该单元格- 该单元格刚刚输入了可见字段: collectionView(_:willDisplay:forItemAt:) 。 集合视图正在删除单元格- 该单元格在可见字段之外 : collectionView(didEndDisplaying:forItemAt:) 。 iOS10中的单元生命周期 在iOS10中,单元的生命周期与在iOS9中基本相同。 但是,有一些显着差异。 […]

这些iPhone应用程序可以使您的社交媒体游戏更高

社会企业家精神再次受到炒作,并通过吸引各个领域的巨大关注而迅速增长。 社会企业家是在卫生,教育,企业等许多领域推动社会创新和变革的人。 商业企业家和社会企业家在建立强大且可持续发展的组织时大致相似。 整个社会企业家精神的概念似乎是如此吸引人,并很有希望,以至于青年人中的大多数年轻人都倾向于将其作为职业道路。 就像每枚硬币都有两个面一样,这个词也被称为“混合祝福”。 从积极的意义上讲,它表示感知和实践机会的特殊能力。 在这里,我们将开箱即用的思维与独特的品牌结合在一起,决心创造新的事物。 但另一方面,这些活动确实需要多年的经验才能蓬勃发展并对他人产生强大影响。 以下是社交企业家可以使用的一些顶级移动应用程序: 1. 预算 -这是一个微型捐赠应用程序,它将生动活泼的日常活动和游戏变成了促进社会变革的强大工具。 如果您喜欢向别人的朋友敢,那么这个程序就是给您的。 安装该应用程序后,设置一个挑战并邀请朋友完成它。 如果挑战未成功完成,则必须将固定的捐赠给您选择的组织。 捐款范围从$ 1到$ 5不等,只需捐款即可。 2. We Day – 我们的应用程序适合那些正在寻找一种可以贡献出有意义的东西的人。 首先,公司或慈善机构会提出挑战,如果有用户完成挑战,那么它就会成为挑战。 一些资金捐赠给慈善机构,用户自己也可以创造挑战。 we.org公司已与许多品牌合作,以授权慈善捐款。 3. 微型英雄-在线调查是已知的最简单的赚钱方法之一。 但是此应用程序提供了在进行这些调查时捐赠一定数量的工具。 您需要做的就是去Apple的iTunes商店,然后下载该应用程序以开始。选择您想要捐赠的原因,然后开始问答以赚钱。 4. Givvr-此应用程序遵循与Micro Hero应用程序类似的概念,但它处理的是按收看付费广告,而不是在线调查。 使用此应用程序,用户可以通过观看赞助的应用程序来捐出自己的收入。 这些是一些iPhone应用程序,它们使用户可以为良好的事业做出很小的贡献。 您可以在专业人员的帮助下创建自己的微捐赠应用程序,也可以选择澳大利亚最好的Iphone App Development。 借此,您可以了解社会企业家策略,并在社交平台上获得经验。

Swift 3’de核心数据

核心数据,模型型号为nesneleriniyönetmek,编号为kullandığınızbirçerçevedir。 核心数据genellikle,模型模型50 ila 70oranındaazalır。yazdığınızkodmiktarınınyüzde。 Birkaçgözeçarpanyerleşiközelliğivardır:veri izlemedeğişikliği,veriyi geri alma ve tekrarlama,filtreleme,diske kaydetme gibi。 哈迪(Hadi Deneyelim)! Xcode的单一视图应用程序iPhone应用程序。 在核心数据列表中查看核心数据: 核心数据kullan kutusunuişaretlediğinizde,Xcode,AppDelegate.swiftuygulamasında 核心数据堆栈 olarak bilinenşeylerintamamlayıcıko​​dunuoluşturacaktır。 核心数据堆栈 ,核心数据 ,从bilgi kaydetmek到almakiçinkullanılanbirtakımnesnelerden发行。 Bunuyaptıktan声音.xcdatamodeledadlıbir dosyaadıgöreceksinizTıklayın,实体ve属性göreceksiniz。 Burada待办事项清单任务实体的名称和任务实体描述属性的名称。 她的Entity’yekarşılıkbir NSManagedObject tipindekarşılıkgelen otomatik olarak birsınıfoluşur。 NSManagedObjectÇekirdekVeri modeli nesnesinin gerekli olantümtemeldavranışlarınıuygulayan通用birsınıfdır。 Görevlerieklemekiçinstoaryboard’daekranlarımıyapalım。 ViewController’dagörevlerimizilisteliyeceğizAddTaskViewController’dadagörevlerimiziekleyebileceğimizbir ekranoluşturduk。 AddTaskViewController ikinci viewcontroller’da verilerini kaydetmeyebaşlayalım。 数据仓库erişebilmeliyiz。 AppDelegate.swiftüzerinetıklarsanız,专业知识核心数据ilililiyöntemleriveözellikleriöncedenoluşturulduğunugöreceksiniz。 Dolayısıyla,tekyapmamızgereken buyöntemleriveözelliklerikullanmaktır。 NSPersistentContainer或其他数据集。 Şimdigörevekledğimizbuttonun cilickolayınıyazalımve girilengörevtanımınıTask’ımınızıniçinekayıtedelim。 […]

将任何iOS设备变成台式机

SSMP或“第二屏幕模式协议”是一种轻量级框架,允许用户按应用程序将其设备用作桌面。 只需将您的设备连接到显示器,该设备就会变成键盘和触控板,并且您有了台式机。 最好的是,它只需要最少两行代码。 这个框架是我创建的,并且是100%开放源代码。 该项目在Swift中。 克隆GitHub存储库并构建框架 从发行版下载框架 使用迦太基(推荐) 椰子足 如果您不使用Cocoapods,或者只想查看仓库,请单击此处。 今天,我们将与Cocoapods一起安装它。 您要安装Cocoapods,请单击此处。 如果还没有,请创建一个Xcode项目。 项目准备就绪后,将其关闭并转到其文件夹。 现在打开终端并输入以下内容(记住在每个命令后按回车键): cd PathToYourProject pod初始化 纳米Podfile 在Pods for ” your project”下方,添加: 豆荚“ SSMP” 按control-X Y enter 。 那应该使您回到正常的命令行中。 对于最后的步骤,键入: 吊舱安装 这将安装所有pod并创建一个xcworkspace ,您将从现在开始使用它而不是xcproj 。 您可能需要按CMD+B来重建项目。 最后,让我们开始编码。 在您的AppDelegate.swift ,导入SSMP 。 然后转到didFinishLaunchingWithOptions并设置您希望外部显示器具有的视图控制器: SSMPApp.default.viewController = MyViewController() 如果使用情节提要,请执行以下操作: 让故事板:UIStoryboard = UIStoryboard(名称:“ Main”,包:nil) 让VC = storyboard.initiateInitialViewController() SSMPApp.default.viewController = […]

iOS工程师工具包(或如何保持理智)

我曾经使用命令行进行所有操作。 编译,在Vim中编写代码,推送到git –这几乎是您作为iOS工程师所需要做的一切。 没有错。 如果您不遗余力地执行所有这些操作,则将为您提供更多功能,但这不是我的事。 远离命令行的一部分涉及构建我的工具箱。 对于软件工程师来说,有很多选择,其中很多几乎是相同的。 很难在此,那个或另一个之间做出决定。 费力地扭了扭我的噩梦之后,我终于完成了我的工具箱。 这是该列表,无特定顺序。 XCode 嗯是的 XCode。 有99%的iOS工程师选择IDE,这是有充分理由的。 是否希望看到您的应用程序外观的可视化表示形式? 您可以! 想实时运行和调试您的应用? 你做那件事! 是否要使用iPhone必须提供的所有UI元素和高级功能? XCode涵盖了您。 大量的iOS和MacOS文档可以直接在XCode中找到。 您的测试套件,性能和代码分析器,CPU和内存管理-全部都可以下载到7到8 GB。 美丽。 另外,它本身也得到了Apple的大力支持。 当然,它有其陷阱。 我与XCode有爱恨交织的关系。 当它起作用时,它使我高兴地哭泣。 如果不是这样,我的愤怒就符合圣经(例如旧约/希腊神话级别的愤怒)。 吉特·克拉肯 与我一起/见过的许多开发人员在版本控制方面都有不同的看法(管理多个人同时在同一个项目上工作)。 在与Git(较流行的版本控制形式)接口方面,有很多不同的方法可以采用。 我以前在git中使用命令行。 我感觉像是个坏蛋,黑色背景上所有的绿色文字。 是的,我就是那个家伙 。 没事,但是后来我被介绍给Git的GUI界面。 具体来说,我是SALIDO的一位同事向我介绍GitKraken的,而我再也没有回过头。 如果您曾经使用过git客户端,那么它具有您期望的所有功能。 您可以独占(或隔离)存储库中的特定分支。 查看提交历史记录,查看发布标签,启动拉取请求,使用git flow等。 在SALIDO,我们正在更改代码库并几乎不断推出新功能,GitKraken是必不可少的(而且非常漂亮)。 当办公室中的另一位iOS工程师创建合并冲突或清除您的代码时,它可以帮助您避免出现“我会勒死您”的感觉。 你知道你是谁。 Fastlane + CircleCI 现在,这是我所有工具中的圣杯。 如果必须在使用Fastlane + CircleCI进行部署还是在Google文档中编写和测试我的所有代码之间进行选择,那么我会每次都选择Fastlane + CircleCI。 还记得我说过SALIDO不断推出super […]

iOS App开发趋势将主导2018年

今年(2018年)在客户体验/满意度方面取得了很大飞跃。 因此,诸如Apple和Android之类的技术领导者正计划一方面用更广泛的设备来抢占更广阔的范围和应用程序参考。 在此时,您可能会意识到移动应用程序开发行业实际上是Android和iOS的双头垄断。 然而,引人入胜的部分甚至是假设Android的Appstore上有许多应用程序,而售罄的每十部新的明智手机中,几乎有九部是Android,iOS为开发人员和企业赚取了更多现金。 1.苹果推出了Swift 4 如果您是iOS开发人员,则很可能必须记住Swift是什么。 它是Apple开发的一种编程语言,可为iOS和Linux操作系统提供支持。 此外,它甚至可以针对macOS和tvOS进行预编译。 它将被视为Swift 3的高级版本。 它是一种功能强大的编程语言,易于理解和学习,并且因其可量化性和强大的安全性而被认为。 对开发人员来说有什么用? 首先,应用程序的Develoong将变得更加简单,其次,它不会消耗工具的大量内存。 因此,在Swift 4的帮助下,iOS应用在2018年被收购并更快地开发。 2. AR和VR-iOS区域内的流行语 如果您忽略了这一点,那么今年各大技术公司在其主题演讲中都表示,2018年将成为增强现实(AR)的一年,而iOS则是最重要的一年。 作为成熟的iOS应用程序开发人员,您必须记住增强现实和虚拟现实。 如果我们倾向于返回iOS 11的发布,那么我们将明确记住,最受关注的功能是对增强现实应用程序的支持。 同样,我们可以看到IOS应用程序基于增强现实和虚拟现实的思想。 如果您忽略了这一点,大多数技术公司都在谈论AR和VR排除2018年,IOS处于领先地位,因为iOS开发人员被告知技术进步,并且他们拥有所有必要的工具来构成大多数这项新技术。 3.混合应用 跨平台应用程序已经存在了相当长的一段时间,但是由于它们的性能和用户专业知识不佳,因此它们几乎没有产生太大的影响,因为它们主要是挤在各种移动应用程序中的互联网应用程序。 情况不再如此。 当前有几种改进的工具,例如Native Script,React Native,可用于应用程序开发,这使得混合应用程序在界面和性能方面与各自的同类应用程序没有区别。 由于混合应用程序可以为企业节省大量时间和金钱,因此这种转变绝对会在2018年动摇移动应用程序交易。 4.专注于WatchOS 我们都知道,智能手机目前已达到饱和,并且可穿戴设备周围的嗡嗡声已经持续了很长时间。 因此,一旦Apple在2018年初推出WatchOS4,就表明您已经准备好在2018年为大量响应式和更高质量的手表应用做好准备。 而且,由于大多数智能手表应用程序多年来一直被困在健康和导航领域,因此该设备可以通过一些令人兴奋的新选项进行重新发明了。 5.支持AI和ML的应用 在今年的AI阶段,我们已经看到了许多实验和活动,其中有多个应用程序部署了bot和不同的相似功能。 这将被视为纯粹的基础,而我们倾向于期望从明年开始真正的转型。 企业很快就会意识到,理想的用户体验将不会有问题,因为不同的用户与同等的应用程序有着不同的愿望。 为了填补这一空白,从明年开始的iOS应用程序开发可以转移到将机器学习集成到应用程序中,以提供动态的用户体验,从而根据用户塑造自身并随着时间的流逝而变得更高。 您的应用程序是否能够面对这种变革浪潮? 还是您想为自己的企业拥有一个面向未来的应用程序? TOPS Technologies的我们将协助您提高技能,这将帮助您获得相关工作,也将帮助您改善业务。 有关更多详细信息,请访问:https://www.tops-int.com/iphone-training-course/