Commit 51ceb8e0 authored by iOS-银 恭敬's avatar iOS-银 恭敬

feat:创建项目

parent 52d04309
#!/usr/bin/env python
# coding=utf-8
from biplist import *
import os
import subprocess
from util.colorlog import *
#PLIST_PATH="/Consignor4ios/Supporting Files/Consignor4ios-Info.plist"
VERSION_KEY = "CFBundleShortVersionString"
BUILD_VERSON_KEY = "CFBundleVersion"
HOOKS_PATH = ".git/hooks"
PROJECT_SUF = ".xcodeproj"
PLIST_SUF = ".plist"
#公开
def add_version_if_need():
if open_fun() == "YES":
print "已开启自动递增build版本号的功能"
change_plist_verson(True)
pass
else:
print_log("未开启自动递增build版本号的功能,如需开启请执行:git config githooks.autoversion \"YES\"")
pass
def reduce_version_if_need():
if open_fun() == "YES":
change_plist_verson(False)
pass
pass
def read_current_project_version():
#需要在仓库里配置.plist_path文件,并填好对应的plist文件便可以拼接版本号
config_path = get_project_path()+os.path.sep+".plist_path"
if not(os.path.exists(config_path) and os.path.isfile(config_path)):
return ""
pass
with open(config_path, 'r+') as f:
relative_path = f.read()
pass
plist_path = get_project_path()+os.path.sep+relative_path
if not check_file_is_plsit(plist_path):
print plist_path+"不是plist文件"
return ""
pass
current_version = read_plist_for_key(plist_path, VERSION_KEY, False, "")
if len(current_version)>0:
return "["+current_version+"]"
pass
return ""
pass
def write_version(current_version,plist_path,add):
if current_version == None:
return
las_dot_index = current_version.rfind('.')
pre_version_str = current_version[0:las_dot_index+1]
last_version_str = current_version[las_dot_index+1:len(current_version)]
version = str(int(last_version_str)+1) if add else str(int(last_version_str)-1)
write_version_str = pre_version_str+version
plist_dic = readPlist(plist_path)
plist_dic[BUILD_VERSON_KEY] = write_version_str
writePlist(plist_dic,plist_path)
if add:
loggreen("已成功将版本号"+current_version+"改成为"+write_version_str)
pass
else:
logred("已成功将版本号"+current_version+"回滚至"+write_version_str)
pass
def print_log(log):
print log
pass
def read_version(plist_path, can_raise):
if not os.path.exists(plist_path):
log = "路径%s找不到plist文件" % plist_path
raise(IOError(log)) if can_raise else print_log(log)
return None
pass
try:
plist = readPlist(plist_path)
try:
version = plist[BUILD_VERSON_KEY]
return version
except(KeyError, Exception),e:
log = plist_path+"文件里没有这个key:"+BUILD_VERSON_KEY
raise(IOError(log)) if can_raise else print_log(log)
return None
except (InvalidPlistException, NotBinaryPlistException), e:
log = "路径%s不是plist文件" % plist_path
raise(IOError(log)) if can_raise else print_log(log)
return None
pass
def get_project_path():
current_path = os.getcwd()
if current_path.find(HOOKS_PATH):
return current_path.replace(HOOKS_PATH,'')
else:
raise Exception("路径不在.git/hooks,请检查")
pass
def plist_father_path():
preject_path = get_project_path()
for file in os.listdir(preject_path):
if PROJECT_SUF in file:
preject_path = preject_path+'/'+file.replace(PROJECT_SUF,'')
break
pass
return preject_path+"/Supporting Files/"
pass
def plist_paths(plist_father_path):
plists = []
if not os.path.isdir(plist_father_path):
return plists
pass
for file in os.listdir(plist_father_path):
if PLIST_SUF in file:
plists.append(plist_father_path+file)
pass
pass
return plists
pass
def change_plist_verson(add):
plist_exist = True
current_version = ""
plist_path_arr = plist_paths(plist_father_path())
if len(plist_path_arr) == 0:
plist_exist = False
pass
#先尝试去默认路径读取
for plist_path in plist_path_arr:
current_version = read_version(plist_path,False)
if current_version == None:
plist_exist = False
break
pass
else:
write_version(current_version, plist_path, add)
pass
pass
#如果读取不到再到配置的路径读取
if not plist_exist:
config_plist_paths = ""
try:
config_plist_paths = subprocess.check_output('git config githooks.plistpaths', shell=True).strip()
pass
except subprocess.CalledProcessError as e:
log = "默认路径没有plist文件,请在%s路径下配置plist的相对路径,如果有多个," % get_project_path()
example = "请以逗号隔开,示例:git config githooks.plistpaths \"xxx/info.plist,ooo/info2.plist\""
ex = log+example
raise IOError(ex)
config_plist_path_arr = config_plist_paths.split(',')
#遍历的是相对路径
for config_relative_plist_path in config_plist_path_arr:
#绝对路径
config_plist_path = get_project_path()+config_relative_plist_path
current_version = read_version(config_plist_path,True)
write_version(current_version, config_plist_path, add)
pass
pass
#公开
def open_fun():
return check_out_put("git config githooks.autoversion", False, "NO")
pass
def reset_autoversion_state():
if check_out_put('git config githooks.autoversion', False ,"NO") == "YES":
check_out_put('git config githooks.autoversion \"NO\"', False, "")
pass
pass
pass
def check_out_put(cammand, can_raise, return_value):
try:
return subprocess.check_output(cammand, shell=True).strip()
pass
except subprocess.CalledProcessError as e:
if can_raise:
raise(e)
else:
return return_value
pass
pass
# 基础方法
def check_file_is_plsit(plist_path):
try:
plist = readPlist(plist_path)
return True
except (InvalidPlistException, NotBinaryPlistException), e:
return False
pass
# 此方法必须先验证是plist文件
def read_plist_for_key(plist_path, key, can_raise, return_value):
plist = readPlist(plist_path)
try:
return plist[key]
except(KeyError, Exception),e:
return return_value
pass
#!/usr/bin/env python
# coding=utf-8
import sys
import os
import re
import subprocess
from util.colorlog import *
imgList = []
def open_fun():
return check_out_put('git config githooks.sameimg', False ,"YES")
pass
def reset_check_same_img_state():
if check_out_put('git config githooks.sameimg', False ,"YES") == "NO":
check_out_put('git config githooks.sameimg \"YES\"', False, "")
pass
pass
def check_out_put(cammand, can_raise, return_value):
try:
return subprocess.check_output(cammand, shell=True).strip()
pass
except subprocess.CalledProcessError as e:
if can_raise:
raise(e)
else:
return return_value
pass
pass
class CheckImgName(object):
"""检查相同图片名字"""
#获取项目名字
@classmethod
def __getProjectName(self, projectPath):
#列出目录下的所有文件和目录
projectName = "a";
#这是所有文件的名字
files = os.listdir(projectPath)
for fileName in files:
workspace = ".xcworkspace";
if workspace in fileName:
#print "'%s'" % fileName;
projectName = fileName.replace(workspace,"");
break;
pass
xcodeproj = ".xcodeproj";
if xcodeproj in fileName:
#print "'%s'" % fileName;
projectName = fileName.replace(xcodeproj,"");
break;
pass
return projectName;
@classmethod
def __findImgFolder(self, pattern,directory):
found =[]
for (thisdir,subsHere,filesHere) in os.walk(directory):
for file in filesHere + subsHere:
if pattern in file:
found.append(os.path.join(thisdir,file))
break;
pass
pass
pass
return found
#是否有子文件
@classmethod
def __hasSubFolder(self, folderPath):
files = os.listdir(folderPath)
count = 0;
for file in files:
if file[0] != ".":
count = count + 1;
#print "%s" % file;
pass
pass
pass
if count == 0:
return False;
else:
return True;
pass
#检查是否有相同的图片
@classmethod
def __checkHasSameImg(self):
listCount = len(imgList);
#print "项目里共有图片%d对" % listCount
nameEqual = False;
for i in range(listCount-1):
for j in range(listCount-i-1):
if imgList[i] == imgList[i+j+1]:
logstr = "你有相同名字的图片%s 请修改后再尝试提交" % imgList[i];
logred(logstr)
nameEqual = True;
pass
pass
pass
pass
return nameEqual;
#把path下所有图片放入数组
@classmethod
def __putImgInList(self, path):
#print "Images.xcassets路经:%s\n" % path;
#这是Images.xcassets内所有文件的名字
files = os.listdir(path)
for file in files:
#跳过隐藏文件
if file[0] == ".":
continue;
pass
#文件的绝对路径
filePath = path + "/" + file;
#跳过文件
if os.path.isfile(filePath):
continue;
pass
#图片名字
fileExtensionName = os.path.splitext(file)[1];
if fileExtensionName == ".imageset":
imgName = file.replace(".imageset","")
imgList.append(imgName);
pass
#不是文件,递归
if self.__hasSubFolder(filePath):
self.__putImgInList(filePath);
#print "有子文件夹 %s" % filePath;
pass
pass
@classmethod
def check_img_if_need(self):
if open_fun() == "NO":
print "未开启检查同名图片的功能,如需开启请执行git config githooks.sameimg \"YES\""
return
pass
print "已开启检查同名图片的功能"
#当前脚本所在的路径
currentPath = os.path.realpath(__file__);
#项目所在的路径
currentFilePath = "/.git/hooks/" + os.path.basename(__file__);
#print "当前文件相对路径:'%s'\n" % currentFilePath;
projectPath = currentPath.replace(currentFilePath,"");
projectPath = projectPath + "/"+ self.__getProjectName(projectPath)
#print "您的项目路径:'%s'\n" % projectPath;
#图片文件的路径
fatherImgPath = projectPath
foundList = self.__findImgFolder("Assets.xcassets",projectPath);
if len(foundList) > 0:
fatherImgPath = foundList[0]
pass
else:
foundList = self.__findImgFolder("Images.xcassets",projectPath)
if len(foundList) > 0:
fatherImgPath = foundList[0]
pass
#判断图片路径文件夹是否存在
if fatherImgPath == projectPath:
# print "图片目录不存在,可提交";
return
pass
else:
self.__putImgInList(fatherImgPath);
if self.__checkHasSameImg() == True:
# print "有重名图片,不可提交"
sys.exit(-1)
pass
else:
# print "没有重复图片名字,可提交"
return
pass
pass
\ No newline at end of file
#!/usr/bin/env python
# coding=utf-8
import sys
import re
import subprocess
import reviewboard
maxSubjectLen = 50
typesList = ['feat','fix','docs','style','refactor','test','chore']
MERGE_BRANCH_TEXT = "Merge branch"
CONFLICTS_TEXT = "# Conflicts:"
GLOBAL_BLUR_JIRA_ID = ""
GLOBAL_TRUE_JIRA_ID = ""
def open_fun():
return check_out_put('git config githooks.checkmsg', False ,"YES")
pass
def mark_did_blur_check():
if check_out_put("git config githooks.blurcheck", False, None):
check_out_put("git config --unset githooks.blurcheck", False, "")
pass
pass
def reset_check_msg_state():
if check_out_put('git config githooks.checkmsg', False ,"YES") == "NO":
check_out_put('git config githooks.checkmsg \"YES\"', False, "")
pass
pass
def check_out_put(cammand, can_raise, return_value):
try:
return subprocess.check_output(cammand, shell=True).strip()
pass
except subprocess.CalledProcessError as e:
if can_raise:
raise(e)
else:
return return_value
pass
pass
def check_contain_chinese(check_str):
for ch in check_str.decode('utf-8'):
if u'\u4e00' <= ch <= u'\u9fff':
return True
return False
pass
#xm检查的类
class CheckMsgStyle(object):
"""检查提交信息规范的功能"""
@classmethod
def headerTypesList(self):
return typesList
pass
@classmethod
def check_msg_if_need(self):
if open_fun() == "NO":
print "未开启检查提交信息规范的功能,如需开启请执行:git config githooks.checkmsg \"YES\""
return
pass
print "已开启检查提交信息规范的功能"
content = self.__commitMsg()
self.__CheckMsg(content)
pass
#检查提交信息是否正常
@classmethod
def __CheckMsg(self, content):
logContent = "你的提交信息如下:\n%s" % content
#0.处理合并时有冲突的情况
if MERGE_BRANCH_TEXT in content and CONFLICTS_TEXT in content:
content = "feat:"+content
pass
#1.匹配空行
changeLineRegex = re.compile(r".*\S\n\n.*\S", re.S)
changeLineMachObject = changeLineRegex.match(content)
if not changeLineMachObject:
print logContent
print "msg格式不对,必须包含一个空行以区分header和body,且空行前后内容不为空"
reviewboard.reset_review_state()
sys.exit(1)
#2.匹配冒号
headerContent = content.split('\n\n',1)[0]
colonRegex = re.compile(r".*\S:.*\S", re.I)
colonMachObject = colonRegex.match(content)
if not colonMachObject:
print logContent
print "msg格式不对,header中必须有一个冒号(:)来区分type和subject,冒号前后内容不为空"
reviewboard.reset_review_state()
sys.exit(1)
#3如果有(),校验jira
typeJiraContent = headerContent.split(':',1)[0]
typeContent = typeJiraContent
#3.1校验小括号
jiraRegex = re.compile(r".*\S\(.*\S\)", re.I)
jiraMachObject = jiraRegex.match(typeJiraContent)
if jiraMachObject:
typeContent = typeJiraContent.split('(',1)[0]
#4.匹配type
if typeContent in typesList:
pass
else:
print "你的type是:%s" % typeContent
print "type类型不正确,必须为下面其中一个:%s" % typesList
reviewboard.reset_review_state()
sys.exit(1)
#4校验subject字数
#冲突的时候,不校验subject字数
if MERGE_BRANCH_TEXT in content and CONFLICTS_TEXT in content:
return
pass
subjectContent = headerContent.split(':',1)[1]
trimSpaceContent = subjectContent.replace(' ','')#去除掉空格再检查
subjectLen = len(trimSpaceContent.decode('utf-8'))
if subjectLen>maxSubjectLen:
print "subject最多%s个字,你超出了%d个字" % (maxSubjectLen,subjectLen-maxSubjectLen)
print "你的subject是:%s" % subjectContent
reviewboard.reset_review_state()
sys.exit(1)
#5 JIRA模糊匹配
blur = self.__check_is_blur_JIRAID(content, typeContent)
did_check_blur = check_out_put("git config githooks.blurcheck", False, "NO")
if blur and (did_check_blur == "NO") and len(GLOBAL_TRUE_JIRA_ID)>0:
print "\n检测到你可能想要提交到JIRA注释里,但是JIRA号填写的位置不对,应该在冒号前边,如果确实要将提交信息添加到JIRA里,请按以下信息重新修改后提交;如果不是,再次直接提交即可"
print content.replace(GLOBAL_BLUR_JIRA_ID,GLOBAL_TRUE_JIRA_ID)
check_out_put("git config githooks.blurcheck \"YES\"", False, True)
exit(-1)
pass
print "提交中.."
@classmethod
def __check_is_blur_JIRAID(self, content, type_content):
#初步检测包含:(
if content.find(type_content+":(") != 0:
return False
pass
#检测其后边含有)
blur_content = content[len(type_content+":("):]
if blur_content.find(")") == -1:
return False
pass
#模糊匹配出的JIRAID,看最后的JIRAID)后是否还有内容
blur_jira_ID = blur_content[0:blur_content.find(")")]
if blur_content.find("\n") == blur_content.find(")")+1:
return False
pass
#匹配出的JIRAID为空
if len(blur_jira_ID)==0:
return False
pass
#匹配出的JIRAID有中文
if check_contain_chinese(blur_jira_ID):
return False
pass
global GLOBAL_BLUR_JIRA_ID
global GLOBAL_TRUE_JIRA_ID
GLOBAL_BLUR_JIRA_ID = ":("+blur_jira_ID+")"
GLOBAL_TRUE_JIRA_ID = "("+blur_jira_ID+"):"
return True
pass
#获取提交的内容
@classmethod
def __commitMsg(self):
commit_msg_filepath = sys.argv[1]
with open(commit_msg_filepath, 'r+') as f:
content = f.read()
return content
pass
MIT License
Copyright (c) 2017 spWang
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
#!/usr/bin/env python
# coding=utf-8
import sys
import re
import subprocess
import reviewboard
import AutoVersion
'''公开函数'''
#添加subject
def add_subject_if_need():
commit_msg_filepath = sys.argv[1]
with open(commit_msg_filepath, 'r+') as f:
msg = f.read()
pass
if text_contain_blankrow(msg):
with open(commit_msg_filepath, 'w') as f:
result = reviewboard.mark_if_need_review(msg)
f.write(result)
return
pass
with open(commit_msg_filepath, 'w') as f:
result = reviewboard.mark_if_need_review(msg)
if ":" in result:
result = result+"\n"+"1."+result[result.index(":")+1:]
pass
f.write(result)
pass
pass
#添加版本号/分支名字
def add_premsg_if_need():
if add_msg_state() == "NO":
print "未开启添加版本号/分支的功能,如需开启请执行git config githooks.premsg \"YES\""
return
pass
print "已开启提交信息前添加分支/版本号功能"
pre_text = get_add_text()
commit_msg_filepath = sys.argv[1]
with open(commit_msg_filepath, 'r+') as f:
commti_msg = f.read()
pass
with open(commit_msg_filepath, 'w') as f:
result = pre_text+commti_msg
f.write(result)
pass
pass
#获取当前拼接的内容
def get_add_text():
version = AutoVersion.read_current_project_version()
if len(version)>0:
return version
pass
#拼接分支
return "["+current_branch()+"]"
pass
def reset_add_premsg_state():
if check_out_put('git config githooks.premsg', False ,"YES") == "NO":
check_out_put('git config githooks.premsg \"YES\"', False, "")
pass
pass
def add_msg_state():
return check_out_put('git config githooks.premsg', False, "YES")
pass
'''私有函数'''
def current_branch():
branch = check_out_put("git symbolic-ref --short -q HEAD", False, "HEAD")
if len(branch) == 0:
branch = "HEAD"
return branch
pass
#分割分支的全路径
lists = branch.split('/')
now_branch = lists[len(lists)-1]
#当前分支不包含父文件夹名字,则给拼上
if len(lists)>2:
father_dir = lists[len(lists)-2]
if now_branch.find(father_dir) == -1:
now_branch = father_dir+"/"+now_branch
pass
pass
return now_branch
pass
#文本是否包含空行
def text_contain_blankrow(content):
changeLineRegex = re.compile(r".*\S\n\n.*\S", re.S)
changeLineMachObject = changeLineRegex.match(content)
if changeLineMachObject:
return True
pass
return False
pass
#基础方法
def check_out_put(cammand, can_raise, return_value):
try:
return subprocess.check_output(cammand, shell=True).strip()
pass
except subprocess.CalledProcessError as e:
if can_raise:
raise(e)
else:
return return_value
pass
pass
\ No newline at end of file
#!/bin/bash
ln -s /usr/local/bin/clang-format /usr/bin/clang-format
ln -s /usr/local/bin/rbt /usr/bin/rbt
\ No newline at end of file
gitHooks版本更新功能提示:
1.5.1:越来越好用了:
a)增加了强制更新的脚本的方式,项目下执行python .git/hooks/upgrade.py
b)改进了初始化配置的方式,在拉取的脚本代码目录下执行python setup.py
c)通过发邮件来统计了下使用的习惯
d)初始化或强制更新命令后加-d可抹掉本仓库githooks配置; 加-a可抹掉全局githooks配置; 加-v=xxx可升级至指定版本; 加-v可输出版本; 加--help可查看帮助
e)如果你填写jiraID时不小心写在了冒号的后边,会智能匹配帮助你应该怎么填写正确的
1.5.0:更新了初始化配置的方式;
提交信息改为拼接ios项目版本号(暂未开启),等功能
1.4.8:修改了xcode模板里life cycle的拼写错误问题
1.4.7:修复了首次使用脚本无法更新的问题,原因是未找到本地的版本记录导致
1.4.6: 1)配置xcode模板已经开启生效,需要根据引导提示执行命令去实现。2)邮件服务器和邮箱使用货车帮邮箱发送通知邮件。3)紧急修复了当使用自动review功能时,因为mac SIP权限未禁用时导致无法向/user/rbt/里写入文件而创建软链接时的解决方案,可根据对应提示进行操作
1.4.5:配置xcode模板。
1.4.4:修复编译报错。检查更新时的请求添加请求头。
1.4.3:重写了自动更新模块,并修复了一些小问题。处理了一些异常的情况, 比如更新代码失败时, 满足特定条件则不抛出异常, 另外增加了失败的报警邮件。增加了很多更友好的打印
1.4.2:修复自动发送reviewRequest只能在命令行使用,不能在sourcetree中使用的问题
1.4.1:修复bug:首次更新未使用过reviewRequest功能而导致的崩溃问题
1.4.0:本版有两个内容更新:1)自动帮你发布reviewRequest,方法是:在提交信息前加关键字,例如review_test:test; 目前支持的关键字有"review_","rbt_", "re_", "review-","rbt-", "re-"。2)使用统一的xcode模板,会帮你自动设置统一的模板给xcode,但是还未上传模板过去,因此此功能暂未生效。
1.3.5:解决了上一版clang-format只能在命令行使用而不能在sourcetree中使用的问题
1.3.4:clang-format文件先试用一下,先只对.h文件格式化
1.3.3:修复了上一版的bug,将header前的类型如fix:去除掉了,看起来更像自己写的subject
1.3.2:如果没有填写subject,则将header自动填充为subject
1.3.1:对subject 字数超长的,给出提示超过了多少字
1.3.0:增加了自动代码格式化的功能,使用如下:
1.此功能需确保每个人都安装了clang-format,安装方法:执行两个命令brew update & brew upgrade和brew install clang-format;其中第一个命令如果更新过就不用再更新了,安装成功的话:执行clang-format --help有输出
2.在.git同级目录下增加自定义的.clang-format配置文件生效,不配置不生效;
3.配置文件参考:
http://www.cnblogs.com/PaulpauL/p/5929753.html
http://clang.llvm.org/docs/ClangFormatStyleOptions.html
1.2.2:更改了下载代码的超时时长为30秒;更新成功,会有统计到我这里😁
1.2.1:所有功能增加控制开关,开启命令如下:
   1. commit message前自动拼上当前版本号,默认开启(git config githooks.premsg "YES")
   2. ios项目检查项目中图片相同的名字,默认开启(git config githooks.sameimg "YES")
   3. commit message规范化提交,默认开启(git config githooks.checkmsg "YES")
   4. 自动将你的commit message填充到JIRA上注释上,默认开启(git config githooks.notejira "YES")
   5. 项目build版本号commit后自动加1,默认关闭(git config githooks.autoversion "YES")
1.2.0:增加了自动更新版本号的功能,此功能默认关闭,开启方法:cd到你的仓库,执行git config githooks.autoversion "YES"
1.1.9:重构了自建更新功能,将版本信息存储的位置改了,这样就更方便用命令删除版本信息,从而强制更新脚本
1.1.8:针对某个仓库定制是否需要添加当前分支到提交信息的前面,cd到你的仓库, 执行命令git config githooks.premsg "NO"即可关闭,配置其他任何内容则开启,这个命令在使用文档我也更新到了,看这儿http://git.quantgroup.cn/APP/gitHooks
1.1.7:当有提交信息为解决冲突时,不校验subject字数
1.1.6:修改了获取当前分支的方式,全路径为最后路径
1.1.5:修复了subject和body中存下小括号时导致issueID解析错误的问题
1.1.4:自建更新检测时长改为了24小时检查一次
1.1.3:解决了当提交代码因冲突导致检测commit-msg不通过的问题
1.1.2:增加了jira的功能,可以将commit-msg填充到jira上啦,前提是你必须填正确的jira号
1.1.1:自建更新功能fix:文件夹复制导致目录乱的修复
1.1.0:修复了因提交时未在某分支上造成的崩溃
1.0.9:在控制台输出的更新文案做了代码优化
1.0.8:在控制台输出了本次更新的内容,并且高亮显示,方便各位看官看到脚本更新了啥
1.0.7:可以在提交信息的type后可以加入issueID啦,header的格式为type(issueID):subject;当然也可以不加,header的格式依然为type:subject(注:关键字小括号()校验)
#!/usr/bin/python
# -*- coding: UTF-8 -*-
import subprocess
import os
import reviewboard
from util.colorlog import *
def format_files():
if check_out_put("git config githooks.clangformat", False) == "NO":
print "你未开启自动代码格式的功能,如需开启请执行git config githooks.clangformat \"YES\""
return
pass
if not clang_format_file_exist():
print "未检测到.clang-format文件,无需开启格式化代码功能"
return
if not file_path_exist("/usr/local/bin/clang-format"):
login = "如果你还未安装clang-format,请执行下面两个命令(第一个命令如果已经更新过就不用再执行了):\nbrew update & brew upgrade\nbrew install clang-format"
logout = login+"\n看到在这个地址下载表示正确更新了:https://homebrew.bintray.com/bottles/clang-format-2017-06-22.high_sierra.bottle.tar.gz"
logred("然后可以执行brew list看是否安装成功")
logred(logout)
exit(-1)
pass
file_path = "/usr/bin/clang-format"
if not file_path_exist(file_path):
reviewboard.log_operation_not_permitted(file_path, "自动代码格式化", "git config githooks.clangformat \"NO\"")
exit(-1)
pass
print "正在格式化代码..."
output = check_out_put("git diff-index --cached --name-only HEAD",False)
for file in output.split("\n"):
format_codefile(file)
pass
print "格式化代码完成"
pass
def format_codefile(file):
if len(file) == 0:
return
pass
if not file_path_exist(file):
print "不在的文件:%s" % file
return
suf = file.split(".")[-1]
# if suf == "h" or suf == "m" or suf == "mm" or suf == "c":
if suf == "h" or suf=="m":
print "格式化:"+file
check_out_put("clang-format -i -style=file "+file,True)
add_cammand = "git add "+file
check_out_put(add_cammand, True)
pass
pass
#send.py也用到这里
def clang_format_file_exist():
clang_format_file = os.getcwd()+"/.clang-format"
return file_path_exist(clang_format_file)
pass
def file_path_exist(file):
return os.path.exists(file) and os.path.isfile(file)
pass
def check_out_put(cammand,can_raise):
try:
return subprocess.check_output(cammand, shell=True).strip()
pass
except subprocess.CalledProcessError as e:
if can_raise:
raise(e)
else:
return None
pass
pass
def hooks_path():
currentPath = os.path.realpath(__file__);
fileName = os.path.basename(__file__);
return currentPath.replace(fileName,"");
pass
#!/usr/bin/env python
# coding=utf-8
import sys
import subprocess
import AutoVersion
import addpremsg
from CommitMsgStyle import CheckMsgStyle
def main():
addpremsg.add_subject_if_need()
CheckMsgStyle.check_msg_if_need()
addpremsg.add_premsg_if_need()
AutoVersion.add_version_if_need()
pass
if __name__ == '__main__':
print "-->git hook脚本commit-msg开始执行"
main()
print "-->git hook脚本commit-msg执行完毕\n"
pass
#!/usr/bin/python
# -*- coding: UTF-8 -*-
import os
import subprocess
def open_execute_limit():
check_file_access("pre-applypatch")
check_file_access("applypatch-msg")
check_file_access("pre-commit")
check_file_access("prepare-commit-msg")
check_file_access("commit-msg")
check_file_access("post-commit")
check_file_access("pre-push")
check_file_access("update")
check_file_access("post-update")
check_file_access("pre-rebase")
check_file_access("pre-receive")
pass
def check_file_access(file_name):
file_path = hooks_path()+file_name
file_access = os.access(file_path,os.X_OK)
if os.path.exists(file_path) and file_access == False:
cammand = "chmod +x "+file_name
subprocess.Popen(cammand,shell=True,cwd=hooks_path())
print file_name+"可执行权限未开启,已为你重新开启"
pass
pass
def hooks_path():
currentPath = os.path.realpath(__file__);
fileName = os.path.basename(__file__);
return currentPath.replace(fileName,"");
pass
#!/usr/bin/env python
# coding=utf-8
import os
import sys
import subprocess
#公开
def clear_all_config():
print " 抹掉全局githooks配置"
check_out_put("git config --global githooks.testabc 1", False, "")
print check_out_put("git config --global --remove-section githooks", False, "抹除全局githooks所有配置失败")
pass
def clear_current_repo_config():
print " 抹掉当前仓库的githooks配置("+os.getcwd()+")"
check_out_put("git config githooks.testabc 1", False, "")
print check_out_put("git config --remove-section githooks", False, "抹除本仓库githooks所有配置失败")
pass
def log_help(is_setup):
print "帮助文档:"
print "-h --help 输出帮助文档"
if is_setup:
print "-d 抹掉setup.plist配置下的仓库的githooks配置"
pass
else:
print "-d 抹掉当前仓库的githooks配置"
pass
print "-a 抹掉全局的githoks配置"
print "-v --version 输出当前仓库githooks的版本"
print "-v=xxx --version=xxx 升级至指定的版本号"
pass
#私有
def check_out_put(cammand, can_raise, return_value):
try:
return subprocess.check_output(cammand, shell=True).strip()
pass
except subprocess.CalledProcessError as e:
if can_raise:
raise(e)
else:
return return_value
pass
pass
#!/usr/bin/env python
# coding=utf-8
import re
import os
import sys
import subprocess
import addpremsg
import statistics
from jira import JIRA
from CommitMsgStyle import CheckMsgStyle
from util.colorlog import *
JIRA_SERVER_URL = 'http://jira.quantgroup.cn/'
GOLBAL_JIRA_ID = ""
GOLBAL_COMMIT_MSG = ""
'''公开方法'''
def note_jira_if_need(review_url):
jira_state = check_out_put("git config githooks.notejira", False, "YES")
if jira_state == "NO":
print "未开启填充注释到jira的功能,如需开启请执行git config githooks.notejira \"YES\""
return
pass
print "已开启填充注释到jira的功能"
if not can_note_jira():
return
pass
note_jira(review_url)
pass
#重置状态
def reset_jira_state():
if check_out_put('git config githooks.notejira', False ,"YES") == "NO":
check_out_put('git config githooks.notejira \"YES\"', False, "")
pass
pass
'''私有方法'''
def can_note_jira():
#1.检查最后一笔提交是不是本人
local_user = check_out_put("git config user.name",False,"")
global_local_user = check_out_put("git config --global user.name",False,"")
last_commit_user = check_out_put("git log --format=%an -n 1",False,"None")
if last_commit_user != global_local_user and last_commit_user != local_user:
print "最后一笔提交不是你的,无法填充打jira注释"
return False
pass
#2.检查是不是在没有commit的情况下执行了push
result = check_out_put("git status",True,"")
need_push = "use \"git push\" to publish your local commits"
need_pull = "use \"git pull\" to merge the remote branch into yours"
if not need_push in result and not need_pull in result:
print "你当前没有什么可以push的东西,因此也不需要去填充jira"
return False
pass
#3.填充过了就不需要填充jira了
#4.未检查到jira号,不填充
commit_message = check_out_put("git log --format=%B -n 1", False, "")
left_bracket_location = commit_message.find("(")
right_bracket_location = commit_message.find("):")
if left_bracket_location == -1 or right_bracket_location == -1:
print "未检测到关键字():"
print "表示没有填写jira号,不填充jira注释"
return False
pass
#5.从提交信息第一个字符到冒号之间,检查header类型是否匹配,以防止匹配到了后边的提交信息,导致JIRA号匹配错误
add_text = addpremsg.get_add_text()
header = commit_message[0:left_bracket_location].replace(add_text,"")
issue_id = commit_message[left_bracket_location+1:right_bracket_location]
if not header in CheckMsgStyle.headerTypesList():
print "检测到的header是"+header
print "header类型不在可选列表中,无法做JIRA号的匹配,不填充jira注释"
return False
pass
#6.检查JIRA号是否存在
if len(issue_id) == 0:
print "你的jira_id为空,无法填充jira注释"
return False
pass
#7.检查jira用户名
if len(jira_user_name()) == 0 or len(jira_user_pwd()) == 0:
print "你没有为JIRA配置用户名或密码,请按照如下命令格式分别配置用户名和密码"
print "git config --global jira.user \"xxx@56qq.com\""
print "git config --global jira.pwd \"xxxpwd\""
exit(-1)
pass
global GOLBAL_JIRA_ID;
global GOLBAL_COMMIT_MSG;
GOLBAL_JIRA_ID = issue_id
GOLBAL_COMMIT_MSG = commit_message
statistics.add_jira_count()
return True
pass
def update_jira_comment(issue_id, commit_message):
jira_user = jira_user_name()
jira_password = jira_user_pwd()
authed_jira = JIRA(server=(JIRA_SERVER_URL), basic_auth=(jira_user, jira_password))
issue = authed_jira.issue(issue_id)
authed_jira.add_comment(issue, commit_message)
def note_jira(review_url):
if not review_url:
review_url = ""
pass
if GOLBAL_JIRA_ID == "":
print "获取JIRA号异常,JIRA号为空,无法填充jira注释"
return
pass
if GOLBAL_COMMIT_MSG == "":
print "读取commit msg异常,无法填充jira注释"
return
pass
if len(review_url)>0:
statistics.add_review_jira_count()
pass
print "你填写的jira号是:"+GOLBAL_JIRA_ID
print "正在把msg填充到jira...请稍候"
commit_message = GOLBAL_COMMIT_MSG + review_url
update_jira_comment(GOLBAL_JIRA_ID,commit_message)
print "填充完成,提交中...请稍候"
pass
def jira_user_name():
return check_out_put("git config jira.user", False, "")
pass
def jira_user_pwd():
return check_out_put("git config jira.pwd", False, "")
pass
#基础支撑方法
def check_out_put(cammand, can_raise, return_value):
try:
return subprocess.check_output(cammand, shell=True).strip()
pass
except subprocess.CalledProcessError as e:
if can_raise:
raise(e)
else:
return return_value
pass
pass
#!/usr/bin/python
# -*- coding: UTF-8 -*-
import statistics
import CommitMsgStyle
def main():
statistics.add_all_commit_count()
CommitMsgStyle.mark_did_blur_check()
pass
if __name__ == '__main__':
print "-->git hook脚本post-commit开始执行"
main()
print "-->git hook脚本post-commit执行完毕\n"
pass
\ No newline at end of file
#!/usr/bin/python
# -*- coding: UTF-8 -*-
def main():
pass
if __name__ == '__main__':
print "-->git hook脚本post-update开始执行"
main()
print "-->git hook脚本post-update执行完毕\n"
pass
\ No newline at end of file
#!/usr/bin/python
# -*- coding: UTF-8 -*-
import clangformat
import executelimit
import xcodetemplate
from util.colorlog import *
def main():
executelimit.open_execute_limit()
xcodetemplate.move_template_if_need()
clangformat.format_files()
pass
if __name__ == '__main__':
logblue("提示:如果出现导入包报错,例如找不到包xxx, ImportError: No module named xxx, 请执行pip install xxx --user安装依赖包,或执行其他命令安装",)
print "\n-->git hook脚本pre-commit开始执行"
main()
print "-->git hook脚本pre-commit执行完毕\n"
pass
\ No newline at end of file
#!/usr/bin/env python
# coding=utf-8
import notejira
import reviewboard
def main():
review_url = reviewboard.post_review_if_need()
notejira.note_jira_if_need(review_url)
pass
if __name__ == '__main__':
print "-->git hook脚本pre-push开始执行"
main()
print "-->git hook脚本pre-push执行完毕\n"
# print "测试环境,正常结束"
# exit(-1)
pass
\ No newline at end of file
#!/usr/bin/env python
# coding=utf-8
import sys
from CheckSameImgName import CheckImgName
def main():
CheckImgName.check_img_if_need()
pass
if __name__ == '__main__':
print "-->git hook脚本prepare-commit-msg开始执行"
main()
print "-->git hook脚本prepare-commit-msg执行完毕\n"
pass
#!/usr/bin/env python
# coding=utf-8
import subprocess
import os
import statistics
from util.colorlog import *
'''公开函数'''
def key_words():
return ["review_","rbt_", "re_", "review-","rbt-", "re-"]
pass
def log_operation_not_permitted(file_path, func_desc, cammand):
print "\n"
print file_path+"文件不存在,无法在非命令行中使用"+func_desc+"的功能,你需要:"
print "复制下个命令创建软链接后再尝试提交代码"
print "sudo bash " + hooks_path()+"bridge.sh"
print "如果遇到提示权限不允许(Operation not permitted),说明你的mac SIP权限没有禁用,无法向/ust/bin里边添加文件去创建软链接,解决办法看下边的网址:"
print "http://blog.csdn.net/u012165769/article/details/50477410"
print "如果你仍然想继续此次提交,暂时不使用"+func_desc+"的功能,执行命令"+cammand+"后继续提交代码"
print "\n"
pass
def post_review_if_need():
review_state = check_out_put("git config githooks.review", False, "NO")
if review_state == "NO":
return ""
pass
advice = " 如果你遇到自动发送review的问题,请先将问题反馈脚本开发者,然后执行此命令(git config githooks.review \"NO\")临时关闭一次此功能,最后重新push。\n"
logblue(advice)
check_cammand_can_execute()
print "正在发送reviewboard请求..."
result = check_out_put('rbt post -g -p',False, "")
reset_review_state()
print "发送reviewboard请求完毕;结果:\n"+result
if not "\n" in result:
return result
pass
return result[result.index("\n")+1:]
pass
#根据提交信息检测是否需要标记review,并返回提交信息
def mark_if_need_review(commit_msg):
for keyword in key_words():
if commit_msg.find(keyword) == 0:
check_out_put("git config githooks.review YES", False, "")
statistics.add_review_count()
return commit_msg[commit_msg.index(keyword)+len(keyword):]
pass
pass
return commit_msg
pass
#重置review的状态
def reset_review_state():
if check_out_put('git config githooks.review', False ,"NO") == "YES":
check_out_put('git config githooks.review \"NO\"', False, "")
pass
pass
'''私有函数'''
def file_path_exist(file):
return os.path.exists(file) and os.path.isfile(file)
pass
def check_cammand_can_execute():
file_path = "/usr/bin/rbt"
if not file_path_exist(file_path):
log_operation_not_permitted(file_path, "自动发送review", "git config githooks.review \"NO\"")
exit(-1)
pass
pass
'''基础方法'''
def hooks_path():
currentPath = os.path.realpath(__file__);
fileName = os.path.basename(__file__);
return currentPath.replace(fileName,"");
pass
def check_out_put(cammand, can_raise, return_value):
try:
return subprocess.check_output(cammand, shell=True).strip()
pass
except subprocess.CalledProcessError as e:
if can_raise:
raise(e)
else:
return return_value
pass
pass
\ No newline at end of file
#!/usr/bin/python
# -*- coding: UTF-8 -*-
import os
import smtplib
import socket
import getpass
from email.mime.text import MIMEText
import subprocess
import clangformat
import statistics
# mail_to_list=['shuaipeng.wang@56qq.com']
mail_to_list=['ios-team@56qq.com']
# mail_host="mail.huochebang.com"
# mail_sender_name="githooks.ios@huochebang.com"
# mail_sender_pwd="chuth-7?7vephaJere"
mail_host="smtp.qq.com"
mail_sender_name="iOS@qq.com"
mail_sender_pwd="QuantGroupNo1.."
def send_mail_for_content(content):
if not content:
return
pass
content = content+config_content()+statistics.data_collect()
mail_title = user_name()+" (githooks脚本更新成功)"
mail_content = user_name()+":"+content
success = send_mail(mail_to_list,mail_title,mail_content)
return success
pass
#失败次数告警
def send_mail_for_fail(count,fail_type,traceback):
if not count or not fail_type or not traceback:
return
pass
content = fail_type+"("+"当前失败次数为第"+count+"次)\n"+"最后一次失败回溯:\n"+traceback
mail_title = user_name()+" (githooks脚本更新失败)"
mail_content = user_name()+":"+content
success = send_mail(mail_to_list,mail_title,mail_content)
return success
pass
def send_mail(to_list,title,content):
me = "githooks-service"+"<"+mail_sender_name+">"
msg = MIMEText(content,'plain', 'utf-8')
msg['Subject'] = title
msg['From'] = me
msg['To'] = ";".join(to_list)
try:
server = smtplib.SMTP()
server.connect(mail_host)
server.login(mail_sender_name,mail_sender_pwd)
server.sendmail(me, to_list, msg.as_string())
server.close()
print "通知邮件已经发送成功。"
return True
except Exception, e:
print "通知邮件发送失败。"
print e
return False
def config_content():
project = "\n\n"+"项目路径: "+os.getcwd()+"\n\n"
xcode_template = "1.xcode统一模板:"+xcode_template_state()+"\n"
sameimg = "2.iOS项目检查同名图片: "+sameimg_state()+"\n"
checkmsg = "3.commitMsg规范性检查: "+checkmsg_state()+"\n"
premsg = "4.拼接版本号/分支到提交信息: "+premsg_state()+"\n"
autoversion = "5.自动递增版本号: "+autoversion_state()+"\n"
notejira = "6.填充注释到JIRA: "+notejira_state()+"\n"
clang_format = "7..clang-format文件是否存在: "+clang_format_file()+"\n"
return project+xcode_template+sameimg+checkmsg+premsg+autoversion+notejira+clang_format
pass
def xcode_template_state():
return check_out_put('git config githooks.xcodetemplate', False, "YES")
pass
def sameimg_state():
return check_out_put('git config githooks.sameimg', False, "YES")
pass
def checkmsg_state():
return check_out_put('git config githooks.checkmsg', False, "YES")
pass
def premsg_state():
return check_out_put('git config githooks.premsg', False, "YES")
pass
def autoversion_state():
return check_out_put('git config githooks.autoversion', False, "NO")
pass
def notejira_state():
return check_out_put('git config githooks.notejira', False, "YES")
pass
def clang_format_file():
open_state = clangformat.clang_format_file_exist()
return "NO" if not open_state else "YES"
pass
def user_name():
name = check_out_put('git config user.name', False, "")
if not len(name):
name = check_out_put('git config --global user.name', False, "未知名字")
pass
return name
pass
def check_out_put(cammand, can_raise, return_value):
try:
return subprocess.check_output(cammand, shell=True).strip()
pass
except subprocess.CalledProcessError as e:
if can_raise:
raise(e)
else:
return return_value
pass
pass
<?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>jiar_name</key>
<string>yingongjing</string>
<key>jira_pwd</key>
<string>QuantGroupNo1..</string>
<key>project_paths</key>
<array>
<string>/Users/QuantGroup/Desktop/办公/WorkSpace/CreditWallet</string>
<string>/Users/QuantGroup/Desktop/办公/WorkSpace/btlk-ios</string>
<string>/Users/QuantGroup/Desktop/办公/WorkSpace/TensorFlow</string>
<string></string>
<string></string>
<string></string>
</array>
</dict>
</plist>
#!/usr/bin/env python
# coding=utf-8
import os
import re
import sys
import subprocess
import shutil
import executelimit
import hookshelp
from util.colorlog import *
from biplist import *
reload(sys)
sys.setdefaultencoding('utf8')
TEMPLATE_FILE = ".git_template"+os.path.sep
CONFIG_FILE = "setup.plist"
GIT_FILE = ".git"
PRE_COMMIT_FILE = "pre-commit"
GLOBAL_clear_repo_config = False
def main():
#检查依赖库是否安装及自动安装
# check_dependent_balls()
#检查用户名和密码,项目全路径是否正确配置
project_paths = check_require_config()
#创建模板文件夹
template_path = create_template_dir()
#开启当前文件夹下文件的可执行权限
executelimit.open_execute_limit()
#拷贝当前文件夹下所有文件到模板文件夹
copy_code_files_to_template(template_path)
#配置全局的git模板文件路径
config_global_git()
#所有项目执行命令
setup_all_projects(project_paths)
pass
def check_dependent_balls():
print " 正在检查依赖库是否安装"
pip_list = check_out_put("pip list --format=legacy", False, None)
if not pip_list:
logred(" 未安装pip,请参考gitHub页面里常见问题一栏pip的安装方案来安装pip")
exit(-1)
pass
if not "jira" in pip_list:
print " 正在安装jira..."
check_out_put("pip install jira --user", False, None)
pass
#安装后重新检测一次
pip_list = check_out_put("pip list --format=legacy", False, None)
result = True
if not "jira" in pip_list:
print " 请手动安装jira"
result = False
pass
if not result:
exit(-1)
pass
pass
def check_require_config():
print " 正在检查必要的配置项"
config_path = current_path()+CONFIG_FILE
if not (os.path.exists(config_path) and os.path.isfile(config_path)):
log = " 未知原因导致配置文件"+config_path+"不存在,请联系开发者"
logred(log)
exit(-1)
pass
if not check_file_is_plsit(config_path):
log = " 未知原因导致配置文件"+config_path+"不是plist文件,无法读取,请联系开发者"
logred(log)
exit(-1)
pass
result = True
jira_name = read_plist_for_key(config_path, "jiar_name", False, "")
if not len(jira_name):
logred(" 未配置JIRA用户名")
result = False
pass
jira_pwd = read_plist_for_key(config_path, "jira_pwd", False, "")
if not len(jira_name):
logred(" 未配置JIRA密码")
result = False
pass
check_out_put("git config --global jira.user "+jira_name, False, None)
check_out_put("git config --global jira.pwd "+jira_pwd, False, None)
project_paths = read_plist_for_key(config_path, "project_paths", False, None)
if not project_paths:
logred(" "+CONFIG_FILE+"中project_paths必须是数组")
result = False
pass
no_project = True
for project_path in project_paths:
if not len(project_path):
continue
pass
no_project = False
if not(os.path.exists(project_path) and os.path.isdir(project_path)):
logred(" 无法检测到项目路径" + project_path + ",请检查"+CONFIG_FILE+"文件里项目路径的配置")
result = False
pass
else:
project_git_path = project_path+os.path.sep+GIT_FILE
if not(os.path.exists(project_git_path) and os.path.isdir(project_git_path)):
logred(" 项目路径不正确" + project_path + ",必须配置到其下包含.git的目录为止")
result = False
pass
pass
if no_project:
logred(" 至少需要配置一个项目全路径")
pass
if not result:
logred("请在当前目录下找到"+CONFIG_FILE+"完善上述配置项后重新执行当前命令")
exit(-1)
pass
return project_paths
pass
def create_template_dir():
rootdir = os.environ['HOME']
template_path = rootdir+os.path.sep+TEMPLATE_FILE
if os.path.exists(template_path):
print " 发现旧的模板路径存在,正在删除旧目录"
shutil.rmtree(template_path)
pass
if not os.path.exists(template_path):
print " 正在初始化模板目录"
os.makedirs(template_path)
pass
return template_path
pass
def copy_code_files_to_template(template_path):
print " 正在复制代码到模板目录"
new_path = template_path+"hooks"+os.path.sep
shutil.copytree(current_path(),new_path)
pass
def config_global_git():
print " 正在配置全局的git初始化模板"
check_out_put("git config --global init.templatedir ~/.git_template", True, "")
pass
def setup_all_projects(project_paths):
for project_path in project_paths:
if not len(project_path):
continue
pass
print " 正在初始化项目"+project_path
hooks_path = project_path+os.path.sep+GIT_FILE+os.path.sep+"hooks"
if os.path.exists(hooks_path):
print " 发现旧hooks存在,正在删除旧目录"
shutil.rmtree(hooks_path)
pass
os.chdir(project_path)
setup = "git init"
check_out_put(setup, True, None)
if GLOBAL_clear_repo_config:
hookshelp.clear_current_repo_config()
pass
success = check_setup_success(hooks_path)
if success:
loggreen(" "+project_path+"初始化完成")
pass
else:
logred(" "+project_path+"初始化失败")
pass
pass
def check_setup_success(hooks_path):
pre_commit = hooks_path+os.path.sep+PRE_COMMIT_FILE
if os.path.exists(pre_commit) and os.path.isfile(pre_commit):
if os.access(pre_commit,os.X_OK):
return True
pass
pass
return False
pass
#处理参数
def deal_argv(argv):
for i in range(1, len(argv)):
if argv[i] == "-d":
global GLOBAL_clear_repo_config
GLOBAL_clear_repo_config = True
pass
if argv[i] == "-a":
hookshelp.clear_all_config()
pass
if i==1 and (argv[i] == "-h" or argv[i] == "--help"):
hookshelp.log_help(True)
exit(0)
pass
pass
# 基础方法
def check_file_is_plsit(plist_path):
try:
plist = readPlist(plist_path)
return True
except (InvalidPlistException, NotBinaryPlistException), e:
return False
pass
# 此方法必须先验证是plist文件
def read_plist_for_key(plist_path, key, can_raise, return_value):
plist = readPlist(plist_path)
try:
return plist[key]
except(KeyError, Exception),e:
return return_value
pass
def current_path():
currentPath = os.path.realpath(__file__);
fileName = os.path.basename(__file__);
return currentPath.replace(fileName,"");
pass
def check_out_put(cammand, can_raise, return_value):
try:
return subprocess.check_output(cammand, shell=True).strip()
pass
except subprocess.CalledProcessError as e:
if can_raise:
raise(e)
else:
return return_value
pass
pass
#入口
if __name__ == '__main__':
deal_argv(sys.argv)
logblue("开始初始化配置...")
main()
logblue("恭喜你,所有初始化配置完毕👏👏👏")
pass
\ No newline at end of file
#!/usr/bin/python
# -*- coding: UTF-8 -*-
import os
import subprocess
#statistics
#公开
def data_collect():
commits = float(all_commit_count())
if commits == 0.0:
return ""
pass
reviews = float(review_count())
jiras = float(jira_count())
review_jira = float(review_jira_count())
review_percent = "%.2f%%" % (reviews/commits*100)
jira_percent = "%.2f%%" % (jiras/commits*100)
review_jira_percent = "%.2f%%" % (review_jira/commits*100)
title = "\n\n数据统计(本统计用来看下使用习惯,并无其他用途)\n"
commit_count_str = "本周期本仓库总提交数量:"+str(int(commits))
review_count_str = "\n使用自动review功能的数量:"+str(int(reviews))+"(占比:"+str(review_percent)+")"
jira_count_str = "\n使用填充jira功能的数量:"+str(int(jiras))+"(占比:"+str(jira_percent)+")"
review_jira_count_str = "\n同时使用jira和review功能的数量:"+str(int(review_jira))+"(占比:"+str(review_jira_percent)+")"
text = title+commit_count_str+review_count_str+jira_count_str+review_jira_count_str
clear_all_counts()
return text
pass
def add_all_commit_count():
commits = int(all_commit_count())
result = str(commits+1)
check_out_put("git config githooks.commitnum "+result, False, "")
pass
def add_review_count():
reviews = int(review_count())
result = str(reviews+1)
check_out_put("git config githooks.reviewnum "+result, False, "")
pass
def add_jira_count():
jiras = int(jira_count())
result = str(jiras+1)
check_out_put("git config githooks.jiranum "+result, False, "")
pass
def add_review_jira_count():
review_jira = int(review_jira_count())
result = str(review_jira+1)
check_out_put("git config githooks.reviewjiranum "+result, False, "")
pass
def clear_all_counts():
check_out_put("git config --unset githooks.commitnum", False, "0")
check_out_put("git config --unset githooks.reviewnum", False, "0")
check_out_put("git config --unset githooks.jiranum", False, "0")
check_out_put("git config --unset githooks.reviewjiranum", False, "0")
pass
#私有
def all_commit_count():
return check_out_put("git config githooks.commitnum", False, "0")
pass
def review_count():
return check_out_put("git config githooks.reviewnum", False, "0")
pass
def jira_count():
return check_out_put("git config githooks.jiranum", False, "0")
pass
def review_jira_count():
return check_out_put("git config githooks.reviewjiranum", False, "0")
pass
def check_out_put(cammand, can_raise, return_value):
try:
return subprocess.check_output(cammand, shell=True).strip()
pass
except subprocess.CalledProcessError as e:
if can_raise:
raise(e)
else:
return return_value
pass
pass
#!/usr/bin/env python
# coding=utf-8
import os
import sys
import re
import subprocess
import autoupdate
import hookshelp
GLOBAL_version = ""
#公开
def deal_argv(argv):
for i in range(1, len(argv)):
if i==1 and (argv[i] == "-h" or argv[i] == "--help"):
hookshelp.log_help(False)
exit(0)
if i==1 and (argv[i] == "-v" or argv[i] == "--version"):
print check_out_put("git config githooks.version", True, "未发现版本号")
exit(0)
pass
if argv[i] == "-d":
hookshelp.clear_current_repo_config()
pass
if argv[i] == "-a":
hookshelp.clear_all_config()
pass
if argv[i].find("-v=")==0:
check_argv_version(argv[i][3:])
pass
if argv[i].find("--version=")==0:
check_argv_version(argv[i][10:])
pass
pass
pass
def check_argv_version(version_str):
if not len(version_str):
print "未输入版本号"
return
pass
global GLOBAL_version
GLOBAL_version = version_str
pass
def main():
print "执行强制更新,清空上次更新的时间"
check_out_put("git config githooks.updateTime \"0\"", False, "")
autoupdate.update_to_define_version(GLOBAL_version)
print "强制更新执行完毕"
pass
def check_out_put(cammand, can_raise, return_value):
try:
return subprocess.check_output(cammand, shell=True).strip()
pass
except subprocess.CalledProcessError as e:
if can_raise:
raise(e)
else:
return return_value
pass
pass
if __name__ == '__main__':
deal_argv(sys.argv)
main()
pass
# -*- coding: utf-8 -*-
from colorlog import logred
__all__ = (
)
#!/usr/bin/env python
# coding=utf-8
import re, os, sys
def logblack(msg):
print "\033[1;30;1m%s\033[0m" % msg;
pass
def logred(msg):
print "\033[1;31;1m%s\033[0m" % msg;
pass
def loggreen(msg):
print "\033[1;32;1m%s\033[0m" % msg;
pass
def logyellow(msg):
print "\033[1;33;1m%s\033[0m" % msg;
pass
def logblue(msg):
print "\033[1;34;1m%s\033[0m" % msg;
pass
def logpurple(msg):
print "\033[1;35;1m%s\033[0m" % msg;
pass
def logbule_green(msg):
print "\033[1;36;1m%s\033[0m" % msg;
pass
def loggray(msg):
print "\033[1;37;1m%s\033[0m" % msg;
pass
\ No newline at end of file
#!/usr/bin/env python
# coding=utf-8
import subprocess
import os
import shutil
import filecmp
TEMPLATE_VERSION = 2
new_default_file_path = "/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/Library/Xcode/Templates/File Templates/Source"
new_file_category_path = "/Applications/Xcode.app/Contents/Developer/Library/Xcode/Templates/File Templates/Source/Objective-C File.xctemplate"
template_file = os.path.expanduser('~')+"/.git_template/hooks/xcodetemplate"
template_version_cammand = "git config --global githooks.templateversion"
def move_template_if_need():
if not template_version_can_move():
remove_template_dir()
return
pass
default_name = "/Cocoa Touch Class.xctemplate"
old_default_path = template_file+default_name
new_default_path = new_default_file_path+default_name
move_template(old_default_path,new_default_path,True,False)
category_name = "/CategoryNSObject"
old_category_path = template_file+category_name
new_category_path = new_file_category_path+category_name
move_template(old_category_path,new_category_path,False,True)
remove_template_dir()
pass
def move_template(old_file_path,new_file_path,log,write_version):
if not os.path.exists(old_file_path) or not os.path.exists(new_file_path):
return
pass
if os.access(new_default_file_path,os.W_OK):
# print "有权限,干"
shutil.rmtree(new_file_path)
shutil.copytree(old_file_path,new_file_path)
if write_version:
write_template_version()
pass
else:
if log:
print "💎💎💎我中断了你的提交,原因是:没有root权限,无法为你复制xcode模板"
print "请在项目下执行sudo git commit -m \"test\"\n此命令会执行脚本让我们使用统一的xcode模板,执行完毕后再尝试你的提交💎💎💎"
exit(-1)
pass
pass
pass
def remove_template_dir():
path = savePath()+"xcodetemplate"
if os.path.exists(path):
shutil.rmtree(path)
pass
pass
def template_version_can_move():
#开启此功能,才移动
if check_out_put("git config githooks.xcodetemplate", False, "YES") == "NO":
print "未开启统一xcode模板功能,如需开启请执行git config githooks.xcodetemplate \"YES\""
return
pass
print "已开启统一xcode模板功能"
#1.文件不同,需要复制过去
file = "/Cocoa Touch Class.xctemplate/NSObjectObjective-C/___FILEBASENAME___.h"
xcode_file = new_default_file_path+file
xcode_template_file = template_file+file
#1.1模板没有,不复制
if not os.path.exists(xcode_template_file):
print "脚本未提供xcode模板文件,无需复制xcode模板"
return False
pass
#1.2未安装xcode,不复制
xcode_app = "/Applications/Xcode.app"
if not os.path.exists(xcode_app):
print "未安装xcode,不需要复制xcode模板"
return False
pass
#1.3xcode文件没有,复制
if not os.path.exists(xcode_file):
print "你的xcode没有模板文件,正在帮你复制模板..."
return True
pass
#2.内容不同,复制
equale = filecmp.cmp(xcode_file,xcode_template_file)
if not equale:
print "你的xcode的模板和提供的模板不一致,正在帮你复制模板..."
return True
pass
#3.内容相同,看标记版本
template_version = check_out_put(template_version_cammand, False, None)
if not template_version or int(template_version)<TEMPLATE_VERSION:
print "xcode模板已更新,正在帮你复制模板..."
return True
pass
print "xcode模板一致,不需要复制模板"
return False
pass
def write_template_version():
cammand = template_version_cammand+ ' ' +str(TEMPLATE_VERSION)
check_out_put(cammand, True, "")
print "xcode模板复制成功"
pass
#项目/.git/hooks/
def savePath():
currentPath = os.path.realpath(__file__);
fileName = os.path.basename(__file__);
hooksPath = currentPath.replace(fileName,"");
return hooksPath
pass
def check_out_put(cammand, can_raise, return_value):
try:
return subprocess.check_output(cammand, shell=True).strip()
pass
except subprocess.CalledProcessError as e:
if can_raise:
raise(e)
else:
return return_value
pass
pass
//
// ___VARIABLE_extendedClass:identifier___+___VARIABLE_productName:identifier___.h
// ___PROJECTNAME___
//
// Created by ___FULLUSERNAME___ on ___DATE___.
//___COPYRIGHT___
//
___IMPORTHEADER_extendedClass___
NS_ASSUME_NONNULL_BEGIN
@interface ___VARIABLE_extendedClass:identifier___ (___VARIABLE_productName:identifier___)
@end
NS_ASSUME_NONNULL_END
//
// ___VARIABLE_extendedClass:identifier___+___VARIABLE_productName:identifier___.m
// ___PROJECTNAME___
//
// Created by ___FULLUSERNAME___ on ___DATE___.
//___COPYRIGHT___
//
#import "___VARIABLE_extendedClass:identifier___+___VARIABLE_productName:identifier___.h"
@interface ___VARIABLE_extendedClass:identifier___ ()
@end
@implementation ___VARIABLE_extendedClass:identifier___ (___VARIABLE_productName:identifier___)
#pragma mark - public Method
#pragma mark - private Method
@end
//
// ___FILENAME___
// ___PROJECTNAME___
//
// Created by ___FULLUSERNAME___ on ___DATE___.
//___COPYRIGHT___
//
___IMPORTHEADER_cocoaTouchSubclass___
NS_ASSUME_NONNULL_BEGIN
@interface ___FILEBASENAMEASIDENTIFIER___ : ___VARIABLE_cocoaTouchSubclass___
@end
NS_ASSUME_NONNULL_END
//
// ___FILENAME___
// ___PROJECTNAME___
//
// Created by ___FULLUSERNAME___ on ___DATE___.
//___COPYRIGHT___
//
#import "___FILEBASENAME___.h"
@interface ___FILEBASENAMEASIDENTIFIER___()
@end
@implementation ___FILEBASENAMEASIDENTIFIER___
#pragma mark - public Method
#pragma mark - private Method
@end
//___FILEHEADER___
import UIKit
class ___FILEBASENAMEASIDENTIFIER___: ___VARIABLE_cocoaTouchSubclass___ {
}
<?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>Kind</key>
<string>Xcode.IDEFoundation.TextSubstitutionFileTemplateKind</string>
<key>Description</key>
<string>A Cocoa Touch class.</string>
<key>Summary</key>
<string>A Cocoa Touch class</string>
<key>SortOrder</key>
<string>1</string>
<key>DefaultCompletionName</key>
<string>MyClass</string>
<key>Platforms</key>
<array>
<string>com.apple.platform.iphoneos</string>
</array>
<key>Options</key>
<array>
<dict>
<key>Identifier</key>
<string>productName</string>
<key>Required</key>
<true/>
<key>Name</key>
<string>Class:</string>
<key>Description</key>
<string>The name of the class to create</string>
<key>Type</key>
<string>text</string>
<key>NotPersisted</key>
<true/>
</dict>
<dict>
<key>Identifier</key>
<string>cocoaTouchSubclass</string>
<key>Required</key>
<string>YES</string>
<key>Name</key>
<string>Subclass of:</string>
<key>Description</key>
<string>What class to subclass in the new file</string>
<key>Type</key>
<string>class</string>
<key>Default</key>
<string>NSObject</string>
<key>FallbackHeader</key>
<string>#import &lt;UIKit/UIKit.h&gt;</string>
<key>Values</key>
<array>
<string>NSObject</string>
<string>UIView</string>
<string>UIViewController</string>
<string>UITableViewController</string>
<string>UITableViewCell</string>
<string>UICollectionViewController</string>
<string>UICollectionViewCell</string>
<string>UICollectionReusableView</string>
</array>
<key>Suffixes</key>
<dict>
<key>UIViewController</key>
<string>ViewController</string>
<key>UITableViewController</key>
<string>TableViewController</string>
<key>UITableViewCell</key>
<string>TableViewCell</string>
<key>UICollectionViewController</key>
<string>CollectionViewController</string>
<key>UICollectionViewCell</key>
<string>CollectionViewCell</string>
<key>UICollectionReusableView</key>
<string>CollectionReusableView</string>
</dict>
</dict>
<dict>
<key>Identifier</key>
<string>XIB</string>
<key>Name</key>
<string>Also create XIB file</string>
<key>Description</key>
<string>Whether to create a XIB file with the same name</string>
<key>Type</key>
<string>checkbox</string>
<key>RequiredOptions</key>
<dict>
<key>cocoaTouchSubclass</key>
<array>
<string>UIViewController</string>
<string>UITableViewController</string>
<string>UITableViewCell</string>
<string>UICollectionViewController</string>
<string>UICollectionViewCell</string>
<string>UICollectionReusableView</string>
</array>
</dict>
<key>Default</key>
<string>false</string>
<key>NotPersisted</key>
<true/>
</dict>
<dict>
<key>Identifier</key>
<string>languageChoice</string>
<key>Required</key>
<true/>
<key>Name</key>
<string>Language:</string>
<key>Description</key>
<string>The implementation language</string>
<key>Type</key>
<string>popup</string>
<key>Default</key>
<string>Objective-C</string>
<key>Values</key>
<array>
<string>Swift</string>
<string>Objective-C</string>
</array>
<key>MainTemplateFiles</key>
<dict>
<key>Swift</key>
<string>___FILEBASENAME___.swift</string>
<key>Objective-C</key>
<string>___FILEBASENAME___.m</string>
</dict>
<key>AllowedTypes</key>
<dict>
<key>Swift</key>
<array>
<string>public.swift-source</string>
</array>
<key>Objective-C</key>
<array>
<string>public.objective-c-source</string>
<string>public.objective-c-plus-plus-source</string>
</array>
</dict>
</dict>
</array>
</dict>
</plist>
//
// ___FILENAME___
// ___PROJECTNAME___
//
// Created by ___FULLUSERNAME___ on ___DATE___.
//___COPYRIGHT___
//
___IMPORTHEADER_cocoaTouchSubclass___
NS_ASSUME_NONNULL_BEGIN
@interface ___FILEBASENAMEASIDENTIFIER___ : ___VARIABLE_cocoaTouchSubclass___
@end
NS_ASSUME_NONNULL_END
//
// ___FILENAME___
// ___PROJECTNAME___
//
// Created by ___FULLUSERNAME___ on ___DATE___.
//___COPYRIGHT___
//
#import "___FILEBASENAME___.h"
@interface ___FILEBASENAMEASIDENTIFIER___ ()
@end
@implementation ___FILEBASENAMEASIDENTIFIER___
#pragma mark - life cycle
@end
//___FILEHEADER___
import UIKit
class ___FILEBASENAMEASIDENTIFIER___: ___VARIABLE_cocoaTouchSubclass___ {
}
//
// ___FILENAME___
// ___PROJECTNAME___
//
// Created by ___FULLUSERNAME___ on ___DATE___.
//___COPYRIGHT___
//
___IMPORTHEADER_cocoaTouchSubclass___
NS_ASSUME_NONNULL_BEGIN
@interface ___FILEBASENAMEASIDENTIFIER___ : ___VARIABLE_cocoaTouchSubclass___
@end
NS_ASSUME_NONNULL_END
//
// ___FILENAME___
// ___PROJECTNAME___
//
// Created by ___FULLUSERNAME___ on ___DATE___.
//___COPYRIGHT___
//
#import "___FILEBASENAME___.h"
@interface ___FILEBASENAMEASIDENTIFIER___ ()
@end
@implementation ___FILEBASENAMEASIDENTIFIER___
#pragma mark - life cycle
- (void)awakeFromNib {
[super awakeFromNib];
}
@end
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="11134" systemVersion="15F34" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11106"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<collectionReusableView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" id="U6b-Vx-4bR">
<rect key="frame" x="0.0" y="0.0" width="320" height="50"/>
<autoresizingMask key="autoresizingMask"/>
</collectionReusableView>
</objects>
</document>
//___FILEHEADER___
import UIKit
class ___FILEBASENAMEASIDENTIFIER___: ___VARIABLE_cocoaTouchSubclass___ {
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
}
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="13142" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12042"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<collectionReusableView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" id="U6b-Vx-4bR">
<rect key="frame" x="0.0" y="0.0" width="320" height="50"/>
<autoresizingMask key="autoresizingMask"/>
<viewLayoutGuide key="safeArea" id="VXr-Tz-HHm"/>
</collectionReusableView>
</objects>
</document>
//
// ___FILENAME___
// ___PROJECTNAME___
//
// Created by ___FULLUSERNAME___ on ___DATE___.
//___COPYRIGHT___
//
___IMPORTHEADER_cocoaTouchSubclass___
NS_ASSUME_NONNULL_BEGIN
@interface ___FILEBASENAMEASIDENTIFIER___ : ___VARIABLE_cocoaTouchSubclass___
@end
NS_ASSUME_NONNULL_END
//
// ___FILENAME___
// ___PROJECTNAME___
//
// Created by ___FULLUSERNAME___ on ___DATE___.
//___COPYRIGHT___
//
#import "___FILEBASENAME___.h"
@interface ___FILEBASENAMEASIDENTIFIER___ ()
@end
@implementation ___FILEBASENAMEASIDENTIFIER___
#pragma mark - life cycle
@end
//___FILEHEADER___
import UIKit
class ___FILEBASENAMEASIDENTIFIER___: ___VARIABLE_cocoaTouchSubclass___ {
}
//
// ___FILENAME___
// ___PROJECTNAME___
//
// Created by ___FULLUSERNAME___ on ___DATE___.
//___COPYRIGHT___
//
___IMPORTHEADER_cocoaTouchSubclass___
NS_ASSUME_NONNULL_BEGIN
@interface ___FILEBASENAMEASIDENTIFIER___ : ___VARIABLE_cocoaTouchSubclass___
@end
NS_ASSUME_NONNULL_END
//
// ___FILENAME___
// ___PROJECTNAME___
//
// Created by ___FULLUSERNAME___ on ___DATE___.
//___COPYRIGHT___
//
#import "___FILEBASENAME___.h"
@interface ___FILEBASENAMEASIDENTIFIER___ ()
@end
@implementation ___FILEBASENAMEASIDENTIFIER___
#pragma mark - life cycle
- (void)awakeFromNib {
[super awakeFromNib];
}
@end
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="11134" systemVersion="15F34" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11106"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<collectionViewCell opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" id="gTV-IL-0wX" customClass="___FILEBASENAMEASIDENTIFIER___">
<rect key="frame" x="0.0" y="0.0" width="50" height="50"/>
<autoresizingMask key="autoresizingMask"/>
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center">
<rect key="frame" x="0.0" y="0.0" width="50" height="50"/>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
</view>
</collectionViewCell>
</objects>
</document>
//___FILEHEADER___
import UIKit
class ___FILEBASENAMEASIDENTIFIER___: ___VARIABLE_cocoaTouchSubclass___ {
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
}
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="13142" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12042"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<collectionViewCell opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" id="gTV-IL-0wX" customClass="___FILEBASENAMEASIDENTIFIER___" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="50" height="50"/>
<autoresizingMask key="autoresizingMask"/>
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center">
<rect key="frame" x="0.0" y="0.0" width="50" height="50"/>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
</view>
<viewLayoutGuide key="safeArea" id="ZTg-uK-7eu"/>
</collectionViewCell>
</objects>
</document>
//___FILEHEADER___
___IMPORTHEADER_cocoaTouchSubclass___
NS_ASSUME_NONNULL_BEGIN
@interface ___FILEBASENAMEASIDENTIFIER___ : ___VARIABLE_cocoaTouchSubclass___
@end
NS_ASSUME_NONNULL_END
//___FILEHEADER___
#import "___FILEBASENAME___.h"
@interface ___FILEBASENAMEASIDENTIFIER___ ()
@end
@implementation ___FILEBASENAMEASIDENTIFIER___
static NSString * const reuseIdentifier = @"Cell";
#pragma mark - life cycle
- (void)viewDidLoad {
[super viewDidLoad];
// Uncomment the following line to preserve selection between presentations
// self.clearsSelectionOnViewWillAppear = NO;
// Register cell classes
[self.collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:reuseIdentifier];
// Do any additional setup after loading the view.
}
#pragma mark <UICollectionViewDataSource>
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView {
#warning Incomplete implementation, return the number of sections
return 0;
}
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
#warning Incomplete implementation, return the number of items
return 0;
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:reuseIdentifier forIndexPath:indexPath];
// Configure the cell
return cell;
}
#pragma mark <UICollectionViewDelegate>
/*
// Uncomment this method to specify if the specified item should be highlighted during tracking
- (BOOL)collectionView:(UICollectionView *)collectionView shouldHighlightItemAtIndexPath:(NSIndexPath *)indexPath {
return YES;
}
*/
/*
// Uncomment this method to specify if the specified item should be selected
- (BOOL)collectionView:(UICollectionView *)collectionView shouldSelectItemAtIndexPath:(NSIndexPath *)indexPath {
return YES;
}
*/
/*
// Uncomment these methods to specify if an action menu should be displayed for the specified item, and react to actions performed on the item
- (BOOL)collectionView:(UICollectionView *)collectionView shouldShowMenuForItemAtIndexPath:(NSIndexPath *)indexPath {
return NO;
}
- (BOOL)collectionView:(UICollectionView *)collectionView canPerformAction:(SEL)action forItemAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender {
return NO;
}
- (void)collectionView:(UICollectionView *)collectionView performAction:(SEL)action forItemAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender {
}
*/
@end
//___FILEHEADER___
import UIKit
private let reuseIdentifier = "Cell"
class ___FILEBASENAMEASIDENTIFIER___: ___VARIABLE_cocoaTouchSubclass___ {
override func viewDidLoad() {
super.viewDidLoad()
// Uncomment the following line to preserve selection between presentations
// self.clearsSelectionOnViewWillAppear = false
// Register cell classes
self.collectionView!.register(UICollectionViewCell.self, forCellWithReuseIdentifier: reuseIdentifier)
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
}
*/
// MARK: UICollectionViewDataSource
override func numberOfSections(in collectionView: UICollectionView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 0
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of items
return 0
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath)
// Configure the cell
return cell
}
// MARK: UICollectionViewDelegate
/*
// Uncomment this method to specify if the specified item should be highlighted during tracking
override func collectionView(_ collectionView: UICollectionView, shouldHighlightItemAt indexPath: IndexPath) -> Bool {
return true
}
*/
/*
// Uncomment this method to specify if the specified item should be selected
override func collectionView(_ collectionView: UICollectionView, shouldSelectItemAt indexPath: IndexPath) -> Bool {
return true
}
*/
/*
// Uncomment these methods to specify if an action menu should be displayed for the specified item, and react to actions performed on the item
override func collectionView(_ collectionView: UICollectionView, shouldShowMenuForItemAt indexPath: IndexPath) -> Bool {
return false
}
override func collectionView(_ collectionView: UICollectionView, canPerformAction action: Selector, forItemAt indexPath: IndexPath, withSender sender: Any?) -> Bool {
return false
}
override func collectionView(_ collectionView: UICollectionView, performAction action: Selector, forItemAt indexPath: IndexPath, withSender sender: Any?) {
}
*/
}
//___FILEHEADER___
___IMPORTHEADER_cocoaTouchSubclass___
NS_ASSUME_NONNULL_BEGIN
@interface ___FILEBASENAMEASIDENTIFIER___ : ___VARIABLE_cocoaTouchSubclass___
@end
NS_ASSUME_NONNULL_END
//___FILEHEADER___
#import "___FILEBASENAME___.h"
@interface ___FILEBASENAMEASIDENTIFIER___ ()
@end
@implementation ___FILEBASENAMEASIDENTIFIER___
static NSString * const reuseIdentifier = @"Cell";
#pragma mark - life cycle
- (void)viewDidLoad {
[super viewDidLoad];
// Uncomment the following line to preserve selection between presentations
// self.clearsSelectionOnViewWillAppear = NO;
// Register cell classes
[self.collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:reuseIdentifier];
// Do any additional setup after loading the view.
}
#pragma mark <UICollectionViewDataSource>
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView {
#warning Incomplete implementation, return the number of sections
return 0;
}
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
#warning Incomplete implementation, return the number of items
return 0;
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:reuseIdentifier forIndexPath:indexPath];
// Configure the cell
return cell;
}
#pragma mark <UICollectionViewDelegate>
/*
// Uncomment this method to specify if the specified item should be highlighted during tracking
- (BOOL)collectionView:(UICollectionView *)collectionView shouldHighlightItemAtIndexPath:(NSIndexPath *)indexPath {
return YES;
}
*/
/*
// Uncomment this method to specify if the specified item should be selected
- (BOOL)collectionView:(UICollectionView *)collectionView shouldSelectItemAtIndexPath:(NSIndexPath *)indexPath {
return YES;
}
*/
/*
// Uncomment these methods to specify if an action menu should be displayed for the specified item, and react to actions performed on the item
- (BOOL)collectionView:(UICollectionView *)collectionView shouldShowMenuForItemAtIndexPath:(NSIndexPath *)indexPath {
return NO;
}
- (BOOL)collectionView:(UICollectionView *)collectionView canPerformAction:(SEL)action forItemAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender {
return NO;
}
- (void)collectionView:(UICollectionView *)collectionView performAction:(SEL)action forItemAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender {
}
*/
@end
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="13142" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12042"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="___FILEBASENAMEASIDENTIFIER___">
<connections>
<outlet property="view" destination="i5M-Pr-FkT" id="sfx-zR-JGt"/>
</connections>
</placeholder>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<collectionView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" dataMode="none" id="i5M-Pr-FkT">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<viewLayoutGuide key="safeArea" id="ij9-ne-Ryy"/>
<collectionViewFlowLayout key="collectionViewLayout" minimumLineSpacing="10" minimumInteritemSpacing="10" id="c7k-ch-oGk">
<size key="itemSize" width="50" height="50"/>
<size key="headerReferenceSize" width="0.0" height="0.0"/>
<size key="footerReferenceSize" width="0.0" height="0.0"/>
<inset key="sectionInset" minX="0.0" minY="0.0" maxX="0.0" maxY="0.0"/>
</collectionViewFlowLayout>
<connections>
<outlet property="dataSource" destination="-1" id="Tng-2m-Rnh"/>
<outlet property="delegate" destination="-1" id="9aC-8N-iBw"/>
</connections>
</collectionView>
</objects>
</document>
//___FILEHEADER___
import UIKit
private let reuseIdentifier = "Cell"
class ___FILEBASENAMEASIDENTIFIER___: ___VARIABLE_cocoaTouchSubclass___ {
override func viewDidLoad() {
super.viewDidLoad()
// Uncomment the following line to preserve selection between presentations
// self.clearsSelectionOnViewWillAppear = false
// Register cell classes
self.collectionView!.register(UICollectionViewCell.self, forCellWithReuseIdentifier: reuseIdentifier)
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
}
*/
// MARK: UICollectionViewDataSource
override func numberOfSections(in collectionView: UICollectionView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 0
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of items
return 0
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath)
// Configure the cell
return cell
}
// MARK: UICollectionViewDelegate
/*
// Uncomment this method to specify if the specified item should be highlighted during tracking
override func collectionView(_ collectionView: UICollectionView, shouldHighlightItemAt indexPath: IndexPath) -> Bool {
return true
}
*/
/*
// Uncomment this method to specify if the specified item should be selected
override func collectionView(_ collectionView: UICollectionView, shouldSelectItemAt indexPath: IndexPath) -> Bool {
return true
}
*/
/*
// Uncomment these methods to specify if an action menu should be displayed for the specified item, and react to actions performed on the item
override func collectionView(_ collectionView: UICollectionView, shouldShowMenuForItemAt indexPath: IndexPath) -> Bool {
return false
}
override func collectionView(_ collectionView: UICollectionView, canPerformAction action: Selector, forItemAt indexPath: IndexPath, withSender sender: Any?) -> Bool {
return false
}
override func collectionView(_ collectionView: UICollectionView, performAction action: Selector, forItemAt indexPath: IndexPath, withSender sender: Any?) {
}
*/
}
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="13142" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12042"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="___FILEBASENAMEASIDENTIFIER___" customModuleProvider="target">
<connections>
<outlet property="view" destination="i5M-Pr-FkT" id="sfx-zR-JGt"/>
</connections>
</placeholder>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<collectionView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" dataMode="none" id="i5M-Pr-FkT">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<viewLayoutGuide key="safeArea" id="oBF-5E-Iza"/>
<collectionViewFlowLayout key="collectionViewLayout" minimumLineSpacing="10" minimumInteritemSpacing="10" id="c7k-ch-oGk">
<size key="itemSize" width="50" height="50"/>
<size key="headerReferenceSize" width="0.0" height="0.0"/>
<size key="footerReferenceSize" width="0.0" height="0.0"/>
<inset key="sectionInset" minX="0.0" minY="0.0" maxX="0.0" maxY="0.0"/>
</collectionViewFlowLayout>
<connections>
<outlet property="dataSource" destination="-1" id="Tng-2m-Rnh"/>
<outlet property="delegate" destination="-1" id="9aC-8N-iBw"/>
</connections>
</collectionView>
</objects>
</document>
//
// ___FILENAME___
// ___PROJECTNAME___
//
// Created by ___FULLUSERNAME___ on ___DATE___.
//___COPYRIGHT___
//
___IMPORTHEADER_cocoaTouchSubclass___
NS_ASSUME_NONNULL_BEGIN
@interface ___FILEBASENAMEASIDENTIFIER___ : ___VARIABLE_cocoaTouchSubclass___
@end
NS_ASSUME_NONNULL_END
//
// ___FILENAME___
// ___PROJECTNAME___
//
// Created by ___FULLUSERNAME___ on ___DATE___.
//___COPYRIGHT___
//
#import "___FILEBASENAME___.h"
@interface ___FILEBASENAMEASIDENTIFIER___ ()
@end
@implementation ___FILEBASENAMEASIDENTIFIER___
#pragma mark - life cycle
- (void)awakeFromNib {
[super awakeFromNib];
}
@end
//___FILEHEADER___
import UIKit
class ___FILEBASENAMEASIDENTIFIER___: ___VARIABLE_cocoaTouchSubclass___ {
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
//
// ___FILENAME___
// ___PROJECTNAME___
//
// Created by ___FULLUSERNAME___ on ___DATE___.
//___COPYRIGHT___
//
___IMPORTHEADER_cocoaTouchSubclass___
NS_ASSUME_NONNULL_BEGIN
@interface ___FILEBASENAMEASIDENTIFIER___ : ___VARIABLE_cocoaTouchSubclass___
@end
NS_ASSUME_NONNULL_END
//
// ___FILENAME___
// ___PROJECTNAME___
//
// Created by ___FULLUSERNAME___ on ___DATE___.
//___COPYRIGHT___
//
#import "___FILEBASENAME___.h"
@interface ___FILEBASENAMEASIDENTIFIER___ ()
@end
@implementation ___FILEBASENAMEASIDENTIFIER___
#pragma mark - life cycle
- (void)awakeFromNib {
[super awakeFromNib];
}
@end
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="11134" systemVersion="15F34" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11106"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" id="KGk-i7-Jjw" customClass="___FILEBASENAMEASIDENTIFIER___">
<rect key="frame" x="0.0" y="0.0" width="320" height="44"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="KGk-i7-Jjw" id="H2p-sc-9uM">
<frame key="frameInset" width="320" height="43"/>
<autoresizingMask key="autoresizingMask"/>
</tableViewCellContentView>
</tableViewCell>
</objects>
</document>
//___FILEHEADER___
import UIKit
class ___FILEBASENAMEASIDENTIFIER___: ___VARIABLE_cocoaTouchSubclass___ {
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="13142" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12042"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" id="KGk-i7-Jjw" customClass="___FILEBASENAMEASIDENTIFIER___" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="320" height="44"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="KGk-i7-Jjw" id="H2p-sc-9uM">
<rect key="frame" x="0.0" y="0.0" width="320" height="43"/>
<autoresizingMask key="autoresizingMask"/>
</tableViewCellContentView>
<viewLayoutGuide key="safeArea" id="njF-e1-oar"/>
</tableViewCell>
</objects>
</document>
//___FILEHEADER___
___IMPORTHEADER_cocoaTouchSubclass___
NS_ASSUME_NONNULL_BEGIN
@interface ___FILEBASENAMEASIDENTIFIER___ : ___VARIABLE_cocoaTouchSubclass___
@end
NS_ASSUME_NONNULL_END
//___FILEHEADER___
#import "___FILEBASENAME___.h"
@interface ___FILEBASENAMEASIDENTIFIER___ ()
@end
@implementation ___FILEBASENAMEASIDENTIFIER___
#pragma mark - life cycle
- (void)viewDidLoad {
[super viewDidLoad];
}
#pragma mark - UITableViewDataSource
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 0;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return 0;
}
/*
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:<#@"reuseIdentifier"#> forIndexPath:indexPath];
// Configure the cell...
return cell;
}
*/
#pragma mark - UITableViewDelegate
@end
//___FILEHEADER___
import UIKit
class ___FILEBASENAMEASIDENTIFIER___: ___VARIABLE_cocoaTouchSubclass___ {
override func viewDidLoad() {
super.viewDidLoad()
// Uncomment the following line to preserve selection between presentations
// self.clearsSelectionOnViewWillAppear = false
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 0
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return 0
}
/*
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifier", for: indexPath)
// Configure the cell...
return cell
}
*/
/*
// Override to support conditional editing of the table view.
override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
// Return false if you do not want the specified item to be editable.
return true
}
*/
/*
// Override to support editing the table view.
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
// Delete the row from the data source
tableView.deleteRows(at: [indexPath], with: .fade)
} else if editingStyle == .insert {
// Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
}
}
*/
/*
// Override to support rearranging the table view.
override func tableView(_ tableView: UITableView, moveRowAt fromIndexPath: IndexPath, to: IndexPath) {
}
*/
/*
// Override to support conditional rearranging of the table view.
override func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -> Bool {
// Return false if you do not want the item to be re-orderable.
return true
}
*/
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
}
*/
}
//___FILEHEADER___
___IMPORTHEADER_cocoaTouchSubclass___
NS_ASSUME_NONNULL_BEGIN
@interface ___FILEBASENAMEASIDENTIFIER___ : ___VARIABLE_cocoaTouchSubclass___
@end
NS_ASSUME_NONNULL_END
//___FILEHEADER___
#import "___FILEBASENAME___.h"
@interface ___FILEBASENAMEASIDENTIFIER___ ()
@end
@implementation ___FILEBASENAMEASIDENTIFIER___
#pragma mark - life cycle
- (void)viewDidLoad {
[super viewDidLoad];
}
#pragma mark - UITableViewDataSource
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 0;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return 0;
}
/*
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:<#@"reuseIdentifier"#> forIndexPath:indexPath];
// Configure the cell...
return cell;
}
*/
#pragma mark - UITableViewDelegate
@end
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="13142" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12042"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="___FILEBASENAMEASIDENTIFIER___">
<connections>
<outlet property="view" destination="i5M-Pr-FkT" id="sfx-zR-JGt"/>
</connections>
</placeholder>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<tableView opaque="NO" clipsSubviews="YES" clearsContextBeforeDrawing="NO" contentMode="scaleToFill" bouncesZoom="NO" style="plain" separatorStyle="default" rowHeight="44" sectionHeaderHeight="22" sectionFooterHeight="22" id="i5M-Pr-FkT">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<viewLayoutGuide key="safeArea" id="NQg-xp-Ubj"/>
<connections>
<outlet property="dataSource" destination="-1" id="Tng-2m-Rnh"/>
<outlet property="delegate" destination="-1" id="9aC-8N-iBw"/>
</connections>
</tableView>
</objects>
</document>
//___FILEHEADER___
import UIKit
class ___FILEBASENAMEASIDENTIFIER___: ___VARIABLE_cocoaTouchSubclass___ {
override func viewDidLoad() {
super.viewDidLoad()
// Uncomment the following line to preserve selection between presentations
// self.clearsSelectionOnViewWillAppear = false
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 0
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return 0
}
/*
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifier", for: indexPath)
// Configure the cell...
return cell
}
*/
/*
// Override to support conditional editing of the table view.
override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
// Return false if you do not want the specified item to be editable.
return true
}
*/
/*
// Override to support editing the table view.
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
// Delete the row from the data source
tableView.deleteRows(at: [indexPath], with: .fade)
} else if editingStyle == .insert {
// Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
}
}
*/
/*
// Override to support rearranging the table view.
override func tableView(_ tableView: UITableView, moveRowAt fromIndexPath: IndexPath, to: IndexPath) {
}
*/
/*
// Override to support conditional rearranging of the table view.
override func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -> Bool {
// Return false if you do not want the item to be re-orderable.
return true
}
*/
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
}
*/
}
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="13142" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12042"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="___FILEBASENAMEASIDENTIFIER___" customModuleProvider="target">
<connections>
<outlet property="view" destination="i5M-Pr-FkT" id="sfx-zR-JGt"/>
</connections>
</placeholder>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<tableView opaque="NO" clipsSubviews="YES" clearsContextBeforeDrawing="NO" contentMode="scaleToFill" bouncesZoom="NO" style="plain" separatorStyle="default" rowHeight="44" sectionHeaderHeight="22" sectionFooterHeight="22" id="i5M-Pr-FkT">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<viewLayoutGuide key="safeArea" id="vLr-E1-eTs"/>
<connections>
<outlet property="dataSource" destination="-1" id="Tng-2m-Rnh"/>
<outlet property="delegate" destination="-1" id="9aC-8N-iBw"/>
</connections>
</tableView>
</objects>
</document>
//
// ___FILENAME___
// ___PROJECTNAME___
//
// Created by ___FULLUSERNAME___ on ___DATE___.
//___COPYRIGHT___
//
___IMPORTHEADER_cocoaTouchSubclass___
NS_ASSUME_NONNULL_BEGIN
@interface ___FILEBASENAMEASIDENTIFIER___ : ___VARIABLE_cocoaTouchSubclass___
@end
NS_ASSUME_NONNULL_END
//
// ___FILENAME___
// ___PROJECTNAME___
//
// Created by ___FULLUSERNAME___ on ___DATE___.
//___COPYRIGHT___
//
#import "___FILEBASENAME___.h"
@interface ___FILEBASENAMEASIDENTIFIER___ ()
@end
@implementation ___FILEBASENAMEASIDENTIFIER___
#pragma mark - life cycle
- (void)viewDidLoad {
[super viewDidLoad];
}
#pragma mark - actions
#pragma mark - public Method
#pragma mark - private Method
#pragma mark - delegate
#pragma mark - getters and setters
@end
//___FILEHEADER___
import UIKit
class ___FILEBASENAMEASIDENTIFIER___: ___VARIABLE_cocoaTouchSubclass___ {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
}
*/
}
//
// ___FILENAME___
// ___PROJECTNAME___
//
// Created by ___FULLUSERNAME___ on ___DATE___.
//___COPYRIGHT___
//
___IMPORTHEADER_cocoaTouchSubclass___
NS_ASSUME_NONNULL_BEGIN
@interface ___FILEBASENAMEASIDENTIFIER___ : ___VARIABLE_cocoaTouchSubclass___
@end
NS_ASSUME_NONNULL_END
//
// ___FILENAME___
// ___PROJECTNAME___
//
// Created by ___FULLUSERNAME___ on ___DATE___.
//___COPYRIGHT___
//
#import "___FILEBASENAME___.h"
@interface ___FILEBASENAMEASIDENTIFIER___ ()
@end
@implementation ___FILEBASENAMEASIDENTIFIER___
#pragma mark - life cycle
- (void)viewDidLoad {
[super viewDidLoad];
}
#pragma mark - actions
#pragma mark - public Method
#pragma mark - private Method
#pragma mark - delegate
#pragma mark - getters and setters
@end
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="11134" systemVersion="15F34" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11106"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="___FILEBASENAMEASIDENTIFIER___">
<connections>
<outlet property="view" destination="i5M-Pr-FkT" id="sfx-zR-JGt"/>
</connections>
</placeholder>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<view clearsContextBeforeDrawing="NO" contentMode="scaleToFill" id="i5M-Pr-FkT">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</view>
</objects>
</document>
//___FILEHEADER___
import UIKit
class ___FILEBASENAMEASIDENTIFIER___: ___VARIABLE_cocoaTouchSubclass___ {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
}
*/
}
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="13142" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12042"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="___FILEBASENAMEASIDENTIFIER___" customModuleProvider="target">
<connections>
<outlet property="view" destination="i5M-Pr-FkT" id="sfx-zR-JGt"/>
</connections>
</placeholder>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<view clearsContextBeforeDrawing="NO" contentMode="scaleToFill" id="i5M-Pr-FkT">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<viewLayoutGuide key="safeArea" id="fnl-2z-Ty3"/>
</view>
</objects>
</document>
//
// ___FILENAME___
// ___PROJECTNAME___
//
// Created by ___FULLUSERNAME___ on ___DATE___.
//___COPYRIGHT___
//
___IMPORTHEADER_cocoaTouchSubclass___
NS_ASSUME_NONNULL_BEGIN
@interface ___FILEBASENAMEASIDENTIFIER___ : ___VARIABLE_cocoaTouchSubclass___
@end
NS_ASSUME_NONNULL_END
//
// ___FILENAME___
// ___PROJECTNAME___
//
// Created by ___FULLUSERNAME___ on ___DATE___.
//___COPYRIGHT___
//
#import "___FILEBASENAME___.h"
@interface ___FILEBASENAMEASIDENTIFIER___ ()
@end
@implementation ___FILEBASENAMEASIDENTIFIER___
#pragma mark - life cycle
@end
//___FILEHEADER___
import UIKit
class ___FILEBASENAMEASIDENTIFIER___: ___VARIABLE_cocoaTouchSubclass___ {
/*
// Only override draw() if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
override func draw(_ rect: CGRect) {
// Drawing code
}
*/
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment